aboutsummaryrefslogtreecommitdiff
path: root/utils/plot-atmospheric-fluxes
diff options
context:
space:
mode:
authortlatorre <tlatorre@uchicago.edu>2020-02-19 11:53:35 -0600
committertlatorre <tlatorre@uchicago.edu>2020-02-19 11:53:35 -0600
commit3550aafb5784197cd2b0a3e5c1af645175084234 (patch)
treedd725e3dc49296b423019c9c4cb3e203c09893d9 /utils/plot-atmospheric-fluxes
parentf41d2cfab0ebe9d8a41333b6a24cf44550ce68d2 (diff)
downloadsddm-3550aafb5784197cd2b0a3e5c1af645175084234.tar.gz
sddm-3550aafb5784197cd2b0a3e5c1af645175084234.tar.bz2
sddm-3550aafb5784197cd2b0a3e5c1af645175084234.zip
add scripts to apply neutrino oscillations to the atmospheric fluxes
This commit adds four scripts: 1. calculate-atmospheric-oscillations This script uses an independent python package called nucraft to calculate the neutrino oscillation probabilities for atmospheric neutrinos. These probabilities are calculated as a function of energy and cosine of the zenith angle and stored in text files. 2. apply-atmospheric-oscillations This script combines the high energy 2D atmospheric neutrino flux from Barr and the low energy 1D flux from Battistoni and then applies neutrino oscillations to them. The results are then stored in new flux files that can be used with a modified version of GENIE to simulate the oscillated atmospheric neutrino flux from 10 MeV to 10 GeV. 3. plot-atmospheric-fluxes This is a simple script to plot the atmospheric flux files produced by apply-atmospheric-oscillations. 4. plot-atmospheric-oscillations This is a simple script to plot the 2D neutrino oscillation probabilities.
Diffstat (limited to 'utils/plot-atmospheric-fluxes')
-rwxr-xr-xutils/plot-atmospheric-fluxes285
1 files changed, 285 insertions, 0 deletions
diff --git a/utils/plot-atmospheric-fluxes b/utils/plot-atmospheric-fluxes
new file mode 100755
index 0000000..fb4861d
--- /dev/null
+++ b/utils/plot-atmospheric-fluxes
@@ -0,0 +1,285 @@
+#!/usr/bin/env python
+"""
+Script for plotting solar fluxes from
+http://www-pnp.physics.ox.ac.uk/~barr/fluxfiles/0403i/index.html.
+"""
+from __future__ import print_function, division
+import numpy as np
+import os
+
+def splitext(path):
+ """
+ Like os.path.splitext() except it returns the full extension if the
+ filename has multiple extensions, for example:
+
+ splitext('foo.tar.gz') -> 'foo', '.tar.gz'
+ """
+ full_root, full_ext = os.path.splitext(path)
+ while True:
+ root, ext = os.path.splitext(full_root)
+ if ext:
+ full_ext = ext + full_ext
+ full_root = root
+ else:
+ break
+
+ return full_root, full_ext
+
+# Taken from https://raw.githubusercontent.com/mwaskom/seaborn/c73055b2a9d9830c6fbbace07127c370389d04dd/seaborn/utils.py
+def despine(fig=None, ax=None, top=True, right=True, left=False,
+ bottom=False, offset=None, trim=False):
+ """Remove the top and right spines from plot(s).
+
+ fig : matplotlib figure, optional
+ Figure to despine all axes of, default uses current figure.
+ ax : matplotlib axes, optional
+ Specific axes object to despine.
+ top, right, left, bottom : boolean, optional
+ If True, remove that spine.
+ offset : int or dict, optional
+ Absolute distance, in points, spines should be moved away
+ from the axes (negative values move spines inward). A single value
+ applies to all spines; a dict can be used to set offset values per
+ side.
+ trim : bool, optional
+ If True, limit spines to the smallest and largest major tick
+ on each non-despined axis.
+
+ Returns
+ -------
+ None
+
+ """
+ # Get references to the axes we want
+ if fig is None and ax is None:
+ axes = plt.gcf().axes
+ elif fig is not None:
+ axes = fig.axes
+ elif ax is not None:
+ axes = [ax]
+
+ for ax_i in axes:
+ for side in ["top", "right", "left", "bottom"]:
+ # Toggle the spine objects
+ is_visible = not locals()[side]
+ ax_i.spines[side].set_visible(is_visible)
+ if offset is not None and is_visible:
+ try:
+ val = offset.get(side, 0)
+ except AttributeError:
+ val = offset
+ _set_spine_position(ax_i.spines[side], ('outward', val))
+
+ # Potentially move the ticks
+ if left and not right:
+ maj_on = any(
+ t.tick1line.get_visible()
+ for t in ax_i.yaxis.majorTicks
+ )
+ min_on = any(
+ t.tick1line.get_visible()
+ for t in ax_i.yaxis.minorTicks
+ )
+ ax_i.yaxis.set_ticks_position("right")
+ for t in ax_i.yaxis.majorTicks:
+ t.tick2line.set_visible(maj_on)
+ for t in ax_i.yaxis.minorTicks:
+ t.tick2line.set_visible(min_on)
+
+ if bottom and not top:
+ maj_on = any(
+ t.tick1line.get_visible()
+ for t in ax_i.xaxis.majorTicks
+ )
+ min_on = any(
+ t.tick1line.get_visible()
+ for t in ax_i.xaxis.minorTicks
+ )
+ ax_i.xaxis.set_ticks_position("top")
+ for t in ax_i.xaxis.majorTicks:
+ t.tick2line.set_visible(maj_on)
+ for t in ax_i.xaxis.minorTicks:
+ t.tick2line.set_visible(min_on)
+
+ if trim:
+ # clip off the parts of the spines that extend past major ticks
+ xticks = ax_i.get_xticks()
+ if xticks.size:
+ firsttick = np.compress(xticks >= min(ax_i.get_xlim()),
+ xticks)[0]
+ lasttick = np.compress(xticks <= max(ax_i.get_xlim()),
+ xticks)[-1]
+ ax_i.spines['bottom'].set_bounds(firsttick, lasttick)
+ ax_i.spines['top'].set_bounds(firsttick, lasttick)
+ newticks = xticks.compress(xticks <= lasttick)
+ newticks = newticks.compress(newticks >= firsttick)
+ ax_i.set_xticks(newticks)
+
+ xticks_minor = [t for t in ax_i.get_xticks(minor=True) if firsttick < t < lasttick]
+ ax_i.set_xticks(xticks_minor,minor=True)
+
+ yticks = ax_i.get_yticks()
+ if yticks.size:
+ firsttick = np.compress(yticks >= min(ax_i.get_ylim()),
+ yticks)[0]
+ lasttick = np.compress(yticks <= max(ax_i.get_ylim()),
+ yticks)[-1]
+ ax_i.spines['left'].set_bounds(firsttick, lasttick)
+ ax_i.spines['right'].set_bounds(firsttick, lasttick)
+ newticks = yticks.compress(yticks <= lasttick)
+ newticks = newticks.compress(newticks >= firsttick)
+ ax_i.set_yticks(newticks)
+
+ yticks_minor = [t for t in ax_i.get_yticks(minor=True) if firsttick < t < lasttick]
+ ax_i.set_yticks(yticks_minor,minor=True)
+
+# Data is in the form: Fluxes in bins of neutrino energy (equally spaced bins
+# in logE with 10 bins per decade with the low edge of the first bin at 100
+# MeV) and zenith angle (20 bins equally spaced in cos(zenith) with bin width
+# 0.1), integrated over azimuth. Note logE means log to base e. Fluxes below 10
+# GeV are from the 3D calculation.
+
+# The files all have the same format. After the initial comment lines (starting
+# with a # character), the files contain one line per bin. No smoothoing
+# between bins has been done. The columns are:
+#
+# 1. Energy at centre of bin in GeV
+# 2. Zenith files: Cos(zenith) at centre of bin
+# Azimuth files: Azimuth at centre of bin (degrees)
+# 3. Flux in dN/dlogE in /m**2/steradian/sec
+# 4. MC Statistical error on the flux
+# 5. Number of unweighted events entering the bin (not too useful)
+
+if __name__ == '__main__':
+ import argparse
+ import matplotlib
+ import glob
+
+ parser = argparse.ArgumentParser("plot solar fluxes")
+ parser.add_argument("filenames", nargs='+', help="filenames of flux files")
+ parser.add_argument("--save", action="store_true", default=False, help="save plots")
+ args = parser.parse_args()
+
+ if args.save:
+ # default \textwidth for a fullpage article in Latex is 16.50764 cm.
+ # You can figure this out by compiling the following TeX document:
+ #
+ # \documentclass{article}
+ # \usepackage{fullpage}
+ # \usepackage{layouts}
+ # \begin{document}
+ # textwidth in cm: \printinunitsof{cm}\prntlen{\textwidth}
+ # \end{document}
+
+ width = 16.50764
+ width /= 2.54 # cm -> inches
+ # According to this page:
+ # http://www-personal.umich.edu/~jpboyd/eng403_chap2_tuftegospel.pdf,
+ # Tufte suggests an aspect ratio of 1.5 - 1.6.
+ height = width/1.5
+ FIGSIZE = (width,height)
+
+ import matplotlib.pyplot as plt
+
+ font = {'family':'serif', 'serif': ['computer modern roman']}
+ plt.rc('font',**font)
+
+ plt.rc('text', usetex=True)
+ else:
+ # on retina screens, the default plots are way too small
+ # by using Qt5 and setting QT_AUTO_SCREEN_SCALE_FACTOR=1
+ # Qt5 will scale everything using the dpi in ~/.Xresources
+ import matplotlib
+ matplotlib.use("Qt5Agg")
+
+ import matplotlib.pyplot as plt
+
+ # Default figure size. Currently set to my monitor width and height so that
+ # things are properly formatted
+ FIGSIZE = (13.78,7.48)
+
+ font = {'family':'serif', 'serif': ['computer modern roman']}
+ plt.rc('font',**font)
+
+ # Make the defalt font bigger
+ plt.rc('font', size=22)
+
+ plt.rc('text', usetex=True)
+
+ fig = plt.figure(figsize=FIGSIZE)
+
+ colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
+ linestyles = ['-','--']
+
+ def key(filename):
+ head, tail = os.path.split(filename)
+
+ k = 0
+ if tail.startswith('fmax'):
+ k += 1
+ if 'nue' in tail:
+ k += 10
+ elif 'nbe' in tail:
+ k += 20
+ elif 'num' in tail:
+ k += 30
+ elif 'nbm' in tail:
+ k += 40
+ elif 'nut' in tail:
+ k += 50
+ elif 'nbt' in tail:
+ k += 60
+
+ return k
+
+ for filename in sorted(args.filenames,key=key):
+ head, tail = os.path.split(filename)
+
+ print(filename)
+
+ data = np.genfromtxt(filename)
+
+ shape1 = len(np.unique(data[:,0]))
+
+ x = data[:,0].reshape((-1,shape1))
+ y = data[:,1].reshape((-1,shape1))
+ z = data[:,2].reshape((-1,shape1))
+
+ # Convert to MeV
+ x *= 1000.0
+ z /= 1000.0
+
+ zbins = np.linspace(-1,1,21)
+ dz = zbins[1] - zbins[0]
+
+ x = x[0]
+ # Integrate over cos(theta) and multiply by 2*pi to convert 3D flux to
+ # a total flux
+ y = np.sum(z*dz,axis=0)*2*np.pi
+
+ if 'sno_nue' in tail:
+ plt.plot(x,y,color=colors[0],linestyle=linestyles[0],label=r'$\nu_e$')
+ elif 'sno_nbe' in tail:
+ plt.plot(x,y,color=colors[0],linestyle=linestyles[1],label=r'$\overline{\nu}_e$')
+ elif 'sno_num' in tail:
+ plt.plot(x,y,color=colors[1],linestyle=linestyles[0],label=r'$\nu_\mu$')
+ elif 'sno_nbm' in tail:
+ plt.plot(x,y,color=colors[1],linestyle=linestyles[1],label=r'$\overline{\nu}_\mu$')
+ elif 'sno_nut' in tail:
+ plt.plot(x,y,color=colors[2],linestyle=linestyles[0],label=r'$\nu_\tau$')
+ elif 'sno_nbt' in tail:
+ plt.plot(x,y,color=colors[2],linestyle=linestyles[1],label=r'$\overline{\nu}_\tau$')
+
+ plt.gca().set_xscale("log")
+ plt.gca().set_yscale("log")
+ despine(fig,trim=True)
+ plt.xlabel("$E$ (MeV)")
+ plt.ylabel(r"$\mathrm{d}\Phi/\mathrm{d}E$ (1/$\mathrm{m}^2$/sec/MeV)")
+ plt.legend()
+ plt.tight_layout()
+
+ if args.save:
+ plt.savefig("irc01_atmospheric_flux.pdf")
+ plt.savefig("irc01_atmospheric_flux.eps")
+
+ plt.show()