file src/RightHandedNeutrinos.cpp

[No description available] More…

Namespaces

Name
Gambit
TODO: see if we can use this one:
Gambit::NeutrinoBit
Eigen

Detailed Description

Author:

Date:

  • 2017 February
  • 2017 Oct
  • 2018
  • 2019
  • 2020
  • 2018
  • 2018 May

Module function definitions for NeutrinoBit exclusive for right-handed neutrinos


Authors


Source code

//   GAMBIT: Global and Modular BSM Inference Tool
//   *********************************************
///  \file
///
///  Module function definitions for NeutrinoBit 
///  exclusive for right-handed neutrinos
///
///  *********************************************
/// 
///  Authors
/// 
///  \author Suraj Krishnamurthy
///          (S.Krishnamurthy@uva.nl) 
///  \date 2017 February
///
///  \author Tomas Gonzalo
///          (t.e.gonzalo@fys.uio.no)
///  \date 2017 Oct
///  \date 2018
///  \date 2019
///  \date 2020
///
///  \author Marcin Chrzaszcz
///          (mchrzasz@cern.ch)
///  \date 2018
///
///  \author Julia Harz
///          (jharz@lpthe.jussieu.fr)
///  \date 2018 May
///
///  *********************************************

#include <cmath>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <sstream>

#include "gambit/Elements/gambit_module_headers.hpp"
#include "gambit/Utils/numerical_constants.hpp"
#include "gambit/Utils/integration.hpp"
#include <unsupported/Eigen/MatrixFunctions>
#include "gambit/Utils/statistics.hpp"
#include "gambit/NeutrinoBit/NeutrinoBit_rollcall.hpp"
#include "gambit/NeutrinoBit/NeutrinoInterpolator.hpp"

//#define NEUTRINOBIT_DEBUG

using namespace Eigen;

namespace Gambit
{
  namespace NeutrinoBit
  {
    // Lambda function 
    double lambda(double a, double b, double c)
    {
      return a*a + b*b + c*c - 2*a*b - 2*b*c - 2*c*a;
    }

    // Decay with of RHNs to charged lepton and pseudoscalar meson
    // From 0705.1729, 0901.3589, 1805.08567 and 1905.00284
    double Gamma_RHN2Pplusl(double GF, double mN, double ml, double mP, double fP, double VCKM, double Usq)
    {
      double xl = pow(ml/mN,2);
      double xP = pow(mP/mN,2);

      return ( GF*GF * fP*fP * VCKM*VCKM * Usq * mN*mN*mN ) / (16*pi) * ( pow(1 - xl,2) - xP*(1 + xl) )*sqrt(lambda(1,xP,xl));
    }

    // Decay widths of RHNs, for BBN (N -> pi+ l-)
    void Gamma_RHN2piplusl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2piplusl;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_pi_plus = meson_masses.pi_plus;
      static const double f_pi_plus = meson_decay_constants.pi_plus;
      // Take from the model parameters (Wolfenstein) PDG value: 0.97434
      double Vud = 1.0 - 0.5*pow(*Param["CKM_lambda"],2);
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_pi_plus+m_lep[j]))
          {
            gamma[i] +=  Gamma_RHN2Pplusl(sminputs.GF, M[i], m_lep[j], m_pi_plus, f_pi_plus, Vud, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> K+ l-)
    void Gamma_RHN2Kplusl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Kplusl;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_K_plus = meson_masses.kaon_plus;
      static const double f_K_plus = meson_decay_constants.K_plus;
      // Take from the model parameters (Wolfenstein) PDG value: 0.22506
      double Vus = *Param["CKM_lambda"];
      std::vector<double> m_lep(3), gamma(3), M(3);
       // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_K_plus+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Pplusl(sminputs.GF, M[i], m_lep[j], m_K_plus, f_K_plus, Vus, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> D+ l-)
    void Gamma_RHN2Dplusl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Dplusl;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_D_plus = meson_masses.D_plus;
      static const double f_D_plus = meson_decay_constants.D_plus;
      // Take from the model parameters (Wolfenstein) PDG value: 0.22492
      double Vcd = -*Param["CKM_lambda"];
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_D_plus+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Pplusl(sminputs.GF, M[i], m_lep[j], m_D_plus, f_D_plus, Vcd, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> Ds+ l-)
    void Gamma_RHN2Dsl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Dsl;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_D_s = meson_masses.D_s;
      static const double f_D_s = meson_decay_constants.D_s;
      // Take from the model parameters (Wolfenstein) PDG value: 0.97351
      double Vcs = 1 - 0.5*pow(*Param["CKM_lambda"],2);
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_D_s+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Pplusl(sminputs.GF, M[i], m_lep[j], m_D_s, f_D_s, Vcs, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> B+ l-)
    void Gamma_RHN2Bplusl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Bplusl;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_B_plus = meson_masses.B_plus;
      static const double f_B_plus = meson_decay_constants.B_plus;
      // Take from the model parameters (Wolfenstein) PDG value: 0.00357 (absolute value)
      double Vub = *Param["CKM_A"]*pow(*Param["CKM_lambda"],3)*sqrt(pow(*Param["CKM_rhobar"],2) + pow(*Param["CKM_etabar"],2));
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_B_plus+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Pplusl(sminputs.GF, M[i], m_lep[j], m_B_plus, f_B_plus, Vub, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> Bc+ l-)
    void Gamma_RHN2Bcl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Bcl;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_B_c = meson_masses.B_c;
      static const double f_B_c = meson_decay_constants.B_c;
      // Take from the model parameters (Wolfenstein) PDG value: 0.0411
      double Vcb = *Param["CKM_A"]*pow(*Param["CKM_lambda"],2);
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_B_c+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Pplusl(sminputs.GF, M[i], m_lep[j], m_B_c, f_B_c, Vcb, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay with of RHNs to neutrino and neutral pseudoscalar meson
    // From 0705.1729, 1805.08567 and 1905.00284 (mind that the latter uses Dirac neutrinos)
    double Gamma_RHN2P0nu(double GF, double mN, double mP, double fP, double Usq)
    {
      double xP = pow(mP/mN,2);

      return ( pow(GF,2) * pow(fP,2) * Usq * pow(mN,3) ) / (32*pi) * pow(1-xP,2);
    }

    // Decay widths of RHNs, for BBN (N -> pi0 nu)
    void Gamma_RHN2pi0nu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2pi0nu;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_pi_0 = meson_masses.pi0;
      static const double f_pi_0 = meson_decay_constants.pi0;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_pi_0)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2P0nu(sminputs.GF, M[i], m_pi_0, f_pi_0, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> eta nu)
    void Gamma_RHN2etanu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2etanu;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_eta = meson_masses.eta;
      static const double f_eta = meson_decay_constants.eta;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_eta)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2P0nu(sminputs.GF, M[i], m_eta, f_eta, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> eta' nu)
    void Gamma_RHN2etaprimenu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2etaprimenu;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_eta_prime = meson_masses.eta_prime;
      static const double f_eta_prime = meson_decay_constants.eta_prime;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_eta_prime)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2P0nu(sminputs.GF, M[i], m_eta_prime, f_eta_prime, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> etac nu)
    void Gamma_RHN2etacnu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2etacnu;
      SMInputs sminputs = *Dep::SMINPUTS;

      static const double m_eta_c = meson_masses.eta_c;
      static const double f_eta_c = meson_decay_constants.eta_c;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_eta_c)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2P0nu(sminputs.GF, M[i], m_eta_c, f_eta_c, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay with of RHNs to charged lepton and vector meson
    // From 1805.08567 and 1905.00284
    double Gamma_RHN2Vplusl(double GF, double mN, double ml, double mV, double fV, double VCKM, double Usq)
    {
      double xl = pow(ml/mN,2);
      double xV = pow(mV/mN,2);

      return ( pow(GF,2) * pow(fV,2) * pow(VCKM,2) * Usq * pow(mN,3) ) / (16*pi) * ( pow(1-xl,2) + xV*(1+xl) -2*pow(xV,2) ) * sqrt(lambda(1,xV,xl));
    }

    // Decay widths of RHNs, for BBN (N -> rho+ l-)
    void Gamma_RHN2rhoplusl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2rhoplusl;
      SMInputs sminputs = *Dep::SMINPUTS;
 
      static const double m_rho_plus = meson_masses.rho_plus;
      static const double f_rho_plus = meson_decay_constants.rho_plus;
      // Take from the model parameters (Wolfenstein) PDG value: 0.97434
      double Vud = 1.0 - 0.5*pow(*Param["CKM_lambda"],2);
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_rho_plus+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Vplusl(sminputs.GF, M[i], m_lep[j], m_rho_plus, f_rho_plus, Vud, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHN, for BBN (N -> D*+ l-)
    void Gamma_RHN2Dstarplusl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Dstarplusl;
      SMInputs sminputs = *Dep::SMINPUTS;
 
      static const double m_Dstar_plus = meson_masses.Dstar_plus;
      static const double f_Dstar_plus = meson_decay_constants.Dstar_plus;
      // Take from the model parameters (Wolfenstein) PDG value: 0.22492
      double Vcd = -*Param["CKM_lambda"];
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_Dstar_plus+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Vplusl(sminputs.GF, M[i], m_lep[j], m_Dstar_plus, f_Dstar_plus, Vcd, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHN, for BBN (N -> Ds*+ l-)
    void Gamma_RHN2Dstarsl(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Dstarsl;
      SMInputs sminputs = *Dep::SMINPUTS;
 
      static const double m_Dstar_s = meson_masses.Dstar_s;
      static const double f_Dstar_s = meson_decay_constants.Dstar_s;
      // Take from the model parameters (Wolfenstein) PDG value: 0.97351
      double Vcs = 1 - 0.5*pow(*Param["CKM_lambda"],2);
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          if (M[i] > (m_Dstar_s+m_lep[j]))
          {
            gamma[i] += Gamma_RHN2Vplusl(sminputs.GF, M[i], m_lep[j], m_Dstar_s, f_Dstar_s, Vcs, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay with of RHNs to neutrino and neutral vector meson
    // From 1805.08567 and 1905.00284 (mind that the latter uses Dirac neutrinos)
    double Gamma_RHN2V0nu(double GF, double mN, double mV, double fV, double kappaV, double Usq)
    {
      double xV = pow(mV/mN,2);

      return ( pow(GF,2) * pow(fV,2) * pow(kappaV,2) * Usq * pow(mN,3) ) / (32*pi) * (1 + 2*xV) * pow(1 - xV,2);
    }

    // Decay widths of RHNs, for BBN (N -> rho0 nu)
    void Gamma_RHN2rho0nu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2rho0nu;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      static const double m_rho0 = meson_masses.rho0;
      static const double f_rho0 = meson_decay_constants.rho0;
      // kappa value from 1805.08567, it differs from that in 1905.00284
      double kappa_rho0 = 1 - 2*sinW2_eff;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_rho0)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2V0nu(sminputs.GF, M[i], m_rho0, f_rho0, kappa_rho0, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> omega nu)
    void Gamma_RHN2omeganu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2omeganu;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      static const double m_omega = meson_masses.omega;
      static const double f_omega = meson_decay_constants.omega;
      // kappa value from 1805.08567 and 1905.00284
      double kappa_omega = (4./3.)*sinW2_eff;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_omega)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2V0nu(sminputs.GF, M[i], m_omega, f_omega, kappa_omega, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> phi nu)
    void Gamma_RHN2phinu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2phinu;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      static const double m_phi = meson_masses.phi;
      static const double f_phi = meson_decay_constants.phi;
      // kappa value from 1805.08567 and 1905.00284
      double kappa_phi = (4./3.)*sinW2_eff - 1;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_phi)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2V0nu(sminputs.GF, M[i], m_phi, f_phi, kappa_phi, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> J/psi nu)
    void Gamma_RHN2Jpsinu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2Jpsinu;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      static const double m_Jpsi = meson_masses.Jpsi;
      static const double f_Jpsi = meson_decay_constants.Jpsi;
      // kappa value from 1805.08567
      double kappa_Jpsi = 1 - (8./3.)*sinW2_eff;
      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        if (M[i] > m_Jpsi)
        {
          for (int j=0; j<3; j++)
          {
            gamma[i] += Gamma_RHN2V0nu(sminputs.GF, M[i], m_Jpsi, f_Jpsi, kappa_Jpsi, Usq(j,i));
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> nu nu nu)
    // Formula is from [arXiv:0705.1729]
    void Gamma_RHN23nu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN23nu;
      SMInputs sminputs = *Dep::SMINPUTS;

      std::vector<double> gamma(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = ( (pow(sminputs.GF,2) * pow(M[i],5)) / (192*pow(pi,3)) ) * (Usq(0,i)+Usq(1,i)+Usq(2,i));
      }
      result = gamma;
    }

    // Helper function; formula is in [arXiv:1208.4607v2]
    double S(double xa, double xb)
    {
      return sqrt((1-pow((xa+xb),2))*(1-pow((xa-xb),2)));
    }

    // Also helper function; formula is in [arXiv:1208.4607v2]
    double g(double xa, double xb)
    {
     return (1 - (7*pow(xa,2)) - (7*pow(xb,2)) - (7*pow(xa,4)) - (7*pow(xb,4)) + (12*pow(xa,2)*pow(xb,2)) - (7*pow(xa,2)*pow(xb,4)) - (7*pow(xa,4)*pow(xb,2)) + pow(xa,6) + pow(xb,6));
    }

    // Decay widths of RHNs, for BBN (N -> l+ l- nu)
    // Formula is from [arXiv:1208.4607v2]
    void Gamma_RHN2llnu(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2llnu;
      SMInputs sminputs = *Dep::SMINPUTS;

      double x_a, x_b;
      std::vector<double> m_lep(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          for (int k=0; k<3; k++)
          {
            if ( (j != k) and (M[i] > (m_lep[j]+m_lep[k])) )
            {
              x_a = m_lep[j]/M[i];
              x_b = m_lep[k]/M[i];
              gamma[i] += ( (pow(sminputs.GF,2)*pow(M[i],5)) / (192*pow(pi,3)) ) * Usq(j,i) * (S(x_a,x_b)*g(x_a,x_b) - (x_a < 1E-2 ? -12*pow(x_a,4) : 12*pow(x_a,4)*log( (1 - (S(x_a,x_b)*(1+pow(x_a,2)-pow(x_b,2))) - (2*pow(x_b,2)) + pow((pow(x_a,2)-pow(x_b,2)),2)) / (2*pow(x_a,2)) ) ) - (x_b < 1E-2 ? -12*pow(x_b,4) : 12*pow(x_b,4)*log( (1 - (S(x_a,x_b)*(1-pow(x_a,2)+pow(x_b,2))) - (2*pow(x_a,2)) + pow((pow(x_a,2)-pow(x_b,2)),2)) / (2*pow(x_b,2)) ) ) + (x_a < 1E-2 or x_b < 1E-2 ? -12*pow(x_a,4)*pow(x_b,4) : 12*pow(x_a,4)*pow(x_b,4)*log( (1 - (S(x_a,x_b)*(1-pow(x_a,2)-pow(x_b,2))) - (2*pow(x_a,2)) - (2*pow(x_b,2)) + pow(x_a,4) + pow(x_b,4)) / (2*pow(x_a,2)*pow(x_b,2)) ) ) );
            }
          }
        }
      }
      result = gamma;
    }

    // Helper function; formula is in [arXiv:0705.1729]
    // This function varies wrt the paper to include the x^4 factor up front and a cutoff for small x
    double L(double x)
    {
      if(x < 1E-2)
        return -pow(x,4);
      return pow(x,4)*log((1-(3*pow(x,2.0))-((1-pow(x,2.0))*sqrt(1 - (4*pow(x,2.0))))) / (pow(x,2.0)*(1+sqrt(1 - (4*pow(x,2.0))))) );
    }

    // Decay widths of RHNs, for BBN (N -> nu l+ l-)
    // Formula is from [arXiv:0705.1729]
    void Gamma_RHN2null(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2null;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      double C1 = 0.25*(1 - (4*sinW2_eff) + (8*pow(sinW2_eff,2)));
      double C2 = 0.5*sinW2_eff*((2*sinW2_eff) - 1);
      double C3 = 0.25*(1 + (4*sinW2_eff) + (8*pow(sinW2_eff,2)));
      double C4 = 0.5*sinW2_eff*((2*sinW2_eff) + 1);
      std::vector<double> m_lep(3), gamma(3), M(3);
       // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          for (int k=0; k<3; k++)
          {
            if (M[i] > (2*m_lep[k]))
            {
              double x_l = m_lep[k]/M[i];
              if (j == k)
              {
                gamma[i] += ( (pow(sminputs.GF,2)*pow(M[i],5)) / (192*pow(pi,3)) ) * Usq(j,i) * ( (C3*(((1 - (14*pow(x_l,2)) - (2*pow(x_l,4)) - (12*pow(x_l,6)))*sqrt(1 - (4*pow(x_l,2)))) + (12*(pow(x_l,4)-1)*L(x_l)))) + 4*C4*((pow(x_l,2)*(2 + (10*pow(x_l,2)) - (12*pow(x_l,4)))*sqrt(1 - (4*pow(x_l,2)))) + 6*(1.0-2*pow(x_l,2)+2*pow(x_l,4))*L(x_l)) );
              }
              else
              {
                gamma[i] += ( (pow(sminputs.GF,2)*pow(M[i],5)) / (192*pow(pi,3)) ) * Usq(j,i) * ( (C1*(((1 - (14*pow(x_l,2)) - (2*pow(x_l,4)) - (12*pow(x_l,6)))*sqrt(1 - (4*pow(x_l,2)))) + (12*(pow(x_l,4)-1)*L(x_l)))) + (4*C2*((pow(x_l,2)*(2 + (10*pow(x_l,2)) - (12*pow(x_l,4)))*sqrt(1 - (4*pow(x_l,2)))) + (6*(1.0-2*pow(x_l,2)+2*pow(x_l,4))*L(x_l)))) );
              }
            }
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> nu u ubar)
    // Formula is from 1805.08567
    void Gamma_RHN2nuuubar(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2nuuubar;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      double C1 = 0.25*(1-(8./3)*sinW2_eff +(32./9)*pow(sinW2_eff,2));
      double C2 = (sinW2_eff/3.)*((4./3)*sinW2_eff -1);
      std::vector<double> m_uquark(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_uquark[0] = *Param["mU"];
      m_uquark[1] = *Param["mCmC"];
      m_uquark[2] = *Param["mT"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          for (int k=0; k<3; k++)
          {
            if (M[i] > (2*m_uquark[k]))
            {
              double x = m_uquark[k]/M[i];
              // Colour factor 3
              // x^4 factor included in L function
              gamma[i] += 3*( (pow(sminputs.GF,2)*pow(M[i],5)) / (192*pow(pi,3)) ) * Usq(j,i) * (C1 * ( (1. - 14*pow(x,2) - 2*pow(x,4) -12*pow(x,6))*sqrt(1. - 4*pow(x,2)) + 12*(pow(x,4) - 1.)*L(x) ) + 4*C2 * (pow(x,2)*(2. +  10*pow(x,2) - 12*pow(x,4))*sqrt(1. - 4*pow(x,2)) + 6*(1. - 2*pow(x,2) + 2*pow(x,4))*L(x)) );
            }
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> nu d dbar)
    // Formula is from 1805.08567
    void Gamma_RHN2nuddbar(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2nuddbar;
      SMInputs sminputs = *Dep::SMINPUTS;
      double sinW2_eff = Dep::prec_sinW2_eff->central;

      double C1 = 0.25*(1-(4./3)*sinW2_eff+(8./9)*pow(sinW2_eff,2));
      double C2 = (sinW2_eff/6)*((2./3)*sinW2_eff - 1);
      std::vector<double> m_dquark(3), gamma(3), M(3);
      // Since we scan the SLHA2 model, we take masses from it
      m_dquark[0] = *Param["mD"];
      m_dquark[1] = *Param["mS"];
      m_dquark[2] = *Param["mBmB"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      // Helper function; formula is in 0705.1729 and 1805.08567
      // This function varies wrt the paper to include the x^4 factor up front and a cutoff for small x
      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          for (int k=0; k<3; k++)
          {
            if (M[i] > (2*m_dquark[k]))
            {
              double x = m_dquark[k]/M[i];
              // Colour factor 3
              // x^4 factor included in L function
              gamma[i] += 3*( (pow(sminputs.GF,2)*pow(M[i],5)) / (192*pow(pi,3)) ) * Usq(j,i) * (C1 * ( (1. - 14*pow(x,2) - 2*pow(x,4) -12*pow(x,6))*sqrt(1. - 4*pow(x,2)) + 12*(pow(x,4) - 1.)*L(x) ) + 4*C2 * (pow(x,2)*(2. +  10*pow(x,2) - 12*pow(x,4))*sqrt(1. - 4*pow(x,2)) + 6*(1. - 2*pow(x,2) + 2*pow(x,4))*L(x)) );
 
            }
          }
        }
      }
      result = gamma;
    }

    // Decay widths of RHNs, for BBN (N -> l u dbar)
    // Formula is from 1805.08567
    void Gamma_RHN2ludbar(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_RHN2ludbar;
      SMInputs sminputs = *Dep::SMINPUTS;

      // Take from the model parameters (Wolfenstein) (absolute values)
      // Values from PDG: V =  { {0.97434,0.22506,0.00357},
      //                         {0.22492,0.97351,0.0411},
      //                         {0.00875,0.0403,0.99915} };
      double Vus = *Param["CKM_lambda"];
      double Vud = 1.0 - 0.5*pow(Vus,2);
      double Vub = *Param["CKM_A"]*pow(Vus,3)*sqrt(pow(*Param["CKM_rhobar"],2) + pow(*Param["CKM_etabar"],2));
      double Vcd = Vus;
      double Vcs = Vud;
      double Vcb = *Param["CKM_A"]*pow(Vus,2);
      double Vtd = Vcb*Vus*sqrt(pow(1.0 - *Param["CKM_rhobar"],2) + pow(*Param["CKM_etabar"],2));
      double Vts = Vcb;
      double Vtb = 1;
      std::vector<std::vector<double> > V = { {Vud, Vus, Vub}, {Vcd, Vcs, Vcb}, {Vtd, Vts, Vtb} };

      std::vector<double> m_lep(3), m_uquark(3), m_dquark(3), gamma(3), M(3), decay_prod(3), two_heaviest(2);
      // Since we scan the SLHA2 model, we take masses from it
      m_lep[0] = *Param["mE"];
      m_lep[1] = *Param["mMu"];
      m_lep[2] = *Param["mTau"];
      m_uquark[0] = *Param["mU"];
      m_uquark[1] = *Param["mCmC"];
      m_uquark[2] = *Param["mT"];
      m_dquark[0] = *Param["mD"];
      m_dquark[1] = *Param["mS"];
      m_dquark[2] = *Param["mBmB"];
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2(); // |\Theta_{ij}|^2

      for (int i=0; i<3; i++)
      {
        gamma[i] = 0;
        for (int j=0; j<3; j++)
        {
          for (int k=0; k<3; k++)
          {
            for (int l=0; l<3; l++)
            {
              if (M[i] > (m_lep[j]+m_uquark[k]+m_dquark[l]))
              {
                /*decay_prod[0] = m_lep[j];
                decay_prod[1] = m_uquark[k];
                decay_prod[2] = m_dquark[l];
                two_heaviest = two_heaviest_sort(decay_prod);
                x = two_heaviest[0]/M[i];
                y = two_heaviest[1]/M[i];*/
                double xl = m_lep[j]/M[i];
                double xu = m_uquark[k]/M[i];
                double xd = m_dquark[l]/M[i];
                std::function<double(double)> I = [&xu,&xd,&xl](double x)
                {
                  return 12*(x - pow(xl,2) - pow(xd,2))*(1 + pow(xu,2) - x)/x*sqrt(lambda(x,pow(xl,2),pow(xd,2))*lambda(1,x,pow(xu,2)));
                };

                // Colour factor 3
                gamma[i] += 3*( (pow(sminputs.GF,2)*pow(M[i],5)) / (192*pow(pi,3)) ) * Usq(j,i) * pow(V[k][l],2) * Utils::integrate_cquad(I, pow(xd+xl,2), pow(1-xu,2), 0, 1e-2);
              }
            }
          }
        }
      }
      result = gamma;
    }

    // Calculates total decay width for each RHN [GeV]
    void Gamma_BBN(std::vector<double>& result)
    {
      using namespace Pipes::Gamma_BBN;

      // Charged pseudoscalar mesons
      std::vector<double> RHN2piplusl = *Dep::Gamma_RHN2piplusl;
      std::vector<double> RHN2Kplusl = *Dep::Gamma_RHN2Kplusl;
      std::vector<double> RHN2Dplusl = *Dep::Gamma_RHN2Dplusl;
      std::vector<double> RHN2Dsl = *Dep::Gamma_RHN2Dsl;
      std::vector<double> RHN2Bplusl = *Dep::Gamma_RHN2Bplusl;
      std::vector<double> RHN2Bcl = *Dep::Gamma_RHN2Bcl;

      // Neutral pseudoscalar mesons
      std::vector<double> RHN2pi0nu = *Dep::Gamma_RHN2pi0nu;
      std::vector<double> RHN2etanu = *Dep::Gamma_RHN2etanu;
      std::vector<double> RHN2etaprimenu = *Dep::Gamma_RHN2etaprimenu;
      std::vector<double> RHN2etacnu = *Dep::Gamma_RHN2etacnu;

      // Charged vector mesons
      std::vector<double> RHN2rhoplusl = *Dep::Gamma_RHN2rhoplusl;
      std::vector<double> RHN2Dstarplusl = *Dep::Gamma_RHN2Dstarplusl;
      std::vector<double> RHN2Dstarsl = *Dep::Gamma_RHN2Dstarsl;

      // Neutral vector mesons
      std::vector<double> RHN2rho0nu = *Dep::Gamma_RHN2rho0nu;
      std::vector<double> RHN2omeganu = *Dep::Gamma_RHN2omeganu;
      std::vector<double> RHN2phinu = *Dep::Gamma_RHN2phinu;
      std::vector<double> RHN2Jpsinu = *Dep::Gamma_RHN2Jpsinu;

      // Fully leptonic decays 
      std::vector<double> RHN23nu = *Dep::Gamma_RHN23nu;
      std::vector<double> RHN2llnu = *Dep::Gamma_RHN2llnu;
      std::vector<double> RHN2null = *Dep::Gamma_RHN2null;

      // Free quarks
      std::vector<double> RHN2nuuubar = *Dep::Gamma_RHN2nuuubar;
      std::vector<double> RHN2nuddbar = *Dep::Gamma_RHN2nuddbar;
      std::vector<double> RHN2ludbar = *Dep::Gamma_RHN2ludbar;

      std::vector<double> gamma_total(3), M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];
      
      // Hadronization scale 
      static const double LQCD = 1; // (GeV)

      // factor of 2 in front accounts for Majorana nature
      for (int i=0; i<3; i++)
      {
        // Fully leptonic decays are open above and below LQCD
        gamma_total[i] = 2*(RHN23nu[i] + RHN2llnu[i] + RHN2null[i]);

        // Meson decays only matter below LQCD. Beyond, only decays to free quarks matter
        if(M[i] < LQCD)
        {
          gamma_total[i] += 2*(RHN2piplusl[i] + RHN2Kplusl[i] + RHN2Dplusl[i] + RHN2Dsl[i] + RHN2Bplusl[i] + RHN2Bcl[i]);
          gamma_total[i] += 2*(RHN2pi0nu[i] + RHN2etanu[i] + RHN2etaprimenu[i] + RHN2etacnu[i]);
          gamma_total[i] += 2*(RHN2rhoplusl[i] + RHN2Dstarplusl[i] + RHN2Dstarsl[i]);
          gamma_total[i] += 2*(RHN2rho0nu[i] + RHN2omeganu[i] + RHN2phinu[i] + RHN2Jpsinu[i]);
        }
        else
        {
          gamma_total[i] += 2*(RHN2nuuubar[i] + RHN2nuddbar[i] + RHN2ludbar[i]);
        }
      }
      result = gamma_total;
    }

    // BBN constraint likelihood : lifetime must be less than 0.1s [arXiv:1202.2841]
    // This is here implemented as step function in the likelihood
    void lnL_bbn(double& result_bbn)
    {
      using namespace Pipes::lnL_bbn;
      std::vector<double> gamma = *Dep::Gamma_BBN;
      result_bbn = 0.0;
      for(int i=0; i<3; i++)
      {
        if((hbar/gamma[i])>0.1)
        {
          result_bbn = -100;
        }
      }
    }

    // Lepton universality constraint: R_{e,mu)^pi. Computation from 1502.00477. R_pi_SM from Phys. Rev. Lett 99, 231801.
    void RHN_R_pi(double& R_pi)
    {
      using namespace Pipes::RHN_R_pi;
      SMInputs sminputs = *Dep::SMINPUTS;
      static const double m_pi = meson_masses.pi_plus;
      static const double R_pi_SM = 1.2354e-4;
      double r_e_pi = pow(sminputs.mE,2)/pow(m_pi,2);
      double r_mu_pi = pow(sminputs.mMu,2)/pow(m_pi,2);
      double e_f_pi = 0.0, mu_f_pi = 0.0, d_r_pi = 1.0;
      std::vector<double> M(3), r_I_pi(3), G_e_pi = {0.0,0.0,0.0}, G_mu_pi = {0.0,0.0,0.0};
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2();

      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      for (int i=0; i<3; i++)
      {
        r_I_pi[i] = pow(M[i], 2)/pow(m_pi, 2);
 
        if(M[i] + sminputs.mMu < m_pi)
        {
          G_mu_pi[i] = ( r_mu_pi + r_I_pi[i] - pow((r_mu_pi - r_I_pi[i]), 2) ) * sqrt(1.0 - 2.0*(r_mu_pi + r_I_pi[i]) + pow(r_mu_pi - r_I_pi[i], 2)) / (r_mu_pi * pow((1.0 - r_mu_pi), 2));
        } 
        else
          G_mu_pi[i] = 0.0;

        if(M[i] + sminputs.mE < m_pi)
        {
          G_e_pi[i] = ( r_e_pi + r_I_pi[i] - pow(r_e_pi - r_I_pi[i], 2) ) * sqrt(1.0 - 2.0*(r_e_pi + r_I_pi[i]) + pow((r_e_pi - r_I_pi[i]), 2)) / (r_e_pi * pow((1.0 - r_e_pi), 2));
        }
        else
          G_e_pi[i] = 0.0;

        e_f_pi += Usq(0,i) * (G_e_pi[i] - 1.0);
        mu_f_pi += Usq(1,i) * (G_mu_pi[i] - 1.0);
  
      }

      d_r_pi = ((1.0 + e_f_pi)/(1.0 + mu_f_pi));
      R_pi = R_pi_SM * d_r_pi;
 
    }

    // Lepton universality constraint: R_(e,mu)^K. Computation from 1502.00477. R_K_SM from Phys. Rev. Lett 99, 231801.
    void RHN_R_K(double& R_K)
    {
      using namespace Pipes::RHN_R_K;
      SMInputs sminputs = *Dep::SMINPUTS;
      static const double m_K = meson_masses.kaon_plus; 
      static const double R_K_SM = 2.477e-5;
      double r_e_K = pow(sminputs.mE,2)/pow(m_K,2);
      double r_mu_K = pow(sminputs.mMu,2)/pow(m_K,2);
      double e_f_K = 0.0, mu_f_K = 0.0, d_r_K = 1.0;
      std::vector<double> M(3), r_I_K(3), G_e_K = {0.0,0.0,0.0}, G_mu_K = {0.0,0.0,0.0};
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2();

      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      for (int i=0; i<3; i++)
      {
        r_I_K[i] = pow(M[i], 2)/pow(m_K,2);

        if(M[i] + sminputs.mMu < m_K)
        {
          G_mu_K[i] = ( r_mu_K + r_I_K[i] - pow((r_mu_K - r_I_K[i]), 2) ) * sqrt(1.0 - 2.0*(r_mu_K + r_I_K[i]) + pow(r_mu_K - r_I_K[i], 2)) / (r_mu_K * pow((1.0 - r_mu_K), 2));
        } 
        else
          G_mu_K[i] = 0.0;

        if(M[i] + sminputs.mE < m_K)
        {
          G_e_K[i] = ( r_e_K + r_I_K[i] - pow((r_e_K - r_I_K[i]), 2) ) * sqrt(1.0 - 2.0*(r_e_K + r_I_K[i]) + pow((r_e_K - r_I_K[i]), 2)) / (r_e_K * pow((1.0 - r_e_K), 2));
        }
        else
          G_e_K[i] = 0.0;

        e_f_K += Usq(0,i) * (G_e_K[i] - 1.0);
        mu_f_K += Usq(1,i) * (G_mu_K[i] - 1.0);
          
      }

      d_r_K = ((1.0 + e_f_K)/(1.0 + mu_f_K));
      R_K = R_K_SM * d_r_K;
    }

    // Lepton universality constraint: R_(e,mu)^tau. Computation from 1502.00477. R_tau_SM from Int. J. Mod. Phys. A 24, 715, 2009.
    void RHN_R_tau(double& R_tau)
    {
      using namespace Pipes::RHN_R_tau;
      SMInputs sminputs = *Dep::SMINPUTS;
      static const double m_tau = sminputs.mTau;  // GeV
      static const double R_tau_SM = 0.973;
      double e_f_tau = 0.0, mu_f_tau = 0.0, d_r_tau = 1.0;
      std::vector<double> M(3);
      Matrix3d Usq = Dep::SeesawI_Theta->cwiseAbs2();

      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      for (int i=0; i<3; i++)
      {
        if(M[i] > m_tau)
        {
          e_f_tau  -= Usq(0,i);
          mu_f_tau -= Usq(1,i);
        }
        else
        {
          e_f_tau += 0.0;
          mu_f_tau += 0.0;
        }
      }

      d_r_tau = ((1.0 + mu_f_tau)/(1.0 + e_f_tau));
      R_tau = R_tau_SM * d_r_tau;
    }

    // Lepton universality from W decays
    // 0: R(W->mu nu/W->e nu) from LHCb 1608.01484
    // 1: R(W->tau nu/W->e nu) from LEP 1302.3415
    // 2: R(W->tau nu/W->mu nu) from LEP 1302.3415
    void RHN_R_W(std::vector<double> &R_W)
    {
      using namespace Pipes::RHN_R_W;
      std::vector<double> Wdecays = *Dep::W_to_l_decays;

      R_W.clear();

      R_W.push_back(Wdecays[1]/Wdecays[0]);
      R_W.push_back(Wdecays[2]/Wdecays[0]);
      R_W.push_back(Wdecays[2]/Wdecays[1]);
    }

    // Log-likelihood for the lepton universality constraint R_(e,mu)^K
    void lnL_R_K(double& result)
    {
      using namespace Pipes::lnL_R_K;
      double R_K = *Dep::R_K;
      double R_K_exp = 2.488e-5; // 1212.4012 (Phys. Lett. B 719 (2013), 326)
      double R_K_err = 0.010e-5;

      result = Stats::gaussian_loglikelihood(R_K, R_K_exp, 0.0, R_K_err, false);
    }

    // Log-likelihood for the lepton universality constraint R_(e,mu)^pi
    void lnL_R_pi(double& result)
    {
      using namespace Pipes::lnL_R_pi;
      double R_pi = *Dep::R_pi;
      double R_pi_exp = 1.2327e-4; // PDG average from Phys.Rev.Lett. 70 (1993) 17-20, Phys. Rev. Lett. 115, 071801 (2015), Phys. Rev. Lett. 68, 3000 (1992)
      double R_pi_err = 0.0023e-4;

      result = Stats::gaussian_loglikelihood(R_pi, R_pi_exp, 0.0, R_pi_err, false);
    }

    // Log-likelihood for the lepton universality constraint R_(e,mu)^tau
    void lnL_R_tau(double& result)
    {
      using namespace Pipes::lnL_R_tau;
      double R_tau = *Dep::R_tau;
      double R_tau_exp = 0.9762; // 1612.07233 (Phys. Rev. D 86, 010001) 
      double R_tau_err = 0.0028;

      result = Stats::gaussian_loglikelihood(R_tau, R_tau_exp, 0.0, R_tau_err, false);
    }
 
    // Log-likelihood for the lepton universality constraint R_(e,mu)^W 
    void lnL_R_W(double& result)
    {
      using namespace Pipes::lnL_R_W;
      std::vector<double> R_W = *Dep::R_W;

      std::vector<double> R_W_exp = {0.986, 1.043, 1.070}; // PDG 18
      std::vector<double> R_W_err = {0.013, 0.024, 0.026}; // PDG 18

      result = Stats::gaussian_loglikelihood(R_W[0], R_W_exp[0], 0.0, R_W_err[0], false);
      result += Stats::gaussian_loglikelihood(R_W[1], R_W_exp[1], 0.0, R_W_err[1], false);
      result += Stats::gaussian_loglikelihood(R_W[2], R_W_exp[2], 0.0, R_W_err[2], false);
    }

    // Calculate 0nubb half-life [1/yr] for 136Xe 0nubb detector, for the RHN model
    void RHN_Thalf_0nubb_Xe(double& result)
    {
      using namespace Pipes::RHN_Thalf_0nubb_Xe;
      double mp, A_0nubb_Xe, p2_0nubb_Xe, prefactor;
      std::vector<double> M(3);
      std::complex<double> sum = {0.0,0.0};

      // Relevant model parameters
      Matrix3cd theta = *Dep::SeesawI_Theta;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      // Nuisance parameters following the definitions in Faessler et al. 2014 (1408.6077)
      A_0nubb_Xe = runOptions->getValueOrDef<double>(8.74, "A");
      A_0nubb_Xe *= 1e-10;  // [1/yr]
      p2_0nubb_Xe = pow(runOptions->getValueOrDef<double>(183.0, "p"), 2);
      p2_0nubb_Xe *= 1e-6;  // MeV^2 --> GeV^2
      mp = 0.938;  // [GeV] (PDG 2014)

      // Lifetime equation is adopted from Faessler+14, Eq. (13)
      prefactor = A_0nubb_Xe*mp*mp/p2_0nubb_Xe/p2_0nubb_Xe;
      for (int i=0; i<3; i++)
      {
        sum+=pow(theta(0,i),2)*M[i]*p2_0nubb_Xe/(p2_0nubb_Xe+pow(M[i], 2));
      }
      result = prefactor * abs(sum) * abs(sum);
    }
    
    // Calculate 0nubb half-life [1/yr] for 76Ge 0nubb detector, for the RHN model
    void RHN_Thalf_0nubb_Ge(double& result)
    {
      using namespace Pipes::RHN_Thalf_0nubb_Ge;
      double mp, A_0nubb_Ge, p2_0nubb_Ge, prefactor;
      std::vector<double> M(3);
      std::complex<double> sum = {0.0,0.0};

      // Relevant model parameters
      Matrix3cd theta = *Dep::SeesawI_Theta;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      // Nuisance parameters following the definitions in Faessler et al. 2014 (1408.6077)
      A_0nubb_Ge = runOptions->getValueOrDef<double>(5.05, "A");
      A_0nubb_Ge *= 1e-10;  // [1/yr]
      p2_0nubb_Ge = pow(runOptions->getValueOrDef<double>(163.0, "p"), 2);
      p2_0nubb_Ge *= 1e-6;  // MeV^2 --> GeV^2
      mp = 0.938;  // [GeV] (PDG 2014)

      // Lifetime equation is adopted from Faessler+14, Eq. (13)
      prefactor = A_0nubb_Ge*mp*mp/p2_0nubb_Ge/p2_0nubb_Ge;
      for (int i=0; i<3; i++)
      {
        sum+=pow(theta(0,i),2)*M[i]*p2_0nubb_Ge/(p2_0nubb_Ge+pow(M[i], 2));
      }
      result = prefactor * abs(sum) * abs(sum);
    }

    // KamLAND-Zen: Phys. Rev. Lett 117 (2016) 082503
    void lnL_0nubb_KamLAND_Zen(double& result)
    {
      using namespace Pipes::lnL_0nubb_KamLAND_Zen;
      double tau_limit = 1.07e26;  // [yr] 90% CL

      double Thalf = *Dep::Thalf_0nubb_Xe;

      // Factor 1.28155 corresponds to one-sided UL at 90% CL
      result = Stats::gaussian_upper_limit(Thalf, 0., 0., tau_limit/1.28155, false);
    }

    // GERDA: Phys. Rev. Lett. 111 (2013) 122503
    //        Update: Nature 544 (2017) 47
    void lnL_0nubb_GERDA(double& result)
    {
      using namespace Pipes::lnL_0nubb_GERDA;
      double tau_limit = 5.3e25;  // [yr] 90% CL

      double Thalf = *Dep::Thalf_0nubb_Ge;

      // Factor 1.28155 corresponds to one-sided UL at 90% CL
      result = Stats::gaussian_upper_limit(Thalf, 0., 0., tau_limit/1.28155, false);
    }

    // Unified 0nubb likelihood
    void lnL_0nubb(double &result)
    {
      using namespace Pipes::lnL_0nubb;
      result = *Dep::lnL_0nubb_KamLAND_Zen + *Dep::lnL_0nubb_GERDA;
    }

    // Calculate mbb for 136Xe 0nubb detector, for the RHN model
    void RHN_mbb_0nubb_Xe(double& result)
    {
      using namespace Pipes::RHN_mbb_0nubb_Xe;
      double p2_0nubb_Xe;
      std::vector<double> M(3);
      std::complex<double> sum = {0.0,0.0};

      // Relevant model parameters
      Matrix3cd m_light = *Dep::m_nu;
      Matrix3cd U_light = *Dep::UPMNS;
      Matrix3cd theta = *Dep::SeesawI_Theta;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      // Nuisance parameters following the definitions in Faessler et al. 2014 (1408.6077)
      p2_0nubb_Xe = pow(runOptions->getValueOrDef<double>(178.0, "p"), 2);
      p2_0nubb_Xe *= 1e-6;  // MeV^2 --> GeV^2
      
      // mbb equation is adopted from Drewes, Eijima 2017, Eq. (14) and following
      for (int i=0; i<3; i++)
      {
        sum += pow(U_light(0,i),2)*m_light(i,i);
      }
      for (int i=0; i<3; i++)
      {
        sum += pow(theta(0,i),2)*M[i]*(p2_0nubb_Xe/(p2_0nubb_Xe + pow(M[i],2)));
      }
      result = abs(sum);
    } 
    
    // Calculate mbb for 76Ge 0nubb detector, for the RHN model
    void RHN_mbb_0nubb_Ge(double& result)
    {
      using namespace Pipes::RHN_mbb_0nubb_Ge;
      double p2_0nubb_Ge;
      std::vector<double> M(3);
      std::complex<double> sum = {0.0,0.0};

      // Relevant model parameters
      Matrix3cd m_light = *Dep::m_nu;
      Matrix3cd U_light = *Dep::UPMNS;
      Matrix3cd theta = *Dep::SeesawI_Theta;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      // Nuisance parameters following the definitions in Faessler et al. 2014 (1408.6077)
      p2_0nubb_Ge = pow(runOptions->getValueOrDef<double>(159.0, "p"), 2);
      p2_0nubb_Ge *= 1e-6;  // MeV^2 --> GeV^2
      
      // mbb equation is adopted from Drewes, Eijima 2017, Eq. (14) and following
      for (int i=0; i<3; i++)
      {
        sum += pow(U_light(0,i),2)*m_light(i,i);
      }
      for (int i=0; i<3; i++)
      {
        sum += pow(theta(0,i),2)*M[i]*(p2_0nubb_Ge/(p2_0nubb_Ge + pow(M[i],2)));
      }
      result = abs(sum);
    } 
    
    // KamLAND-Zen: Phys. Rev. Lett 117 (2016) 082503
    void lnL_mbb_0nubb_KamLAND_Zen(double& result)
    {
      using namespace Pipes::lnL_mbb_0nubb_KamLAND_Zen;
      double mbb_limit = 0.165*1e-9;  // [GeV] mbb < (0.061-0.165)eV at 90% C

      double mbb = *Dep::mbb_0nubb_Xe;

      // Factor 1.28155 corresponds to one-sided UL at 90% CL
      result = Stats::gaussian_upper_limit(mbb, 0., 0., mbb_limit/1.28155, false);
    }

    // GERDA: Phys. Rev. Lett. 111 (2013) 122503
    //        Update: Nature 544 (2017) 47
    void lnL_mbb_0nubb_GERDA(double& result)
    {
      using namespace Pipes::lnL_mbb_0nubb_GERDA;
      double mbb_limit = 0.33*1e-9;  // [GeV] mbb < (0.15-0.33)eV at 90% CL

      double mbb = *Dep::mbb_0nubb_Ge;

      // Factor 1.28155 corresponds to one-sided UL at 90% CL
      result = Stats::gaussian_upper_limit(mbb, 0., 0., mbb_limit/1.28155, false);
    }

    // Unified 0nubb likelihood based on mbb
    void lnL_mbb_0nubb(double &result)
    {
      using namespace Pipes::lnL_mbb_0nubb;
      result = *Dep::lnL_mbb_0nubb_KamLAND_Zen + *Dep::lnL_mbb_0nubb_GERDA;
    }

    // Helper function to fill all needed experimental info for CKM unitarity
    void fill_ckm_exp(Matrix3cd &Theta, double GF, triplet<double> (&Vus_exp)[8], triplet<double> &Vud_exp, double (&f)[8])
    {
      // Vus
      // Experimental values determined for K and tau decays. From table 1 in 1502.00477
      double V_us_exp[] = {0.2163, 0.2166, 0.2155, 0.2160, 0.2158, 0.2262, 0.2214, 0.2173};
      double err_V_us_exp[] = {0.0006, 0.0006, 0.0013, 0.0011, 0.0014, 0.0013, 0.0022, 0.0022};
      double f_plus = 0.959;
      double err_f_plus = 0.005;
      for(int i=0; i<5; i++)
      {
        V_us_exp[i] /= f_plus;
        err_V_us_exp[i] = sqrt(pow(err_V_us_exp[i] / f_plus,2) + pow(V_us_exp[i] * err_f_plus / f_plus, 2));
      }
 
      // Fill Vus_exp triplet
      for(int i=0; i<8; i++)
      {
        Vus_exp[i].central = V_us_exp[i];
        Vus_exp[i].upper = err_V_us_exp[i];
        Vus_exp[i].lower = err_V_us_exp[i];
      }

      // Vud
      // Combined value from the PDG
      static const double V_ud_exp = 0.97417;
      static const double err_V_ud_exp = 0.00021;
 
      // Fill Vud_exp triplet
      Vud_exp.central = V_ud_exp;
      Vud_exp.upper = err_V_ud_exp;
      Vud_exp.lower = err_V_ud_exp;

      // f
      Matrix3d ThetaNorm = (Theta * Theta.adjoint()).real();
      double Gmu = sqrt(GF*GF*(1. - ThetaNorm(1,1) - ThetaNorm(0,0)));

      f[0] = pow(GF/Gmu,2)*(1 - ThetaNorm(0,0));
      f[1] = f[0];
      f[2] = f[0];
      f[3] = pow(GF/Gmu,2)*(1 - ThetaNorm(1,1));
      f[4] = f[3];
      f[5] = 1 + ThetaNorm(1,1);
      f[6] = 1 + ThetaNorm(0,0) + ThetaNorm(1,1) - ThetaNorm(2,2);
      f[7] = 1 + 0.2*ThetaNorm(0,0) - 0.9*ThetaNorm(1,1) - 0.2*ThetaNorm(2,2);
    }
     
    // CKM unitarity constraint: optimize on Vus from Theta [PDG 2016]
    void calc_Vus(double& result_Vus)
    {
      using namespace Pipes::calc_Vus;
      SMInputs sminputs = *Dep::SMINPUTS;
      Matrix3cd Theta = *Dep::SeesawI_Theta;

      // Fill experimental data
      triplet<double> V_us_exp[8];
      triplet<double> V_ud_exp;
      double f[8];
      fill_ckm_exp(Theta, sminputs.GF, V_us_exp, V_ud_exp, f);
      
      // For the minimalization it's much better to transform the Vud experimental result to Vus the same as we do for theory and minimize only Vus
      double V_us_from_Vud=sqrt(1.-V_ud_exp.central*V_ud_exp.central);
      double err_V_us_from_Vud_exp= ( (V_ud_exp.central)/(sqrt(1-V_ud_exp.central*V_ud_exp.central))  ) * V_ud_exp.upper;
                                             
      double est_Vus = 0.;
      double sum_numerator = 0;
      double sum_denominator = 0;
      for (int i=0; i<7; i++)
      {
        sum_numerator += (V_us_exp[i].central/f[i]) / (V_us_exp[i].upper*V_us_exp[i].upper /( f[i]*f[i]));
        sum_denominator += 1./(V_us_exp[i].upper*V_us_exp[i].upper /( f[i]*f[i]));
      }
      // now vud
      sum_numerator += (V_us_from_Vud /f[0]) / ( (err_V_us_from_Vud_exp*err_V_us_from_Vud_exp)/( f[0]*f[0]));
      sum_denominator +=  1./( (err_V_us_from_Vud_exp*err_V_us_from_Vud_exp) /( f[0]*f[0]) );

      est_Vus = sum_numerator/sum_denominator;
      
      result_Vus = est_Vus;
    }

    // CKM unitarity constraint: V_ud should lie within 3sigma of the world average [PDG 2016]
    void lnL_ckm_Vusmin(double& result_ckm)
    {
      using namespace Pipes::lnL_ckm_Vusmin;
      SMInputs sminputs = *Dep::SMINPUTS;
      Matrix3cd Theta = *Dep::SeesawI_Theta;
      double V_us = *Dep::calc_Vus;

      // Fill experimental data
      triplet<double> V_us_exp[8];
      triplet<double> V_ud_exp;
      double f[8];
      fill_ckm_exp(Theta, sminputs.GF, V_us_exp, V_ud_exp, f);
 
      double chi2 = 0.0;
      for (int i=0; i<7; i++)
        chi2 += pow( (sqrt(pow(V_us,2)*f[i]) - V_us_exp[i].central) / V_us_exp[i].upper, 2);
      // According to 1407.6607 the correction for Vud is the same as K->pi e nu (f[0])
      double V_ub = 3.94e-3;
      chi2 += pow( (sqrt((1 - pow(V_us,2) - pow(V_ub,2))*f[0]) - V_ud_exp.central)/ V_ud_exp.upper/1.20, 2);
      result_ckm = -0.5*chi2;
    }
  
    // CKM unitarity constraint: V_ud should lie within 3sigma of the world average [PDG 2016]
    void lnL_ckm_Vus(double& result_ckm)
    {
      using namespace Pipes::lnL_ckm_Vus;
      SMInputs sminputs = *Dep::SMINPUTS;
      Matrix3cd Theta = *Dep::SeesawI_Theta;
      double V_us = *Param["CKM_lambda"];

      // Fill experimental data
      triplet<double> V_us_exp[8];
      triplet<double> V_ud_exp;
      double f[8];
      fill_ckm_exp(Theta, sminputs.GF, V_us_exp, V_ud_exp, f);

      double chi2 = 0.0;
      for (int i=0; i<7; i++)
        chi2 += pow( (sqrt(pow(V_us,2)*f[i]) - V_us_exp[i].central) / V_us_exp[i].upper, 2);
      // According to 1407.6607 the correction for Vud is the same as K->pi e nu (f[0])
      chi2 += pow( (sqrt((1 - pow(V_us,2))*f[0]) - V_ud_exp.central)/ V_ud_exp.upper, 2);
      result_ckm = -0.5*chi2;
    }
    
    // Likelihood contribution from PIENU; searched for extra peaks in the spectrum of pi -> mu + nu. Constrains |U_ei|^2 at 90% in the mass range 60-129 MeV. [Phys. Rev. D, 84 (052002), 2011] (arXiv:1106.4055)
    void lnL_pienu(double& result)
    {
      using namespace Pipes::lnL_pienu;

      // Mass range of experiment
      static const double low_lim = 0.06060759493670887;  // GeV
      static const double upp_lim = 0.12926582278481014;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Ue1;
      mixing_sq[1] = *Dep::Ue2;
      mixing_sq[2] = *Dep::Ue3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/pienu.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^2.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2.0*pi/1.28/1.28);
        }
        else
        {
          U[i] = s.eval(M[i]);
          result += Stats::gaussian_upper_limit(mixing_sq[i]/U[i], 0, 0, 1/1.28, false);  // exp_error = abs(exp_value - 90CL_value), exp_value = 0, 1.28: 90% CL limit for half-Gaussian.
        }
      }
    }

    // Likelihood contribution from PS191, electron sector; looked for charged tracks originating from RHN decays: nu_r -> l(-) + l(+) + nu / l + pi / e + pi(+) + pi(0). Constrains |U_ei|^2 at 90% in the mass range 20-450 MeV. Function also incorporates a later re-interpretation of the data to account for neutral current interaction (ignored in original) as well as the RHNs' Majorana nature. [Original: Phys. Lett. B, 203(3):332-334, 1988][Re-interp.: JHEP, 2012(6):1-27 (arXiv:1112.3319)]
    void lnL_ps191_e(double& result)
    {
      using namespace Pipes::lnL_ps191_e;

      // Mass range of experiment
      static const double low_lim = 0.011810586;  // GeV
      static const double upp_lim = 0.4491907842;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);
      double c_e = 0.5711;
      double c_mu = 0.1265;
      double c_tau = 0.1265;

      mixing_sq[0] = *Dep::Ue1 * ((c_e * *Dep::Ue1) + (c_mu * *Dep::Um1) + (c_tau * *Dep::Ut1));
      mixing_sq[1] = *Dep::Ue2 * ((c_e * *Dep::Ue2) + (c_mu * *Dep::Um2) + (c_tau * *Dep::Ut2));
      mixing_sq[2] = *Dep::Ue3 * ((c_e * *Dep::Ue3) + (c_mu * *Dep::Um3) + (c_tau * *Dep::Ut3));
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/ps191_e.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 90% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          U[i] = s.eval(M[i]);
          result += -2.44*(mixing_sq[i]/pow(U[i],2));
        }
      }
    }

    // Likelihood contribution from PS191, muon sector. Constrains |U_(mu,i)|^2 at 90% in the mass range 20-450 MeV. Description & references above.
    void lnL_ps191_mu(double& result)
    {
      using namespace Pipes::lnL_ps191_mu;

      // Mass range of experiment
      static const double low_lim = 0.0103348894;  // GeV
      static const double upp_lim = 0.3610401587;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);
      double c_e = 0.5711;
      double c_mu = 0.1265;
      double c_tau = 0.1265;

      mixing_sq[0] = *Dep::Um1 * ((c_e * *Dep::Ue1) + (c_mu * *Dep::Um1) + (c_tau * *Dep::Ut1));
      mixing_sq[1] = *Dep::Um2 * ((c_e * *Dep::Ue2) + (c_mu * *Dep::Um2) + (c_tau * *Dep::Ut2));
      mixing_sq[2] = *Dep::Um3 * ((c_e * *Dep::Ue3) + (c_mu * *Dep::Um3) + (c_tau * *Dep::Ut3));
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/ps191_mu.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 90% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          U[i] = s.eval(M[i]);
          result += -2.44*(mixing_sq[i]/pow(U[i],2));
        }
      }
    }

    // Likelihood contribution from CHARM, electron sector; searched for charged and neutral current decays of RHNs. Constrains |U_ei|^2 at 90% in the mass range 0.5-2.8 GeV. [Phys. Lett. B, 166(4):473-478, 1986]
    void lnL_charm_e(double& result)
    {
      using namespace Pipes::lnL_charm_e;

      // Mass range of experiment
      static const double low_lim = 0.1595725833606568;  // GeV
      static const double upp_lim = 2.0814785578102417;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);
      double c_e = 0.5711;
      double c_mu = 0.1265;
      double c_tau = 0.1265;

      mixing_sq[0] = *Dep::Ue1 * ((c_e * *Dep::Ue1) + (c_mu * *Dep::Um1) + (c_tau * *Dep::Ut1));
      mixing_sq[1] = *Dep::Ue2 * ((c_e * *Dep::Ue2) + (c_mu * *Dep::Um2) + (c_tau * *Dep::Ut2));
      mixing_sq[2] = *Dep::Ue3 * ((c_e * *Dep::Ue3) + (c_mu * *Dep::Um3) + (c_tau * *Dep::Ut3));
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/charm_e.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 90% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          U[i] = s.eval(M[i])/sqrt(2);  // Division by sqrt(2) to account for Majorana nature.
          result += -2.44*(mixing_sq[i]/pow(U[i],2));
        }
      }
    }

    // Likelihood contribution from CHARM, muon sector. Constrains |U_(mu,i)|^2 at 90% in the mass range 0.5-2.8 GeV. Description & references above.
    void lnL_charm_mu(double& result)
    {
      using namespace Pipes::lnL_charm_mu;

      // Mass range of experiment
      static const double low_lim = 0.4483153374997989;  // GeV
      static const double upp_lim = 1.9231171483448785;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);
      double c_e = 0.5711;
      double c_mu = 0.1265;
      double c_tau = 0.1265;

      mixing_sq[0] = *Dep::Um1 * ((c_e * *Dep::Ue1) + (c_mu * *Dep::Um1) + (c_tau * *Dep::Ut1));
      mixing_sq[1] = *Dep::Um2 * ((c_e * *Dep::Ue2) + (c_mu * *Dep::Um2) + (c_tau * *Dep::Ut2));
      mixing_sq[2] = *Dep::Um3 * ((c_e * *Dep::Ue3) + (c_mu * *Dep::Um3) + (c_tau * *Dep::Ut3));
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/charm_mu.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 90% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          U[i] = s.eval(M[i])/sqrt(2);  // Division by sqrt(2) to account for Majorana nature.
          result += -2.44*(mixing_sq[i]/pow(U[i],2));
        }
      }
    }

    // Likelihood contribution from DELPHI's short-lived RHN analysis; searched for charged and neutral current decays of RHNs. Constrains |U_ei|^2, |U_(mu,i)|^2 as well as |U_(tau,i)|^2 at 95% in the mass range 3.5-50 GeV. [Z. Phys. C, 74(1):57-71, 1997]
    void lnL_delphi_short_lived(double& result)
    {
      using namespace Pipes::lnL_delphi_short_lived;

      // Mass range of experiment
      static const double low_lim = 1.8102188251700203;  // GeV
      static const double upp_lim = 80.00000000000006;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(9);

      mixing_sq[0] = *Dep::Ue1;  // This is |U_{e1}|^2 etc
      mixing_sq[1] = *Dep::Ue2;
      mixing_sq[2] = *Dep::Ue3;
      mixing_sq[3] = *Dep::Um1;
      mixing_sq[4] = *Dep::Um2;
      mixing_sq[5] = *Dep::Um3;
      mixing_sq[6] = *Dep::Ut1;
      mixing_sq[7] = *Dep::Ut2;
      mixing_sq[8] = *Dep::Ut3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/delphi_short_lived.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 95% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          for (int j=i; j<9; j+=3)
          {
            U[i] = s.eval(M[i]);
            result += -3.09*(pow(mixing_sq[j]/U[i],2));
          }
        }
      }
    }

    // Likelihood contribution from DELPHI's long-lived RHN analysis; searched for charged and neutral current decays of RHNs. Constrains |U_ei|^2, |U_(mu,i)|^2 as well as |U_(tau,i)|^2 at 95% in the mass range 0.5-4.2 GeV. [Z. Phys. C, 74(1):57-71, 1997]
    void lnL_delphi_long_lived(double& result)
    {
      using namespace Pipes::lnL_delphi_long_lived;

      // Mass range of experiment
      static const double low_lim = 0.4383563;  // GeV
      static const double upp_lim = 4.1954595;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(9);

      mixing_sq[0] = *Dep::Ue1;  // This is |U_{e1}|^2 etc
      mixing_sq[1] = *Dep::Ue2;
      mixing_sq[2] = *Dep::Ue3;
      mixing_sq[3] = *Dep::Um1;
      mixing_sq[4] = *Dep::Um2;
      mixing_sq[5] = *Dep::Um3;
      mixing_sq[6] = *Dep::Ut1;
      mixing_sq[7] = *Dep::Ut2;
      mixing_sq[8] = *Dep::Ut3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/delphi_long_lived.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 95% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          for (int j=i; j<9; j+=3)
          {
            U[i] = s.eval(M[i]);
            result += -3.09*(pow(mixing_sq[j]/U[i],2));
          }
        }
      }
    }

    // Likelihood contribution from ATLAS, electron sector; looked at the production and decay chain: pp -> W*(+-) -> l(+-) + nu_r. nu_r then decays into an on-shell W and a lepton; the W decays primarily into a qq pair. Constrains |U_ei|^2 at 95% in the mass range 50-500 GeV. [JHEP, 07:162, 2015 (arXiv:1506.06020)]
    void lnL_atlas_e(double& result)
    {
      using namespace Pipes::lnL_atlas_e;

      // Mass range of experiment
      static const double low_lim = 100.1041668;  // GeV
      static const double upp_lim = 476.1458333;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Ue1;
      mixing_sq[1] = *Dep::Ue2;
      mixing_sq[2] = *Dep::Ue3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/atlas_e.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^4.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2.0*pi/1.64/1.64);
        }
        else
        {
          U[i] = s.eval(M[i]);
          result += Stats::gaussian_upper_limit(pow(mixing_sq[i]/U[i],2), 0, 0, 1/1.64, false);  // exp_error = abs(exp_value - 95CL_value), exp_value = 0, 1.64: 95% CL limit for half-Gaussian.
        }
      }
    }

    // Likelihood contribution from ATLAS, muon sector. Constrains |U_(mu,i)|^2 at 95% in the mass range 50-500 GeV. Description & references above.
    void lnL_atlas_mu(double& result)
    {
      using namespace Pipes::lnL_atlas_mu;

      // Mass range of experiment
      static const double low_lim = 101.89094090419824;  // GeV
      static const double upp_lim = 500.76903192219294;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Um1;
      mixing_sq[1] = *Dep::Um2;
      mixing_sq[2] = *Dep::Um3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/atlas_mu.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^4.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2.0*pi/1.64/1.64);
        }
        else
        {
          U[i] = s.eval(M[i]);
          result += Stats::gaussian_upper_limit(pow(mixing_sq[i]/U[i],2), 0, 0, 1/1.64, false); // exp_error = abs(exp_value - 95CL_value), exp_value = 0, 1.64: 95% CL limit for half-Gaussian.
        }
      }
    }

    // Likelihood contribution from E949; used the kaon decay: K(+) -> mu(+) + nu_r. Constrains |U_(mu,i)|^2 at 90% in the mass range 175-300 MeV. [Phys. Rev. D, 91, 052001 (2015) (arXiv:1411.3963v2)]
    void lnL_e949(double& result)
    {
      using namespace Pipes::lnL_e949;

      // Mass range of experiment
      static const double low_lim = 0.1794613032227713;  // GeV
      static const double upp_lim = 0.2995365796283227;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Um1;
      mixing_sq[1] = *Dep::Um2;
      mixing_sq[2] = *Dep::Um3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/e949.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^2.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2.0*pi/1.28/1.28);
        }
        else
        {
          U[i] = s.eval(M[i])/sqrt(2);  // Division by sqrt(2) to account for Majorana nature.
          result += Stats::gaussian_upper_limit(mixing_sq[i]/U[i], 0, 0,  1/1.28, false); // exp_error = abs(exp_value - 90CL_value), exp_value = 0, 1.28: 90% CL limit for half-Gaussian.
        }
      }
    }

    // Likelihood contribution from NuTeV; used RHN decays into muonic final states (mu + mu + nu / mu + e + nu / mu + pi / mu + rho). Constrains |U_(mu,i)|^2 at 90% CL in the mass range 0.25-2 GeV. [Phys. Rev. Lett., 83:4943-4946, 1999 (arXiv:hep-ex/9908011)]
    void lnL_nutev(double& result)
    {
      using namespace Pipes::lnL_nutev;

      // Mass range of experiment
      static const double low_lim = 0.2116390354;  // GeV
      static const double upp_lim = 2.0161957132;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Um1;
      mixing_sq[1] = *Dep::Um2;
      mixing_sq[2] = *Dep::Um3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/nutev.csv");

      // Assume scaling with |U|^4, zero bkg, number of events at 90% CL is
      // reverse engineered.  We assume that lnL = mu_sig is a faithful
      // approximation to the true Poisson likelihood.
      result = 0;
      for (int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
          result += 0;
        else
        {
          U[i] = s.eval(M[i]);
          result += -2.44*(pow(mixing_sq[i]/U[i],2));
        }
      }
    }

    // Likelihood contribution from a re-interpretation of CHARM data; assumes tau mixing is dominant. Constrains |U_(tau,i)|^2 at 90% CL in the mass range 10-290 MeV. [Phys. Lett. B, 550(1-2):8-15, 2002 (arXiv:hep-ph/0208075)]
    void lnL_charm_tau(double& result)
    {
      using namespace Pipes::lnL_charm_tau;

      // Mass range of experiment
      static const double low_lim = 0.010685579196217497;  // GeV
      static const double upp_lim = 0.288745725563687;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Ut1;
      mixing_sq[1] = *Dep::Ut2;
      mixing_sq[2] = *Dep::Ut3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/tau.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^4.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2*pi/1.28/1.28);
        }
        else
        {
          U[i] = s.eval(M[i])/sqrt(2);  // Division by sqrt(2) to account for Majorana nature.
          result += Stats::gaussian_upper_limit(pow(mixing_sq[i]/U[i],2), 0, 0, 1/1.28, false); // exp_error = abs(exp_value - 95CL_value), exp_value = 0, 1.28: 90% CL limit for half-Gaussian.
        }
      }
    }

    // Likelihood contribution from LHC (CMS), electron sector; looked at the production and decay chain: pp -> W*(+-) -> l(+-) + nu_r. nu_r then decays into an on-shell W and a lepton; the W decays primarily into a qq pair. Constrains |U_ei|^2 at 95% in the mass range 1-1.2e3 GeV. [arXiv:1802.02965v1]
    void lnL_lhc_e(double& result)
    {
      using namespace Pipes::lnL_lhc_e;

      // Mass range of experiment
      static const double low_lim = 1.0293246;  // GeV
      static const double upp_lim = 1e3;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Ue1;
      mixing_sq[1] = *Dep::Ue2;
      mixing_sq[2] = *Dep::Ue3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/lhc_e.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^4.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2.0*pi/1.64/1.64);
        }
        else
        {
          U[i] = s.eval(M[i]);
          result += Stats::gaussian_upper_limit(pow(mixing_sq[i]/U[i],2), 0, 0, 1/1.64, false);  // exp_error = abs(exp_value - 95CL_value), exp_value = 0, 1.64: 95% CL limit for half-Gaussian.
        }
      }
    }

    // Likelihood contribution from LHC (CMS), muon sector. Constrains |U_(mu,i)|^2 at 95% in the mass range 1-1.2e3 GeV. Description and references above.
    void lnL_lhc_mu(double& result)
    {
      using namespace Pipes::lnL_lhc_mu;

      // Mass range of experiment
      static const double low_lim = 1.0145564;  // GeV
      static const double upp_lim = 985.652549;  // GeV
      std::vector<double> M(3), U(3), mixing_sq(3);

      mixing_sq[0] = *Dep::Um1;
      mixing_sq[1] = *Dep::Um2;
      mixing_sq[2] = *Dep::Um3;
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      static NeutrinoInterpolator s("NeutrinoBit/data/lhc_mu.csv");

      // Assume Gaussian errors with zero mean and that limits scale as |U|^4.
      result = 0;
      for(int i=0; i<3; i++)
      {
        if ( (M[i] < low_lim) or (M[i] > upp_lim) )
        {
          result += -0.5*log(2.0*pi/1.64/1.64);
        }
        else
        {
          U[i] = s.eval(M[i]);
          result += Stats::gaussian_upper_limit(pow(mixing_sq[i]/U[i],2), 0, 0, 1/1.64, false);  // exp_error = abs(exp_value - 95CL_value), exp_value = 0, 1.64: 95% CL limit for half-Gaussian.
        }
      }
    }

    // Squared matrix element |Theta(1,1)|^2
    void Ue1(double& Ue1_sq)
    {
      using namespace Pipes::Ue1;
      Ue1_sq = ((*Dep::SeesawI_Theta).cwiseAbs2())(0,0);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Ue1 = " << Ue1_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Ue1_sq > upper_limit) or (lower_limit != -1 and Ue1_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
    }

    // Phase of matrix element Theta(1,1)
    void Ue1_phase(double& Ue1_p)
    {
      using namespace Pipes::Ue1_phase;
      Ue1_p = std::arg((*Dep::SeesawI_Theta)(0,0));
    }

    // Squared matrix element |Theta(2,1)|^2
    void Um1(double& Um1_sq)
    {
      using namespace Pipes::Um1;
      Um1_sq = (Dep::SeesawI_Theta->cwiseAbs2())(1,0);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Um1 = " << Um1_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Um1_sq > upper_limit) or (lower_limit != -1 and Um1_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(2,1)
    void Um1_phase(double& Um1_p)
    {
      using namespace Pipes::Um1_phase;
      Um1_p = std::arg((*Dep::SeesawI_Theta)(1,0));
    }

    // Squared matrix element |Theta(3,1)|^2
    void Ut1(double& Ut1_sq)
    {
      using namespace Pipes::Ut1;
      Ut1_sq = (Dep::SeesawI_Theta->cwiseAbs2())(2,0);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Ut1 = " << Ut1_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Ut1_sq > upper_limit) or (lower_limit != -1 and Ut1_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(3,1)
    void Ut1_phase(double& Ut1_p)
    {
      using namespace Pipes::Ut1_phase;
      Ut1_p = std::arg((*Dep::SeesawI_Theta)(2,0));
    }

    // Squared matrix element |Theta(1,2)|^2
    void Ue2(double& Ue2_sq)
    {
      using namespace Pipes::Ue2;
      Ue2_sq = (Dep::SeesawI_Theta->cwiseAbs2())(0,1);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Ue2 = " << Ue2_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Ue2_sq > upper_limit) or (lower_limit != -1 and Ue2_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(1,2)
    void Ue2_phase(double& Ue2_p)
    {
      using namespace Pipes::Ue2_phase;
      Ue2_p = std::arg((*Dep::SeesawI_Theta)(0,1));
    }

    // Squared matrix element |Theta(2,2)|^2
    void Um2(double& Um2_sq)
    {
      using namespace Pipes::Um2;
      Um2_sq = (Dep::SeesawI_Theta->cwiseAbs2())(1,1);
 
      #ifdef NEUTRINOBIT_DEBUG
        cout << "Um2 = " << Um2_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Um2_sq > upper_limit) or (lower_limit != -1 and Um2_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(2,2)
    void Um2_phase(double& Um2_p)
    {
      using namespace Pipes::Um2_phase;
      Um2_p = std::arg((*Dep::SeesawI_Theta)(1,1));
    }

    // Squared matrix element |Theta(3,2)|^2
    void Ut2(double& Ut2_sq)
    {
      using namespace Pipes::Ut2;
      Ut2_sq = (Dep::SeesawI_Theta->cwiseAbs2())(2,1);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Ut2 = " << Ut2_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Ut2_sq > upper_limit) or (lower_limit != -1 and Ut2_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(3,2)
    void Ut2_phase(double& Ut2_p)
    {
      using namespace Pipes::Ut2_phase;
      Ut2_p = std::arg((*Dep::SeesawI_Theta)(2,1));
    }

    // Squared matrix element |Theta(1,3)|^2
    void Ue3(double& Ue3_sq)
    {
      using namespace Pipes::Ue3;
      Ue3_sq = (Dep::SeesawI_Theta->cwiseAbs2())(0,2);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Ue3 = " << Ue3_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Ue3_sq > upper_limit) or (lower_limit != -1 and Ue3_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(1,3)
    void Ue3_phase(double& Ue3_p)
    {
      using namespace Pipes::Ue3_phase;
      Ue3_p = std::arg((*Dep::SeesawI_Theta)(0,2));
    }

    // Squared matrix element |Theta(2,3)|^2
    void Um3(double& Um3_sq)
    {
      using namespace Pipes::Um3;
      Um3_sq = (Dep::SeesawI_Theta->cwiseAbs2())(1,2);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Um3 = " << Um3_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Um3_sq > upper_limit) or (lower_limit != -1 and Um3_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(2,3)
    void Um3_phase(double& Um3_p)
    {
      using namespace Pipes::Um3_phase;
      Um3_p = std::arg((*Dep::SeesawI_Theta)(1,2));
    }

    // Squared matrix element |Theta(3,3)|^2
    void Ut3(double& Ut3_sq)
    {
      using namespace Pipes::Ut3;
      Ut3_sq = (Dep::SeesawI_Theta->cwiseAbs2())(2,2);

      #ifdef NEUTRINOBIT_DEBUG
        cout << "Ut3 = " << Ut3_sq << endl;
      #endif

      double upper_limit = runOptions->getValueOrDef<double>(-1, "upper_limit");
      double lower_limit = runOptions->getValueOrDef<double>(-1, "lower_limit");

      if( (upper_limit != -1 and Ut3_sq > upper_limit) or (lower_limit != -1 and Ut3_sq < lower_limit) )
      {
        std::ostringstream msg;
        msg << "Coupling outside of given limits";
        logger() << msg.str() << EOM;
        invalid_point().raise(msg.str());
      }
 
    }

    // Phase of matrix element Theta(3,3)
    void Ut3_phase(double& Ut3_p)
    {
      using namespace Pipes::Ut3_phase;
      Ut3_p = std::arg((*Dep::SeesawI_Theta)(2,2));
    }

    // Step-function log likelihood on the perturbativity of the Yukawas
    void perturbativity_likelihood(double &lnL)
    {
      using namespace Pipes::perturbativity_likelihood;
      SMInputs sminputs = *Dep::SMINPUTS;

      Matrix3d MN;
      MN << *Param["M_1"], 0, 0,
            0, *Param["M_2"], 0,
            0, 0, *Param["M_3"];

      double vev= 1. / sqrt(sqrt(2.)*sminputs.GF);

      // Yukawa coupling |F|^2 from eq 26 in 1502.00477
      Matrix3cd F2 = 1.0/pow(vev,2) * *Dep::SeesawI_Theta * Dep::SeesawI_Theta->adjoint() * MN * MN;
      
      lnL = 0;
      for(int i=0; i<3; i++)
        for(int j=0; j<3; j++)
          if(F2(i,j).real() >= 4*pi) 
          {
            std::ostringstream msg;
            msg << "Yukawas not perturbative; point invalidated.";
            logger() << msg.str() << EOM;
            invalid_point().raise(msg.str());
            return ;
          }
    }

    // Artificial slide likelihood on couplings and masses
    void coupling_slide(double &lnL)
    {
      using namespace Pipes::coupling_slide;
      int I = runOptions->getValueOrDef<int>(1, "I");
      int flavour = runOptions->getValueOrDef<int>(1, "i");
      double slope = runOptions->getValueOrDef<double>(1, "slope");
      double mslope = runOptions->getValueOrDef<double>(0, "mslope");
      std::vector<double> M(3);
      M[0] = *Param["M_1"];
      M[1] = *Param["M_2"];
      M[2] = *Param["M_3"];

      if (flavour > 0)
      {
        double U = (Dep::SeesawI_Theta->cwiseAbs2())(flavour-1,I-1);
        lnL = slope*log10(std::min(U, 1.)) + mslope*log10(M[I-1]);
      }
      else
      {
        double U1 = (Dep::SeesawI_Theta->cwiseAbs2())(0, I-1);
        double U2 = (Dep::SeesawI_Theta->cwiseAbs2())(1, I-1);
        double U3 = (Dep::SeesawI_Theta->cwiseAbs2())(2, I-1);
        lnL = 0;
        lnL += slope*log10(std::min(U1, 1.)) + mslope*log10(M[I-1]);
        lnL += slope*log10(std::min(U2, 1.)) + mslope*log10(M[I-1]);
        lnL += slope*log10(std::min(U3, 1.)) + mslope*log10(M[I-1]);
      }
    }
  }
}

Updated on 2023-06-26 at 21:36:54 +0000