#include "EXTAMP/Dipole_Wrapper_Process.H" #include "EXTAMP/External_ME_Interface.H" #include "PHASIC++/Selectors/Combined_Selector.H" #include "PHASIC++/Main/Process_Integrator.H" #include "PHASIC++/Process/Spin_Color_Correlated_ME2.H" #include "PDF/Main/NLOMC_Base.H" #include "ATOOLS/Org/MyStrStream.H" #include using namespace EXTAMP; Dipole_Wrapper_Process::Dipole_Wrapper_Process(const RS_Process& rsproc, CS_Dipole* dipole, BEAM::Beam_Spectra_Handler* beam, PDF::ISR_Handler* isr) : p_dipole(dipole) { /* Follow sherpa convetions here and set iFlavIJ()); PHASIC::Process_Base::SortFlavours(m_born_procinfo); m_born_flavs = m_born_procinfo.ExtractFlavours(); /* This wrapper has to call it's Init method itself, since it's not initialized through the framework like other procs. This re-orders flavours according to Sherpa conventions (reordering is also applied to the Process_Info instance). */ PHASIC::Process_Base::Init(rsproc.Info(), beam, isr, 0); /* Not done in any of the PHASIC base classes */ m_mincpl = rsproc.Info().m_mincpl; m_maxcpl = rsproc.Info().m_maxcpl; /* Construct an index maps such that Flavours[i] == Dipole()->Flavours[m_indexmap[i]] Flavours[m_inversemap] == Dipole()->Flavours[i] */ m_indexmap = ConstructIndexMapping(Dipole()->Flavours(), BornFlavours(),NIn()); m_inversemap = InvertIndexMapping(m_indexmap); /* Cross-check index mapping */ for(size_t i(0); iFlavours()[m_indexmap[i]]); assert(BornFlavours()[m_inversemap[i]] == dipole->Flavours()[i]); } /* Set name in accordance with Sherpa conventions so that it can be identified by MC@NLO process. Save name of born config as well for NLO_subevts. */ m_born_name = PHASIC::Process_Base::GenerateName(m_born_procinfo.m_ii, m_born_procinfo.m_fi); m_name = rsproc.Name()+"_RS"+ ATOOLS::ToString(I())+"_"+ ATOOLS::ToString(J())+"_"+ ATOOLS::ToString(K()); /* Set couplings: copy pointers from dipole correlated ME */ m_cpls.insert(std::make_pair("Alpha_QCD",Dipole()->CorrelatedME()->RunningQCD())); m_cpls.insert(std::make_pair("Alpha_QED",Dipole()->CorrelatedME()->RunningQED())); /* Fill Comninable Info */ m_cluster_flav_map = External_ME_Interface::ConstructCombinableMap(Flavours(),Info(),NIn()); m_id_vector = ConstructIDVector(); /* Cross-check index mapping in ID vector */ for(size_t i(0); i id = ATOOLS::ID(IDVector()[i]); /* Only check non-clustered flavours to avoid doing the flavour combination here*/ if (id.size()>1) continue; /* Now check that the flavour in real configuration has been properly mapped */ const ATOOLS::Flavour& real_flav = Flavours()[id.front()]; if(real_flav != born_flav) THROW(fatal_error, "Inconsistent flavour mapping"); } m_moms.resize(Dipole()->Flavours().size()); } PHASIC::Process_Info Dipole_Wrapper_Process::ConstructBornProcessInfo (const PHASIC::Process_Info& rsinfo, size_t i, size_t j, const ATOOLS::Flavour& flav_ij) { PHASIC::Process_Info ret(rsinfo); ret.m_fi.m_nlotype&=~ATOOLS::nlo_type::real; ret.Combine(i,j,flav_ij); return ret; } std::vector Dipole_Wrapper_Process::ConstructIndexMapping (const ATOOLS::Flavour_Vector& dipole_flavs, const ATOOLS::Flavour_Vector& process_flavs, size_t nin) { /* Construct Process_Info with same ordering as in dipole_flavs */ PHASIC::Subprocess_Info in; PHASIC::Subprocess_Info fi; for(size_t i(0); i ret; ret.resize(dipole_flavs.size()); for (size_t i(0);i fi_tags; pi.m_fi.GetTags(fi_tags); size_t nout = dipole_flavs.size()-nin; for (size_t i(0);i Dipole_Wrapper_Process::InvertIndexMapping (const std::vector& map) { std::vector ret(map.size()); for(size_t i(0); i Dipole_Wrapper_Process::ConstructIDVector() const { std::vector tmp = Dipole()->IDVector(); std::vector ret = tmp; for(size_t i(0); ifind(ATOOLS::nlo_type::rsub)==p_apmap->end()) (*p_apmap)[ATOOLS::nlo_type::rsub] = new PHASIC::StringProcess_Map(); std::string fname(m_name); size_t len(m_pinfo.m_addname.length()); if (len) fname=fname.erase(fname.rfind(m_pinfo.m_addname),len); (*(*p_apmap)[ATOOLS::nlo_type::rsub])[fname]=this; } bool Dipole_Wrapper_Process::Combinable(const size_t &idi,const size_t &idj) { bool ret = m_cluster_flav_map.find(idi | idj)!=m_cluster_flav_map.end(); return ret; } const ATOOLS::Flavour_Vector &Dipole_Wrapper_Process::CombinedFlavour(const size_t &idij) { std::map::const_iterator it = m_cluster_flav_map.find(idij); if(it==m_cluster_flav_map.end()) THROW(fatal_error, "Internal error"); return it->second; } void Dipole_Wrapper_Process::SetNLOMC(PDF::NLOMC_Base *const nlomc) { PHASIC::Process_Base::SetNLOMC(nlomc); Dipole()->SetSubtractionType(nlomc->SubtractionType()); } void Dipole_Wrapper_Process::CalcKinematics(const ATOOLS::Vec4D_Vector& p) { if(!Dipole()) THROW(fatal_error, "Invalid dipole pointer"); /* Dipole and dipole wrapper share the same flavour and momentum ordering in the real emission configuration, pass momentum on as-is */ Dipole()->CalcKinematics(p); /* Apply re-mapping of (Born-) momenta, invert incoming momenta for NLO_subevts */ for(size_t i(0); iMomenta()[m_indexmap[i]]); for(size_t i(NIn()); iMomenta()[m_indexmap[i]]); } double Dipole_Wrapper_Process::Calc(ATOOLS::NLO_subevt* const evt) { double sign = MCModeSign(evt); double dxs = (sign==0.0) ? 0.0 : sign*Dipole()->Calc()/NormFac(); /* NLO_subevts of RS proc are added up by PHASIC::Single_Process, hence add a minus sign here */ evt->m_me = -dxs; evt->m_mewgt = -dxs; evt->m_result = -dxs; return dxs; } void Dipole_Wrapper_Process::CalculateScale(const ATOOLS::Vec4D_Vector& real_p, const ATOOLS::Vec4D_Vector& born_p, ATOOLS::NLO_subevt* const evt) { /* The scale setter knows about the RS and all other dipoles via the subevtlist of the RS. Scale setting might be based on the real configuration so we have to set the momenta of the RS here before calling the scale setter. */ RS_Process* rsproc = ((RS_Process*)(evt->p_real->p_proc)); rsproc->Integrator()->SetMomenta(real_p); /* Locally set flavours to born configuration in order not to confuse the scale setter */ ATOOLS::Flavour_Vector tmp = m_flavs; m_flavs = m_born_flavs; ScaleSetter()->CalculateScale(born_p); m_flavs = tmp; } double Dipole_Wrapper_Process::Partonic(const ATOOLS::Vec4D_Vector &p, int mode) { CalcKinematics(p); /* Use the trigger method of the RS process to set the m_trig member of this subevent. Also triggers the real and all other dipoles but whatever... */ PHASIC::Process_Base* rsproc = (PHASIC::Process_Base*)(p_subevent->p_real->p_proc); ATOOLS::NLO_subevtlist* subs = rsproc->GetRSSubevtList(); rsproc->Selector()->RSTrigger(subs); /* This method is called in MC@NLO matching from outside an RS_Process. Hence have to calc scales ourselves */ CalculateScale(p, m_moms, p_subevent); double dxs = Calc(p_subevent); return m_lastxs = m_mewgtinfo.m_B = dxs; } int Dipole_Wrapper_Process::MCModeSign(ATOOLS::NLO_subevt* const evt) const { /* m_mcmode is nonzero if this process is used to calcualte the DA-DS term or the DA term as defined in arXiv:1111.1200. Since DA=DS apart from theta functions implementing the PS starting scale in DA, we just have to implement a sign \in {-1,0,1} here. */ double kt2 = GetKT2ofSplitting(*Dipole()->LastKinematics()); double kt2max = GetMaxKT2ForDA(); evt->m_kt2 = kt2; int DS(1),DA(kt2<=kt2max*(1.0+1e-6)); /* For the calculation of DA-DS, another minus sign is added in PHASIC::Single_Process. Need to compensate for that here by yet another minus sign. */ if (m_mcmode==0) return + DS; if (m_mcmode==1) return -(DA-DS); if (m_mcmode==2) return + DA; THROW(fatal_error, "Unknown MC-mode "+ATOOLS::ToString(m_mcmode)); } double Dipole_Wrapper_Process::GetKT2ofSplitting(const Dipole_Kinematics& kin) const { /* Get hardness of splitting as defined by shower. Only needed for MC@NLO. */ if(!p_nlomc) return 0.0; double x = kin.ShowerX() ; double y = kin.ShowerY() ; double Q2 = kin.ShowerQ2(); return p_nlomc->KT2(*p_subevent,x,y,Q2); } double Dipole_Wrapper_Process::GetMaxKT2ForDA() const { /* Get the maximum hardness for a splitting: PS starting scale */ if(p_subevent->m_mu2.size()>(ATOOLS::stp::size+ATOOLS::stp::res)) return p_subevent->m_mu2[(ATOOLS::stp::id(ATOOLS::stp::size+ATOOLS::stp::res))]; return p_subevent->m_mu2[(ATOOLS::stp::res)]; }