file src/SpecBit_MSSM.cpp

[No description available] More…

Namespaces

Name
Gambit
TODO: see if we can use this one:
Gambit::SpecBit

Detailed Description

Author:

Date:

  • 2014 Sep - Dec, 2015 Jan - Mar
  • 2015 Apr
  • 2016 June
  • 2015, 2016

Functions of module SpecBit

These functions link ModelParameters to Spectrum objects in various ways (by running spectrum generators, etc.)


Authors (add name and date if you modify):


Source code

//   GAMBIT: Global and Modular BSM Inference Tool
//   *********************************************
///  \file
///
///  Functions of module SpecBit
///
///  These functions link ModelParameters to
///  Spectrum objects in various ways (by running
///  spectrum generators, etc.)
///
///  *********************************************
///
///  Authors (add name and date if you modify):
///
///  \author Ben Farmer
///          (benjamin.farmer@fysik.su.se)
///    \date 2014 Sep - Dec, 2015 Jan - Mar
///
///  \author Christopher Rogan
///          (christophersrogan@gmail.com)
///  \date 2015 Apr
///
///  \author Tomas Gonzalo
///          (t.e.gonzalo@fys.uio.no)
///  \date 2016 June
///
///  \author Pat Scott
///          (p.scott@imperial.ac.uk)
///  \date 2015, 2016
///
///  *********************************************

#include <string>
#include <sstream>
#include <cmath>
#include <complex>

#include "gambit/Elements/gambit_module_headers.hpp"
#include "gambit/Elements/spectrum_factories.hpp"
#include "gambit/Elements/smlike_higgs.hpp"
#include "gambit/Elements/slhaea_spec_helpers.hpp"
#include "gambit/Models/SimpleSpectra/MSSMSimpleSpec.hpp"
#include "gambit/Utils/stream_overloads.hpp" // Just for more convenient output to logger
#include "gambit/Utils/util_macros.hpp"
#include "gambit/SpecBit/SpecBit_rollcall.hpp"
#include "gambit/SpecBit/SpecBit_helpers.hpp"
#include "gambit/SpecBit/QedQcdWrapper.hpp"
#include "gambit/SpecBit/MSSMSpec.hpp"
#include "gambit/SpecBit/model_files_and_boxes.hpp" // #includes lots of flexiblesusy headers and defines interface classes
#include "gambit/Printers/printermanager.hpp" // Needed by get_MSSM_spectrum_from_postprocessor to access reader object
#include "gambit/Printers/baseprinter.hpp" // Needed by get_MSSM_spectrum_from_postprocessor to use reader object

// Flexible SUSY stuff (should not be needed by the rest of gambit)
#include "flexiblesusy/src/ew_input.hpp"
#include "flexiblesusy/src/lowe.h" // From softsusy; used by flexiblesusy
#include "flexiblesusy/src/numerics2.hpp"
//#include "flexiblesusy/src/mssm_twoloophiggs.hpp"
#include "flexiblesusy/src/spectrum_generator_settings.hpp"

// Switch for debug mode
//#define SPECBIT_DEBUG

namespace Gambit
{

  namespace SpecBit
  {
    using namespace LogTags;
    using namespace flexiblesusy;

    // To check if a model is currently being scanned:
    // bool Pipes::<fname>::ModelInUse(str model_name)

    /// @{ Non-Gambit convenience functions
    //  =======================================================================
    //  These are not known to Gambit, but they do basically all the real work.
    //  The Gambit module functions merely wrap the functions here and hook
    //  them up to their dependencies, and input parameters.

    /// Check that the spectrum has the canonical LSP for the model being scanned.
    void check_LSP(const Spectrum& spec, const Options& runOptions, bool gravitino_is_canonical_LSP)
    {
      double msqd  = spec.get(Par::Pole_Mass, 1000001, 0);
      double msqu  = spec.get(Par::Pole_Mass, 1000002, 0);
      double msl   = spec.get(Par::Pole_Mass, 1000011, 0);
      double msneu = spec.get(Par::Pole_Mass, 1000012, 0);
      double mglui = spec.get(Par::Pole_Mass, 1000021, 0);
      double mchi0 = std::abs(spec.get(Par::Pole_Mass, 1000022, 0));
      double mchip = std::abs(spec.get(Par::Pole_Mass, 1000024, 0));
      double m_canonical_LSP;

      if (gravitino_is_canonical_LSP)
      {
        if (not runOptions.getValueOrDef<bool>(true, "only_gravitino_LSP")) return;
        m_canonical_LSP = spec.get(Par::Pole_Mass, 1000039, 0);
      }
      else
      {
        if (not runOptions.getValueOrDef<bool>(true, "only_neutralino_LSP")) return;
        m_canonical_LSP = mchi0;
      }

      // Check if the canonical LSP in the MSSM version scanned is actually the LSP for this point.
      if (m_canonical_LSP > mchip ||
          m_canonical_LSP > mglui ||
          m_canonical_LSP > msl   ||
          m_canonical_LSP > msneu ||
          m_canonical_LSP > msqu  ||
          m_canonical_LSP > msqd  ||
          m_canonical_LSP > mchi0   )
       {
         str canonical_LSP = (gravitino_is_canonical_LSP ? "Gravitino" : "Neutralino");
         invalid_point().raise(canonical_LSP + " is not LSP.");
       }
    }
    /// Helper to work with pointer
    void check_LSP(const Spectrum* spec, const Options& runOptions, bool gravitino_model)
    {
      check_LSP(*spec, runOptions, gravitino_model);
    }

    /// Check that the gravitino is actually light, and retrieve its mass
    double get_light_gravitino_mass(const std::map<str, safe_ptr<const double> >& Param, const Options& runOptions)
    {
      static const double gmax = runOptions.getValueOrDef<double>(10.0, "max_permitted_gravitino_mass_GeV");
      double mG = *Param.at("mG");
      if (mG > gmax)
      {
        std::ostringstream msg;
        msg << "Gravitino mass ("<<mG<<" GeV) is greater than permitted in *_lightgravitno models ("<<gmax<<" GeV).\n"
            << "If you know what you are doing(!), this behaiour can be modified with option max_permitted_gravitino_mass_GeV.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }
      return mG;
    }

    /// Add the gravitino mass if it is present (spectrum object version)
    void add_gravitino_mass(Spectrum& spec, const std::map<str, safe_ptr<const double> >& Param, const Options& runOptions)
    {
      spec.get_HE().set_override(Par::Pole_Mass, get_light_gravitino_mass(Param,runOptions), "~G", true);
    }

    /// Add the gravitino mass if it is present (SLHAea version)
    void add_gravitino_mass(SLHAstruct& slha, const std::map<str, safe_ptr<const double> >& Param, const Options& runOptions)
    {
      slha["MASS"][""] << 1000039 << get_light_gravitino_mass(Param,runOptions) << "# ~G";
    }

    /// Add the gravitino mass to a spectrum object if it is present in an SLHAea object
    void add_gravitino_mass_from_slhaea(Spectrum& spec, const SLHAstruct& input_slha)
    {
      SLHAstruct::const_iterator block = input_slha.find("MASS");
      const std::vector<std::string> key(1, "1000039");
      if(block != input_slha.end() and block->find(key) != block->end())
      {
        spec.get_HE().set_override(Par::Pole_Mass, SLHAea::to<double>(input_slha.at("MASS").at(1).at(1000039)), "~G", true);
      }
    }


    /// Compute an MSSM spectrum using flexiblesusy
    // In GAMBIT there are THREE flexiblesusy MSSM spectrum generators currently in
    // use, for each of three possible boundary condition types:
    //   - GUT scale input
    //   - Electroweak symmetry breaking scale input
    //   - Intermediate scale Q input
    // These each require slightly different setup, but once that is done the rest
    // of the code required to run them is the same; this is what is contained in
    // the below template function.
    // MI for Model Interface, as defined in model_files_and_boxes.hpp
    template <class MI>
    Spectrum run_FS_spectrum_generator
        ( const typename MI::InputParameters& input
        , const SMInputs& sminputs
        , const Options& runOptions
        , const std::map<str, safe_ptr<const double> >& input_Param
        , bool gravitino_model
        )
    {
      // SoftSUSY object used to set quark and lepton masses and gauge
      // couplings in QEDxQCD effective theory
      // Will be initialised by default using values in lowe.h, which we will
      // override next.
      softsusy::QedQcd oneset;

      // Fill QedQcd object with SMInputs values
      setup_QedQcd(oneset,sminputs);

      // Run everything to Mz
      //oneset.toMz();

      // Create spectrum generator object
      typename MI::SpectrumGenerator spectrum_generator;

      // Spectrum generator settings
      // Default options copied from flexiblesusy/src/spectrum_generator_settings.hpp
      //
      // | enum                             | possible values              | default value   |
      // |----------------------------------|------------------------------|-----------------|
      // | precision                        | any positive double          | 1.0e-4          |
      // | max_iterations                   | any positive double          | 0 (= automatic) |
      // | algorithm                        | 0 (two-scale) or 1 (lattice) | 0 (= two-scale) |
      // | calculate_sm_masses              | 0 (no) or 1 (yes)            | 0 (= no)        |
      // | pole_mass_loop_order             | 0, 1, 2                      | 2 (= 2-loop)    |
      // | ewsb_loop_order                  | 0, 1, 2                      | 2 (= 2-loop)    |
      // | beta_loop_order                  | 0, 1, 2                      | 2 (= 2-loop)    |
      // | threshold_corrections_loop_order | 0, 1                         | 1 (= 1-loop)    |
      // | higgs_2loop_correction_at_as     | 0, 1                         | 1 (= enabled)   |
      // | higgs_2loop_correction_ab_as     | 0, 1                         | 1 (= enabled)   |
      // | higgs_2loop_correction_at_at     | 0, 1                         | 1 (= enabled)   |
      // | higgs_2loop_correction_atau_atau | 0, 1                         | 1 (= enabled)   |

      Spectrum_generator_settings settings;
      settings.set(Spectrum_generator_settings::precision, runOptions.getValueOrDef<double>(1.0e-4,"precision_goal"));
      settings.set(Spectrum_generator_settings::max_iterations, runOptions.getValueOrDef<double>(0,"max_iterations"));
      settings.set(Spectrum_generator_settings::calculate_sm_masses, runOptions.getValueOrDef<bool> (false, "calculate_sm_masses"));
      settings.set(Spectrum_generator_settings::pole_mass_loop_order, runOptions.getValueOrDef<int>(2,"pole_mass_loop_order"));
      settings.set(Spectrum_generator_settings::pole_mass_loop_order, runOptions.getValueOrDef<int>(2,"ewsb_loop_order"));
      settings.set(Spectrum_generator_settings::beta_loop_order, runOptions.getValueOrDef<int>(2,"beta_loop_order"));
      settings.set(Spectrum_generator_settings::threshold_corrections_loop_order, runOptions.getValueOrDef<int>(2,"threshold_corrections_loop_order"));
      settings.set(Spectrum_generator_settings::higgs_2loop_correction_at_as, runOptions.getValueOrDef<int>(1,"higgs_2loop_correction_at_as"));
      settings.set(Spectrum_generator_settings::higgs_2loop_correction_ab_as, runOptions.getValueOrDef<int>(1,"higgs_2loop_correction_ab_as"));
      settings.set(Spectrum_generator_settings::higgs_2loop_correction_at_at, runOptions.getValueOrDef<int>(1,"higgs_2loop_correction_at_at"));
      settings.set(Spectrum_generator_settings::higgs_2loop_correction_atau_atau, runOptions.getValueOrDef<int>(1,"higgs_2loop_correction_atau_atau"));
      settings.set(Spectrum_generator_settings::top_pole_qcd_corrections, runOptions.getValueOrDef<int>(1,"top_pole_qcd_corrections"));
      settings.set(Spectrum_generator_settings::beta_zero_threshold, runOptions.getValueOrDef<double>(1.000000000e-14,"beta_zero_threshold"));
      settings.set(Spectrum_generator_settings::eft_matching_loop_order_up, runOptions.getValueOrDef<int>(1,"eft_matching_loop_order_up"));
      settings.set(Spectrum_generator_settings::eft_matching_loop_order_down, runOptions.getValueOrDef<int>(1,"eft_matching_loop_order_down"));
      settings.set(Spectrum_generator_settings::threshold_corrections, runOptions.getValueOrDef<int>(123111321,"threshold_corrections"));

      spectrum_generator.set_settings(settings);

      // Generate spectrum
      spectrum_generator.run(oneset, input);

      // Extract report on problems...
      const typename MI::Problems& problems = spectrum_generator.get_problems();

      // Create Model_interface to carry the input and results, and know
      // how to access the flexiblesusy routines.
      // Note: Output of spectrum_generator.get_model() returns type, e.g. CMSSM.
      // Need to convert it to type CMSSM_slha (which alters some conventions of
      // parameters into SLHA format)
      MI model_interface(spectrum_generator,oneset,input);

      // Create SubSpectrum object to wrap flexiblesusy data
      // THIS IS STATIC so that it lives on once we leave this module function. We
      // therefore cannot run the same spectrum generator twice in the same loop and
      // maintain the spectrum resulting from both. But we should never want to do
      // this.
      // A pointer to this object is what gets turned into a SubSpectrum pointer and
      // passed around Gambit.
      //
      // This object will COPY the interface data members into itself, so it is now the
      // one-stop-shop for all spectrum information, including the model interface object.
      MSSMSpec<MI> mssmspec(model_interface, "FlexibleSUSY", "2.0.beta");

      // Add extra information about the scales used to the wrapper object
      // (last parameter turns on the 'allow_new' option for the override setter, which allows
      //  us to set parameters that don't previously exist)
      mssmspec.set_override(Par::mass1,spectrum_generator.get_high_scale(),"high_scale",true);
      mssmspec.set_override(Par::mass1,spectrum_generator.get_susy_scale(),"susy_scale",true);
      mssmspec.set_override(Par::mass1,spectrum_generator.get_low_scale(), "low_scale", true);


      // Has the user chosen to override any pole mass values?
      // This will typically break consistency, but may be useful in some special cases
      if (runOptions.hasKey("override_FS_pole_masses"))
      {
        std::vector<str> particle_names = runOptions.getNames("override_FS_pole_masses");
        for (auto& name : particle_names)
        {
          double mass = runOptions.getValue<double>("override_FS_pole_masses", name);
          mssmspec.set_override(Par::Pole_Mass, mass, name);
        }
      }

      // Add theory errors
      static const MSSM_strs ms;

      static const std::vector<int> i12     = initVector(1,2);
      static const std::vector<int> i123    = initVector(1,2,3);
      static const std::vector<int> i1234   = initVector(1,2,3,4);
      static const std::vector<int> i123456 = initVector(1,2,3,4,5,6);

      // 3% theory "error"
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_pred, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_pred, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_strs_1_6, i123456, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_strs_1_6, i123456, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, "~chi0", i1234, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, "~chi0", i1234, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_strs_1_3, i123, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_strs_1_3, i123, true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_strs_1_2, i12,  true);
      mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_strs_1_2, i12,  true);

      // Do the lightest Higgs mass separately.  The default in most codes is 3 GeV. That seems like
      // an underestimate if the stop masses are heavy enough, but an overestimate for most points.
      double rd_mh1 = 2.0 / mssmspec.get(Par::Pole_Mass, ms.h0, 1);
      mssmspec.set_override(Par::Pole_Mass_1srd_high, rd_mh1, ms.h0, 1, true);
      mssmspec.set_override(Par::Pole_Mass_1srd_low,  rd_mh1, ms.h0, 1, true);

      // Do the W mass separately.  Here we use 10 MeV based on the size of corrections from two-loop papers and advice from Dominik Stockinger.
      double rd_mW = 0.01 / mssmspec.get(Par::Pole_Mass, "W+");
      mssmspec.set_override(Par::Pole_Mass_1srd_high, rd_mW, "W+", true);
      mssmspec.set_override(Par::Pole_Mass_1srd_low,  rd_mW, "W+", true);

      // Save the input value of TanBeta
      // Probably need to make it a full requirement of the MSSM SpectrumContents
      if(input_Param.find("TanBeta") != input_Param.end())
      {
        mssmspec.set_override(Par::dimensionless, *input_Param.at("TanBeta"), "tanbeta(mZ)", true);
      }

      // Create a second SubSpectrum object to wrap the qedqcd object used to initialise the spectrum generator
      // Attach the sminputs object as well, so that SM pole masses can be passed on (these aren't easily
      // extracted from the QedQcd object, so use the values that we put into it.)
      QedQcdWrapper qedqcdspec(oneset,sminputs);

      // Deal with points where spectrum generator encountered a problem
      #ifdef SPECBIT_DEBUG
        std::cout<<"Problem? "<<problems.have_problem()<<std::endl;
      #endif
      if( problems.have_problem() )
      {
         if( runOptions.getValueOrDef<bool>(false,"invalid_point_fatal") )
         {
            ///TODO: Need to tell gambit that the spectrum is not viable somehow. For now
            /// just die.
            std::ostringstream errmsg;
            errmsg << "A serious problem was encountered during spectrum generation!; ";
            errmsg << "Message from FlexibleSUSY below:" << std::endl;
            problems.print_problems(errmsg);
            problems.print_warnings(errmsg);
            SpecBit_error().raise(LOCAL_INFO,errmsg.str());
         }
         else
         {
            /// Check what the problem was
            /// see: contrib/MassSpectra/flexiblesusy/src/problems.hpp
            std::ostringstream msg;
            //msg << "";
            //if( have_bad_mass()      ) msg << "bad mass " << std::endl; // TODO: check which one
            //if( have_tachyon()       ) msg << "tachyon" << std::endl;
            //if( have_thrown()        ) msg << "error" << std::endl;
            //if( have_non_perturbative_parameter()   ) msg << "non-perturb. param" << std::endl; // TODO: check which
            //if( have_failed_pole_mass_convergence() ) msg << "fail pole mass converg." << std::endl; // TODO: check which
            //if( no_ewsb()            ) msg << "no ewsb" << std::endl;
            //if( no_convergence()     ) msg << "no converg." << std::endl;
            //if( no_perturbative()    ) msg << "no pertub." << std::endl;
            //if( no_rho_convergence() ) msg << "no rho converg." << std::endl;
            //if( msg.str()=="" ) msg << " Unrecognised problem! ";

            /// Fast way for now:
            problems.print_problems(msg);
            invalid_point().raise(msg.str()); //TODO: This message isn't ending up in the logs.
         }
      }

      if( problems.have_warning() )
      {
         std::ostringstream msg;
         problems.print_warnings(msg);
         SpecBit_warning().raise(LOCAL_INFO,msg.str()); //TODO: Is a warning the correct thing to do here?
      }

      // Write SLHA file (for debugging purposes...)
      #ifdef SPECBIT_DEBUG
         typename MI::SlhaIo slha_io;
         slha_io.set_spinfo(problems);
         slha_io.set_sminputs(oneset);
         slha_io.set_minpar(input);
         slha_io.set_extpar(input);
         slha_io.set_spectrum(mssmspec.model_interface.model);
         slha_io.write_to_file("SpecBit/initial_CMSSM_spectrum->slha");
      #endif

      // Retrieve any mass cuts
      static const Spectrum::mc_info mass_cut = runOptions.getValueOrDef<Spectrum::mc_info>(Spectrum::mc_info(), "mass_cut");
      static const Spectrum::mr_info mass_ratio_cut = runOptions.getValueOrDef<Spectrum::mr_info>(Spectrum::mr_info(), "mass_ratio_cut");

      // Package QedQcd SubSpectrum object, MSSM SubSpectrum object, and SMInputs struct into a 'full' Spectrum object
      Spectrum spec(qedqcdspec,mssmspec,sminputs,&input_Param,mass_cut,mass_ratio_cut);

      // Add the gravitino mass if it is present
      if (gravitino_model) add_gravitino_mass(spec, input_Param, runOptions);

      // Make sure that LSP conditions are respected
      check_LSP(spec, runOptions, gravitino_model);

      return spec;
    }

  //Version for 1.5.1 commented out because we should make it possible to support FS versions in parallel.

  // template <class MI>
  //   Spectrum run_FS1_5_1_spectrum_generator
  //       ( const typename MI::InputParameters& input
  //       , const SMInputs& sminputs
  //       , const Options& runOptions
  //       , const std::map<str, safe_ptr<const double> >& input_Param
  //       )
  //   {
  //     // SoftSUSY object used to set quark and lepton masses and gauge
  //     // couplings in QEDxQCD effective theory
  //     // Will be initialised by default using values in lowe.h, which we will
  //     // override next.
  //     softsusy::QedQcd oneset;

  //     // Fill QedQcd object with SMInputs values
  //     setup_QedQcd(oneset,sminputs);

  //     // Run everything to Mz
  //     oneset.toMz();

  //     // Create spectrum generator object
  //     typename MI::SpectrumGenerator spectrum_generator;

  //     // Spectrum generator settings
  //     // Default options copied from flexiblesusy/src/spectrum_generator_settings.hpp
  //     //
  //     // | enum                             | possible values              | default value   |
  //     // |----------------------------------|------------------------------|-----------------|
  //     // | precision                        | any positive double          | 1.0e-4          |
  //     // | max_iterations                   | any positive double          | 0 (= automatic) |
  //     // | algorithm                        | 0 (two-scale) or 1 (lattice) | 0 (= two-scale) |
  //     // | calculate_sm_masses              | 0 (no) or 1 (yes)            | 0 (= no)        |
  //     // | pole_mass_loop_order             | 0, 1, 2                      | 2 (= 2-loop)    |
  //     // | ewsb_loop_order                  | 0, 1, 2                      | 2 (= 2-loop)    |
  //     // | beta_loop_order                  | 0, 1, 2                      | 2 (= 2-loop)    |
  //     // | threshold_corrections_loop_order | 0, 1                         | 1 (= 1-loop)    |
  //     // | higgs_2loop_correction_at_as     | 0, 1                         | 1 (= enabled)   |
  //     // | higgs_2loop_correction_ab_as     | 0, 1                         | 1 (= enabled)   |
  //     // | higgs_2loop_correction_at_at     | 0, 1                         | 1 (= enabled)   |
  //     // | higgs_2loop_correction_atau_atau | 0, 1                         | 1 (= enabled)   |

  //     spectrum_generator.set_precision_goal                  (runOptions.getValueOrDef<double>(1.0e-4,"precision_goal"));
  //     spectrum_generator.set_max_iterations                  (runOptions.getValueOrDef<double>(0,     "max_iterations"));
  //     spectrum_generator.set_calculate_sm_masses             (runOptions.getValueOrDef<bool>  (false, "calculate_sm_masses"));
  //     spectrum_generator.set_pole_mass_loop_order            (runOptions.getValueOrDef<int>   (2,     "pole_mass_loop_order"));
  //     spectrum_generator.set_ewsb_loop_order                 (runOptions.getValueOrDef<int>   (2,     "ewsb_loop_order"));
  //     spectrum_generator.set_beta_loop_order                 (runOptions.getValueOrDef<int>   (2,     "beta_loop_order"));
  //     spectrum_generator.set_threshold_corrections_loop_order(runOptions.getValueOrDef<int>   (2,     "threshold_corrections_loop_order"));

  //     // Higgs loop corrections are a little different... sort them out now
  //     Two_loop_corrections two_loop_settings;

  //     // alpha_t alpha_s
  //     // alpha_b alpha_s
  //     // alpha_t^2 + alpha_t alpha_b + alpha_b^2
  //     // alpha_tau^2
  //     two_loop_settings.higgs_at_as = runOptions.getValueOrDef<bool>(true,"use_higgs_2loop_at_as");
  //     two_loop_settings.higgs_ab_as = runOptions.getValueOrDef<bool>(true,"use_higgs_2loop_ab_as");
  //     two_loop_settings.higgs_at_at = runOptions.getValueOrDef<bool>(true,"use_higgs_2loop_at_at");
  //     two_loop_settings.higgs_atau_atau = runOptions.getValueOrDef<bool>(true,"use_higgs_2loop_atau_atau");

  //     spectrum_generator.set_two_loop_corrections(two_loop_settings);

  //     // Generate spectrum
  //     spectrum_generator.run(oneset, input);

  //     // Extract report on problems...
  //     const typename MI::Problems& problems = spectrum_generator.get_problems();

  //     // Create Model_interface to carry the input and results, and know
  //     // how to access the flexiblesusy routines.
  //     // Note: Output of spectrum_generator.get_model() returns type, e.g. CMSSM.
  //     // Need to convert it to type CMSSM_slha (which alters some conventions of
  //     // parameters into SLHA format)
  //     MI model_interface(spectrum_generator,oneset,input);

  //     // Create SubSpectrum object to wrap flexiblesusy data
  //     // THIS IS STATIC so that it lives on once we leave this module function. We
  //     // therefore cannot run the same spectrum generator twice in the same loop and
  //     // maintain the spectrum resulting from both. But we should never want to do
  //     // this.
  //     // A pointer to this object is what gets turned into a SubSpectrum pointer and
  //     // passed around Gambit.
  //     //
  //     // This object will COPY the interface data members into itself, so it is now the
  //     // one-stop-shop for all spectrum information, including the model interface object.
  //     MSSMSpec<MI> mssmspec(model_interface, "FlexibleSUSY", "1.5.1");

  //     // Add extra information about the scales used to the wrapper object
  //     // (last parameter turns on the 'allow_new' option for the override setter, which allows
  //     //  us to set parameters that don't previously exist)
  //     mssmspec.set_override(Par::mass1,spectrum_generator.get_high_scale(),"high_scale",true);
  //     mssmspec.set_override(Par::mass1,spectrum_generator.get_susy_scale(),"susy_scale",true);
  //     mssmspec.set_override(Par::mass1,spectrum_generator.get_low_scale(), "low_scale", true);

  //     // Add theory errors
  //     static const MSSM_strs ms;

  //     static const std::vector<int> i12     = initVector(1,2);
  //     static const std::vector<int> i123    = initVector(1,2,3);
  //     static const std::vector<int> i1234   = initVector(1,2,3,4);
  //     static const std::vector<int> i123456 = initVector(1,2,3,4,5,6);

  //     // 3% theory "error"
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_pred, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_pred, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_strs_1_6, i123456, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_strs_1_6, i123456, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, "~chi0", i1234, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, "~chi0", i1234, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_strs_1_3, i123, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_strs_1_3, i123, true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_high, 0.03, ms.pole_mass_strs_1_2, i12,  true);
  //     mssmspec.set_override_vector(Par::Pole_Mass_1srd_low,  0.03, ms.pole_mass_strs_1_2, i12,  true);

  //     // Do the lightest Higgs mass separately.  The default in most codes is 3 GeV. That seems like
  //     // an underestimate if the stop masses are heavy enough, but an overestimate for most points.
  //     double rd_mh1 = 2.0 / mssmspec.get(Par::Pole_Mass, ms.h0, 1);
  //     mssmspec.set_override(Par::Pole_Mass_1srd_high, rd_mh1, ms.h0, 1, true);
  //     mssmspec.set_override(Par::Pole_Mass_1srd_low,  rd_mh1, ms.h0, 1, true);

  //     // Do the W mass separately.  Here we use 10 MeV based on the size of corrections from two-loop papers and advice from Dominik Stockinger.
  //     double rd_mW = 0.01 / mssmspec.get(Par::Pole_Mass, "W+");
  //     mssmspec.set_override(Par::Pole_Mass_1srd_high, rd_mW, "W+", true);
  //     mssmspec.set_override(Par::Pole_Mass_1srd_low,  rd_mW, "W+", true);

  //     // Save the input value of TanBeta
  //     // Probably need to make it a full requirement of the MSSM SpectrumContents
  //     if(input_Param.find("TanBeta") != input_Param.end())
  //     {
  //       mssmspec.set_override(Par::dimensionless, *input_Param.at("TanBeta"), "tanbeta(mZ)", true);
  //     }

  //     // Create a second SubSpectrum object to wrap the qedqcd object used to initialise the spectrum generator
  //     // Attach the sminputs object as well, so that SM pole masses can be passed on (these aren't easily
  //     // extracted from the QedQcd object, so use the values that we put into it.)
  //     QedQcdWrapper qedqcdspec(oneset,sminputs);

  //     // Deal with points where spectrum generator encountered a problem
  //     #ifdef SPECBIT_DEBUG
  //       std::cout<<"Problem? "<<problems.have_problem()<<std::endl;
  //     #endif
  //     if( problems.have_problem() )
  //     {
  //        if( runOptions.getValueOrDef<bool>(false,"invalid_point_fatal") )
  //        {
  //           ///TODO: Need to tell gambit that the spectrum is not viable somehow. For now
  //           /// just die.
  //           std::ostringstream errmsg;
  //           errmsg << "A serious problem was encountered during spectrum generation!; ";
  //           errmsg << "Message from FlexibleSUSY below:" << std::endl;
  //           problems.print_problems(errmsg);
  //           problems.print_warnings(errmsg);
  //           SpecBit_error().raise(LOCAL_INFO,errmsg.str());
  //        }
  //        else
  //        {
  //           /// Check what the problem was
  //           /// see: contrib/MassSpectra/flexiblesusy/src/problems.hpp
  //           std::ostringstream msg;
  //           //msg << "";
  //           //if( have_bad_mass()      ) msg << "bad mass " << std::endl; // TODO: check which one
  //           //if( have_tachyon()       ) msg << "tachyon" << std::endl;
  //           //if( have_thrown()        ) msg << "error" << std::endl;
  //           //if( have_non_perturbative_parameter()   ) msg << "non-perturb. param" << std::endl; // TODO: check which
  //           //if( have_failed_pole_mass_convergence() ) msg << "fail pole mass converg." << std::endl; // TODO: check which
  //           //if( no_ewsb()            ) msg << "no ewsb" << std::endl;
  //           //if( no_convergence()     ) msg << "no converg." << std::endl;
  //           //if( no_perturbative()    ) msg << "no pertub." << std::endl;
  //           //if( no_rho_convergence() ) msg << "no rho converg." << std::endl;
  //           //if( msg.str()=="" ) msg << " Unrecognised problem! ";

  //           /// Fast way for now:
  //           problems.print_problems(msg);
  //           invalid_point().raise(msg.str()); //TODO: This message isn't ending up in the logs.
  //        }
  //     }

  //     if( problems.have_warning() )
  //     {
  //        std::ostringstream msg;
  //        problems.print_warnings(msg);
  //        SpecBit_warning().raise(LOCAL_INFO,msg.str()); //TODO: Is a warning the correct thing to do here?
  //     }

  //     // Write SLHA file (for debugging purposes...)
  //     #ifdef SPECBIT_DEBUG
  //        typename MI::SlhaIo slha_io;
  //        slha_io.set_spinfo(problems);
  //        slha_io.set_sminputs(oneset);
  //        slha_io.set_minpar(input);
  //        slha_io.set_extpar(input);
  //        slha_io.set_spectrum(mssmspec.model_interface.model);
  //        slha_io.write_to_file("SpecBit/initial_CMSSM_spectrum->slha");
  //     #endif

  //     // Retrieve any mass cuts
  //     static const Spectrum::mc_info mass_cut = runOptions.getValueOrDef<Spectrum::mc_info>(Spectrum::mc_info(), "mass_cut");
  //     static const Spectrum::mr_info mass_ratio_cut = runOptions.getValueOrDef<Spectrum::mr_info>(Spectrum::mr_info(), "mass_ratio_cut");

  //     // Package QedQcd SubSpectrum object, MSSM SubSpectrum object, and SMInputs struct into a 'full' Spectrum object
  //     return Spectrum(qedqcdspec,mssmspec,sminputs,&input_Param,mass_cut,mass_ratio_cut);
  //   }

    /// Helper function for setting 3x3 matrix-valued parameters
    //  Names must conform to convention "<parname>_ij"
    Eigen::Matrix<double,3,3> fill_3x3_parameter_matrix(const std::string& rootname, const std::map<str, safe_ptr<const double> >& Param)
    {
      Eigen::Matrix<double,3,3> output;
      for(int i=0; i<3; ++i) for(int j=0; j<3; ++j)
      {
        output(i,j) = *Param.at(rootname + "_" + std::to_string(i+1) + std::to_string(j+1));
      }
      return output;
    }

    /// As above, but for symmetric input (i.e. 6 entries, assumed to be the upper triangle)
    Eigen::Matrix<double,3,3> fill_3x3_symmetric_parameter_matrix(const std::string& rootname, const std::map<str, safe_ptr<const double> >& Param)
    {
      Eigen::Matrix<double,3,3> output;
      for(int i=0; i<3; ++i) for(int j=0; j<3; ++j)
      {
        str parname = rootname + "_" + ( i < j ? std::to_string(i+1) + std::to_string(j+1) : std::to_string(j+1) + std::to_string(i+1));
        output(i,j) = *Param.at(parname);
      }
      return output;
    }

    /// Helper function for filling MSSM63-compatible input parameter objects
    /// Leaves out mHu2, mHd2, SignMu, (mA, mu) because we use two different parameterisations of these
    template <class T>
    void fill_MSSM63_input(T& input, const std::map<str, safe_ptr<const double> >& Param )
    {
      //double valued parameters
      input.TanBeta     = *Param.at("TanBeta");
      input.MassBInput  = *Param.at("M1");
      input.MassWBInput = *Param.at("M2");
      input.MassGInput  = *Param.at("M3");

      // Sanity checks
      if(input.TanBeta<0)
      {
        std::ostringstream msg;
        msg << "Tried to set TanBeta parameter to a negative value ("<<input.TanBeta<<")! This parameter must be positive. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }

      //3x3 matrices; filled with the help of a convenience function
      input.mq2Input = fill_3x3_symmetric_parameter_matrix("mq2", Param);
      input.ml2Input = fill_3x3_symmetric_parameter_matrix("ml2", Param);
      input.md2Input = fill_3x3_symmetric_parameter_matrix("md2", Param);
      input.mu2Input = fill_3x3_symmetric_parameter_matrix("mu2", Param);
      input.me2Input = fill_3x3_symmetric_parameter_matrix("me2", Param);
      input.Aeij = fill_3x3_parameter_matrix("Ae", Param);
      input.Adij = fill_3x3_parameter_matrix("Ad", Param);
      input.Auij = fill_3x3_parameter_matrix("Au", Param);

      #ifdef SPECBIT_DEBUG
        #define INPUT(p) input.p
        #define ostr std::cout
        #define oend std::endl
        ostr << "TanBeta = " << INPUT(TanBeta) << ", " << oend ;
        ostr << "mq2Input = " << oend << INPUT(mq2Input) << ", " << oend;
        ostr << "ml2Input = " << oend << INPUT(ml2Input) << ", " << oend;
        ostr << "md2Input = " << oend << INPUT(md2Input) << ", " << oend;
        ostr << "mu2Input = " << oend << INPUT(mu2Input) << ", " << oend;
        ostr << "me2Input = " << oend << INPUT(me2Input) << ", " << oend;
        ostr << "MassBInput = " << INPUT(MassBInput) << ", " << oend;
        ostr << "MassWBInput = " << INPUT(MassWBInput) << ", " << oend;
        ostr << "MassGInput = " << INPUT(MassGInput) << ", " << oend;
        ostr << "Aeij = " << oend << INPUT(Aeij) << ", " << oend;
        ostr << "Adij = " << oend << INPUT(Adij) << ", " << oend;
        ostr << "Auij = " << oend << INPUT(Auij) << ", " << oend;
        #undef INPUT
        #undef ostr
        #undef oend
      #endif
    }


    // Similar to above, except this is for MSSMEFTHiggs spectrum
    // generator and a few others where different names for inputs for many
    // parameters are used. This should be standardised.

    template <class T>
    void fill_MSSM63_input_altnames(T& input, const std::map<str, safe_ptr<const double> >& Param )
    {
      //double valued parameters
      input.TanBeta     = *Param.at("TanBeta");
      input.M1Input  = *Param.at("M1");
      input.M2Input = *Param.at("M2");
      input.M3Input  = *Param.at("M3");

      // Sanity checks
      if(input.TanBeta<0)
      {
        std::ostringstream msg;
        msg << "Tried to set TanBeta parameter to a negative value ("<<input.TanBeta<<")! This parameter must be positive. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }

      //3x3 matrices; filled with the help of a convenience function
      input.mq2Input = fill_3x3_symmetric_parameter_matrix("mq2", Param);
      input.ml2Input = fill_3x3_symmetric_parameter_matrix("ml2", Param);
      input.md2Input = fill_3x3_symmetric_parameter_matrix("md2", Param);
      input.mu2Input = fill_3x3_symmetric_parameter_matrix("mu2", Param);
      input.me2Input = fill_3x3_symmetric_parameter_matrix("me2", Param);
      input.AeInput = fill_3x3_parameter_matrix("Ae", Param);
      input.AdInput= fill_3x3_parameter_matrix("Ad", Param);
      input.AuInput = fill_3x3_parameter_matrix("Au", Param);

      #ifdef SPECBIT_DEBUG
        #define INPUT(p) input.p
        #define ostr std::cout
        #define oend std::endl
        ostr << "TanBeta = " << INPUT(TanBeta) << ", " << oend ;
        ostr << "mq2Input = " << oend << INPUT(mq2Input) << ", " << oend;
        ostr << "ml2Input = " << oend << INPUT(ml2Input) << ", " << oend;
        ostr << "md2Input = " << oend << INPUT(md2Input) << ", " << oend;
        ostr << "mu2Input = " << oend << INPUT(mu2Input) << ", " << oend;
        ostr << "me2Input = " << oend << INPUT(me2Input) << ", " << oend;
        ostr << "M1Input = " << INPUT(M1Input) << ", " << oend;
        ostr << "M2Input = " << INPUT(M2Input) << ", " << oend;
        ostr << "M3Input = " << INPUT(M3Input) << ", " << oend;
        ostr << "AeInput = " << oend << INPUT(AeInput) << ", " << oend;
        ostr << "AdInput = " << oend << INPUT(AdInput) << ", " << oend;
        ostr << "AuInput = " << oend << INPUT(AuInput) << ", " << oend;
        #undef INPUT
        #undef ostr
        #undef oend
      #endif

    }


    /// @} End module convenience functions


    /// @{ Gambit module functions
    //  =======================================================================
    //  These are wrapped up in Gambit functor objects according to the
    //  instructions in the rollcall header

    // Functions to change the capability associated with a Spectrum object to "SM_spectrum"
    void convert_MSSM_to_SM   (Spectrum &result) {result = *Pipes::convert_MSSM_to_SM::Dep::MSSM_spectrum;}
    void convert_NMSSM_to_SM  (Spectrum &result) {result = *Pipes::convert_NMSSM_to_SM::Dep::NMSSM_spectrum;}
    void convert_E6MSSM_to_SM (Spectrum &result) {result = *Pipes::convert_E6MSSM_to_SM::Dep::E6MSSM_spectrum;}

    void get_MSSM_spectrum_SPheno (Spectrum& spectrum)
    {
      namespace myPipe = Pipes::get_MSSM_spectrum_SPheno;
      const SMInputs &sminputs = *myPipe::Dep::SMINPUTS;

      // Set up the input structure
      Finputs inputs;
      inputs.sminputs = sminputs;
      inputs.param = myPipe::Param;
      inputs.options = myPipe::runOptions;

      // Retrieve any mass cuts
      static const Spectrum::mc_info mass_cut = myPipe::runOptions->getValueOrDef<Spectrum::mc_info>(Spectrum::mc_info(), "mass_cut");
      static const Spectrum::mr_info mass_ratio_cut = myPipe::runOptions->getValueOrDef<Spectrum::mr_info>(Spectrum::mr_info(), "mass_ratio_cut");

      // Get the spectrum from the Backend
      myPipe::BEreq::SPheno_MSSMspectrum(spectrum, inputs);

      // Drop SLHA files if requested
      spectrum.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }


    // Runs FlexibleSUSY MSSMEFTHiggs model spectrum generator with SUSY
    // scale boundary conditions, ie accepts MSSM parameters at MSUSY,
    // and has DRbar mA and mu as an input and mHu2 and mHd2 as EWSB
    // outputs, so it is for the MSSMatMSUSY_mA model.
    #if(FS_MODEL_MSSMatMSUSYEFTHiggs_mAmu_IS_BUILT)
    void get_MSSMatMSUSY_mA_spectrum_FlexibleEFTHiggs (Spectrum& result)
    {
      // Access the pipes for this function to get model and parameter information
      namespace myPipe = Pipes::get_MSSMatMSUSY_mA_spectrum_FlexibleEFTHiggs;

      // Get SLHA2 SMINPUTS values
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;

      // Get input parameters (from flexiblesusy namespace)
      MSSMatMSUSYEFTHiggs_mAmu_input_parameters input;
      input.MuInput  = *myPipe::Param.at("mu");
      // This FS spectrum generator has mA as the parameter
      input.mAInput = *myPipe::Param.at("mA");
      fill_MSSM63_input_altnames(input,myPipe::Param); // Fill the rest
      result = run_FS_spectrum_generator<MSSMatMSUSYEFTHiggs_mAmu_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atMSUSY_mA_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }
    #endif

    // Runs FlexibleSUSY MSSMEFTHiggs model spectrum generator
    // and has m3^2 and mu as EWSB outputs, so it is for the
    // MSSMatQ_model.
    #if(FS_MODEL_MSSMEFTHiggs_IS_BUILT)
    void get_MSSMatQ_spectrum_FlexibleEFTHiggs (Spectrum& result)
    {
      // Access the pipes for this function to get model and parameter information
      namespace myPipe = Pipes::get_MSSMatQ_spectrum_FlexibleEFTHiggs;

      // Get SLHA2 SMINPUTS values
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;

      // Get input parameters (from flexiblesusy namespace)
      MSSMEFTHiggs_input_parameters input;
      // MSSMatQ also requires input scale to be supplied with name MSUSY
      input.MSUSY  = *myPipe::Param.at("Qin");
      input.mHu2IN = *myPipe::Param.at("mHu2");
      input.mHd2IN = *myPipe::Param.at("mHd2");
      input.SignMu = *myPipe::Param.at("SignMu");
      fill_MSSM63_input_altnames(input,myPipe::Param); // Fill the rest
      result = run_FS_spectrum_generator<MSSMEFTHiggs_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atQ_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }
    #endif

    // Runs FlexibleSUSY MSSMEFTHiggs_mAmu spectrum generator with
    // boundary conditions at a user specified scale, ie accepts MSSM
    // parameters at Q, and has DRbar mA and mu as an input and mHu2
    // and mHd2 as EWSB outputs, so it is for the MSSMatMSUSY_mA model.
    #if(FS_MODEL_MSSMEFTHiggs_mAmu_IS_BUILT)
    void get_MSSMatQ_mA_spectrum_FlexibleEFTHiggs (Spectrum& result)
    {
      // Access the pipes for this function to get model and parameter information
      namespace myPipe = Pipes::get_MSSMatQ_mA_spectrum_FlexibleEFTHiggs;

      // Get SLHA2 SMINPUTS values
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;

      // Get input parameters (from flexiblesusy namespace)
      MSSMEFTHiggs_mAmu_input_parameters input;
      input.MuInput  = *myPipe::Param.at("mu");
      // This FS spectrum generator has mA as the parameter
      input.mAInput = *myPipe::Param.at("mA");
      // Note: Qin has been named MSUSY inside the spectrum generator
      // but it is a user-input scale in this case.
      input.MSUSY = *myPipe::Param.at("Qin");
      // Fill the rest.
      // Note: This particular spectrum generator has been created with
      // different names for parameter inputs.  We should standardise this
      fill_MSSM63_input_altnames(input,myPipe::Param);
      result = run_FS_spectrum_generator<MSSMEFTHiggs_mAmu_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atQ_mA_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }
    #endif

    // Runs FlexibleSUSY MSSM spectrum generator with CMSSM (GUT scale) boundary conditions
    // In principle an identical spectrum can be obtained from the function
    // get_MSSMatGUT_spectrum_FS
    // by setting the input parameters to match the CMSSM assumptions
    #if(FS_MODEL_CMSSM_IS_BUILT)
    void get_CMSSM_spectrum_FS (Spectrum& result)
    {

      // Access the pipes for this function to get model and parameter information
      namespace myPipe = Pipes::get_CMSSM_spectrum_FS;

      // Get SLHA2 SMINPUTS values
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;

      // Get input parameters (from flexiblesusy namespace)
      CMSSM_input_parameters input;

      input.m0      = *myPipe::Param["M0"];
      input.m12     = *myPipe::Param["M12"];
      input.TanBeta = *myPipe::Param["TanBeta"];
      input.SignMu  = *myPipe::Param["SignMu"];
      input.Azero   = *myPipe::Param["A0"];

      // Sanity checks
      if(input.TanBeta<0)
      {
        std::ostringstream msg;
        msg << "Tried to set TanBeta parameter to a negative value ("<<input.TanBeta<<")! This parameter must be positive. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }
      if(input.SignMu!=-1 and input.SignMu!=1)
      {
        std::ostringstream msg;
        msg << "Tried to set SignMu parameter to a value that is not a sign! ("<<input.SignMu<<")! This parameter must be set to either 1 or -1. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }

      // Run spectrum generator
      result = run_FS_spectrum_generator<CMSSM_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param, false);

      // Only allow neutralino LSPs.
      if (not has_neutralino_LSP(result)) invalid_point().raise("Neutralino is not LSP.");

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }
    #endif

    // Runs FlexibleSUSY MSSM spectrum generator with EWSB scale input (boundary conditions)
    #if(FS_MODEL_MSSM_IS_BUILT)
    void get_MSSMatQ_spectrum_FS (Spectrum& result)
    {
      using namespace softsusy;
      namespace myPipe = Pipes::get_MSSMatQ_spectrum_FS;
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;
      MSSM_input_parameters input;
      input.Qin    = *myPipe::Param.at("Qin"); // MSSMatQ also requires input scale to be supplied
      input.mHu2IN = *myPipe::Param.at("mHu2");
      input.mHd2IN = *myPipe::Param.at("mHd2");
      input.SignMu = *myPipe::Param.at("SignMu");
      if(input.SignMu!=-1 and input.SignMu!=1)
      {
        std::ostringstream msg;
        msg << "Tried to set SignMu parameter to a value that is not a sign! ("<<input.SignMu<<")! This parameter must be set to either 1 or -1. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }
      fill_MSSM63_input(input,myPipe::Param);

      // Run spectrum generator
      result = run_FS_spectrum_generator<MSSM_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atQ_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");
    }
    #endif

    // Runs FlexibleSUSY MSSM spectrum generator with EWSB scale input (boundary conditions)
    // but with mA and mu as parameters instead of mHu2 and mHd2
    #if(FS_MODEL_MSSM_mAmu_IS_BUILT)
    void get_MSSMatQ_mA_spectrum_FS (Spectrum& result)
    {
      using namespace softsusy;
      namespace myPipe = Pipes::get_MSSMatQ_mA_spectrum_FS;
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;
      MSSM_mAmu_input_parameters input;
      input.Qin      = *myPipe::Param.at("Qin"); // MSSMatQ also requires input scale to be supplied
      input.MuInput  = *myPipe::Param.at("mu");
      // Note this spectrum generator mA2 is the parameter.
      // However this freedom is not used in GAMBIT
      // and not needed as mA is a DRbar mass eigenstate for a scalar
      double mA = *myPipe::Param.at("mA");
      input.mA2Input = mA*mA;
      fill_MSSM63_input(input,myPipe::Param); // Fill the rest

      // Run spectrum generator
      result = run_FS_spectrum_generator<MSSM_mAmu_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atQ_mA_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");
    }
    #endif

    // Runs FlexibleSUSY MSSM spectrum generator with GUT scale input (boundary conditions)
    #if(FS_MODEL_MSSMatMGUT_IS_BUILT)
    void get_MSSMatMGUT_spectrum_FS (Spectrum& result)
    {
      using namespace softsusy;
      namespace myPipe = Pipes::get_MSSMatMGUT_spectrum_FS;
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;
      MSSMatMGUT_input_parameters input;
      input.mHu2IN = *myPipe::Param.at("mHu2");
      input.mHd2IN = *myPipe::Param.at("mHd2");
      input.SignMu = *myPipe::Param.at("SignMu");
      if(input.SignMu!=-1 and input.SignMu!=1)
      {
        std::ostringstream msg;
        msg << "Tried to set SignMu parameter to a value that is not a sign! ("<<input.SignMu<<")! This parameter must be set to either 1 or -1. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }
      fill_MSSM63_input(input,myPipe::Param);

      // Run the spectrum generator
      result = run_FS_spectrum_generator<MSSMatMGUT_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atMGUT_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");
    }
    #endif

    // Runs FlexibleSUSY MSSMatMGUTEFTHiggs model spectrum generator
    // and has m3^2 and mu as EWSB outputs, so it is for the
    // MSSMatMGUT_model.
    #if(FS_MODEL_MSSMatMGUTEFTHiggs_IS_BUILT)
    void get_MSSMatMGUT_spectrum_FlexibleEFTHiggs (Spectrum& result)
    {
      // Access the pipes for this function to get model and parameter information
      namespace myPipe = Pipes::get_MSSMatMGUT_spectrum_FlexibleEFTHiggs;

      // Get SLHA2 SMINPUTS values
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;

      // Get input parameters (from flexiblesusy namespace)
      MSSMatMGUTEFTHiggs_input_parameters input;
      input.mHu2IN = *myPipe::Param.at("mHu2");
      input.mHd2IN = *myPipe::Param.at("mHd2");
      input.SignMu = *myPipe::Param.at("SignMu");
      if(input.SignMu!=-1 and input.SignMu!=1)
      {
        std::ostringstream msg;
        msg << "Tried to set SignMu parameter to a value that is not a sign! ("<<input.SignMu<<")! This parameter must be set to either 1 or -1. Please check your inifile and try again.";
        SpecBit_error().raise(LOCAL_INFO,msg.str());
      }

      fill_MSSM63_input(input,myPipe::Param); // Fill the rest

      // Run the spectrum generator
      result = run_FS_spectrum_generator<MSSMatMGUTEFTHiggs_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atMGUT_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }
    #endif


    // Runs FlexibleSUSY MSSM spectrum generator with GUT scale input (boundary conditions)
    // but with mA and mu as parameters instead of mHu2 and mHd2
    #if(FS_MODEL_MSSMatMGUT_mAmu_IS_BUILT)
    void get_MSSMatMGUT_mA_spectrum_FS (Spectrum& result)
    {
      using namespace softsusy;
      namespace myPipe = Pipes::get_MSSMatMGUT_mA_spectrum_FS;
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;
      MSSMatMGUT_mAmu_input_parameters input;
      input.MuInput  = *myPipe::Param.at("mu");
      // Note this spectrum generator mA2 is the parameter.
      // However this freedom is not used in GAMBIT
      // and not needed as mA is a DRbar mass eigenstate for a scalar
      double mA = *myPipe::Param.at("mA");
      input.mA2Input = mA*mA;
      fill_MSSM63_input(input,myPipe::Param); // Fill the rest

      // Run the spectrum generator
      result = run_FS_spectrum_generator<MSSMatMGUT_mAmu_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atMGUT_mA_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");
    }
    #endif

    // Runs FlexibleSUSY MSSMatMGUTEFTHiggs model spectrum generator
    // and has m3^2 and mu as EWSB outputs, so it is for the
    // MSSMatMGUT_model.
    #if(FS_MODEL_MSSMatMGUTEFTHiggs_mAmu_IS_BUILT)
    void get_MSSMatMGUT_mA_spectrum_FlexibleEFTHiggs (Spectrum& result)
    {
      // Access the pipes for this function to get model and parameter information
      namespace myPipe = Pipes::get_MSSMatMGUT_mA_spectrum_FlexibleEFTHiggs;

      // Get SLHA2 SMINPUTS values
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;

      // Get input parameters (from flexiblesusy namespace)
      MSSMatMGUTEFTHiggs_mAmu_input_parameters input;
      input.MuInput  = *myPipe::Param.at("mu");
      // Note this spectrum generator mA2 is the parameter.
      // However this freedom is not used in GAMBIT
      // and not needed as mA is a DRbar mass eigenstate for a scalar
      double mA = *myPipe::Param.at("mA");
      input.mA2Input = mA*mA;

      fill_MSSM63_input(input,myPipe::Param); // Fill the rest

      // Run the specctrum generator
      result = run_FS_spectrum_generator<MSSMatMGUTEFTHiggs_mAmu_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atMGUT_mA_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");

    }
    #endif


    // Runs FlexibleSUSY MSSM spectrum generator with SUSY scale input (boundary conditions)
    // but with mA and mu as parameters instead of mHu2 and mHd2
    #if(FS_MODEL_MSSMatMSUSY_mAmu_IS_BUILT)
    void get_MSSMatMSUSY_mA_spectrum_FS (Spectrum& result)
    {
      using namespace softsusy;
      namespace myPipe = Pipes::get_MSSMatMSUSY_mA_spectrum_FS;
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS;
      MSSMatMSUSY_mAmu_input_parameters input;
      input.MuInput  = *myPipe::Param.at("mu");
      // Note this spectrum generator mA2 is the parameter.
      // However this freedom is not used in GAMBIT
      // and not needed as mA is a DRbar mass eigenstate for a scalar
      double mA = *myPipe::Param.at("mA");
      input.mA2Input = mA*mA;    // FS has mA^2 as the parameter
      fill_MSSM63_input(input,myPipe::Param); // Fill the rest

      // Run the spectrum generator
      result = run_FS_spectrum_generator<MSSMatMSUSY_mAmu_interface<ALGORITHM1>>(input,sminputs,*myPipe::runOptions,myPipe::Param,myPipe::ModelInUse("MSSM63atMSUSY_mA_mG"));

      // Drop SLHA files if requested
      result.drop_SLHAs_if_requested(myPipe::runOptions, "GAMBIT_unimproved_spectrum");
    }
    #endif

    void get_GUTMSSMB_spectrum (Spectrum &/*result*/)
    {
      // Placeholder
    }

    /// @{
    /// Functions to decompose Spectrum object (of type MSSM_spectrum)

    /// @}
    /// Retrieve SubSpectrum* to SM LE model from Spectrum object
    /// DEPENDENCY(MSSM_spectrum, Spectrum)
    void get_SM_SubSpectrum_from_MSSM_Spectrum (const SubSpectrum* &result)
    {
      namespace myPipe = Pipes::get_SM_SubSpectrum_from_MSSM_Spectrum;
      const Spectrum& matched_spectra(*myPipe::Dep::unimproved_MSSM_spectrum);
      result = &matched_spectra.get_LE();
    }

    /// Extract an SLHAea version of the spectrum contained in a Spectrum object, in SLHA1 format
    void get_MSSM_spectrum_as_SLHAea_SLHA1(SLHAstruct &result)
    {
      result = Pipes::get_MSSM_spectrum_as_SLHAea_SLHA1::Dep::unimproved_MSSM_spectrum->getSLHAea(1);
    }

    /// Extract an SLHAea version of the spectrum contained in a Spectrum object, in SLHA2 format
    void get_MSSM_spectrum_as_SLHAea_SLHA2(SLHAstruct &result)
    {
      result = Pipes::get_MSSM_spectrum_as_SLHAea_SLHA2::Dep::unimproved_MSSM_spectrum->getSLHAea(2);
    }

    /// Get an MSSMSpectrum object from an SLHA file
    /// Wraps it up in MSSMSimpleSpec; i.e. no RGE running possible.
    /// This is mainly for testing against benchmark points, but may be a useful last
    /// resort for interacting with "difficult" spectrum generators.
    void get_MSSM_spectrum_from_SLHAfile(Spectrum &result)
    {
      // Static counter running in a loop over all filenames
      static unsigned int counter = 0;
      static long int ncycle = 1;
      SLHAstruct input_slha;

      namespace myPipe = Pipes::get_MSSM_spectrum_from_SLHAfile;

      // Read filename from yaml file
      std::vector<std::string> filenames =
        myPipe::runOptions->getValue<std::vector<std::string>>("filenames");

      // Check how many loop over the input files we are doing.
      long int cycles = myPipe::runOptions->getValueOrDef<int>(-1,"cycles");

      // Check if we have completed the requested number of cycles
      if(cycles>0 and ncycle>cycles)
      {
         std::ostringstream msg;
         msg << "Preset number of loops through input files reached! Stopping. (tried to start cycle "<<ncycle<<" of "<<cycles<<")";
         SpecBit_error().raise(LOCAL_INFO,msg.str());
      }

      std::string filename = filenames[counter];

      logger() << "Reading SLHA file: " << filename << EOM;
      std::ifstream ifs(filename.c_str());
      if(!ifs.good()){ SpecBit_error().raise(LOCAL_INFO,"ERROR: SLHA file not found."); }
      ifs >> input_slha;
      ifs.close();
      counter++;
      if( counter >= filenames.size() )
      {
        logger() << "Returning to start of input SLHA file list (finished "<<ncycle<<" cycles)" << EOM;
        counter = 0;
        ncycle++;
      }

      // Retrieve any mass cuts
      static const Spectrum::mc_info mass_cut = myPipe::runOptions->getValueOrDef<Spectrum::mc_info>(Spectrum::mc_info(), "mass_cut");
      static const Spectrum::mr_info mass_ratio_cut = myPipe::runOptions->getValueOrDef<Spectrum::mr_info>(Spectrum::mr_info(), "mass_ratio_cut");

      // Create Spectrum object from the slhaea object
      result = spectrum_from_SLHAea<MSSMSimpleSpec, SLHAstruct>(input_slha, input_slha, mass_cut, mass_ratio_cut);

      // Add getter for susy scale if option set for this
      bool add_susy_scale = myPipe::runOptions->getValueOrDef<bool>(false,"assume_Q_is_MSUSY");
      if(add_susy_scale)
      {
         result.get_HE().set_override(Par::mass1,result.get_HE().GetScale(),"susy_scale",true);
      }

      // Add the gravitino mass to a spectrum object if it has been provided in an SLHAea object
      add_gravitino_mass_from_slhaea(result, input_slha);

      // No sneaking in charged LSPs via SLHA, jävlar.
      check_LSP(result, *myPipe::runOptions, result.has(Par::Pole_Mass, "~G"));
    }

    /// Get an MSSMSpectrum object from an SLHAstruct
    /// Wraps it up in MSSMSimpleSpec; i.e. no RGE running possible.
    /// This can be used as a poor-man's interface to backend spectrum generators
    void get_MSSM_spectrum_from_SLHAstruct(Spectrum& result)
    {
      namespace myPipe = Pipes::get_MSSM_spectrum_from_SLHAstruct;
      const SLHAstruct& input_slha = *myPipe::Dep::unimproved_MSSM_spectrum; // Retrieve dependency on SLHAstruct

      // Retrieve any mass cuts
      static const Spectrum::mc_info mass_cut = myPipe::runOptions->getValueOrDef<Spectrum::mc_info>(Spectrum::mc_info(), "mass_cut");
      static const Spectrum::mr_info mass_ratio_cut = myPipe::runOptions->getValueOrDef<Spectrum::mr_info>(Spectrum::mr_info(), "mass_ratio_cut");

      // Create Spectrum object from the slhaea object
      result = spectrum_from_SLHAea<MSSMSimpleSpec, SLHAstruct>(input_slha, input_slha, mass_cut, mass_ratio_cut);

      // Add the gravitino mass to the spectrum object if it has been provided in the SLHAea object
      add_gravitino_mass_from_slhaea(result, input_slha);

      // No sneaking in charged LSPs via SLHA, jävlar.
      check_LSP(result, *myPipe::runOptions, result.has(Par::Pole_Mass, "~G"));

      // In order to translate from e.g. MSSM63atMGUT to MSSM63atQ, we need
      // to know that input scale Q. This is generally not stored in SLHA format,
      // but we need it, so if you want to produce a Spectrum object this way you
      // will need to add this information to your SLHAstruct:
      // BLOCK GAMBIT
      //   1     <high_scale>    # Input scale of (upper) boundary conditions, e.g. GUT scale

      // Need to check if this information exists:
      SLHAstruct::const_iterator block = input_slha.find("GAMBIT");
      std::vector<std::string> key(1, "1");
      if(block == input_slha.end() or block->find(key) == block->end())
      {
        // Big problem
        std::ostringstream errmsg;
        errmsg << "Error constructing Spectrum object from a pre-existing SLHAstruct!    " << endl
               << "The supplied SLHAstruct did not have the special GAMBIT block added.  " << endl
               << "This block carries extra information from the spectrum generator      " << endl
               << "that is usually thrown away, but which is needed for properly creating" << endl
               << "a Spectrum object. In whatever module function created the SLHAstruct " << endl
               << "that you want to use, please add code that adds the following         " << endl
               << "information to the SLHAstruct (SLHAea::Coll):                         " << endl
               << "  BLOCK GAMBIT                                                        " << endl
               << " 1 <high_scale>  # Input scale of (upper) boundary conditions, e.g. GUT scale\n";
        SpecBit_error().raise(LOCAL_INFO,errmsg.str());
      }

      // OK the GAMBIT block exists, add the data to the MSSM SubSpectrum object.
      result.get_HE().set_override(Par::mass1,SLHAea::to<double>(input_slha.at("GAMBIT").at(1).at(1)), "high_scale", false);
    }

    /// Get pre-computed MSSM spectrum from previous output file
    /// This function ONLY works when the scan is driven by the postprocessor!
    /// This is because it relies on the global reader object created by the
    /// postprocessor to retrieve output.
    void get_MSSM_spectrum_from_postprocessor(Spectrum& result)
    {
      namespace myPipe = Pipes::get_MSSM_spectrum_from_postprocessor;
      const SMInputs& sminputs = *myPipe::Dep::SMINPUTS; // Retrieve dependency on SLHAstruct

      // Retrieve the spectrum from whatever the point the global reader object is pointed at.
      // This should be the same point that the postprocessor has retrieved the
      // current set of ModelParameters from.
      // Will throw an error if no reader object exists, i.e. if the postprocessor is not
      // driving this scan.

      // Retrieve MSSM spectrum info into an SLHAea object
      MSSM_SLHAstruct mssm_in; // Special type to trigger specialised MSSM retrieve routine
      bool mssm_is_valid = get_pp_reader().retrieve(mssm_in,"MSSM_spectrum");

      // Retrieve SM spectrum info into an SLHAea object
      // (should really match SMINPUTS, but better to use what is actually in the reported output spectrum)
      SMslha_SLHAstruct sm_in;
      bool sm_is_valid = get_pp_reader().retrieve(sm_in,"MSSM_spectrum");

      // Check if a valid spectrum was retrived
      // (if the required datasets don't exist an error will be thrown,
      //  so this is just checking that the spectrum was computed for
      //  the current input point)
      if(not (mssm_is_valid and sm_is_valid)) invalid_point().raise("Postprocessor: precomputed spectrum was set 'invalid' for this point");

      // Dump spectrum to output for testing
      SLHAstruct mssm = mssm_in; // Only this type has stream overloads etc. defined
      SLHAstruct sm = sm_in;

      // Turns out we don't generically save tan_beta(mZ)_DRbar, so need to extract
      // this from model parameters (it is always an input, so we should have it in those)
      double tbmZ = *myPipe::Param.at("TanBeta");
      SLHAea_add(mssm, "MINPAR", 3, tbmZ, "tan beta (mZ)_DRbar");
      SLHAea_add(sm, "MINPAR", 3, tbmZ, "tan beta (mZ)_DRbar");

      // Retrieve any mass cuts (could just cut with postprocessor, but I
      // guess can leave this feature in for compatibility with usage
      // of other Spectrum objects.
      static const Spectrum::mc_info mass_cut = myPipe::runOptions->getValueOrDef<Spectrum::mc_info>(Spectrum::mc_info(), "mass_cut");
      static const Spectrum::mr_info mass_ratio_cut = myPipe::runOptions->getValueOrDef<Spectrum::mr_info>(Spectrum::mr_info(), "mass_ratio_cut");

      // Create HE simple SubSpectrum object from the SLHAea object
      MSSMSimpleSpec he(mssm);

      // Create SMSimpleSpec SubSpectrum object from SMInputs
      SMSimpleSpec le(sm);

      // Create full Spectrum object
      result = Spectrum(le,he,sminputs,NULL,mass_cut,mass_ratio_cut);

      // Need to add high scale to high energy spectrum
      result.get_HE().set_override(Par::mass1,he.GetScale(), "high_scale", true);
      result.get_HE().set_override(Par::mass1,he.GetScale(), "susy_scale", true);

    }

    /// FeynHiggs SUSY masses and mixings
    void FeynHiggs_MSSMMasses(fh_MSSMMassObs_container &result)
    {
      using namespace Pipes::FeynHiggs_MSSMMasses;

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FeynHiggs_MSSMMasses ******" << endl;
      #endif

      // zero if minimal, non-zero if non-minimal flavour violation
      int nmfv;

      // MSf(s,t,g) MFV squark masses with indices
      // s = 1..2   sfermion index
      // t = 1..5   sfermion type nu,e,u,d,?
      // g = 1..3   generation index
      Farray<fh_real, 1,2, 1,5, 1,3> MSf;

      // USf(s1,s2,t,g) MFV squark mixing matrices with indices
      // s1 = 1..2  sfermion index (mass eigenstates)
      // s2 = 1..2  sfermion index (gauge eigenstates, L/R)
      // t  = 1..5  sfermion type nu,e,u,d,?
      // g  = 1..3  generation index
      Farray<fh_complex, 1,2, 1,2, 1,5, 1,3> USf;

      // NMFV squark masses, with indices
      // a = 1..6   extended sfermion index
      // t = 1..5   sfermion type
      Farray<fh_real, 1,6, 1,5> MASf;

      // NMFV squark mixing matrices, with indices
      // a1 = 1..6  extended sfermion index (mass eigenstates)
      // a2 = 1..6  extended sfermion index (gauge eigenstates)
      //  t = 1..5  sftermion type nu,e,u,d,?
      Farray<fh_complex, 1,36, 1,5> UASf;

      // chargino masses
      Farray<fh_real, 1,2> MCha;

      // chargino mixing matrices (mass,gauge) eigenstates (2 x 2)
      Farray<fh_complex, 1,4> UCha;
      Farray<fh_complex, 1,4> VCha;

      // neutralino masses
      Farray<fh_real, 1,4> MNeu;

      // neutralino mixing matrices (mass,gauge) eigenstates (4 x 4)
      Farray<fh_complex, 1,16> ZNeu;

      // correction to bottom Yukawa coupling
      fh_complex DeltaMB;

      // gluino mass
      fh_real MGl;

      // tree-level Higgs masses (Mh, MH, MA, MHpm)
      Farray<fh_real, 1,4> MHtree;

      // tree-level Higgs mixing parameters sin alpha
      fh_real SAtree;

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FHGetPara ******" << endl;
      #endif

      int error = 1;
      BEreq::FHGetPara(error, nmfv, MSf, USf, MASf, UASf,
           MCha, UCha, VCha, MNeu, ZNeu,
           DeltaMB, MGl, MHtree, SAtree);
      if (error != 0)
      {
        std::ostringstream err;
        err << "BEreq::FHGetPara raised error flag: " << error << ".";
        invalid_point().raise(err.str());
      }

      fh_MSSMMassObs_container MassObs;
      for(int i = 0; i < 2; i++)
        for(int j = 0; j < 5; j++)
          for(int k = 0; k < 3; k++)
            MassObs.MSf[i][j][k] = MSf(i+1,j+1,k+1);
      for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
          for(int k = 0; k < 5; k++)
            for(int l = 0; l < 3; l++)
              MassObs.USf[i][j][k][l] = USf(i+1,j+1,k+1,l+1);
      for(int i = 0; i < 6; i++)
        for(int j = 0; j < 5; j++)
          MassObs.MASf[i][j] = MASf(i+1,j+1);
      for(int i = 0; i < 36; i++)
        for(int j = 0; j < 5; j++)
          MassObs.UASf[i][j] = UASf(i+1,j+1);
      for(int i = 0; i < 2; i++)
        MassObs.MCha[i] = MCha(i+1);
      for(int i = 0; i < 4; i++)
      {
        MassObs.UCha[i] = UCha(i+1);
        MassObs.VCha[i] = VCha(i+1);
      }
      for(int i = 0; i < 4; i++)
        MassObs.MNeu[i] = MNeu(i+1);
      for(int i = 0; i < 16; i++)
        MassObs.ZNeu[i] = ZNeu(i+1);
      MassObs.deltaMB = DeltaMB;
      MassObs.MGl = MGl;
      for(int i = 0; i < 4; i++)
        MassObs.MHtree[i] = MHtree(i+1);
      MassObs.SinAlphatree = SAtree;

      result = MassObs;
    }


    /// Higgs masses and mixings with theoretical uncertainties
    void FeynHiggs_AllHiggsMasses(fh_HiggsMassObs_container &result)
    {
      using namespace Pipes::FeynHiggs_AllHiggsMasses;

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FeynHiggs_HiggsMasses ******" << endl;
      #endif

      // Higgs mass with
      // 0 - m1 (Mh in rMSSM)
      // 1 - m2 (MH in rMSSM)
      // 2 - m3 (MA in rMSSM)
      // 3 - MHpm
      Farray<fh_real, 1,4> MHiggs;
      Farray<fh_real, 1,4> DeltaMHiggs;

      // sine of effective Higgs mixing angle, alpha_eff
      fh_complex SAeff;
      fh_complex DeltaSAeff;

      // matrix needed to rotate Higgs
      // mass matrix to diagonal form
      Farray<fh_complex, 1,3, 1,3> UHiggs;
      Farray<fh_complex, 1,3, 1,3> DeltaUHiggs;

      // matrix of Z-factors needed to combine
      // amplitudes involving on-shell Higgs
      Farray<fh_complex, 1,3, 1,3> ZHiggs;
      Farray<fh_complex, 1,3, 1,3> DeltaZHiggs;

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FHHiggsCorr ******" << endl;
      #endif

      int error = 1;
      BEreq::FHHiggsCorr(error, MHiggs, SAeff, UHiggs, ZHiggs);
      if (error != 0)
      {
        std::ostringstream err;
        err << "BEreq::FHHiggsCorr raised error flag: " << error << ".";
        invalid_point().raise(err.str());
      }

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FHUncertainties ******" << endl;
      #endif

      error = 1;
      BEreq::FHUncertainties(error, DeltaMHiggs, DeltaSAeff, DeltaUHiggs, DeltaZHiggs);
      if (error != 0)
      {
        std::ostringstream err;
        err << "BEreq::FHUncertainties raised error flag: " << error << ".";
        invalid_point().raise(err.str());
      }

      fh_HiggsMassObs_container HiggsMassObs;
      for(int i = 0; i < 4; i++)
      {
        HiggsMassObs.MH[i] = MHiggs(i+1);
        HiggsMassObs.deltaMH[i] = DeltaMHiggs(i+1);
      }
      HiggsMassObs.SinAlphaEff = SAeff;
      HiggsMassObs.deltaSinAlphaEff = DeltaSAeff;
      for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
        {
          HiggsMassObs.UH[i][j] = UHiggs(i+1,j+1);
          HiggsMassObs.deltaUH[i][j] = DeltaUHiggs(i+1,j+1);
          HiggsMassObs.ZH[i][j] = ZHiggs(i+1,j+1);
          HiggsMassObs.deltaZH[i][j] = DeltaZHiggs(i+1,j+1);
        }

      result = HiggsMassObs;
    }

////////////////////////
//// Higgs routines ////
////////////////////////

    /// Call FHCouplings from FeynHiggs and collect the output
    void FeynHiggs_Couplings(fh_Couplings_container &result)
    {
      using namespace Pipes::FeynHiggs_Couplings;

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FeynHiggs_Couplings ******" << endl;
      #endif

      // what to use for internal Higgs mixing
      // (ex. in couplings)
      // (default = 1)
      // 0 - no mixing
      // 1 - UHiggs
      // 2 - ZHiggs
      int uzint = 2;
      // what to use for external Higgs mixing
      // (ex. in decays)
      // (default = 2)
      // 0 - no mixing
      // 1 - UHiggs
      // 2 - ZHiggs
      int uzext = 2;
      // which effective bottom mass to use
      int mfeff = 1;

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FHSelectUZ ******" << endl;
      #endif

      int error = 1;
      BEreq::FHSelectUZ(error, uzint, uzext, mfeff);
      if (error != 0)
      {
        std::ostringstream err;
        err << "BEreq::FHSelectUZ raised error flag: " << error << ".";
        invalid_point().raise(err.str());
      }

      Farray<fh_complex, 1,ncouplings> couplings;        // MSSM Higgs couplings
      Farray<fh_complex, 1,ncouplingsms> couplings_sm;  // SM Higgs couplings
      Farray<fh_real, 1,ngammas> gammas;                // Higgs decay widths and BR's (MSSM)
      Farray<fh_real, 1,ngammasms> gammas_sm;           // Higgs decay widths and BR's (SM)
      int fast = 1;  // include off-diagonal fermion decays? (1 = no)

      #ifdef SPECBIT_DEBUG
        cout << "****** calling FHCouplings ******" << endl;
      #endif

      error = 1;
      BEreq::FHCouplings(error, couplings, couplings_sm,
                         gammas, gammas_sm, fast);
      if (error != 0)
      {
        std::ostringstream err;
        err << "BEreq::FHCouplings raised error flag: " << error << ".";
        invalid_point().raise(err.str());
      }

      fh_Couplings_container Couplings;
      for(int i = 0; i < ncouplings; i++) Couplings.couplings[i] = couplings(i+1);
      for(int i = 0; i < ncouplingsms; i++) Couplings.couplings_sm[i] = couplings_sm(i+1);
      for(int i = 0; i < ngammas; i++) Couplings.gammas[i] = gammas(i+1);
      for(int i = 0; i < ngammasms; i++) Couplings.gammas_sm[i] = gammas_sm(i+1);
      Couplings.calculator = BEreq::FHCouplings.origin();
      Couplings.calculator_version = BEreq::FHCouplings.version();

      result = Couplings;
    }


    /// Helper function to work out if the LSP is invisible, and if so, which particle it is.
    std::vector<std::pair<str,str>> get_invisibles(const SubSpectrum& spec)
    {
      // Get the lighter of the lightest neutralino and the lightest sneutrino
      std::pair<str,double> neutralino("~chi0_1", spec.get(Par::Pole_Mass,"~chi0",1));
      std::pair<str,double> sneutrino("~nu_1", spec.get(Par::Pole_Mass,"~nu",1));
      std::pair<str,double> lnp = (neutralino.second < sneutrino.second ? neutralino : sneutrino);

      // Work out if this is indeed the LSP, and if decays of at least one neutral higgs to it are kinematically possible.
      bool inv_lsp = spec.get(Par::Pole_Mass,"~chi+",1) > lnp.second and
                     spec.get(Par::Pole_Mass,"~g") > lnp.second and
                     spec.get(Par::Pole_Mass,"~d",1) > lnp.second and
                     spec.get(Par::Pole_Mass,"~u",1) > lnp.second and
                     spec.get(Par::Pole_Mass,"~e-",1) > lnp.second and
                     (spec.get(Par::Pole_Mass,"h0",2) > 2.*lnp.second or
                      spec.get(Par::Pole_Mass,"A0") > 2.*lnp.second);

      // Create a vector containing all invisible products of higgs decays.
      if (inv_lsp) return initVector<std::pair<str,str>>(std::make_pair(lnp.first,lnp.first));
      return std::vector<std::pair<str,str>>();
    }

    /// Put together the Higgs couplings for the MSSM, from partial widths only
    void MSSM_higgs_couplings_pwid(HiggsCouplingsTable &result)
    {
      using namespace Pipes::MSSM_higgs_couplings_pwid;

      // Retrieve spectrum contents
      const SubSpectrum& spec = Dep::MSSM_spectrum->get_HE();

      // Set up neutral Higgses
      static const std::vector<str> sHneut = initVector<str>("h0_1", "h0_2", "A0");
      result.set_n_neutral_higgs(3);

      // Set up charged Higgses
      result.set_n_charged_higgs(1);

      // Set the CP of the Higgs states.  Note that this would need to be more sophisticated to deal with the complex MSSM!
      result.CP[0] = 1;  //h0_1
      result.CP[1] = 1;  //h0_2
      result.CP[2] = -1; //A0

      // Work out which SM values correspond to which SUSY Higgs
      int higgs = (SMlike_higgs_PDG_code(spec) == 25 ? 0 : 1);
      int other_higgs = (higgs == 0 ? 1 : 0);

      // Set the decays
      result.set_neutral_decays_SM(higgs, sHneut[higgs], *Dep::Reference_SM_Higgs_decay_rates);
      result.set_neutral_decays_SM(other_higgs, sHneut[other_higgs], *Dep::Reference_SM_other_Higgs_decay_rates);
      result.set_neutral_decays_SM(2, sHneut[2], *Dep::Reference_SM_A0_decay_rates);
      result.set_neutral_decays(0, sHneut[0],  *Dep::Higgs_decay_rates);
      result.set_neutral_decays(1, sHneut[1], *Dep::h0_2_decay_rates);
      result.set_neutral_decays(2, sHneut[2], *Dep::A0_decay_rates);
      result.set_charged_decays(0, "H+", *Dep::H_plus_decay_rates);
      result.set_t_decays(*Dep::t_decay_rates);

      // Use them to compute effective couplings for all neutral higgses, except for hhZ.
      for (int i = 0; i < 3; i++)
      {
        result.C_WW2[i] = result.compute_effective_coupling(i, std::pair<int,int>(24, 0), std::pair<int,int>(-24, 0));
        result.C_ZZ2[i] = result.compute_effective_coupling(i, std::pair<int,int>(23, 0), std::pair<int,int>(23, 0));
        result.C_tt2[i] = result.compute_effective_coupling(i, std::pair<int,int>(6, 1), std::pair<int,int>(-6, 1));
        result.C_bb2[i] = result.compute_effective_coupling(i, std::pair<int,int>(5, 1), std::pair<int,int>(-5, 1));
        result.C_cc2[i] = result.compute_effective_coupling(i, std::pair<int,int>(4, 1), std::pair<int,int>(-4, 1));
        result.C_tautau2[i] = result.compute_effective_coupling(i, std::pair<int,int>(15, 1), std::pair<int,int>(-15, 1));
        result.C_gaga2[i] = result.compute_effective_coupling(i, std::pair<int,int>(22, 0), std::pair<int,int>(22, 0));
        result.C_gg2[i] = result.compute_effective_coupling(i, std::pair<int,int>(21, 0), std::pair<int,int>(21, 0));
        result.C_mumu2[i] = result.compute_effective_coupling(i, std::pair<int,int>(13, 1), std::pair<int,int>(-13, 1));
        result.C_Zga2[i] = result.compute_effective_coupling(i, std::pair<int,int>(23, 0), std::pair<int,int>(21, 0));
        result.C_ss2[i] = result.compute_effective_coupling(i, std::pair<int,int>(3, 1), std::pair<int,int>(-3, 1));
      }

      // Calculate hhZ effective couplings.  Here we scale out the kinematic prefactor
      // of the decay width, assuming we are well above threshold if the channel is open.
      // If not, we simply assume SM couplings.
      const double mZ = Dep::MSSM_spectrum->get(Par::Pole_Mass,23,0);
      const double scaling = 8.*sqrt(2.)*pi/Dep::MSSM_spectrum->get_SMInputs().GF;
      for(int i = 0; i < 3; i++)
      for(int j = 0; j < 3; j++)
      {
        double mhi = spec.get(Par::Pole_Mass, sHneut[i]);
        double mhj = spec.get(Par::Pole_Mass, sHneut[j]);
        if (mhi > mhj + mZ and result.get_neutral_decays(i).has_channel(sHneut[j], "Z0"))
        {
          double gamma = result.get_neutral_decays(i).width_in_GeV*result.get_neutral_decays(i).BF(sHneut[j], "Z0");
          double k[2] = {(mhj + mZ)/mhi, (mhj - mZ)/mhi};
          for (int l = 0; l < 2; l++) k[l] = (1.0 - k[l]) * (1.0 + k[l]);
          double K = mhi*sqrt(k[0]*k[1]);
          result.C_hiZ2[i][j] = scaling / (K*K*K) * gamma;
        }
        else // If the channel is missing from the decays or kinematically disallowed, just return the SM result.
        {
          result.C_hiZ2[i][j] = 1.;
        }
      }

      // Work out which invisible decays are possible
      result.invisibles = get_invisibles(spec);
    }


    /// Put together the Higgs couplings for the MSSM, using FeynHiggs
    void MSSM_higgs_couplings_FeynHiggs(HiggsCouplingsTable &result)
    {
      using namespace Pipes::MSSM_higgs_couplings_FeynHiggs;

      // Retrieve spectrum contents
      const SubSpectrum& spec = Dep::MSSM_spectrum->get_HE();
      const SMInputs& sminputs = Dep::MSSM_spectrum->get_SMInputs();

      // Set up neutral Higgses
      static const std::vector<str> sHneut = initVector<str>("h0_1", "h0_2", "A0");

      // Work out which SM values correspond to which SUSY Higgs
      int higgs = (SMlike_higgs_PDG_code(spec) == 25 ? 0 : 1);
      int other_higgs = (higgs == 0 ? 1 : 0);

      // Set the decays
      result.set_neutral_decays_SM(higgs, sHneut[higgs], *Dep::Reference_SM_Higgs_decay_rates);
      result.set_neutral_decays_SM(other_higgs, sHneut[other_higgs], *Dep::Reference_SM_other_Higgs_decay_rates);
      result.set_neutral_decays_SM(2, sHneut[2], *Dep::Reference_SM_A0_decay_rates);
      result.set_neutral_decays(0, sHneut[0], *Dep::Higgs_decay_rates);
      result.set_neutral_decays(1, sHneut[1], *Dep::h0_2_decay_rates);
      result.set_neutral_decays(2, sHneut[2], *Dep::A0_decay_rates);
      result.set_charged_decays(0, "H+", *Dep::H_plus_decay_rates);
      result.set_t_decays(*Dep::t_decay_rates);

      // Use the branching fractions to compute gluon, gamma/Z and second generation fermionic effective couplings
      for (int i = 0; i < 3; i++)
      {
        result.C_gg2[i] = result.compute_effective_coupling(i, std::pair<int,int>(21, 0), std::pair<int,int>(21, 0));
        result.C_gaga2[i] = result.compute_effective_coupling(i, std::pair<int,int>(22, 0), std::pair<int,int>(22, 0));
        result.C_Zga2[i] = result.compute_effective_coupling(i, std::pair<int,int>(23, 0), std::pair<int,int>(22, 0));
        result.C_mumu2[i] = result.compute_effective_coupling(i, std::pair<int,int>(13, 1), std::pair<int,int>(-13, 1));
        result.C_ss2[i] = result.compute_effective_coupling(i, std::pair<int,int>(3, 1), std::pair<int,int>(-3, 1));
        result.C_cc2[i] = result.compute_effective_coupling(i, std::pair<int,int>(4, 1), std::pair<int,int>(-4, 1));
      }

      // Use couplings to get effective third-generation couplings
      for(int i = 0; i < 3; i++)
      {
        // Compute effective couplings
        double g2_s[3], g2_p[3];
        for (int j = 0; j < 3; j++) // j=0,1,2 => tau, t, b
        {
          fh_complex fh_L = Dep::FH_Couplings_output->couplings[H0FF(i+1,j+2,3,3)-1];
          fh_complex fh_R = Dep::FH_Couplings_output->couplings[H0FF(i+1,j+2,3,3)+Roffset-1];
          fh_complex fh_SM_L = Dep::FH_Couplings_output->couplings_sm[H0FF(i+1,j+2,3,3)-1];
          fh_complex fh_SM_R = Dep::FH_Couplings_output->couplings_sm[H0FF(i+1,j+2,3,3)+RSMoffset-1];
          std::complex<double> L(fh_L.re,fh_L.im);
          std::complex<double> R(fh_R.re,fh_R.im);
          std::complex<double> SM_L(fh_SM_L.re,fh_SM_L.im);
          std::complex<double> SM_R(fh_SM_R.re,fh_SM_R.im);
          g2_s[j] = 0.25*pow(std::abs(R/SM_R + L/SM_L), 2.);
          g2_p[j] = 0.25*pow(std::abs(R/SM_R - L/SM_L), 2.);
        }
        result.C_tautau2[i] = g2_s[0] + g2_p[0];
        result.C_tt2[i]     = g2_s[1] + g2_p[1];
        result.C_bb2[i]     = g2_s[2] + g2_p[2];

        // Calculate CP of state
        if(g2_p[2] < 1e-10)
          result.CP[i] = 1;
        else if(g2_s[2] < 1e-10)
          result.CP[i] = -1;
        else
          result.CP[i] = 0.;
      }

      // Use couplings to get di-boson effective couplings
      for(int i = 0; i < 3; i++)
      {
        fh_complex c_gWW = Dep::FH_Couplings_output->couplings[H0VV(i+1,4)-1];
        fh_complex c_gWW_SM = Dep::FH_Couplings_output->couplings_sm[H0VV(i+1,4)-1];
        fh_complex c_gZZ = Dep::FH_Couplings_output->couplings[H0VV(i+1,3)-1];
        fh_complex c_gZZ_SM = Dep::FH_Couplings_output->couplings_sm[H0VV(i+1,3)-1];
        std::complex<double> WW(c_gWW.re,c_gWW.im);
        std::complex<double> WW_SM(c_gWW_SM.re,c_gWW_SM.im);
        std::complex<double> ZZ(c_gZZ.re,c_gZZ.im);
        std::complex<double> ZZ_SM(c_gZZ_SM.re,c_gZZ_SM.im);
        result.C_WW2[i] = pow(std::abs(WW/WW_SM), 2.);
        result.C_ZZ2[i] = pow(std::abs(ZZ/ZZ_SM), 2.);
      }

      // Use couplings to get hhZ effective couplings
      double norm = sminputs.GF*sqrt(2.)*sminputs.mZ*sminputs.mZ;
      for(int i = 0; i < 3; i++)
      for(int j = 0; j < 3; j++)
      {
        fh_complex c_gHV = Dep::FH_Couplings_output->couplings[H0HV(i+1,j+1)-1];
        double g2HV = c_gHV.re*c_gHV.re+c_gHV.im*c_gHV.im;
        result.C_hiZ2[i][j] = g2HV/norm;
      }

      // Work out which invisible decays are possible
      result.invisibles = get_invisibles(spec);
    }


/////////////////////////////
//// Map output routines ////
/////////////////////////////

    /// @{ Convert MSSM type Spectrum object into a map, so it can be printed
    template<class Contents>
    void fill_map_from_subspectrum(std::map<std::string,double>&, const SubSpectrum&);

    /// Adds additional information from interesting combinations of MSSM parameters
    void add_extra_MSSM_parameter_combinations(std::map<std::string,double>& specmap, const SubSpectrum& mssm)
    {
      double At = 0;
      double Ab = 0;
      const double Yt = mssm.get(Par::dimensionless, "Yu", 3, 3);
      const double Yb = mssm.get(Par::dimensionless, "Yd", 3, 3);
      if(std::abs(Yt) > 1e-12)
      {
        At = mssm.get(Par::mass1, "TYu", 3, 3) / Yt;
      }
      if(std::abs(Yb) > 1e-12)
      {
        Ab = mssm.get(Par::mass1, "TYd", 3, 3) / Yb;
      }

      const double MuSUSY = mssm.get(Par::mass1, "Mu");
      const double tb = mssm.get(Par::dimensionless, "tanbeta");

      specmap["Xt"] = At - MuSUSY / tb;
      specmap["Xb"] = Ab - MuSUSY * tb;
      /// Determine which states are the third gens then add them for printing
      str msf1, msf2;
      /// Since this is for printing we only want to invalidate the point
      /// if this is completely wrong.
      /// We can also plot the mixing if we are suspicious.
      const static double tol = 0.5;
      const static bool pt_error = true;
      slhahelp::family_state_mix_matrix("~u", 3, msf1, msf2, mssm, tol,
                                        LOCAL_INFO, pt_error);
      specmap["mstop1"] =  mssm.get(Par::Pole_Mass, msf1);
      specmap["mstop2"] =  mssm.get(Par::Pole_Mass, msf2);
      slhahelp::family_state_mix_matrix("~d", 3, msf1, msf2, mssm, tol,
                                        LOCAL_INFO, pt_error);
      specmap["msbottom1"] =  mssm.get(Par::Pole_Mass, msf1);
      specmap["msbottom2"] =  mssm.get(Par::Pole_Mass, msf2);
      slhahelp::family_state_mix_matrix("~e-", 3, msf1, msf2, mssm, tol,
                                        LOCAL_INFO, pt_error);
      specmap["mstau1"] =  mssm.get(Par::Pole_Mass, msf1);
      specmap["mstau2"] =  mssm.get(Par::Pole_Mass, msf2);
      /// return mass eigenstate strings that best represent required gauge
      /// eigenstate
      const str gs_suL = slhahelp::mass_es_from_gauge_es("~u_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msupL"] = mssm.get(Par::Pole_Mass,gs_suL);
      const str gs_scL = slhahelp::mass_es_from_gauge_es("~c_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["mscharmL"] = mssm.get(Par::Pole_Mass,gs_scL);
      const str gs_sdL = slhahelp::mass_es_from_gauge_es("~d_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msdownL"] = mssm.get(Par::Pole_Mass,gs_sdL);
      const str gs_ssL = slhahelp::mass_es_from_gauge_es("~s_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msstrangeL"] = mssm.get(Par::Pole_Mass,gs_ssL);
      const str gs_suR = slhahelp::mass_es_from_gauge_es("~u_R", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msupR"] = mssm.get(Par::Pole_Mass,gs_suR);
      const str gs_scR = slhahelp::mass_es_from_gauge_es("~c_R", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["mscharmR"] = mssm.get(Par::Pole_Mass,gs_scR);
      const str gs_sdR = slhahelp::mass_es_from_gauge_es("~d_R", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msdownR"] = mssm.get(Par::Pole_Mass,gs_sdR);
      const str gs_ssR = slhahelp::mass_es_from_gauge_es("~s_R", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msstrangeR"] = mssm.get(Par::Pole_Mass,gs_ssR);
      const str gs_seL = slhahelp::mass_es_from_gauge_es("~e_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["mselectronL"] = mssm.get(Par::Pole_Mass,gs_seL);
      const str gs_sMuL = slhahelp::mass_es_from_gauge_es("~mu_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msmuonL"] = mssm.get(Par::Pole_Mass,gs_sMuL);
      const str gs_seR = slhahelp::mass_es_from_gauge_es("~e_R", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["mselectronR"] = mssm.get(Par::Pole_Mass,gs_seR);
      const str gs_sMuR = slhahelp::mass_es_from_gauge_es("~mu_R", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msmuonR"] = mssm.get(Par::Pole_Mass,gs_sMuR);
      const str gs_snu1 = slhahelp::mass_es_from_gauge_es("~nu_e_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msnue"] = mssm.get(Par::Pole_Mass,gs_snu1);
      const str gs_snu2 = slhahelp::mass_es_from_gauge_es("~nu_mu_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msnumu"] = mssm.get(Par::Pole_Mass,gs_snu2);
      const str gs_snu3 = slhahelp::mass_es_from_gauge_es("~nu_tau_L", mssm, tol,
                                                         LOCAL_INFO, pt_error);
      specmap["msnutau"] = mssm.get(Par::Pole_Mass,gs_snu3);

    }

    void get_MSSM_spectrum_as_map (std::map<std::string,double>& specmap)
    {
      namespace myPipe = Pipes::get_MSSM_spectrum_as_map;
      const Spectrum& mssmspec(*myPipe::Dep::MSSM_spectrum);
      try
      {
         fill_map_from_subspectrum<SpectrumContents::SM>  (specmap, mssmspec.get_LE());
      }
      catch(const Gambit::exception&)
      {
         // The above will fail for the SimpleSpectrum versions of the MSSM spectrum
         // because it uses SM_slha rather than SM for the LE subspectrum
         // TODO: Would be better to do this in a more elegant way than with exception
         // handling
         fill_map_from_subspectrum<SpectrumContents::SM_slha>  (specmap, mssmspec.get_LE());
      }
      fill_map_from_subspectrum<SpectrumContents::MSSM>(specmap, mssmspec.get_HE());
      add_extra_MSSM_parameter_combinations(specmap, mssmspec.get_HE());
    }
    void get_unimproved_MSSM_spectrum_as_map (std::map<std::string,double>& specmap)
    {
      namespace myPipe = Pipes::get_unimproved_MSSM_spectrum_as_map;
      const Spectrum& mssmspec(*myPipe::Dep::unimproved_MSSM_spectrum);
      try
      {
         fill_map_from_subspectrum<SpectrumContents::SM>  (specmap, mssmspec.get_LE());
      }
      catch(const Gambit::exception&)
      {
         // The above will fail for the SimpleSpectrum versions of the MSSM spectrum
         // because it uses SM_slha rather than SM for the LE subspectrum
         // TODO: Would be better to do this in a more elegant way than with exception
         // handling
         fill_map_from_subspectrum<SpectrumContents::SM_slha>  (specmap, mssmspec.get_LE());
      }
      fill_map_from_subspectrum<SpectrumContents::MSSM>(specmap, mssmspec.get_HE());
      add_extra_MSSM_parameter_combinations(specmap, mssmspec.get_HE());
    }
    /// @}

    /// Extract all parameters from a subspectrum and put them into a map
    template<class Contents>
    void fill_map_from_subspectrum(std::map<std::string,double>& specmap, const SubSpectrum& subspec)
    {
      /// Add everything... use spectrum contents routines to automate task (make sure to use correct template parameter!)
      static const Contents contents;
      static const std::vector<SpectrumParameter> required_parameters = contents.all_parameters();

      for(std::vector<SpectrumParameter>::const_iterator it = required_parameters.begin();
           it != required_parameters.end(); ++it)
      {
         const Par::Tags        tag   = it->tag();
         const std::string      name  = it->name();
         const std::vector<int> shape = it->shape();

         /// Verification routine should have taken care of invalid shapes etc, so won't check for that here.

         // Check scalar case
         if(shape.size()==1 and shape[0]==1)
         {
           std::ostringstream label;
           label << name <<" "<< Par::toString.at(tag);
           specmap[label.str()] = subspec.get(tag,name);
           // Check again ignoring overrides (if the value has an override defined)
           if(subspec.has(tag,name,overrides_only) and
              subspec.has(tag,name,ignore_overrides))
           {
             label << " (unimproved)";
             specmap[label.str()] = subspec.get(tag,name,ignore_overrides);
           }
         }
         // Check vector case
         else if(shape.size()==1 and shape[0]>1)
         {
           for(int i = 1; i<=shape[0]; ++i) {
             std::ostringstream label;
             label << name <<"_"<<i<<" "<< Par::toString.at(tag);
             specmap[label.str()] = subspec.get(tag,name,i);
             // Check again ignoring overrides
             if(subspec.has(tag,name,i,overrides_only) and
                subspec.has(tag,name,i,ignore_overrides))
             {
               label << " (unimproved)";
               specmap[label.str()] = subspec.get(tag,name,i,ignore_overrides);
             }
           }
         }
         // Check matrix case
         else if(shape.size()==2)
         {
           for(int i = 1; i<=shape[0]; ++i) {
             for(int j = 1; j<=shape[0]; ++j) {
               std::ostringstream label;
               label << name <<"_("<<i<<","<<j<<") "<<Par::toString.at(tag);
               specmap[label.str()] = subspec.get(tag,name,i,j);
               // Check again ignoring overrides
               if(subspec.has(tag,name,i,j,overrides_only) and
                  subspec.has(tag,name,i,j,ignore_overrides))
               {
                 label << " (unimproved)";
                 specmap[label.str()] = subspec.get(tag,name,i,j,ignore_overrides);
               }
             }
           }
         }
         // Deal with all other cases
         else
         {
           // ERROR
           std::ostringstream errmsg;
           errmsg << "Error, invalid parameter received while converting SubSpectrum with contents \""<<contents.getName()<<"\" to map of strings! This should no be possible if the spectrum content verification routines were working correctly; they must be buggy, please report this.";
           errmsg << "Problematic parameter was: "<< tag <<", " << name << ", shape="<< shape;
           SpecBit_error().forced_throw(LOCAL_INFO,errmsg.str());
         }
      }
      // add the gravitino mass
      if (subspec.has(Par::Pole_Mass, "~G")) specmap["~G Pole_Mass"] = subspec.get(Par::Pole_Mass, "~G");
      // add the scale!
      specmap["scale(Q)"] = subspec.GetScale();
    }

    void FeynHiggs_HiggsMass(triplet<double>& result)
    {
      using namespace Pipes::FeynHiggs_HiggsMass;
      //FH indices: 0=h0_1, 1=h0_2
      int i = 0;
      const SubSpectrum& spec = Dep::unimproved_MSSM_spectrum->get_HE();
      int higgs = SMlike_higgs_PDG_code(spec);
      if (higgs == 25) i = 0;
      else if (higgs == 35) i = 1;
      else SpecBit_error().raise(LOCAL_INFO, "Urecognised SM-like Higgs PDG code!");
      result.central = Dep::HiggsMasses->MH[i];
      result.upper = Dep::HiggsMasses->deltaMH[i];
      result.lower = Dep::HiggsMasses->deltaMH[i];
    }

    void FeynHiggs_HeavyHiggsMasses(map_int_triplet_dbl& result)
    {
      using namespace Pipes::FeynHiggs_HeavyHiggsMasses;
      const int neutrals[2] = {25, 35};
      int i = -1;
      const SubSpectrum& spec = Dep::unimproved_MSSM_spectrum->get_HE();
      int higgs = SMlike_higgs_PDG_code(spec);
      if (higgs == neutrals[0]) i = 1;
      else if (higgs == neutrals[1]) i = 0;
      else SpecBit_error().raise(LOCAL_INFO, "Urecognised SM-like Higgs PDG code!");
      result.clear();
      result[neutrals[i]].central = Dep::HiggsMasses->MH[i];
      result[neutrals[i]].upper = Dep::HiggsMasses->deltaMH[i];
      result[neutrals[i]].lower = Dep::HiggsMasses->deltaMH[i];
      result[36].central = Dep::HiggsMasses->MH[2];
      result[36].upper = Dep::HiggsMasses->deltaMH[2];
      result[36].lower = Dep::HiggsMasses->deltaMH[2];
      result[37].central = Dep::HiggsMasses->MH[3];
      result[37].upper = Dep::HiggsMasses->deltaMH[3];
      result[37].lower = Dep::HiggsMasses->deltaMH[3];
    }

    void SUSYHD_HiggsMass(triplet<double>& result)
    {
      using namespace Pipes::SUSYHD_HiggsMass;

      const Spectrum& fullspectrum = *Dep::unimproved_MSSM_spectrum;
      SLHAea::Coll slhaea = fullspectrum.getSLHAea(1);

      #ifdef SPECBIT_DEBUG
        cout << "****** calling SUSYHD_HiggsMass ******" << endl;
      #endif

      MList<MReal> parameterList = {
        SLHAea::to<double>(slhaea.at("HMIX").at(2).at(1)), // tanbeta
        SLHAea::to<double>(slhaea.at("MSOFT").at(1).at(1)), // M1
        SLHAea::to<double>(slhaea.at("MSOFT").at(2).at(1)), // M2
        SLHAea::to<double>(slhaea.at("MSOFT").at(3).at(1)), // M3
        SLHAea::to<double>(slhaea.at("HMIX").at(1).at(1)), // mu
        SLHAea::to<double>(slhaea.at("AU").at(3).at(2)), // At
        SLHAea::to<double>(slhaea.at("MSOFT").at(43).at(1)), // mQ3
        SLHAea::to<double>(slhaea.at("MSOFT").at(46).at(1)), // mU3
        SLHAea::to<double>(slhaea.at("MSOFT").at(49).at(1)), // mD3
        SLHAea::to<double>(slhaea.at("MSOFT").at(42).at(1)), // mQ2
        SLHAea::to<double>(slhaea.at("MSOFT").at(45).at(1)), // mU2
        SLHAea::to<double>(slhaea.at("MSOFT").at(48).at(1)), // mD2
        SLHAea::to<double>(slhaea.at("MSOFT").at(41).at(1)), // mQ1
        SLHAea::to<double>(slhaea.at("MSOFT").at(44).at(1)), // mU1
        SLHAea::to<double>(slhaea.at("MSOFT").at(47).at(1)), // mD1
        SLHAea::to<double>(slhaea.at("MSOFT").at(33).at(1)), // mL3
        SLHAea::to<double>(slhaea.at("MSOFT").at(36).at(1)), // mE3
        SLHAea::to<double>(slhaea.at("MSOFT").at(32).at(1)), // mL2
        SLHAea::to<double>(slhaea.at("MSOFT").at(35).at(1)), // mE2
        SLHAea::to<double>(slhaea.at("MSOFT").at(31).at(1)), // mL1
        SLHAea::to<double>(slhaea.at("MSOFT").at(34).at(1)), // mE1
        sqrt(SLHAea::to<double>(slhaea.at("HMIX").at(4).at(1))) // mA
      };

      MReal MHiggs = BEreq::SUSYHD_MHiggs(parameterList);

      #ifdef SPECBIT_DEBUG
        cout << "****** calling SUSYHD_DeltaHiggsMass ******" << endl;
      #endif

      MReal DeltaMHiggs = BEreq::SUSYHD_DeltaMHiggs(parameterList);

      result.central = MHiggs;

      bool use_SHD_uncertainty = runOptions->getValueOrDef<bool>(true, "use_SUSYHD_uncertainty");

      if(use_SHD_uncertainty)
      {
        result.upper = DeltaMHiggs;
        result.lower = DeltaMHiggs;
      }
      else
      {
        result.upper = 0.0;
        result.lower = 0.0;
      }

    }


    /// @} End Gambit module functions

  } // end namespace SpecBit
} // end namespace Gambit

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