aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xcontour.py62
-rwxr-xr-xfig2.py180
-rwxr-xr-xfig2_austin.py162
-rwxr-xr-xfr.py12
-rwxr-xr-xplot_sens.py32
-rwxr-xr-xsens.py28
-rw-r--r--submitter/sens_dag.py17
-rw-r--r--utils/enums.py6
-rw-r--r--utils/fr.py6
-rw-r--r--utils/gf.py5
-rw-r--r--utils/llh.py27
-rw-r--r--utils/plot.py197
12 files changed, 616 insertions, 118 deletions
diff --git a/contour.py b/contour.py
index 5615fe2..35119e8 100755
--- a/contour.py
+++ b/contour.py
@@ -10,6 +10,7 @@ HESE flavour ratio contour
from __future__ import absolute_import, division
+import os
import argparse
from functools import partial
@@ -22,7 +23,7 @@ from utils import misc as misc_utils
from utils import mn as mn_utils
from utils import plot as plot_utils
from utils.enums import str_enum
-from utils.enums import DataType, Likelihood, ParamTag
+from utils.enums import DataType, Likelihood, ParamTag, PriorsCateg
from utils.param import Param, ParamSet, get_paramsets
from pymultinest import Analyzer, run
@@ -32,12 +33,13 @@ def define_nuisance():
"""Define the nuisance parameters."""
nuisance = []
tag = ParamTag.NUISANCE
+ lg_prior = PriorsCateg.LIMITEDGAUSS
nuisance.extend([
- Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0. , 50.], std=0.3, tag=tag),
- Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 50.], std=0.05, tag=tag),
- Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroNorm', value=6.9, seed=[0.1, 10.], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.1, 10.], std=0.4, prior=lg_prior, tag=tag),
+ Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 20.], std=2.4, prior=lg_prior, tag=tag),
+ Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 10.], std=0.1, tag=tag),
+ Param(name='astroNorm', value=6.9, seed=[0., 5. ], ranges=[0. , 20.], std=1.5, tag=tag),
+ Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
])
return ParamSet(nuisance)
@@ -120,12 +122,12 @@ def gen_identifier(args):
def gen_figtext(args, asimov_paramset):
f = ''
if args.data is DataType.REAL:
- f += 'IceCube Preliminary\n'
+ f += 'IceCube Preliminary'
else:
ir1, ir2, ir3 = misc_utils.solve_ratio(args.injected_ratio)
- f += 'Injected ratio = [{0}, {1}, {2}]\n'.format(ir1, ir2, ir3)
+ f += 'Injected ratio = [{0}, {1}, {2}]'.format(ir1, ir2, ir3)
for param in asimov_paramset:
- f += 'Injected {0} = {1:.3f}\n'.format(
+ f += '\nInjected {0:20s} = {1:.3f}'.format(
param.name, param.nominal_value
)
return f
@@ -210,6 +212,9 @@ def main():
fitter = fitter
)
+ cwd = os.getcwd()
+ os.chdir(prefix[:-len(os.path.basename(prefix))])
+
print 'Running evidence calculation for {0}'.format(prefix)
run(
LogLikelihood = lnProbEval,
@@ -217,12 +222,14 @@ def main():
n_dims = n_params,
n_live_points = args.mn_live_points,
evidence_tolerance = args.mn_tolerance,
- outputfiles_basename = prefix,
+ outputfiles_basename = prefix[-len(os.path.basename(prefix)):],
importance_nested_sampling = True,
resume = False,
verbose = True
)
+ os.chdir(cwd)
+
# Analyze
analyser = Analyzer(
outputfiles_basename=prefix, n_params=n_params
@@ -230,38 +237,53 @@ def main():
print analyser
pranges = hypo_paramset.ranges
+
+ bf = analyser.get_best_fit()['parameters']
+ for i in xrange(len(bf)):
+ bf[i] = (pranges[i][1]-pranges[i][0])*bf[i] + pranges[i][0]
+ print 'bestfit = ', bf
+ print 'bestfit log_likelihood', analyser.get_best_fit()['log_likelihood']
+
+ print
+ print '{0:50} = {1}'.format('global evidence', analyser.get_stats()['global evidence'])
+ print
+
fig_text = gen_figtext(args, asimov_paramset)
+ fig_text += '\nBestfit LLH = {0}'.format(analyser.get_best_fit()['log_likelihood'])
+ fig_text += '\nBestfits = '
+ for x in bf: fig_text += '{0:.2f} '.format(x)
- if args.plot_chains:
+ if args.plot_chains or args.plot_triangle:
chains = analyser.get_data()[:,2:]
for x in chains:
for i in xrange(len(x)):
x[i] = (pranges[i][1]-pranges[i][0])*x[i] + pranges[i][0]
+
+ if args.plot_chains:
+ of = outfile[:5]+outfile[5:].replace('data', 'plots')+'_posterior'
plot_utils.chainer_plot(
infile = chains,
- outfile = outfile[:5]+outfile[5:].replace('data', 'plots')+'_posterior',
- outformat = ['pdf'],
+ outfile = of,
+ outformat = ['png'],
args = args,
llh_paramset = hypo_paramset,
fig_text = fig_text
)
+ print 'Saved plot', of
if args.plot_triangle:
- llh = analyser.get_data()[:,1]
+ llh = -0.5 * analyser.get_data()[:,1]
- chains = analyser.get_data()[:,2:]
- for x in chains:
- for i in xrange(len(x)):
- x[i] = (pranges[i][1]-pranges[i][0])*x[i] + pranges[i][0]
flavour_angles = chains[:,-2:]
flavour_ratios = np.array(
- map(fr_utils.angles_to_fr, flavour_angles), dtype=np.float
+ map(fr_utils.angles_to_fr, flavour_angles)
)
+ of = outfile[:5]+outfile[5:].replace('data', 'plots')+'_triangle'
plot_utils.triangle_project(
frs = flavour_ratios,
llh = llh,
- outfile = outfile[:5]+outfile[5:].replace('data', 'plots')+'_triangle',
+ outfile = of,
outformat = ['png'],
args = args,
llh_paramset = hypo_paramset,
diff --git a/fig2.py b/fig2.py
new file mode 100755
index 0000000..90aca90
--- /dev/null
+++ b/fig2.py
@@ -0,0 +1,180 @@
+#! /usr/bin/env python
+# author : S. Mandalia
+# s.p.mandalia@qmul.ac.uk
+#
+# date : February 24, 2019
+
+"""
+HESE BSM Flavour Figure 2
+"""
+
+from __future__ import absolute_import, division
+
+import argparse
+from functools import partial
+
+import numpy as np
+
+from utils import fr as fr_utils
+from utils import misc as misc_utils
+from utils import plot as plot_utils
+from utils.enums import str_enum
+from utils.enums import Likelihood, ParamTag, PriorsCateg
+from utils.param import Param, ParamSet
+
+from matplotlib import pyplot as plt
+
+from pymultinest import Analyzer
+
+
+def define_nuisance():
+ """Define the nuisance parameters."""
+ nuisance = []
+ tag = ParamTag.NUISANCE
+ lg_prior = PriorsCateg.LIMITEDGAUSS
+ nuisance.extend([
+ Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.1, 10.], std=0.4, prior=lg_prior, tag=tag),
+ Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 20.], std=2.4, prior=lg_prior, tag=tag),
+ Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 10.], std=0.1, tag=tag),
+ Param(name='astroNorm', value=6.9, seed=[0., 5. ], ranges=[0. , 20.], std=1.5, tag=tag),
+ Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ ])
+ return ParamSet(nuisance)
+
+
+def get_paramsets(args, nuisance_paramset):
+ paramset = []
+ if args.likelihood in [Likelihood.GOLEMFIT, Likelihood.GF_FREQ]:
+ gf_nuisance = [x for x in nuisance_paramset.from_tag(ParamTag.NUISANCE)]
+ paramset.extend(gf_nuisance)
+ tag = ParamTag.BESTFIT
+ paramset.extend([
+ Param(name='astroFlavorAngle1', value=0, ranges=[0., 1.], std=0.2, tag=tag),
+ Param(name='astroFlavorAngle2', value=0, ranges=[-1., 1.], std=0.2, tag=tag),
+ ])
+ paramset = ParamSet(paramset)
+ return paramset
+
+
+def process_args(args):
+ """Process the input args."""
+ if args.likelihood is not Likelihood.GOLEMFIT \
+ and args.likelihood is not Likelihood.GF_FREQ:
+ raise AssertionError(
+ 'Likelihood method {0} not supported for this '
+ 'script!\nChoose either GOLEMFIT or GF_FREQ'.format(
+ str_enum(args.likelihood)
+ )
+ )
+
+def parse_args(args=None):
+ """Parse command line arguments"""
+ parser = argparse.ArgumentParser(
+ description="HESE BSM Flavour Figure 2",
+ formatter_class=misc_utils.SortingHelpFormatter,
+ )
+ parser.add_argument(
+ '--likelihood', default='golemfit',
+ type=partial(misc_utils.enum_parse, c=Likelihood),
+ choices=Likelihood, help='likelihood contour'
+ )
+ parser.add_argument(
+ '--contour-dir', type=str,
+ help='Path to directory containing MultiNest runs'
+ )
+ parser.add_argument(
+ '--outfile', type=str, default='./untitled',
+ help='Output path'
+ )
+ if args is None: return parser.parse_args()
+ else: return parser.parse_args(args.split())
+
+
+def main():
+ args = parse_args()
+ process_args(args)
+ misc_utils.print_args(args)
+
+ paramset = get_paramsets(args, define_nuisance())
+ n_params = len(paramset)
+ print n_params
+
+ # Data
+ data_path = '{0}/{1}/real'.format(
+ args.contour_dir, str_enum(args.likelihood).lower()
+ )
+ prefix = '{0}/_{1}_REAL_mn_'.format(data_path, str_enum(args.likelihood))
+ analyser = Analyzer(
+ outputfiles_basename=prefix, n_params=n_params
+ )
+ print analyser
+
+ pranges = paramset.ranges
+
+ bf = analyser.get_best_fit()['parameters']
+ for i in xrange(len(bf)):
+ bf[i] = (pranges[i][1]-pranges[i][0])*bf[i] + pranges[i][0]
+ print 'bestfit = ', bf
+ print 'bestfit log_likelihood', analyser.get_best_fit()['log_likelihood']
+
+ print
+ print '{0:50} = {1}'.format('global evidence', analyser.get_stats()['global evidence'])
+ print
+
+ chains = analyser.get_data()[:,2:]
+ for x in chains:
+ for i in xrange(len(x)):
+ x[i] = (pranges[i][1]-pranges[i][0])*x[i] + pranges[i][0]
+
+ llh = -0.5 * analyser.get_data()[:,1]
+
+ flavour_angles = chains[:,-2:]
+ flavour_ratios = np.array(
+ map(fr_utils.angles_to_fr, flavour_angles)
+ )
+
+ nbins = 25
+
+ fig = plt.figure(figsize=(8, 8))
+ ax = fig.add_subplot(111)
+ tax = plot_utils.get_tax(ax, scale=nbins)
+
+ plot_utils.flavour_contour(
+ frs = flavour_ratios,
+ ax = ax,
+ nbins = nbins,
+ coverage = 99,
+ linewidth = 2,
+ color = 'green'
+ )
+
+ plot_utils.flavour_contour(
+ frs = flavour_ratios,
+ ax = ax,
+ nbins = nbins,
+ coverage = 90,
+ linewidth = 2,
+ color = 'blue'
+ )
+
+ plot_utils.flavour_contour(
+ frs = flavour_ratios,
+ ax = ax,
+ nbins = nbins,
+ coverage = 68,
+ linewidth = 2,
+ color = 'red'
+ )
+
+ ax.legend()
+
+ fig.savefig('test.png', bbox_inches='tight', dpi=150)
+
+ print "DONE!"
+
+
+main.__doc__ = __doc__
+
+
+if __name__ == '__main__':
+ main()
diff --git a/fig2_austin.py b/fig2_austin.py
new file mode 100755
index 0000000..8a4cc01
--- /dev/null
+++ b/fig2_austin.py
@@ -0,0 +1,162 @@
+#! /usr/bin/env python
+# author : S. Mandalia
+# s.p.mandalia@qmul.ac.uk
+#
+# date : February 24, 2019
+
+"""
+HESE BSM Flavour Figure 2
+"""
+
+from __future__ import absolute_import, division
+
+import argparse
+from functools import partial
+
+import numpy as np
+
+from utils import fr as fr_utils
+from utils import misc as misc_utils
+from utils import plot as plot_utils
+from utils.enums import str_enum
+from utils.enums import Likelihood, ParamTag, PriorsCateg
+from utils.param import Param, ParamSet
+
+from matplotlib import pyplot as plt
+
+# from pymultinest import Analyzer
+import json
+
+
+def define_nuisance():
+ """Define the nuisance parameters."""
+ nuisance = []
+ tag = ParamTag.NUISANCE
+ lg_prior = PriorsCateg.LIMITEDGAUSS
+ nuisance.extend([
+ Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.1, 10.], std=0.4, prior=lg_prior, tag=tag),
+ Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 20.], std=2.4, prior=lg_prior, tag=tag),
+ Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 10.], std=0.1, tag=tag),
+ Param(name='astroNorm', value=6.9, seed=[0., 5. ], ranges=[0. , 20.], std=1.5, tag=tag),
+ Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ ])
+ return ParamSet(nuisance)
+
+
+def get_paramsets(args, nuisance_paramset):
+ paramset = []
+ if args.likelihood in [Likelihood.GOLEMFIT, Likelihood.GF_FREQ]:
+ gf_nuisance = [x for x in nuisance_paramset.from_tag(ParamTag.NUISANCE)]
+ paramset.extend(gf_nuisance)
+ tag = ParamTag.BESTFIT
+ paramset.extend([
+ Param(name='astroFlavorAngle1', value=0, ranges=[0., 1.], std=0.2, tag=tag),
+ Param(name='astroFlavorAngle2', value=0, ranges=[-1., 1.], std=0.2, tag=tag),
+ ])
+ paramset = ParamSet(paramset)
+ return paramset
+
+
+def process_args(args):
+ """Process the input args."""
+ if args.likelihood is not Likelihood.GOLEMFIT \
+ and args.likelihood is not Likelihood.GF_FREQ:
+ raise AssertionError(
+ 'Likelihood method {0} not supported for this '
+ 'script!\nChoose either GOLEMFIT or GF_FREQ'.format(
+ str_enum(args.likelihood)
+ )
+ )
+
+def parse_args(args=None):
+ """Parse command line arguments"""
+ parser = argparse.ArgumentParser(
+ description="HESE BSM Flavour Figure 2",
+ formatter_class=misc_utils.SortingHelpFormatter,
+ )
+ parser.add_argument(
+ '--likelihood', default='golemfit',
+ type=partial(misc_utils.enum_parse, c=Likelihood),
+ choices=Likelihood, help='likelihood contour'
+ )
+ parser.add_argument(
+ '--contour-dir', type=str,
+ help='Path to directory containing MultiNest runs'
+ )
+ parser.add_argument(
+ '--outfile', type=str, default='./untitled',
+ help='Output path'
+ )
+ if args is None: return parser.parse_args()
+ else: return parser.parse_args(args.split())
+
+
+def main():
+ args = parse_args()
+ process_args(args)
+ misc_utils.print_args(args)
+
+ paramset = get_paramsets(args, define_nuisance())
+ n_params = len(paramset)
+ print n_params
+
+ # Data
+ data_path = '/home/aschneider/programs/GOLEMSPACE/sources/GolemFit/scripts/diffuse/mcmcs/results/dpl_numu_prior_flavor_20190302-162221-a747f528-8aa6-4488-8c80-059572c099fe.json'
+ with open(data_path) as f:
+ d_json = json.load(f)
+
+ names = d_json['func_args']
+ chains = np.array(d_json['chain'])
+ print 'names', names
+ print 'chains.shape', chains.shape
+
+ flavour_angles = chains[:,5:7]
+ flavour_ratios = np.array(
+ map(fr_utils.angles_to_fr, flavour_angles)
+ )
+
+ nbins = 25
+
+ fig = plt.figure(figsize=(8, 8))
+ ax = fig.add_subplot(111)
+ tax = plot_utils.get_tax(ax, scale=nbins)
+
+ plot_utils.flavour_contour(
+ frs = flavour_ratios,
+ ax = ax,
+ nbins = nbins,
+ coverage = 99,
+ linewidth = 2,
+ color = 'green'
+ )
+
+ plot_utils.flavour_contour(
+ frs = flavour_ratios,
+ ax = ax,
+ nbins = nbins,
+ coverage = 90,
+ linewidth = 2,
+ color = 'blue'
+ )
+
+ plot_utils.flavour_contour(
+ frs = flavour_ratios,
+ ax = ax,
+ nbins = nbins,
+ coverage = 68,
+ linewidth = 2,
+ color = 'red'
+ )
+
+ ax.legend()
+
+ fig.savefig('test_austin.png', bbox_inches='tight', dpi=150)
+
+ print "DONE!"
+
+
+main.__doc__ = __doc__
+
+
+if __name__ == '__main__':
+ main()
diff --git a/fr.py b/fr.py
index 3c46623..5bc4c82 100755
--- a/fr.py
+++ b/fr.py
@@ -32,7 +32,7 @@ def define_nuisance():
tag = ParamTag.SM_ANGLES
nuisance = []
g_prior = PriorsCateg.GAUSSIAN
- hg_prior = PriorsCateg.HALFGAUSS
+ hg_prior = PriorsCateg.LIMITEDGAUSS
e = 1e-9
nuisance.extend([
Param(name='s_12_2', value=0.307, seed=[0.26, 0.35], ranges=[0., 1.], std=0.013, tex=r's_{12}^2', prior=g_prior, tag=tag),
@@ -62,11 +62,11 @@ def define_nuisance():
])
tag = ParamTag.NUISANCE
nuisance.extend([
- Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0. , 50.], std=0.3, tag=tag),
- Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 50.], std=0.05, tag=tag),
- Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroNorm', value=6.9, seed=[0.1, 10.], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroDeltaGamma', value=2.5, seed=[1. , 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.1, 10.], std=0.4, prior=lg_prior, tag=tag),
+ Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 20.], std=2.4, prior=lg_prior, tag=tag),
+ Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 10.], std=0.1, tag=tag),
+ Param(name='astroNorm', value=6.9, seed=[0., 5. ], ranges=[0. , 20.], std=1.5, tag=tag),
+ Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
])
return ParamSet(nuisance)
diff --git a/plot_sens.py b/plot_sens.py
index 2e3bcd0..b12389f 100755
--- a/plot_sens.py
+++ b/plot_sens.py
@@ -34,13 +34,13 @@ def define_nuisance():
"""Define the nuisance parameters."""
tag = ParamTag.SM_ANGLES
g_prior = PriorsCateg.GAUSSIAN
- hg_prior = PriorsCateg.HALFGAUSS
+ lg_prior = PriorsCateg.LIMITEDGAUSS
e = 1e-9
nuisance = [
- Param(name='s_12_2', value=0.307, seed=[0.26, 0.35], ranges=[0., 1.], std=0.013, tex=r's_{12}^2', prior=g_prior, tag=tag),
- Param(name='c_13_4', value=1-(0.02206)**2, seed=[0.995, 1-e], ranges=[0., 1.], std=0.00147, tex=r'c_{13}^4', prior=hg_prior, tag=tag),
- Param(name='s_23_2', value=0.538, seed=[0.31, 0.75], ranges=[0., 1.], std=0.069, tex=r's_{23}^2', prior=g_prior, tag=tag),
- Param(name='dcp', value=4.08404, seed=[0+e, 2*np.pi-e], ranges=[0., 2*np.pi], std=2.0, tex=r'\delta_{CP}', tag=tag),
+ Param(name='s_12_2', value=0.307, seed=[0.26, 0.35], ranges=[0., 1.], std=0.013, tex=r's_{12}^2', prior=lg_prior, tag=tag),
+ Param(name='c_13_4', value=(1-(0.02206))**2, seed=[0.950, 0.961], ranges=[0., 1.], std=0.00147, tex=r'c_{13}^4', prior=lg_prior, tag=tag),
+ Param(name='s_23_2', value=0.538, seed=[0.31, 0.75], ranges=[0., 1.], std=0.069, tex=r's_{23}^2', prior=lg_prior, tag=tag),
+ Param(name='dcp', value=4.08404, seed=[0+e, 2*np.pi-e], ranges=[0., 2*np.pi], std=2.0, tex=r'\delta_{CP}', tag=tag),
Param(
name='m21_2', value=7.40E-23, seed=[7.2E-23, 7.6E-23], ranges=[6.80E-23, 8.02E-23],
std=2.1E-24, tex=r'\Delta m_{21}^2{\rm GeV}^{-2}', prior=g_prior, tag=tag
@@ -52,11 +52,11 @@ def define_nuisance():
]
tag = ParamTag.NUISANCE
nuisance.extend([
- Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0. , 50.], std=0.3, tag=tag),
- Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 50.], std=0.05, tag=tag),
- Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroNorm', value=6.9, seed=[0.1, 10.], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroDeltaGamma', value=2.5, seed=[1. , 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.1, 10.], std=0.4, prior=lg_prior, tag=tag),
+ Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 20.], std=2.4, prior=lg_prior, tag=tag),
+ Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 10.], std=0.1, tag=tag),
+ Param(name='astroNorm', value=6.9, seed=[0., 5. ], ranges=[0. , 20.], std=1.5, tag=tag),
+ Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
])
return ParamSet(nuisance)
@@ -233,11 +233,13 @@ def main():
infile += '/gaussian/'
if args.likelihood is Likelihood.GAUSSIAN:
infile += '{0}/'.format(str(args.sigma_ratio).replace('.', '_'))
- # infile += '/DIM{0}/fix_ifr/{1}/{2}/{3}/fr_stat'.format(
- infile += '/DIM{0}/fix_ifr/prior/{1}/{2}/{3}/fr_stat'.format(
+ infile += '/DIM{0}/fix_ifr/{1}/{2}/{3}/fr_stat'.format(
+ # infile += '/DIM{0}/fix_ifr/prior/{1}/{2}/{3}/fr_stat'.format(
# infile += '/DIM{0}/fix_ifr/{1}/{2}/{3}/old/fr_stat'.format(
# infile += '/DIM{0}/fix_ifr/seed2/{1}/{2}/{3}/fr_stat'.format(
# infile += '/DIM{0}/fix_ifr/100TeV/{1}/{2}/{3}/fr_stat'.format(
+ # infile += '/DIM{0}/fix_ifr/strictprior/{1}/{2}/{3}/fr_stat'.format(
+ # infile += '/DIM{0}/fix_ifr/noprior/{1}/{2}/{3}/fr_stat'.format(
dim, *map(misc_utils.parse_enum, [args.stat_method, args.run_method, args.data])
) + misc_utils.gen_identifier(argsc)
print '== {0:<25} = {1}'.format('infile', infile)
@@ -283,10 +285,12 @@ def main():
base_infile += '/gaussian/'
if args.likelihood is Likelihood.GAUSSIAN:
base_infile += '{0}/'.format(str(args.sigma_ratio).replace('.', '_'))
- # base_infile += '/DIM{0}/fix_ifr'.format(dim)
- base_infile += '/DIM{0}/fix_ifr/prior'.format(dim)
+ base_infile += '/DIM{0}/fix_ifr'.format(dim)
+ # base_infile += '/DIM{0}/fix_ifr/prior'.format(dim)
# base_infile += '/DIM{0}/fix_ifr/seed2'.format(dim)
# base_infile += '/DIM{0}/fix_ifr/100TeV'.format(dim)
+ # base_infile += '/DIM{0}/fix_ifr/strictprior'.format(dim)
+ # base_infile += '/DIM{0}/fix_ifr/noprior'.format(dim)
for isrc, src in enumerate(args.source_ratios):
argsc.source_ratio = src
diff --git a/sens.py b/sens.py
index 0646e6e..7049b83 100755
--- a/sens.py
+++ b/sens.py
@@ -35,11 +35,12 @@ def define_nuisance():
tag = ParamTag.SM_ANGLES
nuisance = []
g_prior = PriorsCateg.GAUSSIAN
+ lg_prior = PriorsCateg.LIMITEDGAUSS
e = 1e-9
nuisance.extend([
- Param(name='s_12_2', value=0.307, seed=[0.26, 0.35], ranges=[0., 1.], std=0.013, tex=r's_{12}^2', prior=g_prior, tag=tag),
- Param(name='c_13_4', value=(1-(0.02206))**2, seed=[0.950, 0.961], ranges=[0., 1.], std=0.00147, tex=r'c_{13}^4', prior=g_prior, tag=tag),
- Param(name='s_23_2', value=0.538, seed=[0.31, 0.75], ranges=[0., 1.], std=0.069, tex=r's_{23}^2', prior=g_prior, tag=tag),
+ Param(name='s_12_2', value=0.307, seed=[0.26, 0.35], ranges=[0., 1.], std=0.013, tex=r's_{12}^2', prior=lg_prior, tag=tag),
+ Param(name='c_13_4', value=(1-(0.02206))**2, seed=[0.950, 0.961], ranges=[0., 1.], std=0.00147, tex=r'c_{13}^4', prior=lg_prior, tag=tag),
+ Param(name='s_23_2', value=0.538, seed=[0.31, 0.75], ranges=[0., 1.], std=0.069, tex=r's_{23}^2', prior=lg_prior, tag=tag),
Param(name='dcp', value=4.08404, seed=[0+e, 2*np.pi-e], ranges=[0., 2*np.pi], std=2.0, tex=r'\delta_{CP}', tag=tag),
Param(
name='m21_2', value=7.40E-23, seed=[7.2E-23, 7.6E-23], ranges=[6.80E-23, 8.02E-23],
@@ -52,11 +53,21 @@ def define_nuisance():
])
tag = ParamTag.NUISANCE
nuisance.extend([
- Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0. , 50.], std=0.3, tag=tag),
- Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 50.], std=0.05, tag=tag),
- Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroNorm', value=6.9, seed=[0.1, 10.], ranges=[0. , 50.], std=0.1, tag=tag),
- Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.1, 10.], std=0.4, prior=lg_prior, tag=tag),
+ Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 20.], std=2.4, prior=lg_prior, tag=tag),
+ Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 10.], std=0.1, tag=tag),
+ Param(name='astroNorm', value=6.9, seed=[0., 5. ], ranges=[0. , 20.], std=1.5, tag=tag),
+ Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ # Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0. , 50.], std=0.3, tag=tag),
+ # Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 50.], std=0.05, tag=tag),
+ # Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0. , 50.], std=0.1, tag=tag),
+ # Param(name='astroNorm', value=6.9, seed=[0.1, 10.], ranges=[0. , 50.], std=0.1, tag=tag),
+ # Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[-5., 5. ], std=0.1, tag=tag)
+ # Param(name='convNorm', value=1., seed=[0.5, 2. ], ranges=[0.5, 2. ], std=0.3, tag=tag),
+ # Param(name='promptNorm', value=0., seed=[0. , 6. ], ranges=[0. , 6. ], std=0.05, tag=tag),
+ # Param(name='muonNorm', value=1., seed=[0.1, 2. ], ranges=[0.1, 2. ], std=0.1, tag=tag),
+ # Param(name='astroNorm', value=6.9, seed=[0.1, 10.], ranges=[0.1, 10.], std=0.1, tag=tag),
+ # Param(name='astroDeltaGamma', value=2.5, seed=[2.4, 3. ], ranges=[2.4, 3. ], std=0.1, tag=tag)
])
return ParamSet(nuisance)
@@ -299,6 +310,7 @@ def main():
continue
print '## Evidence = {0}'.format(stat)
elif args.stat_method is StatCateg.FREQUENTIST:
+ raise NotImplementedError('Still needs testing')
def fn(x):
# TODO(shivesh): should be seed or ranges?
# Force prior ranges to be inside "seed"
diff --git a/submitter/sens_dag.py b/submitter/sens_dag.py
index bfab194..7423d34 100644
--- a/submitter/sens_dag.py
+++ b/submitter/sens_dag.py
@@ -35,17 +35,18 @@ GLOBAL_PARAMS.update(dict(
# MultiNest
GLOBAL_PARAMS.update(dict(
- # mn_live_points = 1000,
- mn_live_points = 500,
+ mn_live_points = 1000,
+ # mn_live_points = 500,
+ # mn_live_points = 300,
# mn_tolerance = 0.1,
mn_tolerance = 0.3,
mn_output = './mnrun'
))
# FR
-dimension = [6]
+# dimension = [6]
# dimension = [3, 6]
-# dimension = [3, 4, 5, 6, 7, 8]
+dimension = [3, 4, 5, 6, 7, 8]
GLOBAL_PARAMS.update(dict(
threads = 1,
binning = '6e4 1e7 20',
@@ -84,7 +85,9 @@ outfile = 'dagman_FR_SENS_{0}_{1}_{2}_{3}'.format(
# outfile += '_seed2'
# outfile += '_tol03'
# outfile += '_NULL'
-outfile += '_prior'
+# outfile += '_prior'
+# outfile += '_strictprior'
+# outfile += '_noprior'
outfile += '.submit'
golemfitsourcepath = os.environ['GOLEMSOURCEPATH'] + '/GolemFit'
condor_script = golemfitsourcepath + '/scripts/flavour_ratio/submitter/sens_submit.sub'
@@ -112,7 +115,9 @@ with open(outfile, 'w') as f:
# output += 'seed2/'
# output += 'mn_noverlap/'
# output += 'tol_03/'
- output += 'prior/'
+ # output += 'prior/'
+ # output += 'strictprior/'
+ # output += 'noprior/'
for r in xrange(sens_runs):
print 'run', r
f.write('JOB\tjob{0}\t{1}\n'.format(job_number, condor_script))
diff --git a/utils/enums.py b/utils/enums.py
index 22f91b8..a7982f8 100644
--- a/utils/enums.py
+++ b/utils/enums.py
@@ -50,9 +50,9 @@ class ParamTag(Enum):
class PriorsCateg(Enum):
- UNIFORM = 1
- GAUSSIAN = 2
- HALFGAUSS = 3
+ UNIFORM = 1
+ GAUSSIAN = 2
+ LIMITEDGAUSS = 3
class MCMCSeedType(Enum):
diff --git a/utils/fr.py b/utils/fr.py
index 528557a..1d12fe5 100644
--- a/utils/fr.py
+++ b/utils/fr.py
@@ -97,9 +97,9 @@ def angles_to_fr(src_angles):
spsi2 = SIN(psi)**2
cspi2 = 1. - spsi2
- x = sphi2*cspi2
- y = sphi2*spsi2
- z = cphi2
+ x = float(abs(sphi2*cspi2))
+ y = float(abs(sphi2*spsi2))
+ z = float(abs(cphi2))
return x, y, z
diff --git a/utils/gf.py b/utils/gf.py
index 1998484..2c794d3 100644
--- a/utils/gf.py
+++ b/utils/gf.py
@@ -85,6 +85,11 @@ def steering_params(args):
params.maxFitEnergy = 1E7 # GeV
params.load_data_from_text_file = False
+ params.sampleToLoad = gf.sampleTag.MagicTau
+ params.use_legacy_selfveto_calculation = False
+ params.spline_hole_ice = False
+ params.spline_dom_efficiency = False
+
return params
diff --git a/utils/llh.py b/utils/llh.py
index f4404c4..0c1e97d 100644
--- a/utils/llh.py
+++ b/utils/llh.py
@@ -12,7 +12,8 @@ from __future__ import absolute_import, division
from functools import partial
import numpy as np
-from scipy.stats import multivariate_normal, rv_continuous
+import scipy
+from scipy.stats import multivariate_normal, truncnorm
from utils import fr as fr_utils
from utils import gf as gf_utils
@@ -20,10 +21,11 @@ from utils.enums import EnergyDependance, Likelihood, ParamTag, PriorsCateg
from utils.misc import enum_parse, gen_identifier, parse_bool
-class Gaussian(rv_continuous):
- """Gaussian for one dimension."""
- def _pdf(self, x, mu, sig):
- return (1./np.sqrt(2*np.pi*sig**2))*np.exp(-((x-mu)**2)/(2*sig**2))
+def GaussianBoundedRV(loc=0., sigma=1., lower=-np.inf, upper=np.inf):
+ """Normalised gaussian bounded between lower and upper values"""
+ low, up = (lower - loc) / sigma, (upper - loc) / sigma
+ g = scipy.stats.truncnorm(loc=loc, scale=sigma, a=low, b=up)
+ return g
def multi_gaussian(fr, fr_bf, sigma, offset=-320):
@@ -69,13 +71,14 @@ def lnprior(theta, paramset):
prior = 0
for param in paramset:
if param.prior is PriorsCateg.GAUSSIAN:
- prior += Gaussian().logpdf(
- param.nominal_value, param.value, param.std
- )
- elif param.prior is PriorsCateg.HALFGAUSS:
- prior += Gaussian().logpdf(
- param.nominal_value, param.value, param.std
- ) + Gaussian().logcdf(1, param.value, param.std)
+ prior += GaussianBoundedRV(
+ loc=param.nominal_value, sigma=param.std
+ ).logpdf(param.value)
+ elif param.prior is PriorsCateg.LIMITEDGAUSS:
+ prior += GaussianBoundedRV(
+ loc=param.nominal_value, sigma=param.std,
+ lower=param.ranges[0], upper=param.ranges[1]
+ ).logpdf(param.value)
return prior
diff --git a/utils/plot.py b/utils/plot.py
index 0529d5d..91b8b4e 100644
--- a/utils/plot.py
+++ b/utils/plot.py
@@ -15,7 +15,8 @@ from copy import deepcopy
import numpy as np
import numpy.ma as ma
-from scipy import interpolate
+from scipy.interpolate import splev, splprep
+from scipy.ndimage.filters import gaussian_filter
import matplotlib as mpl
mpl.use('Agg')
@@ -28,13 +29,18 @@ from matplotlib.lines import Line2D
from matplotlib.patches import Patch
from getdist import plots, mcsamples
+
import ternary
+from ternary.heatmapping import polygon_generator
+
+import shapely.geometry as geometry
from utils import misc as misc_utils
from utils.enums import DataType, EnergyDependance
from utils.enums import Likelihood, MixingScenario, ParamTag, StatCateg
from utils.fr import angles_to_u, angles_to_fr
+
if os.path.isfile('./plot_llh/paper.mplstyle'):
plt.style.use('./plot_llh/paper.mplstyle')
elif os.environ.get('GOLEMSOURCEPATH') is not None:
@@ -287,12 +293,13 @@ def plot_statistic(data, outfile, outformat, args, scale_param, label=None):
print 'data', data
print 'data.shape', data.shape
scales, statistic = ma.compress_rows(data).T
- scales_rm = scales[1:]
- statistic_rm = statistic[1:]
- tck, u = interpolate.splprep([scales_rm, statistic_rm], s=0)
- scales_rm, statistic_rm = interpolate.splev(np.linspace(0, 1, 1000), tck)
- print 'scales_rm', scales_rm
- print 'statistic_rm', statistic_rm
+ try:
+ tck, u = splprep([scales, statistic], s=0)
+ except:
+ return
+ sc, st = splev(np.linspace(0, 1, 10000), tck)
+ scales_rm = sc[sc >= scales[1]]
+ statistic_rm = st[sc >= scales[1]]
min_idx = np.argmin(scales)
null = statistic[min_idx]
@@ -314,6 +321,10 @@ def plot_statistic(data, outfile, outformat, args, scale_param, label=None):
elif args.stat_method is StatCateg.FREQUENTIST:
ax.set_ylabel(r'$-2\Delta {\rm LLH}$')
+ # ymin = np.round(np.min(reduced_ev) - 1.5)
+ # ymax = np.round(np.max(reduced_ev) + 1.5)
+ # ax.set_ylim((ymin, ymax))
+
ax.plot(scales_rm, reduced_ev)
ax.axhline(y=np.log(10**(3/2.)), color='red', alpha=1., linewidth=1.3)
@@ -565,10 +576,14 @@ def plot_sens_fixed_angle_pretty(data, outfile, outformat, args):
ax.axvline(x=ps, color='purple', alpha=1., linewidth=1.5)
scales, statistic = ma.compress_rows(data[idim][isrc][ian]).T
- scales_rm = scales[1:]
- statistic_rm = statistic[1:]
- tck, u = interpolate.splprep([scales_rm, statistic_rm], s=0)
- scales_rm, statistic_rm = interpolate.splev(np.linspace(0, 1, 1000), tck)
+ try:
+ tck, u = splprep([scales, statistic], s=0)
+ except:
+ return
+ sc, st = splev(np.linspace(0, 1, 10000), tck)
+ scales_rm = sc[sc >= scales[1]]
+ statistic_rm = st[sc >= scales[1]]
+
min_idx = np.argmin(scales)
null = statistic[min_idx]
if args.stat_method is StatCateg.BAYESIAN:
@@ -708,8 +723,8 @@ def plot_sens_fixed_angle(data, outfile, outformat, args):
for ian in xrange(len(data[idim][isrc])):
print '=== an', ian
scales, statistic = data[idim][isrc][ian].T
- tck, u = interpolate.splprep([scales, statistic], s=0)
- scales, statistic = interpolate.splev(np.linspace(0, 1, 1000), tck)
+ tck, u = splprep([scales, statistic], s=0)
+ scales, statistic = splev(np.linspace(0, 1, 1000), tck)
min_idx = np.argmin(scales)
null = statistic[min_idx]
if args.stat_method is StatCateg.BAYESIAN:
@@ -861,7 +876,7 @@ def plot_sens_corr_angle(data, outfile, outformat, args):
print uni, c
print len(uni)
print np.unique(c)
-
+
n = len(uni)
assert len(np.unique(c)) == 1
c = c[0]
@@ -872,7 +887,7 @@ def plot_sens_corr_angle(data, outfile, outformat, args):
sep_arrays = []
for x_i in xrange(n):
sep_arrays.append(col_array[:,x_i])
-
+
print len(sep_arrays)
sep_arrays = sep_arrays[0][:3]
print sep_arrays
@@ -891,7 +906,7 @@ def plot_sens_corr_angle(data, outfile, outformat, args):
ax.set_xlim((mini, maxi))
ax.set_ylim(0, 1)
ax.grid(b=False)
-
+
x_v = reduced_pdat[0].round(decimals=4)
y_v = reduced_pdat[1].round(decimals=4)
uniques = np.unique(x_v)
@@ -924,8 +939,8 @@ def plot_sens_corr_angle(data, outfile, outformat, args):
print 'p_x', p_x
print 'p_y', p_y
try:
- tck, u = interpolate.splprep([p_x, p_y], s=1e-5, per=True)
- xi, yi = interpolate.splev(np.linspace(0, 1, 1000), tck)
+ tck, u = splprep([p_x, p_y], s=1e-5, per=True)
+ xi, yi = splev(np.linspace(0, 1, 1000), tck)
except:
xi, yi = p_x, p_y
ax.fill(xi, yi, 'r', edgecolor='r', linewidth=1)
@@ -953,6 +968,36 @@ def cmap_discretize(cmap, N):
return mpl.colors.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)
+def get_tax(ax, scale):
+ ax.set_aspect('equal')
+
+ # Boundary and Gridlines
+ fig, tax = ternary.figure(ax=ax, scale=scale)
+
+ # Draw Boundary and Gridlines
+ tax.boundary(linewidth=2.0)
+ tax.gridlines(color='grey', multiple=scale/5., linewidth=1.0, alpha=0.4, ls='--')
+ tax.gridlines(color='grey', multiple=scale/10., linewidth=0.5, alpha=0.4, ls=':')
+
+ # Set Axis labels and Title
+ fontsize = 23
+ tax.left_axis_label(r"$f_{\tau}$", fontsize=fontsize+8, offset=0.2, rotation=0)
+ tax.right_axis_label(r"$f_{\mu}$", fontsize=fontsize+8, offset=0.2, rotation=0)
+ tax.bottom_axis_label(r"$f_{e}$", fontsize=fontsize+8, position=(0.55, -0.20/2, 0.5), rotation=0)
+
+ # Remove default Matplotlib axis
+ tax.get_axes().axis('off')
+ tax.clear_matplotlib_ticks()
+
+ # Set ticks
+ ticks = np.linspace(0, 1, 6)
+ tax.ticks(ticks=ticks, locations=ticks*scale, axis='blr', linewidth=1,
+ offset=0.03, fontsize=fontsize, tick_formats='%.1f')
+
+ tax._redraw_labels()
+
+ return tax
+
def triangle_project(frs, llh, outfile, outformat, args, llh_paramset, fig_text):
print 'Making triangle projection'
fontsize = 23
@@ -976,8 +1021,8 @@ def triangle_project(frs, llh, outfile, outformat, args, llh_paramset, fig_text)
mean = np.mean(llh)
sig = np.std(llh)
- min_scale = np.min(llh)
- max_scale = np.max(mean+sig)
+ max_scale = np.max(llh)
+ min_scale = np.min(mean-sig)
norm = mpl.colors.Normalize(vmin=min_scale, vmax=max_scale)
colour = map(alp, map(cmap, map(norm, llh)))
@@ -990,26 +1035,7 @@ def triangle_project(frs, llh, outfile, outformat, args, llh_paramset, fig_text)
gs.update(hspace=0.3, wspace=0.15)
ax = fig.add_subplot(gs[0])
- ax.set_aspect('equal')
-
- # Boundary and Gridlines
- scale = 1
- fig, tax = ternary.figure(ax=ax, scale=scale)
-
- # Draw Boundary and Gridlines
- tax.boundary(linewidth=2.0)
- tax.gridlines(color='grey', multiple=scale/5., linewidth=1.0, alpha=0.4, ls='--')
- tax.gridlines(color='grey', multiple=scale/10., linewidth=0.5, alpha=0.4, ls=':')
-
- # Set Axis labels and Title
- fontsize = 23
- tax.left_axis_label(r"$f_{\tau}$", fontsize=fontsize+8, offset=0.2, rotation=0)
- tax.right_axis_label(r"$f_{\mu}$", fontsize=fontsize+8, offset=0.2, rotation=0)
- tax.bottom_axis_label(r"$f_{e}$", fontsize=fontsize+8, position=(0.55, -0.20/2, 0.5), rotation=0)
-
- # Remove default Matplotlib axis
- tax.get_axes().axis('off')
- tax.clear_matplotlib_ticks()
+ tax = get_tax(ax, scale=1)
# Plot
tax.scatter(frs, marker='o', s=0.1, color=colour)
@@ -1034,13 +1060,92 @@ def triangle_project(frs, llh, outfile, outformat, args, llh_paramset, fig_text)
cb.set_label(r'$LLH$', fontsize=fontsize+5, labelpad=20,
horizontalalignment='center')
- # Set ticks
- tax.ticks(axis='blr', multiple=scale/5., linewidth=1, offset=0.03,
- fontsize=fontsize, tick_formats='%.1f')
-
- tax._redraw_labels()
-
misc_utils.make_dir(outfile)
for of in outformat:
print 'Saving plot as {0}'.format(outfile+'.'+of)
fig.savefig(outfile+'.'+of, bbox_inches='tight', dpi=150)
+
+
+def heatmap(data, scale, vmin=None, vmax=None, style='triangular'):
+ for k, v in data.items():
+ data[k] = np.array(v)
+ style = style.lower()[0]
+ if style not in ["t", "h", 'd']:
+ raise ValueError("Heatmap style must be 'triangular', 'dual-triangular', or 'hexagonal'")
+
+ vertices_values = polygon_generator(data, scale, style)
+
+ vertices = []
+ for v, value in vertices_values:
+ vertices.append(v)
+ return vertices
+
+
+def flavour_contour(frs, ax, nbins, coverage, linewidth=2, color='black'):
+ """Plot the flavour contour for a specified coverage."""
+ # Histogram in flavour space
+ H, b = np.histogramdd(
+ (frs[:,0], frs[:,1], frs[:,2]),
+ bins=(nbins+1, nbins+1, nbins+1), range=((0, 1), (0, 1), (0, 1))
+ )
+ H = H / np.sum(H)
+
+ # 3D smoothing
+ smoothing = 0.05
+ H_s = gaussian_filter(H, sigma=smoothing)
+
+ # Finding coverage
+ H_r = np.ravel(H_s)
+ H_rs = np.argsort(H_r)[::-1]
+ H_crs = np.cumsum(H_r[H_rs])
+ thres = np.searchsorted(H_crs, coverage/100.)
+ mask_r = np.zeros(H_r.shape)
+ mask_r[H_rs[:thres]] = 1
+ mask = mask_r.reshape(H_s.shape)
+
+ # Get vertices inside covered region
+ binx = np.linspace(0, 1, nbins+1)
+ interp_dict = {}
+ for i in xrange(len(binx)):
+ for j in xrange(len(binx)):
+ for k in xrange(len(binx)):
+ if mask[i][j][k] == 1:
+ interp_dict[(i, j, k)] = H_s[i, j, k]
+ vertices = np.array(heatmap(interp_dict, nbins))
+ points = vertices.reshape((len(vertices)*3, 2))
+
+ # Convex full to find points forming exterior bound
+ pc = geometry.MultiPoint(points)
+ polygon = pc.convex_hull
+ ex_cor = np.array(list(polygon.exterior.coords))
+
+ # Join points with a spline
+ tck, u = splprep([ex_cor.T[0], ex_cor.T[1]], s=0., per=1, k=1)
+ xi, yi = map(np.array, splev(np.linspace(0, 1, 300), tck))
+
+ # Spline again to smooth
+ tck, u = splprep([xi, yi], s=0.4, per=1, k=3)
+ xi, yi = map(np.array, splev(np.linspace(0, 1, 300), tck))
+ ev_polygon = np.dstack((xi, yi))[0]
+
+ def project_toflavour(p):
+ """Convert from cartesian to flavour space."""
+ x, y = p
+ b = y / (np.sqrt(3)/2.)
+ a = x - b/2.
+ return [a, b, nbins-a-b]
+
+ # Remove points interpolated outside flavour triangle
+ f_ev_polygon = np.array(map(project_toflavour, ev_polygon))
+ xf, yf, zf = f_ev_polygon.T
+ mask = np.array((xf < 0) | (yf < 0) | (zf < 0) | (xf > nbins) |
+ (yf > nbins) | (zf > nbins))
+ ev_polygon = np.dstack((xi[~mask], yi[~mask]))[0]
+
+ # Plot
+ ax.plot(
+ ev_polygon.T[0], ev_polygon.T[1], linewidth=linewidth, color=color,
+ zorder=2, alpha=0.6, label=r'{0}\%'.format(int(coverage))
+ )
+ ax.scatter(points.T[0], points.T[1], marker='o', s=2, alpha=1, color=color,
+ zorder=3)