├── .github
└── workflows
│ ├── integration.yml
│ ├── publish.yml
│ └── tests.yml
├── .gitignore
├── .gitmodules
├── .readthedocs.yaml
├── LICENSE
├── README.md
├── doc
├── Makefile
├── paper.bib
├── paper.md
├── snewpy-flowchart.pdf
└── source
│ ├── .static
│ └── README
│ ├── .templates
│ └── README
│ ├── citing.rst
│ ├── conf.py
│ ├── contributing.rst
│ ├── flux.rst
│ ├── gettingstarted.rst
│ ├── index.rst
│ ├── luminosity-comparison.pdf
│ ├── luminosity-comparison.png
│ ├── models.rst
│ ├── nb
│ ├── AnalyticFluence.ipynb
│ ├── FlavorTransformation.ipynb
│ ├── README.md
│ ├── SNOwGLoBES_models.ipynb
│ ├── SNOwGLoBES_usage.ipynb
│ ├── ccsn
│ │ ├── Bollig_2016.ipynb
│ │ ├── Bugli_2021.ipynb
│ │ ├── Fischer_2020.ipynb
│ │ ├── Fornax_2019.ipynb
│ │ ├── Fornax_2021.ipynb
│ │ ├── Fornax_2022.ipynb
│ │ ├── Kuroda_2020.ipynb
│ │ ├── Mori_2023.ipynb
│ │ ├── Nakazato_2013.ipynb
│ │ ├── OConnor_2013.ipynb
│ │ ├── OConnor_2015.ipynb
│ │ ├── Sukhbold_2015.ipynb
│ │ ├── Tamborra_2014.ipynb
│ │ ├── Walk_2018.ipynb
│ │ ├── Walk_2019.ipynb
│ │ ├── Warren_2020.ipynb
│ │ └── Zha_2021.ipynb
│ ├── dev
│ │ ├── Detector_demo.ipynb
│ │ ├── ExtendedCoolingTail.ipynb
│ │ └── FluxContainer_demo.ipynb
│ └── presn
│ │ ├── Kato_2017.ipynb
│ │ ├── Odrzywolek_2010.ipynb
│ │ ├── Patton_2017.ipynb
│ │ └── Yoshida_2016.ipynb
│ ├── neutrino.rst
│ ├── notebooks.rst
│ ├── snewpy-logo-bh.png
│ ├── snewpy-logo-revival.png
│ ├── snewpy-logo.png
│ ├── snowglobes.rst
│ └── transformations.rst
├── pyproject.toml
├── pytest.ini
└── python
└── snewpy
├── __init__.py
├── _model_downloader.py
├── flavor.py
├── flavor_transformation
├── TransformationChain.py
├── __init__.py
├── base.py
├── in_earth.py
├── in_sn.py
└── in_vacuum.py
├── flux.py
├── models
├── __init__.py
├── base.py
├── ccsn.py
├── ccsn_loaders.py
├── extended.py
├── model_files.yml
├── presn.py
├── presn_loaders.py
└── registry_model.py
├── neutrino.py
├── rate_calculator.py
├── scripts
├── Analytic.py
├── Convert_to_ROOT.py
├── SNEWS2.0_rate_table.py
├── SNEWS2.0_rate_table_singleexample.py
├── TimeSeries.py
├── make_rate_table_dict.py
├── old_to_snowglobes.py
└── snewpy_to_snewpdag.py
├── snowglobes.py
├── snowglobes_interface.py
├── test
├── __init__.py
├── _rate_crosscheck_table.py
├── simplerate_integrationtest.py
├── test_00_init.py
├── test_01_registry.py
├── test_02_models.py
├── test_03_neutrino.py
├── test_04_xforms.py
├── test_05_snowglobes.py
├── test_flavors.py
├── test_flux_container.py
├── test_presn_rates.py
└── test_rate_calculation.py
└── utils.py
/.github/workflows/integration.yml:
--------------------------------------------------------------------------------
1 | # Workflow that installs SNOwGLoBES and runs an integration test
2 |
3 | name: Integration Tests
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the main branch
7 | on:
8 | push:
9 | branches:
10 | - main
11 | - release_*
12 | pull_request:
13 | branches:
14 | - main
15 | - release_*
16 | types: [opened, synchronize, reopened, ready_for_review]
17 |
18 | jobs:
19 | run:
20 | # Only execute integration tests if PR is *not* a draft
21 | if: github.event.pull_request.draft == false
22 |
23 | strategy:
24 | matrix:
25 | # Test all supported Python versions under Ubuntu
26 | os: [ubuntu-latest]
27 | python-version: ['3.10', '3.11', '3.12', '3.13']
28 | # Additionally, test one Python version under MacOS and Windows, to detect OS-specific issues
29 | include:
30 | - os: macos-latest
31 | python-version: '3.12'
32 | - os: windows-latest
33 | python-version: '3.12'
34 |
35 | runs-on: ${{ matrix.os }}
36 |
37 | steps:
38 | - uses: actions/checkout@v4
39 | - name: Set up Python ${{ matrix.python-version }}
40 | uses: actions/setup-python@v5
41 | with:
42 | python-version: ${{ matrix.python-version }}
43 | - name: Install dependencies
44 | run: |
45 | python -m pip install --upgrade pip
46 | - name: Install SNEWPY
47 | run: |
48 | pip install ".[dev,docs]"
49 | - name: Run Integration Tests
50 | run: |
51 | python -m unittest python/snewpy/test/simplerate_integrationtest.py
52 | pytest -m 'snowglobes'
53 | - uses: r-lib/actions/setup-pandoc@v2
54 |
55 | - name: Build HTML docs
56 | run: |
57 | cd doc/
58 | make html
59 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | # This workflows will upload a Python Package using Twine when a release is published
2 | # Based on https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | name: Publish to PyPI
5 |
6 | on:
7 | push:
8 | tags: # Sequence of patterns matched against refs/tags
9 | - 'v*' # Any tag matching v*, e.g. v1.0, v1.2b1
10 |
11 | jobs:
12 | deploy:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | - name: Set up Python
18 | uses: actions/setup-python@v5
19 | with:
20 | python-version: '3.11'
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install build twine
25 |
26 | - name: Check and get Version Number
27 | id: get_version
28 | run: |
29 | pip install .
30 | PYTHON_VERSION=`python -c 'import snewpy; print(snewpy.__version__)'`
31 | echo "PYTHON_VERSION=${PYTHON_VERSION}"
32 | GIT_VERSION=${GITHUB_REF/refs\/tags\//}
33 | echo "GIT_VERSION=${GIT_VERSION}"
34 | if [ v$PYTHON_VERSION != $GIT_VERSION ]; then exit 1; fi
35 | echo "VERSION=${GIT_VERSION}" >> $GITHUB_OUTPUT
36 |
37 | - name: Build and publish
38 | env:
39 | TWINE_USERNAME: __token__
40 | TWINE_PASSWORD: ${{ secrets.PYPI_JM }}
41 | run: |
42 | python -m build
43 | twine upload dist/*
44 |
45 | - name: Create Draft Release
46 | # Go to https://github.com/SNEWS2/snewpy/releases to edit this draft release and publish it
47 | # Once it is published, the release automatically is pushed to Zenodo: https://doi.org/10.5281/zenodo.4498940
48 | id: create_release
49 | uses: actions/create-release@v1
50 | env:
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | with:
53 | tag_name: ${{ steps.get_version.outputs.VERSION }}
54 | release_name: ${{ steps.get_version.outputs.VERSION }}
55 | body: |
56 | [](https://doi.org/10.5281/zenodo.4498940)
57 | (TODO: This DOI always points to the latest version. Replace it with the DOI for this specific release!)
58 |
59 | List major changes since the last release here: newly added models, new features, etc.
60 | If necessary, also list breaking changes: removed features, renamed command line options, new minimum Python version, etc.
61 |
62 | draft: true
63 | prerelease: false
64 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: tests
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the main branch
7 | on:
8 | push:
9 | branches:
10 | - main
11 | - release_*
12 | pull_request:
13 | branches:
14 | - main
15 | - release_*
16 |
17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
18 | jobs:
19 | # This workflow contains a single job called "build"
20 | build:
21 | strategy:
22 | matrix:
23 | # Test all supported Python versions under Ubuntu
24 | os: [ubuntu-latest]
25 | python-version: ['3.10', '3.11', '3.12', '3.13']
26 | # Additionally, test one Python version under MacOS and Windows, to detect OS-specific issues
27 | include:
28 | - os: macos-latest
29 | python-version: '3.12'
30 | - os: windows-latest
31 | python-version: '3.12'
32 |
33 | # The type of runner that the job will run on.
34 | runs-on: ${{ matrix.os }}
35 |
36 | # Steps represent a sequence of tasks that will be executed as part of the job
37 | steps:
38 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
39 | - uses: actions/checkout@v4
40 |
41 | # Set up the Python environment and dependencies
42 | - name: Set up Python ${{ matrix.python-version }}
43 | uses: actions/setup-python@v5
44 | with:
45 | python-version: ${{ matrix.python-version }}
46 | - name: Install dependencies
47 | run: |
48 | python -m pip install --upgrade pip
49 | - name: Install SNEWPY
50 | run: |
51 | pip install ".[dev]"
52 |
53 | - name: Run unit tests with pytest
54 | run: |
55 | pytest -m 'not snowglobes' -k 'not EarthMatter'
56 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled code and build products
2 | __pycache__/
3 | *.pyc
4 | *.so
5 | build/
6 | dist/
7 | *.egg-info/
8 |
9 | # default directory for downloaded model files
10 | SNEWPY_models/
11 |
12 | ## SNEWPY output files
13 | # snewpy.snowglobes intermediate results
14 | *_SNOprocessed.tar.gz
15 | *.npy
16 | *.npz
17 | # used for integration tests in test_snowglobes.py
18 | fluence_Bollig_2016_s*
19 | # plots generated by FlavorTransformation.ipynb
20 | doc/source/nb/*_adiabaticmsw*.pdf
21 |
22 | # editor-specific
23 | .ipynb_checkpoints/
24 | .vscode/
25 |
26 | #hypothesis package cache
27 | .hypothesis/
28 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SNEWS2/snewpy/dc4c2c76235e3208ff554967180b24fe65173078/.gitmodules
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yaml
2 | # Read the Docs configuration file.
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details.
4 |
5 | version: 2
6 |
7 | # Build information required.
8 | build:
9 | os: "ubuntu-22.04"
10 | tools:
11 | python: "3.11"
12 |
13 | # Python version and requirements to build docs.
14 | python:
15 | install:
16 | - method: pip
17 | path: .
18 | extra_requirements:
19 | - docs
20 |
21 | # Build documentation in the doc/ directory with sphinx.
22 | sphinx:
23 | configuration: doc/source/conf.py
24 | fail_on_warning: true
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, SNEWS2.0
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SNEWPY: Supernova Neutrino Early Warning Models for Python
2 |
3 |
4 |
5 | [](https://zenodo.org/badge/latestdoi/221705586)
6 | [](https://pypi.org/project/snewpy/)
7 | 
8 | [](https://snewpy.readthedocs.io/en/latest/?badge=latest)
9 |
10 | SNEWPY is a Python package for working with supernova neutrinos. It offers …
11 |
12 | * … a simple and unified interface to hundreds of supernova simulations.
13 | * … a large library of flavor transformations that relate neutrino fluxes produced in the supernova to those reaching a detector on Earth.
14 | * … and a Python interface to SNOwGLoBES which lets you estimate and plot event rates in many different neutrino detectors.
15 |
16 |
17 | ## Installation
18 |
19 | Run `pip install snewpy` to install SNEWPY.
20 |
21 | SNEWPY includes a large number of supernova models from different simulation groups. Since these models have a size of several 100 MB, they are not included in the initial install but will be downloaded automatically when needed.
22 | Alternatively, you can run the following command to explicitly download models you want to use to a subdirectory named `SNEWPY-models//` in the current directory:
23 |
24 | `python -c 'import snewpy; snewpy.get_models()'`
25 |
26 |
27 | ## Usage and Documentation
28 |
29 | SNEWPY gives you easy access to hundreds of included SN simulations …
30 | ```Python
31 | import astropy.units as u
32 | from snewpy.models.ccsn import Nakazato_2013, Bollig_2016
33 |
34 | # Initialise two SN models. This automatically downloads the required data files if necessary.
35 | nakazato = Nakazato_2013(progenitor_mass=20*u.solMass, revival_time=100*u.ms, metallicity=0.004, eos='shen')
36 | bollig = Bollig_2016(progenitor_mass=27*u.solMass)
37 | ```
38 |
39 | … and many flavor transformations that neutrinos could experience on the way to Earth …
40 | ```Python
41 | from snewpy.flavor_transformation import AdiabaticMSW
42 | from snewpy.neutrino import MassHierarchy
43 |
44 | # Adiabatic MSW flavor transformation with normal mass ordering
45 | msw_nmo = AdiabaticMSW(mh=MassHierarchy.NORMAL)
46 | ```
47 |
48 | … letting you quickly calculate the neutrino flux reaching Earth:
49 | ```Python
50 | times = [0.5, 1] * u.s
51 | energies = range(5,50) * u.MeV
52 | # Assume a SN at the fiducial distance of 10 kpc and normal mass ordering.
53 | flux = bollig.get_flux(times, energies, distance=10*u.kpc, flavor_xform=msw_nmo)
54 | ```
55 |
56 | You can also calculate the observed event rate in all neutrino detectors supported by SNOwGLoBES, use the included SN models and flavor transformations in third-party code (like event generators), and much more.
57 |
58 | Jupyter notebooks showcasing the downloadable supernova models available through SNEWPY and much of its functionality are available in the `doc/source/nb/` subfolder.
59 | Additional example scripts are in the
60 | `python/snewpy/scripts/` subfolder.
61 |
62 | Papers describing SNEWPY and the underlying physics are published in the Astrophysical Journal ([DOI:10.3847/1538-4357/ac350f](https://dx.doi.org/10.3847/1538-4357/ac350f), [arXiv:2109.08188](https://arxiv.org/abs/2109.08188)) and the Journal of Open Source Software ([DOI:10.21105/joss.03772](https://dx.doi.org/10.21105/joss.03772)).
63 |
64 | For more, see the [full documentation on Read the Docs](https://snewpy.rtfd.io/).
65 |
66 | ## Contributing
67 |
68 | **Your contributions to SNEWPY are welcome!** For minor changes, simply submit a pull request. If you plan larger changes, it’s probably a good idea to open an issue first to coordinate our work.
69 |
70 | We use a [Fork & Pull Request](https://docs.github.com/en/get-started/quickstart/fork-a-repo) workflow, which is common on GitHub.
71 | Please see the [Contributing page](https://snewpy.readthedocs.io/en/stable/contributing.html) in our full documentation for details.
--------------------------------------------------------------------------------
/doc/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -W --keep-going -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/snewpy.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/snewpy.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/snewpy"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/snewpy"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/doc/snewpy-flowchart.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SNEWS2/snewpy/dc4c2c76235e3208ff554967180b24fe65173078/doc/snewpy-flowchart.pdf
--------------------------------------------------------------------------------
/doc/source/.static/README:
--------------------------------------------------------------------------------
1 | Add any paths that contain custom static files (such as style sheets) here,
2 | relative to this directory. They are copied after the builtin static files, so
3 | a file named "default.css" will overwrite the builtin "default.css."
4 |
--------------------------------------------------------------------------------
/doc/source/.templates/README:
--------------------------------------------------------------------------------
1 | Add any paths that contain templates here, relative to this directory.
2 |
--------------------------------------------------------------------------------
/doc/source/citing.rst:
--------------------------------------------------------------------------------
1 | Citing SNEWPY
2 | =============
3 |
4 | If you use SNEWPY for your own research, please cite the following two papers:
5 |
6 | * \A. L. Baxter `et al.` (SNEWS Collaboration):
7 | “SNEWPY: A Data Pipeline from Supernova Simulations to Neutrino Signals”.
8 | Journal of Open Source Software 6 (2021) 67, 03772.
9 | [`DOI:10.21105/joss.03772 `_]
10 |
11 | * \A. L. Baxter `et al.` (SNEWS Collaboration):
12 | “SNEWPY: A Data Pipeline from Supernova Simulations to Neutrino Signals”.
13 | Astrophysical Journal 925 (2022) 107.
14 | [`arXiv:2109.08188 `_, `DOI:10.3847/1538-4357/ac350f `_]
15 |
16 |
17 | To refer to a specific version of SNEWPY (e.g. to ensure reproducibility), you
18 | can use the `Zenodo entry for SNEWPY `_.
19 | Zenodo automatically archives all released SNEWPY versions and generates version-specific DOIs.
20 |
21 |
22 | Additionally, SNEWPY lets you access hundreds of supernova models from different modeling groups.
23 | If you use one of these models, please always cite the appropriate reference.
24 | You can find the appropriate reference in the README file downloaded alongside
25 | the model files or in the documentation of the respective :class:`.SupernovaModel` subclass.
--------------------------------------------------------------------------------
/doc/source/contributing.rst:
--------------------------------------------------------------------------------
1 | Contributing to SNEWPY
2 | ======================
3 |
4 | Development of SNEWPY happens in `our repository on GitHub `_.
5 | If you already use GitHub, everything works as you’re used to; if you don’t,
6 | check out `GitHub’s documentation `_ if
7 | anything in this section is unclear.
8 |
9 | Feedback or Problems
10 | --------------------
11 |
12 | The best way to give feedback, request features or report problems is to
13 | `open an issue `_.
14 |
15 |
16 | Contribute Code or Documentation
17 | --------------------------------
18 | **Your contributions to SNEWPY are welcome!**
19 |
20 | To contribute, please `create your own fork `_
21 | of the SNEWPY repository [#fn_fork]_, then clone it::
22 |
23 | git clone git@github.com:/snewpy.git
24 | cd snewpy/
25 |
26 | Next, make your changes and try them out. Where relevant, run tests or build
27 | documentation as described in the following subsections.
28 | Once you're happy with your changes, please
29 | `create a pull request `_.
30 | If you plan larger changes, it’s probably a good idea to open an issue first
31 | to coordinate our work.
32 |
33 | Running Tests
34 | ~~~~~~~~~~~~~
35 |
36 | SNEWPY uses the `pytest `_ package for automated testing.
37 | First, to install the necessary packages, use::
38 |
39 | pip install ".[dev]"
40 |
41 | You can then run the tests using the
42 |
43 | pytest
44 |
45 | command in the SNEWPY root directory. To skip integration tests which depend
46 | on SNOwGLoBES (or to run *only* those tests) you can use one of::
47 |
48 | pytest -m 'snowglobes' # only run tests that depend on SNOwGLoBES
49 | pytest -m 'not snowglobes'
50 |
51 | Normally, integration tests use the release version of SNOwGLoBES that was
52 | installed as a dependency of SNEWPY. To use custom data files, set the
53 | ``$SNOWGLOBES`` environment variable to the path of your SNOwGLoBES directory.
54 |
55 | Building Documentation
56 | ~~~~~~~~~~~~~~~~~~~~~~
57 |
58 | SNEWPY uses `Sphinx `_ for documentation.
59 | See the Sphinx website for an `overview over the markup syntax `_.
60 | First, to install the necessary packages, use::
61 |
62 | pip install ".[docs]"
63 | cd doc/
64 |
65 | You can then build the documentation and view it in your browser::
66 |
67 | make html
68 | open build/index.html
69 |
70 | Contribute Supernova Models
71 | ---------------------------
72 |
73 | If you are a supernova modeler and want to allow us to use your models in
74 | SNEWPY, we are happy to hear from you!
75 | Please `open an issue `_ to discuss
76 | your contribution or simply `submit a pull request
77 | `_ with your model files.
78 |
79 | Ideally, your pull request should include a customized SupernovaModel subclass
80 | to read in your model files. See the code of existing models for examples or
81 | let us know in your issue or pull request if you need help.
82 |
83 |
84 | .. rubric:: Footnotes
85 |
86 | .. [#fn_fork] If you are a member of the SNEWS2 organization on GitHub, you
87 | don’t need to create a fork; simply use ``git clone git@github.com:SNEWS2/snewpy.git`` instead.
88 | In that case, please include your user name in the branch name for clarity.
--------------------------------------------------------------------------------
/doc/source/flux.rst:
--------------------------------------------------------------------------------
1 | Interface for flux and rate calculation
2 | =======================================
3 |
4 | .. note::
5 | Users should consider using high-level interface, provided by :mod:`snewpy.snowglobes`
6 | This low-level interface is not guaranteed to be stable and may change at any time without warning.
7 |
8 | Flux containers
9 | ---------------
10 | .. automodule:: snewpy.flux
11 |
12 | Rate calcuation
13 | ---------------
14 | .. automodule:: snewpy.rate_calculator
15 |
--------------------------------------------------------------------------------
/doc/source/gettingstarted.rst:
--------------------------------------------------------------------------------
1 | Getting Started
2 | ===============
3 |
4 | Installation
5 | ------------
6 |
7 | To use SNEWPY, first install it using pip:
8 |
9 | .. code-block:: console
10 |
11 | $ pip install snewpy
12 |
13 |
14 | .. _sec-download_models:
15 |
16 | Download Supernova Models
17 | -------------------------
18 |
19 | SNEWPY includes a large number of supernova models from different simulation groups.
20 | Since these models have a size of several 100 MB, they are not included in the initial install.
21 | Instead, SNEWPY automatically loads these files the first time you use a model.
22 | Alternatively, you can run the following command to bulk download model files:
23 |
24 | .. code-block:: console
25 |
26 | $ python -c 'import snewpy; snewpy.get_models()'
27 |
28 | Files are downloaded to a hidden directory given by ``snewpy.model_path``.
29 |
30 | .. note::
31 |
32 | The documentation for each model includes more information, including a reference to the corresponding publication
33 | (e.g. DOI or arXiv identifier). If you use one of these models, please always cite the appropriate reference.
34 |
35 |
36 | Usage
37 | -----
38 |
39 | This example script shows how to use SNEWPY to compare the luminosity of two different supernova models:
40 |
41 | .. code-block:: python
42 |
43 | import astropy.units as u
44 | import matplotlib as mpl
45 | import matplotlib.pyplot as plt
46 |
47 | import snewpy
48 | from snewpy.models.ccsn import Nakazato_2013, Bollig_2016
49 | from snewpy.neutrino import Flavor
50 |
51 | mpl.rc('font', size=16)
52 | %matplotlib inline
53 |
54 | # Initialise two different models. This automatically downloads the required data files.
55 | nakazato = Nakazato_2013(progenitor_mass=20*u.solMass, revival_time=100*u.ms, metallicity=0.004, eos='shen')
56 | bollig = Bollig_2016(progenitor_mass=27*u.solMass)
57 |
58 | # Plot luminosity of both models
59 | fig, ax = plt.subplots(1, figsize=(10, 6))
60 |
61 | for flavor in Flavor:
62 | ax.plot(nakazato.time, nakazato.luminosity[flavor]/1e51, # Report luminosity in units foe/s
63 | label=flavor.to_tex() + ' (Nakazato)',
64 | color='C0' if flavor.is_electron else 'C2',
65 | ls='-' if flavor.is_neutrino else '--',
66 | lw=2)
67 |
68 | for flavor in Flavor:
69 | ax.plot(bollig.time, bollig.luminosity[flavor]/1e51, # Report luminosity in units foe/s
70 | label=flavor.to_tex() + ' (Bollig)',
71 | color='C1' if flavor.is_electron else 'C3',
72 | ls='-' if flavor.is_neutrino else '--',
73 | lw=1)
74 |
75 | ax.set(xlim=(-0.05, 0.5), xlabel=r'$t-t_{\rm bounce}$ [s]', ylabel=r'luminosity [foe s$^{-1}$]')
76 | ax.grid()
77 | ax.legend(loc='upper right', ncol=2, fontsize=18)
78 |
79 | This will generate the following figure:
80 |
81 | .. image:: luminosity-comparison.*
82 |
83 |
84 | The SNEWPY repository contains many Jupyter notebooks in ``doc/source/nb/`` with sample code
85 | showing different models or how to apply flavor transformations to the neutrino fluxes.
86 |
87 | More advanced usage of SNEWPY requires SNOwGLoBES and is described in the following section.
88 |
--------------------------------------------------------------------------------
/doc/source/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to SNEWPY!
2 | ==================
3 |
4 | .. image:: snewpy-logo.png
5 | :alt: snewpy logo: The word 'snewpy' in a monospace font, with an explosion emoji inside the letter 'p'.
6 | :width: 600px
7 |
8 | SNEWPY is a Python package for working with supernova neutrinos. It offers …
9 |
10 | * … a simple and unified interface to hundreds of supernova simulations.
11 | * … a large library of flavor transformations that relate neutrino fluxes
12 | produced in the supernova to those reaching a detector on Earth.
13 | * … and a Python interface to SNOwGLoBES which lets you estimate and plot event
14 | rates in many different neutrino detectors.
15 |
16 | This documentation describes SNEWPY’s API. For descriptions of the underlying
17 | physics, please see the paper `arXiv:2109.08188 `_.
18 |
19 | Table of Contents
20 | -----------------
21 |
22 | .. toctree::
23 | :maxdepth: 2
24 |
25 | gettingstarted
26 | snowglobes
27 | models
28 | transformations
29 | neutrino
30 | flux
31 | contributing
32 | citing
33 | notebooks
34 |
35 |
36 | Indices and Search
37 | ------------------
38 |
39 | * :ref:`genindex`
40 | * :ref:`modindex`
41 | * :ref:`search`
42 |
--------------------------------------------------------------------------------
/doc/source/luminosity-comparison.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SNEWS2/snewpy/dc4c2c76235e3208ff554967180b24fe65173078/doc/source/luminosity-comparison.pdf
--------------------------------------------------------------------------------
/doc/source/luminosity-comparison.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SNEWS2/snewpy/dc4c2c76235e3208ff554967180b24fe65173078/doc/source/luminosity-comparison.png
--------------------------------------------------------------------------------
/doc/source/models.rst:
--------------------------------------------------------------------------------
1 | Supernova Models: ``snewpy.models``
2 | ===================================
3 |
4 | Base Class for Supernova Models
5 | -------------------------------
6 | .. autoclass:: snewpy.models.base.SupernovaModel
7 | :members:
8 |
9 | Derived Models
10 | --------------
11 |
12 | Core-Collapse Supernova Models
13 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 |
15 | .. automodule:: snewpy.models.ccsn
16 | :members:
17 | :exclude-members: get_param_combinations, SNOwGLoBES, Analytic3Species
18 |
19 | Presupernova Models
20 | ~~~~~~~~~~~~~~~~~~~
21 | .. automodule:: snewpy.models.presn
22 | :members:
23 | :exclude-members: get_param_combinations
24 |
25 | Other Models
26 | ------------
27 | .. autoclass:: snewpy.models.ccsn.Analytic3Species
28 | :members:
29 |
30 | .. autoclass:: snewpy.models.ccsn.SNOwGLoBES
31 | :members:
32 |
--------------------------------------------------------------------------------
/doc/source/nb/README.md:
--------------------------------------------------------------------------------
1 | # SNEWPY Usage Examples
2 |
3 | The Jupyter notebooks in this directory contain different examples for how to use SNEWPY.
4 |
5 | ## `ccsn` and `presn` Directories
6 |
7 | These directories contain notebooks demonstrating how to use the core-collapse and pre-supernova models available through SNEWPY.
8 |
9 | ## AnalyticFluence
10 |
11 | This notebook demonstrates how to use the `Analytic3Species` class from `snewpy.models` to create an analytic supernova model by specifying the luminosity, mean energy and mean squared energy for three neutrino flavors.
12 |
13 | ## FlavorTransformation
14 |
15 | This notebook demonstrates the flavor transformations available in `snewpy.flavor_transformation`. It was used to produce many of the figures in the SNEWPY ApJ paper.
16 |
17 | ## SNOwGLoBES_models
18 |
19 | This notebook demonstrates how to use the `SNOwGLoBES` class in `snewpy.models`, which can be used with the `Type_Ia` and `PISN` model files that are available for download through SNEWPY.
20 |
21 | ## SNOwGLoBES_usage
22 |
23 | This notebook demonstrates how to use SNEWPY’s `snewpy.snowglobes` module to interact with SNOwGLoBES.
24 |
25 | ## `dev` Directory
26 |
27 | This directory contains notebooks which may be under development or illustrate usage of internal/undocumented APIs.
28 | They are not recommended for general users.
--------------------------------------------------------------------------------
/doc/source/nb/SNOwGLoBES_usage.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# `snewpy.snowglobes` Usage Example\n",
8 | "\n",
9 | "This notebook demonstrates how to use SNEWPY with SNOwGLoBES.\n",
10 | "\n",
11 | "To start, make sure you have SNOwGLoBES installed and have downloaded one of the models that are part of SNEWPY. Adjust the directory paths in the following cell."
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "from astropy import units as u\n",
21 | "import matplotlib.pyplot as plt\n",
22 | "import numpy as np\n",
23 | "\n",
24 | "from snewpy import snowglobes, model_path\n",
25 | "\n",
26 | "SNOwGLoBES_path = None # to use custom SNOwGLoBES detector/channel/smearing files, set SNOwGLoBES directory\n",
27 | "SNEWPY_models_base = model_path # directory containing SNEWPY models\n",
28 | "\n",
29 | "# Hack to ensure that the example file used below is downloaded. Will be fixed in v2.0.\n",
30 | "from snewpy.models.ccsn import Zha_2021\n",
31 | "_ = Zha_2021(progenitor_mass=17*u.solMass)"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "Next, we will set up some basic parameters for the supernova we want to simulate."
39 | ]
40 | },
41 | {
42 | "cell_type": "code",
43 | "execution_count": null,
44 | "metadata": {},
45 | "outputs": [],
46 | "source": [
47 | "# set distance in kpc\n",
48 | "distance = 10\n",
49 | "\n",
50 | "# set SNOwGLoBES detector to use\n",
51 | "detector = \"icecube\"\n",
52 | "\n",
53 | "# set SNEWPY model type and filename\n",
54 | "modeltype = 'Zha_2021'\n",
55 | "model = 's17'\n",
56 | "\n",
57 | "# set desired flavor transformation\n",
58 | "transformation = 'AdiabaticMSW_NMO'\n",
59 | "\n",
60 | "# Construct file system path of model file and name of output file\n",
61 | "# The output file will be stored in the same directory as the model file.\n",
62 | "modelfile = SNEWPY_models_base + \"/\" + modeltype + \"/\" + model + '.dat'\n",
63 | "outfile = modeltype+\"_\"+model+\"_\"+transformation\n",
64 | "\n",
65 | "# There are three ways to select a time range.\n",
66 | "# Option 1 - don't specify tstart and tend, then the whole model is integrated\n",
67 | "#tstart = None\n",
68 | "#tend = None\n",
69 | "\n",
70 | "# Option 2 - specify single tstart and tend, this makes 1 fluence file integrated over the window\n",
71 | "#tstart = 0.7 * u.s\n",
72 | "#tend = 0.8 * u.s\n",
73 | "\n",
74 | "# Option 3 = specify sequence of time intervals, one fluence file is made for each interval\n",
75 | "window_tstart = 0.742\n",
76 | "window_tend = 0.762\n",
77 | "window_bins = 60\n",
78 | "tstart = np.linspace(window_tstart, window_tend, window_bins, endpoint=False) * u.s\n",
79 | "tend = tstart + (window_tend - window_tstart) / window_bins * u.s\n",
80 | "tmid = (tstart + tend) * 0.5"
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "metadata": {},
86 | "source": [
87 | "Now that everything’s set up, let’s start using SNOwGLoBES! Be patient—these three steps together may take a few minutes."
88 | ]
89 | },
90 | {
91 | "cell_type": "code",
92 | "execution_count": null,
93 | "metadata": {},
94 | "outputs": [],
95 | "source": [
96 | "# snowglobes.generate_fluence integrates the model over the specified time window(s)\n",
97 | "# and generates input files for SNOwGLoBES. It returns the full file path of the output file.\n",
98 | "print(\"Preparing fluences ...\")\n",
99 | "tarredfile = snowglobes.generate_fluence(modelfile, modeltype, transformation, distance, outfile, tstart, tend)\n",
100 | "\n",
101 | "# Next, we run SNOwGLoBES. This will loop over all the fluence files in `tarredfile`.\n",
102 | "print(\"Running SNOwGLoBES ...\")\n",
103 | "snowglobes.simulate(SNOwGLoBES_path, tarredfile, detector_input=detector)\n",
104 | "\n",
105 | "# Finally, we collate SNOwGLoBES’ results into a dictionary\n",
106 | "print(\"Collating results ...\")\n",
107 | "tables = snowglobes.collate(SNOwGLoBES_path, tarredfile, skip_plots=True)"
108 | ]
109 | },
110 | {
111 | "cell_type": "markdown",
112 | "metadata": {},
113 | "source": [
114 | "Finally, since we chose option 3 above, and calculated the fluence in 60 time bins, we can now plot the event counts over time."
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "execution_count": null,
120 | "metadata": {},
121 | "outputs": [],
122 | "source": [
123 | "%matplotlib inline\n",
124 | "nevents = np.zeros(len(tmid))\n",
125 | "for i in range(len(tmid)):\n",
126 | " key = f\"Collated_{outfile}_{i}_{detector}_events_smeared_weighted.dat\"\n",
127 | " for j in range(1,len(tables[key]['header'].split())):\n",
128 | " nevents[i] += sum(tables[key]['data'][j])\n",
129 | "\n",
130 | "# nevents is per bin, convert to per ms\n",
131 | "factor = window_bins / (window_tend - window_tstart) / 1000\n",
132 | "\n",
133 | "plt.plot(tmid - 0.742 * u.s, nevents * factor)\n",
134 | "plt.xlabel(\"$t-t_{2c}$ [s]\")\n",
135 | "plt.ylabel(\"Counts [ms$^{-1}$]\")\n",
136 | "plt.show()\n",
137 | "# compare to Figure 5 of Zha et al. (2021)\n",
138 | "print(\"Total Events:\", sum(nevents))"
139 | ]
140 | }
141 | ],
142 | "metadata": {
143 | "interpreter": {
144 | "hash": "12a4e164d15418f42fe0584c567a58ace9669f8a11b564e22ebd17e6959ef919"
145 | },
146 | "kernelspec": {
147 | "display_name": "Python 3.9.5 64-bit ('snews': conda)",
148 | "name": "python3"
149 | },
150 | "language_info": {
151 | "codemirror_mode": {
152 | "name": "ipython",
153 | "version": 3
154 | },
155 | "file_extension": ".py",
156 | "mimetype": "text/x-python",
157 | "name": "python",
158 | "nbconvert_exporter": "python",
159 | "pygments_lexer": "ipython3",
160 | "version": "3.9.5"
161 | }
162 | },
163 | "nbformat": 4,
164 | "nbformat_minor": 4
165 | }
166 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Bollig_2016.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Bollig 2016 Models\n",
8 | "\n",
9 | "Data from Mirizzi et al., particular the models that go into Figure 17. Models (s11.2c and s27.0c) taken from the Garching Supernova archive (https://wwwmpa.mpa-garching.mpg.de/ccsnarchive/data/Bollig2016/) with permission for use in SNEWS2.0.\n",
10 | "\n",
11 | "Reference: Mirizzi et al. Rivista del Nuovo Cimento Vol 39 N. 1-2 (2016)\n",
12 | "- doi:10.1393/ncr/i2016-10120-8\n",
13 | "- arXiv:1508.00785"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import Bollig_2016\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `Bollig_2016` model. We can use the `param` property to view all physics parameters and their possible values:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "Bollig_2016.param"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "We’ll initialise both of these progenitors. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
59 | ]
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": null,
64 | "metadata": {},
65 | "outputs": [],
66 | "source": [
67 | "m11 = Bollig_2016(progenitor_mass=11.2*u.solMass)\n",
68 | "m27 = Bollig_2016(progenitor_mass=27*u.solMass)\n",
69 | "\n",
70 | "m11"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Bollig_2016` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": null,
83 | "metadata": {},
84 | "outputs": [],
85 | "source": [
86 | "fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharex=True, sharey=True, tight_layout=True)\n",
87 | "\n",
88 | "for i, model in enumerate([m11, m27]):\n",
89 | " ax = axes[i]\n",
90 | " for flavor in Flavor:\n",
91 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
92 | " label=flavor.to_tex(),\n",
93 | " color='C0' if flavor.is_electron else 'C1',\n",
94 | " ls='-' if flavor.is_neutrino else ':',\n",
95 | " lw=2)\n",
96 | " ax.set(xlim=(0.0, 0.35),\n",
97 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
98 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
99 | " ax.grid()\n",
100 | " ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
101 | "\n",
102 | "axes[0].set(ylabel=r'luminosity [foe s$^{-1}$]');"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "## Initial and Oscillated Spectra\n",
110 | "\n",
111 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
112 | "\n",
113 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
114 | ]
115 | },
116 | {
117 | "cell_type": "code",
118 | "execution_count": null,
119 | "metadata": {},
120 | "outputs": [],
121 | "source": [
122 | "# Adiabatic MSW effect. NMO is used by default.\n",
123 | "xform_nmo = AdiabaticMSW()\n",
124 | "\n",
125 | "# Energy array and time to compute spectra.\n",
126 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
127 | "E = np.linspace(0,100,201) * u.MeV\n",
128 | "t = 50*u.ms\n",
129 | "\n",
130 | "ispec = model.get_initial_spectra(t, E)\n",
131 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
132 | ]
133 | },
134 | {
135 | "cell_type": "code",
136 | "execution_count": null,
137 | "metadata": {},
138 | "outputs": [],
139 | "source": [
140 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
141 | "\n",
142 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
143 | " ax = axes[i]\n",
144 | " for flavor in Flavor:\n",
145 | " ax.plot(E, spec[flavor],\n",
146 | " label=flavor.to_tex(),\n",
147 | " color='C0' if flavor.is_electron else 'C1',\n",
148 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
149 | " alpha=0.7)\n",
150 | "\n",
151 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
152 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
153 | " ax.grid()\n",
154 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
155 | "\n",
156 | "ax = axes[0]\n",
157 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
158 | "\n",
159 | "fig.tight_layout();"
160 | ]
161 | }
162 | ],
163 | "metadata": {
164 | "kernelspec": {
165 | "display_name": "Python 3.9.5 ('snews')",
166 | "language": "python",
167 | "name": "python3"
168 | },
169 | "language_info": {
170 | "codemirror_mode": {
171 | "name": "ipython",
172 | "version": 3
173 | },
174 | "file_extension": ".py",
175 | "mimetype": "text/x-python",
176 | "name": "python",
177 | "nbconvert_exporter": "python",
178 | "pygments_lexer": "ipython3",
179 | "version": "3.9.5"
180 | },
181 | "vscode": {
182 | "interpreter": {
183 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
184 | }
185 | }
186 | },
187 | "nbformat": 4,
188 | "nbformat_minor": 2
189 | }
190 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Bugli_2021.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Bugli 2021 Models\n",
8 | "\n",
9 | "Data from M. Bugli et al., with permission for use in SNEWS2.0.\n",
10 | "\n",
11 | "Reference: M. Bugli, J. Guilet and M. Obergaulin, \"Three-dimensional core-collapse supernovae with complex magnetic structures: I. Explosion dynamics\", MNRAS 507 (2021) 1\n",
12 | "- https://doi.org/10.1093/mnras/stab2161\n",
13 | "- https://arxiv.org/abs/2105.00665"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "\n",
25 | "from snewpy.neutrino import Flavor\n",
26 | "from snewpy.models.ccsn import Bugli_2021\n",
27 | "\n",
28 | "mpl.rc('font', size=16)\n",
29 | "%matplotlib inline"
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "## Initialize Models\n",
37 | "\n",
38 | "To start, let’s see what progenitors are available for the `Bugli_2021` model. We can use the `param` property to view all physics parameters and their possible values:"
39 | ]
40 | },
41 | {
42 | "cell_type": "code",
43 | "execution_count": null,
44 | "metadata": {},
45 | "outputs": [],
46 | "source": [
47 | "Bugli_2021.param"
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "metadata": {},
53 | "source": [
54 | "We’ll initialise both of these progenitors. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": null,
60 | "metadata": {},
61 | "outputs": [],
62 | "source": [
63 | "mhydro = Bugli_2021(Bfield='hydro',direction='average') \n",
64 | "mL1 = Bugli_2021(Bfield='L1',direction='average',rotation=90)\n",
65 | "mL2 = Bugli_2021(Bfield='L2',direction='average',grav='A')\n",
66 | "\n",
67 | "mL1"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "metadata": {},
73 | "source": [
74 | "Finally, let’s plot the luminosity of different neutrino flavors for this model."
75 | ]
76 | },
77 | {
78 | "cell_type": "code",
79 | "execution_count": null,
80 | "metadata": {},
81 | "outputs": [],
82 | "source": [
83 | "fig, axes = plt.subplots(1, 3, figsize=(12, 5), sharex=True, sharey=True, tight_layout=True)\n",
84 | "\n",
85 | "for i, flavor in enumerate(Flavor):\n",
86 | " if i>2:\n",
87 | " continue\n",
88 | " ax = axes[i]\n",
89 | " for model in [mhydro,mL1, mL2]:\n",
90 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
91 | " label = model.metadata['Bfield'],\n",
92 | " lw=2)\n",
93 | " ax.set(xlim=(0.0, 0.5),\n",
94 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
95 | " title= flavor.to_tex())\n",
96 | " ax.grid()\n",
97 | " ax.legend(loc='upper right', ncol=1, fontsize=18)\n",
98 | "\n",
99 | "axes[0].set(ylabel=r'luminosity [foe s$^{-1}$]');"
100 | ]
101 | }
102 | ],
103 | "metadata": {
104 | "kernelspec": {
105 | "display_name": "Python 3 (ipykernel)",
106 | "language": "python",
107 | "name": "python3"
108 | },
109 | "language_info": {
110 | "codemirror_mode": {
111 | "name": "ipython",
112 | "version": 3
113 | },
114 | "file_extension": ".py",
115 | "mimetype": "text/x-python",
116 | "name": "python",
117 | "nbconvert_exporter": "python",
118 | "pygments_lexer": "ipython3",
119 | "version": "3.12.3"
120 | },
121 | "vscode": {
122 | "interpreter": {
123 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
124 | }
125 | }
126 | },
127 | "nbformat": 4,
128 | "nbformat_minor": 4
129 | }
130 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Fischer_2020.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Fischer 2020 Model\n",
8 | "\n",
9 | "CCSN neutrino model from Tobias Fischer\n",
10 | "\n",
11 | "The citation is: *Neutrino signal from proto-neutron star evolution: Effects of opacities from charged-current-neutrino interactions and inverse neutron decay*, Fischer, Tobias ; Guo, Gang ; Dzhioev, Alan A. ; Martínez-Pinedo, Gabriel ; Wu, Meng-Ru ; Lohs, Andreas ; Qian, Yong-Zhong, Physical Review C, Volume 101, Issue 2, article id.025804 (https://ui.adsabs.harvard.edu/link_gateway/2020PhRvC.101b5804F/doi:10.1103/PhysRevC.101.025804), [arXiv:1804.10890](https://arxiv.org/abs/1804.10890)."
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "import matplotlib as mpl\n",
21 | "import matplotlib.pyplot as plt\n",
22 | "import numpy as np\n",
23 | "\n",
24 | "from astropy import units as u \n",
25 | "from snewpy.neutrino import Flavor\n",
26 | "from snewpy.models.ccsn import Fischer_2020\n",
27 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
28 | "\n",
29 | "mpl.rc('font', size=16)\n",
30 | "%matplotlib inline"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "## Initialize Models\n",
38 | "\n",
39 | "For the `Fischer_2020` model, there’s just a single progenitor available; so let’s initialize it. If this is the first time you’re using this progenitor, snewpy will automatically download the required data files for you."
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": null,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "F2020 = Fischer_2020()\n",
49 | "\n",
50 | "F2020"
51 | ]
52 | },
53 | {
54 | "cell_type": "markdown",
55 | "metadata": {},
56 | "source": [
57 | "Plot the luminosity of different neutrino flavors for this model. "
58 | ]
59 | },
60 | {
61 | "cell_type": "code",
62 | "execution_count": null,
63 | "metadata": {},
64 | "outputs": [],
65 | "source": [
66 | "fig, ax = plt.subplots(1, figsize=(8, 6), tight_layout=False)\n",
67 | "\n",
68 | "for flavor in Flavor:\n",
69 | " if flavor.is_electron == True:\n",
70 | " color='C0'\n",
71 | " else:\n",
72 | " color='C1'\n",
73 | " ls='-' if flavor.is_neutrino else ':'\n",
74 | " lw = 2\n",
75 | " \n",
76 | " ax.plot(F2020.time, F2020.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
77 | " label=flavor.to_tex(), color=color, ls=ls, lw=lw)\n",
78 | " \n",
79 | "ax.set(xlim=(-0.1, 1), xlabel=r'$t-t_{\\rm bounce}$ [s]')\n",
80 | "ax.grid()\n",
81 | "ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
82 | "ax.set(ylabel=r'luminosity [foe s$^{-1}$]');"
83 | ]
84 | },
85 | {
86 | "cell_type": "markdown",
87 | "metadata": {},
88 | "source": [
89 | "## Initial and Oscillated Spectra\n",
90 | "\n",
91 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
92 | "\n",
93 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "# Adiabatic MSW effect. NMO is used by default.\n",
103 | "xform_nmo = AdiabaticMSW()\n",
104 | "\n",
105 | "# Energy array and time to compute spectra.\n",
106 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
107 | "E = np.linspace(0,100,201) * u.MeV\n",
108 | "t = 50*u.ms\n",
109 | "\n",
110 | "ispec = F2020.get_initial_spectra(t, E)\n",
111 | "ospec_nmo = F2020.get_transformed_spectra(t, E, xform_nmo)"
112 | ]
113 | },
114 | {
115 | "cell_type": "code",
116 | "execution_count": null,
117 | "metadata": {},
118 | "outputs": [],
119 | "source": [
120 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
121 | "\n",
122 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
123 | " ax = axes[i]\n",
124 | " for flavor in Flavor:\n",
125 | " if flavor.is_electron == True:\n",
126 | " color='C0'\n",
127 | " else:\n",
128 | " color='C1'\n",
129 | " ax.plot(E, spec[flavor],\n",
130 | " label=flavor.to_tex(),\n",
131 | " color=color,\n",
132 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
133 | " alpha=0.7)\n",
134 | "\n",
135 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
136 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
137 | " ax.grid()\n",
138 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
139 | "\n",
140 | "ax = axes[0]\n",
141 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
142 | "\n",
143 | "fig.tight_layout();"
144 | ]
145 | }
146 | ],
147 | "metadata": {
148 | "kernelspec": {
149 | "display_name": "Python 3 (ipykernel)",
150 | "language": "python",
151 | "name": "python3"
152 | },
153 | "language_info": {
154 | "codemirror_mode": {
155 | "name": "ipython",
156 | "version": 3
157 | },
158 | "file_extension": ".py",
159 | "mimetype": "text/x-python",
160 | "name": "python",
161 | "nbconvert_exporter": "python",
162 | "pygments_lexer": "ipython3",
163 | "version": "3.12.3"
164 | },
165 | "vscode": {
166 | "interpreter": {
167 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
168 | }
169 | }
170 | },
171 | "nbformat": 4,
172 | "nbformat_minor": 4
173 | }
174 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Kuroda_2020.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Kuroda 2020 Models\n",
8 | "\n",
9 | "Models from Takami Kuroda. The paper describing the simulations is \"Impact of magnetic field on neutrino-matter interactions in core-collapse supernova\" arXiv:2009.07733.\n",
10 | "Included with SNEWPY with permission of the authors."
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": null,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "import matplotlib as mpl\n",
20 | "import matplotlib.pyplot as plt\n",
21 | "import numpy as np\n",
22 | "\n",
23 | "from astropy import units as u \n",
24 | "\n",
25 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
26 | "from snewpy.models.ccsn import Kuroda_2020\n",
27 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
28 | "\n",
29 | "mpl.rc('font', size=16)\n",
30 | "%matplotlib inline"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "## Initialize Models\n",
38 | "\n",
39 | "To start, let’s see what progenitors are available for the `Kuroda_2020` model. We can use the `param` property to view all physics parameters and their possible values:"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": null,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "Kuroda_2020.param"
49 | ]
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "metadata": {},
54 | "source": [
55 | "Quite a lot of choice there! However, for this model, not all combinations of these parameters are valid. We can use the `get_param_combinations` function to get a list of all valid combinations or to filter it:"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "# This will print a tuple of all combinations:\n",
65 | "Kuroda_2020.get_param_combinations()"
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "We’ll pick one of these progenitors and initialise it. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
73 | ]
74 | },
75 | {
76 | "cell_type": "code",
77 | "execution_count": null,
78 | "metadata": {},
79 | "outputs": [],
80 | "source": [
81 | "model = Kuroda_2020(**Kuroda_2020.get_param_combinations()[1])\n",
82 | "# This is equivalent to:\n",
83 | "# model = Kuroda_2020(rotational_velocity=1*u.rad/u.s, magnetic_field_exponent=12)\n",
84 | "model"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "metadata": {},
90 | "source": [
91 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Kuroda_2020` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
92 | ]
93 | },
94 | {
95 | "cell_type": "code",
96 | "execution_count": null,
97 | "metadata": {},
98 | "outputs": [],
99 | "source": [
100 | "fig, ax = plt.subplots(1, figsize=(8,6), tight_layout=False)\n",
101 | "\n",
102 | "for flavor in Flavor:\n",
103 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
104 | " label=flavor.to_tex(),\n",
105 | " color = 'C0' if flavor.is_electron else 'C1',\n",
106 | " ls = '-' if flavor.is_neutrino else ':',\n",
107 | " lw = 2 )\n",
108 | "\n",
109 | "ax.set(xlim=(0.0, 0.35),\n",
110 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
111 | " ylabel=r'luminosity [foe s$^{-1}$]')\n",
112 | "ax.grid()\n",
113 | "ax.legend(loc='upper right', ncol=2, fontsize=18);"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {},
119 | "source": [
120 | "## Initial and Oscillated Spectra\n",
121 | "\n",
122 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
123 | "\n",
124 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
125 | ]
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": null,
130 | "metadata": {},
131 | "outputs": [],
132 | "source": [
133 | "# Adiabatic MSW effect. NMO is used by default.\n",
134 | "xform_nmo = AdiabaticMSW()\n",
135 | "\n",
136 | "# Energy array and time to compute spectra.\n",
137 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
138 | "E = np.linspace(0,100,201) * u.MeV\n",
139 | "t = 50*u.ms\n",
140 | "\n",
141 | "ispec = model.get_initial_spectra(t, E)\n",
142 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
143 | ]
144 | },
145 | {
146 | "cell_type": "code",
147 | "execution_count": null,
148 | "metadata": {},
149 | "outputs": [],
150 | "source": [
151 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
152 | "\n",
153 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
154 | " ax = axes[i]\n",
155 | " for flavor in Flavor:\n",
156 | " ax.plot(E, spec[flavor],\n",
157 | " label=flavor.to_tex(),\n",
158 | " color='C0' if flavor.is_electron else 'C1',\n",
159 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
160 | " alpha=0.7)\n",
161 | "\n",
162 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
163 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
164 | " ax.grid()\n",
165 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
166 | "\n",
167 | "ax = axes[0]\n",
168 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
169 | "\n",
170 | "fig.tight_layout();"
171 | ]
172 | }
173 | ],
174 | "metadata": {
175 | "kernelspec": {
176 | "display_name": "Python 3.9.5 ('snews')",
177 | "language": "python",
178 | "name": "python3"
179 | },
180 | "language_info": {
181 | "codemirror_mode": {
182 | "name": "ipython",
183 | "version": 3
184 | },
185 | "file_extension": ".py",
186 | "mimetype": "text/x-python",
187 | "name": "python",
188 | "nbconvert_exporter": "python",
189 | "pygments_lexer": "ipython3",
190 | "version": "3.9.5"
191 | },
192 | "vscode": {
193 | "interpreter": {
194 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
195 | }
196 | }
197 | },
198 | "nbformat": 4,
199 | "nbformat_minor": 2
200 | }
201 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Mori_2023.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "04bf2ca6",
6 | "metadata": {},
7 | "source": [
8 | "# Mori 2023 2D Models with Axionlike Production\n",
9 | "\n",
10 | "Neutrino spectra from a set of 2D simulations with axionlike particle production. The models are described in the paper [Multi-messenger signals of heavy axionlike particles in core-collapse supernovae: two-dimensional simulations](https://arxiv.org/abs/2304.11360) by K. Mori, T. Takiwaki, K. Kotake and S. Horiuchi, Phys. Rev. D 108:063027, 2023.\n",
11 | "\n",
12 | "The following models are supported:\n",
13 | "\n",
14 | "| Model | Axion mass [MeV] | Coupling g10 | tpb,2000 [ms] | Ediag [1e51 erg] | MPNS [Msun] |\n",
15 | "| ----- | ---------------- | ------------ | ------------- | ---------------- | ---------- |\n",
16 | "| Standard | − | 0 | 390 | 0.40 | 1.78 |\n",
17 | "| (100, 2) | 100 | 2 | 385 | 0.37 | 1.77 |\n",
18 | "| (100, 4) | 100 | 4 | 362 | 0.34 | 1.76 |\n",
19 | "| (100, 10) | 100 | 10 | 395 | 0.36 | 1.77 |\n",
20 | "| (100, 12) | 100 | 12 | 357 | 0.43 | 1.77 |\n",
21 | "| (100, 14) | 100 | 14 | 360 | 0.44 | 1.77 |\n",
22 | "| (100, 16) | 100 | 16 | 367 | 0.51 | 1.77 |\n",
23 | "| (100, 20) | 100 | 20 | 330 | 1.10 | 1.74 |\n",
24 | "| (200, 2) | 200 | 2 | 374 | 0.45 | 1.77 |\n",
25 | "| (200, 4) | 200 | 4 | 376 | 0.45 | 1.76 |\n",
26 | "| (200, 6) | 200 | 6 | 333 | 0.54 | 1.75 |\n",
27 | "| (200, 8) | 200 | 8 | 323 | 0.94 | 1.74 |\n",
28 | "| (200, 10) | 200 | 10 | 319 | 1.61 | 1.73 |\n",
29 | "| (200, 20) | 200 | 20 | 248 | 3.87 | 1.62 |"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": null,
35 | "id": "29ceae1a",
36 | "metadata": {},
37 | "outputs": [],
38 | "source": [
39 | "from snewpy.neutrino import Flavor\n",
40 | "from snewpy.models.ccsn import Mori_2023\n",
41 | "\n",
42 | "from astropy import units as u\n",
43 | "from glob import glob\n",
44 | "\n",
45 | "from scipy.interpolate import PchipInterpolator\n",
46 | "\n",
47 | "import numpy as np\n",
48 | "import matplotlib as mpl\n",
49 | "import matplotlib.pyplot as plt"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "id": "1b7566ed",
56 | "metadata": {},
57 | "outputs": [],
58 | "source": [
59 | "mpl.rc('font', size=16)"
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "id": "66d13225",
65 | "metadata": {},
66 | "source": [
67 | "## Initialize the 2D models\n",
68 | "\n",
69 | "Use the `param` property of the model class to see the available parameters. Models are initialized using the `axion_mass` and `axion_coupling` parameters."
70 | ]
71 | },
72 | {
73 | "cell_type": "code",
74 | "execution_count": null,
75 | "id": "f62e690f",
76 | "metadata": {},
77 | "outputs": [],
78 | "source": [
79 | "Mori_2023.param"
80 | ]
81 | },
82 | {
83 | "cell_type": "markdown",
84 | "id": "55479166",
85 | "metadata": {},
86 | "source": [
87 | "The model with `axion_mass=0` and `axion_coupling=0` is a standard simulation with no ALP production."
88 | ]
89 | },
90 | {
91 | "cell_type": "code",
92 | "execution_count": null,
93 | "id": "649f5ae4",
94 | "metadata": {},
95 | "outputs": [],
96 | "source": [
97 | "model_std = Mori_2023(axion_mass=0, axion_coupling=0)\n",
98 | "model_std.metadata"
99 | ]
100 | },
101 | {
102 | "cell_type": "code",
103 | "execution_count": null,
104 | "id": "6f15de7a",
105 | "metadata": {},
106 | "outputs": [],
107 | "source": [
108 | "# Initialize a handful of axion models.\n",
109 | "models = {}\n",
110 | "for (am, ac) in ((100*u.MeV, 2e-10/u.GeV), (200*u.MeV,2e-10/u.GeV), (100*u.MeV,10e-10/u.GeV), (100*u.MeV,20e-10/u.GeV), (200*u.MeV,10e-10/u.GeV), (200*u.MeV,20e-10/u.GeV)):\n",
111 | " models[(am,ac)] = Mori_2023(axion_mass=am, axion_coupling=ac)\n",
112 | "\n",
113 | "models"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "id": "3cab5a9c",
119 | "metadata": {},
120 | "source": [
121 | "## Plot Model Luminosities\n",
122 | "\n",
123 | "Compare axion model luminosity to the standard 2D simulation.\n",
124 | "\n",
125 | "Higher mass models with stronger coupling constants should produce a decrease in neutrino luminosity at all flavors relative to the reference simulation."
126 | ]
127 | },
128 | {
129 | "cell_type": "code",
130 | "execution_count": null,
131 | "id": "8a398333",
132 | "metadata": {},
133 | "outputs": [],
134 | "source": [
135 | "for (m,c), model in models.items():\n",
136 | " \n",
137 | " fig, axes = plt.subplots(2, 4, figsize=(28, 8), sharex=True,\n",
138 | " gridspec_kw={'height_ratios':[3.2,1], 'hspace':0})\n",
139 | " \n",
140 | " Lmin, Lmax = 1e99, -1e99\n",
141 | " dLmin, dLmax = 1e99, -1e99\n",
142 | " \n",
143 | " for j, (flavor) in enumerate([Flavor.NU_E, Flavor.NU_E_BAR, Flavor.NU_X, Flavor.NU_X_BAR]):\n",
144 | " ax = axes[0][j]\n",
145 | " \n",
146 | " ax.plot(model_std.time, model_std.luminosity[flavor]/1e51, 'k', label=r'$20M_\\odot$ reference') # Report luminosity in [foe/s]\n",
147 | " Lmin = np.minimum(Lmin, np.min(model_std.luminosity[flavor].to_value('1e51 erg/s')))\n",
148 | " Lmax = np.maximum(Lmax, np.max(model_std.luminosity[flavor].to_value('1e51 erg/s')))\n",
149 | " \n",
150 | " modlabel = rf\"{flavor.to_tex()}: $m_a=${m.to_string(format='latex_inline')}\\n $g_{{a\\gamma}}=${c.to_string(format='latex_inline')}\"\n",
151 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
152 | " label=modlabel,\n",
153 | " color='C0' if flavor.is_electron else 'C1',\n",
154 | " ls='-' if flavor.is_neutrino else ':',\n",
155 | " lw=2)\n",
156 | " if j==0:\n",
157 | " ax.set(ylabel=r'luminosity [$10^{51}$ erg s$^{-1}$]')\n",
158 | " \n",
159 | " ax.legend(fontsize=12)\n",
160 | " ax.set(xlim=(model_std.time[0].to_value('s'), model_std.time[-1].to_value('s')))\n",
161 | " \n",
162 | " ax = axes[1][j]\n",
163 | " tmin = np.maximum(model.time[0], model_std.time[0]).to_value('s')\n",
164 | " tmax = np.minimum(model.time[-1], model_std.time[-1]).to_value('s')\n",
165 | " times = np.arange(tmin, tmax, 0.001)*u.s\n",
166 | "\n",
167 | " Lstd = PchipInterpolator(model_std.time, model_std.luminosity[flavor].to_value('1e51 erg/s'))\n",
168 | " Lstd_t = Lstd(times)\n",
169 | " select = Lstd_t != 0\n",
170 | " \n",
171 | " Lmod = PchipInterpolator(model.time, model.luminosity[flavor].to_value('1e51 erg/s'))\n",
172 | " Lmod_t = Lmod(times)\n",
173 | " dL = (Lmod_t[select] - Lstd_t[select]) / Lstd_t[select]\n",
174 | " \n",
175 | " dLmin = np.minimum(dLmin, np.min(dL))\n",
176 | " dLmax = np.maximum(dLmax, np.max(dL))\n",
177 | "\n",
178 | " ax.plot(times[select], dL)\n",
179 | " if j==0:\n",
180 | " ax.set(xlabel='time [s]',\n",
181 | " ylabel=r'$\\Delta L_\\nu/L_\\nu$')\n",
182 | " \n",
183 | " for j in range(4):\n",
184 | " axes[0][j].set(ylim=(Lmin, 1.1*Lmax))\n",
185 | " axes[1][j].set(ylim=(dLmin, dLmax))\n",
186 | " \n",
187 | " fig.suptitle(rf\"Axionlike model: $m_a=${m.to_string(format='latex_inline')}, $g_{{a\\gamma}}=${c.to_string(format='latex_inline')}\")"
188 | ]
189 | }
190 | ],
191 | "metadata": {
192 | "kernelspec": {
193 | "display_name": "Python 3 (ipykernel)",
194 | "language": "python",
195 | "name": "python3"
196 | },
197 | "language_info": {
198 | "codemirror_mode": {
199 | "name": "ipython",
200 | "version": 3
201 | },
202 | "file_extension": ".py",
203 | "mimetype": "text/x-python",
204 | "name": "python",
205 | "nbconvert_exporter": "python",
206 | "pygments_lexer": "ipython3",
207 | "version": "3.12.3"
208 | }
209 | },
210 | "nbformat": 4,
211 | "nbformat_minor": 5
212 | }
213 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/OConnor_2013.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# O'Connor 2013 Models\n",
8 | "\n",
9 | "Data from O'Connor & Ott 2013, 32 progenitors (Woosley and Heger 2007) and 2 EOS (LS220 and HShen) for 500 ms post bounce in spherical symmetry (no explosions)\n",
10 | " \n",
11 | "Reference: O'Connor and Ott ApJ 762 126 2013\n",
12 | "- [doi:10.1088/0004-637X/762/2/126](https://doi.org/10.1088/0004-637X/762/2/126)\n",
13 | "- [arXiv:1207.1100](https://arxiv.org/abs/1207.1100)"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import OConnor_2013\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `OConnor_2013` model. We can use the `param` property to view all physics parameters and their possible values:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "OConnor_2013.param"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "Quite a lot of choice there! Let’s initialise all of these progenitors and compare their $\\nu_e$ luminosities. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
59 | ]
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": null,
64 | "metadata": {},
65 | "outputs": [],
66 | "source": [
67 | "models = {}\n",
68 | "for mass in OConnor_2013.param['progenitor_mass']:\n",
69 | " models[int(mass.value)] = OConnor_2013(progenitor_mass=mass, eos='LS220')\n",
70 | "\n",
71 | "for model in models.values():\n",
72 | " plt.plot(model.time, model.luminosity[Flavor.NU_E]/1e51, 'C0', lw=1)\n",
73 | "\n",
74 | "plt.xlabel(r'$t$ [s]')\n",
75 | "plt.ylabel(r'luminosity [foe s$^{-1}$]');"
76 | ]
77 | },
78 | {
79 | "cell_type": "markdown",
80 | "metadata": {},
81 | "source": [
82 | "Finally, let’s plot the luminosity of different neutrino flavors for two of these progenitors. (Note that the `OConnor_2013` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both flavors have the same luminosity.)"
83 | ]
84 | },
85 | {
86 | "cell_type": "code",
87 | "execution_count": null,
88 | "metadata": {},
89 | "outputs": [],
90 | "source": [
91 | "fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharex=True, sharey=True, tight_layout=True)\n",
92 | "\n",
93 | "for i, model in enumerate([models[12], models[20]]):\n",
94 | " ax = axes[i]\n",
95 | " for flavor in Flavor:\n",
96 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
97 | " label=flavor.to_tex(),\n",
98 | " color='C0' if flavor.is_electron else 'C1',\n",
99 | " ls='-' if flavor.is_neutrino else ':',\n",
100 | " lw=2)\n",
101 | " ax.set(xlim=(-0.05, 0.51),\n",
102 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
103 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
104 | " ax.grid()\n",
105 | " ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
106 | "\n",
107 | "axes[0].set(ylabel=r'luminosity [foe s$^{-1}$]');"
108 | ]
109 | },
110 | {
111 | "cell_type": "markdown",
112 | "metadata": {},
113 | "source": [
114 | "## Initial and Oscillated Spectra\n",
115 | "\n",
116 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
117 | "\n",
118 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
119 | ]
120 | },
121 | {
122 | "cell_type": "code",
123 | "execution_count": null,
124 | "metadata": {},
125 | "outputs": [],
126 | "source": [
127 | "# Adiabatic MSW effect. NMO is used by default.\n",
128 | "xform_nmo = AdiabaticMSW()\n",
129 | "\n",
130 | "# Energy array and time to compute spectra.\n",
131 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
132 | "E = np.linspace(0,100,201) * u.MeV\n",
133 | "t = 400*u.ms\n",
134 | "\n",
135 | "ispec = model.get_initial_spectra(t, E)\n",
136 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
137 | ]
138 | },
139 | {
140 | "cell_type": "code",
141 | "execution_count": null,
142 | "metadata": {},
143 | "outputs": [],
144 | "source": [
145 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
146 | "\n",
147 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
148 | " ax = axes[i]\n",
149 | " for flavor in Flavor:\n",
150 | " ax.plot(E, spec[flavor],\n",
151 | " label=flavor.to_tex(),\n",
152 | " color='C0' if flavor.is_electron else 'C1',\n",
153 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
154 | " alpha=0.7)\n",
155 | "\n",
156 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
157 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
158 | " ax.grid()\n",
159 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
160 | "\n",
161 | "ax = axes[0]\n",
162 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
163 | "\n",
164 | "fig.tight_layout();"
165 | ]
166 | }
167 | ],
168 | "metadata": {
169 | "kernelspec": {
170 | "display_name": "Python 3 (ipykernel)",
171 | "language": "python",
172 | "name": "python3"
173 | },
174 | "language_info": {
175 | "codemirror_mode": {
176 | "name": "ipython",
177 | "version": 3
178 | },
179 | "file_extension": ".py",
180 | "mimetype": "text/x-python",
181 | "name": "python",
182 | "nbconvert_exporter": "python",
183 | "pygments_lexer": "ipython3",
184 | "version": "3.7.3"
185 | },
186 | "vscode": {
187 | "interpreter": {
188 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
189 | }
190 | }
191 | },
192 | "nbformat": 4,
193 | "nbformat_minor": 2
194 | }
195 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/OConnor_2015.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# O'Connor 2015 Models\n",
8 | "\n",
9 | "Data from O'Connor 2015, black hole forming simulations of a 40 solar mass progenitor from Woosley and Heger 2007 and the LS220 EOS.\n",
10 | " \n",
11 | "Reference: O'Connor ApJS 219 24 2015\n",
12 | "- doi:10.1088/0067-0049/219/2/24\n",
13 | "- arXiv:1411.7058"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import OConnor_2015\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `OConnor_2015` model. We can use the `param` property to view all physics parameters and their possible values:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "OConnor_2015.param"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "We’ll initialise this progenitor. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
59 | ]
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": null,
64 | "metadata": {},
65 | "outputs": [],
66 | "source": [
67 | "model = OConnor_2015(progenitor_mass=40*u.solMass)\n",
68 | "\n",
69 | "model"
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `OConnor_2015` simulation didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": null,
82 | "metadata": {},
83 | "outputs": [],
84 | "source": [
85 | "fig, ax = plt.subplots(1, figsize=(8, 6), tight_layout=False)\n",
86 | "\n",
87 | "for flavor in Flavor:\n",
88 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
89 | " label=flavor.to_tex(),\n",
90 | " color='C0' if flavor.is_electron else 'C1',\n",
91 | " ls='-' if flavor.is_neutrino else ':',\n",
92 | " lw=2)\n",
93 | "ax.set(xlim=(0, 0.55),\n",
94 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
95 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
96 | "ax.grid()\n",
97 | "ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
98 | "ax.set(ylabel=r'luminosity [foe s$^{-1}$]');\n"
99 | ]
100 | },
101 | {
102 | "cell_type": "markdown",
103 | "metadata": {},
104 | "source": [
105 | "## Initial and Oscillated Spectra\n",
106 | "\n",
107 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
108 | "\n",
109 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": null,
115 | "metadata": {},
116 | "outputs": [],
117 | "source": [
118 | "# Adiabatic MSW effect. NMO is used by default.\n",
119 | "xform_nmo = AdiabaticMSW()\n",
120 | "\n",
121 | "# Energy array and time to compute spectra.\n",
122 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
123 | "E = np.linspace(0,100,201) * u.MeV\n",
124 | "t = 400*u.ms\n",
125 | "\n",
126 | "ispec = model.get_initial_spectra(t, E)\n",
127 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": null,
133 | "metadata": {},
134 | "outputs": [],
135 | "source": [
136 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
137 | "\n",
138 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
139 | " ax = axes[i]\n",
140 | " for flavor in Flavor:\n",
141 | " ax.plot(E, spec[flavor],\n",
142 | " label=flavor.to_tex(),\n",
143 | " color='C0' if flavor.is_electron else 'C1',\n",
144 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
145 | " alpha=0.7)\n",
146 | "\n",
147 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
148 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
149 | " ax.grid()\n",
150 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
151 | "\n",
152 | "ax = axes[0]\n",
153 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
154 | "\n",
155 | "fig.tight_layout();"
156 | ]
157 | }
158 | ],
159 | "metadata": {
160 | "kernelspec": {
161 | "display_name": "Python 3.9.5 ('snews')",
162 | "language": "python",
163 | "name": "python3"
164 | },
165 | "language_info": {
166 | "codemirror_mode": {
167 | "name": "ipython",
168 | "version": 3
169 | },
170 | "file_extension": ".py",
171 | "mimetype": "text/x-python",
172 | "name": "python",
173 | "nbconvert_exporter": "python",
174 | "pygments_lexer": "ipython3",
175 | "version": "3.9.5"
176 | },
177 | "vscode": {
178 | "interpreter": {
179 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
180 | }
181 | }
182 | },
183 | "nbformat": 4,
184 | "nbformat_minor": 2
185 | }
186 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Sukhbold_2015.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Sukhbold 2015 Models\n",
8 | "\n",
9 | "CSN neutrino models from the MPA Garching CCSN archive based on the paper by Sukhbold et al., 2015. The archive is available on\n",
10 | "[their website](https://wwwmpa.mpa-garching.mpg.de/ccsnarchive/data/SEWBJ_2015/index.html), but the data are private and available only upon request.\n",
11 | "Note these are the results using the PROMETHEUS-VERTEX code https://ui.adsabs.harvard.edu/abs/2002A%26A...396..361R/abstract.\n",
12 | "The four models are also described in Appendix C of this paper https://arxiv.org/abs/2010.04728\n",
13 | "\n",
14 | "The citation is: *Core-Collapse Supernovae from 9 to 120 Solar Masses Based on Neutrino-powered Explosions*, Tuguldur Sukhbold, T. Ertl, S. E. Woosley, Justin M. Brown, H.-T. Janka, [Astrophys. J. 821 (2016)\n",
15 | "38](http://dx.doi.org/10.3847/0004-637X/821/1/38), [arXiv:1510.04643](http://arxiv.org/abs/1510.04643)."
16 | ]
17 | },
18 | {
19 | "cell_type": "code",
20 | "execution_count": null,
21 | "metadata": {},
22 | "outputs": [],
23 | "source": [
24 | "import matplotlib as mpl\n",
25 | "import matplotlib.pyplot as plt\n",
26 | "import numpy as np\n",
27 | "\n",
28 | "from astropy import units as u \n",
29 | "\n",
30 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
31 | "from snewpy.models.ccsn import Sukhbold_2015\n",
32 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
33 | "\n",
34 | "mpl.rc('font', size=16)\n",
35 | "%matplotlib inline"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "## Initialize Models\n",
43 | "\n",
44 | "To start, let’s see what progenitors are available for the `Sukhbold_2015` model. We can use the `param` property to view all physics parameters and their possible values:"
45 | ]
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": null,
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "Sukhbold_2015.param"
54 | ]
55 | },
56 | {
57 | "cell_type": "markdown",
58 | "metadata": {},
59 | "source": [
60 | "We’ll initialise two of these progenitors. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": null,
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "mls220 = Sukhbold_2015(progenitor_mass=27*u.solMass, eos='LS220')\n",
70 | "msfho = Sukhbold_2015(progenitor_mass=27*u.solMass, eos='SFHo')\n",
71 | "\n",
72 | "mls220"
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {},
78 | "source": [
79 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Sukhbold_2015` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
80 | ]
81 | },
82 | {
83 | "cell_type": "code",
84 | "execution_count": null,
85 | "metadata": {},
86 | "outputs": [],
87 | "source": [
88 | "\n",
89 | "fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharex=True, sharey=True, tight_layout=True)\n",
90 | "\n",
91 | "for i, model in enumerate([mls220, msfho]):\n",
92 | " ax = axes[i]\n",
93 | " for flavor in Flavor:\n",
94 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
95 | " label=flavor.to_tex(),\n",
96 | " color='C0' if flavor.is_electron else 'C1',\n",
97 | " ls='-' if flavor.is_neutrino else ':',\n",
98 | " lw=2)\n",
99 | " ax.set(xlim=(-0.05, 0.65),\n",
100 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
101 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
102 | " ax.grid()\n",
103 | " ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
104 | "\n",
105 | "axes[0].set(ylabel=r'luminosity [foe s$^{-1}$]');"
106 | ]
107 | },
108 | {
109 | "cell_type": "markdown",
110 | "metadata": {},
111 | "source": [
112 | "## Initial and Oscillated Spectra\n",
113 | "\n",
114 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
115 | "\n",
116 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": null,
122 | "metadata": {},
123 | "outputs": [],
124 | "source": [
125 | "# Adiabatic MSW effect. NMO is used by default.\n",
126 | "xform_nmo = AdiabaticMSW()\n",
127 | "\n",
128 | "# Energy array and time to compute spectra.\n",
129 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
130 | "E = np.linspace(0,100,201) * u.MeV\n",
131 | "t = 50*u.ms\n",
132 | "\n",
133 | "ispec = model.get_initial_spectra(t, E)\n",
134 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
135 | ]
136 | },
137 | {
138 | "cell_type": "code",
139 | "execution_count": null,
140 | "metadata": {},
141 | "outputs": [],
142 | "source": [
143 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
144 | "\n",
145 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
146 | " ax = axes[i]\n",
147 | " for flavor in Flavor:\n",
148 | " ax.plot(E, spec[flavor],\n",
149 | " label=flavor.to_tex(),\n",
150 | " color='C0' if flavor.is_electron else 'C1',\n",
151 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
152 | " alpha=0.7)\n",
153 | "\n",
154 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
155 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
156 | " ax.grid()\n",
157 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
158 | "\n",
159 | "ax = axes[0]\n",
160 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
161 | "\n",
162 | "fig.tight_layout();"
163 | ]
164 | }
165 | ],
166 | "metadata": {
167 | "kernelspec": {
168 | "display_name": "Python 3.9.5 ('snews')",
169 | "language": "python",
170 | "name": "python3"
171 | },
172 | "language_info": {
173 | "codemirror_mode": {
174 | "name": "ipython",
175 | "version": 3
176 | },
177 | "file_extension": ".py",
178 | "mimetype": "text/x-python",
179 | "name": "python",
180 | "nbconvert_exporter": "python",
181 | "pygments_lexer": "ipython3",
182 | "version": "3.9.5"
183 | },
184 | "vscode": {
185 | "interpreter": {
186 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
187 | }
188 | }
189 | },
190 | "nbformat": 4,
191 | "nbformat_minor": 2
192 | }
193 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Tamborra_2014.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Tamborra 2014 Models\n",
8 | "\n",
9 | "Data from Tamborra et al., [Phys. Rev. D90:045032, 2014](http://wwwmpa.mpa-garching.mpg.de/ccsnarchive/data/Tamborra2014/). Models (s20.0c and s27.0c) taken from the Garching Supernova archive (https://wwwmpa.mpa-garching.mpg.de/ccsnarchive/data/Tamborra2014/) with permission for use in SNEWS2.0.\n",
10 | "\n",
11 | "Reference: I. Tamborra et al., *Neutrino emission characteristics and detection opportunities based on three-dimensional supernova simulations*, Phys. Rev D90:045032, 2014.\n",
12 | "- [doi:10.1103/PhysRevD.90.045032](https://journals.aps.org/prd/abstract/10.1103/PhysRevD.90.045032)\n",
13 | "- [arXiv:1406.0006](https://arxiv.org/abs/1406.0006)"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import Tamborra_2014\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `Tamborra_2014` model. We can use the `param` property to view all physics parameters and their possible values. However, for this model, not all combinations of these parameters are valid, so we use the `get_param_combinations` function to get a list of all valid combinations:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "# Tamborra_2014.param\n",
52 | "Tamborra_2014.get_param_combinations()"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "We’ll initialise two of these progenitors. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
60 | ]
61 | },
62 | {
63 | "cell_type": "code",
64 | "execution_count": null,
65 | "metadata": {},
66 | "outputs": [],
67 | "source": [
68 | "m20 = Tamborra_2014(progenitor_mass=20*u.solMass, direction=1)\n",
69 | "m27 = Tamborra_2014(progenitor_mass=27*u.solMass, direction=1)\n",
70 | "\n",
71 | "m20"
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "metadata": {},
77 | "source": [
78 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Tamborra_2014` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {},
85 | "outputs": [],
86 | "source": [
87 | "fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharex=True, sharey=True, tight_layout=True)\n",
88 | "\n",
89 | "for i, model in enumerate([m20, m27]):\n",
90 | " ax = axes[i]\n",
91 | " for flavor in Flavor:\n",
92 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
93 | " label=flavor.to_tex(),\n",
94 | " color='C0' if flavor.is_electron else 'C1',\n",
95 | " ls='-' if flavor.is_neutrino else ':',\n",
96 | " lw=2)\n",
97 | " ax.set(xlim=(0.0, 0.35),\n",
98 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
99 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
100 | " ax.grid()\n",
101 | " ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
102 | "\n",
103 | "axes[0].set(ylabel=r'luminosity [foe s$^{-1}$]');"
104 | ]
105 | },
106 | {
107 | "cell_type": "markdown",
108 | "metadata": {},
109 | "source": [
110 | "## Initial and Oscillated Spectra\n",
111 | "\n",
112 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
113 | "\n",
114 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "execution_count": null,
120 | "metadata": {},
121 | "outputs": [],
122 | "source": [
123 | "# Adiabatic MSW effect. NMO is used by default.\n",
124 | "xform_nmo = AdiabaticMSW()\n",
125 | "\n",
126 | "# Energy array and time to compute spectra.\n",
127 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
128 | "E = np.linspace(0,100,201) * u.MeV\n",
129 | "t = 50*u.ms\n",
130 | "\n",
131 | "ispec = model.get_initial_spectra(t, E)\n",
132 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
133 | ]
134 | },
135 | {
136 | "cell_type": "code",
137 | "execution_count": null,
138 | "metadata": {},
139 | "outputs": [],
140 | "source": [
141 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
142 | "\n",
143 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
144 | " ax = axes[i]\n",
145 | " for flavor in Flavor:\n",
146 | " ax.plot(E, spec[flavor],\n",
147 | " label=flavor.to_tex(),\n",
148 | " color='C0' if flavor.is_electron else 'C1',\n",
149 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
150 | " alpha=0.7)\n",
151 | "\n",
152 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
153 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
154 | " ax.grid()\n",
155 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
156 | "\n",
157 | "ax = axes[0]\n",
158 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]');"
159 | ]
160 | }
161 | ],
162 | "metadata": {
163 | "kernelspec": {
164 | "display_name": "Python 3.9.5 ('snews')",
165 | "language": "python",
166 | "name": "python3"
167 | },
168 | "language_info": {
169 | "codemirror_mode": {
170 | "name": "ipython",
171 | "version": 3
172 | },
173 | "file_extension": ".py",
174 | "mimetype": "text/x-python",
175 | "name": "python",
176 | "nbconvert_exporter": "python",
177 | "pygments_lexer": "ipython3",
178 | "version": "3.9.5"
179 | },
180 | "vscode": {
181 | "interpreter": {
182 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
183 | }
184 | }
185 | },
186 | "nbformat": 4,
187 | "nbformat_minor": 2
188 | }
189 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Walk_2018.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Walk 2018 Models\n",
8 | "\n",
9 | "Models from L. Walk et al., [Phys. Rev. D 98:123001, 2018](https://arxiv.org/abs/1807.02366) (s15.0c) taken from the Garching Supernova archive (https://wwwmpa.mpa-garching.mpg.de/ccsnarchive/data/Walk2018/) with permission for use in SNEWPY.\n",
10 | "\n",
11 | "Reference: L. Walk et al., *Identifying rotation in SASI-dominated core-collapse supernovae with a neutrino gyroscope*, Phys. Rev. D 98:123001, 2018\n",
12 | "- [doi:10.1103/PhysRevD.98.123001](https://journals.aps.org/prd/abstract/10.1103/PhysRevD.98.123001)\n",
13 | "- [arXiv:1807.02366](https://arxiv.org/abs/1807.02366)"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import Walk_2018\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `Walk_2018` model. We can use the `param` property to view all physics parameters and their possible values:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "Walk_2018.param"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "We’ll initialise this progenitor. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
59 | ]
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": null,
64 | "metadata": {},
65 | "outputs": [],
66 | "source": [
67 | "model = Walk_2018(progenitor_mass=15*u.solMass, rotation='fast', direction=1)\n",
68 | "\n",
69 | "model"
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Sukhbold_2015` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": null,
82 | "metadata": {},
83 | "outputs": [],
84 | "source": [
85 | "fig, ax = plt.subplots(1, figsize=(8, 6), tight_layout=False)\n",
86 | "\n",
87 | "for flavor in Flavor:\n",
88 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
89 | " label=flavor.to_tex(),\n",
90 | " color='C0' if flavor.is_electron else 'C1',\n",
91 | " ls='-' if flavor.is_neutrino else ':',\n",
92 | " lw=2)\n",
93 | "ax.set(xlim=(0, 0.35),\n",
94 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
95 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
96 | "ax.grid()\n",
97 | "ax.legend(loc='upper right', ncol=2, fontsize=18)\n",
98 | "ax.set(ylabel=r'luminosity [foe s$^{-1}$]');\n"
99 | ]
100 | },
101 | {
102 | "cell_type": "markdown",
103 | "metadata": {},
104 | "source": [
105 | "## Initial and Oscillated Spectra\n",
106 | "\n",
107 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
108 | "\n",
109 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": null,
115 | "metadata": {},
116 | "outputs": [],
117 | "source": [
118 | "# Adiabatic MSW effect. NMO is used by default.\n",
119 | "xform_nmo = AdiabaticMSW()\n",
120 | "\n",
121 | "# Energy array and time to compute spectra.\n",
122 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
123 | "E = np.linspace(0,100,201) * u.MeV\n",
124 | "t = 50*u.ms\n",
125 | "\n",
126 | "ispec = model.get_initial_spectra(t, E)\n",
127 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": null,
133 | "metadata": {},
134 | "outputs": [],
135 | "source": [
136 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
137 | "\n",
138 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
139 | " ax = axes[i]\n",
140 | " for flavor in Flavor:\n",
141 | " ax.plot(E, spec[flavor],\n",
142 | " label=flavor.to_tex(),\n",
143 | " color='C0' if flavor.is_electron else 'C1',\n",
144 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
145 | " alpha=0.7)\n",
146 | "\n",
147 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
148 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
149 | " ax.grid()\n",
150 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
151 | "\n",
152 | "ax = axes[0]\n",
153 | "ax.set(ylabel=r'flux [s$^{-1}$]')\n",
154 | "\n",
155 | "fig.tight_layout();"
156 | ]
157 | }
158 | ],
159 | "metadata": {
160 | "kernelspec": {
161 | "display_name": "Python 3.9.5 ('snews')",
162 | "language": "python",
163 | "name": "python3"
164 | },
165 | "language_info": {
166 | "codemirror_mode": {
167 | "name": "ipython",
168 | "version": 3
169 | },
170 | "file_extension": ".py",
171 | "mimetype": "text/x-python",
172 | "name": "python",
173 | "nbconvert_exporter": "python",
174 | "pygments_lexer": "ipython3",
175 | "version": "3.9.5"
176 | },
177 | "vscode": {
178 | "interpreter": {
179 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
180 | }
181 | }
182 | },
183 | "nbformat": 4,
184 | "nbformat_minor": 2
185 | }
186 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Walk_2019.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Walk 2019 Models\n",
8 | "\n",
9 | "Models from L. Walk et al., [Phys. Rev. D 101:123013, 2019](https://arxiv.org/abs/1910.12971) (s15.0c) taken from the Garching Supernova archive (https://wwwmpa.mpa-garching.mpg.de/ccsnarchive/data/Walk2019/) with permission for use in SNEWPY.\n",
10 | "\n",
11 | "Reference: L. Walk et al., *Neutrino emission characteristics of black hole formation in three-dimensional simulations of stellar collapse*, Phys. Rev. D 101:123013, 2019\n",
12 | "- [doi:10.1103/PhysRevD.101.123013](https://journals.aps.org/prd/abstract/10.1103/PhysRevD.101.123013)\n",
13 | "- [arXiv:1910.12971](https://arxiv.org/abs/1910.12971)"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import Walk_2019\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `Walk_2019` model. We can use the `param` property to view all physics parameters and their possible values. However, for this model, not all combinations of these parameters are valid, so we use the `get_param_combinations` function to get a list of all valid combinations:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "# Walk_2019.param\n",
52 | "Walk_2019.get_param_combinations()"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "We’ll initialise this progenitor. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
60 | ]
61 | },
62 | {
63 | "cell_type": "code",
64 | "execution_count": null,
65 | "metadata": {},
66 | "outputs": [],
67 | "source": [
68 | "model = Walk_2019(progenitor_mass=40*u.solMass, direction=1)\n",
69 | "\n",
70 | "model"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Walk_2019` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.)"
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": null,
83 | "metadata": {},
84 | "outputs": [],
85 | "source": [
86 | "fig, axes = plt.subplots(1, 1, figsize=(12, 5), sharex=True, sharey=True, tight_layout=True)\n",
87 | "\n",
88 | "for flavor in Flavor:\n",
89 | " axes.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
90 | " label=flavor.to_tex(),\n",
91 | " color='C0' if flavor.is_electron else 'C1',\n",
92 | " ls='-' if flavor.is_neutrino else ':',\n",
93 | " lw=2)\n",
94 | "axes.set(xlim=(0, 0.6),\n",
95 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
96 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
97 | "axes.grid()\n",
98 | "axes.legend(loc='upper right', ncol=2, fontsize=18)\n",
99 | "axes.set(ylabel=r'luminosity [foe s$^{-1}$]');"
100 | ]
101 | },
102 | {
103 | "cell_type": "markdown",
104 | "metadata": {},
105 | "source": [
106 | "## Initial and Oscillated Spectra\n",
107 | "\n",
108 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
109 | "\n",
110 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
111 | ]
112 | },
113 | {
114 | "cell_type": "code",
115 | "execution_count": null,
116 | "metadata": {},
117 | "outputs": [],
118 | "source": [
119 | "# Adiabatic MSW effect. NMO is used by default.\n",
120 | "xform_nmo = AdiabaticMSW()\n",
121 | "\n",
122 | "# Energy array and time to compute spectra.\n",
123 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
124 | "E = np.linspace(0,100,201) * u.MeV\n",
125 | "t = 50*u.ms\n",
126 | "\n",
127 | "ispec = model.get_initial_spectra(t, E)\n",
128 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
129 | ]
130 | },
131 | {
132 | "cell_type": "code",
133 | "execution_count": null,
134 | "metadata": {},
135 | "outputs": [],
136 | "source": [
137 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
138 | "\n",
139 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
140 | " ax = axes[i]\n",
141 | " for flavor in Flavor:\n",
142 | " ax.plot(E, spec[flavor],\n",
143 | " label=flavor.to_tex(),\n",
144 | " color='C0' if flavor.is_electron else 'C1',\n",
145 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
146 | " alpha=0.7)\n",
147 | "\n",
148 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
149 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
150 | " ax.grid()\n",
151 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
152 | "\n",
153 | "ax = axes[0]\n",
154 | "ax.set(ylabel=r'flux [s$^{-1}$]')\n",
155 | "\n",
156 | "fig.tight_layout();"
157 | ]
158 | }
159 | ],
160 | "metadata": {
161 | "kernelspec": {
162 | "display_name": "Python 3.9.5 ('snews')",
163 | "language": "python",
164 | "name": "python3"
165 | },
166 | "language_info": {
167 | "codemirror_mode": {
168 | "name": "ipython",
169 | "version": 3
170 | },
171 | "file_extension": ".py",
172 | "mimetype": "text/x-python",
173 | "name": "python",
174 | "nbconvert_exporter": "python",
175 | "pygments_lexer": "ipython3",
176 | "version": "3.9.5"
177 | },
178 | "vscode": {
179 | "interpreter": {
180 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
181 | }
182 | }
183 | },
184 | "nbformat": 4,
185 | "nbformat_minor": 2
186 | }
187 |
--------------------------------------------------------------------------------
/doc/source/nb/ccsn/Zha_2021.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Zha et al. 2021 Models\n",
8 | "\n",
9 | "Data from Zha et al. 2021, hadron-quark phase transition simulations of a variety of progenitors from Sukhbold et al. 2018 and the STOS EOS with B=145MeV.\n",
10 | " \n",
11 | "Reference: Zha et al. ApJ 911 74 2021\n",
12 | "- [doi:10.3847/1538-4357/abec4c](https://doi.org/10.3847/1538-4357/abec4c)\n",
13 | "- [arXiv:2103.02268](https://arxiv.org/abs/2103.02268)"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import matplotlib as mpl\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "\n",
26 | "from astropy import units as u \n",
27 | "\n",
28 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
29 | "from snewpy.models.ccsn import Zha_2021\n",
30 | "from snewpy.flavor_transformation import NoTransformation, AdiabaticMSW, ThreeFlavorDecoherence\n",
31 | "\n",
32 | "mpl.rc('font', size=16)\n",
33 | "%matplotlib inline"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "metadata": {},
39 | "source": [
40 | "## Initialize Models\n",
41 | "\n",
42 | "To start, let’s see what progenitors are available for the `Zha_2021` model. We can use the `param` property to view all physics parameters and their possible values:"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "Zha_2021.param"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "We’ll initialise two of these progenitors. If this is the first time you’re using a progenitor, snewpy will automatically download the required data files for you."
59 | ]
60 | },
61 | {
62 | "cell_type": "code",
63 | "execution_count": null,
64 | "metadata": {},
65 | "outputs": [],
66 | "source": [
67 | "m19 = Zha_2021(progenitor_mass=19*u.solMass)\n",
68 | "m19_89 = Zha_2021(progenitor_mass=19.89*u.solMass)\n",
69 | "\n",
70 | "m19"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "Finally, let’s plot the luminosity of different neutrino flavors for this model. (Note that the `Zha_2021` simulations didn’t distinguish between $\\nu_x$ and $\\bar{\\nu}_x$, so both have the same luminosity.) We’ll also add zoomed-in plots to see the phase transition better."
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": null,
83 | "metadata": {},
84 | "outputs": [],
85 | "source": [
86 | "fig, axes = plt.subplots(2, 2, figsize=(12, 10), tight_layout=True)\n",
87 | "\n",
88 | "for i, model in enumerate([m19, m19_89]):\n",
89 | " for j in range(2):\n",
90 | " ax = axes[j, i]\n",
91 | " for flavor in Flavor:\n",
92 | " ax.plot(model.time, model.luminosity[flavor]/1e51, # Report luminosity in units foe/s\n",
93 | " label=flavor.to_tex(),\n",
94 | " color='C0' if flavor.is_electron else 'C1',\n",
95 | " ls='-' if flavor.is_neutrino else ':',\n",
96 | " lw=2)\n",
97 | " ax.set(xlim=(-0.05, 0.9) if j==0 else (0.605, 0.665),\n",
98 | " ylim=(0, 600) if j==0 else (0, 125),\n",
99 | " xlabel=r'$t-t_{\\rm bounce}$ [s]',\n",
100 | " ylabel=r'luminosity [foe s$^{-1}$]',\n",
101 | " title=r'{}: {} $M_\\odot$'.format(model.metadata['EOS'], model.metadata['Progenitor mass'].value))\n",
102 | " ax.grid()\n",
103 | " ax.legend(loc='upper right', ncol=2, fontsize=18)"
104 | ]
105 | },
106 | {
107 | "cell_type": "markdown",
108 | "metadata": {},
109 | "source": [
110 | "## Initial and Oscillated Spectra\n",
111 | "\n",
112 | "Plot the neutrino spectra at the source and after the requested flavor transformation has been applied.\n",
113 | "\n",
114 | "### Adiabatic MSW Flavor Transformation: Normal mass ordering"
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "execution_count": null,
120 | "metadata": {},
121 | "outputs": [],
122 | "source": [
123 | "# Adiabatic MSW effect. NMO is used by default.\n",
124 | "xform_nmo = AdiabaticMSW()\n",
125 | "\n",
126 | "# Energy array and time to compute spectra.\n",
127 | "# Note that any convenient units can be used and the calculation will remain internally consistent.\n",
128 | "E = np.linspace(0,100,201) * u.MeV\n",
129 | "t = 400*u.ms\n",
130 | "\n",
131 | "ispec = model.get_initial_spectra(t, E)\n",
132 | "ospec_nmo = model.get_transformed_spectra(t, E, xform_nmo)"
133 | ]
134 | },
135 | {
136 | "cell_type": "code",
137 | "execution_count": null,
138 | "metadata": {},
139 | "outputs": [],
140 | "source": [
141 | "fig, axes = plt.subplots(1,2, figsize=(12,5), sharex=True, sharey=True, tight_layout=True)\n",
142 | "\n",
143 | "for i, spec in enumerate([ispec, ospec_nmo]):\n",
144 | " ax = axes[i]\n",
145 | " for flavor in Flavor:\n",
146 | " ax.plot(E, spec[flavor],\n",
147 | " label=flavor.to_tex(),\n",
148 | " color='C0' if flavor.is_electron else 'C1',\n",
149 | " ls='-' if flavor.is_neutrino else ':', lw=2,\n",
150 | " alpha=0.7)\n",
151 | "\n",
152 | " ax.set(xlabel=r'$E$ [{}]'.format(E.unit),\n",
153 | " title='Initial Spectra: $t = ${:.1f}'.format(t) if i==0 else 'Oscillated Spectra: $t = ${:.1f}'.format(t))\n",
154 | " ax.grid()\n",
155 | " ax.legend(loc='upper right', ncol=2, fontsize=16)\n",
156 | "\n",
157 | "ax = axes[0]\n",
158 | "ax.set(ylabel=r'flux [erg$^{-1}$ s$^{-1}$]')\n",
159 | "\n",
160 | "fig.tight_layout();"
161 | ]
162 | }
163 | ],
164 | "metadata": {
165 | "kernelspec": {
166 | "display_name": "Python 3 (ipykernel)",
167 | "language": "python",
168 | "name": "python3"
169 | },
170 | "language_info": {
171 | "codemirror_mode": {
172 | "name": "ipython",
173 | "version": 3
174 | },
175 | "file_extension": ".py",
176 | "mimetype": "text/x-python",
177 | "name": "python",
178 | "nbconvert_exporter": "python",
179 | "pygments_lexer": "ipython3",
180 | "version": "3.7.3"
181 | },
182 | "vscode": {
183 | "interpreter": {
184 | "hash": "e2528887d751495e023d57d695389d9a04f4c4d2e5866aaf6dc03a1ed45c573e"
185 | }
186 | }
187 | },
188 | "nbformat": 4,
189 | "nbformat_minor": 2
190 | }
191 |
--------------------------------------------------------------------------------
/doc/source/nb/dev/ExtendedCoolingTail.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "bb730907",
6 | "metadata": {},
7 | "source": [
8 | "# Extended Cooling Tail"
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "id": "18341676-5496-4da6-a722-7e730791387a",
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "from snewpy.models.ccsn import Nakazato_2013\n",
19 | "from snewpy.models.extended import ExtendedModel\n",
20 | "from snewpy.neutrino import Flavor, MassHierarchy\n",
21 | "\n",
22 | "from astropy import units as u\n",
23 | "\n",
24 | "import numpy as np\n",
25 | "import matplotlib.pyplot as plt"
26 | ]
27 | },
28 | {
29 | "cell_type": "markdown",
30 | "id": "944473f5-309f-4d65-b857-99f07d496a37",
31 | "metadata": {},
32 | "source": [
33 | "## Model Initialization\n",
34 | "\n",
35 | "Let's use a model from the `Nakazato_2013` family of simulations. There are many model parameters to choose from:"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": null,
41 | "id": "5a329b2c-58be-42d5-be06-70d0afebb633",
42 | "metadata": {},
43 | "outputs": [],
44 | "source": [
45 | "Nakazato_2013.param"
46 | ]
47 | },
48 | {
49 | "cell_type": "markdown",
50 | "id": "11432208-7c45-46d7-b1dc-6bf6d52d0b31",
51 | "metadata": {},
52 | "source": [
53 | "Since there are many valid and invalid combinations of parameters available, we will generate a list of valid parameters using the class function `get_param_combinations` and then select one of the particular models for plotting.\n",
54 | "\n",
55 | "It is not really important which model we choose, so we'll pick a large metallicity model (high $Z$)."
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "id": "62291165-b6db-4dab-b80a-d45058583a83",
62 | "metadata": {},
63 | "outputs": [],
64 | "source": [
65 | "highZ_models = list(params for params in Nakazato_2013.get_param_combinations() if params['metallicity'] == 0.02)"
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "id": "2d791680-42ec-4910-9c40-07749938156a",
71 | "metadata": {},
72 | "source": [
73 | "This should be equivalent to the initialization:\n",
74 | "```\n",
75 | "model = Nakazato_2013(progenitor_mass=20<`_.
6 | When installing SNEWPY, it automatically downloads the detector configurations
7 | from the latest supported SNOwGLoBES version.
8 |
9 | You only need to download SNOwGLoBES manually if you require a custom detector
10 | configuration. In that case, run the following commands:
11 |
12 | .. code-block:: bash
13 |
14 | git clone https://github.com/SNOwGLoBES/snowglobes.git
15 | cd snowglobes
16 | git checkout v1.3
17 | # create custom detector configuration
18 | export SNOWGLOBES=${PWD} # or use `SNOwGLoBESdir` parameter as documented below
19 |
20 |
21 | Usage
22 | -----
23 | .. automodule:: snewpy.snowglobes
24 | :members:
25 | :member-order: bysource
26 |
27 | Low-level interface
28 | -------------------
29 | .. automodule:: snewpy.snowglobes_interface
30 | :members: SimpleRate
31 |
--------------------------------------------------------------------------------
/doc/source/transformations.rst:
--------------------------------------------------------------------------------
1 | Flavor Transformations: ``snewpy.flavor_transformation``
2 | ========================================================
3 |
4 | .. automodule:: snewpy.flavor_transformation
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=61"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "snewpy"
7 | dynamic = ["version"]
8 | description = "A Python package for working with supernova neutrinos"
9 | authors = [{ name = "SNEWS Collaboration", email = "snews2.0@lists.bnl.gov" }]
10 | license = { text = "BSD" }
11 | readme = {file = "README.md", content-type = "text/markdown"}
12 |
13 | requires-python = ">=3.9"
14 |
15 | dependencies = [
16 | "numpy",
17 | "scipy",
18 | "astropy >= 4.3",
19 | "pandas",
20 | "tqdm",
21 | "matplotlib",
22 | "h5py",
23 | "requests",
24 | "pyyaml",
25 | "snowglobes_data == 1.3.2"
26 | ]
27 |
28 | [project.optional-dependencies]
29 | dev = ["hypothesis", "pytest"]
30 | docs = ["numpydoc", "nbsphinx", "ipykernel", "tqdm[notebook]"]
31 |
32 | [project.urls]
33 | "Homepage" = "https://github.com/SNEWS2/snewpy"
34 | "Bug Tracker" = "https://github.com/SNEWS2/snewpy/issues"
35 |
36 |
37 | [tool.setuptools.dynamic]
38 | version = {attr = "snewpy.__version__"}
39 |
40 | [tool.setuptools.packages.find]
41 | where = ["python"]
42 | include = [
43 | "snewpy",
44 | "snewpy.*",
45 | ]
46 | exclude = [
47 | "snewpy.scripts",
48 | ]
49 |
50 | [tool.setuptools.package-data]
51 | "snewpy.models" = ["*.yml"]
52 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | markers=
3 | snowglobes: tests that require SNOwGLoBES installed
4 | crosscheck: integration tests for comparison with reference output
5 | timing: benchmark testing of the function execution time
6 | BEMEWS: use external BEMEWS package for calculation of EarthMatter effect
7 |
8 |
9 |
--------------------------------------------------------------------------------
/python/snewpy/__init__.py:
--------------------------------------------------------------------------------
1 | # Licensed under a 3-clause BSD style license - see LICENSE.rst
2 | # -*- coding: utf-8 -*-
3 | """
4 | ======
5 | snewpy
6 | ======
7 | Front-end for supernova models which provide neutrino luminosity and spectra.
8 | """
9 |
10 | from sys import exit
11 | import os
12 |
13 | try:
14 | from astropy.config.paths import get_cache_dir
15 | except ModuleNotFoundError:
16 | # when imported by setup.py before dependencies are installed
17 | get_cache_dir = lambda: '.'
18 |
19 |
20 | __version__ = '1.6'
21 |
22 |
23 | src_path = os.path.realpath(__path__[0])
24 | base_path = os.sep.join(src_path.split(os.sep)[:-2])
25 | model_path = os.path.join(get_cache_dir(), 'snewpy', 'models')
26 |
27 | def get_models(models=None, download_dir=None):
28 | """Download model files from the snewpy repository.
29 |
30 | Parameters
31 | ----------
32 | models : list or str
33 | Models to download. Can be 'all', name of a single model or list of model names.
34 | download_dir : str
35 | [Deprecated, do not use.]
36 | """
37 | from concurrent.futures import ThreadPoolExecutor, as_completed
38 | from warnings import warn
39 | from .models.registry_model import all_models
40 |
41 | if download_dir is not None:
42 | warn("The `download_dir` argument to `get_models` is deprecated and will be removed soon.", FutureWarning, stacklevel=2)
43 |
44 | all_models = {m.__name__: m for m in all_models}
45 | all_model_names = sorted(all_models.keys())
46 |
47 | if models == "all":
48 | models = all_model_names
49 | elif isinstance(models, str):
50 | models = [models]
51 | elif models is None:
52 | # Select model(s) to download
53 | print(f"Available models in SNEWPY v{__version__}: {all_model_names}")
54 |
55 | selected = input("\nType a model name, 'all' to download all models or to cancel: ").strip()
56 | if selected == "all":
57 | models = all_model_names
58 | elif selected == "":
59 | exit()
60 | elif selected in all_model_names:
61 | models = {selected}
62 | while True:
63 | selected = input("\nType another model name or if you have selected all models you want to download: ").strip()
64 | if selected in all_model_names:
65 | models.add(selected)
66 | elif selected == "":
67 | break
68 | else:
69 | print(f"'{selected}' is not a valid model name. Please check for typos.")
70 | else:
71 | print(f"'{selected}' is not a valid model name. Please check for typos.")
72 | exit()
73 |
74 | print(f"\nYou have selected the models: {models}\n")
75 |
76 | pool = ThreadPoolExecutor(max_workers=8)
77 | results = []
78 | print(f"Downloading files for {models} to '{model_path}' ...")
79 | for model in models:
80 | for progenitor in all_models[model].get_param_combinations():
81 | results.append(pool.submit(all_models[model], **progenitor))
82 |
83 | exceptions = []
84 | for result in as_completed(results):
85 | if result.exception() is not None:
86 | exceptions.append(result.exception())
87 | if exceptions:
88 | print(f"ERROR: {len(exceptions)} exceptions occured. ({exceptions})")
89 | print("Please check your internet connection and try again later. If this persists, please report it at https://github.com/SNEWS2/snewpy/issues")
90 | exit(1)
91 | pool.shutdown(wait=False)
92 |
--------------------------------------------------------------------------------
/python/snewpy/_model_downloader.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Utility module that reads a local YAML file listing the data files
3 | available for the models supported by SNEWPY. These data files can either
4 | be local (in the SNEWPY source tree) or uploaded to a model repository on
5 | Zenodo. The YAML file supports regular expressions to allow matching of all
6 | possible model files.
7 | """
8 |
9 | import hashlib
10 | import os
11 | import requests
12 | import yaml
13 |
14 | from dataclasses import dataclass
15 | from importlib.resources import open_text
16 | from pathlib import Path
17 | from tqdm.auto import tqdm
18 |
19 | from snewpy import model_path
20 | from snewpy import __version__ as snewpy_version
21 |
22 | import logging
23 |
24 | logger = logging.getLogger("FileHandle")
25 |
26 |
27 | def _md5(fname: str) -> str:
28 | """calculate the md5sum hash of a file."""
29 | hash_md5 = hashlib.md5()
30 | with open(fname, "rb") as f:
31 | for chunk in iter(lambda: f.read(4096), b""):
32 | hash_md5.update(chunk)
33 | return hash_md5.hexdigest()
34 |
35 |
36 | def _download(src: str, dest: str, chunk_size=8192):
37 | """Download a file from 'src' to 'dest' and show the progress bar."""
38 | # make sure parent dir exists
39 | Path(dest).parent.mkdir(exist_ok=True, parents=True)
40 | with requests.get(src, stream=True) as r:
41 | r.raise_for_status()
42 | fileSize = int(r.headers.get("content-length", 0))
43 | with tqdm(
44 | desc=dest.name,
45 | total=fileSize,
46 | unit="iB",
47 | unit_scale=True,
48 | unit_divisor=1024,
49 | ) as bar:
50 | with open(dest, "wb") as f:
51 | for chunk in r.iter_content(chunk_size=chunk_size):
52 | size = f.write(chunk)
53 | bar.update(size)
54 |
55 |
56 | class ChecksumError(FileNotFoundError):
57 | """Raise an exception due to a mismatch in the MD5 checksum."""
58 |
59 | def __init__(self, path, md5_exp, md5_actual):
60 | super().__init__(f"Checksum error for file {path}: {md5_actual}!={md5_exp}")
61 |
62 | pass
63 |
64 |
65 | class MissingFileError(FileNotFoundError):
66 | """Raise an exception due to a missing file."""
67 |
68 | pass
69 |
70 |
71 | @dataclass
72 | class FileHandle:
73 | """Object storing local path, remote URL (optional), and MD5 sum
74 | (optional) for a SNEWPY model file. If the requested file is already present
75 | locally, open it. Otherwise, download it from the remote URL to the desired
76 | local path.
77 | """
78 |
79 | path: Path
80 | remote: str = None
81 | md5: str | None = None
82 |
83 | def check(self) -> None:
84 | """Check if the given file exists locally and has a correct md5 sum.
85 | Raises
86 | ------
87 | :class:`MissingFileError`
88 | if the local copy of the file is missing
89 | :class:`ChecksumError`
90 | if the local file exists, but the checksum is wrong"""
91 | if not self.path.exists():
92 | raise MissingFileError(self.path)
93 | if self.md5:
94 | logger.info(f"File {self.path}: checking md5")
95 | md5 = _md5(self.path)
96 | logger.debug(f"{md5} vs expected {self.md5}")
97 | if md5 != self.md5:
98 | raise ChecksumError(self.path, self.md5, md5)
99 |
100 | def load(self) -> Path:
101 | """Make sure that local file exists and has a correct checksum.
102 | Download the file if needed.
103 | """
104 | try:
105 | self.check()
106 | except FileNotFoundError as e:
107 | logger.info(f"Downloading file {self.path}")
108 | _download(self.remote, self.path)
109 | self.check()
110 | return self.path
111 |
112 |
113 | def _from_zenodo(zenodo_id: str, filename: str):
114 | """Access files on Zenodo.
115 |
116 | Parameters
117 | ----------
118 | zenodo_id : Zenodo record for model files.
119 | filename : Expected filename storing simulation data.
120 |
121 | Returns
122 | -------
123 | file_url, md5sum
124 | """
125 | zenodo_url = f"https://zenodo.org/api/records/{zenodo_id}"
126 | record = requests.get(zenodo_url).json()
127 | # Search for file string in Zenodo request for this record.
128 | file = next((_file for _file in record["files"] if _file["key"] == filename), None)
129 |
130 | # If matched, return a tuple of URL and checksum.Otherwise raise an exception.
131 | if file is not None:
132 | return file["links"]["self"], file["checksum"].split(":")[1]
133 | else:
134 | raise MissingFileError(filename)
135 |
136 |
137 | class ModelRegistry:
138 | """Access model data. Configuration for each model is in a YAML file
139 | distributed with SNEWPY.
140 | """
141 |
142 | def __init__(self, config_file: Path = None, local_path: str = model_path):
143 | """
144 | Parameters
145 | ----------
146 | config_file: YAML configuration file. If None (default) use the 'model_files.yml' from SNEWPY resources
147 | local_path: local installation path (defaults to astropy cache).
148 | """
149 | if config_file is None:
150 | context = open_text("snewpy.models", "model_files.yml")
151 | else:
152 | context = open(config_file)
153 | with context as f:
154 | self.config = yaml.safe_load(f)
155 | self.models = self.config["models"]
156 | self.local_path = local_path
157 |
158 | def get_file(self, config_path: str, filename: str) -> Path:
159 | """Get the requested data file from the models file repository
160 |
161 | Parameters
162 | ----------
163 | config_path : dot-separated path of the model in the YAML configuration (e.g. "ccsn.Bollig_2016")
164 | filename : Name of simulation datafile, or a relative path from the model sub-directory
165 |
166 | Returns
167 | -------
168 | Path of downloaded file.
169 | """
170 | tokens = config_path.split(".")
171 | config = self.models
172 | for t in tokens:
173 | config = config[t]
174 | # store the model name
175 | model = tokens[-1]
176 | # Get data from GitHub or Zenodo.
177 | repo = config["repository"]
178 | if repo == "zenodo":
179 | url, md5 = _from_zenodo(zenodo_id=config["zenodo_id"], filename=filename)
180 | else:
181 | # format the url directly
182 | params = {
183 | "model": model,
184 | "filename": filename,
185 | "snewpy_version": snewpy_version,
186 | }
187 | params.update(
188 | config
189 | ) # default parameters can be overriden in the model config
190 | url, md5 = repo.format(**params), None
191 | localpath = Path(model_path) / str(model)
192 | localpath.mkdir(exist_ok=True, parents=True)
193 | fh = FileHandle(path=localpath / filename, remote=url, md5=md5)
194 | return fh.load()
195 |
196 |
197 | registry = ModelRegistry()
198 |
199 |
200 | class LocalFileLoader:
201 | @classmethod
202 | def request_file(cls, filename:str)->Path:
203 | "Require that the provided filename exists locally"
204 | path = Path(filename).absolute()
205 | if not path.exists():
206 | raise FileNotFoundError(path)
207 | return path
208 |
209 | class RegistryFileLoader(LocalFileLoader):
210 | _registry = registry
211 |
212 | @classmethod
213 | def request_file(cls, filename:str)->Path:
214 | "Request file from the model registry"
215 | return cls._registry.get_file(cls._config_path, filename)
216 |
217 |
218 | def get_model_data(model: str, filename: str) -> Path:
219 | """Get the requested data file from the models file repository
220 |
221 | Parameters
222 | ----------
223 | model : dot-separated path of the model in the YAML configuration (e.g. "ccsn.Bollig_2016")
224 | filename : Absolute path to simulation datafile, or a relative path from the model sub-directory
225 |
226 | Returns
227 | -------
228 | Path of downloaded file.
229 | """
230 | if os.path.isabs(filename):
231 | return Path(filename)
232 | else:
233 | return registry.get_file(model, filename)
234 |
--------------------------------------------------------------------------------
/python/snewpy/flavor_transformation/TransformationChain.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """TransformationChain
3 |
4 | Class for calculating the combined probability matrix of the neutrino flavor transformation in SN, vacuum and Earth matter."""
5 |
6 |
7 | from snewpy.flavor import FlavorMatrix
8 | from snewpy.neutrino import MixingParameters, ThreeFlavorMixingParameters, FourFlavorMixingParameters
9 | from .base import FlavorTransformation
10 | from .in_sn import SNTransformation
11 | from .in_vacuum import VacuumTransformation, NoVacuumTransformation
12 | from .in_earth import EarthTransformation, NoEarthMatter
13 | from collections import namedtuple
14 |
15 | #a tuple to hold the transformations list
16 | TransformationsTuple = namedtuple('TransformationsTuple',['in_sn','in_vacuum','in_earth'])
17 |
18 | class TransformationChain(FlavorTransformation):
19 |
20 | r"""This class calculates the probability matrix :math:`P_{\beta\alpha}` of the :math:`\nu_\alpha\to\nu_\beta` flavor transition as multiplication of :math:`P^{SN}` transformation in SN, :math:`P^{Vac}` transformation in vacuum and :math:`P^{Earth}` transformation in Earth:
21 |
22 | .. math::
23 |
24 | P_{\beta\alpha} = \sum\limits_{i,j}P^{Earth}_{\beta j} \cdot P^{Vac}_{ji} \cdot P^{SN}_{i\alpha}
25 | """
26 | def __init__(self,
27 | in_sn: SNTransformation,
28 | in_vacuum: VacuumTransformation=NoVacuumTransformation(),
29 | in_earth: EarthTransformation=NoEarthMatter(),
30 | *,
31 | mixing_params:ThreeFlavorMixingParameters|FourFlavorMixingParameters=MixingParameters()
32 | ):
33 | """
34 | Parameters
35 | ----------
36 | in_sn
37 | Transformation in Supernova.
38 | in_vacuum
39 | Transformation in Vacuum.
40 | By default NoVacuumTransformation is applied
41 | in_earth
42 | Transformation in Earth.
43 | By default NoEarthTransformation is applied
44 |
45 | Keyword mixing_params
46 | Neutrino mixing parameters (to be applied to all individual transformations in chain)
47 | By default use standard `MixingParameters` with normal neutrino ordering
48 | """
49 | self.transforms = TransformationsTuple(in_sn, in_vacuum, in_earth)
50 | self.set_mixing_params(mixing_params)
51 |
52 | def set_mixing_params(self, mixing_params):
53 | """Update the mixing parameters in all transformations in chain"""
54 | #set the mixing parameters to all the inner classes
55 | self.mixing_params = mixing_params
56 | for t in self.transforms:
57 | t.mixing_params = mixing_params
58 |
59 | def P_ff(self, t, E)->FlavorMatrix:
60 | return self.transforms.in_earth.P_fm(t,E) @ \
61 | self.transforms.in_vacuum.P_mm(t,E) @ \
62 | self.transforms.in_sn.P_mf(t,E)
63 |
64 | def __str__(self):
65 | s = '+'.join([t.__class__.__name__ for t in self.transforms])+'_'+self.mixing_params.mass_order.name
66 | return s
67 |
--------------------------------------------------------------------------------
/python/snewpy/flavor_transformation/__init__.py:
--------------------------------------------------------------------------------
1 | r""" This module implements flavor transformations that describe how neutrinos of
2 | different flavors change into each other between production inside the
3 | supernova and detection on Earth.
4 |
5 | Base Class for Flavor Transformations
6 | -------------------------------------
7 | .. autoclass:: snewpy.flavor_transformation.FlavorTransformation
8 | :members:
9 |
10 |
11 | Available Transformations
12 | -------------------------
13 | .. autoclass:: snewpy.flavor_transformation.NoTransformation
14 | :members:
15 |
16 | .. autoclass:: snewpy.flavor_transformation.CompleteExchange
17 | :members:
18 |
19 | .. autoclass:: snewpy.flavor_transformation.ThreeFlavorDecoherence
20 | :members:
21 |
22 | .. autoclass:: snewpy.flavor_transformation.TransformationChain
23 | :members:
24 |
25 | Concrete transformations
26 | ------------------------
27 | .. automodule:: snewpy.flavor_transformation.in_sn
28 | :members:
29 | :member-order: bysource
30 |
31 | .. automodule:: snewpy.flavor_transformation.in_vacuum
32 | :members:
33 | :member-order: bysource
34 |
35 | .. automodule:: snewpy.flavor_transformation.in_earth
36 | :members:
37 | :member-order: bysource
38 | """
39 | from snewpy.flavor import FlavorMatrix, ThreeFlavor
40 | from . import in_sn, in_earth, in_vacuum
41 | from .base import FlavorTransformation
42 | from .TransformationChain import TransformationChain
43 | from snewpy.neutrino import ThreeFlavorMixingParameters, FourFlavorMixingParameters
44 |
45 | def construct_chain(*transformation_classes):
46 | """Create a function that constructs a chain from given transformation_steps"""
47 | def construct(mixing_params:ThreeFlavorMixingParameters|FourFlavorMixingParameters,
48 | )->TransformationChain:
49 | """Construct a transformation chain with
50 | Parameters
51 | ----------
52 | mixing_params:
53 | neutrino mixing parameters to be passed to the internal transformation steps
54 |
55 | Returns
56 | -------
57 | A TransformationChain object with {step_names} transformations
58 | """
59 | #initialize the transformations
60 | transformations = [value() for value in transformation_classes]
61 | #create the chain
62 | return TransformationChain(*transformations, mixing_params=mixing_params)
63 | #update the docstring
64 | step_names=[cls.__qualname__ for cls in transformation_classes]
65 | construct.__doc__ = construct.__doc__.format(step_names=step_names)
66 | return construct
67 |
68 | # define default values for backward compatibility
69 | AdiabaticMSW = construct_chain(in_sn.AdiabaticMSW)
70 | NonAdiabaticMSWH = construct_chain(in_sn.NonAdiabaticMSWH)
71 | AdiabaticMSWes = construct_chain(in_sn.AdiabaticMSWes)
72 | NonAdiabaticMSWes = construct_chain(in_sn.NonAdiabaticMSWes)
73 | TwoFlavorDecoherence = construct_chain(in_sn.TwoFlavorDecoherence)
74 | NeutrinoDecay = construct_chain(in_sn.AdiabaticMSW,
75 | in_vacuum.NeutrinoDecay)
76 | QuantumDecoherence = construct_chain(in_sn.AdiabaticMSW,
77 | in_vacuum.QuantumDecoherence)
78 | EarthMatter = lambda mixing_params,AltAz: TransformationChain(
79 | in_sn.AdiabaticMSW(),
80 | in_earth=in_earth.EarthMatter(SNAltAz=AltAz),
81 | mixing_params=mixing_params
82 | )
83 |
84 |
85 | # Phenomenological transformations that cannot be represented as a TransformationChain
86 | class NoTransformation(FlavorTransformation):
87 | """Survival probabilities for no oscillation case."""
88 |
89 | def P_ff(self, t, E):
90 | r"""This transformation returns the object without transform,
91 | so the transformation probability matrix is unit:
92 |
93 | .. math::
94 |
95 | P_{\alpha\beta} = I_{\alpha\beta}
96 | """
97 | p = FlavorMatrix.eye(ThreeFlavor)
98 | return p
99 |
100 | def apply_to(self, flux):
101 | return flux
102 |
103 |
104 | class CompleteExchange(FlavorTransformation):
105 | """Survival probabilities for the case when the electron flavors
106 | are half exchanged with the mu flavors and the half with the tau flavors.
107 | """
108 |
109 | def P_ff(self, t, E):
110 | @FlavorMatrix.from_function(ThreeFlavor)
111 | def P(f1, f2):
112 | return (f1.is_neutrino == f2.is_neutrino)*(f1 != f2)*0.5
113 |
114 | return P
115 |
116 |
117 | class ThreeFlavorDecoherence(FlavorTransformation):
118 | """Equal mixing of all threen eutrino matter states and antineutrino matter states"""
119 |
120 | def P_ff(self, t, E):
121 | """Equal mixing so Earth matter has no effect"""
122 | @FlavorMatrix.from_function(ThreeFlavor)
123 | def P(f1, f2):
124 | return (f1.is_neutrino == f2.is_neutrino)*1/3.
125 | return P
126 |
--------------------------------------------------------------------------------
/python/snewpy/flavor_transformation/base.py:
--------------------------------------------------------------------------------
1 | from abc import abstractmethod, ABC
2 |
3 | from snewpy.flux import Container
4 | from snewpy.flavor import FlavorMatrix
5 | from snewpy.neutrino import MixingParameters, ThreeFlavorMixingParameters, FourFlavorMixingParameters
6 |
7 | class ThreeFlavorTransformation:
8 | _mixing_params = ThreeFlavorMixingParameters(**MixingParameters())
9 |
10 | @property
11 | def mixing_params(self):
12 | return self._mixing_params
13 |
14 | @mixing_params.setter
15 | def mixing_params(self, val):
16 | return self._mixing_params.update(**val)
17 |
18 | class FourFlavorTransformation(ThreeFlavorTransformation):
19 | _mixing_params = FourFlavorMixingParameters(**MixingParameters())
20 |
21 |
22 | class FlavorTransformation(ABC):
23 | """Generic interface to compute neutrino and antineutrino survival probability."""
24 |
25 | def __str__(self):
26 | return self.__class__.__name__
27 |
28 | @abstractmethod
29 | def P_ff(self, t, E) -> FlavorMatrix:
30 | r"""Transition probability matrix in flavor basis :math:`P_{\alpha\to\beta}`
31 |
32 | Parameters
33 | ----------
34 | """
35 | pass
36 |
37 | def apply_to(self, flux: Container) -> Container:
38 | r"""Apply this transformation to the given flux, return transformaed flux"""
39 | M = self.P_ff(flux.time, flux.energy)
40 | M = (flux.flavor_scheme <FlavorMatrix:
31 | pass
32 | ###############################################################################
33 |
34 | class NoEarthMatter(EarthTransformation):
35 | def __init__(self, mixing_params=None):
36 | self.mixing_params = mixing_params or MixingParameters('NORMAL')
37 |
38 | def P_fm(self, t, E)->FlavorMatrix:
39 | D = self.mixing_params.VacuumMixingMatrix().abs2()
40 | return D
41 | ###############################################################################
42 |
43 | class EarthMatter(ThreeFlavorTransformation, EarthTransformation):
44 |
45 | def __init__(self, SNAltAz, mixing_params=None):
46 | """Initialize flavor transformation
47 |
48 | Parameters
49 | ----------
50 | mixing_params : ThreeFlavorMixingParameters instance or None
51 | SNAltAz : astropy AltAz object
52 | """
53 | if BEMEWS is None:
54 | raise ModuleNotFoundError('BEMEWS module is not found. Please make sure BEMEWS is installed to use EarthMatter transformation')
55 | if(mixing_params):
56 | self.mixing_params=mixing_params
57 | self.SNAltAz = SNAltAz
58 |
59 | self.prior_E = None # used to store energy array from previous calls to get_probabilities
60 | self.prior_D = None
61 |
62 |
63 | # Initialize BEMEWS input data object
64 | self.settings = BEMEWS.InputDataBEMEWS()
65 |
66 | self.settings.altitude = self.SNAltAz.alt.deg
67 | self.settings.azimuth = self.SNAltAz.az.deg
68 |
69 | self.settings.densityprofile = str(files(BEMEWS.data).joinpath('PREM.rho.dat'))
70 | self.settings.electronfraction = str(files(BEMEWS.data).joinpath('PREM.Ye.dat'))
71 | self.settings.accuracy = 1.01e-9
72 | self.settings.outputflag = False
73 | self.settings.stepcounterlimit = False
74 |
75 | def _update_settings(self):
76 | """Put the values from mixing_parameters into self.settings"""
77 | self.settings.deltam_21 = self.mixing_params.dm21_2.to_value('eV**2')
78 | self.settings.deltam_32 = self.mixing_params.dm32_2.to_value('eV**2')
79 | self.settings.theta12 = self.mixing_params.theta12.to_value('deg')
80 | self.settings.theta13 = self.mixing_params.theta13.to_value('deg')
81 | self.settings.theta23 = self.mixing_params.theta23.to_value('deg')
82 | self.settings.deltaCP = self.mixing_params.deltaCP.to_value('deg')
83 |
84 | def P_fm(self, t, E):
85 | #update the settings - in case mixing_params were changed
86 | self._update_settings()
87 |
88 | if self.prior_E != None:
89 | # Use cached result if possible
90 | if u.allclose(self.prior_E, E) == True:
91 | return self.prior_D
92 |
93 | self.prior_E = E
94 |
95 | #- Set the input energy bins
96 | E = E.to_value('MeV')
97 | self.settings.NE = len(E)
98 | self.settings.Emin = E[0]
99 | self.settings.Emax = E[-1]
100 |
101 | #run the calculation
102 | Pfm = np.asarray(BEMEWS.Run(self.settings))
103 | #matrix from BEMEWS needs to be rearranged to match SNEWPY flavor indicii ordering
104 | #Pfm contains P(nu_alpha -> nu_i) index order is (nu/nubar, energy, alpha, i)
105 | #We convert the array dimensions:
106 | Pfm = np.swapaxes(Pfm, 1,3) #(nu/nubar, i, alpha, energy)
107 |
108 | P = FlavorMatrix.zeros(
109 | flavor=self.mixing_params.basis_mass,
110 | from_flavor=self.mixing_params.basis_flavor,
111 | extra_dims=E.shape)
112 |
113 | P["NU","NU"] = Pfm[0]
114 | P["NU_BAR","NU_BAR"] = Pfm[1]
115 | self.prior_D = P
116 | return P
117 |
--------------------------------------------------------------------------------
/python/snewpy/flavor_transformation/in_vacuum.py:
--------------------------------------------------------------------------------
1 | r"""
2 | Transformations in vacuum
3 | =========================
4 |
5 | Transitions of neutrino mass states :math:`\nu_i\to\nu_j` in vacuum
6 | """
7 | from abc import abstractmethod, ABC
8 | import numpy as np
9 | from astropy import units as u
10 | from astropy import constants as c
11 |
12 | from snewpy.flavor import FlavorMatrix
13 | from .base import ThreeFlavorTransformation, FourFlavorTransformation
14 | from snewpy.neutrino import MassHierarchy
15 |
16 | ###############################################################################
17 | # Vacuum transformations
18 | ###############################################################################
19 | class VacuumTransformation(ABC):
20 | @abstractmethod
21 | def P_mm(self, t, E)->FlavorMatrix:
22 | pass
23 | ###############################################################################
24 | class NoVacuumTransformation(VacuumTransformation):
25 | def P_mm(self, t, E)->FlavorMatrix:
26 | return FlavorMatrix.eye(self.mixing_params.basis_mass)
27 |
28 | class NeutrinoDecay(VacuumTransformation, ThreeFlavorTransformation):
29 | """Decay effect, where the heaviest neutrino decays to the lightest
30 | neutrino. For a description and typical parameters, see A. de Gouvêa et al.,
31 | PRD 101:043013, 2020, arXiv:1910.01127.
32 | """
33 | def __init__(self, mass=1*u.eV/c.c**2, tau=1*u.day, dist=10*u.kpc):
34 | """Initialize transformation matrix.
35 |
36 | Parameters
37 | ----------
38 | mass : astropy.units.quantity.Quantity
39 | Mass of the heaviest neutrino; expect in eV/c^2.
40 | tau : astropy.units.quantity.Quantity
41 | Lifetime of the heaviest neutrino.
42 | dist : astropy.units.quantity.Quantity
43 | Distance to the supernova.
44 | """
45 | self.m = mass
46 | self.tau = tau
47 | self.d = dist
48 |
49 | def gamma(self, E):
50 | """Decay width of the heaviest neutrino mass.
51 |
52 | Parameters
53 | ----------
54 | E : float
55 | Energy of the nu3.
56 |
57 | Returns
58 | -------
59 | Gamma : float
60 | Decay width of the neutrino mass, in units of 1/length.
61 |
62 | :meta private:
63 | """
64 | return self.m*c.c / (E*self.tau)
65 |
66 | def P_mm(self, t, E)->FlavorMatrix:
67 | decay_factor = np.exp(-self.gamma(E)*self.d)
68 | PND = FlavorMatrix.eye(self.mixing_params.basis_mass, extra_dims=E.shape)
69 |
70 | if self.mixing_params.mass_order == MassHierarchy.NORMAL:
71 | PND['NU_1','NU_3'] = 1 - decay_factor
72 | PND['NU_3','NU_3'] = decay_factor
73 |
74 | else:
75 | PND['NU_2','NU_2'] = decay_factor
76 | PND['NU_3','NU_2'] = 1 - decay_factor
77 |
78 | PND['NU_BAR','NU_BAR'] = PND['NU','NU']
79 | return PND
80 |
81 | ###############################################################################
82 |
83 | class QuantumDecoherence(VacuumTransformation, ThreeFlavorTransformation):
84 | """Quantum Decoherence, where propagation in vacuum leads to equipartition
85 | of states. For a description and typical parameters, see M. V. dos Santos et al.,
86 | 2023, arXiv:2306.17591.
87 | """
88 | def __init__(self, Gamma3=1e-27*u.eV, Gamma8=1e-27*u.eV, dist=10*u.kpc, n=0, E0=10*u.MeV):
89 | """Initialize transformation matrix.
90 |
91 | Parameters
92 | ----------
93 | Gamma3 : astropy.units.quantity.Quantity
94 | Quantum decoherence parameter; expect in eV.
95 | Gamma8 : astropy.units.quantity.Quantity
96 | Quantum decoherence parameter; expect in eV.
97 | dist : astropy.units.quantity.Quantity
98 | Distance to the supernova.
99 | n : float
100 | Exponent of power law for energy dependent quantum decoherence parameters,
101 | i.e. Gamma = Gamma0*(E/E0)**n. If not specified, it is taken as zero.
102 | E0 : astropy.units.quantity.Quantity
103 | Reference energy in the power law Gamma = Gamma0*(E/E0)**n. If not specified,
104 | it is taken as 10 MeV. Note that if n = 0, quantum decoherence parameters are independent
105 | of E0.
106 | """
107 | self.Gamma3 = (Gamma3 / (c.hbar.to('eV s') * c.c)).to('1/kpc')
108 | self.Gamma8 = (Gamma8 / (c.hbar.to('eV s') * c.c)).to('1/kpc')
109 | self.d = dist
110 | self.n = n
111 | self.E0 = E0
112 |
113 | def P_mm(self, t, E)->FlavorMatrix:
114 | PQD = FlavorMatrix.zeros(self.mixing_params.basis_mass, extra_dims=E.shape)
115 | x = (E/self.E0)**self.n
116 | PQD['NU_1','NU_1'] = 1/3 + 1/2 * np.exp(-(self.Gamma3 + self.Gamma8 / 3) * x * self.d) \
117 | + 1/6 * np.exp(-self.Gamma8 * x * self.d)
118 |
119 | PQD['NU_1','NU_2'] = 1/3 - 1/2 * np.exp(-(self.Gamma3 + self.Gamma8 / 3) * x * self.d) \
120 | + 1/6 * np.exp(-self.Gamma8 * x * self.d)
121 |
122 | PQD['NU_1','NU_3'] = 1/3 - 1/3 * np.exp(-self.Gamma8 * x * self.d)
123 |
124 | PQD['NU_2',['NU_1','NU_2','NU_3']] = PQD['NU_1',['NU_2','NU_1','NU_3']]
125 | PQD['NU_2','NU_2'] = PQD['NU_1','NU_1']
126 | PQD['NU_2','NU_3'] = PQD['NU_1','NU_3']
127 |
128 | PQD['NU_3','NU_1'] = PQD['NU_1','NU_3']
129 | PQD['NU_3','NU_2'] = PQD['NU_2','NU_3']
130 |
131 | PQD['NU_3','NU_3'] = 1/3 + 2/3 * np.exp(-self.Gamma8 * x * self.d)
132 | PQD['NU_BAR','NU_BAR'] = PQD['NU','NU']
133 | return PQD
134 |
--------------------------------------------------------------------------------
/python/snewpy/models/__init__.py:
--------------------------------------------------------------------------------
1 | from warnings import warn
2 |
3 | from . import ccsn
4 |
5 |
6 | def __getattr__(name):
7 | if name in dir(ccsn):
8 | warn(f"{__name__}.{name} is moved to {__name__}.ccsn.{name}", FutureWarning, stacklevel=2)
9 | return getattr(ccsn, name)
10 | raise AttributeError(f"module {__name__} has no attribute {name}")
11 |
--------------------------------------------------------------------------------
/python/snewpy/models/extended.py:
--------------------------------------------------------------------------------
1 | from warnings import warn
2 |
3 | import numpy as np
4 | from astropy import units as u
5 |
6 | from snewpy.neutrino import Flavor
7 | from snewpy.models.base import SupernovaModel
8 |
9 |
10 | class ExtendedModel(SupernovaModel):
11 | """Class defining a supernova model with a cooling tail extension."""
12 |
13 | def __init__(self, base_model):
14 | """Initialize extended supernova model class."""
15 | if not isinstance(base_model, SupernovaModel):
16 | raise TypeError("ExtendedModel.__init__ requires a SupernovaModel object")
17 |
18 | self.__dict__ = base_model.__dict__.copy()
19 | for method_name in dir(base_model):
20 | if callable(getattr(base_model, method_name)) and method_name[0] != '_':
21 | if method_name == 'get_initial_spectra':
22 | self._get_initial_spectra = getattr(base_model, method_name)
23 | else:
24 | setattr(self, method_name, getattr(base_model, method_name))
25 | self.t_final = self.time[-1]
26 | self.L_final = {flv: self.luminosity[flv][-1] for flv in Flavor}
27 |
28 | def get_initial_spectra(self, *args, **kwargs):
29 | """Get neutrino spectra/luminosity curves before oscillation"""
30 | return self._get_initial_spectra(*args, **kwargs)
31 |
32 | def get_extended_luminosity(self, t, k=-1., A=None, tau_c=36. * u.s, alpha=2.66, flavor = Flavor.NU_E):
33 | """Get neutrino luminosity from supernova cooling tail luminosity model.
34 |
35 | Parameters
36 | ----------
37 | t : astropy.Quantity
38 | Time to evaluate luminosity.
39 | k : float
40 | Power law factor (default: -1)
41 | A : astropy.Quantity
42 | Normalization factor (default: None, automatically match original model data)
43 | tau_c : astropy.Quantity
44 | Exponential decay characteristic timescale (default: 36 s)
45 | alpha : float
46 | Exponential decay factor (default: 2.66)
47 |
48 | Returns
49 | -------
50 | astropy.Quantity
51 | Luminosity calculated from cooling tail model.
52 | """
53 | if t.value < 0.5:
54 | warn("Extended luminosity model not applicable to early times")
55 | if A is None:
56 | tf = self.t_final
57 | Lf = self.L_final[flavor]
58 | A = Lf / (tf.value**k * np.exp(-(tf/tau_c)**alpha))
59 | return A * t.value**k * np.exp(-(t/tau_c)**alpha)
60 |
61 | def extend(self, ts, k=-1., A=None, tau_c=36. * u.s, alpha=2.66):
62 | """Extend supernova model to specific times.
63 |
64 | Parameters
65 | ----------
66 | ts : astropy.Quantity
67 | Times to add to supernova model.
68 | k : float
69 | Power law factor (default: -1)
70 | A : astropy.Quantity
71 | Normalization factor (default: None, automatically match original model data)
72 | tau_c : astropy.Quantity
73 | Exponential decay characteristic timescale (default: 36 s)
74 | alpha : float
75 | Exponential decay factor (default: 2.66)
76 | """
77 | # Select times after the end of the model
78 | select = ts > self.t_final
79 |
80 | for t in ts[select]:
81 | self.time = np.append(self.time, t)
82 | for flavor in Flavor:
83 | L_ext = self.get_extended_luminosity(t, k = k, A = A, tau_c = tau_c, alpha = alpha, flavor = flavor)
84 | self.luminosity[flavor] = np.append(self.luminosity[flavor], L_ext)
85 | self.meanE[flavor] = np.append(self.meanE[flavor], self.meanE[flavor][-1])
86 | self.pinch[flavor] = np.append(self.pinch[flavor], self.pinch[flavor][-1])
87 |
--------------------------------------------------------------------------------
/python/snewpy/models/model_files.yml:
--------------------------------------------------------------------------------
1 | # Define remote locations of simulation files for each model.
2 | # 1. Model data on GitHub must provide the SNEWPY release version.
3 | # 2. Model data on Zenodo must provide the Zenodo record ID.
4 |
5 | config:
6 | - &snewpy "https://github.com/SNEWS2/snewpy/raw/v{snewpy_version}/models/{model}/{filename}"
7 | - &ccsn_repository "https://github.com/SNEWS2/snewpy-models-ccsn/raw/v0.3/models/{model}/{filename}"
8 | - &presn_repository "https://github.com/SNEWS2/snewpy-models-presn/raw/v0.2/models/{model}/{filename}"
9 |
10 | models:
11 | ccsn:
12 | Bollig_2016:
13 | repository: *ccsn_repository
14 |
15 | Fornax_2019:
16 | repository: *ccsn_repository
17 |
18 | Fornax_2021:
19 | repository: *ccsn_repository
20 |
21 | Fornax_2022:
22 | repository: *ccsn_repository
23 |
24 | Kuroda_2020:
25 | repository: *ccsn_repository
26 |
27 | Nakazato_2013:
28 | repository: *ccsn_repository
29 |
30 | OConnor_2013:
31 | repository: *ccsn_repository
32 |
33 | OConnor_2015:
34 | repository: *ccsn_repository
35 |
36 | Sukhbold_2015:
37 | repository: *ccsn_repository
38 |
39 | Tamborra_2014:
40 | repository: *ccsn_repository
41 |
42 | Walk_2018:
43 | repository: *ccsn_repository
44 |
45 | Walk_2019:
46 | repository: *ccsn_repository
47 |
48 | Warren_2020:
49 | repository: *ccsn_repository
50 |
51 | Zha_2021:
52 | repository: *ccsn_repository
53 |
54 | Mori_2023:
55 | repository: *ccsn_repository
56 |
57 | Bugli_2021:
58 | repository: *ccsn_repository
59 |
60 | Fischer_2020:
61 | repository: *ccsn_repository
62 |
63 | presn:
64 |
65 | Odrzywolek_2010:
66 | repository: *presn_repository
67 |
68 | Patton_2017:
69 | repository: *presn_repository
70 |
71 | Kato_2017:
72 | repository: *presn_repository
73 |
74 | Yoshida_2016:
75 | repository: *presn_repository
76 |
--------------------------------------------------------------------------------
/python/snewpy/models/presn.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | The submodule ``snewpy.models.presn`` contains models of presupernova neutrino fluxes,
4 | derived from the :class:`SupernovaModel` base class.
5 | """
6 | import snewpy.models.presn_loaders as loaders
7 | from snewpy.models.registry_model import RegistryModel
8 | from astropy import units as u
9 |
10 | @RegistryModel(
11 | progenitor_mass = [15, 25]<`_
16 |
17 | Dataset available on `Odrzywolek’s website `_
18 | """
19 | def __init__(self, progenitor_mass:u.Quantity):
20 | filename=f"s{progenitor_mass.to_value('Msun'):.0f}_nuebar_data.txt"
21 | super().__init__(filename)
22 |
23 | @RegistryModel(
24 | progenitor_mass = [15, 30]<`_
29 |
30 | Dataset available on Zenodo (`DOI:10.5281/zenodo.2626645 `_)
31 | """
32 | def __init__(self, progenitor_mass:u.Quantity):
33 | filename=f"totalLuminosity_{progenitor_mass.to_value('Msun'):.0f}SolarMass.dat"
34 | super().__init__(filename)
35 |
36 | @RegistryModel(
37 | progenitor_mass = [12, 15]<`_
42 |
43 | Dataset available on `Zenodo `__
44 | """
45 | def __init__(self, progenitor_mass:u.Quantity):
46 | path=f"pre_collapse/m{progenitor_mass.to_value('Msun'):.0f}"
47 | super().__init__(path)
48 |
49 |
50 | @RegistryModel(
51 | progenitor_mass = [12, 15, 20]<`_
56 |
57 | Dataset available on `Zenodo `__
58 | """
59 | def __init__(self, progenitor_mass:u.Quantity):
60 | path=f"t_spc_m{progenitor_mass.to_value('Msun'):.0f}_1.2.txt"
61 | super().__init__(path)
62 |
--------------------------------------------------------------------------------
/python/snewpy/models/presn_loaders.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | The submodule ``snewpy.models.presn_loaders`` contains classes to load pre-supernova
4 | models from files stored on disk.
5 | """
6 |
7 | import numpy as np
8 | import pandas as pd
9 | from scipy.interpolate import interp1d
10 | from astropy import units as u
11 | from snewpy.models.base import SupernovaModel
12 | from snewpy.neutrino import Flavor
13 | from pathlib import Path
14 |
15 | def _interp_T(t0, v0, dt=1e-3, dv=1e-10, axis=0):
16 | "dilog interpolation"
17 | lt0 = np.log(t0 + dt)
18 | lv0 = np.log(v0 + dv)
19 | lv1 = interp1d(lt0, lv0, axis=axis, fill_value=0, copy=False, bounds_error=False)
20 | return lambda t1: np.exp(lv1(np.log(t1 + dt)))
21 |
22 |
23 | def _interp_E(e0, v0, axis=1):
24 | "linear interpolation"
25 | return interp1d(e0, v0, axis=axis, fill_value=0, copy=False, bounds_error=False)
26 |
27 |
28 | def _interp_TE(times, energies, array, ax_t=1, ax_e=2):
29 | def _f(t, E):
30 | a_t = _interp_T(times, array, axis=ax_t)(t)
31 | a_te = _interp_E(energies, a_t, axis=ax_e)(E)
32 | return a_te
33 |
34 | return _f
35 |
36 | class Odrzywolek_2010(SupernovaModel):
37 | """Set up a presupernova model, based on
38 | [A. Odrzywolek and A. Heger, Acta Phys. Polon. B 41 (2010) 1611.]
39 | """
40 |
41 | def __init__(self, filename:str):
42 | df = pd.read_csv(
43 | self.request_file(filename),
44 | sep=r'\s+',
45 | skiprows=1,
46 | usecols=[1,6,7,8],
47 | names=["time","a","alpha","b"],
48 | index_col="time",
49 | )
50 | # interpolated in time
51 | self.df_t = _interp_T(df.index, df)
52 | self.factor = {}
53 | for f in Flavor:
54 | if f.is_electron:
55 | self.factor[f] = 1.0
56 | else:
57 | # nuX/nuE ratio from Odrzywolek paper: (arXiv:astro-ph/0311012)
58 | self.factor[f] = 0.19
59 | time = -df.index.to_numpy() << u.s
60 | super().__init__(time, self.metadata)
61 |
62 | def get_initial_spectra(self, t, E, flavors=Flavor):
63 | # negative t for time before SN
64 | t = -t.to_value("s")
65 | E = E.to_value("MeV")
66 | df = self.df_t(t)
67 | a, alpha, b = df.T
68 | Enu = np.expand_dims(E, 1)
69 | a = np.expand_dims(a, 0)
70 | alpha = np.expand_dims(alpha, 0)
71 | b = np.expand_dims(b, 0)
72 | fluence = a * Enu ** alpha * np.exp(-b * Enu) / (u.MeV * u.s)
73 | result = {f: fluence.T * self.factor[f] for f in flavors}
74 | return result
75 |
76 |
77 | class Patton_2017(SupernovaModel):
78 | """Set up a presupernova model based on
79 | [Kelly M. Patton et al 2017 ApJ 851 6,
80 | https://doi.org/10.5281/zenodo.2598709]
81 | """
82 | def __init__(self, filename:str):
83 | df = pd.read_csv(
84 | self.request_file(filename),
85 | comment="#",
86 | sep=r'\s+',
87 | names=["time","Enu",Flavor.NU_E,Flavor.NU_E_BAR,Flavor.NU_MU,Flavor.NU_MU_BAR],
88 | usecols=range(6),
89 | )
90 |
91 | df[Flavor.NU_TAU] = df[Flavor.NU_MU]
92 | df[Flavor.NU_TAU_BAR] = df[Flavor.NU_MU_BAR]
93 |
94 | df = df.set_index(["time", "Enu"])
95 | times = df.index.levels[0].to_numpy()
96 | energies = df.index.levels[1].to_numpy()
97 | df = df.unstack("Enu")
98 | # make a 3d array
99 | self.array = np.stack([df[f] for f in Flavor], axis=0)
100 | self.interpolated = _interp_TE(
101 | times, energies, self.array, ax_t=1, ax_e=2
102 | )
103 | super().__init__(-times << u.hour, self.metadata)
104 |
105 | def get_initial_spectra(self, t, E, flavors=Flavor):
106 | t = np.array(-t.to_value("hour"), ndmin=1)
107 | E = np.array(E.to_value("MeV"), ndmin=1)
108 | flux = self.interpolated(t, E) / (u.MeV * u.s)
109 | return {f: flux[f] for f in flavors}
110 |
111 | class Kato_2017(SupernovaModel):
112 | """Set up a presupernova model based on
113 | [Chinami Kato et al 2017 ApJ 848 48]
114 | """
115 | def __init__(self, path):
116 | fluxes = {}
117 | #reading the time steps values:
118 | times, step = np.loadtxt(self.request_file(f"{path}/total_nue/lightcurve_nue_all.dat"), usecols=[0, 3]).T
119 |
120 | file_base = {Flavor.NU_E: 'total_nue/spe_all',
121 | Flavor.NU_E_BAR: 'total_nueb/spe_all',
122 | Flavor.NU_MU: 'total_nux/spe_sum_mu_nu',
123 | Flavor.NU_MU_BAR: 'total_nux/spe_sum_mu',
124 | Flavor.NU_TAU: 'total_nux/spe_sum_mu_nu',
125 | Flavor.NU_TAU_BAR: 'total_nux/spe_sum_mu'
126 | }
127 | for flv,file_base in file_base.items():
128 | d2NdEdT = []
129 | for s in step:
130 | energies, dNdE = np.loadtxt(
131 | self.request_file(f"{path}/{file_base}{s:05.0f}.dat")
132 | ).T
133 | d2NdEdT += [dNdE]
134 | fluxes[flv] = np.stack(d2NdEdT)
135 | self.array = np.stack([fluxes[f] for f in Flavor], axis=0)
136 | self.interpolated = _interp_TE(
137 | times, energies, self.array, ax_t=1, ax_e=2
138 | )
139 | super().__init__(-times << u.s, self.metadata)
140 |
141 | def get_initial_spectra(self, t, E, flavors=Flavor):
142 | t = np.array(-t.to_value("s"), ndmin=1)
143 | E = np.array(E.to_value("MeV"), ndmin=1)
144 | flux = self.interpolated(t, E) / (u.MeV * u.s)
145 | return {f: flux[f] for f in flavors}
146 |
147 | class Yoshida_2016(SupernovaModel):
148 | """Set up a presupernova model based on
149 | [Yoshida et al. (2016), PRD 93, 123012.]
150 | """
151 | def __init__(self, filename):
152 | with open(self.request_file(filename)) as f:
153 | data = []
154 | T = []
155 | while (line := f.readline()):
156 | if not line: break
157 | T += [float(line.split()[1])]
158 | data += [[np.loadtxt(f, max_rows=100).flatten() for i in range(4)]]
159 | times = np.array(T)
160 | energies = np.concatenate([
161 | np.linspace(0,10,1001)[1:],
162 | np.linspace(10,20,501)[1:]
163 | ])
164 | dNdEdT = np.stack(data, axis=1)
165 | #rearrange flavors from NU_E, NU_E_BAR,_NU_X, NU_X_BAR to current
166 | indices = np.argsort([Flavor.NU_E,Flavor.NU_E_BAR, Flavor.NU_X, Flavor.NU_X_BAR])
167 | dNdEdT = dNdEdT.take(indices, axis=0)
168 |
169 | self.interpolated = _interp_TE(
170 | times, energies, dNdEdT, ax_t=1, ax_e=2
171 | )
172 | super().__init__(-times << u.s, self.metadata)
173 |
174 | def get_initial_spectra(self, t, E, flavors=Flavor):
175 | t = np.array(-t.to_value("s"), ndmin=1)
176 | E = np.array(E.to_value("MeV"), ndmin=1)
177 | flux = self.interpolated(t, E) / (u.MeV * u.s)
178 | return {f: flux[f] for f in flavors}
179 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/Analytic.py:
--------------------------------------------------------------------------------
1 | """This model stub allows one to generate simple analytic models and then
2 | read them into the Analytic3Species model found in model.py which is part of SNEWPY.
3 | """
4 |
5 | from astropy.table import Table
6 | import numpy as np
7 |
8 | total_energy = (5e52,5e52,2e53)
9 | mean_energy = (15., 15., 15.)
10 | rms_or_pinch = "pinch"
11 | pinch_values = (2.3, 2.3, 2.3)
12 | # Alternative parameters, for rms. Uncomment the following 2 lines:
13 | #rms_or_pinch = "rms"
14 | #rms_energy = (280, 280, 280)
15 |
16 | file_name = "analytic.dat"
17 |
18 | table = Table()
19 | table['TIME'] = np.linspace(0,1,2)
20 | table['L_NU_E'] = np.linspace(1,1,2)*total_energy[0]
21 | table['L_NU_E_BAR'] = np.linspace(1,1,2)*total_energy[1]
22 | table['L_NU_X'] = np.linspace(1,1,2)*total_energy[2]
23 |
24 | table['E_NU_E'] = np.linspace(1,1,2)*mean_energy[0]
25 | table['E_NU_E_BAR'] = np.linspace(1,1,2)*mean_energy[1]
26 | table['E_NU_X'] = np.linspace(1,1,2)*mean_energy[2]
27 |
28 | if rms_or_pinch == "rms":
29 | table['RMS_NU_E'] = np.linspace(1,1,2)*rms_energy[0]
30 | table['RMS_NU_E_BAR'] = np.linspace(1,1,2)*rms_energy[1]
31 | table['RMS_NU_X'] = np.linspace(1,1,2)*rms_energy[2]
32 | table['ALPHA_NU_E'] = (2.0 * table['E_NU_E'] ** 2 - table['RMS_NU_E'] ** 2) / (
33 | table['RMS_NU_E'] ** 2 - table['E_NU_E'] ** 2)
34 | table['ALPHA_NU_E_BAR'] = (2.0 * table['E_NU_E_BAR'] ** 2 - table['RMS_NU_E_BAR'] ** 2) / (
35 | table['RMS_NU_E_BAR'] ** 2 - table['E_NU_E_BAR'] ** 2)
36 | table['ALPHA_NU_X'] = (2.0 * table['E_NU_X'] ** 2 - table['RMS_NU_X'] ** 2) / (
37 | table['RMS_NU_X'] ** 2 - table['E_NU_X'] ** 2)
38 | elif rms_or_pinch == "pinch":
39 | table['ALPHA_NU_E'] = np.linspace(1,1,2)*pinch_values[0]
40 | table['ALPHA_NU_E_BAR'] = np.linspace(1,1,2)*pinch_values[1]
41 | table['ALPHA_NU_X'] = np.linspace(1,1,2)*pinch_values[2]
42 | table['RMS_NU_E'] = (2.0 + table['ALPHA_NU_E'])/(1.0 + table['ALPHA_NU_E'])*table['E_NU_E']**2
43 | table['RMS_NU_E_BAR'] = (2.0 + table['ALPHA_NU_E_BAR'])/(1.0 + table['ALPHA_NU_E_BAR'])*table['E_NU_E_BAR']**2
44 | table['RMS_NU_X'] = (2.0 + table['ALPHA_NU_X'])/(1.0 + table['ALPHA_NU_X'])*table['E_NU_X']**2
45 | else:
46 | print("incorrect second moment method: rms or pinch")
47 | table.write(file_name,format='ascii')
48 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/Convert_to_ROOT.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """ This module allows to gather the SNOwGLoBES outputs into a ROOT files with 2D histograms.
3 | These 2D histograms show the rate as a function of time and energy for any given interaction channel.
4 | There is one ROOT file per detector, per smearing option, and per weighting option.
5 | Generate a tarball with the input files by running
6 | snowglobes.generate_time_series(...)
7 | snowglobes.simulate(...)
8 | snowglobes.collate(...)
9 |
10 | Then, to run the code, use:
11 | python Convert_to_ROOT.py
12 | """
13 |
14 | import tarfile
15 | from pathlib import Path
16 | from tempfile import TemporaryDirectory
17 | import argparse
18 |
19 | import numpy as np
20 |
21 | import ROOT
22 |
23 | # Get the path to input files (command line argument). Default is current folder
24 | parser = argparse.ArgumentParser()
25 | parser.add_argument('path', help='Path to tarball containing collated SNEWPY outputs')
26 | args = parser.parse_args()
27 |
28 | tarball_path = args.path
29 |
30 | # Iterate over collated SNEWPY files and gather all time bins
31 | outputnames = {}
32 | hist_dict = {}
33 | tmin,tmax,ntbins = 0,0,0
34 | column_names = []
35 | with TemporaryDirectory(prefix='snowglobes') as tempdir:
36 | #Extracts data from tarfile and sets up lists of paths and fluxfilenames for later use
37 | with tarfile.open(tarball_path) as tar:
38 | tar.extractall(tempdir)
39 |
40 | flux_files = list(Path(tempdir).glob('*/Collated*.dat'))
41 | first = True
42 | for flux_file in flux_files:
43 | flux_root = flux_file.stem
44 | if first:
45 | # Time boundaries are formatted to be %0.3f in generate_time_series
46 | tmin,tmax,ntbins = flux_root.split(',')
47 | tmin = float(".".join(tmin.split('.')[-2:]))
48 | tmax = float(tmax)
49 | ntbins = int(ntbins.split('-')[0])
50 | print(f"Time binning is: tmin={tmin}, tmax={tmax}, ntbins={ntbins}")
51 | first = False
52 | # Get column names
53 | with open(flux_file) as fin:
54 | column_names = fin.readline().split()[1:]
55 | # Find general name for flux file as well as value of time bin
56 | flux_start, flux_end = flux_root.split('tbin')
57 | flux_split = flux_end.split('.')
58 | time_bin = int(flux_split[0])
59 | time_val = (time_bin - 0.5) * (tmax - tmin)/ntbins + tmin
60 | flux_end = ".".join(flux_split[1:])
61 | flux_root = flux_start + flux_end
62 | # Get data from file
63 | data = np.loadtxt(flux_file, skiprows=2)
64 | energies = data[:, 0]
65 | data = data[:, 1:]
66 | # Initialize histograms and open files
67 | file_name = f"{Path(tarball_path).parent}/{flux_root}.root"
68 | energy_step = 0.5 * (energies[1] - energies[0])
69 | if file_name not in outputnames.keys():
70 | print(f"Creating file {file_name}")
71 | rootfile = ROOT.TFile.Open(file_name, "RECREATE")
72 | outputnames[file_name] = rootfile
73 | for channel in column_names:
74 | histname = f"{flux_root}_{channel}"
75 | hist_dict[histname] = ROOT.TH2D(channel, f"Rates for {channel} interaction",
76 | len(energies), energies[0] - energy_step,
77 | energies[-1] + energy_step,
78 | ntbins, tmin, tmax)
79 | hist_dict[histname].SetDirectory(rootfile)
80 | # Fill histogram with table
81 | for energ,line in zip(energies,data):
82 | for l,c in zip(line,column_names):
83 | histname = f"{flux_root}_{c}"
84 | hist_dict[histname].Fill(energ,time_val,l)
85 | # Save data and close ROOT files
86 | print("Closing files...")
87 | for fname in outputnames:
88 | outputnames[fname].Write()
89 | outputnames[fname].Close()
90 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/SNEWS2.0_rate_table_singleexample.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from snewpy import snowglobes
3 |
4 | SNOwGLoBES_path = None # change to SNOwGLoBES directory if using a custom detector configuration
5 | SNEWPY_model_dir = "/path/to/snewpy/models/" # directory containing model input files
6 |
7 | distance = 10 # Supernova distance in kpc
8 | detector = "wc100kt30prct" #SNOwGLoBES detector for water Cerenkov
9 | modeltype = 'Bollig_2016' # Model type from snewpy.models
10 | model = 's11.2c' # Name of model
11 | transformation = 'AdiabaticMSW_NMO' # Desired flavor transformation
12 |
13 | # Construct file system path of model file and name of output file
14 | model_path = SNEWPY_model_dir + "/" + modeltype + "/" + model
15 | outfile = modeltype + "_" + model + "_" + transformation
16 |
17 | # Now, do the main work:
18 | print("Generating fluence files ...")
19 | tarredfile = snowglobes.generate_fluence(model_path, modeltype, transformation, distance, outfile)
20 |
21 | print("Simulating detector effects with SNOwGLoBES ...")
22 | snowglobes.simulate(SNOwGLoBES_path, tarredfile, detector_input=detector)
23 |
24 | print("Collating results ...")
25 | tables = snowglobes.collate(SNOwGLoBES_path, tarredfile, skip_plots=True)
26 |
27 |
28 | # Use results to print the number of events in different interaction channels
29 | key = f"Collated_{outfile}_{detector}_events_smeared_weighted.dat"
30 | total_events = 0
31 | for i, channel in enumerate(tables[key]['header'].split()):
32 | if i == 0:
33 | continue
34 | n_events = sum(tables[key]['data'][i])
35 | total_events += n_events
36 | print(f"{channel:10}: {n_events:.3f} events")
37 |
38 | #Super-K has 32kT inner volume
39 | print("Total events in Super-K-like detector:",0.32*total_events)
40 |
41 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/TimeSeries.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from snewpy import snowglobes
4 |
5 | SNOwGLoBES_path = None # change to SNOwGLoBES directory if using a custom detector configuration
6 |
7 | # arguments for generate_time_series
8 | model_file = "/path/to/snewpy/models/Nakazato_2013/nakazato-LS220-BH-z0.004-s30.0.fits"
9 | modeltype = 'Nakazato_2013'
10 | transformation = 'AdiabaticMSW_NMO'
11 | d = 10 # Supernova distance in kpc
12 |
13 | # Running the modules
14 | outfile = snowglobes.generate_time_series(model_file, modeltype, transformation, d)
15 | snowglobes.simulate(SNOwGLoBES_path, outfile, detector_input="icecube")
16 | snowglobes.collate(SNOwGLoBES_path, outfile)
17 |
18 | # An additional, optional argument in simulate() is the detector name, if one wants to only run 1 detector, rather than all of them.
19 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/make_rate_table_dict.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from astropy import units as u
3 | from snewpy import snowglobes
4 | from pprint import pprint
5 | from datetime import datetime
6 |
7 | def calc_total_rate(model_name, model_file,transform,detectors):
8 | #guess model name
9 | model_path = f'models/{model_name}/{model_file}'
10 | path = snowglobes.generate_fluence(model_path,model_name,transform,d=10)
11 | tables = snowglobes.simulate(None,path,detector_input=detectors)
12 | result = {}
13 | for det,table in tables.items():
14 | table = list(table.values())[0] #take the first and the only time bin
15 | result[det] = table['weighted']['smeared'].values.sum()
16 | return result
17 |
18 | def make_rate_table(detectors=['icecube','wc100kt30prct','ar40kt','novaND','novaFD','halo1','halo2']):
19 | result = {( model_name, model_file, xform):
20 | calc_total_rate(model_name,model_file,xform,detectors)
21 | for xform in ['AdiabaticMSW_NMO','AdiabaticMSW_IMO']
22 | for model_file in ['s11.2c','s27.0c']
23 | for model_name in ['Bollig_2016']
24 | }
25 | return result
26 |
27 | if __name__=='__main__':
28 | result = make_rate_table()
29 | #save the crosscheck table in a file
30 | print(f'#generated by "make_rate_table.py" on {str(datetime.now())}')
31 | print('rate_table= \\')
32 | pprint(result)
33 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/old_to_snowglobes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | snewpy.scripts.to_snowglobes
4 | ============================
5 |
6 | Convert an arbitrary model to SNOwGLoBES format. Based on SNEWPY.py script by
7 | E. O'Connor and J. P. Kneller.
8 |
9 | This version will subsample the times in a supernova model, produce energy
10 | tables expected by SNOwGLoBES, and compress the output into a tarfile.
11 | """
12 |
13 | import numpy as np
14 | from argparse import ArgumentParser
15 |
16 | import os
17 | import io
18 | import tarfile
19 |
20 | import logging
21 |
22 | from snewpy.models import *
23 | from snewpy.flavor_transformation import *
24 |
25 | def main(options=None):
26 | # Parse command-line arguments.
27 | p = ArgumentParser(description='Convert to SNOwGLoBES format.')
28 | p.add_argument('infile', nargs=1,
29 | help='Supernova model input file (Nakazato only).')
30 | p.add_argument('-o', '--output', default=None,
31 | help='Output tarfile name (if customization desired)')
32 |
33 | tbingroup = p.add_mutually_exclusive_group()
34 | tbingroup.add_argument('-n', '--ntbins', type=int,
35 | help='Number of bins used to sample model')
36 | tbingroup.add_argument('-t', '--deltat', type=float,
37 | help='Time binning used to sample model [sec]')
38 |
39 | p.add_argument('-v', '--verbose', action='store_true', default=False,
40 | help='Activate verbose log for debugging')
41 |
42 | if options is None:
43 | args = p.parse_args()
44 | else:
45 | args = p.parse_args(options)
46 |
47 | # Set verbosity of the log.
48 | if args.verbose:
49 | logging.basicConfig(level=logging.DEBUG)
50 | else:
51 | logging.basicConfig(level=logging.INFO)
52 |
53 | # Load up the model. To do: support more than Nakazato format.
54 | infile = args.infile[0]
55 | snmodel = Nakazato2013(infile, NoTransformation())
56 |
57 | # Subsample the model time. Default to 30 time slices.
58 | tmin = snmodel.get_time()[0]
59 | tmax = snmodel.get_time()[-1]
60 | if args.deltat is not None:
61 | dt = args.deltat
62 | elif args.ntbins is not None:
63 | dt = (tmax - tmin) / (args.ntbins+1)
64 | else:
65 | dt = (tmax - tmin) / 31
66 |
67 | tedges = np.arange(tmin, tmax, dt)
68 | times = 0.5*(tedges[1:] + tedges[:-1])
69 |
70 | # Generate output.
71 | if args.output is not None:
72 | tfname = args.output
73 | else:
74 | tfname = infile.replace('.fits', '.SNOformat.tar.bz2')
75 |
76 | with tarfile.open(tfname, 'w:bz2') as tf:
77 | d = 10. *1000.*3.086e+18 # luminosity to fluence
78 | keV = 1e3 * 1.60218e-12 # eV to erg
79 | MeV = 1e6 * 1.60218e-12
80 | GeV = 1e9 * 1.60218e-12
81 |
82 | energy = np.linspace(0, 100, 501) * MeV
83 |
84 | # Loop over sampled times.
85 | for i, t in enumerate(times):
86 | osc_spectra = snmodel.get_oscillatedspectra(t, energy)
87 | osc_fluence = {}
88 | table = []
89 |
90 | table.append('# TBinMid={:g}sec@(tBinWidth={:g}s)(eBinWidth=0.2MeV) Fluence in Number Neutrinos per cm^2'.format(t, dt))
91 | table.append('# E(GeV) NuE NuMu NuTau aNuE aNuMu aNuTau')
92 |
93 | # Generate energy + number flux table.
94 | for j, E in enumerate(energy):
95 | for flavor in Flavor:
96 | osc_fluence[flavor] = osc_spectra[flavor][j] * dt * 200.*keV / (4.*np.pi*d**2)
97 |
98 | s = '{:17.8E}'.format(E/GeV)
99 | s = '{}{:17.8E}'.format(s, osc_fluence[Flavor.nu_e])
100 | s = '{}{:17.8E}'.format(s, osc_fluence[Flavor.nu_x]/2)
101 | s = '{}{:17.8E}'.format(s, osc_fluence[Flavor.nu_x]/2)
102 | s = '{}{:17.8E}'.format(s, osc_fluence[Flavor.nu_e_bar])
103 | s = '{}{:17.8E}'.format(s, osc_fluence[Flavor.nu_x_bar]/2)
104 | s = '{}{:17.8E}'.format(s, osc_fluence[Flavor.nu_x_bar]/2)
105 | table.append(s)
106 | logging.debug(s)
107 |
108 | # Encode energy/flux table and output to file in tar archive.
109 | output = '\n'.join(table).encode('ascii')
110 |
111 | infoname = '{:02d}Tbins/{}-tbin{:02d}.NoOsc.dat'.format(
112 | len(times),
113 | os.path.basename(infile).replace('.fits', ''),
114 | i + 1)
115 | info = tarfile.TarInfo(name=infoname)
116 | info.size = len(output)
117 |
118 | logging.info('Time {:g} s; writing {} to {}'.format(t, infoname, tfname))
119 | tf.addfile(info, io.BytesIO(output))
120 |
121 |
--------------------------------------------------------------------------------
/python/snewpy/scripts/snewpy_to_snewpdag.py:
--------------------------------------------------------------------------------
1 |
2 | from snewpy import snowglobes
3 | import numpy as np
4 | from astropy import units as u
5 |
6 | SNOwGLoBES_path = None # change to SNOwGLoBES directory if using a custom detector configuration
7 | SNEWPY_models_base = "/location/of/models/" #where models (aka input for to_snowglobes) is located
8 | output_path = "/path/to/output/" #where the output files will be located
9 |
10 | #set distance in kpc
11 | distance=10
12 |
13 | #set SNOwGLoBES detector to use
14 | detector = "icecube"
15 |
16 | #set SNEWPY model type and filename
17 | modeltype = 'Tamborra_2014'
18 | modeldir = SNEWPY_models_base+"/"+modeltype+"/"
19 | model = 's20.0c_3D_dir1'
20 | filename = model
21 |
22 | #set desired flavor transformation prescription
23 | transformation = 'AdiabaticMSW_NMO'
24 |
25 | #snewpy.snowglobes creates a tarred file of snowglobes fluences
26 | #this is stored in the model types directory in snewpy/models
27 | outfile = modeltype+"_"+model+"_"+transformation
28 |
29 | #Specify sequence of time intervals, one fluence
30 | #file is made for each elements with dt=tstart[i]-tend[i]
31 | window_tstart = 0.001
32 | window_tend = 0.331
33 | window_bins = 330
34 | tstart = np.linspace(window_tstart,window_tend,window_bins,endpoint=False)*u.s
35 | tend = tstart + (window_tend-window_tstart)/window_bins*u.s
36 | tmid = (tstart+tend)*0.5
37 |
38 | #Generate fluence file for SNOwGLoBES (there are two options, here we use generate_fluence)
39 | print("Preparing fluences...")
40 | tarredfile = snowglobes.generate_fluence(modeldir+filename, modeltype, transformation, distance, outfile,tstart,tend)
41 | print("Done fluences...")
42 |
43 | print("Running snowglobes...")
44 | #now run SNOwGLoBES, this will loop over all the fluence files in `tarredfile`
45 | snowglobes.simulate(SNOwGLoBES_path, tarredfile, detector_input=detector)
46 | print("Done snowglobes...")
47 |
48 | #now collate results of output of SNOwGLoBES
49 | print("Collating...")
50 | tables = snowglobes.collate(SNOwGLoBES_path, tarredfile, skip_plots=True)
51 |
52 | #read results from SNOwGLoBES and put lightcurve in output file for snewpdag
53 | fout = open(output_path+"snewpy_output_"+detector+"_"+modeltype+"_"+filename+"_1msbin.txt", "a")
54 | nevents = np.zeros(len(tmid))
55 | for i in range(len(tmid)):
56 | key = "Collated_"+outfile+"_"+str(i)+"_"+detector+"_events_smeared_weighted.dat"
57 | for j in range(1,len(tables[key]['header'].split())):
58 | nevents[i] += sum(tables[key]['data'][j])
59 | print(i, "\t" , nevents[i], file=fout)
60 |
--------------------------------------------------------------------------------
/python/snewpy/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SNEWS2/snewpy/dc4c2c76235e3208ff554967180b24fe65173078/python/snewpy/test/__init__.py
--------------------------------------------------------------------------------
/python/snewpy/test/_rate_crosscheck_table.py:
--------------------------------------------------------------------------------
1 | #generated by "make_rate_table.py" on 2023-06-02 14:05:49.504799
2 | rate_table= \
3 | {('Bollig_2016', 's11.2c', 'AdiabaticMSW_IMO'): {'ar40kt': 2509.8697955822163,
4 | 'halo1': 3.7370536263087892,
5 | 'halo2': 47.30447628238974,
6 | 'icecube': 332587.17107218195,
7 | 'novaFD': 1955.8019842893325,
8 | 'novaND': 41.91004252048569,
9 | 'wc100kt30prct': 12723.21134007678},
10 | ('Bollig_2016', 's11.2c', 'AdiabaticMSW_NMO'): {'ar40kt': 2696.396378383758,
11 | 'halo1': 4.261668358305819,
12 | 'halo2': 53.94516909247873,
13 | 'icecube': 320766.2999953658,
14 | 'novaFD': 1947.9479073661355,
15 | 'novaND': 41.741740872131466,
16 | 'wc100kt30prct': 12645.788228369147},
17 | ('Bollig_2016', 's27.0c', 'AdiabaticMSW_IMO'): {'ar40kt': 5192.001233928208,
18 | 'halo1': 8.180298088573792,
19 | 'halo2': 103.54807707055436,
20 | 'icecube': 659449.9649827218,
21 | 'novaFD': 3638.7825760092305,
22 | 'novaND': 77.97391234305493,
23 | 'wc100kt30prct': 23596.070817047243},
24 | ('Bollig_2016', 's27.0c', 'AdiabaticMSW_NMO'): {'ar40kt': 5520.903869068218,
25 | 'halo1': 9.105675711861647,
26 | 'halo2': 115.2617178716664,
27 | 'icecube': 661517.4858901916,
28 | 'novaFD': 3734.4927394154874,
29 | 'novaND': 80.02484441604615,
30 | 'wc100kt30prct': 24241.28672972421}}
31 |
--------------------------------------------------------------------------------
/python/snewpy/test/simplerate_integrationtest.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Integration test based on SNEWS2.0_rate_table_singleexample.py
3 | """
4 | import unittest
5 | from snewpy import snowglobes
6 | from snewpy import model_path
7 |
8 | from snewpy.models import ccsn
9 | import astropy.units as u
10 |
11 | def preload_model(name:str, **parameters):
12 | #initialize the model with given name and parameters
13 | model = ccsn.__dict__[name](**parameters)
14 | return model
15 |
16 |
17 | class TestSimpleRate(unittest.TestCase):
18 |
19 | def test_simplerate(self):
20 | """Integration test based on SNEWS2.0_rate_table_singleexample.py
21 | """
22 | # Hardcoded paths on GitHub Action runner machines
23 | SNOwGLoBES_path = None
24 |
25 | distance = 10 # Supernova distance in kpc
26 | detector = "wc100kt30prct" #SNOwGLoBES detector for water Cerenkov
27 | modeltype = 'Bollig_2016' # Model type from snewpy.models
28 | model = 's11.2c' # Name of model
29 | transformation = 'AdiabaticMSW_NMO' # Desired flavor transformation
30 |
31 | # Construct file system path of model file and name of output file
32 | model_file_path = f'{model_path}/{modeltype}/{model}'
33 | outfile = f'{modeltype}_{model}_{transformation}'
34 |
35 | #make sure the model files are loaded
36 | preload_model(modeltype, progenitor_mass=11.2*u.Msun)
37 |
38 | # Now, do the main work:
39 | print("Generating fluence files ...")
40 | tarredfile = snowglobes.generate_fluence(model_file_path, modeltype, transformation, distance, outfile)
41 |
42 | print("Simulating detector effects with SNOwGLoBES ...")
43 | snowglobes.simulate(SNOwGLoBES_path, tarredfile, detector_input=detector, detector_effects=True)
44 |
45 | print("Collating results ...")
46 | tables = snowglobes.collate(SNOwGLoBES_path, tarredfile, skip_plots=True, smearing=True)
47 |
48 | # Use results to print the number of events in different interaction channels
49 | key = f"Collated_{outfile}_{detector}_events_unsmeared_weighted.dat"
50 | total_events = 0
51 | for i, channel in enumerate(tables[key]['header'].split()):
52 | if i == 0:
53 | continue
54 | n_events = sum(tables[key]['data'][i])
55 | total_events += n_events
56 | print(f"{channel:10}: {n_events:.3f} events")
57 |
58 | #Super-K has 32kT inner volume
59 | print("Total events in Super-K-like detector:" , 0.32*total_events)
60 |
61 | # Use results to print the number of events in different interaction channels
62 | # with efficiency and smearing
63 | key = f"Collated_{outfile}_{detector}_events_smeared_weighted.dat"
64 | total_events_smeared = 0
65 | for i, channel in enumerate(tables[key]['header'].split()):
66 | if i == 0:
67 | continue
68 | n_events = sum(tables[key]['data'][i])
69 | total_events_smeared += n_events
70 | print(f"{channel:10}: {n_events:.3f} events (smeared)")
71 |
72 | #Super-K has 32kT inner volume
73 | print("Total events in Super-K-like detector (with smearing):" , 0.32*total_events_smeared)
74 |
75 | # We do not use the SNOwGLoBES scaling factors but use other constants so we do not
76 | # expect the results to agree to 7 digits. Here sub-permille agreement is good enough.
77 | sk_expected = 4491.783259
78 | sk_expected_smeared = 4065.662374
79 | sk_computed = 0.32 * total_events
80 | sk_computed_smeared = 0.32 * total_events_smeared
81 | discrepancy = abs(sk_computed - sk_expected)/sk_expected
82 | discrepancy_smeared = abs(sk_computed_smeared - sk_expected_smeared)/sk_expected_smeared
83 |
84 | assert discrepancy < 0.001, f"Number of unsmeared events computed for SK is {sk_computed}, should be {sk_expected}"
85 | assert discrepancy_smeared < 0.001, f"Number of smeared events computed for SK is {sk_computed_smeared}, should be {sk_expected_smeared}"
86 |
--------------------------------------------------------------------------------
/python/snewpy/test/test_00_init.py:
--------------------------------------------------------------------------------
1 | import snewpy
2 | import unittest
3 |
4 |
5 | class TestInit(unittest.TestCase):
6 | def test_version_exists(self):
7 | self.assertTrue(hasattr(snewpy, '__version__'))
--------------------------------------------------------------------------------
/python/snewpy/test/test_03_neutrino.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Unit tests for the neutrino submodule.
3 | """
4 |
5 | import unittest
6 |
7 | from snewpy.neutrino import Flavor, MassHierarchy, MixingParameters
8 |
9 | from astropy import units as u
10 |
11 | class TestNeutrino(unittest.TestCase):
12 |
13 | def test_flavor(self):
14 | """
15 | Neutrino flavor types
16 | """
17 | nue = Flavor.NU_E
18 | self.assertTrue(nue.is_electron)
19 | self.assertTrue(nue.is_neutrino)
20 | self.assertFalse(nue.is_antineutrino)
21 |
22 | numu = Flavor.NU_MU
23 | self.assertFalse(numu.is_electron)
24 | self.assertTrue(numu.is_neutrino)
25 | self.assertFalse(numu.is_antineutrino)
26 |
27 | nueb = Flavor.NU_E_BAR
28 | self.assertTrue(nueb.is_electron)
29 | self.assertFalse(nueb.is_neutrino)
30 | self.assertTrue(nueb.is_antineutrino)
31 |
32 | numub = Flavor.NU_MU_BAR
33 | self.assertFalse(numub.is_electron)
34 | self.assertFalse(numub.is_neutrino)
35 | self.assertTrue(numub.is_antineutrino)
36 |
37 |
38 | def test_mixing_nmo(self):
39 | """
40 | Mixing parameter values; NMO
41 | """
42 | # By default, return mixing parameters for NMO.
43 | mixpars = MixingParameters(version="NuFIT5.0")
44 | self.assertEqual(mixpars.theta12, 33.44 * u.deg)
45 | self.assertEqual(mixpars.theta13, 8.57 * u.deg)
46 | self.assertEqual(mixpars.theta23, 49.20 * u.deg)
47 | self.assertEqual(mixpars.deltaCP, 197 * u.deg)
48 | self.assertEqual(mixpars.dm21_2, 7.42e-5 * u.eV**2)
49 | self.assertEqual(mixpars.dm31_2, 2.517e-3 * u.eV**2)
50 |
51 |
52 | def test_mixing_imo(self):
53 | """
54 | Mixing parameter values; IMO
55 | """
56 | mixpars = MixingParameters(MassHierarchy.INVERTED, version="NuFIT5.0")
57 | self.assertEqual(mixpars.theta12, 33.45 * u.deg)
58 | self.assertEqual(mixpars.theta13, 8.60 * u.deg)
59 | self.assertEqual(mixpars.theta23, 49.30 * u.deg)
60 | self.assertEqual(mixpars.deltaCP, 282 * u.deg)
61 | self.assertEqual(mixpars.dm21_2, 7.42e-5 * u.eV**2)
62 | self.assertEqual(mixpars.dm32_2, -2.498e-3 * u.eV**2)
63 |
--------------------------------------------------------------------------------
/python/snewpy/test/test_05_snowglobes.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pathlib import Path
3 | from snewpy.test._rate_crosscheck_table import rate_table
4 | from snewpy import snowglobes, model_path
5 |
6 | from snewpy.models import ccsn
7 | import astropy.units as u
8 |
9 | pytestmark=pytest.mark.snowglobes
10 |
11 | #get available model parameters from table
12 | param_values = list(rate_table.keys())
13 | #get available detectors from table
14 | detectors = list(list(rate_table.values())[0].keys())
15 |
16 | #make sure the model files are loaded
17 | model = ccsn.Bollig_2016
18 | for params in model.get_param_combinations():
19 | model(**params)
20 |
21 | def fluence_calculation(model_name,model_file,transform):
22 | #generating fluence file
23 | model_file_path = f'{model_path}/{model_name}/{model_file}'
24 | print(model_file_path)
25 | return snowglobes.generate_fluence(model_file_path, model_name, transform,d=10)
26 |
27 | def rates_calculation(fluence_file):
28 | tables = snowglobes.simulate(None,fluence_file,detector_input=detectors)
29 | result = {}
30 | for det,table in tables.items():
31 | table = list(table.values())[0] #take the first and the only time bin
32 | result[det] = table['weighted']['smeared'].values.sum()
33 | return result
34 |
35 | @pytest.mark.xfail(reason="Rate calculation uses `get_transformed_flux`, which is currently hard coded to a TwoFlavor scheme.", raises=AttributeError)
36 | @pytest.mark.parametrize('model_parameters',param_values)
37 | def test_total_rate_equals_table_value(model_parameters):
38 | fluence_file = fluence_calculation(*model_parameters)
39 | calculated_rates = rates_calculation(fluence_file)
40 | for detector in detectors:
41 | expected = pytest.approx(rate_table[model_parameters][detector], rel=0.01)
42 | assert calculated_rates[detector] == expected, f"Crosscheck failed for {detector}"
43 |
--------------------------------------------------------------------------------
/python/snewpy/test/test_flavors.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import numpy as np
3 | import snewpy.flavor
4 | from snewpy.flavor import TwoFlavor,ThreeFlavor,FourFlavor, FlavorMatrix, FlavorScheme
5 |
6 | flavor_schemes = TwoFlavor,ThreeFlavor,FourFlavor
7 |
8 | class TestFlavorScheme:
9 | @staticmethod
10 | def test_flavor_scheme_lengths():
11 | assert len(TwoFlavor)==4
12 | assert len(ThreeFlavor)==6
13 | assert len(FourFlavor)==8
14 |
15 | @staticmethod
16 |
17 | def test_getitem_string():
18 | assert TwoFlavor['NU_E'] == TwoFlavor.NU_E
19 | assert TwoFlavor['NU_X'] == TwoFlavor.NU_X
20 | #short notations
21 | assert ThreeFlavor['E'] == ThreeFlavor['e'] == ThreeFlavor.NU_E
22 | assert ThreeFlavor['MU'] == ThreeFlavor['mu'] == ThreeFlavor.NU_MU
23 | assert ThreeFlavor['MU_BAR'] == ThreeFlavor['mu_bar'] == ThreeFlavor.NU_MU_BAR
24 | with pytest.raises(KeyError):
25 | TwoFlavor['NU_MU']
26 | with pytest.raises(KeyError):
27 | ThreeFlavor['NU_X']
28 | @staticmethod
29 | def test_getitem_collective_names():
30 | assert ThreeFlavor['NU']==(ThreeFlavor.NU_E, ThreeFlavor.NU_MU, ThreeFlavor.NU_TAU)
31 | assert ThreeFlavor['NU']==ThreeFlavor['e','mu','tau']
32 | assert ThreeFlavor['NU_BAR']==(ThreeFlavor.NU_E_BAR, ThreeFlavor.NU_MU_BAR, ThreeFlavor.NU_TAU_BAR)
33 | assert ThreeFlavor['NU_BAR']==ThreeFlavor['e_bar','mu_bar','tau_bar']
34 | @staticmethod
35 | def test_getitem_enum():
36 | assert TwoFlavor[TwoFlavor.NU_E] == TwoFlavor.NU_E
37 | assert TwoFlavor[TwoFlavor.NU_X] == TwoFlavor.NU_X
38 | with pytest.raises(TypeError):
39 | TwoFlavor[ThreeFlavor.NU_E]
40 |
41 | @staticmethod
42 | def test_values_from_different_enums():
43 | assert TwoFlavor.NU_E==ThreeFlavor.NU_E
44 | assert TwoFlavor.NU_E_BAR==ThreeFlavor.NU_E_BAR
45 |
46 | @staticmethod
47 | def test_makeFlavorScheme():
48 | TestFlavor = FlavorScheme.from_lepton_names('TestFlavor',leptons=['A','B','C'])
49 | assert len(TestFlavor)==6
50 | assert [f.name for f in TestFlavor]==['NU_A','NU_A_BAR','NU_B','NU_B_BAR','NU_C','NU_C_BAR']
51 |
52 | @staticmethod
53 | def test_flavor_properties():
54 | f = ThreeFlavor.NU_E
55 | assert f.is_neutrino
56 | assert f.is_electron
57 | assert not f.is_muon
58 | assert not f.is_tauon
59 | assert f.lepton=='E'
60 |
61 | f = ThreeFlavor.NU_MU
62 | assert f.is_neutrino
63 | assert not f.is_electron
64 | assert f.is_muon
65 | assert not f.is_tauon
66 | assert f.lepton=='MU'
67 |
68 | f = ThreeFlavor.NU_E_BAR
69 | assert not f.is_neutrino
70 | assert f.is_electron
71 | assert not f.is_muon
72 | assert not f.is_tauon
73 | assert f.lepton=='E'
74 |
75 | f = ThreeFlavor.NU_MU_BAR
76 | assert not f.is_neutrino
77 | assert not f.is_electron
78 | assert f.is_muon
79 | assert not f.is_tauon
80 | assert f.lepton=='MU'
81 |
82 | f = ThreeFlavor.NU_TAU
83 | assert f.is_neutrino
84 | assert not f.is_electron
85 | assert not f.is_muon
86 | assert f.is_tauon
87 | assert f.lepton=='TAU'
88 |
89 | f = ThreeFlavor.NU_TAU_BAR
90 | assert not f.is_neutrino
91 | assert not f.is_electron
92 | assert not f.is_muon
93 | assert f.is_tauon
94 | assert f.lepton=='TAU'
95 |
96 | class TestFlavorMatrix:
97 | @staticmethod
98 | def test_init_square_matrix():
99 | m = FlavorMatrix(array=np.ones(shape=(4,4)), flavor=TwoFlavor)
100 | assert m.shape == (4,4)
101 | assert m.flavor_in == TwoFlavor
102 | assert m.flavor_out == TwoFlavor
103 |
104 | @staticmethod
105 | def test_getitem():
106 | m = FlavorMatrix.eye(TwoFlavor,TwoFlavor)
107 | assert m[TwoFlavor.NU_E, TwoFlavor.NU_E]==1
108 | assert m['NU_E','NU_E']==1
109 | assert m['NU_E','NU_X']==0
110 | assert np.allclose(m['NU_E'], [1,0,0,0])
111 | assert np.allclose(m['NU_E'], m['NU_E',:])
112 |
113 | @staticmethod
114 | def test_getitem_submatrix():
115 | m = FlavorMatrix.eye(TwoFlavor)
116 | assert np.allclose(m[['e','x'],['e','x']], [[1,0],[0,1]])
117 | assert np.allclose(m[:,:].array, m.array)
118 |
119 | @staticmethod
120 | def test_getitem_short():
121 | m = FlavorMatrix.eye(ThreeFlavor,ThreeFlavor)
122 | assert m['NU_E','NU_E']==m['e','e']
123 | assert m['NU_MU','NU_E']==m['mu','e']
124 | assert m['NU_E_BAR','NU_E']==m['e_bar','e']
125 | assert m['NU_TAU_BAR','NU_TAU']==m['tau_bar','tau']
126 |
127 | @staticmethod
128 | def test_setitem():
129 | m = FlavorMatrix.eye(TwoFlavor,TwoFlavor)
130 | m['NU_E']=[2,3,4,5]
131 | assert m['NU_E','NU_E']==2
132 | assert m['NU_E','NU_X']==4
133 | #check that nothing changed in other parts
134 | assert m['NU_X','NU_E']==0
135 | assert m['NU_X','NU_X']==1
136 | m['NU_E','NU_E_BAR']=123
137 | assert m['NU_E','NU_E_BAR']==123
138 |
139 | @staticmethod
140 | def test_init_square_matrix_with_wrong_shape_raises_ValueError():
141 | with pytest.raises(ValueError):
142 | m = FlavorMatrix(array=np.ones(shape=(4,5)), flavor=TwoFlavor)
143 | with pytest.raises(ValueError):
144 | m = FlavorMatrix(array=np.ones(shape=(5,5)), flavor=TwoFlavor)
145 | with pytest.raises(ValueError):
146 | m = FlavorMatrix(array=np.ones(shape=(5,4)), flavor=TwoFlavor)
147 |
148 | @staticmethod
149 | def test_conversion_matrices_for_same_flavor_are_unity():
150 | for flavor in [TwoFlavor,ThreeFlavor,FourFlavor]:
151 | matrix = flavor>>flavor
152 | assert isinstance(matrix, FlavorMatrix)
153 | assert np.allclose(matrix.array, np.eye(len(flavor)))
154 |
155 | @staticmethod
156 | @pytest.mark.parametrize('flavor_in',flavor_schemes)
157 | @pytest.mark.parametrize('flavor_out',flavor_schemes)
158 | def test_conversion_matrices(flavor_in, flavor_out):
159 | M = flavor_in>>flavor_out
160 | assert M==flavor_out<1)
133 | note(f'Array is {str(f.array)}')
134 | fI = f.integrate(axis)
135 | for a in Axes:
136 | if a!=axis:
137 | assert fI.shape[a] == f.shape[a]
138 | else:
139 | assert fI.shape[a] == 1
140 | #check the resulting array values
141 | assert fI.unit == f.unit*f.axes[axis].unit
142 |
143 | @given(f=random_flux_containers())
144 | def test_save_and_load(f):
145 | with TemporaryDirectory() as tmpdir:
146 | fname = os.path.join(tmpdir, 'flux.npz')
147 | f.save(fname)
148 | f1 = Container.load(fname)
149 | assert f1 == f
--------------------------------------------------------------------------------
/python/snewpy/test/test_presn_rates.py:
--------------------------------------------------------------------------------
1 | import astropy.units as u
2 | import numpy as np
3 | from snewpy.models import presn
4 | from snewpy.neutrino import MassHierarchy, MixingParameters
5 | from snewpy.flavor_transformation import AdiabaticMSW
6 | from snewpy.rate_calculator import RateCalculator
7 | import pytest
8 |
9 | pytestmark=pytest.mark.snowglobes
10 |
11 | rc = RateCalculator()
12 |
13 | distance = 200*u.pc
14 | #SNOwGLoBES detector for water Cerenkov
15 | T = np.geomspace(-1*u.hour, -1*u.min,1000)
16 | E = np.linspace(0,20,100)*u.MeV
17 |
18 | @pytest.mark.xfail(reason="`model.get_flux` uses `get_transformed_flux`, which is currently hard coded to a TwoFlavor scheme.", raises=AttributeError)
19 | @pytest.mark.parametrize('model_class',[presn.Odrzywolek_2010, presn.Kato_2017, presn.Patton_2017, presn.Yoshida_2016])
20 | @pytest.mark.parametrize('transformation',[AdiabaticMSW(MixingParameters(mh)) for mh in MassHierarchy])
21 | @pytest.mark.parametrize('detector', ["wc100kt30prct"])
22 | def test_presn_rate(model_class, transformation, detector):
23 | model = model_class(progenitor_mass=15*u.Msun)
24 | flux = model.get_flux(T, E, distance=distance, flavor_xform=transformation)
25 | rate = rc.run(flux, detector='scint20kt', detector_effects=False)['ibd']
26 | ibd_events = rate.integrate_or_sum('time').integrate_or_sum('energy').array.squeeze()
27 | assert 10np.ndarray:
4 | """Expand the dimensions of the array, adding dimensions of len=1 to the right,
5 | so total dimensions equal to `ndim`"""
6 | new_shape = (list(a.shape)+[1]*ndim)[:ndim]
7 | return a.reshape(new_shape)
--------------------------------------------------------------------------------