├── fig └── .gitignore ├── img ├── prob_3nu_vacuum_vs_energy_ee_em_et.png └── prob_3nu_vacuum_vs_baseline_ee_em_et.png ├── test ├── example_2nu_trivial.py ├── example_3nu_trivial.py ├── example_2nu_vacuum.py ├── example_2nu_vacuum_coeffs.py ├── example_3nu_matter.py ├── example_3nu_vacuum.py ├── example_3nu_liv.py ├── example_3nu_nsi.py ├── example_3nu_vacuum_coeffs.py ├── oscprob3nu_plotpaper.py ├── oscprob2nu_plotpaper.py ├── oscprob2nu_plot.py └── oscprob3nu_plot.py ├── LICENSE ├── src ├── oscprob2nu.py ├── globaldefs.py ├── hamiltonians2nu.py ├── hamiltonians3nu.py └── oscprob3nu.py ├── run_testsuite.py └── README.md /fig/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /img/prob_3nu_vacuum_vs_energy_ee_em_et.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbustama/NuOscProbExact/HEAD/img/prob_3nu_vacuum_vs_energy_ee_em_et.png -------------------------------------------------------------------------------- /img/prob_3nu_vacuum_vs_baseline_ee_em_et.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbustama/NuOscProbExact/HEAD/img/prob_3nu_vacuum_vs_baseline_ee_em_et.png -------------------------------------------------------------------------------- /test/example_2nu_trivial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the trivial 2nu example shown in README.md. 3 | 4 | Runs the trivial two-neutrino example shown in README.md 5 | 6 | References 7 | ---------- 8 | 9 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 10 | a fast general-purpose computation method for two and three neutrino 11 | flavors", arXiv:1904.XXXXX. 12 | 13 | Created: 2019/04/29 23:22 14 | Last modified: 2019/04/29 23:22 15 | """ 16 | 17 | 18 | from __future__ import print_function 19 | 20 | __version__ = "1.0" 21 | __author__ = "Mauricio Bustamante" 22 | __email__ = "mbustamante@gmail.com" 23 | 24 | 25 | import sys 26 | sys.path.append('../src') 27 | 28 | import oscprob2nu 29 | 30 | 31 | hamiltonian = [ 32 | [1.0+0.0j, 1.0+2.0j], 33 | [1.0-2.0j, 3.0+0.0j] 34 | ] 35 | 36 | L = 1.0 37 | 38 | Pee, Pem, Pme, Pmm = oscprob2nu.probabilities_2nu(hamiltonian, L) 39 | 40 | print("Pee = %6.5f, Pem = %6.5f" % (Pee, Pem)) 41 | print("Pme = %6.5f, Pmm = %6.5f" % (Pme, Pmm)) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mauricio Bustamante 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/example_3nu_trivial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the trivial 3nu example shown in README.md. 3 | 4 | Runs the trivial three-neutrino example shown in README.md 5 | 6 | References 7 | ---------- 8 | 9 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 10 | a fast general-purpose computation method for two and three neutrino 11 | flavors", arXiv:1904.XXXXX. 12 | 13 | Created: 2019/04/29 23:22 14 | Last modified: 2019/04/29 23:22 15 | """ 16 | 17 | 18 | from __future__ import print_function 19 | 20 | __version__ = "1.0" 21 | __author__ = "Mauricio Bustamante" 22 | __email__ = "mbustamante@gmail.com" 23 | 24 | 25 | import sys 26 | sys.path.append('../src') 27 | 28 | import oscprob3nu 29 | 30 | hamiltonian = [ 31 | [1.0+0.0j, 0.0+2.0j, 0.0-1.0j], 32 | [0.0-2.0j, 3.0+0.0j, 3.0+0.0j], 33 | [0.0+1.0j, 3.0-0.0j, 5.0+0.0j] 34 | ] 35 | 36 | L = 1.0 37 | 38 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = \ 39 | oscprob3nu.probabilities_3nu(hamiltonian, L) 40 | 41 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 42 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 43 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 44 | -------------------------------------------------------------------------------- /test/example_2nu_vacuum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the vacuum 2nu example shown in README.md. 3 | 4 | Runs the two-neutrino example of oscillations in vacuum shown in 5 | README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/30 00:18 15 | Last modified: 2019/04/30 00:18 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob2nu 32 | import hamiltonians2nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | h_vacuum_energy_indep = \ 39 | hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent( S23_NO_BF, 40 | D31_NO_BF) 41 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 42 | 43 | Pee, Pem, Pme, Pmm = oscprob2nu.probabilities_2nu( h_vacuum, 44 | baseline*CONV_KM_TO_INV_EV) 45 | 46 | print("Pee = %6.5f, Pem = %6.5f" % (Pee, Pem)) 47 | print("Pme = %6.5f, Pmm = %6.5f" % (Pme, Pmm)) 48 | -------------------------------------------------------------------------------- /test/example_2nu_vacuum_coeffs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the vacuum coefficients 2nu example shown in README.md. 3 | 4 | Runs the two-neutrino example of coefficients for oscillations in 5 | vacuum shown in README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/30 00:24 15 | Last modified: 2019/04/30 00:24 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob2nu 32 | import hamiltonians2nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | h_vacuum_energy_indep = \ 39 | hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent( S23_NO_BF, 40 | D31_NO_BF) 41 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 42 | 43 | h1, h2, h3 = oscprob2nu.hamiltonian_2nu_coefficients(h_vacuum) 44 | print('h1: {:.4e}'.format(h1)) 45 | print('h2: {:.4e}'.format(h2)) 46 | print('h3: {:.4e}'.format(h3)) 47 | print() 48 | 49 | evol_operator = oscprob2nu.evolution_operator_2nu( h_vacuum, 50 | baseline*CONV_KM_TO_INV_EV) 51 | print('U2 = ') 52 | with np.printoptions(precision=3, suppress=True): 53 | print(np.array(evol_operator)) 54 | -------------------------------------------------------------------------------- /test/example_3nu_matter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the matter 3nu example shown in README.md. 3 | 4 | Runs the three-neutrino example of oscillations in matter shown in 5 | README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/30 00:36 15 | Last modified: 2019/04/30 00:36 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob3nu 32 | import hamiltonians3nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | h_vacuum_energy_indep = \ 39 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 40 | S23_NO_BF, 41 | S13_NO_BF, 42 | DCP_NO_BF, 43 | D21_NO_BF, 44 | D31_NO_BF) 45 | 46 | # Units of VCC_EARTH_CRUST: [eV] 47 | h_matter = hamiltonians3nu.hamiltonian_3nu_matter( h_vacuum_energy_indep, 48 | energy, 49 | VCC_EARTH_CRUST) 50 | 51 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = \ 52 | oscprob3nu.probabilities_3nu(h_matter, baseline*CONV_KM_TO_INV_EV) 53 | 54 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 55 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 56 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 57 | -------------------------------------------------------------------------------- /test/example_3nu_vacuum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the vacuum 3nu example shown in README.md. 3 | 4 | Runs the three-neutrino example of oscillations in vacuum shown in 5 | README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/29 23:48 15 | Last modified: 2019/04/29 23:48 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob3nu 32 | import hamiltonians3nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | # Use the NuFit 4.0 best-fit values of the mixing parameters pulled from 39 | # globaldefs. NO means "normal ordering"; change NO to IO if you want 40 | # to use inverted ordering. 41 | h_vacuum_energy_indep = \ 42 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 43 | S23_NO_BF, 44 | S13_NO_BF, 45 | DCP_NO_BF, 46 | D21_NO_BF, 47 | D31_NO_BF) 48 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 49 | 50 | # CONV_KM_TO_INV_EV is pulled from globaldefs; it converts km to eV^{-1} 51 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = \ 52 | oscprob3nu.probabilities_3nu( h_vacuum, baseline*CONV_KM_TO_INV_EV) 53 | 54 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 55 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 56 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 57 | -------------------------------------------------------------------------------- /test/example_3nu_liv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the LIV 3nu example shown in README.md. 3 | 4 | Runs the three-neutrino example of oscillations with LIV shown in 5 | README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/30 00:50 15 | Last modified: 2019/04/30 00:50 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob3nu 32 | import hamiltonians3nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | h_vacuum_energy_indep = \ 39 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 40 | S23_NO_BF, 41 | S13_NO_BF, 42 | DCP_NO_BF, 43 | D21_NO_BF, 44 | D31_NO_BF) 45 | 46 | # The values of the LIV parameters (SXI12, SXI23, SXI13, DXICP, B1, B2, 47 | # B3, LAMBDA) are read from globaldefs 48 | h_liv = hamiltonians3nu.hamiltonian_3nu_liv(h_vacuum_energy_indep, energy, 49 | SXI12, SXI23, SXI13, DXICP, 50 | B1, B2, B3, LAMBDA) 51 | 52 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = \ 53 | oscprob3nu.probabilities_3nu(h_liv, baseline*CONV_KM_TO_INV_EV) 54 | 55 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 56 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 57 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 58 | -------------------------------------------------------------------------------- /test/example_3nu_nsi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the NSI 3nu example shown in README.md. 3 | 4 | Runs the three-neutrino example of oscillations in matter with NSI 5 | shown in README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/30 00:46 15 | Last modified: 2019/04/30 00:46 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob3nu 32 | import hamiltonians3nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | h_vacuum_energy_indep = \ 39 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 40 | S23_NO_BF, 41 | S13_NO_BF, 42 | DCP_NO_BF, 43 | D21_NO_BF, 44 | D31_NO_BF) 45 | 46 | # EPS_3 is the 3x3 matrix of NSI strength parameters, read from 47 | # globaldefs; see that file to find the values 48 | h_nsi = hamiltonians3nu.hamiltonian_3nu_nsi(h_vacuum_energy_indep, 49 | energy, 50 | VCC_EARTH_CRUST, 51 | EPS_3) 52 | 53 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = \ 54 | oscprob3nu.probabilities_3nu(h_nsi, baseline*CONV_KM_TO_INV_EV) 55 | 56 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 57 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 58 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 59 | -------------------------------------------------------------------------------- /test/example_3nu_vacuum_coeffs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Run the vacuum coefficients 3nu example shown in README.md. 3 | 4 | Runs the three-neutrino example of coefficients for oscillations in 5 | vacuum shown in README.md 6 | 7 | References 8 | ---------- 9 | 10 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities: 11 | a fast general-purpose computation method for two and three neutrino 12 | flavors", arXiv:1904.XXXXX. 13 | 14 | Created: 2019/04/29 23:48 15 | Last modified: 2019/04/29 23:48 16 | """ 17 | 18 | 19 | from __future__ import print_function 20 | 21 | __version__ = "1.0" 22 | __author__ = "Mauricio Bustamante" 23 | __email__ = "mbustamante@gmail.com" 24 | 25 | 26 | import sys 27 | sys.path.append('../src') 28 | 29 | import numpy as np 30 | 31 | import oscprob3nu 32 | import hamiltonians3nu 33 | from globaldefs import * 34 | 35 | energy = 1.e9 # Neutrino energy [eV] 36 | baseline = 1.3e3 # Baseline [km] 37 | 38 | h_vacuum_energy_indep = \ 39 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 40 | S23_NO_BF, 41 | S13_NO_BF, 42 | DCP_NO_BF, 43 | D21_NO_BF, 44 | D31_NO_BF) 45 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 46 | 47 | h1, h2, h3, h4, h5, h6, h7, h8 = \ 48 | oscprob3nu.hamiltonian_3nu_coefficients(h_vacuum) 49 | print('h1: {:.4e}'.format(h1)) 50 | print('h2: {:.4e}'.format(h2)) 51 | print('h3: {:.4e}'.format(h3)) 52 | print('h4: {:.4e}'.format(h4)) 53 | print('h5: {:.4e}'.format(h5)) 54 | print('h6: {:.4e}'.format(h6)) 55 | print('h7: {:.4e}'.format(h7)) 56 | print('h8: {:.4e}'.format(h8)) 57 | print() 58 | 59 | u0, u1, u2, u3, u4, u5, u6, u7, u8 = \ 60 | oscprob3nu.evolution_operator_3nu_u_coefficients( \ 61 | h_vacuum, 62 | baseline*CONV_KM_TO_INV_EV) 63 | print('u0: {:.4f}'.format(u0)) 64 | print('u1: {:.4f}'.format(u1)) 65 | print('u2: {:.4f}'.format(u2)) 66 | print('u3: {:.4f}'.format(u3)) 67 | print('u4: {:.4f}'.format(u4)) 68 | print('u5: {:.4f}'.format(u5)) 69 | print('u6: {:.4f}'.format(u6)) 70 | print('u7: {:.4f}'.format(u7)) 71 | print('u8: {:.4f}'.format(u8)) 72 | print() 73 | 74 | evol_operator = \ 75 | oscprob3nu.evolution_operator_3nu(h_vacuum, baseline*CONV_KM_TO_INV_EV) 76 | print('U3 = ') 77 | with np.printoptions(precision=3, suppress=True): 78 | print(np.array(evol_operator)) 79 | -------------------------------------------------------------------------------- /src/oscprob2nu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Compute the two-neutrino flavor-transition probability. 3 | 4 | This module contains all the necessary routines to compute the 5 | two-neutrino flavor-transition probabilities using the SU(2) 6 | exponential expansion. 7 | 8 | Routine listings 9 | ---------------- 10 | 11 | * hamiltonian_2nu_coefficients - Returns coefficients of Hamiltonian 12 | * modulus - Returns the modulus of a vector 13 | * evolution_operator_2nu_u_coefficients - Returns the :math:`u_k` 14 | * evolution_operator_2nu - Returns evolution operator :math:`U_2` 15 | * probabilities_2nu - Returns the oscillation probabilities 16 | 17 | References 18 | ---------- 19 | 20 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities 21 | with arbitrary time-independent Hamiltonians", arXiv:1904.XXXXX. 22 | 23 | Created: 2019/04/20 19:07 24 | Last modified: 2019/04/22 20:33 25 | """ 26 | 27 | 28 | __version__ = "1.0" 29 | __author__ = "Mauricio Bustamante" 30 | __email__ = "mbustamante@gmail.com" 31 | 32 | 33 | from numpy import * 34 | import numpy as np 35 | import cmath 36 | import cmath as cmath 37 | 38 | sigma_1 = [[0.0, 1.0], [1.0, 0.0]] 39 | sigma_2 = [[0.0, -1.0j], [1.0j, 0]] 40 | sigma_3 = [[1.0, 0.0], [0.0, -1.0]] 41 | sigma = [sigma_1, sigma_2, sigma_3] 42 | identity = [[1.0, 0.0], [0.0, 1.0]] 43 | base = [identity, sigma_1, sigma_2, sigma_3] 44 | 45 | 46 | def hamiltonian_2nu_coefficients(hamiltonian_matrix): 47 | r"""Returns the h_k of the SU(2)-expansion of the 2nu Hamiltonian. 48 | 49 | Computes the coefficients :math:`h_1, ..., h_3` in the SU(s) 50 | expansion of the provided three-flavor Hamiltonian 51 | `hamiltonian_matrix`, which is assumed to be given in the flavor 52 | basis. The Hamiltonian is a :math:`2\times2` Hermitian matrix. 53 | 54 | Parameters 55 | ---------- 56 | hamiltonian_matrix : array_like 57 | Two-flavor Hamiltonian matrix, given as the list 58 | [[H11, H12], [H12*, H22], where the componentes Hij are complex 59 | numbers. 60 | 61 | Returns 62 | ------- 63 | list 64 | List of coefficients [h1, h2, h3]. These are complex numbers, 65 | in general. 66 | 67 | Example 68 | ------- 69 | >>> hamiltonian_matrix = [ 70 | ... [1.0+0.0j, 0.0+2.0j], 71 | ... [0.0-2.0j, 3.0+0.0j] 72 | ... ] 73 | >>> h_coeffs = hamiltonian_2nu_coefficients(hamiltonian_matrix) 74 | >>> print(h_coeffs) 75 | [2j, 0.0, (-1+0j)] 76 | """ 77 | H11 = hamiltonian_matrix[0][0] 78 | H12 = hamiltonian_matrix[0][1] 79 | H21 = hamiltonian_matrix[1][0] 80 | H22 = hamiltonian_matrix[1][1] 81 | 82 | # h0 = (H11+H22)/2.0 # Not used 83 | h1 = H12.real 84 | h2 = -H12.imag 85 | h3 = (H11-H22)/2.0 86 | 87 | return [h1, h2, h3] 88 | 89 | 90 | def modulus(h_coeffs): 91 | r"""Returns the modulus of the vector of h_k coefficients, |h|. 92 | 93 | Returns the modulus of the vector of h_k coefficients of the SU(2) 94 | expansion of the two-neutrino Hamiltonian, 95 | |h| = sqrt(|h_1|^1+|h_2|^2+|h_3|^2) 96 | 97 | Parameters 98 | ---------- 99 | h_coeffs : array_like 100 | Eight-component vector. 101 | 102 | Returns 103 | ------- 104 | float 105 | Modulus |h| 106 | """ 107 | # h_abs = |h| 108 | h_abs = sqrt(sum([abs(h)**2.0 for h in h_coeffs])) 109 | 110 | return h_abs 111 | 112 | 113 | def evolution_operator_2nu_u_coefficients(hamiltonian_matrix, L): 114 | r"""Returns coefficients u0, ..., u3 of the 2nu evolution operator. 115 | 116 | Returns the four coefficients u0, ..., u3 of the two-neutrino 117 | time-evolution operator U2(L) in its SU(2) exponential expansion, 118 | i.e., U2 = u0*I + i*u_k*sigma^k. 119 | 120 | Parameters 121 | ---------- 122 | hamiltonian_matrix : list 123 | 2x2 Hamiltonian, [[H11,H12],[H21,H22]]. 124 | L : float 125 | Baseline. 126 | 127 | Returns 128 | ------- 129 | list 130 | The four coefficients [u0, u1, u2, u3] 131 | 132 | Example 133 | ------- 134 | >>> hamiltonian_matrix = [ 135 | ... [1.0+0.0j, 0.0+2.0j], 136 | ... [0.0-2.0j, 3.0+0.0j] 137 | ... ] 138 | >>> L = 1.0 139 | >>> u_coeffs = \ 140 | ... evolution_operator_2nu_u_coefficients(hamiltonian_matrix, L) 141 | >>> print(u_coeffs) 142 | [-0.6172728764571667, (-0-0.7036898157513979j), -0.0, 143 | (0.35184490787569894-0j)] 144 | """ 145 | # [h1, h2, h3] 146 | h_coeffs = hamiltonian_2nu_coefficients(hamiltonian_matrix) 147 | 148 | # h_abs = |h| 149 | h_abs = modulus(h_coeffs) 150 | 151 | u0 = cos(h_abs*L) 152 | ss = -sin(h_abs*L)/h_abs 153 | uk = [h_coeffs[k]*ss for k in range(0,3)] 154 | 155 | # [u0, u1, u2, u3] 156 | u_coeffs = [u0]+uk 157 | 158 | return u_coeffs 159 | 160 | 161 | def evolution_operator_2nu(hamiltonian_matrix, L): 162 | r"""Returns the 2nu time-evolution operator in its SU(2) expanstion. 163 | 164 | Returns the two-neutrino time-evolution operator U2(L) in its 165 | exponential SU(2) expansion U2(L) = u0*I + i*u_k*sigma^k. This is 166 | a 2x2 unitary matrix. 167 | 168 | Parameters 169 | ---------- 170 | hamiltonian_matrix : list 171 | 2x2 Hamiltonian, [[H11,H12],[H21,H22]]. 172 | L : float 173 | Baseline. 174 | 175 | Returns 176 | ------- 177 | list 178 | The U2(L) time-evolution operator, a 2x2 unitary complex matrix. 179 | 180 | Example 181 | ------- 182 | >>> hamiltonian_matrix = [ 183 | ... [1.0+0.0j, 0.0+2.0j], 184 | ... [0.0-2.0j, 3.0+0.0j] 185 | ... ] 186 | >>> L = 1.0 187 | >>> U2 = evolution_operator_2nu(hamiltonian_matrix, L) 188 | >>> print(U2) 189 | [[ 0.312551-0.495018j -0.374011+0.523265j 0.170201-0.463257j] 190 | [ 0.374011-0.523265j -0.051331-0.047068j -0.384525+0.65848j ] 191 | [-0.170201+0.463257j -0.384525+0.65848j -0.173265+0.380716j]] 192 | """ 193 | u0, u1, u2, u3 = \ 194 | evolution_operator_2nu_u_coefficients(hamiltonian_matrix, L) 195 | 196 | evolution_operator = [ 197 | [u0+1.j*u3, 1.j*u1+u2], 198 | [1.j*u1-u2, u0-1.j*u3] 199 | ] 200 | 201 | return evolution_operator 202 | 203 | 204 | def probabilities_2nu(hamiltonian_matrix, L): 205 | r"""Returns the 2nu oscillation probability. 206 | 207 | Returns the three-neutrino oscillation probabilities 208 | Pee, Pem, Pme, Pmm. 209 | 210 | Parameters 211 | ---------- 212 | hamiltonian_matrix : list 213 | 2x2 Hamiltonian, [[H11,H12],[H21,H22]]. 214 | L : float 215 | Baseline. 216 | 217 | Returns 218 | ------- 219 | list 220 | Two-neutrino probabilities Pee, Pem, Pme, Pmm 221 | 222 | Example 223 | ------- 224 | >>> hamiltonian_matrix = [ 225 | ... [1.0+0.0j, 0.0+2.0j], 226 | ... [0.0-2.0j, 3.0+0.0j] 227 | ... ] 228 | >>> L = 1.0 229 | >>> Pee, Pem, Pme, Pmm = \ 230 | ... probabilities_2nu(hamiltonian_matrix, 1.0) 231 | >>> print(Pee, Pem, Pme, Pmm) 232 | 0.504820 0.495179 0.495179 0.504820 233 | """ 234 | # [h1, h2, h3] 235 | h_coeffs = hamiltonian_2nu_coefficients(hamiltonian_matrix) 236 | 237 | # h_abs = |h| 238 | h_abs = modulus(h_coeffs) 239 | 240 | Pem = abs(h_coeffs[0])**2.0 / h_abs**2.0 * pow(sin(h_abs*L), 2.0) 241 | Pme = Pem 242 | Pee = 1.0-Pem 243 | Pmm = 1.0-Pme 244 | 245 | return Pee, Pem, Pme, Pmm 246 | -------------------------------------------------------------------------------- /run_testsuite.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Produce a suite of test plots of probabilities. 3 | 4 | Running this module ('python run_testsuite.py') creates a number of 5 | test plots of the two- and three-neutrino oscillation probabilities 6 | vs. baseline and vs. energy. Also generates the plot included in the 7 | paper. 8 | 9 | Created: 2019/04/22 16:23 10 | Last modified: 2019/04/27 17:19 11 | """ 12 | 13 | 14 | from __future__ import print_function 15 | 16 | __version__ = "1.0" 17 | __author__ = "Mauricio Bustamante" 18 | __email__ = "mbustamante@gmail.com" 19 | 20 | 21 | from numpy import * 22 | 23 | import sys 24 | sys.path.append('./src') 25 | sys.path.append('./test') 26 | 27 | import oscprob2nu_plot 28 | import oscprob3nu_plot 29 | import oscprob2nu_plotpaper 30 | import oscprob3nu_plotpaper 31 | 32 | 33 | print('NuOscProbExact: Running test suite (plots will be stored inside ./fig)') 34 | print() 35 | 36 | print('Generating plots of 2nu probability vs. baseline:') 37 | 38 | for case in ['vacuum', 'matter', 'nsi', 'liv']: 39 | 40 | print(' Case: '+case) 41 | 42 | print(' Pee, Pem... ', end='') 43 | oscprob2nu_plot.plot_probability_2nu_vs_baseline( 44 | case, '12', energy=1.e-2, 45 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=3000, 46 | plot_prob_ee=True, plot_prob_em=True, plot_prob_mm=False, 47 | output_filename='prob_2nu_'+case+'_vs_baseline_ee_em', 48 | output_format='png', output_path='./fig/', 49 | legend_loc='center left', legend_ncol=1) 50 | print('Done') 51 | 52 | print(' Pmm, Pmt... ', end='') 53 | oscprob2nu_plot.plot_probability_2nu_vs_baseline( 54 | case, '23', energy=1.e-2, 55 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=3000, 56 | plot_prob_ee=True, plot_prob_em=True, plot_prob_mm=False, 57 | output_filename='prob_2nu_'+case+'_vs_baseline_mm_mt', 58 | output_format='png', output_path='./fig/', 59 | legend_loc='center left', legend_ncol=1) 60 | print('Done') 61 | 62 | print(' Done') 63 | 64 | print() 65 | 66 | print('Generating plots of 2nu probability vs. energy:') 67 | 68 | for case in ['vacuum', 'matter', 'nsi', 'liv']: 69 | 70 | print(' Case: '+case) 71 | 72 | print(' Pee, Pem... ', end='') 73 | oscprob2nu_plot.plot_probability_2nu_vs_energy( 74 | case, '12', baseline=1.e3, 75 | log10_energy_min=-1.0, log10_energy_max=1.0, 76 | log10_energy_npts=600, 77 | plot_prob_ee=True, plot_prob_em=True, plot_prob_mm=False, 78 | output_filename='prob_2nu_'+case+'_vs_energy_ee_em', 79 | output_format='png', output_path='./fig/', 80 | legend_loc='center right', legend_ncol=1) 81 | print('Done') 82 | 83 | print(' Pee, Pem... ', end='') 84 | oscprob2nu_plot.plot_probability_2nu_vs_energy( 85 | case, '23', baseline=1.e3, 86 | log10_energy_min=-1.0, log10_energy_max=1.0, 87 | log10_energy_npts=600, 88 | plot_prob_ee=True, plot_prob_em=True, plot_prob_mm=False, 89 | output_filename='prob_2nu_'+case+'_vs_energy_mm_mt', 90 | output_format='png', output_path='./fig/', 91 | legend_loc='center right', legend_ncol=1) 92 | print('Done') 93 | 94 | 95 | print(' Done') 96 | 97 | print() 98 | 99 | print('Generating plots of 3nu probability vs. baseline:') 100 | 101 | for case in ['vacuum', 'matter', 'nsi', 'liv']: 102 | 103 | print(' Case: '+case) 104 | 105 | print(' Pee, Pem, Pet... ', end='') 106 | # Pee, Pem, Pet 107 | oscprob3nu_plot.plot_probability_3nu_vs_baseline( 108 | case, energy=1.e-2, 109 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=1000, 110 | plot_prob_ee=True, plot_prob_em=True, plot_prob_et=True, 111 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 112 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 113 | output_filename='prob_3nu_'+case+'_vs_baseline_ee_em_et', 114 | output_format='png', output_path='./fig/', 115 | legend_loc='center left', legend_ncol=1) 116 | print('Done') 117 | 118 | print(' Pme, Pmm, Pmt... ', end='') 119 | # Pme, Pmm, Pmt 120 | oscprob3nu_plot.plot_probability_3nu_vs_baseline( 121 | case, energy=1.e-2, 122 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=1000, 123 | plot_prob_ee=False, plot_prob_em=False, plot_prob_et=False, 124 | plot_prob_me=True, plot_prob_mm=True, plot_prob_mt=True, 125 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 126 | output_filename='prob_3nu_'+case+'_vs_baseline_me_mm_mt', 127 | output_format='png', output_path='./fig/', 128 | legend_loc='center left', legend_ncol=1) 129 | print('Done') 130 | 131 | print(' Pte, Ptm, Ptt... ', end='') 132 | # Pte, Ptm, Ptt 133 | oscprob3nu_plot.plot_probability_3nu_vs_baseline( 134 | case, energy=1.e-2, 135 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=1000, 136 | plot_prob_ee=False, plot_prob_em=False, plot_prob_et=False, 137 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 138 | plot_prob_te=True, plot_prob_tm=True, plot_prob_tt=True, 139 | output_filename='prob_3nu_'+case+'_vs_baseline_te_tm_tt', 140 | output_format='png', output_path='./fig/', 141 | legend_loc='center left', legend_ncol=1) 142 | print('Done') 143 | print(' Done') 144 | 145 | print() 146 | 147 | print('Generating plots of 3nu probability vs. energy:') 148 | 149 | for case in ['vacuum', 'matter', 'nsi', 'liv']: 150 | 151 | print(' Case: '+case) 152 | 153 | print(' Pee, Pem, Pet... ', end='') 154 | # Pee, Pem, Pet 155 | oscprob3nu_plot.plot_probability_3nu_vs_energy( 156 | case, baseline=1.e3, 157 | log10_energy_min=-1.0, log10_energy_max=1.0, 158 | log10_energy_npts=200, 159 | plot_prob_ee=True, plot_prob_em=True, plot_prob_et=True, 160 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 161 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 162 | output_filename='prob_3nu_'+case+'_vs_energy_ee_em_et', 163 | output_format='png', output_path='./fig/', 164 | legend_loc='center right', legend_ncol=1) 165 | print('Done') 166 | 167 | print(' Pme, Pmm, Pmt... ', end='') 168 | # Pme, Pmm, Pmt 169 | oscprob3nu_plot.plot_probability_3nu_vs_energy( 170 | case, baseline=1.e3, 171 | log10_energy_min=-1.0, log10_energy_max=1.0, 172 | log10_energy_npts=200, 173 | plot_prob_ee=False, plot_prob_em=False, plot_prob_et=False, 174 | plot_prob_me=True, plot_prob_mm=True, plot_prob_mt=True, 175 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 176 | output_filename='prob_3nu_'+case+'_vs_energy_me_mm_mt', 177 | output_format='png', output_path='./fig/', 178 | legend_loc='center right', legend_ncol=1) 179 | print('Done') 180 | 181 | print(' Pte, Ptm, Ptt... ', end='') 182 | # Pte, Ptm, Ptt 183 | oscprob3nu_plot.plot_probability_3nu_vs_energy( 184 | case, baseline=1.e3, 185 | log10_energy_min=-1.0, log10_energy_max=1.0, 186 | log10_energy_npts=200, 187 | plot_prob_ee=False, plot_prob_em=False, plot_prob_et=False, 188 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 189 | plot_prob_te=True, plot_prob_tm=True, plot_prob_tt=True, 190 | output_filename='prob_3nu_'+case+'_vs_energy_te_tm_tt', 191 | output_format='png', output_path='./fig/', 192 | legend_loc='center right', legend_ncol=1) 193 | print('Done') 194 | print(' Done') 195 | 196 | print() 197 | 198 | print('Generating 2nu plot in paper... ', end='') 199 | oscprob2nu_plotpaper.plot_probability_2nu_vs_energy_compare( \ 200 | output_format='png', output_path='./fig/') 201 | print('Done') 202 | 203 | print() 204 | 205 | print('Generating 3nu plot in paper... ', end='') 206 | oscprob3nu_plotpaper.plot_probability_3nu_vs_energy_compare( \ 207 | output_format='png', output_path='./fig/') 208 | print('Done') 209 | -------------------------------------------------------------------------------- /src/globaldefs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Contains physical constants and unit-conversion constants. 3 | 4 | This module contains contains values of physical constants and 5 | unit-conversion factors used by the various modules of NuOscProbExact. 6 | The core modules oscprob2nu.py and oscprob3nu.py do not require these 7 | constants. 8 | 9 | Created: 2019/04/17 17:03 10 | Last modified: 2019/04/29 23:39 11 | """ 12 | 13 | 14 | __version__ = "1.0" 15 | __author__ = "Mauricio Bustamante" 16 | __email__ = "mbustamante@gmail.com" 17 | 18 | 19 | from numpy import * 20 | import numpy as np 21 | 22 | 23 | CONV_KM_TO_INV_EV = 5.06773e9 24 | r"""float: Module-level constant 25 | 26 | Multiplicative conversion factor from km to eV^{-1}. 27 | Units: [km^{-1} eV^{-1}]. 28 | """ 29 | 30 | CONV_CM_TO_INV_EV = CONV_KM_TO_INV_EV*1.e-5 31 | r"""float: Module-level constant 32 | 33 | Multiplicative conversion factor from cm to eV^{-1}. 34 | Units: [cm^{-1} eV^{-1}] 35 | """ 36 | 37 | CONV_INV_EV_TO_CM = 1./CONV_CM_TO_INV_EV 38 | r"""float: Module-level constant 39 | 40 | Multiplicative conversion factor from eV^{-1} to cm. 41 | Units: [eV cm] 42 | """ 43 | 44 | CONV_EV_TO_G = 1.783e-33 45 | r"""float: Module-level constant 46 | 47 | Multiplicative conversion factor from eV^{-1} to grams. 48 | Units: [g eV^{-1}] 49 | """ 50 | 51 | CONV_G_TO_EV = 1./CONV_EV_TO_G 52 | r"""float: Module-level constant 53 | 54 | Multiplicative conversion factor from grams to eV^{-1}. 55 | Units: [eV g^{-1}] 56 | """ 57 | 58 | GF = 1.1663787e-23 59 | r"""float: Module-level constant 60 | 61 | Fermi constant. 62 | Units: [eV^{-1}] 63 | """ 64 | 65 | MASS_ELECTRON = 0.5109989461e6 66 | r"""float: Module-level constant 67 | 68 | Electron mass. 69 | Units: [eV] 70 | """ 71 | 72 | MASS_PROTON = 938.272046e6 73 | r"""float: Module-level constant 74 | 75 | Proton mass. 76 | Units: [eV] 77 | """ 78 | 79 | MASS_NEUTRON = 939.565379e6 80 | r"""float: Module-level constant 81 | 82 | Neutron mass. 83 | Units: [eV] 84 | """ 85 | 86 | ELECTRON_FRACTION_EARTH_CRUST = 0.5 87 | r"""float: Module-level constant 88 | 89 | Electron fraction in the Earth's crust. 90 | Units: [Adimensional] 91 | """ 92 | 93 | DENSITY_MATTER_CRUST_G_PER_CM3 = 3.0 94 | r"""float: Module-level constant 95 | 96 | Average matter density in the Earth's crust. 97 | Units: [g cm^{-3}] 98 | """ 99 | 100 | # NUM_DENSITY_E_EARTH_CRUST = DENSITY_MATTER_CRUST_G_PER_CM3 * CONV_G_TO_EV \ 101 | # / ((MASS_PROTON+MASS_NEUTRON)/2.0) \ 102 | # * ELECTRON_FRACTION_EARTH_CRUST \ 103 | # / pow(CONV_CM_TO_INV_EV, 3.0) 104 | NUM_DENSITY_E_EARTH_CRUST = DENSITY_MATTER_CRUST_G_PER_CM3 * CONV_G_TO_EV \ 105 | / ((MASS_PROTON+MASS_NEUTRON)/2.0) \ 106 | * ELECTRON_FRACTION_EARTH_CRUST \ 107 | / pow(CONV_CM_TO_INV_EV, 3.0) 108 | r"""float: Module-level constant 109 | 110 | Electron number density in the Earth's crust 111 | Units: [eV^3] 112 | """ 113 | 114 | VCC_EARTH_CRUST = sqrt(2.0)*GF*NUM_DENSITY_E_EARTH_CRUST 115 | r"""float: Module-level constant 116 | 117 | Charged-current matter potential in the Earth's crust. 118 | Units: [eV] 119 | """ 120 | 121 | S12_NO_BF = sqrt(0.310) 122 | r"""float: Module-level constant 123 | 124 | Lepton mixing angle sin(theta_12), best fit from NuFit 4.0, assuming 125 | normal ordering with SK atmospheric data. 126 | Units: [Adimensional] 127 | """ 128 | 129 | S23_NO_BF = sqrt(0.582) 130 | r"""float: Module-level constant 131 | 132 | Lepton mixing angle sin(theta_23), best fit from NuFit 4.0, assuming 133 | normal ordering with SK atmospheric data. 134 | Units: [Adimensional] 135 | """ 136 | 137 | S13_NO_BF = sqrt(2.240e-2) 138 | r"""float: Module-level constant 139 | 140 | Lepton mixing angle sin(theta_13), best fit from NuFit 4.0, assuming 141 | normal ordering with SK atmospheric data. 142 | Units: [Adimensional] 143 | """ 144 | 145 | DCP_NO_BF = 217./180.*np.pi 146 | r"""float: Module-level constant 147 | 148 | Lepton CP-violation phase delta_CP, best fit from NuFit 4.0, assuming 149 | normal ordering with SK atmospheric data. 150 | Units: [radian] 151 | """ 152 | 153 | D21_NO_BF = 7.39e-5 154 | r"""float: Module-level constant 155 | 156 | Mass-squared difference Delta m^2_21, best fit from NuFit 4.0, assuming 157 | normal ordering with SK atmospheric data. 158 | Units: [eV^2] 159 | """ 160 | 161 | D31_NO_BF = 2.525e-3 162 | r"""float: Module-level constant 163 | 164 | Mass-squared difference Delta m^2_31, best fit from NuFit 4.0, assuming 165 | normal ordering with SK atmospheric data. 166 | Units: [eV^2] 167 | """ 168 | 169 | S12_IO_BF = sqrt(0.310) 170 | r"""float: Module-level constant 171 | 172 | Lepton mixing angle sin(theta_12), best fit from NuFit 4.0, assuming 173 | inverted ordering with SK atmospheric data. 174 | Units: [Adimensional] 175 | """ 176 | 177 | S23_IO_BF = sqrt(0.582) 178 | r"""float: Module-level constant 179 | 180 | Lepton mixing angle sin(theta_23), best fit from NuFit 4.0, assuming 181 | inverted ordering with SK atmospheric data. 182 | Units: [Adimensional] 183 | """ 184 | 185 | S13_IO_BF = sqrt(2.263e-2) 186 | r"""float: Module-level constant 187 | 188 | Lepton mixing angle sin(theta_13), best fit from NuFit 4.0, assuming 189 | inverted ordering with SK atmospheric data. 190 | Units: [Adimensional] 191 | """ 192 | 193 | DCP_IO_BF = 280./180.*np.pi 194 | r"""float: Module-level constant 195 | 196 | Lepton CP-violation phase delta_CP, best fit from NuFit 4.0, assuming 197 | inverted ordering with SK atmospheric data. 198 | Units: [radian] 199 | """ 200 | 201 | D21_IO_BF = 7.39e-5 202 | r"""float: Module-level constant 203 | 204 | Mass-squared difference Delta m^2_21, best fit from NuFit 4.0, assuming 205 | normal ordering with SK atmospheric data. 206 | Units: [eV^2] 207 | """ 208 | 209 | D32_IO_BF = -2.512e-3 210 | r"""float: Module-level constant 211 | 212 | Mass-squared difference Delta m^2_32, best fit from NuFit 4.0, assuming 213 | normal ordering with SK atmospheric data. 214 | Units: [eV^2] 215 | """ 216 | 217 | D31_IO_BF = D32_IO_BF+D21_IO_BF 218 | r"""float: Module-level constant 219 | 220 | Mass-squared difference Delta m^2_31, best fit from NuFit 4.0, assuming 221 | inverted ordering with SK atmospheric data. 222 | Units: [eV^2] 223 | """ 224 | 225 | EPS_EE = 0.06 226 | r"""float: Module-level constant 227 | 228 | Total NSI strength parameter eps_ee computed using values of the u and d 229 | quark parameters compatible at 2sigma with LMA+coherent from 1805.04530. 230 | Units: [Adimensional] 231 | """ 232 | 233 | EPS_EM = -0.06 234 | r"""float: Module-level constant 235 | 236 | Total NSI strength parameter eps_em computed using values of the u and d 237 | quark parameters compatible at 2sigma with LMA+coherent from 1805.04530. 238 | Units: [Adimensional] 239 | """ 240 | 241 | EPS_ET = 0.0 242 | r"""float: Module-level constant 243 | 244 | Total NSI strength parameter eps_et computed using values of the u and d 245 | quark parameters compatible at 2sigma with LMA+coherent from 1805.04530. 246 | Units: [Adimensional] 247 | """ 248 | 249 | EPS_MM = 1.2 250 | r"""float: Module-level constant 251 | 252 | Total NSI strength parameter eps_mm computed using values of the u and d 253 | quark parameters compatible at 2sigma with LMA+coherent from 1805.04530. 254 | Units: [Adimensional] 255 | """ 256 | 257 | EPS_MT = 0.0 258 | r"""float: Module-level constant 259 | 260 | Total NSI strength parameter eps_mt computed using values of the u and d 261 | quark parameters compatible at 2sigma with LMA+coherent from 1805.04530. 262 | Units: [Adimensional] 263 | """ 264 | 265 | EPS_TT = 0.0 266 | r"""float: Module-level constant 267 | 268 | Total NSI strength parameter eps_tt computed using values of the u and d 269 | quark parameters compatible at 2sigma with LMA+coherent from 1805.04530. 270 | Units: [Adimensional] 271 | """ 272 | 273 | EPS_2 = [EPS_EE, EPS_EM, EPS_MM] 274 | r"""float: Module-level constant 275 | 276 | Vector of total NSI strength parameters for two-neutrino oscillations. 277 | Used in oscprob2nu_plot.py. 278 | Units: [Adimensional] 279 | """ 280 | 281 | EPS_3 = [EPS_EE, EPS_EM, EPS_ET, EPS_MM, EPS_MT, EPS_TT] 282 | r"""float: Module-level constant 283 | 284 | Vector of total NSI strength parameters for three-neutrino oscillations. 285 | Used in oscprob3nu_plot.py. 286 | Units: [Adimensional] 287 | """ 288 | 289 | # LIV parameters 290 | # Compatible with 90% C.L. upper limits on c^(4) from 1709.03434 291 | SXI12 = 0.0 292 | r"""float: Module-level constant 293 | 294 | LIV lepton mixing angle sin(xi_12). 295 | Units: [Adimensional] 296 | """ 297 | 298 | SXI23 = 0.0 299 | r"""float: Module-level constant 300 | 301 | LIV lepton mixing angle sin(xi_23). 302 | Units: [Adimensional] 303 | """ 304 | 305 | SXI13 = 0.0 306 | r"""float: Module-level constant 307 | 308 | LIV lepton mixing angle sin(xi_13). 309 | Units: [Adimensional] 310 | """ 311 | 312 | DXICP = 0.0 313 | r"""float: Module-level constant 314 | 315 | LIV CP-violation phase. 316 | Units: [radian] 317 | """ 318 | 319 | B1 = 1.e-9 320 | r"""float: Module-level constant 321 | 322 | LIV eigenvalue b_1. 323 | Units: [eV] 324 | """ 325 | 326 | B2 = 1.e-9 327 | r"""float: Module-level constant 328 | 329 | LIV eigenvalue b_2. 330 | Units: [eV] 331 | """ 332 | B3 = 2.e-9 333 | r"""float: Module-level constant 334 | 335 | LIV eigenvalue b_3. 336 | Units: [eV] 337 | """ 338 | 339 | LAMBDA = 1.e12 # [eV] 340 | r"""float: Module-level constant 341 | 342 | LIV energy scale Lambda. 343 | Units: [eV] 344 | """ 345 | -------------------------------------------------------------------------------- /test/oscprob3nu_plotpaper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Produce the plot of 3nu probabilities vs. energy shown in the paper. 3 | 4 | Contains a routine to generate and save the plot of three-neutrino 5 | probabilities vs. energy that is included in the paper. 6 | 7 | Routine listings 8 | ---------------- 9 | 10 | * plot_probability_3nu_vs_energy_compare - Generates, saves the plot 11 | 12 | References 13 | ---------- 14 | 15 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities 16 | with arbitrary time-independent Hamiltonians", arXiv:1904.XXXXX. 17 | 18 | Created: 2019/04/17 18:08 19 | Last modified: 2019/04/22 20:36 20 | """ 21 | 22 | 23 | __version__ = "1.0" 24 | __author__ = "Mauricio Bustamante" 25 | __email__ = "mbustamante@nbi.ku.dk" 26 | 27 | 28 | from numpy import * 29 | import numpy as np 30 | from pylab import * 31 | from matplotlib import * 32 | import matplotlib as mpl 33 | 34 | import sys 35 | sys.path.append('../src') 36 | 37 | import oscprob3nu 38 | import hamiltonians3nu 39 | from globaldefs import * 40 | 41 | 42 | def plot_probability_3nu_vs_energy_compare(output_format='pdf', 43 | output_path='./fig/'): 44 | r"""Generates and saves a plot of 3nu probabilities vs. energy. 45 | 46 | Generates and saves a plot of three-neutrino probabilities vs. 47 | energy for oscillations in vacuum, matter, with NSI, and with 48 | CPT-odd LIV. This is the same plot that is included in the paper. 49 | 50 | Parameters 51 | ---------- 52 | output_format : str, optional 53 | File extension of the plot to save (e.g., 'pdf', 'png', 'jpg'). 54 | output_path : str, optional 55 | File path where to save the plot. 56 | 57 | Returns 58 | ------- 59 | None 60 | The plot is generated and saved. 61 | """ 62 | # Baseline (DUNE) 63 | l = 1.3e3*CONV_KM_TO_INV_EV # [eV^{-1}] 64 | 65 | # Neutrino energies 66 | log10_energy_nu_min = log10(5.e-1) # [GeV] 67 | log10_energy_nu_max = log10(3.e1) # [GeV] 68 | log10_energy_nu_npts = 400 69 | log10_energy_nu = np.linspace( log10_energy_nu_min, 70 | log10_energy_nu_max, 71 | log10_energy_nu_npts) 72 | energy_nu = [10.**x for x in log10_energy_nu] # [GeV] 73 | 74 | # Plot formatting 75 | mpl.rcParams['xtick.labelsize']=28 76 | mpl.rcParams['ytick.labelsize']=28 77 | mpl.rcParams['legend.fontsize']=21 78 | mpl.rcParams['legend.borderpad']=0.4 79 | mpl.rcParams['axes.labelpad']=10 80 | mpl.rcParams['ps.fonttype']=42 81 | mpl.rcParams['pdf.fonttype']=42 82 | 83 | fig, axes = plt.subplots(3, 1, figsize=[8,15]) 84 | fig.subplots_adjust(hspace=0.05, wspace=0.05) 85 | 86 | h_vacuum_energy_indep = \ 87 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 88 | S23_NO_BF, 89 | S13_NO_BF, 90 | DCP_NO_BF, 91 | D21_NO_BF, 92 | D31_NO_BF) 93 | 94 | prob_vacuum = [oscprob3nu.probabilities_3nu( \ 95 | np.multiply(1./x/1.e9, h_vacuum_energy_indep), l) \ 96 | for x in energy_nu] 97 | 98 | # Uncomment to compare to the probability computed with the standard 99 | # ocillation formula in vacuum 100 | # U = hamiltonians3nu.pmns_mixing_matrix( S12_NO_BF, 101 | # S23_NO_BF, 102 | # S13_NO_BF, 103 | # DCP_NO_BF) 104 | # prob_vacuum_std = [hamiltonians3nu.probabilities_3nu_vacuum_std( \ 105 | # U, D21_BF, D31_BF, x, l/CONV_KM_TO_INV_EV) \ 106 | # for x in energy_nu] 107 | 108 | prob_matter = [oscprob3nu.probabilities_3nu( \ 109 | hamiltonians3nu.hamiltonian_3nu_matter( \ 110 | h_vacuum_energy_indep, x*1.e9, VCC_EARTH_CRUST), l) 111 | for x in energy_nu] 112 | 113 | # eps = eps_ee, eps_em, eps_et, eps_mm, eps_mt, eps_tt 114 | prob_nsi = [oscprob3nu.probabilities_3nu( \ 115 | hamiltonians3nu.hamiltonian_3nu_nsi( \ 116 | h_vacuum_energy_indep, x*1.e9, VCC_EARTH_CRUST, EPS_3), 117 | l) 118 | for x in energy_nu] 119 | 120 | prob_liv = [oscprob3nu.probabilities_3nu( \ 121 | hamiltonians3nu.hamiltonian_3nu_liv( \ 122 | h_vacuum_energy_indep, x*1.e9, SXI12, SXI23, SXI13, 123 | DXICP, B1, B2, B3, LAMBDA), l) 124 | for x in energy_nu] 125 | 126 | # Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt 127 | for i, ax in enumerate(np.array(axes).reshape((1,3))[0]): 128 | 129 | if (i == 0): # Pee 130 | 131 | p_vacuum = [x[0] for x in prob_vacuum] 132 | # p_vacuum_std = [x[0] for x in prob_vacuum_std] 133 | p_matter = [x[0] for x in prob_matter] 134 | p_nsi = [x[0] for x in prob_nsi] 135 | p_liv = [x[0] for x in prob_liv] 136 | 137 | ylabel = r'$P_{\nu_e \to \nu_e}$' 138 | 139 | elif (i == 1): # Pme 140 | 141 | p_vacuum = [x[3] for x in prob_vacuum] 142 | # p_vacuum_std = [x[3] for x in prob_vacuum_std] 143 | p_matter = [x[3] for x in prob_matter] 144 | p_nsi = [x[3] for x in prob_nsi] 145 | p_liv = [x[3] for x in prob_liv] 146 | 147 | ylabel = r'$P_{\nu_\mu \to \nu_e}$' 148 | 149 | elif (i == 2): # Pmm 150 | 151 | p_vacuum = [x[4] for x in prob_vacuum] 152 | # p_vacuum_std = [x[4] for x in prob_vacuum_std] 153 | p_matter = [x[4] for x in prob_matter] 154 | p_nsi = [x[4] for x in prob_nsi] 155 | p_liv = [x[4] for x in prob_liv] 156 | 157 | ylabel = r'$P_{\nu_\mu \to \nu_\mu}$' 158 | ax.set_xlabel(r'Neutrino energy [GeV]', fontsize=27) 159 | 160 | ax.set_ylabel(ylabel, fontsize=27) 161 | 162 | ax.plot(energy_nu, p_vacuum, color='C0', ls='-', lw=3.0, zorder=1, 163 | label=r'Vacuum') 164 | # ax.plot(energy_nu, p_vacuum_std, color='k', ls='-', lw=3.0, zorder=1, 165 | # label=r'Vacuum std.') 166 | ax.plot(energy_nu, p_matter, color='C1', ls='--', lw=3.0, zorder=1, 167 | label=r'Matter') 168 | ax.plot(energy_nu, p_nsi, color='C2', ls=':', lw=3.0, zorder=1, 169 | label=r'NSI') 170 | ax.plot(energy_nu, p_liv, color='C3', ls='-.', lw=3.0, zorder=1, 171 | label=r'CPT-odd LIV') 172 | 173 | ax.tick_params('both', length=10, width=2, which='major') 174 | ax.tick_params('both', length=5, width=1, which='minor') 175 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 176 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 177 | ax.tick_params(axis='x', which='minor', bottom=True) 178 | ax.tick_params(axis='x', which='minor', top=True) 179 | ax.tick_params(axis='y', which='minor', left=True) 180 | ax.tick_params(axis='y', which='minor', right=True) 181 | ax.tick_params(bottom=True, top=True, left=True, right=True) 182 | 183 | ax.set_xlim([10.**log10_energy_nu_min, 10.**log10_energy_nu_max]) 184 | ax.set_xscale('log') 185 | 186 | if (i == 0): 187 | 188 | ax.set_xticklabels([]) 189 | ax_yticks_major = np.array([0.85, 0.90, 0.95, 1.00]) 190 | ax.set_yticks(ax_yticks_major, minor=False) 191 | ax_yticks_minor = np.array([0.86, 0.87, 0.88, 0.89, 192 | 0.91, 0.92, 0.93, 0.94, 0.96, 0.97, 193 | 0.98, 0.99]) 194 | ax.set_yticks(ax_yticks_minor, minor=True) 195 | ax.set_ylim([0.85, 1.00]) 196 | ax.legend(loc='lower right', ncol=1, frameon=False, 197 | columnspacing=1.) 198 | 199 | elif (i == 1): 200 | 201 | ax.set_xticklabels([]) 202 | ax_yticks_major = np.array([0.00, 0.05, 0.10]) 203 | ax.set_yticks(ax_yticks_major, minor=False) 204 | ax_yticks_minor = np.array([0.01, 0.02, 0.03, 0.04, 0.06, 0.07, 205 | 0.08, 0.09, 0.11, 0.12]) 206 | ax.set_yticks(ax_yticks_minor, minor=True) 207 | ax.set_ylim([0.00, 0.12]) 208 | 209 | elif (i == 2): 210 | 211 | ax_yticks_major = np.array([0.00, 0.25, 0.50, 0.75]) 212 | ax.set_yticks(ax_yticks_major, minor=False) 213 | ax_yticks_minor = np.array([0.05, 0.10, 0.15, 0.20, 0.30, 0.35, 214 | 0.40, 0.45, 0.55, 0.60, 0.65, 0.70, 215 | 0.80, 0.85, 0.90, 0.95]) 216 | ax.set_yticks(ax_yticks_minor, minor=True) 217 | ax.set_ylim([0.0, 1.0]) 218 | 219 | pylab.savefig(output_path+'prob_3nu_vs_energy_compare.'+output_format, 220 | bbox_inches='tight', dpi=300) 221 | 222 | return 223 | 224 | # plot_probability_3nu_vs_energy_compare( output_format='pdf', 225 | # output_path='../fig/') 226 | 227 | -------------------------------------------------------------------------------- /test/oscprob2nu_plotpaper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Produce the plot of 2nu probabilities vs. energy shown in the paper. 3 | 4 | Contains a routine to generate and save the plot of two-neutrino 5 | probabilities vs. energy that is included in the paper. 6 | 7 | Routine listings 8 | ---------------- 9 | 10 | * plot_probability_2nu_vs_energy_compare - Generates, saves the plot 11 | 12 | References 13 | ---------- 14 | 15 | .. [1] Mauricio Bustamante, "Exact neutrino oscillation probabilities 16 | with arbitrary time-independent Hamiltonians", arXiv:1904.XXXXX. 17 | 18 | Created: 2019/04/26 21:20 19 | Last modified: 2019/04/26 21:20 20 | """ 21 | 22 | 23 | __version__ = "1.0" 24 | __author__ = "Mauricio Bustamante" 25 | __email__ = "mbustamante@nbi.ku.dk" 26 | 27 | 28 | from numpy import * 29 | import numpy as np 30 | from pylab import * 31 | from matplotlib import * 32 | import matplotlib as mpl 33 | 34 | import sys 35 | sys.path.append('../src') 36 | 37 | import oscprob2nu 38 | import hamiltonians2nu 39 | from globaldefs import * 40 | 41 | 42 | def plot_probability_2nu_vs_energy_compare(output_format='pdf', 43 | output_path='./fig/'): 44 | r"""Generates and saves a plot of 2nu probabilities vs. energy. 45 | 46 | Generates and saves a plot of two-neutrino probabilities vs. 47 | energy for oscillations in vacuum, matter, with NSI, and with 48 | CPT-odd LIV. This is the same plot that is included in the paper. 49 | 50 | Parameters 51 | ---------- 52 | output_format : str, optional 53 | File extension of the plot to save (e.g., 'pdf', 'png', 'jpg'). 54 | output_path : str, optional 55 | File path where to save the plot. 56 | 57 | Returns 58 | ------- 59 | None 60 | The plot is generated and saved. 61 | """ 62 | # Baseline (DUNE) 63 | l = 1.3e3*CONV_KM_TO_INV_EV # [eV^{-1}] 64 | 65 | # Neutrino energies 66 | log10_energy_nu_min = log10(5.e-1) # [GeV] 67 | log10_energy_nu_max = log10(4.e1) # [GeV] 68 | log10_energy_nu_npts = 400 69 | log10_energy_nu = np.linspace( log10_energy_nu_min, 70 | log10_energy_nu_max, 71 | log10_energy_nu_npts) 72 | energy_nu = [10.**x for x in log10_energy_nu] # [GeV] 73 | 74 | # Plot formatting 75 | mpl.rcParams['xtick.labelsize']=28 76 | mpl.rcParams['ytick.labelsize']=28 77 | mpl.rcParams['legend.fontsize']=21 78 | mpl.rcParams['legend.borderpad']=0.4 79 | mpl.rcParams['axes.labelpad']=10 80 | mpl.rcParams['ps.fonttype']=42 81 | mpl.rcParams['pdf.fonttype']=42 82 | 83 | fig, axes = plt.subplots(3, 1, figsize=[8,15]) 84 | fig.subplots_adjust(hspace=0.05, wspace=0.05) 85 | 86 | h_vacuum_energy_indep = \ 87 | hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent( S23_NO_BF, 88 | D31_NO_BF) 89 | 90 | prob_vacuum = [oscprob2nu.probabilities_2nu( \ 91 | np.multiply(1./x/1.e9, h_vacuum_energy_indep), l) \ 92 | for x in energy_nu] 93 | 94 | # Uncomment to compare to the probability computed with the standard 95 | # ocillation formula in vacuum 96 | # prob_vacuum_std = [hamiltonians2nu.probabilities_2nu_vacuum_std( \ 97 | # S23_NO_BF, D31_NO_BF, x, l/CONV_KM_TO_INV_EV) \ 98 | # for x in energy_nu] 99 | 100 | prob_matter = [oscprob2nu.probabilities_2nu( \ 101 | hamiltonians2nu.hamiltonian_2nu_matter( \ 102 | h_vacuum_energy_indep, x*1.e9, VCC_EARTH_CRUST), l) 103 | for x in energy_nu] 104 | 105 | # Uncomment to compare to the probability computed with the standard 106 | # ocillation formula in matter 107 | # prob_matter_std = [hamiltonians2nu.probabilities_2nu_matter_std( \ 108 | # S23_NO_BF, D31_NO_BF, VCC_EARTH_CRUST, x, 109 | # l/CONV_KM_TO_INV_EV) \ 110 | # for x in energy_nu] 111 | 112 | prob_nsi = [oscprob2nu.probabilities_2nu( \ 113 | hamiltonians2nu.hamiltonian_2nu_nsi( \ 114 | h_vacuum_energy_indep, x*1.e9, VCC_EARTH_CRUST, EPS_2), 115 | l) 116 | for x in energy_nu] 117 | 118 | prob_liv = [oscprob2nu.probabilities_2nu( \ 119 | hamiltonians2nu.hamiltonian_2nu_liv( \ 120 | h_vacuum_energy_indep, x*1.e9, SXI12, B1, B3, LAMBDA), 121 | l) 122 | for x in energy_nu] 123 | 124 | # Pee, Pem, Pmm 125 | for i, ax in enumerate(np.array(axes).reshape((1,3))[0]): 126 | 127 | if (i == 0): # Pee 128 | 129 | p_vacuum = [x[0] for x in prob_vacuum] 130 | # p_vacuum_std = [x[0] for x in prob_vacuum_std] 131 | p_matter = [x[0] for x in prob_matter] 132 | # p_matter_std = [x[0] for x in prob_matter_std] 133 | p_nsi = [x[0] for x in prob_nsi] 134 | p_liv = [x[0] for x in prob_liv] 135 | 136 | ylabel = r'$P_{\nu_e \to \nu_e}$' 137 | 138 | elif (i == 1): # Pme 139 | 140 | p_vacuum = [x[2] for x in prob_vacuum] 141 | # p_vacuum_std = [x[2] for x in prob_vacuum_std] 142 | p_matter = [x[2] for x in prob_matter] 143 | # p_matter_std = [x[2] for x in prob_matter_std] 144 | p_nsi = [x[2] for x in prob_nsi] 145 | p_liv = [x[2] for x in prob_liv] 146 | 147 | ylabel = r'$P_{\nu_\mu \to \nu_e}$' 148 | 149 | elif (i == 2): # Pmm 150 | 151 | p_vacuum = [x[3] for x in prob_vacuum] 152 | # p_vacuum_std = [x[3] for x in prob_vacuum_std] 153 | p_matter = [x[3] for x in prob_matter] 154 | # p_matter_std = [x[3] for x in prob_matter_std] 155 | p_nsi = [x[3] for x in prob_nsi] 156 | p_liv = [x[3] for x in prob_liv] 157 | 158 | ylabel = r'$P_{\nu_\mu \to \nu_\mu}$' 159 | ax.set_xlabel(r'Neutrino energy [GeV]', fontsize=27) 160 | 161 | ax.set_ylabel(ylabel, fontsize=27) 162 | 163 | ax.plot(energy_nu, p_vacuum, color='C0', ls='-', lw=3.0, zorder=1, 164 | label=r'Vacuum') 165 | # ax.plot(energy_nu, p_vacuum_std, color='k', ls='-', lw=3.0, zorder=1, 166 | # label=r'Vacuum std.') 167 | ax.plot(energy_nu, p_matter, color='C1', ls='--', lw=3.0, zorder=1, 168 | label=r'Matter') 169 | # ax.plot(energy_nu, p_matter_std, color='b', ls='--', lw=3.0, zorder=1, 170 | # label=r'Matter std.') 171 | ax.plot(energy_nu, p_nsi, color='C2', ls=':', lw=3.0, zorder=1, 172 | label=r'NSI') 173 | ax.plot(energy_nu, p_liv, color='C3', ls='-.', lw=3.0, zorder=1, 174 | label=r'CPT-odd LIV') 175 | 176 | ax.tick_params('both', length=10, width=2, which='major') 177 | ax.tick_params('both', length=5, width=1, which='minor') 178 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 179 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 180 | ax.tick_params(axis='x', which='minor', bottom=True) 181 | ax.tick_params(axis='x', which='minor', top=True) 182 | ax.tick_params(axis='y', which='minor', left=True) 183 | ax.tick_params(axis='y', which='minor', right=True) 184 | ax.tick_params(bottom=True, top=True, left=True, right=True) 185 | 186 | ax.set_xlim([10.**log10_energy_nu_min, 10.**log10_energy_nu_max]) 187 | ax.set_xscale('log') 188 | 189 | if (i == 0): 190 | 191 | ax.set_xticklabels([]) 192 | ax_yticks_major = np.array([0.00, 0.25, 0.50, 0.75, 1.00]) 193 | ax.set_yticks(ax_yticks_major, minor=False) 194 | ax_yticks_minor = np.array([0.05, 0.10, 0.15, 0.20, 0.30, 0.35, 195 | 0.40, 0.45, 0.55, 0.60, 0.65, 0.70, 196 | 0.80, 0.85, 0.90, 0.95]) 197 | ax.set_yticks(ax_yticks_minor, minor=True) 198 | ax.set_ylim([0.0, 1.0]) 199 | ax.legend(loc='lower right', ncol=1, frameon=False, 200 | columnspacing=1.) 201 | 202 | elif (i == 1): 203 | 204 | ax.set_xticklabels([]) 205 | ax_yticks_major = np.array([0.00, 0.25, 0.50, 0.75]) 206 | ax.set_yticks(ax_yticks_major, minor=False) 207 | ax_yticks_minor = np.array([0.05, 0.10, 0.15, 0.20, 0.30, 0.35, 208 | 0.40, 0.45, 0.55, 0.60, 0.65, 0.70, 209 | 0.80, 0.85, 0.90, 0.95]) 210 | ax.set_yticks(ax_yticks_minor, minor=True) 211 | ax.set_ylim([0.0, 1.]) 212 | 213 | elif (i == 2): 214 | 215 | ax_yticks_major = np.array([0.00, 0.25, 0.50, 0.75]) 216 | ax.set_yticks(ax_yticks_major, minor=False) 217 | ax_yticks_minor = np.array([0.05, 0.10, 0.15, 0.20, 0.30, 0.35, 218 | 0.40, 0.45, 0.55, 0.60, 0.65, 0.70, 219 | 0.80, 0.85, 0.90, 0.95]) 220 | ax.set_yticks(ax_yticks_minor, minor=True) 221 | ax.set_ylim([0.0, 1.0]) 222 | 223 | pylab.savefig(output_path+'prob_2nu_vs_energy_compare.'+output_format, 224 | bbox_inches='tight', dpi=300) 225 | 226 | return 227 | 228 | # plot_probability_2nu_vs_energy_compare( output_format='pdf', 229 | # output_path='../fig/') 230 | 231 | -------------------------------------------------------------------------------- /src/hamiltonians2nu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Compute two-neutrino Hamiltonians for selected scenarios. 3 | 4 | This module contains the routines to compute the two-neutrino 5 | Hamiltonians for the following scenarios: oscillations in vacuum, in 6 | matter of constant density, in matter with non-standard interactions 7 | (NSI), and in a CPT-odd Lorentz invariance-violating background (LIV). 8 | 9 | Routine listings 10 | ---------------- 11 | 12 | * mixing_matrix_2nu - Returns 2x2 rotation matrix 13 | * hamiltonian_2nu_vacuum_energy_independent - Returns H_vac (no 1/E) 14 | * hamiltonian_2nu_matter - Returns H_matter 15 | * hamiltonian_2nu_nsi - Returns H_NSI 16 | * hamiltonian_2nu_liv - Returns H_LIV 17 | 18 | Created: 2019/04/21 15:00 19 | Last modified: 2019/04/23 21:04 20 | """ 21 | 22 | 23 | __version__ = "1.0" 24 | __author__ = "Mauricio Bustamante" 25 | __email__ = "mbustamante@gmail.com" 26 | 27 | 28 | from numpy import * 29 | import numpy as np 30 | import cmath 31 | import cmath as cmath 32 | import copy as cp 33 | 34 | import oscprob3nu 35 | from globaldefs import * 36 | 37 | 38 | def mixing_matrix_2nu(sth): 39 | r"""Returns the 2x2 rotation matrix. 40 | 41 | Computes and returns a 2x2 real rotation matrix parametrized by a 42 | single rotation angle theta. 43 | 44 | Parameters 45 | ---------- 46 | sth : float 47 | Sin(theta). 48 | 49 | Returns 50 | ------- 51 | list 52 | Rotation matrix [[cth, sth], [-sth, cth]], with cth = cos(theta) 53 | and sth = sin(theta). 54 | """ 55 | cth = sqrt(1.0-sth*sth) 56 | 57 | U00 = cth 58 | U01 = sth 59 | U10 = -sth 60 | U11 = cth 61 | 62 | return [[U00,U01],[U10,U11]] 63 | 64 | 65 | def hamiltonian_2nu_vacuum_energy_independent(sth, Dm2, 66 | compute_matrix_multiplication=False): 67 | r"""Returns the two-neutrino Hamiltonian for vacuum oscillations. 68 | 69 | Computes and returns the 2x2 real two-neutrino Hamiltonian for 70 | oscillations in vacuum, parametrized by a single mixing angle theta 71 | and a single mass-squared difference Dm2. The Hamiltonian is 72 | H = (1/2)*R.M2.R^dagger, with R the 2x2 rotation matrix and M2 the 73 | mass matrix. The multiplicative factor 1/E is not applied. 74 | 75 | Parameters 76 | ---------- 77 | sth : float 78 | Sin(theta). 79 | Dm2 : float 80 | Mass-squared difference Delta m^2. 81 | compute_matrix_multiplication : bool, optional 82 | If False (default), use the pre-computed expressions; otherwise, 83 | multiply R.M2.R^dagger live. 84 | 85 | Returns 86 | ------- 87 | list 88 | Hamiltonian 2x2 matrix. 89 | """ 90 | th = np.arcsin(sth) 91 | c2th = cos(2.0*th) 92 | s2th = sin(2.0*th) 93 | 94 | f = 1./4. 95 | 96 | if not compute_matrix_multiplication: 97 | 98 | H00 = Dm2*c2th 99 | H01 = -Dm2*s2th 100 | H10 = H01 101 | H11 = -H00 102 | 103 | H = [[H00*f,H01*f], [H10*f,H11*f]] 104 | 105 | else: 106 | 107 | # PMNS matrix 108 | R = np.array(mixing_matrix_2nu(sth)) 109 | # Mass matrix 110 | M2 = np.array([[Dm2, 0.0], [0.0, -Dm2]]) 111 | # Hamiltonian 112 | H = list(f*np.matmul(R, np.matmul(M2, matrix.transpose(R)))) 113 | 114 | return H 115 | 116 | 117 | def probabilities_2nu_vacuum_std(sth, Dm2, energy, L): 118 | r"""Returns 2nu oscillation vacuum probabilities, std. computation. 119 | 120 | Returns the probabilities for two-neutrino oscillations in vacuum, 121 | computed using the standard analytical expression of the 122 | probabilities. 123 | 124 | Parameters 125 | ---------- 126 | sth : float 127 | Sin(theta). 128 | Dm2 : float 129 | Mass-squared difference Delta m^2. 130 | energy : float 131 | Neutrino energy. 132 | L : float 133 | Baseline. 134 | 135 | Returns 136 | ------- 137 | list 138 | List of probabilities [Pee, Pem, Pme, Pmm]. 139 | """ 140 | arg = 1.27*Dm2*L/energy#/4.0 141 | cth = sqrt(1.0-sth*sth) 142 | s2th = 2.0*sth*cth 143 | 144 | Pem = s2th*s2th * pow(sin(arg), 2.0) 145 | Pme = Pem 146 | Pee = 1.0-Pem 147 | Pmm = 1.0-Pme 148 | 149 | prob = [Pee, Pem, Pme, Pmm] 150 | 151 | return prob 152 | 153 | 154 | def hamiltonian_2nu_matter(h_vacuum_energy_independent, energy, VCC): 155 | r"""Returns the two-neutrino Hamiltonian for matter oscillations. 156 | 157 | Computes and returns the 2x2 real two-neutrino Hamiltonian for 158 | oscillations in matter with constant density. 159 | 160 | Parameters 161 | ---------- 162 | h_vacuum_energy_independent : list 163 | Energy-independent part of the two-neutrino Hamiltonian for 164 | oscillations in vacuum. This is computed by the routine 165 | hamiltonian_2nu_vacuum_energy_independent. 166 | energy : float 167 | Neutrino energy. 168 | VCC : float 169 | Potential due to charged-current interactions of nu_e with 170 | electrons. 171 | 172 | Returns 173 | ------- 174 | list 175 | Hamiltonian 2x2 matrix. 176 | """ 177 | h_matter = cp.deepcopy(h_vacuum_energy_independent) 178 | h_matter = np.multiply(1.0/energy, h_matter) 179 | 180 | # Add the matter potential to the ee term to find the matter 181 | # Hamiltonian 182 | h_matter[0][0] += VCC 183 | 184 | return h_matter 185 | 186 | 187 | def probabilities_2nu_matter_std(sth, Dm2, VCC, energy, L): 188 | r"""Returns 2nu oscillation matter probabilities, std. computation. 189 | 190 | Returns the probabilities for two-neutrino oscillations in matter, 191 | computed using the standard analytical expression of the 192 | probabilities. 193 | 194 | Parameters 195 | ---------- 196 | sth : float 197 | Sin(theta). 198 | Dm2 : float 199 | Mass-squared difference Delta m^2. 200 | VCC : float 201 | Potential due to charged-current interactions of nu_e with 202 | electrons. 203 | energy : float 204 | Neutrino energy. 205 | L : float 206 | Baseline. 207 | 208 | Returns 209 | ------- 210 | list 211 | List of probabilities [Pee, Pem, Pme, Pmm]. 212 | """ 213 | x = 2.0*VCC*(energy*1.e9)/Dm2 214 | cth = sqrt(1.0-sth*sth) 215 | s2th = 2.0*sth*cth 216 | s2thsq = s2th*s2th 217 | c2th = sqrt(1.0-s2thsq) 218 | 219 | Dm2m = Dm2*sqrt(s2thsq+pow(c2th-x, 2.0)) 220 | s2thmsq = s2thsq / (s2thsq+pow(c2th-x, 2.0)) 221 | 222 | arg = 1.27*Dm2m*L/energy#/4.0 223 | 224 | Pem = s2thmsq * pow(sin(arg), 2.0) 225 | Pme = Pem 226 | Pee = 1.0-Pem 227 | Pmm = 1.0-Pme 228 | 229 | prob = [Pee, Pem, Pme, Pmm] 230 | 231 | return prob 232 | 233 | 234 | def hamiltonian_2nu_nsi(h_vacuum_energy_independent, energy, VCC, eps): 235 | r"""Returns the two-neutrino Hamiltonian for oscillations with NSI. 236 | 237 | Computes and returns the 2x2 real two-neutrino Hamiltonian for 238 | oscillations with non-standard interactions (NSI) in matter with 239 | constant density. 240 | 241 | Parameters 242 | ---------- 243 | h_vacuum_energy_independent : list 244 | Energy-independent part of the two-neutrino Hamiltonian for 245 | oscillations in vacuum. This is computed by the routine 246 | hamiltonian_2nu_vacuum_energy_independent. 247 | energy : float 248 | Neutrino energy. 249 | VCC : float 250 | Potential due to charged-current interactions of nu_e with 251 | electrons. 252 | eps : list 253 | Vector of NSI strength parameters: eps = eps_ee, eps_em, eps_mm. 254 | 255 | Returns 256 | ------- 257 | list 258 | Hamiltonian 2x2 matrix. 259 | """ 260 | h_nsi = cp.deepcopy(h_vacuum_energy_independent) 261 | h_nsi = np.multiply(1.0/energy, h_nsi) 262 | 263 | eps_ee, eps_em, eps_mm = eps 264 | 265 | h_nsi[0][0] += VCC*(1.0+eps_ee) 266 | h_nsi[0][1] += VCC*eps_em 267 | h_nsi[1][0] += VCC*np.conj(eps_em) 268 | h_nsi[1][1] += VCC*eps_mm 269 | 270 | return h_nsi 271 | 272 | 273 | def hamiltonian_2nu_liv(h_vacuum_energy_independent, energy, sxi, 274 | b1, b2, Lambda): 275 | r"""Returns the two-neutrino Hamiltonian for oscillations with LIV. 276 | 277 | Computes and returns the 2x2 real two-neutrino Hamiltonian for 278 | oscillations in a CPT-odd Lorentz invariance-violating background. 279 | 280 | Parameters 281 | ---------- 282 | h_vacuum_energy_independent : list 283 | Energy-independent part of the two-neutrino Hamiltonian for 284 | oscillations in vacuum. This is computed by the routine 285 | hamiltonian_2nu_vacuum_energy_independent. 286 | energy : float 287 | Neutrino energy. 288 | sxi : float 289 | Sin(xi), with xi the rotation angle between the space of the 290 | eigenvectors of B2 and the flavor states. 291 | b1 : float 292 | Eigenvalue b1 of the LIV operator B2. 293 | b2 : float 294 | Eigenvalue b2 of the LIV operator B2. 295 | Lambda : float 296 | Energy scale of the LIV operator B2. 297 | 298 | Returns 299 | ------- 300 | list 301 | Hamiltonian 2x2 matrix. 302 | """ 303 | h_liv = cp.deepcopy(h_vacuum_energy_independent) 304 | h_liv = np.multiply(1.0/energy, h_liv) 305 | 306 | f = energy/Lambda 307 | cxi = sqrt(1.0-sxi-sxi) 308 | 309 | h_liv[0][0] += f*(b1*cxi*cxi + b2*sxi*sxi) 310 | h_liv[0][1] += f*((-b1+b2)*cxi*sxi) 311 | h_liv[1][0] += f*((-b1+b2)*cxi*sxi) 312 | h_liv[1][1] += f*(b2*cxi*cxi + b1*sxi*sxi) 313 | 314 | return h_liv 315 | -------------------------------------------------------------------------------- /src/hamiltonians3nu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Compute three-neutrino Hamiltonians for selected scenarios. 3 | 4 | This module contains the routines to compute the three-neutrino 5 | Hamiltonians for the following scenarios: oscillations in vacuum, in 6 | matter of constant density, in matter with non-standard interactions 7 | (NSI), and in a CPT-odd Lorentz invariance-violating background (LIV). 8 | 9 | Routine listings 10 | ---------------- 11 | 12 | * mixing_matrix_2nu - Returns 2x2 rotation matrix 13 | * hamiltonian_2nu_vacuum_energy_independent - Returns H_vac (no 1/E) 14 | * delta - Kronecker delta 15 | * J - Product of four elements of PMNS matrix 16 | * probabilities_3nu_vacuum_std - Vacuum probability, std. formula 17 | * hamiltonian_2nu_matter - Returns H_matter 18 | * hamiltonian_2nu_nsi - Returns H_NSI 19 | * hamiltonian_2nu_liv - Returns H_LIV 20 | 21 | Created: 2019/04/17 17:14 22 | Last modified: 2019/04/30 01:03 23 | """ 24 | 25 | 26 | __version__ = "1.0" 27 | __author__ = "Mauricio Bustamante" 28 | __email__ = "mbustamante@gmail.com" 29 | 30 | 31 | from numpy import * 32 | import numpy as np 33 | import cmath 34 | import cmath as cmath 35 | import copy as cp 36 | 37 | import oscprob3nu 38 | from globaldefs import * 39 | 40 | 41 | def pmns_mixing_matrix(s12, s23, s13, dCP): 42 | r"""Returns the 3x3 PMNS mixing matrix. 43 | 44 | Computes and returns the 3x3 complex PMNS mixing matrix 45 | parametrized by three rotation angles, theta_12, theta_23, theta_13, 46 | and one CP-violation phase, delta_CP. 47 | 48 | Parameters 49 | ---------- 50 | s12 : float 51 | Sin(theta_12). 52 | s23 : float 53 | Sin(theta_23). 54 | s13 : float 55 | Sin(theta_13). 56 | dCP : float 57 | delta_CP [radian]. 58 | 59 | Returns 60 | ------- 61 | list 62 | 3x3 PMNS mixing matrix. 63 | """ 64 | c12 = sqrt(1.0-s12*s12) 65 | c23 = sqrt(1.0-s23*s23) 66 | c13 = sqrt(1.0-s13*s13) 67 | 68 | cdCP = cos(dCP) 69 | sdCP = sin(dCP) 70 | 71 | U00 = c12*c13 72 | U01 = s12*c13 73 | U02 = s13*complex(cdCP,-sdCP) 74 | U10 = -s12*c23 - c12*s23*s13*complex(cdCP,sdCP) 75 | U11 = c12*c23 - s12*s23*s13*complex(cdCP,sdCP) 76 | U12 = s23*c13 77 | U20 = s12*s23 - c12*c23*s13*complex(cdCP,sdCP) 78 | U21 = -c12*s23 - s12*c23*s13*complex(cdCP,sdCP) 79 | U22 = c23*c13 80 | 81 | return [[U00,U01,U02],[U10,U11,U12],[U20,U21,U22]] 82 | 83 | 84 | def hamiltonian_3nu_vacuum_energy_independent(s12, s23, s13, dCP, D21, D31, 85 | compute_matrix_multiplication=False): 86 | r"""Returns the three-neutrino Hamiltonian for vacuum oscillations. 87 | 88 | Computes and returns the 3x3 complex three-neutrino Hamiltonian for 89 | oscillations in vacuum, parametrized by three mixing angles --- 90 | theta_12, theta_23, theta_13 --- one CP-violation phase --- delta_CP 91 | --- and two mass-squared difference --- Delta m^2_21, Delta m^2_31. 92 | The Hamiltonian is H = (1/2)*R.M2.R^dagger, with R the 3x3 PMNS 93 | matrix and M2 the mass matrix. The multiplicative factor 1/E is not 94 | applied. 95 | 96 | Parameters 97 | ---------- 98 | s12 : float 99 | Sin(theta_12). 100 | s23 : float 101 | Sin(theta_23). 102 | s13 : float 103 | Sin(theta_13). 104 | D21 : float 105 | Mass-squared difference Delta m^2_21. 106 | D31 : float 107 | Mass-squared difference Delta m^2_31. 108 | compute_matrix_multiplication : bool, optional 109 | If False (default), use the pre-computed expressions; otherwise, 110 | multiply R.M2.R^dagger live. 111 | 112 | Returns 113 | ------- 114 | list 115 | Hamiltonian 3x3 matrix. 116 | """ 117 | c12 = sqrt(1.0-s12*s12) 118 | c23 = sqrt(1.0-s23*s23) 119 | c13 = sqrt(1.0-s13*s13) 120 | 121 | f = 1./2. 122 | 123 | if not compute_matrix_multiplication: 124 | 125 | # All Hij have units of [eV^2] 126 | H00 = c13*c13*D21*s12*s12 + D31*s13*s13 127 | H01 = c12*c13*c23*D21*s12 + \ 128 | c13*(D31-D21*s12*s12)*s13*s23*complex(cos(dCP),-sin(dCP)) 129 | H02 = c13*c23*(D31-D21*s12*s12)*s13*complex(cos(dCP),-sin(dCP)) - \ 130 | c12*c13*D21*s12*s23 131 | H10 = c12*c13*c23*D21*s12 + \ 132 | c13*(D31-D21*s12*s12)*s13*s23*complex(cos(dCP),sin(dCP)) 133 | H11 = c12*c12*c23*c23*D21 + (c13*c13*D31 + D21*s12*s12*s13*s13)*s23*s23 - \ 134 | 2.0*c12*c23*D21*s12*s13*s23*cos(dCP) 135 | H12 = c13*c13*c23*D31*s23 + \ 136 | (c23*s12*s13*complex(cos(dCP),-sin(dCP)) + c12*s23) * \ 137 | (-c12*c23*D21 + D21*s12*s13*s23*complex(cos(dCP),sin(dCP))) 138 | H20 = c13*c23*(D31-D21*s12*s12)*s13*complex(cos(dCP),sin(dCP)) - \ 139 | c12*c13*D21*s12*s23 140 | H21 = c13*c13*c23*D31*s23 - \ 141 | D21*(c23*s12*s13*complex(cos(dCP),sin(dCP)) + c12*s23) * \ 142 | (c12*c23 - s12*s13*s23*complex(cos(dCP),-sin(dCP))) 143 | H22 = c23*c23*(c13*c13*D31 + D21*s12*s12*s13*s13) + c12*c12*D21*s23*s23 + \ 144 | 2.0*c12*c23*D21*s12*s13*s23*cos(dCP) 145 | 146 | H = [[H00*f,H01*f,H02*f], [H10*f,H11*f,H12*f], [H20*f,H21*f,H22*f]] 147 | 148 | else: 149 | 150 | # PMNS matrix 151 | R = np.array(pmns_mixing_matrix(s12, s23, s13, dCP)) 152 | # Mass matrix 153 | M2 = np.array([[0.0, 0.0, 0.0], [0.0, D21, 0.0], [0.0, 0.0, D31]]) 154 | # Hamiltonian 155 | H = list(f*np.matmul(R, np.matmul(M2, np.conj(matrix.transpose(R))))) 156 | 157 | return H 158 | 159 | 160 | def delta(a, b): 161 | r"""Returns the Kronecker delta function. 162 | 163 | Returns the delta function delta(a, b) = 1 if a == b and 0 if 164 | a != b. 165 | 166 | Parameters 167 | ---------- 168 | a : int 169 | First index. 170 | b : int 171 | Second index. 172 | 173 | Returns 174 | ------- 175 | int 176 | delta(a, b). 177 | """ 178 | if (a == b): 179 | return 1 180 | else: 181 | return 0 182 | 183 | 184 | def J(U, alpha, beta, k, j): 185 | r"""Returns U*_ak * U_bk * U_aj * U*_bj, with U the PMNS matrix. 186 | 187 | Returns the product U*_ak * U_bk * U_aj * U*_bj, where U is the 188 | PMNS mixing matrix. This product appears in the standard expression 189 | for the three-neutrino oscillation probability in vacuum. 190 | 191 | Parameters 192 | ---------- 193 | U : list 194 | 3x3 PMNS complex mixing matrix. 195 | alpha : int 196 | Index of the initial flavor (0: e, 1: mu, 2: tau). 197 | beta : int 198 | Index of the final flavor (0: e, 1: mu, 2: tau). 199 | k : int 200 | First index of the sum over mass eigenstates (k = 0, 1, 2). 201 | j : int 202 | First index of the sum over mass eigenstates (k = 0, 1, 2). 203 | 204 | Returns 205 | ------- 206 | float 207 | J(U, alpha, beta, j, j) 208 | """ 209 | return np.conj(U[alpha][k])*U[beta][k]*U[alpha][j]*np.conj(U[beta][j]) 210 | 211 | 212 | def probabilities_3nu_vacuum_std(U, D21, D31, energy, L): 213 | r"""Returns 3nu oscillation vacuum probabilities, std. computation. 214 | 215 | Returns the probabilities for three-neutrino oscillations in vacuum, 216 | computed using the standard analytical expression of the 217 | probabilities. 218 | 219 | Parameters 220 | ---------- 221 | U : list 222 | 3x3 PMNS complex mixing matrix. 223 | D21 : float 224 | Mass-squared difference Delta m^2_21. 225 | D31 : float 226 | Mass-squared difference Delta m^2_31. 227 | energy : float 228 | Neutrino energy. 229 | L : float 230 | Baseline. 231 | 232 | Returns 233 | ------- 234 | list 235 | List of probabilities [Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, 236 | Ptt]. 237 | """ 238 | D32 = D31-D21 239 | arg21 = 2.54*D21*L/energy#/2.0 240 | arg31 = 2.54*D31*L/energy#/2.0 241 | arg32 = 2.54*D32*L/energy#/2.0 242 | s21 = sin(arg21) 243 | s31 = sin(arg31) 244 | s32 = sin(arg32) 245 | ss21 = pow(sin(arg21/2.0), 2.0) 246 | ss31 = pow(sin(arg31/2.0), 2.0) 247 | ss32 = pow(sin(arg32/2.0), 2.0) 248 | 249 | # Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt 250 | prob = [delta(alpha, beta) \ 251 | - 4.0 * ( J(U, alpha, beta, 1, 0).real*ss21 252 | + J(U, alpha, beta, 2, 0).real*ss31 253 | + J(U, alpha, beta, 2, 1).real*ss32 ) \ 254 | + 2.0 * ( J(U, alpha, beta, 1, 0).imag*s21 255 | + J(U, alpha, beta, 2, 0).imag*s31 256 | + J(U, alpha, beta, 2, 1).imag*s32 ) \ 257 | for alpha in [0,1,2] for beta in [0,1,2]] 258 | 259 | return prob 260 | 261 | 262 | def hamiltonian_3nu_matter(h_vacuum_energy_independent, energy, VCC): 263 | r"""Returns the three-neutrino Hamiltonian for matter oscillations. 264 | 265 | Computes and returns the 3x3 real three-neutrino Hamiltonian for 266 | oscillations in matter with constant density. 267 | 268 | Parameters 269 | ---------- 270 | h_vacuum_energy_independent : list 271 | Energy-independent part of the three-neutrino Hamiltonian for 272 | oscillations in vacuum. This is computed by the routine 273 | hamiltonian_3nu_vacuum_energy_independent. 274 | energy : float 275 | Neutrino energy. 276 | VCC : float 277 | Potential due to charged-current interactions of nu_e with 278 | electrons. 279 | 280 | Returns 281 | ------- 282 | list 283 | Hamiltonian 3x3 matrix. 284 | """ 285 | 286 | h_matter = cp.deepcopy(h_vacuum_energy_independent) 287 | h_matter = np.multiply(1.0/energy, h_matter) 288 | 289 | # Add the matter potential to the ee term to find the matter 290 | # Hamiltonian 291 | h_matter[0][0] += VCC 292 | 293 | return h_matter 294 | 295 | 296 | def hamiltonian_3nu_nsi(h_vacuum_energy_independent, energy, VCC, eps): 297 | r"""Returns the three-neutrino Hamiltonian for oscillations w/ NSI. 298 | 299 | Computes and returns the 3x3 complex three-neutrino Hamiltonian for 300 | oscillations with non-standard interactions (NSI) in matter with 301 | constant density. 302 | 303 | Parameters 304 | ---------- 305 | h_vacuum_energy_independent : list 306 | Energy-independent part of the two-neutrino Hamiltonian for 307 | oscillations in vacuum. This is computed by the routine 308 | hamiltonian_2nu_vacuum_energy_independent. 309 | energy : float 310 | Neutrino energy. 311 | VCC : float 312 | Potential due to charged-current interactions of nu_e with 313 | electrons. 314 | eps : list 315 | Vector of NSI strength parameters: eps = eps_ee, eps_em, eps_et, 316 | eps_mm, eps_mt, eps_tt. 317 | 318 | Returns 319 | ------- 320 | list 321 | Hamiltonian 3x3 matrix. 322 | """ 323 | h_nsi = cp.deepcopy(h_vacuum_energy_independent) 324 | h_nsi = np.multiply(1.0/energy, h_nsi) 325 | 326 | eps_ee, eps_em, eps_et, eps_mm, eps_mt, eps_tt = eps 327 | 328 | h_nsi[0][0] += VCC*(1.0+eps_ee) 329 | h_nsi[0][1] += VCC*eps_em 330 | h_nsi[0][2] += VCC*eps_et 331 | h_nsi[1][0] += VCC*np.conj(eps_em) 332 | h_nsi[1][1] += VCC*eps_mm 333 | h_nsi[1][2] += VCC*eps_mt 334 | h_nsi[2][0] += VCC*np.conj(eps_et) 335 | h_nsi[2][1] += VCC*np.conj(eps_mt) 336 | h_nsi[2][2] += VCC*eps_tt 337 | 338 | return h_nsi 339 | 340 | 341 | def hamiltonian_3nu_liv(h_vacuum_energy_independent, energy, sxi12, sxi23, 342 | sxi13, dxiCP, b1, b2, b3, Lambda): 343 | r"""Returns the three-neutrino Hamiltonian for oscillations w/ LIV. 344 | 345 | Computes and returns the 3x3 complex three-neutrino Hamiltonian for 346 | oscillations in a CPT-odd Lorentz invariance-violating background. 347 | 348 | Parameters 349 | ---------- 350 | h_vacuum_energy_independent : list 351 | Energy-independent part of the two-neutrino Hamiltonian for 352 | oscillations in vacuum. This is computed by the routine 353 | hamiltonian_2nu_vacuum_energy_independent. 354 | energy : float 355 | Neutrino energy. 356 | sxi12 : float 357 | Sin(xi_12), with xi_12 the one of the mixing angles between the 358 | space of the eigenvectors of B3 and the flavor states. 359 | sxi23 : float 360 | Sin(xi_23), with xi_23 the one of the mixing angles between the 361 | space of the eigenvectors of B3 and the flavor states. 362 | sxi13 : float 363 | Sin(xi_12), with xi_13 the one of the mixing angles between the 364 | space of the eigenvectors of B3 and the flavor states. 365 | dciCP : float 366 | CP-violation angle of the LIV operator B3 [radian]. 367 | b1 : float 368 | Eigenvalue b1 of the LIV operator B3. 369 | b2 : float 370 | Eigenvalue b2 of the LIV operator B3. 371 | b3 : float 372 | Eigenvalue b3 of the LIV operator B3. 373 | Lambda : float 374 | Energy scale of the LIV operator B2. 375 | 376 | Returns 377 | ------- 378 | list 379 | Hamiltonian 3x3 matrix. 380 | """ 381 | 382 | h_liv = cp.deepcopy(h_vacuum_energy_independent) 383 | h_liv = np.multiply(1.0/energy, h_liv) 384 | 385 | f = energy/Lambda 386 | # PMNS-like mixing matrix 387 | R = np.array(pmns_mixing_matrix(sxi12, sxi23, sxi13, dxiCP)) 388 | # B matrix 389 | B = np.array([[b1, 0.0, 0.0], [0.0, b2, 0.0], [0.0, 0.0, b3]]) 390 | # LIV term 391 | H = list(f*np.matmul(R, np.matmul(B, np.conj(matrix.transpose(R))))) 392 | 393 | h_liv[0][0] += H[0][0] 394 | h_liv[0][1] += H[0][1] 395 | h_liv[0][2] += H[0][2] 396 | h_liv[1][0] += H[1][0] 397 | h_liv[1][1] += H[1][1] 398 | h_liv[1][2] += H[1][2] 399 | h_liv[2][0] += H[2][0] 400 | h_liv[2][1] += H[2][1] 401 | h_liv[2][2] += H[2][2] 402 | 403 | return h_liv 404 | -------------------------------------------------------------------------------- /src/oscprob3nu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Compute the three-neutrino flavor-transition probability. 3 | 4 | This module contains all the necessary routines to compute the 5 | three-neutrino flavor-transition probabilities using the SU(3) 6 | exponential expansion. 7 | 8 | Routine listings 9 | ---------------- 10 | 11 | * hamiltonian_3nu_coefficients - Returns coefficients of Hamiltonian 12 | * tensor_d - Returns the value of the tensor :math:`d_{i,jk}` 13 | * star - Returns the SU(3) product :math:`(h * h)_k` 14 | * su3_invariants - Returns the SU(3) invariants :math:`|h|^2, ` 15 | * psi_roots - Returns the roots of the characteristic equation 16 | * evolution_operator_3nu_u_coefficients - Returns the :math:`u_k` 17 | * evolution_operator_3nu - Returns evolution operator :math:`U_3` 18 | * probabilities_3nu - Returns the oscillation probabilities 19 | 20 | References 21 | ---------- 22 | 23 | .. [1] A.J. MacFarlane, A. Sudbery, and P.H. Weisz, "On Gell-Mann's 24 | :math:`\lambda`-matrices, :math:`d`- and math`f`-tensors, octets, and 25 | parametrizations of SU(3)", Commun. Math. Phys. 11, 77 (1968). 26 | 27 | .. [2] Mauricio Bustamante, "Exact neutrino oscillation probabilities 28 | with arbitrary time-independent Hamiltonians", arXiv:1904.XXXXX. 29 | 30 | Created: 2019/04/11 15:36 31 | Last modified: 2019/04/20 18:47 32 | """ 33 | 34 | 35 | __version__ = "1.0" 36 | __author__ = "Mauricio Bustamante" 37 | __email__ = "mbustamante@gmail.com" 38 | 39 | 40 | from numpy import * 41 | import numpy as np 42 | import cmath 43 | import cmath as cmath 44 | 45 | 46 | SQRT3 = sqrt(3.0) 47 | r"""float: Module-level constant 48 | 49 | Constant equal to sqrt(3.0). 50 | """ 51 | 52 | SQRT3_INV = 1./sqrt(3.0) 53 | r"""float: Module-level constant 54 | 55 | Constant equal to 1.0/sqrt(3.0). 56 | """ 57 | 58 | NEG_HALF_SQRT3_INV = -SQRT3_INV/2.0 59 | r"""float: Module-level constant 60 | 61 | Constant equal to -1.0/sqrt(3.0)/2.0. 62 | """ 63 | 64 | 65 | def hamiltonian_3nu_coefficients(hamiltonian_matrix): 66 | r"""Returns the h_k of the SU(3)-expansion of the 3nu Hamiltonian. 67 | 68 | Computes the coefficients :math:`h_1, ..., h_8` in the SU(3) 69 | expansion of the provided three-flavor Hamiltonian 70 | `hamiltonian_matrix`, which is assumed to be given in the flavor 71 | basis. The Hamiltonian is a :math:`3\times3` Hermitian matrix. 72 | 73 | Parameters 74 | ---------- 75 | hamiltonian_matrix : array_like 76 | Three-flavor Hamiltonian matrix, given as the list 77 | [[H11, H12, H13], [H12*, H22, H23], [H13*, H23*, H33]], where 78 | the componentes Hij are complex numbers. 79 | 80 | Returns 81 | ------- 82 | list 83 | List of coefficients [h1, h2, h3, h4, h5, h6, h7, h8]. These 84 | are complex numbers, in general. 85 | 86 | Example 87 | ------- 88 | >>> hamiltonian_matrix = [ 89 | ... [1.0+0.0j, 0.0+2.0j, 0.0-1.0j], 90 | ... [0.0-2.0j, 3.0+0.0j, 3.0+0.0j], 91 | ... [0.0+1.0j, 3.0-0.0j, -5.0+0.0j] 92 | ... ] 93 | >>> h_coeffs = hamiltonian_3nu_coefficients(hamiltonian_matrix) 94 | >>> print(h_coeffs) 95 | [0.0, -2.0, (-1+0j), 0.0, 1.0, 3.0, -0.0, (-1.7320508075688774+0j)] 96 | """ 97 | H11 = hamiltonian_matrix[0][0] 98 | H12 = hamiltonian_matrix[0][1] 99 | H13 = hamiltonian_matrix[0][2] 100 | H21 = hamiltonian_matrix[1][0] 101 | H22 = hamiltonian_matrix[1][1] 102 | H23 = hamiltonian_matrix[1][2] 103 | H31 = hamiltonian_matrix[2][0] 104 | H32 = hamiltonian_matrix[2][1] 105 | H33 = hamiltonian_matrix[2][2] 106 | 107 | # h0 = (H11+H22+H33)/3.0 # Not used 108 | h1 = H12.real 109 | h2 = -H12.imag 110 | h3 = (H11-H22)/2.0 111 | h4 = H13.real 112 | h5 = -H13.imag 113 | h6 = H23.real 114 | h7 = -H23.imag 115 | h8 = (H11+H22-2.0*H33)*SQRT3/6.0 116 | 117 | return [h1, h2, h3, h4, h5, h6, h7, h8] 118 | 119 | 120 | def tensor_d(i, j, k): 121 | r"""Returns the tensor d_ijk of the SU(3) algebra. 122 | 123 | Returns the SU(3) tensor d_ijk. 124 | 125 | Parameters 126 | ---------- 127 | i : int 128 | First index. 129 | j : int 130 | Second index. 131 | k : int 132 | Third index. 133 | 134 | Returns 135 | ------- 136 | float 137 | Value of the tensor d_ijk. 138 | """ 139 | ip1 = i+1 140 | jp1 = j+1 141 | kp1 = k+1 142 | jkp1 = (jp1, kp1) 143 | 144 | if (ip1 == 1): 145 | if jkp1 == (1,8): return SQRT3_INV 146 | if jkp1 == (4,6): return 0.5 147 | if jkp1 == (5,7): return 0.5 148 | if jkp1 == (6,4): return 0.5 149 | if jkp1 == (7,5): return 0.5 150 | if jkp1 == (8,1): return SQRT3_INV 151 | return 0.0 152 | elif (ip1 == 2): 153 | if jkp1 == (2,8): return SQRT3_INV 154 | if jkp1 == (4,7): return -0.5 155 | if jkp1 == (5,6): return 0.5 156 | if jkp1 == (6,5): return 0.5 157 | if jkp1 == (7,4): return -0.5 158 | if jkp1 == (8,2): return SQRT3_INV 159 | return 0.0 160 | elif (ip1 == 3): 161 | if jkp1 == (3,8): return SQRT3_INV 162 | if jkp1 == (4,4): return 0.5 163 | if jkp1 == (5,5): return 0.5 164 | if jkp1 == (6,6): return -0.5 165 | if jkp1 == (7,7): return -0.5 166 | if jkp1 == (8,3): return SQRT3_INV 167 | return 0.0 168 | elif (ip1 == 4): 169 | if jkp1 == (1,6): return 0.5 170 | if jkp1 == (2,7): return -0.5 171 | if jkp1 == (3,4): return 0.5 172 | if jkp1 == (4,3): return 0.5 173 | if jkp1 == (4,8): return NEG_HALF_SQRT3_INV 174 | if jkp1 == (6,1): return 0.5 175 | if jkp1 == (7,2): return -0.5 176 | if jkp1 == (8,4): return NEG_HALF_SQRT3_INV 177 | return 0.0 178 | elif (ip1 == 5): 179 | if jkp1 == (1,7): return 0.5 180 | if jkp1 == (2,6): return 0.5 181 | if jkp1 == (3,5): return 0.5 182 | if jkp1 == (5,3): return 0.5 183 | if jkp1 == (5,8): return NEG_HALF_SQRT3_INV 184 | if jkp1 == (6,2): return 0.5 185 | if jkp1 == (7,1): return 0.5 186 | if jkp1 == (8,5): return NEG_HALF_SQRT3_INV 187 | return 0.0 188 | elif (ip1 == 6): 189 | if jkp1 == (1,4): return 0.5 190 | if jkp1 == (2,5): return 0.5 191 | if jkp1 == (3,6): return -0.5 192 | if jkp1 == (4,1): return 0.5 193 | if jkp1 == (5,2): return 0.5 194 | if jkp1 == (6,3): return -0.5 195 | if jkp1 == (6,8): return NEG_HALF_SQRT3_INV 196 | if jkp1 == (8,6): return NEG_HALF_SQRT3_INV 197 | return 0.0 198 | elif (ip1 == 7): 199 | if jkp1 == (1,5): return 0.5 200 | if jkp1 == (2,4): return -0.5 201 | if jkp1 == (3,7): return -0.5 202 | if jkp1 == (4,2): return -0.5 203 | if jkp1 == (5,1): return 0.5 204 | if jkp1 == (7,3): return -0.5 205 | if jkp1 == (7,8): return NEG_HALF_SQRT3_INV 206 | if jkp1 == (8,7): return NEG_HALF_SQRT3_INV 207 | return 0.0 208 | elif (ip1 == 8): 209 | if jkp1 == (1,1): return SQRT3_INV 210 | if jkp1 == (2,2): return SQRT3_INV 211 | if jkp1 == (3,3): return SQRT3_INV 212 | if jkp1 == (4,4): return NEG_HALF_SQRT3_INV 213 | if jkp1 == (5,5): return NEG_HALF_SQRT3_INV 214 | if jkp1 == (6,6): return NEG_HALF_SQRT3_INV 215 | if jkp1 == (7,7): return NEG_HALF_SQRT3_INV 216 | if jkp1 == (8,8): return -SQRT3_INV 217 | return 0.0 218 | 219 | 220 | def star(i, h_coeffs): 221 | r"""Returns the SU(3) star oroduct (h*h)_i. 222 | 223 | Returns the SU(3) star product (h*h)_i = d_ijk*h^j*h^k (summed over 224 | repeated indices). 225 | 226 | Parameters 227 | ---------- 228 | i : int 229 | Index of the star product. 230 | h_coeffs : array_like 231 | Eight-component vector. 232 | 233 | Returns 234 | ------- 235 | float 236 | Star product (h*h)_i. 237 | """ 238 | res = sum([tensor_d(i,j,k)*h_coeffs[j]*h_coeffs[k] 239 | for j in range(0,8) for k in range(0,8)]) 240 | 241 | return res 242 | 243 | 244 | def su3_invariants(h_coeffs): 245 | r"""Returns the two SU(3) invariants, |h|^2 and . 246 | 247 | Returns the two SU(3) invariants computed from the coefficients 248 | h_coeffs. 249 | 250 | Parameters 251 | ---------- 252 | h_coeffs : array_like 253 | Eight-component vector. 254 | 255 | Returns 256 | ------- 257 | h2 : float 258 | SU(3) invariant |h|^2. 259 | h3 : float 260 | SU(3) invariant . 261 | """ 262 | # h2 = |h|^2 263 | h2 = sum([h*h for h in h_coeffs]) 264 | 265 | # h3 = 266 | h3 = sum([tensor_d(i,j,k)*h_coeffs[i]*h_coeffs[j]*h_coeffs[k] 267 | for i in range(0,8) for j in range(0,8) for k in range(0,8)]) 268 | 269 | return h2, h3 270 | 271 | 272 | def psi_roots(h2, h3): 273 | r"""Returns the roots psi. 274 | 275 | Returns the three latent roots psi of the characteristic equation of 276 | -h_h*lambda^k. These roots are L-independent. 277 | 278 | Parameters 279 | ---------- 280 | h2 : float 281 | SU(3) invariant |h|^2. 282 | h3 : float 283 | SU(3) invariant . 284 | 285 | Returns 286 | ------- 287 | roots : list 288 | The three roots [psi1, psi2, psi3]. 289 | """ 290 | pre = 2.0*sqrt(h2)*SQRT3_INV 291 | chi = cmath.acos(-SQRT3*h3*pow(h2,-1.5)) 292 | 293 | roots = [pre*cmath.cos((chi+2.*np.pi*m)/3.0) for m in [1,2,3]] 294 | 295 | return roots 296 | 297 | 298 | def evolution_operator_3nu_u_coefficients(hamiltonian_matrix, L): 299 | r"""Returns coefficients u0, ..., u8 of the 3nu evolution operator. 300 | 301 | Returns the nine coefficients u0, ..., u8 of the three-neutrino 302 | time-evolution operator U3(L) in its SU(3) exponential expansion, 303 | i.e., U3 = u0*I + i*u_k*lambda^k. 304 | 305 | Parameters 306 | ---------- 307 | hamiltonian_matrix : list 308 | 3x3 Hamiltonian, [[H11,H12,H13],[H21,H22,H23],[H31,H32,H33]]. 309 | L : float 310 | Baseline. 311 | 312 | Returns 313 | ------- 314 | list 315 | The nine coefficients [u0, u1, u2, u3, u4, u5, u6, u7, u8]. 316 | 317 | Example 318 | ------- 319 | >>> hamiltonian_matrix = [ 320 | ... [1.0+0.0j, 0.0+2.0j, 0.0-1.0j], 321 | ... [0.0-2.0j, 3.0+0.0j, 3.0+0.0j], 322 | ... [0.0+1.0j, 3.0-0.0j, -5.0+0.0j] 323 | ... ] 324 | >>> L = 1.0 325 | >>> u_coeffs = \ 326 | ... evolution_operator_3nu_u_coefficients(hamiltonian_matrix, L) 327 | >>> print(u_coeffs) 328 | [(0.02931819348583329-0.05379039810587031j), 0j, 329 | (-0.37401185730706943+0.5232654591567741j), 330 | (-0.22397496123471491-0.18194182213957913j), 0j, 331 | (0.17020182293481648-0.4632575258138263j), 332 | (0.6584815991291701+0.3845256294303862j), 0j, 333 | (-0.37629378652956447-0.17544272350921003j)] 334 | """ 335 | # [h1, h2, h3, h4, h5, h6, h7, h8] 336 | h_coeffs = hamiltonian_3nu_coefficients(hamiltonian_matrix) 337 | 338 | # h2 = |h|^2, h3 = 339 | h2, h3 = su3_invariants(h_coeffs) 340 | 341 | # [psi1, psi2, psi3] 342 | psi = psi_roots(h2, h3) 343 | 344 | # [e^{i*L*psi1}, e^{i*L*psi2}, e^{i*L*psi3}] 345 | exp_psi = [cmath.exp(1.j*L*x) for x in psi] 346 | 347 | u0 = sum([x for x in exp_psi])/3. 348 | uk = [ 1.j*sum([exp_psi[m]*(psi[m]*h_coeffs[k]-star(k,h_coeffs)) \ 349 | /(3.*psi[m]*psi[m]-h2) for m in [0,1,2]]) for k in range(0,8)] 350 | 351 | # [u0, u1, u2, u3, u4, u5, u6, u7, u8] 352 | u_coeffs = [u0]+uk 353 | 354 | return u_coeffs 355 | 356 | 357 | def evolution_operator_3nu(hamiltonian_matrix, L): 358 | r"""Returns the 3nu time-evolution operator in its SU(3) expanstion. 359 | 360 | Returns the three-neutrino time-evolution operator U3(L) in its 361 | exponential SU(3) expansion U3(L) = u0*I + i*u_k*lambda^k. This is 362 | a 3x3 unitary matrix. 363 | 364 | Parameters 365 | ---------- 366 | hamiltonian_matrix : list 367 | 3x3 Hamiltonian, [[H11,H12,H13],[H21,H22,H23],[H31,H32,H33]]. 368 | L : float 369 | Baseline. 370 | 371 | Returns 372 | ------- 373 | list 374 | The U3(L) time-evolution operator, a 3x3 unitary complex matrix. 375 | 376 | Example 377 | ------- 378 | >>> hamiltonian_matrix = [ 379 | ... [1.0+0.0j, 0.0+2.0j, 0.0-1.0j], 380 | ... [0.0-2.0j, 3.0+0.0j, 3.0+0.0j], 381 | ... [0.0+1.0j, 3.0-0.0j, -5.0+0.0j] 382 | ... ] 383 | >>> L = 1.0 384 | >>> U3 = evolution_operator_3nu(hamiltonian_matrix, L) 385 | >>> print(U3) 386 | [[ 0.312551-0.495018j -0.374011+0.523265j 0.170201-0.463257j] 387 | [ 0.374011-0.523265j -0.051331-0.047068j -0.384525+0.65848j ] 388 | [-0.170201+0.463257j -0.384525+0.65848j -0.173265+0.380716j]] 389 | """ 390 | u0, u1, u2, u3, u4, u5, u6, u7, u8 = \ 391 | evolution_operator_3nu_u_coefficients(hamiltonian_matrix, L) 392 | 393 | evolution_operator = [ 394 | [u0+1.j*(u3+u8/SQRT3), 1.j*u1+u2, 1.j*u4+u5], 395 | [1.j*u1-u2, u0-1.j*(u3-u8/SQRT3), 1.j*u6+u7], 396 | [1.j*u4-u5, 1.j*u6-u7, u0-1.j*2.*u8/SQRT3] 397 | ] 398 | 399 | return evolution_operator 400 | 401 | 402 | def probabilities_3nu(hamiltonian_matrix, L): 403 | r"""Returns the 3nu oscillation probability. 404 | 405 | Returns the three-neutrino oscillation probabilities 406 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt. 407 | 408 | Parameters 409 | ---------- 410 | hamiltonian_matrix : list 411 | 3x3 Hamiltonian, [[H11,H12,H13],[H21,H22,H23],[H31,H32,H33]]. 412 | L : float 413 | Baseline. 414 | 415 | Returns 416 | ------- 417 | list 418 | Three-neutrino probabilities 419 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt. 420 | 421 | Example 422 | ------- 423 | >>> hamiltonian_matrix = [ 424 | ... [1.0+0.0j, 0.0+2.0j, 0.0-1.0j], 425 | ... [0.0-2.0j, 3.0+0.0j, 3.0+0.0j], 426 | ... [0.0+1.0j, 3.0-0.0j, -5.0+0.0j] 427 | ... ] 428 | >>> L = 1.0 429 | >>> Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = \ 430 | ... probabilities_3nu(hamiltonian_matrix, 1.0) 431 | >>> print(Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt) 432 | 0.342732 0.413691 0.243576 0.413691 0.0048504 0.58145 0.243576 433 | 0.58145 0.174965 434 | """ 435 | U = evolution_operator_3nu(hamiltonian_matrix, L) 436 | 437 | Pee = abs(U[0][0])**2. 438 | Pem = abs(U[1][0])**2. 439 | Pet = abs(U[2][0])**2. 440 | Pme = abs(U[0][1])**2. 441 | Pmm = abs(U[1][1])**2. 442 | Pmt = abs(U[2][1])**2. 443 | Pte = abs(U[0][2])**2. 444 | Ptm = abs(U[1][2])**2. 445 | Ptt = abs(U[2][2])**2. 446 | 447 | return Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt 448 | -------------------------------------------------------------------------------- /test/oscprob2nu_plot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Routines to plot two-neutrino flavor-transition probabilities. 3 | 4 | This module contains contains routines to plot two-neutrino 5 | oscillation probabilities vs. the neutrino baseline and energy. These 6 | routines are used by run_testsuite.py to produce a suite of test plots. 7 | 8 | Routine listings 9 | ---------------- 10 | 11 | * plot_probability_2nu_vs_baseline - Plot probabilities vs. baseline 12 | * plot_probability_2nu_vs_energy - Plot probabilities vs. energy 13 | 14 | Created: 2019/04/22 18:35 15 | Last modified: 2019/04/22 19:31 16 | """ 17 | 18 | 19 | __version__ = "1.0" 20 | __author__ = "Mauricio Bustamante" 21 | __email__ = "mbustamante@gmail.com" 22 | 23 | 24 | from numpy import * 25 | import numpy as np 26 | from pylab import * 27 | from matplotlib import * 28 | import matplotlib as mpl 29 | 30 | import sys 31 | sys.path.append('../src') 32 | sys.path.append('../test') 33 | 34 | import oscprob2nu 35 | import hamiltonians2nu 36 | from globaldefs import * 37 | 38 | 39 | def plot_probability_2nu_vs_baseline( 40 | case, sector, energy=1.e-1, 41 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=6000, 42 | plot_prob_ee=True, plot_prob_em=True, plot_prob_mm=False, 43 | output_filename='prob_vs_baseline', output_format='pdf', 44 | output_path='../fig/', legend_loc='center left', 45 | legend_ncol=1): 46 | r"""Generates and saves a plot of 2nu probabilities vs. baseline. 47 | 48 | Generates a plot of two-neutrino oscillation probabilities vs. 49 | baseline, for a fixed neutrino energy. The probabilities to be 50 | plotted are turned on and off via the flags plot_prob_ee, 51 | plot_prob_em, etc. (At least one of them must be True.) The 52 | parameter 'case' selects between 'vacuum', 'matter', 'nsi', and 53 | 'liv' (see below). The plot is saved with the provided name and 54 | file format under the specified path. 55 | 56 | Parameters 57 | ---------- 58 | case : str 59 | Not optional. Must be one of the following: 'vacuum', 'matter', 60 | 'nsi', or 'liv'. In each case, the probabilities are computed 61 | using the default parameter values pulled from globaldefs. 62 | sector : str 63 | Not optional. Must be one of the following: '12' (for nu_e <--> 64 | nu_mu oscillations) of '23' (for nu_mu <--> nu_tau 65 | oscillations). 66 | energy : float, optional 67 | Neutrino energy [GeV]. 68 | log10_l_min : float, optional 69 | Log10 of the minimum baseline [km]. 70 | log10_l_max : float, optional 71 | Log10 of the maximum baseline [km]. 72 | log10_l_npts : int, optional 73 | Number of baseline values at which to compute the probabilities. 74 | plot_prob_ee : bool, optional 75 | True to plot Pee (if sector == '12') or Pmm (if sector == '23), 76 | False otherwise. 77 | plot_prob_em : bool, optional 78 | True to plot Pem (if sector == '12') or Pmt (if sector == '23), 79 | False otherwise. 80 | plot_prob_mm : bool, optional 81 | True to plot Pmm (if sector == '12') or Ptt (if sector == '23), 82 | False otherwise. 83 | output_filename : str, optional 84 | File name of plot to save (without the file extension). 85 | output_format : str, optional 86 | File extension of the plot to save (e.g., 'pdf', 'png', 'jpg'). 87 | output_path : str, optional 88 | File path where to save the plot. 89 | legend_loc : str, optional 90 | Location of the legend in the plot. Must be one of the allowed 91 | values of the plot routine of matplotlib. 92 | legend_ncol : int, optional 93 | Number of columns to include in the legend box. Must be at 94 | least 1. 95 | 96 | Returns 97 | ------- 98 | None 99 | The plot is generated and saved. 100 | """ 101 | if (not plot_prob_ee) and (not plot_prob_em) \ 102 | and (not plot_prob_me) and (not plot_prob_mm): 103 | quit() 104 | 105 | # Baselines, L 106 | log10_l_val = np.linspace(log10_l_min, log10_l_max, log10_l_npts) 107 | l_val =[10.**x for x in log10_l_val] 108 | 109 | if sector == '12': 110 | sth = S12_NO_BF 111 | Dm2 = D21_NO_BF 112 | label_ee = r'$P_{\nu_e \to \nu_e}$' 113 | label_em = r'$P_{\nu_e \to \nu_\mu}$' 114 | label_mm = r'$P_{\nu_\mu \to \nu_\mu}$' 115 | color_ee = 'C0' 116 | color_em = 'C1' 117 | color_mm = 'C4' 118 | elif sector == '23': 119 | sth = S23_NO_BF 120 | Dm2 = D31_NO_BF 121 | label_ee = r'$P_{\nu_\mu \to \nu_\mu}$' 122 | label_em = r'$P_{\nu_\mu \to \nu_\tau}$' 123 | label_mm = r'$P_{\nu_\tau \to \nu_\tau}$' 124 | color_ee = 'C4' 125 | color_em = 'C5' 126 | color_mm = 'C8' 127 | 128 | h_vacuum_energy_independent = \ 129 | hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent(sth, Dm2) 130 | 131 | if (case.lower() == 'vacuum'): 132 | 133 | hamiltonian = np.multiply(1./energy/1.e9, h_vacuum_energy_independent) 134 | label_case = r'Vacuum' 135 | 136 | elif (case.lower() == 'matter'): 137 | 138 | hamiltonian = hamiltonians2nu.hamiltonian_2nu_matter( \ 139 | h_vacuum_energy_independent, 140 | energy*1.e9, 141 | VCC_EARTH_CRUST) 142 | label_case = r'Matter' 143 | 144 | elif (case.lower() == 'nsi'): 145 | 146 | hamiltonian = hamiltonians2nu.hamiltonian_2nu_nsi( \ 147 | h_vacuum_energy_independent, 148 | energy*1.e9, 149 | VCC_EARTH_CRUST, 150 | EPS_2) 151 | label_case = r'NSI' 152 | 153 | elif (case.lower() == 'liv'): 154 | 155 | hamiltonian = hamiltonians2nu.hamiltonian_2nu_liv( \ 156 | h_vacuum_energy_independent, 157 | energy*1.e9, 158 | SXI12, 159 | B1, B3, LAMBDA) 160 | label_case = r'CPT-odd LIV' 161 | 162 | 163 | # Each element of prob: [Pee, Pem, Pmm] 164 | prob = [oscprob2nu.probabilities_2nu( hamiltonian, 165 | l*CONV_KM_TO_INV_EV) \ 166 | for l in l_val] 167 | prob_ee = [x[0] for x in prob] 168 | prob_em = [x[1] for x in prob] 169 | prob_mm = [x[3] for x in prob] 170 | 171 | # Formatting 172 | mpl.rcParams['xtick.labelsize']=26 173 | mpl.rcParams['ytick.labelsize']=26 174 | mpl.rcParams['legend.fontsize']=26 175 | mpl.rcParams['legend.borderpad']=0.4 176 | mpl.rcParams['axes.labelpad']=10 177 | mpl.rcParams['ps.fonttype']=42 178 | mpl.rcParams['pdf.fonttype']=42 179 | 180 | fig = plt.figure(figsize=[9,9]) 181 | ax = fig.add_subplot(1,1,1) 182 | 183 | ax.set_xlabel(r'Baseline $L$ [km]', fontsize=25) 184 | ax.set_ylabel(r'Two-neutrino probability', fontsize=25) 185 | 186 | yaxis_minor_locator = mpl.ticker.MultipleLocator(0.1) 187 | ax.yaxis.set_minor_locator(yaxis_minor_locator) 188 | 189 | ax.tick_params('both', length=10, width=2, which='major') 190 | ax.tick_params('both', length=5, width=1, which='minor') 191 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 192 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 193 | ax.tick_params(axis='x', which='minor', bottom=True) 194 | ax.tick_params(axis='x', which='minor', top=True) 195 | ax.tick_params(axis='y', which='minor', left=True) 196 | ax.tick_params(axis='y', which='minor', right=True) 197 | ax.tick_params(bottom=True, top=True, left=True, right=True) 198 | 199 | ax.set_xlim([10.**log10_l_min, 10.**log10_l_max]) 200 | ax.set_xscale('log') 201 | ax.set_ylim([0.0, 1.0]) 202 | 203 | # Plot 204 | if (plot_prob_ee): 205 | ax.plot(l_val, prob_ee, label=label_ee, 206 | color=color_ee, zorder=1) 207 | if (plot_prob_em): 208 | ax.plot(l_val, prob_em, label=label_em, 209 | color=color_em, zorder=1) 210 | if (plot_prob_mm): 211 | ax.plot(l_val, prob_mm, label=label_mm, 212 | color=color_mm, zorder=1) 213 | 214 | # Legend 215 | ax.legend(loc=legend_loc, frameon=False, ncol=legend_ncol) 216 | 217 | # Annotations 218 | ax.annotate( label_case, xy = (0.05, 0.86), \ 219 | xycoords='axes fraction', color='k', fontsize=25, 220 | horizontalalignment='left', rotation=0, zorder=2 ) 221 | ax.annotate( r'$\log_{10}(E/{\rm GeV}) = $' + \ 222 | str(int(log10(energy)*100.)/100.), 223 | xy = (0.05, 0.80), xycoords='axes fraction', color='k', fontsize=25, 224 | horizontalalignment='left', rotation=0, zorder=2 ) 225 | 226 | pylab.savefig(output_path+output_filename+'.'+output_format, 227 | bbox_inches='tight', dpi=100) 228 | 229 | plt.close() 230 | 231 | return 232 | 233 | 234 | def plot_probability_2nu_vs_energy( 235 | case, sector, baseline=1.3e3, 236 | log10_energy_min=-1.0, log10_energy_max=1.0, 237 | log10_energy_npts=200, 238 | plot_prob_ee=True, plot_prob_em=True, plot_prob_mm=False, 239 | output_filename='prob_vs_energy', output_format='pdf', 240 | output_path='../fig/', legend_loc='center right', 241 | legend_ncol=1): 242 | r"""Generates and saves a plot of 2nu probabilities vs. energy. 243 | 244 | Generates a plot of two-neutrino oscillation probabilities vs. 245 | energy, for a fixed neutrino baseline. The probabilities to be 246 | plotted are turned on and off via the flags plot_prob_ee, 247 | plot_prob_em, etc. (At least one of them must be True.) The 248 | parameter 'case' selects between 'vacuum', 'matter', 'nsi', and 249 | 'liv' (see below). The plot is saved with the provided name and 250 | file format under the specified path. 251 | 252 | Parameters 253 | ---------- 254 | case : str 255 | Not optional. Must be one of the following: 'vacuum', 'matter', 256 | 'nsi', or 'liv'. In each case, the probabilities are computed 257 | using the default parameter values pulled from globaldefs. 258 | sector : str 259 | Not optional. Must be one of the following: '12' (for nu_e <--> 260 | nu_mu oscillations) of '23' (for nu_mu <--> nu_tau 261 | oscillations). 262 | baseline : float, optional 263 | Neutrino baseline [km]. 264 | log10_energy_min : float, optional 265 | Log10 of the minimum energy [GeV]. 266 | log10_energy_max : float, optional 267 | Log10 of the maximum energy [GeV]. 268 | log10_energy_npts : int, optional 269 | Number of energy values at which to compute the probabilities. 270 | plot_prob_ee : bool, optional 271 | True to plot Pee (if sector == '12') or Pmm (if sector == '23), 272 | False otherwise. 273 | plot_prob_em : bool, optional 274 | True to plot Pem (if sector == '12') or Pmt (if sector == '23), 275 | False otherwise. 276 | plot_prob_mm : bool, optional 277 | True to plot Pmm (if sector == '12') or Ptt (if sector == '23), 278 | False otherwise. 279 | output_filename : str, optional 280 | File name of plot to save (without the file extension). 281 | output_format : str, optional 282 | File extension of the plot to save (e.g., 'pdf', 'png', 'jpg'). 283 | output_path : str, optional 284 | File path where to save the plot. 285 | legend_loc : str, optional 286 | Location of the legend in the plot. Must be one of the allowed 287 | values of the plot routine of matplotlib. 288 | legend_ncol : int, optional 289 | Number of columns to include in the legend box. Must be at 290 | least 1. 291 | 292 | Returns 293 | ------- 294 | None 295 | The plot is generated and saved. 296 | """ 297 | if (not plot_prob_ee) and (not plot_prob_em) \ 298 | and (not plot_prob_me) and (not plot_prob_mm): 299 | quit() 300 | 301 | baseline = baseline*CONV_KM_TO_INV_EV # [eV^{-1}] 302 | 303 | # Neutrino energies 304 | log10_energy_val = np.linspace( log10_energy_min, log10_energy_max, 305 | log10_energy_npts) 306 | energy_val =[10.**x for x in log10_energy_val] 307 | 308 | if sector == '12': 309 | sth = S12_NO_BF 310 | Dm2 = D21_NO_BF 311 | label_ee = r'$P_{\nu_e \to \nu_e}$' 312 | label_em = r'$P_{\nu_e \to \nu_\mu}$' 313 | label_mm = r'$P_{\nu_\mu \to \nu_\mu}$' 314 | color_ee = 'C0' 315 | color_em = 'C1' 316 | color_mm = 'C4' 317 | elif sector == '23': 318 | sth = S23_NO_BF 319 | Dm2 = D31_NO_BF 320 | label_ee = r'$P_{\nu_\mu \to \nu_\mu}$' 321 | label_em = r'$P_{\nu_\mu \to \nu_\tau}$' 322 | label_mm = r'$P_{\nu_\tau \to \nu_\tau}$' 323 | color_ee = 'C4' 324 | color_em = 'C5' 325 | color_mm = 'C8' 326 | 327 | h_vacuum_energy_independent = \ 328 | hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent(sth, Dm2) 329 | 330 | if (case.lower() == 'vacuum'): 331 | 332 | prob = [oscprob2nu.probabilities_2nu( \ 333 | np.multiply(1./energy/1.e9, h_vacuum_energy_independent), 334 | baseline) \ 335 | for energy in energy_val] 336 | label_case = r'Vacuum' 337 | 338 | elif (case.lower() == 'matter'): 339 | 340 | prob = [oscprob2nu.probabilities_2nu( \ 341 | hamiltonians2nu.hamiltonian_2nu_matter( \ 342 | h_vacuum_energy_independent, 343 | energy*1.e9, 344 | VCC_EARTH_CRUST), 345 | baseline) \ 346 | for energy in energy_val] 347 | label_case = r'Matter' 348 | 349 | elif (case.lower() == 'nsi'): 350 | 351 | prob = [oscprob2nu.probabilities_2nu( \ 352 | hamiltonians2nu.hamiltonian_2nu_nsi( \ 353 | h_vacuum_energy_independent, 354 | energy*1.e9, 355 | VCC_EARTH_CRUST, 356 | EPS_2), 357 | baseline) \ 358 | for energy in energy_val] 359 | label_case = r'NSI' 360 | 361 | elif (case.lower() == 'liv'): 362 | 363 | prob = [oscprob2nu.probabilities_2nu( \ 364 | hamiltonians2nu.hamiltonian_2nu_liv( \ 365 | h_vacuum_energy_independent, 366 | energy*1.e9, 367 | SXI12, 368 | B1, B3, LAMBDA), 369 | baseline) \ 370 | for energy in energy_val] 371 | label_case = r'CPT-odd LIV' 372 | 373 | # Each element of prob: [Pee, Pem, Pmm] 374 | prob_ee = [x[0] for x in prob] 375 | prob_em = [x[1] for x in prob] 376 | prob_mm = [x[3] for x in prob] 377 | 378 | # Formatting 379 | mpl.rcParams['xtick.labelsize']=26 380 | mpl.rcParams['ytick.labelsize']=26 381 | mpl.rcParams['legend.fontsize']=26 382 | mpl.rcParams['legend.borderpad']=0.4 383 | mpl.rcParams['axes.labelpad']=10 384 | mpl.rcParams['ps.fonttype']=42 385 | mpl.rcParams['pdf.fonttype']=42 386 | 387 | fig = plt.figure(figsize=[9,9]) 388 | ax = fig.add_subplot(1,1,1) 389 | 390 | ax.set_xlabel(r'Neutrino energy $E$ [GeV]', fontsize=25) 391 | ax.set_ylabel(r'Two-neutrino probability', fontsize=25) 392 | 393 | yaxis_minor_locator = mpl.ticker.MultipleLocator(0.1) 394 | ax.yaxis.set_minor_locator(yaxis_minor_locator) 395 | 396 | ax.tick_params('both', length=10, width=2, which='major') 397 | ax.tick_params('both', length=5, width=1, which='minor') 398 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 399 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 400 | ax.tick_params(axis='x', which='minor', bottom=True) 401 | ax.tick_params(axis='x', which='minor', top=True) 402 | ax.tick_params(axis='y', which='minor', left=True) 403 | ax.tick_params(axis='y', which='minor', right=True) 404 | ax.tick_params(bottom=True, top=True, left=True, right=True) 405 | 406 | ax.set_xlim([10.**log10_energy_min, 10.**log10_energy_max]) 407 | ax.set_xscale('log') 408 | ax.set_ylim([0.0, 1.0]) 409 | 410 | # Plot 411 | if (plot_prob_ee): 412 | ax.plot(energy_val, prob_ee, label=label_ee, 413 | color=color_ee, zorder=1) 414 | if (plot_prob_em): 415 | ax.plot(energy_val, prob_em, label=label_em, 416 | color=color_em, zorder=1) 417 | if (plot_prob_mm): 418 | ax.plot(energy_val, prob_mm, label=label_mm, 419 | color=color_mm, zorder=1) 420 | 421 | # Legend 422 | ax.legend(loc=legend_loc, frameon=False, ncol=legend_ncol) 423 | 424 | # Annotations 425 | ax.annotate( label_case, xy = (0.05, 0.86), \ 426 | xycoords='axes fraction', color='k', fontsize=25, 427 | horizontalalignment='left', rotation=0, zorder=2 ) 428 | ax.annotate( r'$\log_{10}(L/{\rm km}) = $' + \ 429 | str(int(log10(baseline/CONV_KM_TO_INV_EV)*100.)/100.), 430 | xy = (0.05, 0.80), xycoords='axes fraction', color='k', fontsize=25, 431 | horizontalalignment='left', rotation=0, zorder=2 ) 432 | 433 | pylab.savefig(output_path+output_filename+'.'+output_format, 434 | bbox_inches='tight', dpi=100) 435 | 436 | plt.close() 437 | 438 | return 439 | -------------------------------------------------------------------------------- /test/oscprob3nu_plot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r"""Routines to plot three-neutrino flavor-transition probabilities. 3 | 4 | This module contains contains routines to plot three-neutrino 5 | oscillation probabilities vs. the neutrino baseline and energy. These 6 | routines are used by run_testsuite.py to produce a suite of test plots. 7 | 8 | Routine listings 9 | ---------------- 10 | 11 | * plot_probability_3nu_vs_baseline - Plot probabilities vs. baseline 12 | * plot_probability_3nu_vs_energy - Plot probabilities vs. energy 13 | 14 | Created: 2019/04/17 17:14 15 | Last modified: 2019/04/22 18:01 16 | """ 17 | 18 | 19 | __version__ = "1.0" 20 | __author__ = "Mauricio Bustamante" 21 | __email__ = "mbustamante@gmail.com" 22 | 23 | 24 | from numpy import * 25 | import numpy as np 26 | from pylab import * 27 | from matplotlib import * 28 | import matplotlib as mpl 29 | 30 | import sys 31 | sys.path.append('../src') 32 | sys.path.append('../test') 33 | 34 | import oscprob3nu 35 | import hamiltonians3nu 36 | from globaldefs import * 37 | 38 | 39 | def plot_probability_3nu_vs_baseline( 40 | case, energy=1.e-1, 41 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=6000, 42 | plot_prob_ee=True, plot_prob_em=True, plot_prob_et=True, 43 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 44 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 45 | output_filename='prob_vs_baseline', output_format='pdf', 46 | output_path='../fig/', legend_loc='center left', 47 | legend_ncol=1): 48 | r"""Generates and saves a plot of 3nu probabilities vs. baseline. 49 | 50 | Generates a plot of three-neutrino oscillation probabilities vs. 51 | baseline, for a fixed neutrino energy. The probabilities to be 52 | plotted are turned on and off via the flags plot_prob_ee, 53 | plot_prob_em, etc. (At least one of them must be True.) The 54 | parameter 'case' selects between 'vacuum', 'matter', 'nsi', and 55 | 'liv' (see below). The plot is saved with the provided name and 56 | file format under the specified path. 57 | 58 | Parameters 59 | ---------- 60 | case : str 61 | Not optional. Must be one of the following: 'vacuum', 'matter', 62 | 'nsi', or 'liv'. In each case, the probabilities are computed 63 | using the default parameter values pulled from globaldefs. 64 | energy : float, optional 65 | Neutrino energy [GeV]. 66 | log10_l_min : float, optional 67 | Log10 of the minimum baseline [km]. 68 | log10_l_max : float, optional 69 | Log10 of the maximum baseline [km]. 70 | log10_l_npts : int, optional 71 | Number of baseline values at which to compute the probabilities. 72 | plot_prob_ee : bool, optional 73 | True to plot Pee, False otherwise. 74 | plot_prob_em : bool, optional 75 | True to plot Pem, False otherwise. 76 | plot_prob_et : bool, optional 77 | True to plot Pet, False otherwise. 78 | plot_prob_me : bool, optional 79 | True to plot Pme, False otherwise. 80 | plot_prob_mm : bool, optional 81 | True to plot Pmm, False otherwise. 82 | plot_prob_mt : bool, optional 83 | True to plot Pmt, False otherwise. 84 | plot_prob_te : bool, optional 85 | True to plot Pte, False otherwise. 86 | plot_prob_tm : bool, optional 87 | True to plot Ptm, False otherwise. 88 | plot_prob_tt : bool, optional 89 | True to plot Ptt, False otherwise. 90 | output_filename : str, optional 91 | File name of plot to save (without the file extension). 92 | output_format : str, optional 93 | File extension of the plot to save (e.g., 'pdf', 'png', 'jpg'). 94 | output_path : str, optional 95 | File path where to save the plot. 96 | legend_loc : str, optional 97 | Location of the legend in the plot. Must be one of the allowed 98 | values of the plot routine of matplotlib. 99 | legend_ncol : int, optional 100 | Number of columns to include in the legend box. Must be at 101 | least 1. 102 | 103 | Returns 104 | ------- 105 | None 106 | The plot is generated and saved. 107 | """ 108 | if (not plot_prob_ee) and (not plot_prob_em) and (not plot_prob_et) \ 109 | and (not plot_prob_me) and (not plot_prob_mm) and (not plot_prob_mt) \ 110 | and (not plot_prob_te) and (not plot_prob_tm) and (not plot_prob_tt): 111 | quit() 112 | 113 | # Baselines, L 114 | log10_l_val = np.linspace(log10_l_min, log10_l_max, log10_l_npts) 115 | l_val =[10.**x for x in log10_l_val] 116 | 117 | h_vacuum_energy_independent = \ 118 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 119 | S23_NO_BF, 120 | S13_NO_BF, 121 | DCP_NO_BF, 122 | D21_NO_BF, 123 | D31_NO_BF) 124 | 125 | if (case.lower() == 'vacuum'): 126 | 127 | hamiltonian = np.multiply(1./energy/1.e9, h_vacuum_energy_independent) 128 | label_case = r'Vacuum' 129 | 130 | elif (case.lower() == 'matter'): 131 | 132 | hamiltonian = hamiltonians3nu.hamiltonian_3nu_matter( \ 133 | h_vacuum_energy_independent, 134 | energy*1.e9, 135 | VCC_EARTH_CRUST) 136 | label_case = r'Matter' 137 | 138 | elif (case.lower() == 'nsi'): 139 | 140 | hamiltonian = hamiltonians3nu.hamiltonian_3nu_nsi( \ 141 | h_vacuum_energy_independent, 142 | energy*1.e9, 143 | VCC_EARTH_CRUST, 144 | EPS_3) 145 | label_case = r'NSI' 146 | 147 | elif (case.lower() == 'liv'): 148 | 149 | hamiltonian = hamiltonians3nu.hamiltonian_3nu_liv( \ 150 | h_vacuum_energy_independent, 151 | energy*1.e9, 152 | SXI12, SXI23, SXI13, DXICP, 153 | B1, B2, B3, LAMBDA) 154 | label_case = r'CPT-odd LIV' 155 | 156 | 157 | # Each element of prob: [Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt] 158 | prob = [oscprob3nu.probabilities_3nu( hamiltonian, 159 | l*CONV_KM_TO_INV_EV) \ 160 | for l in l_val] 161 | prob_ee = [x[0] for x in prob] 162 | prob_em = [x[1] for x in prob] 163 | prob_et = [x[2] for x in prob] 164 | prob_me = [x[3] for x in prob] 165 | prob_mm = [x[4] for x in prob] 166 | prob_mt = [x[5] for x in prob] 167 | prob_te = [x[6] for x in prob] 168 | prob_tm = [x[7] for x in prob] 169 | prob_tt = [x[8] for x in prob] 170 | 171 | # Formatting 172 | mpl.rcParams['xtick.labelsize']=26 173 | mpl.rcParams['ytick.labelsize']=26 174 | mpl.rcParams['legend.fontsize']=26 175 | mpl.rcParams['legend.borderpad']=0.4 176 | mpl.rcParams['axes.labelpad']=10 177 | mpl.rcParams['ps.fonttype']=42 178 | mpl.rcParams['pdf.fonttype']=42 179 | 180 | fig = plt.figure(figsize=[9,9]) 181 | ax = fig.add_subplot(1,1,1) 182 | 183 | ax.set_xlabel(r'Baseline $L$ [km]', fontsize=25) 184 | ax.set_ylabel(r'Three-neutrino probability', fontsize=25) 185 | 186 | yaxis_minor_locator = mpl.ticker.MultipleLocator(0.1) 187 | ax.yaxis.set_minor_locator(yaxis_minor_locator) 188 | 189 | ax.tick_params('both', length=10, width=2, which='major') 190 | ax.tick_params('both', length=5, width=1, which='minor') 191 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 192 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 193 | ax.tick_params(axis='x', which='minor', bottom=True) 194 | ax.tick_params(axis='x', which='minor', top=True) 195 | ax.tick_params(axis='y', which='minor', left=True) 196 | ax.tick_params(axis='y', which='minor', right=True) 197 | ax.tick_params(bottom=True, top=True, left=True, right=True) 198 | 199 | ax.set_xlim([10.**log10_l_min, 10.**log10_l_max]) 200 | ax.set_xscale('log') 201 | ax.set_ylim([0.0, 1.0]) 202 | 203 | # Plot 204 | if (plot_prob_ee): 205 | ax.plot(l_val, prob_ee, label=r'$P_{\nu_e \to \nu_e}$', 206 | color='C0', zorder=1) 207 | if (plot_prob_em): 208 | ax.plot(l_val, prob_em, label=r'$P_{\nu_e \to \nu_\mu}$', 209 | color='C1', zorder=1) 210 | if (plot_prob_et): 211 | ax.plot(l_val, prob_et, label=r'$P_{\nu_e \to \nu_\tau}$', 212 | color='C2', zorder=1) 213 | if (plot_prob_me): 214 | ax.plot(l_val, prob_me, label=r'$P_{\nu_\mu \to \nu_e}$', 215 | color='C3', zorder=1) 216 | if (plot_prob_mm): 217 | ax.plot(l_val, prob_mm, label=r'$P_{\nu_\mu \to \nu_\mu}$', 218 | color='C4', zorder=1) 219 | if (plot_prob_mt): 220 | ax.plot(l_val, prob_mt, label=r'$P_{\nu_\mu \to \nu_\tau}$', 221 | color='C5', zorder=1) 222 | if (plot_prob_te): 223 | ax.plot(l_val, prob_te, label=r'$P_{\nu_\tau \to \nu_e}$', 224 | color='C6', zorder=1) 225 | if (plot_prob_tm): 226 | ax.plot(l_val, prob_tm, label=r'$P_{\nu_\tau \to \nu_\mu}$', 227 | color='C7', zorder=1) 228 | if (plot_prob_tt): 229 | ax.plot(l_val, prob_tt, label=r'$P_{\nu_\tau \to \nu_\tau}$', 230 | color='C8', zorder=1) 231 | 232 | # Legend 233 | ax.legend(loc=legend_loc, frameon=False, ncol=legend_ncol) 234 | 235 | # Annotations 236 | ax.annotate( label_case, xy = (0.05, 0.86), \ 237 | xycoords='axes fraction', color='k', fontsize=25, 238 | horizontalalignment='left', rotation=0, zorder=2 ) 239 | ax.annotate( r'$\log_{10}(E/{\rm GeV}) = $' + \ 240 | str(int(log10(energy)*100.)/100.), 241 | xy = (0.05, 0.80), xycoords='axes fraction', color='k', fontsize=25, 242 | horizontalalignment='left', rotation=0, zorder=2 ) 243 | 244 | pylab.savefig(output_path+output_filename+'.'+output_format, 245 | bbox_inches='tight', dpi=100) 246 | 247 | plt.close() 248 | 249 | return 250 | 251 | 252 | def plot_probability_3nu_vs_energy( 253 | case, baseline=1.3e3, 254 | log10_energy_min=-1.0, log10_energy_max=1.0, 255 | log10_energy_npts=200, 256 | plot_prob_ee=True, plot_prob_em=True, plot_prob_et=True, 257 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 258 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 259 | output_filename='prob_vs_energy', output_format='pdf', 260 | output_path='../fig/', legend_loc='center right', 261 | legend_ncol=1): 262 | r"""Generates and saves a plot of 3nu probabilities vs. energy. 263 | 264 | Generates a plot of three-neutrino oscillation probabilities vs. 265 | energy, for a fixed neutrino baseline. The probabilities to be 266 | plotted are turned on and off via the flags plot_prob_ee, 267 | plot_prob_em, etc. (At least one of them must be True.) The 268 | parameter 'case' selects between 'vacuum', 'matter', 'nsi', and 269 | 'liv' (see below). The plot is saved with the provided name and 270 | file format under the specified path. 271 | 272 | Parameters 273 | ---------- 274 | case : str 275 | Not optional. Must be one of the following: 'vacuum', 'matter', 276 | 'nsi', or 'liv'. In each case, the probabilities are computed 277 | using the default parameter values pulled from globaldefs. 278 | baseline : float, optional 279 | Neutrino baseline [km]. 280 | log10_energy_min : float, optional 281 | Log10 of the minimum energy [GeV]. 282 | log10_energy_max : float, optional 283 | Log10 of the maximum energy [GeV]. 284 | log10_energy_npts : int, optional 285 | Number of energy values at which to compute the probabilities. 286 | plot_prob_ee : bool, optional 287 | True to plot Pee, False otherwise. 288 | plot_prob_em : bool, optional 289 | True to plot Pem, False otherwise. 290 | plot_prob_et : bool, optional 291 | True to plot Pet, False otherwise. 292 | plot_prob_me : bool, optional 293 | True to plot Pme, False otherwise. 294 | plot_prob_mm : bool, optional 295 | True to plot Pmm, False otherwise. 296 | plot_prob_mt : bool, optional 297 | True to plot Pmt, False otherwise. 298 | plot_prob_te : bool, optional 299 | True to plot Pte, False otherwise. 300 | plot_prob_tm : bool, optional 301 | True to plot Ptm, False otherwise. 302 | plot_prob_tt : bool, optional 303 | True to plot Ptt, False otherwise. 304 | output_filename : str, optional 305 | File name of plot to save (without the file extension). 306 | output_format : str, optional 307 | File extension of the plot to save (e.g., 'pdf', 'png', 'jpg'). 308 | output_path : str, optional 309 | File path where to save the plot. 310 | legend_loc : str, optional 311 | Location of the legend in the plot. Must be one of the allowed 312 | values of the plot routine of matplotlib. 313 | legend_ncol : int, optional 314 | Number of columns to include in the legend box. Must be at 315 | least 1. 316 | 317 | Returns 318 | ------- 319 | None 320 | The plot is generated and saved. 321 | """ 322 | if (not plot_prob_ee) and (not plot_prob_em) and (not plot_prob_et) \ 323 | and (not plot_prob_me) and (not plot_prob_mm) and (not plot_prob_mt) \ 324 | and (not plot_prob_te) and (not plot_prob_tm) and (not plot_prob_tt): 325 | quit() 326 | 327 | baseline = baseline*CONV_KM_TO_INV_EV # [eV^{-1}] 328 | 329 | # Neutrino energies 330 | log10_energy_val = np.linspace( log10_energy_min, log10_energy_max, 331 | log10_energy_npts) 332 | energy_val =[10.**x for x in log10_energy_val] 333 | 334 | h_vacuum_energy_independent = \ 335 | hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_NO_BF, 336 | S23_NO_BF, 337 | S13_NO_BF, 338 | DCP_NO_BF, 339 | D21_NO_BF, 340 | D31_NO_BF) 341 | 342 | if (case.lower() == 'vacuum'): 343 | 344 | prob = [oscprob3nu.probabilities_3nu( \ 345 | np.multiply(1./energy/1.e9, h_vacuum_energy_independent), 346 | baseline) \ 347 | for energy in energy_val] 348 | label_case = r'Vacuum' 349 | 350 | elif (case.lower() == 'matter'): 351 | 352 | prob = [oscprob3nu.probabilities_3nu( \ 353 | hamiltonians3nu.hamiltonian_3nu_matter( \ 354 | h_vacuum_energy_independent, 355 | energy*1.e9, 356 | VCC_EARTH_CRUST), 357 | baseline) \ 358 | for energy in energy_val] 359 | label_case = r'Matter' 360 | 361 | elif (case.lower() == 'nsi'): 362 | 363 | prob = [oscprob3nu.probabilities_3nu( \ 364 | hamiltonians3nu.hamiltonian_3nu_nsi( \ 365 | h_vacuum_energy_independent, 366 | energy*1.e9, 367 | VCC_EARTH_CRUST, 368 | EPS_3), 369 | baseline) \ 370 | for energy in energy_val] 371 | label_case = r'NSI' 372 | 373 | elif (case.lower() == 'liv'): 374 | 375 | prob = [oscprob3nu.probabilities_3nu( \ 376 | hamiltonians3nu.hamiltonian_3nu_liv( \ 377 | h_vacuum_energy_independent, 378 | energy*1.e9, 379 | SXI12, SXI23, SXI13, DXICP, 380 | B1, B2, B3, LAMBDA), 381 | baseline) \ 382 | for energy in energy_val] 383 | label_case = r'CPT-odd LIV' 384 | 385 | # Each element of prob: [Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt] 386 | prob_ee = [x[0] for x in prob] 387 | prob_em = [x[1] for x in prob] 388 | prob_et = [x[2] for x in prob] 389 | prob_me = [x[3] for x in prob] 390 | prob_mm = [x[4] for x in prob] 391 | prob_mt = [x[5] for x in prob] 392 | prob_te = [x[6] for x in prob] 393 | prob_tm = [x[7] for x in prob] 394 | prob_tt = [x[8] for x in prob] 395 | 396 | # Formatting 397 | mpl.rcParams['xtick.labelsize']=26 398 | mpl.rcParams['ytick.labelsize']=26 399 | mpl.rcParams['legend.fontsize']=26 400 | mpl.rcParams['legend.borderpad']=0.4 401 | mpl.rcParams['axes.labelpad']=10 402 | mpl.rcParams['ps.fonttype']=42 403 | mpl.rcParams['pdf.fonttype']=42 404 | 405 | fig = plt.figure(figsize=[9,9]) 406 | ax = fig.add_subplot(1,1,1) 407 | 408 | ax.set_xlabel(r'Neutrino energy $E$ [GeV]', fontsize=25) 409 | ax.set_ylabel(r'Three-neutrino probability', fontsize=25) 410 | 411 | yaxis_minor_locator = mpl.ticker.MultipleLocator(0.1) 412 | ax.yaxis.set_minor_locator(yaxis_minor_locator) 413 | 414 | ax.tick_params('both', length=10, width=2, which='major') 415 | ax.tick_params('both', length=5, width=1, which='minor') 416 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 417 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 418 | ax.tick_params(axis='x', which='minor', bottom=True) 419 | ax.tick_params(axis='x', which='minor', top=True) 420 | ax.tick_params(axis='y', which='minor', left=True) 421 | ax.tick_params(axis='y', which='minor', right=True) 422 | ax.tick_params(bottom=True, top=True, left=True, right=True) 423 | 424 | ax.set_xlim([10.**log10_energy_min, 10.**log10_energy_max]) 425 | ax.set_xscale('log') 426 | ax.set_ylim([0.0, 1.0]) 427 | 428 | # Plot 429 | if (plot_prob_ee): 430 | ax.plot(energy_val, prob_ee, label=r'$P_{\nu_e \to \nu_e}$', 431 | color='C0', zorder=1) 432 | if (plot_prob_em): 433 | ax.plot(energy_val, prob_em, label=r'$P_{\nu_e \to \nu_\mu}$', 434 | color='C1', zorder=1) 435 | if (plot_prob_et): 436 | ax.plot(energy_val, prob_et, label=r'$P_{\nu_e \to \nu_\tau}$', 437 | color='C2', zorder=1) 438 | if (plot_prob_me): 439 | ax.plot(energy_val, prob_me, label=r'$P_{\nu_\mu \to \nu_e}$', 440 | color='C3', zorder=1) 441 | if (plot_prob_mm): 442 | ax.plot(energy_val, prob_mm, label=r'$P_{\nu_\mu \to \nu_\mu}$', 443 | color='C4', zorder=1) 444 | if (plot_prob_mt): 445 | ax.plot(energy_val, prob_mt, label=r'$P_{\nu_\mu \to \nu_\tau}$', 446 | color='C5', zorder=1) 447 | if (plot_prob_te): 448 | ax.plot(energy_val, prob_te, label=r'$P_{\nu_\tau \to \nu_e}$', 449 | color='C6', zorder=1) 450 | if (plot_prob_tm): 451 | ax.plot(energy_val, prob_tm, label=r'$P_{\nu_\tau \to \nu_\mu}$', 452 | color='C7', zorder=1) 453 | if (plot_prob_tt): 454 | ax.plot(energy_val, prob_tt, label=r'$P_{\nu_\tau \to \nu_\tau}$', 455 | color='C8', zorder=1) 456 | 457 | # Legend 458 | ax.legend(loc=legend_loc, frameon=False, ncol=legend_ncol) 459 | 460 | # Annotations 461 | ax.annotate( label_case, xy = (0.05, 0.86), \ 462 | xycoords='axes fraction', color='k', fontsize=25, 463 | horizontalalignment='left', rotation=0, zorder=2 ) 464 | ax.annotate( r'$\log_{10}(L/{\rm km}) = $' + \ 465 | str(int(log10(baseline/CONV_KM_TO_INV_EV)*100.)/100.), 466 | xy = (0.05, 0.80), xycoords='axes fraction', color='k', fontsize=25, 467 | horizontalalignment='left', rotation=0, zorder=2 ) 468 | 469 | pylab.savefig(output_path+output_filename+'.'+output_format, 470 | bbox_inches='tight', dpi=100) 471 | 472 | plt.close() 473 | 474 | return 475 | 476 | 477 | def plot_probability_3nu_vacuum_vs_l_std(output_format='pdf'): 478 | 479 | # Best-fit values of mixing parameters, normal ordering, from 1708.01186 480 | s12 = sqrt(3.21e-1) 481 | s23 = sqrt(4.30e-1) 482 | s13 = sqrt(2.155e-2) 483 | dCP = 1.4*np.pi # [rad] 484 | D21 = 7.56e-5 # [eV^2] 485 | D31 = 2.55e-3 # [eV^2] 486 | 487 | # Neutrino energy, E 488 | energy_nu = 1.e6 # [eV] 489 | 490 | # Baselines, L 491 | log10_l_min = 0.0 # [km] 492 | log10_l_max = log10(5.e2) # [km] 493 | log10_l_npts = 6000 494 | log10_l_val = np.linspace(log10_l_min, log10_l_max, log10_l_npts) 495 | l_val =[10.**x for x in log10_l_val] 496 | 497 | U = pmns_mixing_matrix(s12, s23, s13, dCP) 498 | 499 | # Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt 500 | lst_prob = [probabilities_3nu_std( U, D21, D31, energy_nu, 501 | l/CONV_KM_TO_INV_EV) \ 502 | for l in l_val] 503 | lst_prob_ee = [x[0] for x in lst_prob] 504 | lst_prob_em = [x[1] for x in lst_prob] 505 | lst_prob_et = [x[2] for x in lst_prob] 506 | 507 | # Plot 508 | mpl.rcParams['xtick.labelsize']=26 509 | mpl.rcParams['ytick.labelsize']=26 510 | mpl.rcParams['legend.fontsize']=26 511 | mpl.rcParams['legend.borderpad']=0.4 512 | mpl.rcParams['axes.labelpad']=10 513 | mpl.rcParams['ps.fonttype']=42 514 | mpl.rcParams['pdf.fonttype']=42 515 | 516 | fig = plt.figure(figsize=[9,9]) 517 | ax = fig.add_subplot(1,1,1) 518 | 519 | ax.set_xlabel(r'Baseline $L$ [km]', fontsize=25) 520 | ax.set_ylabel(r'Probability in vacuum ($E = 1$ MeV)', fontsize=25) 521 | 522 | yaxis_minor_locator = mpl.ticker.MultipleLocator(0.1) 523 | ax.yaxis.set_minor_locator(yaxis_minor_locator) 524 | 525 | ax.tick_params('both', length=10, width=2, which='major') 526 | ax.tick_params('both', length=5, width=1, which='minor') 527 | ax.tick_params(axis='both', which='major', pad=10, direction='in') 528 | ax.tick_params(axis='both', which='minor', pad=10, direction='in') 529 | ax.tick_params(axis='x', which='minor', bottom=True) 530 | ax.tick_params(axis='x', which='minor', top=True) 531 | ax.tick_params(axis='y', which='minor', left=True) 532 | ax.tick_params(axis='y', which='minor', right=True) 533 | ax.tick_params(bottom=True, top=True, left=True, right=True) 534 | 535 | ax.set_xlim([10.**log10_l_min, 10.**log10_l_max]) 536 | ax.set_xscale('log') 537 | ax.set_ylim([0.0, 1.0]) 538 | 539 | # Plot 540 | ax.plot(l_val, lst_prob_ee, label=r'$P_{\nu_e \to \nu_e}$', color='C0', 541 | zorder=1) 542 | ax.plot(l_val, lst_prob_em, label=r'$P_{\nu_e \to \nu_\mu}$', color='C1', 543 | zorder=1) 544 | ax.plot(l_val, lst_prob_et, label=r'$P_{\nu_e \to \nu_\tau}$', color='C2', 545 | zorder=1) 546 | 547 | # Legend 548 | ax.legend(loc='center left', frameon=False, ncol=1) 549 | 550 | pylab.savefig('prob_3nu_vacuum_std_vs_l.'+output_format, bbox_inches='tight', 551 | dpi=300) 552 | 553 | return 554 | 555 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![arXiv](https://img.shields.io/badge/arXiv-1904.12391-orange.svg)](http://arxiv.org/abs/1904.12391) 2 | [![DOI](https://zenodo.org/badge/182178323.svg)](https://zenodo.org/badge/latestdoi/182178323) 3 | 4 | # NuOscProbExact 5 | Code to compute exact two- and three-neutrino oscillation probabilities using SU(2) and SU(3) expansions 6 | 7 | > **In the works:** We are working on optimizing the code to run faster, using Cython 8 | 9 | ## Contents 10 | 11 | 1. [What is NuOscProbExact?](#what-is-nuoscprobexact) 12 | 13 | 2. [Requirements](#requirements) 14 | 15 | 3. [Installation](#installation) 16 | 17 | 4. [Usage and examples](#usage-and-examples) 18 | 1. [Basics](#basics) 19 | 2. [Trivial example](#trivial-example) 20 | 3. [Oscillations in vacuum: fixed energy and baseline](#oscillations-in-vacuum-fixed-energy-and-baseline) 21 | 4. [Three-neutrino oscillations in vacuum: fixed energy, varying baseline](#three-neutrino-oscillations-in-vacuum-fixed-energy-varying-baseline) 22 | 5. [Three-neutrino oscillations in vacuum: fixed baseline, varying energy](#three-neutrino-oscillations-in-vacuum-fixed-baseline-varying-energy) 23 | 6. [Three-neutrino oscillations in matter](#three-neutrino-oscillations-in-matter) 24 | 7. [Three-neutrino oscillations in matter with non-standard interactions (NSI)](#three-neutrino-oscillations-in-matter-with-non-standard-interactions-nsi) 25 | 8. [Three-neutrino oscillations in a Lorentz invariance-violating (LIV) background](#three-neutrino-oscillations-in-a-lorentz-invariance-violating-liv-background) 26 | 9. [Arbitrary Hamiltonians](#arbitrary-hamiltonians) 27 | 28 | 5. [Documentation and help](#documentation-and-help) 29 | 30 | 6. [Citing](#citing) 31 | 32 | 33 | ## What is NuOscProbExact? 34 | 35 | **NuOscProbExact** is a Python implementation of the method developed by [Ohlsson & Snellman](https://arxiv.org/abs/hep-ph/9910546) to compute exact two-flavor and three-flavor neutrino oscillation probabilities for arbitrary time-independent Hamiltonians. The method was revisited and the code presented in the paper *NuOscProbExact: a general-purpose code to compute exact two-flavor and three-flavor neutrino oscillation probabilities* ([arXiv:1904.12391](http://arxiv.org/abs/1904.12391)), by Mauricio Bustamante. 36 | 37 | The method relies on expansions of the Hamiltonian and time-evolution operators in terms of SU(2) and SU(3) matrices in order to obtain concise, analytical, and exact expressions for the probabilities, that are also easy to implement and evaluate. For details of the method, see the paper above. 38 | 39 | **NuOscProbExact** was developed by Mauricio Bustamante. If you use it in your work, please follow the directions on [Citing](#citing). 40 | 41 | 42 | ## Requirements 43 | 44 | **NuOscProbExact** is fully written in Python 3. It uses standard modules that are available, sometimes by default, as part of most Python installations, either stand-alone or via Anaconda: 45 | 46 | * **Bare minimum requirements:** The two core modules (`oscprob2nu.py` and `oscprob3nu.py`) require only `numpy` and `cmath`. These are the bare minimum requirements. 47 | 48 | * **To use the bundled sample Hamiltonians:** The modules containing example Hamiltonians (`hamiltonians2nu.py` and `hamiltonians3nu.py`) require only `numpy`, `cmath`, and `copy` 49 | 50 | * **To run the test suite:** The modules containing the test suites (`oscprob2nu_plot.py`, `oscprob3nu_plot.py`, `oscprob3nu_plotpaper.py`, and `run_testsuite.py`) require only `numpy`, `cmath`, `copy`, and `matplotlib` 51 | 52 | 53 | ## Installation 54 | 55 | Because **NuOscProbExact** is written fully in Python, no compilation or linking is necessary. The installation is simple and consists only in fetching the files from GitHub. 56 | 57 | > **Python 2 compatibility:** The code was written and tested using Python 3. Yet, because the core modules `oscprob2nu` and `oscprob3nu` use only native Python functions and popular modules, they might also run in Python 2. However, this is currently untested. 58 | 59 | Instructions: 60 | 61 | 1. In the file system where you would like to install **NuOscProbExact**, go to the directory where you would like the code to be downloaded, *e.g.*, 62 | ```shell 63 | cd /home/MyProjects 64 | ``` 65 | 66 | 2. From there, fetch the code from the GitHub repository with 67 | ```shell 68 | git clone https://github.com/mbustama/NuOscProbExact.git 69 | ``` 70 | (Alternatively, you can download the zip file from GitHub and uncompress it.) 71 | 72 | Doing this will create the directory `/home/MyProjects/NuOscProbExact`, with the following file structure: 73 | ``` 74 | /NuOscProbExact/README.md The file that you are reading 75 | /NuOscProbExact/run_testsuite.py Run this to execute all the examples and create test plots 76 | /NuOscProbExact/fig Contains plots generated by the test suite (initially empyty) 77 | /NuOscProbExact/img Contains two pre-computed plots displayed in README.md 78 | ../prob_3nu_vacuum_vs_baseline_ee_em_et.png 79 | ../prob_3nu_vacuum_vs_energy_ee_em_et.png 80 | /NuOscProbExact/src Contains the main source files 81 | ../hamiltonians2nu.py Routines to compute example two-flavor Hamiltonians 82 | ../hamiltonians3nu.py Routines to compute example three-flavor Hamiltonians 83 | ../globaldefs.py Physical constants and unit-conversion constants 84 | ../oscprob2nu.py Routines to compute the two-flavor probabilities 85 | ../oscprob3nu.py Routines to compute the three-flavor probabilities 86 | /NuOscProbExact/test Contains the source files to run the test suite 87 | ../example_2nu_trivial.py Two-flavor trivial example 88 | ../example_2nu_vacuum.py Two-flavor example for oscillations in vacuum 89 | ../example_2nu_vacuum_coeffs.py Two-flavor example for coefficients for oscillations in vacuum 90 | ../example_3nu_liv.py Three-flavor example for oscillations with LIV 91 | ../example_3nu_matter.py Three-flavor example for oscillations in matter 92 | ../example_3nu_nsi.py Three-flavor example for oscillations in matter with NSI 93 | ../example_3nu_trivial.py Three-flavor trivial example 94 | ../example_3nu_vacuum.py Three-flavor example for oscillations in vacuum 95 | ../example_3nu_vacuum_coeffs.py Three-flavor example for coefficients for oscillations in vacuum 96 | ../oscprob2nu_plot.py Routines to generate two-flavor probability test plots 97 | ../oscprob3nu_plot.py Routines to generate three-flavor probability test plots 98 | ../oscprob2nu_plotpaper.py Routine to generate the two-flavor plot shown in the paper 99 | ../oscprob3nu_plotpaper.py Routine to generate the three-flavor plot shown in the paper 100 | ``` 101 | Now you are ready to start using **NuOscProbExact**. 102 | 103 | 3. (Optional, recommended) Run the examples 104 | Inside the directory `test/`, we provide several example files to get you started. We also elaborate on these examples later in this README, and show the output thay you should expect from them. To run any of the examples, just execute, *e.g.*, 105 | ```shell 106 | python example_2nu_trivial.py 107 | ``` 108 | Inspecting the example files and reading their description below will help you to learn how to use **NuOscProbExact** in your own project. 109 | 110 | 4. (Optional) Run the test suite 111 | ```shell 112 | cd /home/MyProjects/NuOscProbExact 113 | python run_testsuite.py 114 | ``` 115 | Doing this will do the following: 116 | * Generate plots of the two-neutrino and three-neutrino probabilities *vs.* distance and *vs.* energy, for different oscillation scenarios; and 117 | * Generate the plots of two-neutrino and three-neutrino probabilities *vs.* energy that are included in the paper. 118 | 119 | The plots are stored in the `fig/` directory. The code `run_testsuite.py` calls routines defined in `oscprob2nu_plot.py`, `oscprob3nu_plot.py`, `oscprob2nu_plotpaper.py`, and `oscprob3nu_plotpaper.py`, located in the `NuOscProbExact/test/` directory. Inspecting these files may help you in coding your own project. 120 | 121 | 122 | ## Usage and examples 123 | 124 | There are only two core modules: `oscprobn2nu.py` and `oscprob3nu.py`. Each one is stand-alone (except for the dependencies described [above](#requirements)). To use either module in your code, copy it to your project's working directory, or add their location to the paths where your environment looks for modules, *e.g.*, 125 | ```python 126 | import sys 127 | 128 | sys.path.append('../src') 129 | ``` 130 | 131 | In the examples below, we focus mostly on `oscprob3nu`, but what we show applies to `oscprob2nu` as well. 132 | 133 | 134 | ### Basics 135 | 136 | Most of the time, you will be only interested in computing oscillation probabilities, not in the intermediate steps of the method. The functions to compute and return the probabilities are `probabilities_3nu`, for the three-neutrino case, and `probabilities_2nu`, for the two-neutrino case. Below, we show how to use them. 137 | 138 | #### Three-neutrino oscillations 139 | 140 | The function to compute three-neutrino probabilities is `probabilities_3nu` in the module `oscprob3nu`. It takes as input parameters the `hamiltonian`, in the form of a 3x3 Hermitian matrix, and the baseline `L`. 141 | 142 | This function returns the list of probabilities `Pee` (nu_e --> nu_e), `Pem` (nu_e --> nu_mu), `Pet` (nu_e --> nu_tau), `Pme` (nu_mu --> nu_e), `Pmm` (nu_mu --> nu_mu), `Pmt` (nu_mu --> nu_tau), `Pte` (nu_tau --> nu_e), `Ptm` (nu_tau --> nu_mu), and `Ptt` (nu_tau --> nu_tau). 143 | 144 | To use it, call 145 | ```python 146 | import oscprob3nu 147 | 148 | hamiltonian = [[H11, H12, H13], [H21, H22, H23], [H31, H32, H33]] 149 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu(hamiltonian, L) 150 | ``` 151 | 152 | #### Two-neutrino oscillations 153 | 154 | The function to compute two-neutrino probabilities is `probabilities_2nu` in the module `oscprob2nu`. It takes as input parameters the `hamiltonian`, in the form of a 2x2 Hermitian matrix, and the baseline `L`. 155 | 156 | This function returns the list of probabilities `Pee` (nu_e --> nu_e), `Pem` (nu_e --> nu_mu), `Pme` (nu_mu --> nu_e), and `Pmm` (nu_mu --> nu_mu). (These probabilities could also be `Pmm`, `Pmt`, `Pmt`, and `Ptt` instead, depending on what Hamiltonian you pass to `probabilities_2nu`.) 157 | 158 | To use it, call 159 | ```python 160 | import oscprob2nu 161 | 162 | hamiltonian = [[H11, H12], [H21, H22]] 163 | Pee, Pem, Pme, Pmm = oscprob2nu.probabilities_2nu(hamiltonian, L) 164 | ``` 165 | 166 | > **Important:** If you feed the code a non-Hermitian matrix, it will output nonsensical results 167 | 168 | > **About the units:** The code in the modules `oscprob3nu` and `osprob2nu` does not assume units for any of the model parameters, so you need to make sure that you pass values with the correct units. The module `globaldefs` contains conversion factors which might come in handy for this. 169 | 170 | 171 | ### Trivial example 172 | 173 | #### Three-neutrino oscillations 174 | 175 | As a first, trivial example, we pass an arbitrary Hamiltonian and baseline to `probabilities_3nu`: 176 | ```python 177 | # Find this example in NuOscProbExact/test/example_3nu_trivial.py 178 | 179 | import oscprob3nu 180 | 181 | hamiltonian = [ 182 | [1.0+0.0j, 0.0+2.0j, 0.0-1.0j], 183 | [0.0-2.0j, 3.0+0.0j, 3.0+0.0j], 184 | [0.0+1.0j, 3.0-0.0j, 5.0+0.0j] 185 | ] 186 | 187 | L = 1.0 188 | 189 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu(hamiltonian, L) 190 | 191 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 192 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 193 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 194 | ``` 195 | This returns 196 | ```shell 197 | Pee = 0.34273, Pem = 0.41369, Pet = 0.24358 198 | Pme = 0.41369, Pmm = 0.00485, Pmt = 0.58146 199 | Pte = 0.24358, Ptm = 0.58146, Ptt = 0.17497 200 | ``` 201 | 202 | As expected, `Pme + Pmm + Pmt = 1`, and `Pte + Ptm + Ptt = 1`. 203 | 204 | #### Two-neutrino oscillations 205 | 206 | In this case, we use `probabilities_2nu`: 207 | ```python 208 | # Find this example in NuOscProbExact/test/example_2nu_trivial.py 209 | 210 | import oscprob2nu 211 | 212 | hamiltonian = [ 213 | [1.0+0.0j, 1.0+2.0j], 214 | [1.0-2.0j, 3.0+0.0j] 215 | ] 216 | 217 | L = 1.0 218 | 219 | Pee, Pem, Pme, Pmm = oscprob2nu.probabilities_2nu(hamiltonian, L) 220 | 221 | print("Pee = %6.5f, Pem = %6.5f" % (Pee, Pem)) 222 | print("Pme = %6.5f, Pmm = %6.5f" % (Pme, Pmm)) 223 | ``` 224 | This returns 225 | ```shell 226 | Pee = 0.93213, Pem = 0.06787 227 | Pme = 0.06787, Pmm = 0.93213 228 | ``` 229 | 230 | As expected, `Pem == Pme` and `Pee + Pem = 1`. 231 | 232 | 233 | ### Oscillations in vacuum: fixed energy and baseline 234 | 235 | #### Three-neutrino oscillations 236 | 237 | Now we compute the three-neutrino oscillation probabilities in vacuum. To do this, we can use the routine 238 | ```python 239 | hamiltonian_3nu_vacuum_energy_independent(s12, s23, s13, dCP, D21, D31) 240 | ``` 241 | that is provided in the `hamiltonians3nu` module. It returns the 3x3 Hamiltonian for oscillations in vacuum. The input parameters `s12`, `s23`, `s13`, `dCP`, `D21`, and `D31` are, respectively, sin(theta_12), sin(theta_23), sin(theta_13), delta_CP, Delta m_21^2, and Delta m_31^2. For this example, we set them to their current best-fit values, which we pull from `globaldefs` (inspect that file for more information about these values). 242 | 243 | > **Important:** The function `hamiltonian_3nu_vacuum_energy_independent` returns the Hamiltonian in vacuum **without** multiplying it by the *1/E* prefactor, where *E* is the neutrino energy. It was done in this way so that, if we wish to compute the probabilities at different energies, we need to compute `hamiltonian_3nu_vacuum_energy_independent` only once, and then multiply it by a varying *1/E* prefactor. 244 | 245 | ```python 246 | # Find this example in NuOscProbExact/test/example_3nu_vacuum.py 247 | 248 | import numpy as np 249 | 250 | import oscprob3nu 251 | import hamiltonians3nu 252 | from globaldefs import * 253 | 254 | energy = 1.e9 # Neutrino energy [eV] 255 | baseline = 1.3e3 # Baseline [km] 256 | 257 | # Use the NuFit 4.0 best-fit values of the mixing parameters pulled from globaldefs 258 | # NO means "normal ordering"; change NO to IO if you want to use inverted ordering 259 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( \ 260 | S12_NO_BF, S23_NO_BF, 261 | S13_NO_BF, DCP_NO_BF, 262 | D21_NO_BF, D31_NO_BF) 263 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 264 | 265 | # CONV_KM_TO_INV_EV is pulled from globaldefs; it converts km to eV^{-1} 266 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu( h_vacuum, 267 | baseline*CONV_KM_TO_INV_EV) 268 | 269 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 270 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 271 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 272 | ```` 273 | This returns 274 | ```shell 275 | Pee = 0.92768, Pem = 0.01432, Pet = 0.05800 276 | Pme = 0.04023, Pmm = 0.37887, Pmt = 0.58090 277 | Pte = 0.03210, Ptm = 0.60680, Ptt = 0.36110 278 | ``` 279 | 280 | > **Computing anti-neutrino probabilities**: All of the examples shown in this README (and in the files inside the `test/` directory) are for neutrinos, not anti-neutrinos. If you wish to compute probabilities for anti-neutrinos, a simple way to do this is to pass `-dCP` instead of `dCP` to `hamiltonian_3nu_vacuum_energy_independent` (or to `hamiltonian_2nu_vacuum_energy_independent`). 281 | 282 | > **About `globaldefs`**: This module contains physical constants and unit-conversion constants that are used in the examples and that you can use in your code. 283 | 284 | Sometimes, you might be interested also in returning the coefficients `h1`, ..., `h8` of the expansion of the Hamiltonian in terms of Gell-Mann matrices (Table II in the paper), the coefficients `u0`, ..., `u8` of the SU(3) expansion of the associated time-evolution operator (Eqs. (13) and (14) in the paper), or the time-evolution operator `evol_operator` itself, as a 3x3 matrix (Eq. (15) in the paper). See the paper [arXiv:1904.12391](http://arxiv.org/abs/1904.12391) for details on these quantities. 285 | 286 | The module `oscprob3nu` has functions to do this: 287 | ```python 288 | # Find this example in NuOscProbExact/test/example_3nu_vacuum_coefficients.py 289 | 290 | import numpy as np 291 | 292 | import oscprob3nu 293 | import hamiltonians3nu 294 | from globaldefs import * 295 | 296 | energy = 1.e9 # Neutrino energy [eV] 297 | baseline = 1.3e3 # Baseline [km] 298 | 299 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( \ 300 | S12_NO_BF, S23_NO_BF, 301 | S13_NO_BF, DCP_NO_BF, 302 | D21_NO_BF, D31_NO_BF) 303 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 304 | 305 | h1, h2, h3, h4, h5, h6, h7, h8 = oscprob3nu.hamiltonian_3nu_coefficients(h_vacuum) 306 | print('h1: {:.4e}'.format(h1)) 307 | print('h2: {:.4e}'.format(h2)) 308 | print('h3: {:.4e}'.format(h3)) 309 | print('h4: {:.4e}'.format(h4)) 310 | print('h5: {:.4e}'.format(h5)) 311 | print('h6: {:.4e}'.format(h6)) 312 | print('h7: {:.4e}'.format(h7)) 313 | print('h8: {:.4e}'.format(h8)) 314 | print() 315 | 316 | u0, u1, u2, u3, u4, u5, u6, u7, u8 = oscprob3nu.evolution_operator_3nu_u_coefficients( \ 317 | h_vacuum, 318 | baseline*CONV_KM_TO_INV_EV) 319 | print('u0: {:.4f}'.format(u0)) 320 | print('u1: {:.4f}'.format(u1)) 321 | print('u2: {:.4f}'.format(u2)) 322 | print('u3: {:.4f}'.format(u3)) 323 | print('u4: {:.4f}'.format(u4)) 324 | print('u5: {:.4f}'.format(u5)) 325 | print('u6: {:.4f}'.format(u6)) 326 | print('u7: {:.4f}'.format(u7)) 327 | print('u8: {:.4f}'.format(u8)) 328 | print() 329 | 330 | evol_operator = oscprob3nu.evolution_operator_3nu(h_vacuum, baseline*CONV_KM_TO_INV_EV) 331 | print('U3 = ') 332 | with np.printoptions(precision=3, suppress=True): 333 | print(np.array(evol_operator)) 334 | ``` 335 | This returns 336 | ```shell 337 | h1: -1.0187e-13 338 | h2: -8.4997e-14 339 | h3: -3.4583e-13+0.0000e+00j 340 | h4: -1.0848e-13 341 | h5: -7.2033e-14 342 | h6: 5.9597e-13 343 | h7: 1.5392e-15 344 | h8: -8.2865e-14+0.0000e+00j 345 | 346 | u0: -0.3794+0.5072j 347 | u1: 0.0318+0.1167j 348 | u2: -0.0257+0.1095j 349 | u3: -0.1270+0.4507j 350 | u4: -0.1066+0.1569j 351 | u5: -0.0217+0.0928j 352 | u6: 0.1383-0.7580j 353 | u7: 0.0093-0.0040j 354 | u8: -0.0323+0.1084j 355 | 356 | [[-0.893+0.362j -0.142+0.141j -0.179-0.014j] 357 | [-0.091-0.078j 0.009+0.615j 0.767+0.134j] 358 | [-0.135-0.199j 0.749+0.142j -0.254+0.545j]] 359 | ``` 360 | 361 | 362 | 363 | #### Two-neutrino oscillations 364 | 365 | To compute the two-neutrino oscillation probabilities in vacuum, we can use the routine 366 | ```python 367 | hamiltonian_2nu_vacuum_energy_independent(sth, Dm2) 368 | ``` 369 | that is provided in the `hamiltonians2nu` module. The input parameters `sth`, and `Dm2` are, respectively, sin(theta), and Delta m^2. For this example, we set them to current best-fit values for atmospheric neutrinos. 370 | 371 | ```python 372 | # Find this example in NuOscProbExact/test/example_2nu_vacuum.py 373 | 374 | import numpy as np 375 | 376 | import oscprob2nu 377 | import hamiltonians2nu 378 | from globaldefs import * 379 | 380 | energy = 1.e9 # Neutrino energy [eV] 381 | baseline = 1.3e3 # Baseline [km] 382 | 383 | h_vacuum_energy_indep = hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent(S23_NO_BF, D31_NO_BF) 384 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 385 | 386 | Pee, Pem, Pme, Pmm = oscprob2nu.probabilities_2nu(h_vacuum, baseline*CONV_KM_TO_INV_EV) 387 | 388 | print("Pee = %6.5f, Pem = %6.5f" % (Pee, Pem)) 389 | print("Pme = %6.5f, Pmm = %6.5f" % (Pme, Pmm)) 390 | ```` 391 | This returns 392 | ```shell 393 | Pee = 0.29595, Pem = 0.70405 394 | Pme = 0.70405, Pmm = 0.29595 395 | ``` 396 | 397 | Like in the three-neutrino case, we can also return the coefficients `h1`, `h2`, `h3` of the expansion of the Hamiltonian in terms of Pauli matrices (Table I in the paper), or the time-evolution operator `evol_operator` itself, as a 2x2 matrix (Eq. (5) in the paper). 398 | ```python 399 | # Find this example in NuOscProbExact/test/example_2nu_vacuum_coefficients.py 400 | 401 | import numpy as np 402 | 403 | import oscprob2nu 404 | import hamiltonians2nu 405 | from globaldefs import * 406 | 407 | energy = 1.e9 # Neutrino energy [eV] 408 | baseline = 1.3e3 # Baseline [km] 409 | 410 | h_vacuum_energy_indep = hamiltonians2nu.hamiltonian_2nu_vacuum_energy_independent(S23_NO_BF, D31_NO_BF) 411 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 412 | 413 | h1, h2, h3 = oscprob2nu.hamiltonian_2nu_coefficients(h_vacuum) 414 | print('h1: {:.4e}'.format(h1)) 415 | print('h2: {:.4e}'.format(h2)) 416 | print('h3: {:.4e}'.format(h3)) 417 | print() 418 | 419 | evol_operator = oscprob2nu.evolution_operator_2nu(h_vacuum, baseline*CONV_KM_TO_INV_EV) 420 | print('U2 = ') 421 | with np.printoptions(precision=3, suppress=True): 422 | print(np.array(evol_operator)) 423 | ``` 424 | This returns 425 | ```shell 426 | h1: -6.2270e-13 427 | h2: -0.0000e+00 428 | h3: -1.0352e-13 429 | 430 | U2 = 431 | [[-0.526-0.139j -0. -0.839j] 432 | [ 0. -0.839j -0.526+0.139j]] 433 | ``` 434 | 435 | 436 | ### Three-neutrino oscillations in vacuum: fixed energy, varying baseline 437 | 438 | Now we fix the energy at, say, 10 MeV, and vary the baseline between 1 and 500 km. We use a fine grid in `L` so that the oscillations are clearly rendered. 439 | 440 | ```python 441 | import numpy as np 442 | 443 | import oscprob3nu 444 | import hamiltonians3nu 445 | from globaldefs import * 446 | 447 | energy = 1.e7 # Neutrino energy [eV] 448 | 449 | # Baselines, L 450 | log10_l_min = 0.0 # log10 [km] 451 | log10_l_max = 3.0 # log10 [km] 452 | log10_l_npts = 1000 453 | log10_l_val = np.linspace(log10_l_min, log10_l_max, log10_l_npts) # [km] 454 | l_val = [10.**x for x in log10_l_val] 455 | 456 | 457 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( \ 458 | S12_NO_BF, S23_NO_BF, 459 | S13_NO_BF, DCP_NO_BF, 460 | D21_NO_BF, D31_NO_BF) 461 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 462 | 463 | # Each element of prob: [Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt] 464 | prob = [oscprob3nu.probabilities_3nu(h_vacuum, CONV_KM_TO_INV_EV*l) for l in l_val] 465 | prob_ee = [x[0] for x in prob] # Pee 466 | prob_em = [x[1] for x in prob] # Pem 467 | prob_et = [x[2] for x in prob] # Pet 468 | ``` 469 | 470 | To plot the data: 471 | ```python 472 | from pylab import * 473 | from matplotlib import * 474 | import matplotlib as mpl 475 | 476 | fig = plt.figure(figsize=[9,9]) 477 | ax = fig.add_subplot(1,1,1) 478 | 479 | ax.plot(l_val, prob_ee, label=r'$P_{\nu_e \to \nu_e}$', color='C0', zorder=1) 480 | ax.plot(l_val, prob_em, label=r'$P_{\nu_e \to \nu_\mu}$', color='C1', zorder=1) 481 | ax.plot(l_val, prob_et, label=r'$P_{\nu_e \to \nu_\tau}$', color='C2', zorder=1) 482 | 483 | ax.set_xlabel(r'Baseline $L$ [km]', fontsize=25) 484 | ax.set_ylabel(r'Three-neutrino probability', fontsize=25) 485 | ax.legend(loc='center left', frameon=False) 486 | ax.set_xlim([10.**log10_l_min, 10.**log10_l_max]) 487 | ax.set_xscale('log') 488 | ax.set_ylim([0.0, 1.0]) 489 | 490 | plt.show() 491 | ```` 492 | 493 | Alternatively, you can automatically produce plots of probability *vs.* baseline using the following function from the `oscprob3nu_tests` module: 494 | ```python 495 | import oscprob3nu_tests 496 | 497 | case = 'vacuum' 498 | oscprob3nu_tests.plot_probability_3nu_vs_baseline( 499 | case, energy=1.e-2, 500 | log10_l_min=0.0, log10_l_max=3.0, log10_l_npts=1000, 501 | plot_prob_ee=True, plot_prob_em=True, plot_prob_et=True, 502 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 503 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 504 | output_filename='prob_3nu_vacuum_vs_baseline_ee_em_et', output_format='png', 505 | legend_loc='center left', legend_ncol=1, path_save='../fig/') 506 | ``` 507 | The function `plot_probability_3nu_vs_baseline` assumes that `energy` is in GeV and the (log10) of the baselines `log10_l_min` and `log_l_max` are in km. The function call above produces the following plot: 508 | 509 | 510 | 511 | The parameter `case` can take any of the following values: 512 | * `vacuum`: for oscillations in vacuum, assuming the default values of mixing parameters from the `globaldefs` module 513 | * `matter`: for oscillations in constant matter, assuming the density of the Earth's crust as set in `globaldefs` 514 | * `nsi`: for oscillations in matter with non-standard interactions, with the NSI strengh parameters fixed to the default values in `globaldefs` 515 | * `liv`: for oscillations in a CPT-odd Lorentz invariance-violating (LIV) background, with the LIV parameters fixed to the default values in `globaldefs` 516 | 517 | For more information about these cases, see the paper [arXiv:1904.12391](http://arxiv.org/abs/1904.12391). For more information about how to use `plot_probability_3nu_vs_baseline`, see the documentation of the function in the `oscprob3nu_tests` module. 518 | 519 | 520 | ### Three-neutrino oscillations in vacuum: fixed baseline, varying energy 521 | 522 | Now we fix the baseline at, say, 1300 km, and vary the energy between 100 MeV and 10 GeV. 523 | 524 | ```python 525 | import numpy as np 526 | 527 | import oscprob3nu 528 | import hamiltonians3nu 529 | from globaldefs import * 530 | 531 | baseline = 1.3e3 # Baseline [km] 532 | baseline = baseline*CONV_KM_TO_INV_EV # [eV^{-1}] 533 | 534 | # Neutrino energies 535 | log10_energy_min = -1.0 # [GeV] 536 | log10_energy_max = 1.0 # [GeV] 537 | log10_energy_npts = 200 538 | log10_energy = np.linspace( log10_energy_min, 539 | log10_energy_max, 540 | log10_energy_npts) 541 | energy = [10.**x for x in log10_energy] # [GeV] 542 | 543 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( \ 544 | S12_NO_BF, S23_NO_BF, 545 | S13_NO_BF, DCP_NO_BF, 546 | D21_NO_BF, D31_NO_BF) 547 | 548 | # Each element of prob: [Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt] 549 | prob = [oscprob3nu.probabilities_3nu(np.multiply(1./x/1.e9, h_vacuum_energy_indep), baseline) \ 550 | for x in energy] 551 | prob_ee = [x[0] for x in prob] # Pee 552 | prob_em = [x[1] for x in prob] # Pem 553 | prob_et = [x[2] for x in prob] # Pet 554 | ``` 555 | 556 | To plot the data: 557 | ```python 558 | from pylab import * 559 | from matplotlib import * 560 | import matplotlib as mpl 561 | 562 | fig = plt.figure(figsize=[9,9]) 563 | ax = fig.add_subplot(1,1,1) 564 | 565 | ax.plot(energy, prob_ee, label=r'$P_{\nu_e \to \nu_e}$', color='C0', zorder=1) 566 | ax.plot(energy, prob_em, label=r'$P_{\nu_e \to \nu_\mu}$', color='C1', zorder=1) 567 | ax.plot(energy, prob_et, label=r'$P_{\nu_e \to \nu_\tau}$', color='C2', zorder=1) 568 | 569 | ax.set_xlabel(r'Neutrino energy $E$ [GeV]', fontsize=25) 570 | ax.set_ylabel(r'Three-neutrino probability', fontsize=25) 571 | ax.legend(loc='center right', frameon=False) 572 | ax.set_xlim([10.**log10_energy_min, 10.**log10_energy_max]) 573 | ax.set_xscale('log') 574 | ax.set_ylim([0.0, 1.0]) 575 | 576 | plt.show() 577 | ```` 578 | 579 | Alternatively, you can automatically produce plots of probability using the following function from the `oscprob3nu_tests` module: 580 | ```python 581 | import oscprob3nu_tests 582 | 583 | case = 'vacuum' 584 | oscprob3nu_tests.plot_probability_3nu_vs_energy( 585 | case, baseline=1.e3, 586 | log10_energy_min=-1.0, log10_energy_max=1.0, log10_energy_npts=200, 587 | plot_prob_ee=True, plot_prob_em=True, plot_prob_et=True, 588 | plot_prob_me=False, plot_prob_mm=False, plot_prob_mt=False, 589 | plot_prob_te=False, plot_prob_tm=False, plot_prob_tt=False, 590 | output_filename='prob_3nu_vacuum_vs_energy_ee_em_et', output_format='png', 591 | legend_loc='center right', legend_ncol=1, path_save='../fig/') 592 | ``` 593 | The function `plot_probability_3nu_vs_energy` assumes that `baseline` is in km and the (log10) of the energies `log10_energy_min` and `log10_energy_max` are in GeV. The function call above produces the following plot: 594 | 595 | 596 | 597 | The parameter `case` can take any of the same values as listed [above](#oscillations-in-vacuum-fixed-energy-varying-baseline). 598 | 599 | 600 | ### Three-neutrino oscillations in matter 601 | 602 | For oscillation in matter, we proceed in an analogous way as for oscillations in vacuum. To compute the Hamiltonian in matter, we can use the routine `hamiltonian_matter` in the module `hamiltonians3nu`. First, we need to compute the energy-independent `h_vacuum_energy_independent`, and then pass it to `hamiltonian_3nu_matter`, together with the `energy` and the neutrino-electron charged-current potential `VCC`, with V_CC = sqrt(2.0) * G_F * n_e. This routine is called as: 603 | ```python 604 | hamiltonian_3nu_matter(h_vacuum_energy_independent, energy, VCC) 605 | ``` 606 | 607 | In the example below, we set the matter potential to `VCC_EARTH_CRUST`, which is computed using the averaage electron density of the crust of the Earth (3 g cm^{-3}), and is read from `globaldefs`. 608 | ```python 609 | # Find this example in NuOscProbExact/test/example_3nu_matter.py 610 | 611 | import oscprob3nu 612 | import hamiltonians3nu 613 | from globaldefs import * 614 | 615 | energy = 1.e9 # Neutrino energy [eV] 616 | baseline = 1.3e3 # Baseline [km] 617 | 618 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( \ 619 | S12_NO_BF, S23_NO_BF, 620 | S13_NO_BF, DCP_NO_BF, 621 | D21_NO_BF, D31_NO_BF) 622 | 623 | # Units of VCC_EARTH_CRUST: [eV] 624 | h_matter = hamiltonians3nu.hamiltonian_3nu_matter(h_vacuum_energy_indep, energy, VCC_EARTH_CRUST) 625 | 626 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu( h_matter, 627 | baseline*CONV_KM_TO_INV_EV) 628 | 629 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 630 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 631 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 632 | ```` 633 | This returns 634 | ```shell 635 | Pee = 0.95262, Pem = 0.00623, Pet = 0.04115 636 | Pme = 0.02590, Pmm = 0.37644, Pmt = 0.59766 637 | Pte = 0.02148, Ptm = 0.61733, Ptt = 0.36119 638 | ``` 639 | 640 | ### Three-neutrino oscillations in matter with non-standard interactions (NSI) 641 | 642 | For oscillation in matter with NSI, we can use the routine `hamiltonian_3nu_nsi` in the module `hamiltonians3nu`. First, we need to compute `h_vacuum_energy_independent`, and then pass it to `hamiltonian_nsi`, together with `energy`, `VCC`, and a vector `eps` containing the NSI strength parameters, *i.e.*, 643 | ```python 644 | eps = [eps_ee, eps_em, eps_et, eps_mm, eps_mt, eps_tt] 645 | ``` 646 | This routine is called as 647 | ```python 648 | hamiltonian_3nu_nsi(h_vacuum_energy_independent, energy, VCC, eps) 649 | ``` 650 | 651 | In the example below, we set `eps` to its default value `EPS_3` pulled from `globaldefs`: 652 | ```python 653 | # Find this example in NuOscProbExact/test/example_3nu_nsi.py 654 | 655 | import oscprob3nu 656 | import hamiltonians3nu 657 | from globaldefs import * 658 | 659 | energy = 1.e9 # Neutrino energy [eV] 660 | baseline = 1.3e3 # Baseline [km] 661 | 662 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( \ 663 | S12_NO_BF, S23_NO_BF, 664 | S13_NO_BF, DCP_NO_BF, 665 | D21_NO_BF, D31_NO_BF) 666 | 667 | # EPS_3 is the 3x3 matrix of NSI strength parameters, read from globaldefs; see that file for the values 668 | h_nsi = hamiltonians3nu.hamiltonian_3nu_nsi(h_vacuum_energy_indep, energy, VCC_EARTH_CRUST, EPS_3) 669 | 670 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu( h_nsi, 671 | baseline*CONV_KM_TO_INV_EV) 672 | 673 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 674 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 675 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 676 | ```` 677 | This returns 678 | ```shell 679 | Pee = 0.92494, Pem = 0.01758, Pet = 0.05749 680 | Pme = 0.03652, Pmm = 0.32524, Pmt = 0.63824 681 | Pte = 0.03855, Ptm = 0.65718, Ptt = 0.30427 682 | ``` 683 | 684 | 685 | ### Three-neutrino oscillations in a Lorentz invariance-violating (LIV) background 686 | 687 | For oscillation LIV, we can use the routine `hamiltonian_3nu_liv` in the module `hamiltonians3nu`. As before, first, we need to compute `h_vacuum_energy_independent`, and then pass it to `hamiltonian_3nu_liv`, together with `energy` and the following LIV parameters: `sxi12` (sin(xi_12)), `sxi23` (sin(xi_23)), `sxi13` (sin(xi_13)), `dxiCP` (new CP-violation phase), `b1` (first eigenvalue of the LIV operator), `b2` (second eigenvalue), `b3` (third eigenvalue), and `Lambda` (energy scale of LIV). 688 | 689 | This routine is called as 690 | ```python 691 | hamiltonian_3nu_liv(h_vacuum_energy_independent, energy, sxi12, sxi23, sxi13, dxiCP, b1, b2, b3, Lambda) 692 | ``` 693 | 694 | In the example below, we set the LIV parameters to their default values pulled from `globaldefs`: 695 | ```python 696 | # Find this example in NuOscProbExact/test/example_3nu_liv.py 697 | 698 | import oscprob3nu 699 | import hamiltonians3nu 700 | from globaldefs import * 701 | 702 | energy = 1.e9 # Neutrino energy [eV] 703 | baseline = 1.3e3 # Baseline [km] 704 | 705 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_BF, S23_BF, 706 | S13_BF, DCP_BF, 707 | D21_BF, D31_BF) 708 | 709 | # The values of the LIV parameters (SXI12, SXI23, SXI13, DXICP, B1, B2, B3, LAMBDA) are read 710 | # from globaldefs 711 | h_liv = hamiltonians3nu.hamiltonian_3nu_liv(h_vacuum_energy_indep, energy, 712 | SXI12, SXI23, SXI13, DXICP, 713 | B1, B2, B3, LAMBDA) 714 | 715 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu( h_liv, 716 | baseline*CONV_KM_TO_INV_EV) 717 | 718 | print("Pee = %6.5f, Pem = %6.5f, Pet = %6.5f" % (Pee, Pem, Pet)) 719 | print("Pme = %6.5f, Pmm = %6.5f, Pmt = %6.5f" % (Pme, Pmm, Pmt)) 720 | print("Pte = %6.5f, Ptm = %6.5f, Ptt = %6.5f" % (Pte, Ptm, Ptt)) 721 | ```` 722 | This returns 723 | ```shell 724 | Pee = 0.92721, Pem = 0.05299, Pet = 0.01980 725 | Pme = 0.05609, Pmm = 0.25288, Pmt = 0.69103 726 | Pte = 0.01670, Ptm = 0.69412, Ptt = 0.28917 727 | ``` 728 | 729 | ### Arbitrary Hamiltonians 730 | 731 | Of course, you can supply your custom Hamiltonian and compute the associated oscillation probabilities; see [Trivial example](#trivial-example) above. Usually, you will want to add an extra term from your preferred model to the vacuum Hamiltonian. To do that, take a cue from the examples above. 732 | 733 | In the following example, the function `hamiltonian_mymodel`, supplied by you, should return a 3x3 matrix: 734 | ```python 735 | import oscprob3nu 736 | import hamiltonians3nu 737 | from globaldefs import * 738 | 739 | energy = 1.e9 # Neutrino energy [eV] 740 | baseline = 1.3e3 # Baseline [km] 741 | 742 | h_vacuum_energy_indep = hamiltonians3nu.hamiltonian_3nu_vacuum_energy_independent( S12_BF, S23_BF, 743 | S13_BF, DCP_BF, 744 | D21_BF, D31_BF) 745 | h_vacuum = np.multiply(1./energy, h_vacuum_energy_indep) 746 | h_mymodel = h_vacuum + hamiltonian_mymodel(mymodel_parameters) 747 | 748 | Pee, Pem, Pet, Pme, Pmm, Pmt, Pte, Ptm, Ptt = oscprob3nu.probabilities_3nu( h_mymodel, 749 | baseline*CONV_KM_TO_INV_EV) 750 | 751 | ``` 752 | Though we do not show it here, `hamiltonian_mymodel` could also depend on `energy`. The code for two-neutrino oscillations is analogous, but `hamiltonian_mymodel` should return a 2x2 matrix instead. 753 | 754 | 755 | ## Documentation and help 756 | 757 | All of the modules provided in **NuOscProbExact** have been documented using Python docstrings. These are human-readable by opening the source `.py` files. Alternatively, they can be printed from within an interactive Python session. 758 | 759 | To view the documentation of a module from within an interactive Python session, run, *e.g.*, 760 | ```python 761 | import oscprob3nu 762 | 763 | print(oscprob3nu.__doc__) 764 | ``` 765 | This will print to screen a description of what the module does (in this example, `oscprob3nu`) and a list of the functions that it contains, including a description of each. 766 | 767 | To view the documentation of a particular function from within an interactive Python session, run, *e.g.*, 768 | ```python 769 | import oscprob3nu 770 | 771 | help(oscprob3nu.hamiltonian_3nu_coefficients) 772 | ``` 773 | This will print to screen a description of what the function does (in the example above, `oscprob3nu.hamiltonian_3nu_coefficients`), a list and description of its input parameters, and a description of the values that it returns. 774 | 775 | 776 | ## Citing 777 | 778 | If you use **NuOscProbExact** in your work, we ask you that you please cite the following paper: Mauricio Bustamante, *NuOscProbExact: a general-purpose code to compute exact two-flavor and three-flavor neutrino oscillation probabilities* ([arXiv:1904.12391](http://arxiv.org/abs/1904.12391)). 779 | 780 | If you are citing **NuOscProbExact** in a document that will be uploaded to the arXiv, please consider using the LaTeX or BibTeX entries provided by INSPIRE ([link here](http://inspirehep.net/record/1731803/export/hx)): 781 | ``` 782 | @article{Bustamante:2019ggq, 783 | author = "Bustamante, Mauricio", 784 | title = "{NuOscProbExact: a general-purpose code to compute 785 | exact two-flavor and three-flavor neutrino 786 | oscillation probabilities}", 787 | year = "2019", 788 | eprint = "1904.12391", 789 | archivePrefix = "arXiv", 790 | primaryClass = "hep-ph", 791 | SLACcitation = "%%CITATION = ARXIV:1904.12391;%%" 792 | } 793 | ``` 794 | 795 | 796 | 797 | 798 | 799 | --------------------------------------------------------------------------------