diff --git a/Changes.md b/Changes.md index 0661e98..ec49d4d 100644 --- a/Changes.md +++ b/Changes.md @@ -1,145 +1,145 @@ # Changelog This is the log for changes to the HEJ program. Further changes to the HEJ API are documented in [`Changes-API.md`](Changes-API.md). If you are using HEJ as a library, please also read the changes there. ## Version 2.1 This release adds support for two new processes: * W boson with jets. * Jets with a charged lepton-antilepton pair via a virtual Z boson or photon. In addition, the complete set of first subleading processes (unordered gluon, central and extremal quark-antiquark pair) is implemented for pure jets and W + jets, see [arXiv:TODO](https://arxiv.org/abs/TODO). Unordered gluon emission is also supported for Higgs boson + jets and Z boson/photon + jets. This release include many changes to the code, which affect users of HEJ as a library (see [`Changes-API.md`](Changes-API.md)). ### 2.1.0 #### New Processes * Resummation for W bosons with jets - - New subleading processes `extremal qqx` & `central qqx` for a quark and + - New subleading processes `extremal qqbar` & `central qqbar` for a quark and anti-quark in the final state, e.g. `g g => u d_bar Wm g` (the other subleading processes also work with W's) - `HEJFOG` can generate multiple jets together with a (off-shell) W bosons decaying into lepton & neutrino * Resummation for jets with a charged lepton-antilepton pair via a virtual Z boson or photon. Includes the `unordered` subleading process. * Resummation can now be performed on all subleading processes within pure jets also. This includes `unordered`, `extremal qqbar` and `central qqbar` processes. #### More Physics implementation * Partons now have a Colour charge - Colours are read from and written to LHE files - For reweighted events the colours are created according to leading colour in the FKL limit * Use relative fraction for soft transverse momentum in tagging jets (`soft pt regulator`) as new (optional) parameter. - This supersedes `min extparton pt`, which is marked **deprecated** and will be removed in future versions. - This is a direct replacement for the old `max ext soft pt fraction`, which is also **deprecated**. * Changed the redistribution of the momenta of soft emissions. Now also bosons can take part of the recoil (previously only jets). #### Updates to Runcard * Allow multiplication and division of multiple scale functions e.g. `H_T/2*m_j1j2` * Grouped `event treatment` for subleading channels together in runcard - Rename `non-HEJ` processes to `non-resummable` * Read electro-weak constants from input - new mandatory setting `vev` to change vacuum expectation value - new mandatory settings `particle properties` to specify mass & width of bosons - `HEJFOG`: decays are now specified in `decays` setting (previously under `particle properties`) * Allow loading multiple analyses with `analyses`. The old `analysis` (with "i") is marked **deprecated**. * Optional setting to specify maximal number of Fixed Order events (`max events`, default is all) * Allow changing the regulator lambda in input (`regulator parameter`, only for advanced users) #### Changes to Input/Output * Added support to read & write `hdf5` event files suggested in [arXiv:1905.05120](https://arxiv.org/abs/1905.05120) (needs [HighFive](https://github.com/BlueBrain/HighFive)) * Support input with average weight equal to the cross section (`IDWTUP=1 or 4`) * Support unmodified Les Houches Event Files written by Sherpa with `cross section = sum(weights)/sum(trials)` * Analyses now get general run information (`LHEF::HEPRUP`) in the constructor. **This might break previously written, external analyses!** - external analyses should now be created with `make_analysis(YAML::Node const & config, LHEF::HEPRUP const & heprup)` * Support `rivet` version 3 with both `HepMC` version 2 and 3 - Multiple weights with `rivet 3` will only create one `.yoda` file (instead of one per weight/scale) * Added option to unweight only resummation events (`unweight: {type: resummation}`) * Added option for partially unweighting resummation events, similar to the fixed-order generator. * Improved unweighting algorithm. * Follow HepMC convention for particle Status codes: incoming = 11, decaying = 2, outgoing = 1 (unchanged) #### Miscellaneous * Print cross sections at end of run * Added example analysis & scale to `examples/`. Everything in `examples/` will be build when the flag `-DBUILD_EXAMPLES=TRUE` is set in `cmake`. * Dropped support for HepMC 3.0.0, either HepMC version 2 or >3.1 is required - It is now possible to write out both HepMC 2 and HepMC 3 events at the same time * Require LHADPF version 6. Dropped support for all other versions. * Use `git-lfs` for raw data in test (`make test` now requires `git-lfs`) * Currents are now generated with [`FORM`](https://github.com/vermaseren/form) - `FORM` is included as a `git submodule`, use `git submodule update --init` to download `FORM` * Create [Sphinx](http://sphinx-doc.org/) and [Doxygen](http://doxygen.org/) documentation by `make sphinx` or `make doxygen` in your `build/` folder ## Version 2.0 First release of HEJ 2. Complete code rewrite compared to HEJ 1. Improved matching to Fixed Order ([arXiv:1805.04446](https://arxiv.org/abs/1805.04446)). Implemented processes: Higgs boson with jets (FKL and unordered gluon emission, with finite quark mass loop, [arXiv:1812.08072](https://arxiv.org/abs/1812.08072)), and pure jets (only FKL). See [arXiv:1902.08430](https://arxiv.org/abs/1902.08430) ### 2.0.6 * Fixed compiling rivet when YODA headers are _outside_ of rivet directory ### 2.0.5 * Fixed event classification for input not ordered in rapidity ### 2.0.4 * Fixed wrong path of `HEJ_INCLUDE_DIR` in `hej-config.cmake` ### 2.0.3 * Fixed parsing of (numerical factor) * (base scale) in configuration * Don't change scale names, but sanitise Rivet output file names instead ### 2.0.2 * Changed scale names to `"_over_"` and `"_times_"` for proper file names (was `"/"` and `"*"` before) ### 2.0.1 * Fixed name of fixed-order generator in error message. ### 2.0.0 * First release diff --git a/FixedOrderGen/configFO.yml b/FixedOrderGen/configFO.yml index 7630ec5..d2472dc 100644 --- a/FixedOrderGen/configFO.yml +++ b/FixedOrderGen/configFO.yml @@ -1,95 +1,95 @@ ## Number of generated events events: 200 jets: min pt: 20 # minimal jet pt, should be slightly below analysis cut peak pt: 30 # peak pt of jets, should be at analysis cut algorithm: antikt # jet clustering algorithm R: 0.4 # jet R parameter max rapidity: 5 # maximum jet rapidity ## Particle beam beam: energy: 6500 particles: [p, p] ## PDF ID according to LHAPDF pdf: 230000 ## Scattering process process: p p => Wp 4j ## Particle decays (multiple decays are allowed) decays: Higgs: {into: [photon, photon], branching ratio: 0.0023568762400521404} Wp: {into: [e+, nu_e], branching ratio: 1} Wm: {into: [e-, nu_e_bar]} # equivalent to "branching ratio: 1" ## Fraction of events with two extremal emissions in one direction ## that contain an subleading emission e.g. unordered emission subleading fraction: 0.05 ## Allow different subleading configurations. ## By default all processes are allowed. ## This does not check if the processes are implemented in HEJ! # # subleading channels: # - unordered -# - central qqx -# - extremal qqx +# - central qqbar +# - extremal qqbar ## Unweighting parameters ## remove to obtain weighted events # unweight: # sample size: 200 # should be similar to "events:", but not more than ~10000 # max deviation: 0 ## --- The following settings are equivalent to HEJ, --- ## ## --- see HEJ for reference. --- ## ## Central scale choice scales: max jet pperp ## Event output files event output: - HEJFO.lhe # - HEJFO_events.hepmc ## Analyses # # analyses: ## Rivet analysis # - rivet: MC_XS # rivet analysis name # output: HEJ # name of the yoda files, ".yoda" and scale suffix will be added ## Custom analysis # - plugin: /path/to/libmyanalysis.so # my analysis parameter: some value ## Selection of random number generator and seed random generator: name: mixmax # seed: 1 ## Vacuum expectation value vev: 246.2196508 ## Properties of the weak gauge bosons particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 ## Parameters for Higgs-gluon couplings ## this requires compilation with QCDloop # # Higgs coupling: # use impact factors: false # mt: 174 # include bottom: true # mb: 4.7 diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh index 9c01895..f1d122b 100644 --- a/FixedOrderGen/include/PhaseSpacePoint.hh +++ b/FixedOrderGen/include/PhaseSpacePoint.hh @@ -1,296 +1,296 @@ /** \file PhaseSpacePoint.hh * \brief Contains the PhaseSpacePoint Class * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include #include "boost/iterator/filter_iterator.hpp" #include "HEJ/Event.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/Particle.hh" #include "fastjet/PseudoJet.hh" #include "Decay.hh" #include "Status.hh" #include "Subleading.hh" namespace HEJ { class EWConstants; class PDF; class RNG; } namespace HEJFOG { struct JetParameters; struct Process; //! A point in resummation phase space class PhaseSpacePoint{ public: //! Iterator over partons using ConstPartonIterator = boost::filter_iterator< bool (*)(HEJ::Particle const &), std::vector::const_iterator >; //! Reverse Iterator over partons using ConstReversePartonIterator = std::reverse_iterator< ConstPartonIterator>; //! Default PhaseSpacePoint Constructor PhaseSpacePoint() = delete; //! PhaseSpacePoint Constructor /** * @param proc The process to generate * @param jet_param Jet defintion & cuts * @param pdf The pdf set (used for sampling) * @param E_beam Energie of the beam * @param subl_chance Chance to turn a potentially unordered * emission into an actual one * @param subl_channels Possible subleading channels. * see HEJFOG::Subleading * @param particle_properties Properties of producted boson * * Initially, only FKL phase space points are generated. subl_chance gives * the chance of turning one emissions into a subleading configuration, * i.e. either unordered or central quark/anti-quark pair. Unordered * emissions require that the most extremal emission in any direction is * a quark or anti-quark and the next emission is a gluon. Quark/anti-quark * pairs are only generated for W processes. At most one subleading * emission will be generated in this way. */ PhaseSpacePoint( Process const & proc, JetParameters const & jet_param, HEJ::PDF & pdf, double E_beam, double subl_chance, Subleading subl_channels, ParticlesDecayMap const & particle_decays, HEJ::EWConstants const & ew_parameters, HEJ::RNG & ran ); //! Get Weight Function /** * @returns Weight of Event */ double weight() const{ return weight_; } Status status() const{ return status_; } //! Get Incoming Function /** * @returns Incoming Particles */ std::array const & incoming() const{ return incoming_; } //! Get Outgoing Function /** * @returns Outgoing Particles */ std::vector const & outgoing() const{ return outgoing_; } std::unordered_map> const & decays() const{ return decays_; } //! Iterator to the first outgoing parton ConstPartonIterator begin_partons() const; //! Iterator to the first outgoing parton ConstPartonIterator cbegin_partons() const; //! Iterator to the end of the outgoing partons ConstPartonIterator end_partons() const; //! Iterator to the end of the outgoing partons ConstPartonIterator cend_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator rbegin_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator crbegin_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator rend_partons() const; //! Reverse Iterator to the first outgoing parton ConstReversePartonIterator crend_partons() const; private: static constexpr std::size_t NUM_PARTONS = 11; //!< Number of partons public: using part_mask = std::bitset; //!< Selection mask for partons private: friend HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); /** * @internal * @brief Generate LO parton momentum * * @param np Number of partons to generate * @param is_pure_jets If true ensures momentum conservation in x and y * @param jet_param Jet properties to fulfil * @param max_pt max allowed pt for a parton (typically E_CMS) * @param ran Random Number Generator * * @returns Momentum of partons * * Ensures that each parton is in its own jet. * Generation is independent of parton flavour. Output is sorted in rapidity. */ std::vector gen_LO_partons( int np, bool is_pure_jets, JetParameters const & jet_param, double max_pt, HEJ::RNG & ran ); HEJ::Particle gen_boson( HEJ::ParticleID bosonid, double mass, double width, HEJ::RNG & ran ); template fastjet::PseudoJet gen_last_momentum( ParticleMomenta const & other_momenta, double mass_square, double y ) const; bool jets_ok( std::vector const & Born_jets, std::vector const & partons ) const; /** * @internal * @brief Generate incoming partons according to the PDF * * @param uf Scale used in the PDF */ void reconstruct_incoming( Process const & proc, double subl_chance, Subleading subl_channels, HEJ::PDF & pdf, double E_beam, double uf, HEJ::RNG & ran ); HEJ::ParticleID generate_incoming_id( std::size_t beam_idx, double x, double uf, HEJ::PDF & pdf, part_mask allowed_partons, HEJ::RNG & ran ); //! @brief Returns list of all allowed initial states partons std::array allowed_incoming( Process const & proc, double subl_chance, Subleading subl_channels, HEJ::RNG & ran ); //! Ensure that incoming state compatible with A/W/Z production std::array incoming_AWZ( Process const & proc, Subleading subl_channels, std::array allowed_partons, HEJ::RNG & ran ); - //! Ensure that incoming state compatible with extremal qqx - std::array incoming_eqqx( + //! Ensure that incoming state compatible with extremal qqbar + std::array incoming_eqqbar( std::array allowed_partons, HEJ::RNG & ran); //! Ensure that incoming state compatible with unordered std::array incoming_uno( std::array allowed_partons, HEJ::RNG & ran); bool momentum_conserved(double ep) const; bool extremal_FKL_ok( std::vector const & partons ) const; double random_normal(double stddev, HEJ::RNG & ran); /** * @internal * @brief Turns a FKL configuration into a subleading one * * @param chance Chance to switch to subleading configuration * @param channels Allowed channels for subleading process * @param proc Process to decide which subleading * configurations are allowed * * With a chance of "chance" the FKL configuration is either turned into * a unordered configuration or, for A/W/Z bosons, a configuration with * a central quark/anti-quark pair. */ void maybe_turn_to_subl(double chance, Subleading channels, Process const & proc, HEJ::RNG & ran); void turn_to_subl( Subleading channels, bool can_be_uno_backward, bool can_be_uno_forward, bool allow_strange, HEJ::RNG & ran); void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward, HEJ::RNG & ran); - HEJ::ParticleID select_qqx_flavour(bool allow_strange, HEJ::RNG & ran); - void turn_to_cqqx(bool allow_strange, HEJ::RNG & ran); - void turn_to_eqqx(bool allow_strange, HEJ::RNG & ran); + HEJ::ParticleID select_qqbar_flavour(bool allow_strange, HEJ::RNG & ran); + void turn_to_cqqbar(bool allow_strange, HEJ::RNG & ran); + void turn_to_eqqbar(bool allow_strange, HEJ::RNG & ran); //! decay where we select the decay channel std::vector decay_channel( HEJ::Particle const & parent, std::vector const & decays, HEJ::RNG & ran ); /// @brief setup outgoing partons to ensure correct coupling to boson void couple_boson(HEJ::ParticleID boson, HEJ::RNG & ran); Decay select_decay_channel( std::vector const & decays, HEJ::RNG & ran ); double gen_hard_pt( int np, double ptmin, double ptmax, double y, HEJ::RNG & ran ); double gen_soft_pt(int np, double max_pt, HEJ::RNG & ran); double gen_parton_pt( int count, JetParameters const & jet_param, double max_pt, double y, HEJ::RNG & ran ); //! Iterator over partons (non-const) using PartonIterator = boost::filter_iterator< bool (*)(HEJ::Particle const &), std::vector::iterator >; //! Reverse Iterator over partons (non-const) using ReversePartonIterator = std::reverse_iterator; //! Iterator to the first outgoing parton (non-const) PartonIterator begin_partons(); //! Iterator to the end of the outgoing partons (non-const) PartonIterator end_partons(); //! Reverse Iterator to the first outgoing parton (non-const) ReversePartonIterator rbegin_partons(); //! Reverse Iterator to the first outgoing parton (non-const) ReversePartonIterator rend_partons(); double weight_; Status status_; std::array incoming_; std::vector outgoing_; //! Particle decays in the format {outgoing index, decay products} std::unordered_map> decays_; }; //! Extract HEJ::Event::EventData from PhaseSpacePoint HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); } // namespace HEJFOG diff --git a/FixedOrderGen/include/Subleading.hh b/FixedOrderGen/include/Subleading.hh index f86935a..9f785e1 100644 --- a/FixedOrderGen/include/Subleading.hh +++ b/FixedOrderGen/include/Subleading.hh @@ -1,34 +1,34 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include namespace HEJFOG { namespace subleading { //!< @TODO confusing name with capital Subleading /** * Bit position of different subleading channels * e.g. (unsigned int) 1 => only unordered */ enum Channels: unsigned { uno, unordered = uno, - cqqx, - central_qqx = cqqx, - eqqx, - extremal_qqx = eqqx, + cqqbar, + central_qqbar = cqqbar, + eqqbar, + extremal_qqbar = eqqbar, first = uno, - last = eqqx, + last = eqqbar, }; } // namespace subleading using Subleading = std::bitset; namespace subleading { static constexpr Subleading ALL{~0u}; static constexpr Subleading NONE{0u}; } } // namespace HEJFOG diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc index f7071c3..7e94249 100644 --- a/FixedOrderGen/src/PhaseSpacePoint.cc +++ b/FixedOrderGen/src/PhaseSpacePoint.cc @@ -1,976 +1,976 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "PhaseSpacePoint.hh" #include #include #include #include #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" #include "HEJ/PDF.hh" #include "HEJ/Particle.hh" #include "HEJ/RNG.hh" #include "HEJ/exceptions.hh" #include "HEJ/kinematics.hh" #include "HEJ/utility.hh" #include "JetParameters.hh" #include "Process.hh" namespace HEJFOG { HEJ::Event::EventData to_EventData(PhaseSpacePoint psp){ //! @TODO Same function already in HEJ HEJ::Event::EventData result; result.incoming = std::move(psp).incoming_; // NOLINT(bugprone-use-after-move) result.outgoing = std::move(psp).outgoing_; // NOLINT(bugprone-use-after-move) // technically Event::EventData doesn't have to be sorted, // but PhaseSpacePoint should be anyway assert( std::is_sorted( begin(result.outgoing), end(result.outgoing), HEJ::rapidity_less{} ) ); assert(result.outgoing.size() >= 2); result.decays = std::move(psp).decays_; // NOLINT(bugprone-use-after-move) static_assert( std::numeric_limits::has_quiet_NaN, "no quiet NaN for double" ); constexpr double nan = std::numeric_limits::quiet_NaN(); result.parameters.central = {nan, nan, psp.weight()}; // NOLINT(bugprone-use-after-move) return result; } PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::begin_partons() const { return cbegin_partons(); } PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::cbegin_partons() const { return {HEJ::is_parton, cbegin(outgoing()), cend(outgoing())}; } PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::end_partons() const { return cend_partons(); } PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::cend_partons() const { return {HEJ::is_parton, cend(outgoing()), cend(outgoing())}; } PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::rbegin_partons() const { return crbegin_partons(); } PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::crbegin_partons() const { return std::reverse_iterator( cend_partons() ); } PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::rend_partons() const { return crend_partons(); } PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::crend_partons() const { return std::reverse_iterator( cbegin_partons() ); } PhaseSpacePoint::PartonIterator PhaseSpacePoint::begin_partons() { return {HEJ::is_parton, begin(outgoing_), end(outgoing_)}; } PhaseSpacePoint::PartonIterator PhaseSpacePoint::end_partons() { return {HEJ::is_parton, end(outgoing_), end(outgoing_)}; } PhaseSpacePoint::ReversePartonIterator PhaseSpacePoint::rbegin_partons() { return std::reverse_iterator( end_partons() ); } PhaseSpacePoint::ReversePartonIterator PhaseSpacePoint::rend_partons() { return std::reverse_iterator( begin_partons() ); } namespace { bool can_swap_to_uno( HEJ::Particle const & p1, HEJ::Particle const & p2 ){ assert(is_parton(p1) && is_parton(p2)); return p1.type != HEJ::pid::gluon && p2.type == HEJ::pid::gluon; } size_t count_gluons(PhaseSpacePoint::ConstPartonIterator first, PhaseSpacePoint::ConstPartonIterator last ){ return std::count_if(first, last, [](HEJ::Particle const & p) {return p.type == HEJ::pid::gluon;}); } /** assumes FKL configurations between first and last, * else there can be a quark in a non-extreme position * e.g. uno configuration gqg would pass */ - Subleading possible_qqx( + Subleading possible_qqbar( PhaseSpacePoint::ConstPartonIterator first, - const PhaseSpacePoint::ConstReversePartonIterator& last + PhaseSpacePoint::ConstReversePartonIterator const & last ){ using namespace subleading; assert( std::distance( first,last.base() )>2 ); Subleading channels = ALL; - channels.reset(eqqx); - channels.reset(cqqx); + channels.reset(eqqbar); + channels.reset(cqqbar); auto const ngluon = count_gluons(first,last.base()); if(ngluon < 2) return channels; if(first->type==HEJ::pid::gluon || last->type==HEJ::pid::gluon){ - channels.set(eqqx); + channels.set(eqqbar); } if(std::distance(first,last.base())>=4){ - channels.set(cqqx); + channels.set(cqqbar); } return channels; } template bool uno_possible( PartonIt first_parton, OutIt first_out ){ using namespace HEJ; // Special case: Higgs can not be outside of uno if(first_out->type == pid::Higgs || std::next(first_out)->type==pid::Higgs){ return false; } // decide what kind of subleading process is allowed return can_swap_to_uno( *first_parton, *std::next(first_parton) ); } bool is_AWZ_proccess(Process const & proc){ return proc.boson && HEJ::is_AWZ_boson(*proc.boson); } bool is_up_type(HEJ::Particle const & part){ return is_anyquark(part) && ((std::abs(part.type)%2) == 0); } bool is_down_type(HEJ::Particle const & part){ return is_anyquark(part) && ((std::abs(part.type)%2) != 0); } bool can_couple_to_W( HEJ::Particle const & part, HEJ::pid::ParticleID const W_id ){ const int W_charge = W_id>0?1:-1; return std::abs(part.type) 0 && is_up_type(part)) || (W_charge*part.type < 0 && is_down_type(part)) ); } Subleading ensure_AWZ( double & subl_chance, bool & allow_strange, HEJ::ParticleID const boson, PhaseSpacePoint::ConstPartonIterator first_parton, PhaseSpacePoint::ConstPartonIterator last_parton ){ auto channels = subleading::ALL; if(std::none_of(first_parton, last_parton, [&boson](HEJ::Particle const & p){ return can_couple_to_W(p, boson);})) { - // enforce qqx if A/W/Z can't couple somewhere else + // enforce qqbar if A/W/Z can't couple somewhere else // this is ensured to work through filter_partons in reconstruct_incoming channels.reset(subleading::uno); assert(channels.any()); subl_chance = 1.; // strange not allowed for W if(std::abs(boson)== HEJ::pid::Wp) allow_strange = false; } return channels; } } // namespace void PhaseSpacePoint::turn_to_subl( Subleading const channels, bool const can_be_uno_backward, bool const can_be_uno_forward, bool const allow_strange, HEJ::RNG & ran ){ double const nchannels = channels.count(); double const step = 1./nchannels; weight_*=nchannels; unsigned selected = subleading::first; double rnd = nchannels>1?ran.flat():0.; // @TODO optimise this sampling for(; selected<=subleading::last; ++selected){ assert(rnd>=0); if(channels[selected]){ if(rnd= 2); // decide what kind of subleading process is allowed bool const can_be_uno_backward = uno_possible(cbegin_partons(), outgoing_.cbegin()); bool const can_be_uno_forward = uno_possible(crbegin_partons(), outgoing_.crbegin()); if(channels[subleading::uno]){ channels.set(subleading::uno, can_be_uno_backward || can_be_uno_forward); } - channels &= possible_qqx(cbegin_partons(), crbegin_partons()); + channels &= possible_qqbar(cbegin_partons(), crbegin_partons()); bool allow_strange = true; if(is_AWZ_proccess(proc)) { channels &= ensure_AWZ(chance, allow_strange, *proc.boson, cbegin_partons(), cend_partons()); } std::size_t const nchannels = channels.count(); // no subleading if(nchannels==0) return; if(ran.flat() >= chance){ weight_ /= 1 - chance; return; } weight_ /= chance; // select channel return turn_to_subl( channels, can_be_uno_backward, can_be_uno_forward, allow_strange, ran); } void PhaseSpacePoint::turn_to_uno( const bool can_be_uno_backward, const bool can_be_uno_forward, HEJ::RNG & ran ){ if(!can_be_uno_backward && !can_be_uno_forward) return; if(can_be_uno_backward && can_be_uno_forward){ weight_ *= 2.; if(ran.flat() < 0.5){ return std::swap(begin_partons()->type, std::next(begin_partons())->type); } return std::swap(rbegin_partons()->type, std::next(rbegin_partons())->type); } if(can_be_uno_backward){ return std::swap(begin_partons()->type, std::next(begin_partons())->type); } assert(can_be_uno_forward); std::swap(rbegin_partons()->type, std::next(rbegin_partons())->type); } //! select flavour of quark - HEJ::ParticleID PhaseSpacePoint::select_qqx_flavour( + HEJ::ParticleID PhaseSpacePoint::select_qqbar_flavour( const bool allow_strange, HEJ::RNG & ran ){ const double r1 = 2.*ran.flat()-1.; const double max_flavour = allow_strange?HEJ::N_F:HEJ::N_F-1; weight_ *= max_flavour*2; double const flavour = HEJ::pid::down + std::floor(std::abs(r1)*max_flavour); return static_cast(flavour*(r1<0.?-1:1)); } - void PhaseSpacePoint::turn_to_cqqx(const bool allow_strange, HEJ::RNG & ran){ + void PhaseSpacePoint::turn_to_cqqbar(const bool allow_strange, HEJ::RNG & ran){ // we assume all FKL partons to be gluons auto first = std::next(begin_partons()); auto last = std::next(rbegin_partons()); auto const ng = std::distance(first, last.base()); if(ng < 2) - throw std::logic_error("not enough gluons to create qqx"); - auto flavour = select_qqx_flavour(allow_strange, ran); + throw std::logic_error("not enough gluons to create qqbar"); + auto flavour = select_qqbar_flavour(allow_strange, ran); // select gluon for switch if(ng!=2){ const double steps = 1./(ng-1.); weight_ /= steps; for(auto rnd = ran.flat(); rnd>steps; ++first){ rnd-=steps; } } first->type = flavour; std::next(first)->type = anti(flavour); } - void PhaseSpacePoint::turn_to_eqqx(const bool allow_strange, HEJ::RNG & ran){ + void PhaseSpacePoint::turn_to_eqqbar(const bool allow_strange, HEJ::RNG & ran){ /// find first and last gluon in FKL chain auto first = begin_partons(); const bool can_forward = !is_anyquark(*first); auto last = rbegin_partons(); const bool can_backward = !is_anyquark(*last); if(std::distance(first, last.base()) < 2) - throw std::logic_error("not enough gluons to create qqx"); - auto flavour = select_qqx_flavour(allow_strange, ran); + throw std::logic_error("not enough gluons to create qqbar"); + auto flavour = select_qqbar_flavour(allow_strange, ran); // select gluon for switch if(can_forward && !can_backward){ first->type = flavour; std::next(first)->type = anti(flavour); return; } if(!can_forward && can_backward){ last->type = flavour; std::next(last)->type = anti(flavour); return; } assert(can_forward && can_backward); weight_*=2.; if(ran.flat()>0.5){ first->type = flavour; std::next(first)->type = anti(flavour); return; } last->type = flavour; std::next(last)->type = anti(flavour); } template fastjet::PseudoJet PhaseSpacePoint::gen_last_momentum( ParticleMomenta const & other_momenta, const double mass_square, const double y ) const { std::array pt{0.,0.}; for (auto const & p: other_momenta) { pt[0]-= p.px(); pt[1]-= p.py(); } const double mperp = std::sqrt(pt[0]*pt[0]+pt[1]*pt[1]+mass_square); const double pz=mperp*std::sinh(y); const double E=mperp*std::cosh(y); return {pt[0], pt[1], pz, E}; } Decay PhaseSpacePoint::select_decay_channel( std::vector const & decays, HEJ::RNG & ran ){ double br_total = 0.; for(auto const & decay: decays) br_total += decay.branching_ratio; // adjust weight // this is given by (channel branching ratio)/(chance to pick channel) // where (chance to pick channel) = // (channel branching ratio)/(total branching ratio) weight_ *= br_total; if(decays.size()==1) return decays.front(); const double r1 = br_total*ran.flat(); double br_sum = 0.; for(auto const & decay: decays){ br_sum += decay.branching_ratio; if(r1 < br_sum) return decay; } throw std::logic_error{"unreachable"}; } namespace { //! generate decay products of a boson std::vector decay_boson( HEJ::Particle const & parent, std::vector const & decays, HEJ::RNG & ran ){ if(decays.size() != 2){ throw HEJ::not_implemented{ "only decays into two particles are implemented" }; } std::vector decay_products(decays.size()); for(size_t i = 0; i < decays.size(); ++i){ decay_products[i].type = decays[i]; } // choose polar and azimuth angle in parent rest frame const double E = parent.m()/2; const double theta = 2.*M_PI*ran.flat(); const double cos_phi = 2.*ran.flat()-1.; // Jacobian Factors for W in line 418 const double sin_phi = std::sqrt(1. - cos_phi*cos_phi); // Know 0 < phi < pi const double px = E*std::cos(theta)*sin_phi; const double py = E*std::sin(theta)*sin_phi; const double pz = E*cos_phi; decay_products[0].p.reset(px, py, pz, E); decay_products[1].p.reset(-px, -py, -pz, E); for(auto & particle: decay_products) particle.p.boost(parent.p); return decay_products; } } // namespace std::vector PhaseSpacePoint::decay_channel( HEJ::Particle const & parent, std::vector const & decays, HEJ::RNG & ran ){ const auto channel = select_decay_channel(decays, ran); return decay_boson(parent, channel.products, ran); } namespace { //! adds a particle to target (in correct rapidity ordering) //! @returns positon of insertion auto insert_particle(std::vector & target, HEJ::Particle && particle ){ const auto pos = std::upper_bound( begin(target),end(target),particle,HEJ::rapidity_less{} ); target.insert(pos, std::move(particle)); return pos; } } // namespace PhaseSpacePoint::PhaseSpacePoint( Process const & proc, JetParameters const & jet_param, HEJ::PDF & pdf, double E_beam, double const subl_chance, Subleading subl_channels, ParticlesDecayMap const & particle_decays, HEJ::EWConstants const & ew_parameters, HEJ::RNG & ran ){ assert(proc.njets >= 2); status_ = Status::good; weight_ = 1; // ensure that all setting are consistent if(subl_chance == 0.) subl_channels.reset(); const std::size_t nout = proc.njets + (proc.boson?1:0) + proc.boson_decay.size(); outgoing_.reserve(nout); // generate parton momenta const bool is_pure_jets = (nout == proc.njets); auto partons = gen_LO_partons( proc.njets, is_pure_jets, jet_param, E_beam, ran ); // pre fill flavour with gluons for( auto it = std::make_move_iterator(partons.begin()); it != std::make_move_iterator(partons.end()); ++it ){ outgoing_.emplace_back(HEJ::Particle{HEJ::pid::gluon, *it, {}}); } if(status_ != Status::good) return; if(proc.boson){ // decay boson auto const & boson_prop = ew_parameters.prop(*proc.boson) ; auto boson{ gen_boson(*proc.boson, boson_prop.mass, boson_prop.width, ran) }; const auto pos{insert_particle(outgoing_, std::move(boson))}; const size_t boson_idx = std::distance(begin(outgoing_), pos); auto const & boson_decay = particle_decays.find(*proc.boson); if( !proc.boson_decay.empty() ){ // decay given in proc decays_.emplace( boson_idx, decay_boson(outgoing_[boson_idx], proc.boson_decay, ran) ); } else if( boson_decay != particle_decays.end() && !boson_decay->second.empty() ){ // decay given explicitly decays_.emplace( boson_idx, decay_channel(outgoing_[boson_idx], boson_decay->second, ran) ); } } // normalisation of momentum-conserving delta function weight_ *= std::pow(2*M_PI, 4); /** @TODO * uf (jet_param.min_pt) doesn't correspond to our final scale choice. * The HEJ scale generators currently expect a full event as input, * so fixing this is not completely trivial */ reconstruct_incoming(proc, subl_chance, subl_channels, pdf, E_beam, jet_param.min_pt, ran); if(status_ != Status::good) return; // set outgoing states begin_partons()->type = incoming_[0].type; rbegin_partons()->type = incoming_[1].type; maybe_turn_to_subl(subl_chance, subl_channels, proc, ran); if(proc.boson) couple_boson(*proc.boson, ran); } // pt generation, see eq:pt_sampling in developer manual double PhaseSpacePoint::gen_hard_pt( const int np , const double ptmin, const double ptmax, const double /* y */, HEJ::RNG & ran ){ // heuristic parameter for pt sampling, see eq:pt_par in developer manual const double ptpar = ptmin + np/5.; const double arctan = std::atan((ptmax - ptmin)/ptpar); const double xpt = ran.flat(); const double pt = ptmin + ptpar*std::tan(xpt*arctan); const double cosine = std::cos(xpt*arctan); weight_ *= pt*ptpar*arctan/(cosine*cosine); return pt; } double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, HEJ::RNG & ran) { constexpr double ptpar = 4.; const double r = ran.flat(); const double pt = max_pt + ptpar/np*std::log(r); weight_ *= pt*ptpar/(np*r); return pt; } double PhaseSpacePoint::gen_parton_pt( int count, JetParameters const & jet_param, double max_pt, double y, HEJ::RNG & ran ) { constexpr double p_small_pt = 0.02; if(! jet_param.peak_pt) { return gen_hard_pt(count, jet_param.min_pt, max_pt, y, ran); } const double r = ran.flat(); if(r > p_small_pt) { weight_ /= 1. - p_small_pt; return gen_hard_pt(count, *jet_param.peak_pt, max_pt, y, ran); } weight_ /= p_small_pt; const double pt = gen_soft_pt(count, *jet_param.peak_pt, ran); if(pt < jet_param.min_pt) { weight_=0.0; status_ = Status::not_enough_jets; return jet_param.min_pt; } return pt; } std::vector PhaseSpacePoint::gen_LO_partons( int np, bool is_pure_jets, JetParameters const & jet_param, double max_pt, HEJ::RNG & ran ){ if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"}; weight_ /= std::pow(16.*std::pow(M_PI,3),np); weight_ /= std::tgamma(np+1); //remove rapidity ordering std::vector partons; partons.reserve(np); for(int i = 0; i < np; ++i){ const double y = -jet_param.max_y + 2*jet_param.max_y*ran.flat(); weight_ *= 2*jet_param.max_y; const bool is_last_parton = i+1 == np; if(is_pure_jets && is_last_parton) { constexpr double parton_mass_sq = 0.; partons.emplace_back(gen_last_momentum(partons, parton_mass_sq, y)); break; } const double phi = 2*M_PI*ran.flat(); weight_ *= 2.0*M_PI; const double pt = gen_parton_pt(np, jet_param, max_pt, y, ran); if(weight_ == 0.0) return {}; partons.emplace_back(fastjet::PtYPhiM(pt, y, phi)); assert(jet_param.min_pt <= partons[i].pt()); assert(partons[i].pt() <= max_pt+1e-5); } // Need to check that at LO, the number of jets = number of partons; fastjet::ClusterSequence cs(partons, jet_param.def); auto cluster_jets=cs.inclusive_jets(jet_param.min_pt); if (cluster_jets.size()!=unsigned(np)){ weight_=0.0; status_ = Status::not_enough_jets; return {}; } std::sort(begin(partons), end(partons), HEJ::rapidity_less{}); return partons; } HEJ::Particle PhaseSpacePoint::gen_boson( HEJ::ParticleID bosonid, double mass, double width, HEJ::RNG & ran ){ // Usual phase space measure weight_ /= 16.*std::pow(M_PI, 3); // Generate a y Gaussian distributed around 0 /// @TODO check magic numbers for different boson Higgs /// @TODO better sampling for W const double stddev_y = 1.6; const double y = random_normal(stddev_y, ran); const double r1 = ran.flat(); const double s_boson = mass*( mass + width*std::tan(M_PI/2.*r1 + (r1-1.)*std::atan(mass/width)) ); // off-shell s_boson sampling, compensates for Breit-Wigner /// @TODO use a flag instead if(std::abs(bosonid) == HEJ::pid::Wp){ weight_/=M_PI*M_PI*16.; //Corrects B-W factors, see git issue 132 weight_*= mass*width*( M_PI+2.*std::atan(mass/width) ) / ( 1. + std::cos( M_PI*r1 + 2.*(r1-1.)*std::atan(mass/width) ) ); } return { bosonid, gen_last_momentum(outgoing_, s_boson, y), {} }; } namespace { /// partons are ordered: even = anti, 0 = gluon HEJ::ParticleID index_to_pid(size_t i){ if(!i) return HEJ::pid::gluon; return static_cast( i%2 ? (i+1)/2 : -i/2 ); } /// partons are ordered: even = anti, 0 = gluon size_t pid_to_index(HEJ::ParticleID id){ if(id==HEJ::pid::gluon) return 0; return id>0 ? id*2-1 : std::abs(id)*2; } PhaseSpacePoint::part_mask init_allowed(HEJ::ParticleID const id){ if(std::abs(id) == HEJ::pid::proton) return ~0; PhaseSpacePoint::part_mask out{0}; if(HEJ::is_parton(id)) out[pid_to_index(id)] = true; return out; } /// decides which "index" (see index_to_pid) are allowed for process PhaseSpacePoint::part_mask allowed_quarks(HEJ::ParticleID const boson){ if(std::abs(boson) != HEJ::pid::Wp){ return ~1; // not a gluon } // special case W: // Wp: anti-down or up-type quark, no b/t // Wm: down or anti-up-type quark, no b/t return boson>0?0b00011001100 // NOLINT(readability-magic-numbers) :0b00100110010; // NOLINT(readability-magic-numbers) } } // namespace std::array PhaseSpacePoint::incoming_AWZ( Process const & proc, Subleading const subl_channels, std::array allowed_partons, HEJ::RNG & ran ){ assert(proc.boson); auto couple_parton = allowed_quarks(*proc.boson); - // eqqx possible if one incoming is a gluon - if(proc.njets >= 3 && subl_channels[subleading::eqqx]){ + // eqqbar possible if one incoming is a gluon + if(proc.njets >= 3 && subl_channels[subleading::eqqbar]){ couple_parton.set(pid_to_index(HEJ::ParticleID::gluon)); } if( // coupling possible through input allowed_partons[0] == (couple_parton&allowed_partons[0]) || allowed_partons[1] == (couple_parton&allowed_partons[1]) - // cqqx possible - || (proc.njets >= 4 && subl_channels[subleading::cqqx]) + // cqqbar possible + || (proc.njets >= 4 && subl_channels[subleading::cqqbar]) ){ return allowed_partons; } // only first can couple if( (allowed_partons[0]&couple_parton).any() &&(allowed_partons[1]&couple_parton).none() ){ return {allowed_partons[0]&couple_parton, allowed_partons[1]}; } // only second can couple if( (allowed_partons[0]&couple_parton).none() && (allowed_partons[1]&couple_parton).any() ){ return {allowed_partons[0], allowed_partons[1]&couple_parton}; } // both can couple if( (allowed_partons[0]&couple_parton).any() && (allowed_partons[1]&couple_parton).any() ){ double rnd = ran.flat(); weight_*=3.; if(rnd<1./3.){ return { allowed_partons[0] & couple_parton, allowed_partons[1] & ~couple_parton }; } if(rnd<2./3.){ return { allowed_partons[0] & ~couple_parton, allowed_partons[1] & couple_parton }; } return { allowed_partons[0] & couple_parton, allowed_partons[1] & couple_parton }; } throw std::invalid_argument{"Incoming state not allowed."}; } - std::array PhaseSpacePoint::incoming_eqqx( + std::array PhaseSpacePoint::incoming_eqqbar( std::array allowed_partons, HEJ::RNG & ran ){ auto const gluon_idx = pid_to_index(HEJ::pid::gluon); auto & first_beam = allowed_partons[0]; auto & second_beam = allowed_partons[1]; if(first_beam[gluon_idx] && !second_beam[gluon_idx]){ first_beam.reset(); first_beam.set(gluon_idx); return allowed_partons; } if(!first_beam[gluon_idx] && second_beam[gluon_idx]) { second_beam.reset(); second_beam.set(gluon_idx); return allowed_partons; } if(first_beam[gluon_idx] && second_beam[gluon_idx]) { // both beams can be gluons // if one can't be a quark everything is good auto first_quarks = first_beam; first_quarks.reset(gluon_idx); auto second_quarks = second_beam; second_quarks.reset(gluon_idx); if(first_quarks.none() || second_quarks.none()){ return allowed_partons; } // else choose one to be a gluon double rnd = ran.flat(); weight_*=3.; if(rnd<1./3.){ allowed_partons[0].reset(); allowed_partons[0].set(gluon_idx); allowed_partons[1].reset(gluon_idx); } else if(rnd<2./3.){ allowed_partons[1].reset(); allowed_partons[1].set(gluon_idx); allowed_partons[0].reset(gluon_idx); } else { allowed_partons[0].reset(); allowed_partons[0].set(gluon_idx); allowed_partons[1].reset(); allowed_partons[1].set(gluon_idx); } return allowed_partons; } throw std::invalid_argument{ - "Incoming state not allowed for pure extremal qqx."}; + "Incoming state not allowed for pure extremal qqbar."}; } std::array PhaseSpacePoint::incoming_uno( std::array allowed_partons, HEJ::RNG & ran ){ auto const gluon_idx = pid_to_index(HEJ::pid::gluon); auto & first_beam = allowed_partons[0]; auto first_quarks = first_beam; first_quarks.reset(gluon_idx); auto & second_beam = allowed_partons[1]; auto second_quarks = second_beam; second_quarks.reset(gluon_idx); if(first_quarks.any() && second_quarks.none()){ first_beam.reset(gluon_idx); return allowed_partons; } if(first_quarks.none() && second_quarks.any()) { second_beam.reset(gluon_idx); return allowed_partons; } if(first_quarks.any() && second_quarks.any()) { // both beams can be quarks // if one can't be gluon everything is good if(!first_beam[gluon_idx] || !second_beam[gluon_idx]){ return allowed_partons; } // else choose one to be a quark double rnd = ran.flat(); weight_*=3.; if(rnd<1./3.){ allowed_partons[0].reset(gluon_idx); allowed_partons[1].reset(); allowed_partons[1].set(gluon_idx); } else if(rnd<2./3.){ allowed_partons[1].reset(gluon_idx); allowed_partons[0].reset(); allowed_partons[0].set(gluon_idx); } else { allowed_partons[0].reset(gluon_idx); allowed_partons[1].reset(gluon_idx); } return allowed_partons; } throw std::invalid_argument{ "Incoming state not allowed for pure unordered."}; } /** * @brief Returns list of all allowed initial states partons * * checks which partons are allowed as initial state: * 1. only allow what is given in the Runcard (p -> all) * 2. A/W/Z require something to couple to - * a) no qqx => no incoming gluon + * a) no qqbar => no incoming gluon * b) 2j => no incoming gluon - * c) >3j => can couple OR is gluon => 2 gluons become qqx later - * 3. pure eqqx requires at least one gluon + * c) >3j => can couple OR is gluon => 2 gluons become qqbar later + * 3. pure eqqbar requires at least one gluon * 4. pure uno requires at least one quark */ std::array PhaseSpacePoint::allowed_incoming( Process const & proc, double const subl_chance, Subleading const subl_channels, HEJ::RNG & ran ){ // all possible incoming states std::array allowed_partons{ init_allowed(proc.incoming[0]), init_allowed(proc.incoming[1]) }; // special case A/W/Z if(proc.boson && is_AWZ_proccess(proc)){ allowed_partons = incoming_AWZ(proc, subl_channels, allowed_partons, ran); } // special case: pure subleading if(subl_chance!=1.){ return allowed_partons; } auto other_channels = subl_channels; - // pure eqqx - other_channels.reset(subleading::eqqx); + // pure eqqbar + other_channels.reset(subleading::eqqbar); if(other_channels.none()){ - return incoming_eqqx(allowed_partons, ran); + return incoming_eqqbar(allowed_partons, ran); } other_channels = subl_channels; // pure uno other_channels.reset(subleading::uno); if(other_channels.none()){ return incoming_uno(allowed_partons, ran); } return allowed_partons; } void PhaseSpacePoint::reconstruct_incoming( Process const & proc, double const subl_chance, Subleading const subl_channels, HEJ::PDF & pdf, double E_beam, double uf, HEJ::RNG & ran ){ std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_); // calculate xa, xb const double sqrts=2*E_beam; const double xa=(incoming_[0].E()-incoming_[0].pz())/sqrts; const double xb=(incoming_[1].E()+incoming_[1].pz())/sqrts; // abort if phase space point is outside of collider energy reach if (xa>1. || xb>1.){ weight_=0; status_ = Status::too_much_energy; return; } auto const & ids = proc.incoming; std::array allowed_partons = allowed_incoming(proc, subl_chance, subl_channels, ran); for(size_t i = 0; i < 2; ++i){ if(ids[i] == HEJ::pid::proton || ids[i] == HEJ::pid::p_bar){ // pick ids according to pdfs incoming_[i].type = generate_incoming_id(i, i?xb:xa, uf, pdf, allowed_partons[i], ran); } else { assert(allowed_partons[i][pid_to_index(ids[i])]); incoming_[i].type = ids[i]; } } assert(momentum_conserved(1e-7)); } HEJ::ParticleID PhaseSpacePoint::generate_incoming_id( size_t const beam_idx, double const x, double const uf, HEJ::PDF & pdf, part_mask allowed_partons, HEJ::RNG & ran ){ std::array pdf_wt{}; pdf_wt[0] = allowed_partons[0]? std::fabs(pdf.pdfpt(beam_idx,x,uf,HEJ::pid::gluon)):0.; double pdftot = pdf_wt[0]; for(size_t i = 1; i < pdf_wt.size(); ++i){ pdf_wt[i] = allowed_partons[i]? 4./9.*std::fabs(pdf.pdfpt(beam_idx,x,uf,index_to_pid(i))):0; pdftot += pdf_wt[i]; } const double r1 = pdftot * ran.flat(); double sum = 0; for(size_t i=0; i < pdf_wt.size(); ++i){ if (r1 < (sum+=pdf_wt[i])){ weight_*= pdftot/pdf_wt[i]; return index_to_pid(i); } } std::cerr << "Error in choosing incoming parton: "< allowed_parts; for(auto part_it=begin_partons(); part_it!=end_partons(); ++part_it){ // Wp -> up OR anti-down, Wm -> anti-up OR down, no bottom if ( can_couple_to_W(*part_it, boson) ) allowed_parts.push_back(part_it); } if(allowed_parts.empty()){ throw std::logic_error{"Found no parton for coupling with boson"}; } // select one and flip it size_t idx = 0; if(allowed_parts.size() > 1){ /// @TODO more efficient sampling /// old code: probability[i] = exp(parton[i].y - W.y) idx = std::floor(ran.flat()*allowed_parts.size()); weight_ *= allowed_parts.size(); } const int W_charge = boson>0?1:-1; allowed_parts[idx]->type = static_cast( allowed_parts[idx]->type - W_charge ); } double PhaseSpacePoint::random_normal( double stddev, HEJ::RNG & ran ){ const double r1 = ran.flat(); const double r2 = ran.flat(); const double lninvr1 = -std::log(r1); const double result = stddev*std::sqrt(2.*lninvr1)*std::cos(2.*M_PI*r2); weight_ *= exp(result*result/(2*stddev*stddev))*std::sqrt(2.*M_PI)*stddev; return result; } bool PhaseSpacePoint::momentum_conserved(double ep) const{ fastjet::PseudoJet diff; for(auto const & in: incoming()) diff += in.p; for(auto const & out: outgoing()) diff -= out.p; return HEJ::nearby_ep(diff, fastjet::PseudoJet{}, ep); } } // namespace HEJFOG diff --git a/FixedOrderGen/src/config.cc b/FixedOrderGen/src/config.cc index 3cdacfe..1a282ad 100644 --- a/FixedOrderGen/src/config.cc +++ b/FixedOrderGen/src/config.cc @@ -1,419 +1,419 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "config.hh" #include #include #include #include #include #include #include #include #include "HEJ/PDG_codes.hh" #include "HEJ/YAMLreader.hh" #include "HEJ/exceptions.hh" namespace HEJFOG { using HEJ::set_from_yaml; using HEJ::set_from_yaml_if_defined; using HEJ::pid::ParticleID; namespace { //! Get YAML tree of supported options /** * The configuration file is checked against this tree of options * in assert_all_options_known. */ YAML::Node const & get_supported_options(){ const static YAML::Node supported = [](){ YAML::Node supported; static const auto opts = { "process", "events", "subleading fraction","subleading channels", "scales", "scale factors", "max scale ratio", "pdf", "vev", "event output", "analyses", "analysis", "import scales" }; // add subnodes to "supported" - the assigned value is irrelevant for(auto && opt: opts) supported[opt] = ""; for(auto && jet_opt: {"min pt", "peak pt", "algorithm", "R", "max rapidity"}){ supported["jets"][jet_opt] = ""; } for(auto && particle_type: {"Higgs", "W", "Z"}){ for(auto && particle_opt: {"mass", "width"}){ supported["particle properties"][particle_type][particle_opt] = ""; } } for(auto && particle_type: {"Higgs", "Wp", "W+", "Wm", "W-", "Z"}){ for(auto && particle_opt: {"into", "branching ratio"}){ supported["decays"][particle_type][particle_opt] = ""; } } for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){ supported["Higgs coupling"][opt] = ""; } for(auto && beam_opt: {"energy", "particles"}){ supported["beam"][beam_opt] = ""; } for(auto && unweight_opt: {"sample size", "max deviation"}){ supported["unweight"][unweight_opt] = ""; } for(auto && opt: {"name", "seed"}){ supported["random generator"][opt] = ""; } return supported; }(); return supported; } JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ){ const auto p = HEJ::get_jet_parameters(node, entry); JetParameters result; result.def = p.def; result.min_pt = p.min_pt; set_from_yaml(result.max_y, node, entry, "max rapidity"); set_from_yaml_if_defined(result.peak_pt, node, entry, "peak pt"); if(result.peak_pt && *result.peak_pt <= result.min_pt) throw std::invalid_argument{ "Value of option 'peak pt' has to be larger than 'min pt'." }; return result; } Beam get_Beam( YAML::Node const & node, std::string const & entry ){ Beam beam; std::vector particles; set_from_yaml(beam.energy, node, entry, "energy"); set_from_yaml_if_defined(particles, node, entry, "particles"); if(! particles.empty()){ for(HEJ::ParticleID particle: particles){ if(particle != HEJ::pid::p && particle != HEJ::pid::p_bar){ throw std::invalid_argument{ "Unsupported value in option " + entry + ": particles:" " only proton ('p') and antiproton ('p_bar') beams are supported" }; } } if(particles.size() != 2){ throw std::invalid_argument{"Not exactly two beam particles"}; } beam.particles.front() = particles.front(); beam.particles.back() = particles.back(); } return beam; } std::vector split( std::string const & str, std::string const & delims ){ std::vector result; for(size_t begin = 0, end = 0; end != std::string::npos;){ begin = str.find_first_not_of(delims, end); if(begin == std::string::npos) break; end = str.find_first_of(delims, begin + 1); result.emplace_back(str.substr(begin, end - begin)); } return result; } std::invalid_argument invalid_incoming(std::string const & what){ return std::invalid_argument{ "Incoming particle type " + what + " not supported," " incoming particles have to be 'p', 'p_bar' or partons" }; } std::invalid_argument invalid_outgoing(std::string const & what){ return std::invalid_argument{ "Outgoing particle type " + what + " not supported," " outgoing particles have to be 'j', 'photon', 'H', 'Wm', 'Wp', 'e-', 'e+', 'nu_e', 'nu_e_bar'" }; } HEJ::ParticleID reconstruct_boson_id( std::vector const & ids ){ assert(ids.size()==2); const int pidsum = ids[0] + ids[1]; if(pidsum == +1) { assert(HEJ::is_antilepton(ids[0])); if(HEJ::is_antineutrino(ids[0])) { throw HEJ::not_implemented{"lepton-flavour violating final state"}; } assert(HEJ::is_neutrino(ids[1])); // charged antilepton + neutrino means we had a W+ return HEJ::pid::Wp; } if(pidsum == -1) { assert(HEJ::is_antilepton(ids[0])); if(HEJ::is_neutrino(ids[1])) { throw HEJ::not_implemented{"lepton-flavour violating final state"}; } assert(HEJ::is_antineutrino(ids[0])); // charged lepton + antineutrino means we had a W- return HEJ::pid::Wm; } throw HEJ::not_implemented{ "final state with leptons "+name(ids[0])+" and "+name(ids[1]) +" not supported" }; } Process get_process( YAML::Node const & node, std::string const & entry ){ Process result; std::string process_string; set_from_yaml(process_string, node, entry); assert(! process_string.empty()); const auto particles = split(process_string, " \n\t\v=>"); if(particles.size() < 3){ throw std::invalid_argument{ "Bad format in option process: '" + process_string + "', expected format is 'in1 in2 => out1 ...'" }; } result.incoming.front() = HEJ::to_ParticleID(particles[0]); result.incoming.back() = HEJ::to_ParticleID(particles[1]); for(size_t i = 0; i < result.incoming.size(); ++i){ const HEJ::ParticleID in = result.incoming[i]; if( in != HEJ::pid::proton && in != HEJ::pid::p_bar && !HEJ::is_parton(in) ){ throw invalid_incoming(particles[i]); } } result.njets = 0; for(size_t i = result.incoming.size(); i < particles.size(); ++i){ assert(! particles[i].empty()); if(particles[i] == "j") ++result.njets; else if(std::isdigit(particles[i].front()) && particles[i].back() == 'j') result.njets += std::stoi(particles[i]); else{ const auto pid = HEJ::to_ParticleID(particles[i]); if(pid==HEJ::pid::Higgs || pid==HEJ::pid::Wp || pid==HEJ::pid::Wm){ if(result.boson) throw std::invalid_argument{ "More than one outgoing boson is not supported" }; if(!result.boson_decay.empty()) throw std::invalid_argument{ "Production of a boson together with a lepton is not supported" }; result.boson = pid; } else if (HEJ::is_anylepton(pid)){ // Do not accept more leptons, if two leptons are already mentioned if( result.boson_decay.size()>=2 ) throw std::invalid_argument{"Too many leptons provided"}; if(result.boson) throw std::invalid_argument{ "Production of a lepton together with a boson is not supported" }; result.boson_decay.emplace_back(pid); } else { throw invalid_outgoing(particles[i]); } } } if(result.njets < 2){ throw std::invalid_argument{ "Process has to include at least two jets ('j')" }; } if(!result.boson_decay.empty()){ std::sort(begin(result.boson_decay),end(result.boson_decay)); assert(!result.boson); result.boson = reconstruct_boson_id(result.boson_decay); } return result; } HEJFOG::Subleading to_subleading_channel(YAML::Node const & yaml){ std::string name; using namespace HEJFOG::subleading; set_from_yaml(name, yaml); if(name == "none") return NONE; if(name == "all") return ALL; Subleading channel = NONE; if(name == "unordered" || name == "uno") return channel.set(uno); - if(name == "central qqx" || name == "cqqx") - return channel.set(cqqx); - if(name == "extremal qqx" || name == "eqqx") - return channel.set(eqqx); + if(name == "central qqbar" || name == "cqqbar") + return channel.set(cqqbar); + if(name == "extremal qqbar" || name == "eqqbar") + return channel.set(eqqbar); throw HEJ::unknown_option("Unknown subleading channel '"+name+"'"); } Subleading get_subleading_channels(YAML::Node const & node){ using YAML::NodeType; using namespace HEJFOG::subleading; // all channels allowed by default if(!node) return ALL; switch(node.Type()){ case NodeType::Undefined: return ALL; case NodeType::Null: return NONE; case NodeType::Scalar: return to_subleading_channel(node); case NodeType::Map: throw HEJ::invalid_type{"map is not a valid option for subleading channels"}; case NodeType::Sequence: Subleading channels; for(auto && channel_node: node){ channels |= get_subleading_channels(channel_node); } return channels; } throw std::logic_error{"unreachable"}; } Decay get_decay(YAML::Node const & node, std::string const & entry, std::string const & boson ){ Decay decay; set_from_yaml(decay.products, node, entry, boson, "into"); decay.branching_ratio=1; set_from_yaml_if_defined(decay.branching_ratio, node, entry, boson, "branching ratio"); return decay; } std::vector get_decays(YAML::Node const & node, std::string const & entry, std::string const & boson ){ using YAML::NodeType; if(!node[entry][boson]) return {}; switch(node[entry][boson].Type()){ case NodeType::Null: case NodeType::Undefined: return {}; case NodeType::Scalar: throw HEJ::invalid_type{"value is not a list of decays"}; case NodeType::Map: return {get_decay(node, entry, boson)}; case NodeType::Sequence: std::vector result; for(auto && decay_str: node[entry][boson]){ result.emplace_back(get_decay(decay_str, entry, boson)); } return result; } throw std::logic_error{"unreachable"}; } ParticlesDecayMap get_all_decays(YAML::Node const & node, std::string const & entry ){ if(!node[entry]) return {}; if(!node[entry].IsMap()) throw HEJ::invalid_type{entry + " have to be a map"}; ParticlesDecayMap result; for(auto const & sub_node: node[entry]) { const auto boson = sub_node.first.as(); const auto id = HEJ::to_ParticleID(boson); result.emplace(id, get_decays(node, entry, boson)); } return result; } UnweightSettings get_unweight( YAML::Node const & node, std::string const & entry ){ UnweightSettings result{}; set_from_yaml(result.sample_size, node, entry, "sample size"); if(result.sample_size <= 0){ throw std::invalid_argument{ "negative sample size " + std::to_string(result.sample_size) }; } set_from_yaml(result.max_dev, node, entry, "max deviation"); return result; } Config to_Config(YAML::Node const & yaml){ try{ HEJ::assert_all_options_known(yaml, get_supported_options()); } catch(HEJ::unknown_option const & ex){ throw HEJ::unknown_option{std::string{"Unknown option '"} + ex.what() + "'"}; } Config config; config.process = get_process(yaml, "process"); set_from_yaml(config.events, yaml, "events"); config.jets = get_jet_parameters(yaml, "jets"); config.beam = get_Beam(yaml, "beam"); for(size_t i = 0; i < config.process.incoming.size(); ++i){ auto const & in = config.process.incoming[i]; using namespace HEJ::pid; if( (in == p || in == p_bar) && in != config.beam.particles[i]){ throw std::invalid_argument{ "Particle type of beam " + std::to_string(i+1) + " incompatible" + " with type of incoming particle " + std::to_string(i+1) }; } } set_from_yaml(config.pdf_id, yaml, "pdf"); set_from_yaml(config.subleading_fraction, yaml, "subleading fraction"); if(config.subleading_fraction == 0) config.subleading_channels.reset(); else config.subleading_channels = get_subleading_channels(yaml["subleading channels"]); config.ew_parameters = HEJ::get_ew_parameters(yaml); config.particle_decays = get_all_decays(yaml, "decays"); if(config.process.boson // check that Ws always decay && std::abs(*config.process.boson) == HEJ::ParticleID::Wp && config.process.boson_decay.empty() ){ auto const & decay = config.particle_decays.find(*config.process.boson); if(decay == config.particle_decays.end() || decay->second.empty()) throw std::invalid_argument{ "Decay for "+name(*config.process.boson)+" is required"}; } set_from_yaml_if_defined(config.analyses_parameters, yaml, "analyses"); if(yaml["analysis"].IsDefined()){ std::cerr << "WARNING: Configuration entry 'analysis' is deprecated. " " Use 'analyses' instead.\n"; YAML::Node ana; set_from_yaml(ana, yaml, "analysis"); if(!ana.IsNull()){ config.analyses_parameters.push_back(ana); } } config.scales = HEJ::to_ScaleConfig(yaml); set_from_yaml_if_defined(config.output, yaml, "event output"); config.rng = HEJ::to_RNGConfig(yaml, "random generator"); config.Higgs_coupling = HEJ::get_Higgs_coupling(yaml, "Higgs coupling"); if(yaml["unweight"].IsDefined()) config.unweight = get_unweight(yaml, "unweight"); return config; } } // namespace Config load_config(std::string const & config_file){ try{ return to_Config(YAML::LoadFile(config_file)); } catch(...){ std::cerr << "Error reading " << config_file << ":\n "; throw; } } } // namespace HEJFOG diff --git a/FixedOrderGen/t/CMakeLists.txt b/FixedOrderGen/t/CMakeLists.txt index 61559ff..b2f0fef 100644 --- a/FixedOrderGen/t/CMakeLists.txt +++ b/FixedOrderGen/t/CMakeLists.txt @@ -1,213 +1,213 @@ set(tst_dir "${CMAKE_CURRENT_SOURCE_DIR}") set(runcard_dir ${tst_dir}/runcards) function(add_long_test) set(options NONE) set(oneValueArgs NAME) set(multiValueArgs COMMAND) cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_test( NAME ${TEST_NAME}_short COMMAND ${TEST_COMMAND} short ) if(TEST_ALL) add_test( NAME ${TEST_NAME} COMMAND ${TEST_COMMAND} ) endif() endfunction() foreach(tst W_reconstruct_enu W_2j_classify W_nj_classify) add_executable(test_${tst} ${tst_dir}/${tst}.cc) target_link_libraries(test_${tst} hejfog_lib) add_test(NAME ${tst} COMMAND test_${tst} WORKING_DIRECTORY ${runcard_dir} ) endforeach() # this only tests if the runcard actually works, not if the result is correct add_test( NAME main_example COMMAND HEJFOG ${PROJECT_SOURCE_DIR}/configFO.yml ) add_test( NAME main_2j COMMAND HEJFOG ${runcard_dir}/2j.yml ) add_test( NAME main_h2j COMMAND HEJFOG ${runcard_dir}/h2j.yml ) add_test( NAME main_h2j_decay COMMAND HEJFOG ${runcard_dir}/h2j_decay.yml ) add_test( NAME peakpt COMMAND HEJFOG ${runcard_dir}/2j_peak.yml ) # special case where coupling not trivial add_test( - NAME main_W4j_uno+eqqx - COMMAND HEJFOG ${runcard_dir}/Wp4j_uno+eqqx.yml + NAME main_W4j_uno+eqqbar + COMMAND HEJFOG ${runcard_dir}/Wp4j_uno+eqqbar.yml ) # check that uno emission doesn't change FKL xs add_executable(FKL_uno FKL_uno.cc) target_link_libraries(FKL_uno hejfog_lib) add_test( NAME FKL_uno # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 COMMAND FKL_uno ${runcard_dir}/h3j_uno.yml 0.0243548 0.000119862 ) # xs tests add_executable(xs_gen xs_gen.cc) target_link_libraries(xs_gen hejfog_lib) ## Higgs add_test( NAME xs_hqQ # calculated with Sherpa see #132 COMMAND xs_gen ${runcard_dir}/hqQ.yml 1.612e-02 1.26303e-04 ) add_test( NAME xs_h2j # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 COMMAND xs_gen ${runcard_dir}/h2j.yml 2.04928 0.00956022 ) add_test( NAME xs_h3j # calculated with HEJ revision bd4388fe55cbc3f5a7b6139096456c551294aa31 COMMAND xs_gen ${runcard_dir}/h3j.yml 1.07807 0.0132409 ) add_long_test( NAME xs_h3j_uno # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 COMMAND xs_gen ${runcard_dir}/h3j_uno.yml 0.00347538 3.85875e-05 ) add_test( NAME xs_h5j # calculated with HEJ revision dbde2ffbb3b383ae6709b2424d8f0f9d5658270b COMMAND xs_gen ${runcard_dir}/h5j.yml 0.0112504 0.000199633 ) add_test( NAME xs_h2j_decay # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 COMMAND xs_gen ${runcard_dir}/h2j_decay.yml 0.00466994 2.20995e-05 ) ## pure jets add_test( NAME xs_qQ # calculated with Sherpa see #132 COMMAND xs_gen ${runcard_dir}/qQ.yml 7.354e+05 1.905e+03 ) add_test( NAME xs_2j # calculated with "combined" HEJ svn r3480 COMMAND xs_gen ${runcard_dir}/2j.yml 86.42031848e06 590570 ) add_test( NAME xs_3j_uno # calculated with HEJ revision 59b8452accfe1462796406fa8e008c8b16b9657b COMMAND xs_gen ${runcard_dir}/3j_uno.yml 1.449280e+05 2.318716e+03 ) add_test( - NAME xs_3j_qqx + NAME xs_3j_qqbar # calculated with HEJ revision 9401196fba75b5d16bc33f2a309175fecaca00f1 - COMMAND xs_gen ${runcard_dir}/3j_qqx.yml 62040 1005 + COMMAND xs_gen ${runcard_dir}/3j_qqbar.yml 62040 1005 ) add_long_test( - NAME xs_4j_qqx + NAME xs_4j_qqbar # calculated with HEJ revision 9401196fba75b5d16bc33f2a309175fecaca00f1 - COMMAND xs_gen ${runcard_dir}/4j_qqx.yml 28936 550 + COMMAND xs_gen ${runcard_dir}/4j_qqbar.yml 28936 550 ) add_test( NAME xs_4j # calculated with HEJ revision 59b8452accfe1462796406fa8e008c8b16b9657b COMMAND xs_gen ${runcard_dir}/4j.yml 1.042808e+06 2.137257e+04 ) ## W add_test( NAME xs_WqQ # calculated with Sherpa see #132 COMMAND xs_gen ${runcard_dir}/WpqQ.yml 3.086e+00 4.511e-02 ) add_test( NAME xs_W2j # calculated with HEJ revision 449f2f6b597020e9c9e35699568edc05c827fc11+1 COMMAND xs_gen ${runcard_dir}/Wm2j.yml 4.177443e+02 7.446928e+00 ) add_long_test( - NAME xs_W3j_eqqx + NAME xs_W3j_eqqbar # calculated with HEJ revision 667eb768fbefa99148bf6fe9ffb6fcd16c0f976e - COMMAND xs_gen ${runcard_dir}/Wp3j_qqx.yml 2.267785e+01 3.707774e-01 + COMMAND xs_gen ${runcard_dir}/Wp3j_qqbar.yml 2.267785e+01 3.707774e-01 ) add_long_test( NAME xs_W3j_uno # calculated with HEJ revision 449f2f6b597020e9c9e35699568edc05c827fc11+1 COMMAND xs_gen ${runcard_dir}/Wp3j_uno.yml 3.000955e-01 5.831799e-03 ) add_long_test( - NAME xs_W4j_qqx + NAME xs_W4j_qqbar # calculated with HEJ revision 667eb768fbefa99148bf6fe9ffb6fcd16c0f976e - COMMAND xs_gen ${runcard_dir}/Wp4j_qqx.yml 1.159881e-01 3.633528e-03 + COMMAND xs_gen ${runcard_dir}/Wp4j_qqbar.yml 1.159881e-01 3.633528e-03 ) # Test that sum of partons == proton add_executable(PSP_channel PSP_channel.cc) target_link_libraries(PSP_channel hejfog_lib) # pure jets add_long_test( NAME channel_2j COMMAND PSP_channel ${runcard_dir}/2j.yml ) add_test( - NAME channel_3j_qqx - COMMAND PSP_channel ${runcard_dir}/3j_qqx.yml + NAME channel_3j_qqbar + COMMAND PSP_channel ${runcard_dir}/3j_qqbar.yml ) add_long_test( NAME channel_3j_uno COMMAND PSP_channel ${runcard_dir}/3j_uno.yml ) add_long_test( - NAME channel_4j_qqx - COMMAND PSP_channel ${runcard_dir}/4j_qqx.yml + NAME channel_4j_qqbar + COMMAND PSP_channel ${runcard_dir}/4j_qqbar.yml ) # W+jets # pure jets add_long_test( NAME channel_W2j COMMAND PSP_channel ${runcard_dir}/Wm2j.yml ) add_long_test( NAME channel_W3j_uno COMMAND PSP_channel ${runcard_dir}/Wp3j_uno.yml ) add_long_test( - NAME channel_W3j_eqqx - COMMAND PSP_channel ${runcard_dir}/Wp3j_qqx.yml + NAME channel_W3j_eqqbar + COMMAND PSP_channel ${runcard_dir}/Wp3j_qqbar.yml ) add_long_test( - NAME channel_W4j_qqx - COMMAND PSP_channel ${runcard_dir}/Wp4j_qqx.yml + NAME channel_W4j_qqbar + COMMAND PSP_channel ${runcard_dir}/Wp4j_qqbar.yml ) # Test that each subleading channel is generated consistently add_executable(PSP_subl PSP_subl.cc) target_link_libraries(PSP_subl hejfog_lib) add_long_test( NAME subl_5j COMMAND PSP_subl ${runcard_dir}/5j.yml ) add_long_test( NAME subl_h5j COMMAND PSP_subl ${runcard_dir}/h5j.yml ) add_long_test( NAME subl_W3j - COMMAND PSP_subl ${runcard_dir}/Wp3j_qqx.yml + COMMAND PSP_subl ${runcard_dir}/Wp3j_qqbar.yml ) add_long_test( NAME subl_W5j COMMAND PSP_subl ${runcard_dir}/Wm5j.yml ) diff --git a/FixedOrderGen/t/PSP_channel.cc b/FixedOrderGen/t/PSP_channel.cc index adb7491..4a34cbf 100644 --- a/FixedOrderGen/t/PSP_channel.cc +++ b/FixedOrderGen/t/PSP_channel.cc @@ -1,226 +1,226 @@ /** * check that the sum of all possible quarks is the same as a proton * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include #include #include #include #include "HEJ/Mixmax.hh" #include "HEJ/Event.hh" #include "config.hh" #include "EventGenerator.hh" //! throw error if condition not fulfilled #define ASSERT(x) if(!(x)) { \ throw std::logic_error("Assertion '" #x "' failed."); \ } //! throw error if condition not fulfilled #define ASSERT_THROW(x, exception) try { \ x; \ std::cerr << "'" #x "' did not throw an exception.\n"; \ throw; \ } catch(exception const & ex){ \ std::cout << "throw triggered: " << ex.what() << std::endl; \ } \ catch (...) { \ std::cerr << "Unexpected exception thrown for '" #x "'.\n"; \ throw; \ } namespace { HEJFOG::EventGenerator make_generator( HEJFOG::Config const & config, std::shared_ptr ran ){ return HEJFOG::EventGenerator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; } const static std::array PARTONS{ HEJ::ParticleID::g, HEJ::ParticleID::d, HEJ::ParticleID::u, HEJ::ParticleID::s, HEJ::ParticleID::c, HEJ::ParticleID::b, HEJ::ParticleID::d_bar, HEJ::ParticleID::u_bar, HEJ::ParticleID::s_bar, HEJ::ParticleID::c_bar, HEJ::ParticleID::b_bar }; bool only_channel( HEJFOG::Subleading channels, HEJFOG::subleading::Channels selected ){ return channels[selected] && channels.reset(selected).none(); } void generate_till_throw(HEJFOG::EventGenerator & gen ){ for(std::size_t i=0; i<100; ++i){ auto ev = gen.gen_event(); if(gen.status()==HEJFOG::Status::good){ std::cerr << "Wrongfully generated valid event!" << *ev <<"\n"; throw; } } std::cerr << "Unable to generate a proper throw!\n"; throw; } bool can_couple_to_W(HEJ::ParticleID const id, HEJ::ParticleID const boson){ using namespace HEJ; if(!is_anyquark(id)){ return false; } if(std::abs(id)==HEJ::ParticleID::b || std::abs(id)==HEJ::ParticleID::top) return false; // Wp: if(boson>0){ // anti-down if(id%2==0){ return is_quark(id); } // or up-type quark return is_antiquark(id); } // Wm: // down if(id%2==0){ return is_antiquark(id); } // or anti-up-type quark return is_quark(id); } } int main(int argc, char const *argv[]) { if(argc != 2 && argc != 3){ std::cerr << "Usage: " << argv[0] << " config.yaml\n"; return EXIT_FAILURE; } const bool short_only = argc==3; std::cout <(); if(short_only) config.events/=20.; double tot_weight = 0.; double tot_err = 0.; config.process.incoming[0] = config.process.incoming[1] = HEJ::ParticleID::proton; { HEJFOG::EventGenerator gen = make_generator(config, ran); for(std::size_t i=0; icentral().weight/config.events; tot_weight += wgt; tot_err += wgt*wgt; } } ASSERT(tot_weight!=0.); } tot_err = std::sqrt(tot_err); config.events /= PARTONS.size(); double tot_channels = 0.; double err_channels = 0.; for(auto b1: PARTONS){ for(auto b2: PARTONS){ double wgt_channel = 0.; double err_channel = 0.; config.process.incoming[0] = b1; config.process.incoming[1] = b2; HEJFOG::EventGenerator gen = make_generator(config, ran); std::cout << name(b1) << "+" << name(b2) << " " <3 && config.subleading_fraction>0. - && config.subleading_channels[HEJFOG::subleading::cqqx]){ - // this will force central qqx + && config.subleading_channels[HEJFOG::subleading::cqqbar]){ + // this will force central qqbar } else if(config.process.njets>2 && config.subleading_fraction>0. - && config.subleading_channels[HEJFOG::subleading::eqqx]){ - // this will force extremal qqx + && config.subleading_channels[HEJFOG::subleading::eqqbar]){ + // this will force extremal qqbar } else { auto const boson = *config.process.boson; if(!can_couple_to_W(b1, boson) && !can_couple_to_W(b2, boson) ){ std::cout << "bad " << name(boson) << std::endl; ASSERT_THROW(generate_till_throw(gen), std::invalid_argument); continue; } } } for(std::size_t i=0; icentral().weight/config.events; wgt_channel += wgt; err_channel += wgt*wgt; } } ASSERT(wgt_channel!=0.); tot_channels += wgt_channel; err_channels += err_channel; err_channel = std::sqrt(err_channel); std::cout << "=> " << wgt_channel << " +/- " << err_channel << std::endl; } } err_channels = std::sqrt(err_channels); std::cout << tot_weight << " +/- " << tot_err << " vs. " << tot_channels << " +/- " << err_channels << std::endl; ASSERT(std::abs(tot_weight - tot_channels) < 2.*sqrt(err_channels*err_channels+tot_err*tot_err)); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/PSP_subl.cc b/FixedOrderGen/t/PSP_subl.cc index 067dc39..51d9919 100644 --- a/FixedOrderGen/t/PSP_subl.cc +++ b/FixedOrderGen/t/PSP_subl.cc @@ -1,221 +1,221 @@ /** * check that each subprocess xs is correctly generated when only selecting one * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include #include #include #include #include "HEJ/Mixmax.hh" #include "HEJ/Event.hh" #include "HEJ/CrossSectionAccumulator.hh" #include "config.hh" #include "EventGenerator.hh" //! throw error if condition not fulfilled #define ASSERT(x) if(!(x)) { \ throw std::logic_error("Assertion '" #x "' failed."); \ } //! throw error if condition not fulfilled #define ASSERT_THROW(x, exception) try { \ x; \ std::cerr << "'" #x "' did not throw an exception.\n"; \ throw; \ } catch(exception const & ex){ \ std::cout << "throw triggered: " << ex.what() << std::endl; \ } \ catch (...) { \ std::cerr << "Unexpected exception thrown for '" #x "'.\n"; \ throw; \ } namespace { const static std::array ALL_TYPES{ HEJ::event_type::FKL, HEJ::event_type::unordered_backward, HEJ::event_type::unordered_forward, - HEJ::event_type::extremal_qqxb, - HEJ::event_type::extremal_qqxf, - HEJ::event_type::central_qqx + HEJ::event_type::extremal_qqbar_backward, + HEJ::event_type::extremal_qqbar_forward, + HEJ::event_type::central_qqbar }; HEJFOG::EventGenerator make_generator( HEJFOG::Config const & config, std::shared_ptr ran ){ return HEJFOG::EventGenerator{ config.process, config.beam, HEJ::ScaleGenerator{ config.scales.base, config.scales.factors, config.scales.max_ratio }, config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; } bool valid_type( HEJFOG::Subleading const channels, HEJ::event_type::EventType type ){ using namespace HEJ::event_type; using c = HEJFOG::subleading::Channels; if(channels.none()) return type == FKL; if(channels.count()!=1) return false; if(channels[c::uno]) return type == unob || type == unof; - if(channels[c::eqqx]) - return type == qqxexb || type == qqxexf; - if(channels[c::cqqx]) - return type == qqxmid; + if(channels[c::eqqbar]) + return type == qqbar_exb || type == qqbar_exf; + if(channels[c::cqqbar]) + return type == qqbar_mid; throw HEJ::unknown_option{"wrong channel"}; } void generate_till_throw(HEJFOG::EventGenerator & gen ){ for(std::size_t i=0; i<100; ++i){ auto ev = gen.gen_event(); if(gen.status()==HEJFOG::Status::good){ std::cerr << "Wrongfully generated valid event!" << *ev <<"\n"; throw; } } std::cerr << "Unable to generate a proper throw!\n"; throw; } } int main(int argc, char const *argv[]) { if(argc != 2 && argc != 3){ std::cerr << "Usage: " << argv[0] << " config.yaml\n"; return EXIT_FAILURE; } const bool short_only = argc==3; std::cout <(); if(short_only) config.events/=40.; // mixes sample HEJ::CrossSectionAccumulator xs_tot; config.process.incoming[0] = config.process.incoming[1] = HEJ::ParticleID::proton; config.subleading_fraction=0.8; if(config.process.boson && *config.process.boson == HEJ::ParticleID::Higgs){ config.subleading_channels.reset(); config.subleading_channels.set(HEJFOG::subleading::uno); } else { config.subleading_channels.set(); } { HEJFOG::EventGenerator gen = make_generator(config, ran); for(std::size_t i=0; icentral().weight /= config.events; xs_tot.fill(*ev); } } ASSERT(xs_tot.total().value!=0.); } // config.events /= HEJFOG::subleading::last+1; // pure FKL HEJ::CrossSectionAccumulator xs_subl; { config.subleading_fraction = 0.; HEJFOG::EventGenerator gen = make_generator(config, ran); for(std::size_t i=0; icentral().weight /= config.events; xs_subl.fill(*ev); ASSERT(valid_type(0,ev->type())); } } ASSERT(xs_subl.total().value!=0.); std::cout << "=>\n" << xs_subl << std::endl; } // pure subleading config.subleading_fraction = 1.; for(unsigned channel = HEJFOG::subleading::first; channel <= HEJFOG::subleading::last; ++channel ){ - if(config.process.njets < 4 && channel == HEJFOG::subleading::cqqx) + if(config.process.njets < 4 && channel == HEJFOG::subleading::cqqbar) continue; config.subleading_channels.reset(); config.subleading_channels.set(channel); HEJFOG::EventGenerator gen = make_generator(config, ran); HEJ::CrossSectionAccumulator xs_channel; std::cout << config.subleading_channels << " " <type()==HEJ::event_type::FKL) continue; ev->central().weight /= config.events; xs_subl.fill(*ev); xs_channel.fill(*ev); ASSERT(valid_type(config.subleading_channels,ev->type())); } } ASSERT(xs_subl.total().value!=0.); std::cout << "=>\n" << xs_channel << std::endl; } std::cout << "Total:\n" << xs_tot << " vs.\n" << xs_subl << std::endl; const double max_sigma = short_only?3.:2.; for(auto type: ALL_TYPES){ double diff = 0.; double err = 1.; try { auto const & tot = xs_tot[type]; auto const & subl = xs_subl[type]; diff = tot.value - subl.value; err = sqrt(tot.error+subl.error); } catch (std::out_of_range const &){ std::cout << name(type) << " not set" << std::endl; ASSERT_THROW(xs_tot[type], std::out_of_range); ASSERT_THROW(xs_subl[type], std::out_of_range); continue; } if(std::abs(diff) > max_sigma*err){ std::cerr << "Large difference in " << name(type) << " (" << (diff/err) << " sigma)\n"; return EXIT_FAILURE; } } return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/W_nj_classify.cc b/FixedOrderGen/t/W_nj_classify.cc index 185bc91..44437d8 100644 --- a/FixedOrderGen/t/W_nj_classify.cc +++ b/FixedOrderGen/t/W_nj_classify.cc @@ -1,226 +1,226 @@ /** * \brief check that the PSP generates the all W+jet subleading processes * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include #include #include #include #include #include #include #include #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/EWConstants.hh" #include "HEJ/exceptions.hh" #include "HEJ/Mixmax.hh" #include "HEJ/PDF.hh" #include "HEJ/PDG_codes.hh" #include "fastjet/JetDefinition.hh" #include "Decay.hh" #include "JetParameters.hh" #include "PhaseSpacePoint.hh" #include "Process.hh" #include "Status.hh" #include "Subleading.hh" namespace { using namespace HEJFOG; using namespace HEJ; void print_psp(PhaseSpacePoint const & psp){ std::cerr << "Process:\n" << psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> "; for(auto const & out: psp.outgoing()){ std::cerr << out.type << " "; } std::cerr << "\n"; } void bail_out(PhaseSpacePoint const & psp, std::string msg){ print_psp(psp); throw std::logic_error{msg}; } #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 6) // gcc version < 6 explicitly needs hash function for enum // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 using type_map = std::unordered_map>; #else using type_map = std::unordered_map; #endif } int main(){ constexpr std::size_t n_psp_base = 10375; const JetParameters jet_para{ fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30}; PDF pdf(11000, pid::proton, pid::proton); constexpr double E_cms = 13000.; constexpr double subl_chance = 0.8; const ParticlesDecayMap boson_decays{ {pid::Wp, {Decay{ {pid::e_bar, pid::nu_e}, 1.} }}, {pid::Wm, {Decay{ {pid::e, pid::nu_e_bar}, 1.} }} }; constexpr EWConstants ew_constants{246.2196508, {80.385, 2.085}, {91.187, 2.495}, {125, 0.004165} }; HEJ::Mixmax ran{}; Subleading subl_channels = subleading::ALL; std::vector allowed_types{event_type::FKL, - event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf}; + event_type::unob, event_type::unof, event_type::qqbar_exb, event_type::qqbar_exf}; std::cout << "Wp3j" << std::endl; // Wp3j Process proc {{pid::proton,pid::proton}, 3, pid::Wp, {}}; std::size_t n_psp = n_psp_base; type_map type_counter; for( std::size_t i = 0; i try again ++n_psp; } } std::cout << "Wp+3j: Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "States by classification:\n"; for(auto const & entry: type_counter){ const double fraction = static_cast(entry.second)/n_psp_base; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(25) << (name(entry.first) + std::string(":")) << entry.second << " (" << percent << "%)\n"; } for(auto const & t: allowed_types){ if(type_counter[t] < 0.05 * n_psp_base){ std::cerr << "Less than 5% of the events are of type " << name(t) << std::endl; return EXIT_FAILURE; } } // Wm3j - only uno proc = Process{{pid::proton,pid::proton}, 3, pid::Wm, {}}; n_psp = n_psp_base; subl_channels.reset(); subl_channels.set(subleading::uno); allowed_types = {event_type::FKL, event_type::unob, event_type::unof}; type_counter.clear(); for( std::size_t i = 0; i try again ++n_psp; } } std::cout << "Wm+3j (only uno): Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "States by classification:\n"; for(auto const & entry: type_counter){ const double fraction = static_cast(entry.second)/n_psp_base; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(25) << (name(entry.first) + std::string(":")) << entry.second << " (" << percent << "%)\n"; } for(auto const & t: allowed_types){ if(type_counter[t] < 0.05 * n_psp_base){ std::cerr << "Less than 5% of the events are of type " << name(t) << std::endl; return EXIT_FAILURE; } } // Wm4j proc = Process{{pid::proton,pid::proton}, 4, pid::Wm, {}}; n_psp = n_psp_base; subl_channels.set(); allowed_types = {event_type::FKL, - event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf, - event_type::qqxmid}; + event_type::unob, event_type::unof, event_type::qqbar_exb, event_type::qqbar_exf, + event_type::qqbar_mid}; type_counter.clear(); std::array wpos_counter; // position of the W boson (back, central, forward) for( std::size_t i = 0; itype==pid::Wm){ ++wpos_counter[0][ev.type()]; } else if(ev.outgoing().rbegin()->type==pid::Wm){ ++wpos_counter[2][ev.type()]; } else { ++wpos_counter[1][ev.type()]; } } else { // bad process -> try again ++n_psp; } } std::cout << "Wm+4j: Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "States by classification:\n"; for(auto const & entry: type_counter){ const double fraction = static_cast(entry.second)/n_psp_base; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(25) << (name(entry.first) + std::string(":")) << entry.second << " (" << percent << "%)\n"; } for(auto const & t: allowed_types){ if(type_counter[t] < 0.03 * n_psp_base){ std::cerr << "Less than 3% of the events are of type " << name(t) << std::endl; return EXIT_FAILURE; } } std::cout << "Stats by Wpos:\n"; for(std::size_t i=0; i 2j subleading fraction: 0.1 subleading channels: - - extremal qqx - - central qqx + - extremal qqbar + - central qqbar - unordered scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/runcards/3j_qqx.yml b/FixedOrderGen/t/runcards/3j_qqbar.yml similarity index 97% rename from FixedOrderGen/t/runcards/3j_qqx.yml rename to FixedOrderGen/t/runcards/3j_qqbar.yml index 2873ade..e830941 100644 --- a/FixedOrderGen/t/runcards/3j_qqx.yml +++ b/FixedOrderGen/t/runcards/3j_qqbar.yml @@ -1,41 +1,41 @@ events: 300000 jets: min pt: 50 R: 0.4 algorithm: antikt max rapidity: 4 beam: energy: 6500 particles: [p, p] pdf: 11000 process: p p => 3j subleading fraction: 1 subleading channels: - - eqqx + - eqqbar scales: 91 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/runcards/4j.yml b/FixedOrderGen/t/runcards/4j.yml index 8bd1134..3d50f1d 100644 --- a/FixedOrderGen/t/runcards/4j.yml +++ b/FixedOrderGen/t/runcards/4j.yml @@ -1,43 +1,43 @@ events: 250000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 6500 particles: [p, p] pdf: 11000 process: p p => 4j subleading fraction: 0.1 subleading channels: - - cqqx - - eqqx + - cqqbar + - eqqbar - uno scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/runcards/4j_qqx.yml b/FixedOrderGen/t/runcards/4j_qqbar.yml similarity index 95% rename from FixedOrderGen/t/runcards/4j_qqx.yml rename to FixedOrderGen/t/runcards/4j_qqbar.yml index f16787e..d7bc13d 100644 --- a/FixedOrderGen/t/runcards/4j_qqx.yml +++ b/FixedOrderGen/t/runcards/4j_qqbar.yml @@ -1,42 +1,42 @@ events: 1100000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 3500 particles: [p, p] pdf: 11000 process: p p => 4j subleading fraction: 1 subleading channels: - - cqqx - - eqqx + - cqqbar + - eqqbar scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/runcards/Wp3j_qqx.yml b/FixedOrderGen/t/runcards/Wp3j_qqbar.yml similarity index 97% rename from FixedOrderGen/t/runcards/Wp3j_qqx.yml rename to FixedOrderGen/t/runcards/Wp3j_qqbar.yml index 6a738b9..1579dd8 100644 --- a/FixedOrderGen/t/runcards/Wp3j_qqx.yml +++ b/FixedOrderGen/t/runcards/Wp3j_qqbar.yml @@ -1,40 +1,40 @@ events: 1000000 jets: min pt: 20 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 5000 particles: [p, p] pdf: 11000 process: p p => W+ 3j subleading fraction: 1. subleading channels: - - eqqx + - eqqbar scales: 125 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/runcards/Wp4j_qqx.yml b/FixedOrderGen/t/runcards/Wp4j_qqbar.yml similarity index 95% rename from FixedOrderGen/t/runcards/Wp4j_qqx.yml rename to FixedOrderGen/t/runcards/Wp4j_qqbar.yml index 3228cf1..05702d3 100644 --- a/FixedOrderGen/t/runcards/Wp4j_qqx.yml +++ b/FixedOrderGen/t/runcards/Wp4j_qqbar.yml @@ -1,41 +1,41 @@ events: 1000000 jets: min pt: 40 R: 0.4 algorithm: antikt max rapidity: 6 beam: energy: 6000 particles: [p, p] pdf: 11000 process: p d => W+ 4j subleading fraction: 1. subleading channels: - - cqqx - - eqqx + - cqqbar + - eqqbar scales: 125 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/runcards/Wp4j_uno+eqqx.yml b/FixedOrderGen/t/runcards/Wp4j_uno+eqqbar.yml similarity index 97% rename from FixedOrderGen/t/runcards/Wp4j_uno+eqqx.yml rename to FixedOrderGen/t/runcards/Wp4j_uno+eqqbar.yml index fc223db..1647fbd 100644 --- a/FixedOrderGen/t/runcards/Wp4j_uno+eqqx.yml +++ b/FixedOrderGen/t/runcards/Wp4j_uno+eqqbar.yml @@ -1,42 +1,42 @@ events: 10000 jets: min pt: 40 R: 0.4 algorithm: antikt max rapidity: 6 beam: energy: 6000 particles: [p, p] pdf: 11000 # code should enforce coupling at proton leg process: d p => W+ 4j subleading fraction: 1. subleading channels: - - eqqx + - eqqbar - uno scales: 125 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/config.yml b/config.yml index 8d52b20..6abb5bb 100644 --- a/config.yml +++ b/config.yml @@ -1,129 +1,129 @@ ## Number of attempted resummation phase space points for each input event trials: 10 resummation jets: # resummation jet properties min pt: 30 # minimum jet transverse momentum algorithm: antikt # jet clustering algorithm R: 0.4 # jet R parameter fixed order jets: # properties of input jets min pt: 20 # by default, algorithm and R are like for resummation jets ## Treatment of he various event classes ## the supported settings are: reweight, keep, discard ## non-resummable events cannot be reweighted event treatment: FKL: reweight unordered: keep - extremal qqx: keep - central qqx: keep + extremal qqbar: keep + central qqbar: keep non-resummable: keep ## Central scale choice or choices # ## multiple scales are allowed, e.g. # scales: [125, max jet pperp, H_T/2, 2*jet invariant mass, m_j1j2] scales: 91.188 ## Factors by which the central scales should be multiplied ## renormalisation and factorisation scales are varied independently # # scale factors: [0.5, 0.7071, 1, 1.41421, 2] ## Maximum ratio between renormalisation and factorisation scale # # max scale ratio: 2.0001 ## Import scale setting functions # # import scales: # lib_my_scales.so: [scale0,scale1] ## Unweighting setting ## remove to obtain weighted events # unweight: # # type of unweighting (one of 'weighted', 'resummation', 'partial') # type: partial # trials: 10000 # max deviation: 0 ## Event output files # # the supported formats are # - Les Houches (suffix .lhe) # - HepMC2 (suffix .hepmc2) # - HepMC3 (suffix .hepmc3 or .hepmc) # - HDF5 (suffix .hdf5) # ## An output file's format is deduced either automatically from the suffix ## or from an explicit specification, e.g. ## - Les Houches: outfile # event output: - HEJ.lhe # - HEJ_events.hepmc ## Analyses # # analyses: ## Rivet analysis # - rivet: MC_XS # rivet analysis name # output: HEJ # name of the yoda files, ".yoda" and scale suffix will be added ## Custom analysis # - plugin: /path/to/libmyanalysis.so # my analysis parameter: some value ## Selection of random number generator and seed ## The choices are ## - mixmax (seed is an integer) ## - ranlux64 (seed is a filename containing parameters) random generator: name: mixmax # seed: 1 ## Whether or not to include higher order logs log correction: false ## Vacuum expectation value vev: 246.2196508 ## Properties of the weak gauge bosons particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 ## Parameters for Higgs-gluon couplings ## This requires compilation with QCDloop # # Higgs coupling: # use impact factors: false # mt: 174 # include bottom: true # mb: 4.7 ## ---------------------------------------------------------------------- ## ## The following settings are only intended for advanced users. ## ## Please DO NOT SET them unless you know exactly what you are doing! ## ## ---------------------------------------------------------------------- ## # ## Maximum soft transverse momentum fraction in any tagging jets, e.g. -## extremal or qqx jet +## extremal or qqbar jet # soft pt regulator: 0.1 # ## Minimum transverse momentum of extremal partons ## deprecated: use "soft pt regulator" instead # min extparton pt: 30 # ## deprecated: this cot directly replaced by "soft pt regulator" # max ext soft pt fraction: 0.1 # # max events: -1 # Maximal number of fixed order Events to process # regulator parameter: 0.2 # The regulator lambda for the subtraction terms diff --git a/doc/developer_manual/currents.tex b/doc/developer_manual/currents.tex index 4cc9a60..e142aff 100644 --- a/doc/developer_manual/currents.tex +++ b/doc/developer_manual/currents.tex @@ -1,717 +1,717 @@ \section{Currents} \label{sec:currents_impl} The following section contains a list of all the currents implemented in \HEJ. Clean up of the code structure is ongoing. All $W$+Jet currents are located in \texttt{src/Wjets.cc}, all Higgs+Jets currents are defined in \texttt{src/Hjets.cc}, Z/$\gamma$ + Jet currents are in \texttt{src/Zjets.cc} and pure jet currents are defined in in \texttt{src/jets.cc}. All of these have their own separate header files: \texttt{include/HEJ/Wjets.hh}, \texttt{include/HEJ/Hjets.hh}, \texttt{include/HEJ/Zjets.hh} and \texttt{include/HEJ/jets.hh} respectively. The naming convention for the current contraction $\left\|S_{f_1 f_2\to f_1 f_2}\right\|^2$ is \lstinline!ME_[Boson]_[subleading-type]_[incoming]!. For example \lstinline!ME_W_unob_qq! corresponds to the contraction $j_W^\mu j_{\text{uno}, \mu}$ ($qQ\to \bar{q}WQg$). For bosons on the same side as the subleading we drop the connecting underscore, e.g. \lstinline!ME_Wuno_qq! gives $j_{W,\text{uno}}^\mu j_\mu$ ($qQ\to g\bar{q}WQ$). \subsection{Pure Jets} \subsubsection{Quark} \label{sec:current_quark} \begin{align} j_\mu(p_i,p_j)=\bar{u}(p_i)\gamma_\mu u(p_j) \end{align} The exact form depends on the helicity and direction (forward/backwards) for the quarks. \subsubsection{Gluon} In \HEJ the currents for gluons and quarks are the same, up to a colour factor $K_g/C_F$, where \begin{align} K_g(p_1^-, p_a^-) = \frac{1}{2}\left(\frac{p_1^-}{p_a^-} + \frac{p_a^-}{p_1^-}\right)\left(C_A - \frac{1}{C_A}\right)+\frac{1}{C_A}. \end{align} Thus we can just reuse the results from sec.~\ref{sec:current_quark}. \subsubsection{Single unordered gluon} Configuration $q(p_a) \to g(p_1) q(p_2) g^*(\tilde{q}_2)$~\cite{Andersen:2017kfc} \begin{align} \label{eq:juno} \begin{split} &j^{{\rm uno}\; \mu\ cd}(p_2,p_1,p_a) = i \varepsilon_{1\nu} \left( T_{2i}^{c}T_{ia}^d\ \left(U_1^{\mu\nu}-L^{\mu\nu} \right) + T_{2i}^{d}T_{ia}^c\ \left(U_2^{\mu\nu} + L^{\mu\nu} \right) \right). \\ U_1^{\mu\nu} &= \frac1{s_{21}} \left( j_{21}^\nu j_{1a}^\mu + 2 p_2^\nu j_{2a}^\mu \right) \qquad \qquad U_2^{\mu\nu} = \frac1{t_{a1}} \left( 2 j_{2a}^\mu p_a^\nu - j_{21}^\mu j_{1a}^\nu \right) \\ L^{\mu\nu} &= \frac1{t_{a2}} \left(-2p_1^\mu j_{2a}^\nu+2p_1.j_{2a} g^{\mu\nu} + (\tilde{q}_1+\tilde{q}_2)^\nu j_{2a}^\mu + \frac{t_{b2}}{2} j_{2a}^\mu \left( \frac{p_2^\nu}{p_1.p_2} + \frac{p_b^\nu}{p_1.p_b} \right) \right) , \end{split} \end{align} $j^{{\rm uno}\; \mu}$ is currently not calculated as a separate current, but always as needed for the ME (i.e. in \lstinline!ME_unob_XX!). -\subsubsection{Extremal \texorpdfstring{$q\bar{q}$}{qqx}} +\subsubsection{Extremal \texorpdfstring{$q\bar{q}$}{qqbar}} In Pure jets we also include the subleading process which arises when an incoming gluon splits into a $q\bar{q}$ pair. This splitting impact factor is related to the unordered current by simple means of a crossing symmetry. -\subsubsection{Central \texorpdfstring{$q\bar{q}$}{qqx}} +\subsubsection{Central \texorpdfstring{$q\bar{q}$}{qqbar}} The final subleading process type in the Pure Jets case is Central $q\bar{q}$. In this process type, we have two currents scattering off of each other, but this time, via an effective vertex, which connects together two FKL chains. Each FKL chain t-channel gluon splits into a $q\bar{q}$ and this results in a quark and anti-quark in between the most forward and backward jets. One can see an example of such a process in Figure \ref{fig:qqbarcen_example}. \begin{figure}[ht] \centering \includegraphics[]{Cenqqbar_jx} \caption{Momentum labeling for a central $q\bar{q}$ process.} \label{fig:qqbarcen_example} \end{figure} As the new central $q\bar{q}$ piece contains the quark propagator, we will treat this as part of the skeleton process. This means that we do not impose strong ordering between the $q\bar{q}$-pair taking \begin{align} \label{eq:cenqqbarraporder} y_1 \ll y_q,y_{\bar{q}} \ll y_n. \end{align} The HEJ Matrix element for this process can be calculated as: \begin{align} \label{eq:Mcentral} i\mathcal{M} &= g_s^4 T^d_{1a} T^e_{nb}\ \frac{j_{\mu}(p_a,p_1)\ X^{ab\, \mu \nu}_{{\rm cen}}(p_q,p_{\bar{q}},q_1,q_3)\ j_{\nu}(p_b,p_n)}{t_{a1}t_{bn}}. \end{align} where $X^{\mu \nu}_{\rm cen}$ is given by: \begin{equation} \label{eq:Xcen} \begin{split} X^{\mu \nu}_{\rm cen} ={}&\frac{f^{ced}T^c_{q\bar{q}}}{s_{q\bar{q}}} \left(\eta^{\mu \nu} X_{sym}^\sigma + V^{\mu \nu \sigma}_{\bar{q}g} \right) \bar{u}(p_q) \gamma^\sigma u(p_{\bar{q}}) \\ & \qquad + \frac{i T^d_{qj}T^e_{j\bar{q}}}{(q_1-p_q)^2} X^{\mu\nu}_{\text{qprop}} - \frac{i T^e_{qj}T^d_{j\bar{q}}}{(q_1-p_{\bar{q}})^2} X^{\mu\nu}_{\text{crossed}}\,, \end{split} \end{equation} with \begin{align} \label{eq:Xsym} X_{sym}^\sigma ={}& q_1^2 \left( \frac{p_a^\sigma}{s_{aq} + s_{a\bar{q}}} + \frac{p_1^\sigma}{s_{1q} + s_{1\bar{q}}} \right) - q_3^2 \left( \frac{p_b^\sigma}{s_{bq} + s_{b\bar{q}}} + \frac{p_n^\sigma}{s_{nq} + s_{n\bar{q}}} \right)\,,\\ \label{eq:V3g} V_{3g}^{\mu\nu\sigma} ={}& (q_1 + p_q + p_{\bar{q}})^\nu \eta^{\mu\sigma} + (q_3 - p_q - p_{\bar{q}})^\mu \eta^{\nu\sigma} - (q_1 + q_3)^\sigma \eta^{\mu\nu}\,,\\ \label{eq:Xqprop} X^{\mu\nu}_{\text{qprop}} ={}& \frac{\langle p_q | \mu (q_1-p_q) \nu | p_{\bar{q}}\rangle}{(q_1-p_q)^2}\,,\\ \label{eq:Xcrossed} X^{\mu\nu}_{\text{crossed}} ={}& \frac{\langle p_q | \nu (q_1-p_{\bar{q}}) \mu | p_{\bar{q}}\rangle}{(q_1-p_{\bar{q}})^2}\,, \end{align} and $q_3 = q_1 - p_q - p_{\bar{q}}$. \subsection{Higgs} Different rapidity orderings \todo{give name of functions} \begin{enumerate} \item $qQ\to HqQ/qHQ/qQH$ (any rapidity order, full LO ME) $\Rightarrow$ see~\ref{sec:V_H} \item $qg\to Hqg$ (Higgs outside quark) $\Rightarrow$ see~\ref{sec:V_H} \item $qg\to qHg$ (central Higgs) $\Rightarrow$ see~\ref{sec:V_H} \item $qg\to qgH$ (Higgs outside gluon) $\Rightarrow$ see~\ref{sec:jH_mt} \item $gg\to gHg$ (central Higgs) $\Rightarrow$ see~\ref{sec:V_H} \item $gg\to ggH$ (Higgs outside gluon) $\Rightarrow$ see~\ref{sec:jH_mt} \end{enumerate} \subsubsection{Higgs gluon vertex} \label{sec:V_H} The coupling of the Higgs boson to gluons via a virtual quark loop can be written as \begin{align} \label{eq:VH} V^{\mu\nu}_H(q_1, q_2) = \mathgraphics{V_H.pdf} &= \frac{\alpha_s m^2}{\pi v}\big[ g^{\mu\nu} T_1(q_1, q_2) - q_2^\mu q_1^\nu T_2(q_1, q_2) \big]\, \\ &\xrightarrow{m \to \infty} \frac{\alpha_s}{3\pi v} \left(g^{\mu\nu} q_1\cdot q_2 - q_2^\mu q_1^\nu\right). \end{align} The outgoing momentum of the Higgs boson is $p_H = q_1 - q_2$. As a contraction with two currents this by implemented in \lstinline!cHdot! inside \texttt{src/Hjets.cc}. The form factors $T_1$ and $T_2$ are then given by~\cite{DelDuca:2003ba} \begin{align} \label{eq:T_1} T_1(q_1, q_2) ={}& -C_0(q_1, q_2)\*\left[2\*m^2+\frac{1}{2}\*\left(q_1^2+q_2^2-p_H^2\right)+\frac{2\*q_1^2\*q_2^2\*p_H^2}{\lambda}\right]\notag\\ &-\left[B_0(q_2)-B_0(p_H)\right]\*\frac{q_2^2}{\lambda}\*\left(q_2^2-q_1^2-p_H^2\right)\notag\\ &-\left[B_0(q_1)-B_0(p_H)\right]\*\frac{q_1^2}{\lambda}\*\left(q_1^2-q_2^2-p_H^2\right)-1\,,\displaybreak[0]\\ \label{eq:T_2} T_2(q_1, q_2) ={}& C_0(q_1, q_2)\*\left[\frac{4\*m^2}{\lambda}\*\left(p_H^2-q_1^2-q_2^2\right)-1-\frac{4\*q_1^2\*q_2^2}{\lambda} - \frac{12\*q_1^2\*q_2^2\*p_H^2}{\lambda^2}\*\left(q_1^2+q_2^2-p_H^2\right)\right]\notag\\ &-\left[B_0(q_2)-B_0(p_H)\right]\*\left[\frac{2\*q_2^2}{\lambda}+\frac{12\*q_1^2\*q_2^2}{\lambda^2}\*\left(q_2^2-q_1^2+p_H^2\right)\right]\notag\\ &-\left[B_0(q_1)-B_0(p_H)\right]\*\left[\frac{2\*q_1^2}{\lambda}+\frac{12\*q_1^2\*q_2^2}{\lambda^2}\*\left(q_1^2-q_2^2+p_H^2\right)\right]\notag\\ &-\frac{2}{\lambda}\*\left(q_1^2+q_2^2-p_H^2\right)\,, \end{align} where we have used the scalar bubble and triangle integrals \begin{align} \label{eq:B0} B_0\left(p\right) ={}& \int \frac{d^dl}{i\pi^{\frac{d}{2}}} \frac{1}{\left(l^2-m^2\right)\left((l+p)^2-m^2\right)}\,,\\ \label{eq:C0} C_0\left(p,q\right) ={}& \int \frac{d^dl}{i\pi^{\frac{d}{2}}} \frac{1}{\left(l^2-m^2\right)\left((l+p)^2-m^2\right)\left((l+p-q)^2-m^2\right)}\,, \end{align} and the K\"{a}ll\'{e}n function \begin{equation} \label{eq:lambda} \lambda = q_1^4 + q_2^4 + p_H^4 - 2\*q_1^2\*q_2^2 - 2\*q_1^2\*p_H^2- 2\*q_2^2\*p_H^2\,. \end{equation} The Integrals as such are provided by \QCDloop{} (see wrapper functions \lstinline!B0DD! and \lstinline!C0DD! in \texttt{src/Hjets.cc}). In the code we are sticking to the convention of~\cite{DelDuca:2003ba}, thus instead of the $T_{1/2}$ we implement (in the functions \lstinline!A1! and \lstinline!A2!) \begin{align} \label{eq:A_1} A_1(q_1, q_2) ={}& \frac{i}{16\pi^2}\*T_2(-q_1, q_2)\,,\\ \label{eq:A_2} A_2(q_1, q_2) ={}& -\frac{i}{16\pi^2}\*T_1(-q_1, q_2)\,. \end{align} \subsubsection{Peripheral Higgs emission - Finite quark mass} \label{sec:jH_mt} We describe the emission of a peripheral Higgs boson close to a scattering gluon with an effective current. In the following we consider a lightcone decomposition of the gluon momenta, i.e. $p^\pm = E \pm p_z$ and $p_\perp = p_x + i p_y$. The incoming gluon momentum $p_a$ defines the $-$ direction, so that $p_a^+ = p_{a\perp} = 0$. The outgoing momenta are $p_1$ for the gluon and $p_H$ for the Higgs boson. We choose the following polarisation vectors \begin{equation} \label{eq:pol_vectors} \epsilon_\mu^\pm(p_a) = \epsilon_\mu^\pm(p_a, p_1),\qquad \epsilon_\mu^\pm(p_1) = \epsilon_\mu^\pm(p_1, p_a)\,. \end{equation} The polarisation vectors with two momentum arguments are defined in equation~(\ref{eq:pol_vector}). Following~\cite{DelDuca:2001fn}, we introduce effective polarisation vectors to describe the contraction with the Higgs-boson production vertex eq.~\eqref{eq:VH}: \begin{align} \label{eq:eps_H} \epsilon_{H,\mu}(p_a) = \frac{T_2(p_a, p_a-p_H)}{(p_a-p_H)^2}\big[p_a\cdot p_H\epsilon_\mu(p_a) - p_H\cdot\epsilon(p_a) p_{a,\mu}\big]\,,\\ \epsilon_{H,\mu}^*(p_1) = -\frac{T_2(p_1+p_H, p_1)}{(p_1+p_H)^2}\big[p_1\cdot p_H\epsilon_\mu^*(p_1) - p_H\cdot\epsilon^*(p_1) p_{1,\mu}\big]\,, \end{align} with $T_1$ from equation~\eqref{eq:T_1} and $T_2$ from equation~\eqref{eq:T_2}. Without loss of generality, we consider only the case where the incoming gluon has positive helicity. The remaining helicity configurations can be obtained through parity transformation. Labelling the effective current by the helicities of the gluons we obtain for the same-helicity case \begin{equation} \label{eq:jH_same_helicity} \begin{split} j_{H,\mu}^{++}{}&(p_1,p_a,p_H) = \frac{m^2}{\pi v}\bigg[\\ &-\sqrt{\frac{2p_1^-}{p_a^-}}\frac{p_{1\perp}^*}{|p_{1\perp}|}\frac{t_2}{\spb a.1}\epsilon^{+,*}_{H,\mu}(p_1) +\sqrt{\frac{2p_a^-}{p_1^-}}\frac{p_{1\perp}^*}{|p_{1\perp}|}\frac{t_2}{\spa 1.a}\epsilon^{+}_{H,\mu}(p_a)\\ &+ [1|H|a\rangle \bigg( \frac{\sqrt{2}}{\spa 1.a}\epsilon^{+}_{H,\mu}(p_a) + \frac{\sqrt{2}}{\spb a.1}\epsilon^{+,*}_{H,\mu}(p_1)-\frac{\spa 1.a T_2(p_a, p_a-p_H)}{\sqrt{2}(p_a-p_H)^2}\epsilon^{+,*}_{\mu}(p_1)\\ & \qquad -\frac{\spb a.1 T_2(p_1+p_H, p_1)}{\sqrt{2}(p_1+p_H)^2}\epsilon^{+}_{\mu}(p_a)-\frac{RH_4}{\sqrt{2}\spb a.1}\epsilon^{+,*}_{\mu}(p_1)+\frac{RH_5}{\sqrt{2}\spa 1.a}\epsilon^{+}_{\mu}(p_a) \bigg)\\ & - \frac{[1|H|a\rangle^2}{2 t_1}(p_{a,\mu} RH_{10} - p_{1,\mu} RH_{12})\bigg] \end{split} \end{equation} with $t_1 = (p_a-p_1)^2$, $t_2 = (p_a-p_1-p_H)^2$ and $R = 8 \pi^2$\todo{Code has $R=8\pi^2 i$}. Like other special currents, eq.~\eqref{eq:jH_same_helicity} is implemented in \texttt{current\_generator/include/currents.frm}. The currents with a helicity flip is given through \begin{equation} \label{eq:jH_helicity_flip} \begin{split} j_{H,\mu}^{+-}{}&(p_1,p_a,p_H) = \frac{m^2}{\pi v}\bigg[\\ &-\sqrt{\frac{2p_1^-}{p_a^-}}\frac{p_{1\perp}^*}{|p_{1\perp}|}\frac{t_2}{\spb a.1}\epsilon^{-,*}_{H,\mu}(p_1) +\sqrt{\frac{2p_a^-}{p_1^-}}\frac{p_{1\perp}}{|p_{1\perp}|}\frac{t_2}{\spb a.1}\epsilon^{+}_{H,\mu}(p_a)\\ &+ [1|H|a\rangle \left( \frac{\sqrt{2}}{\spb a.1} \epsilon^{-,*}_{H,\mu}(p_1) -\frac{\spa 1.a T_2(p_a, p_a-p_H)}{\sqrt{2}(p_a-p_H)^2}\epsilon^{-,*}_{\mu}(p_1) - \frac{RH_4}{\sqrt{2}\spb a.1}\epsilon^{-,*}_{\mu}(p_1)\right) \\ &+ [a|H|1\rangle \left( \frac{\sqrt{2}}{\spb a.1}\epsilon^{+}_{H,\mu}(p_a) -\frac{\spa 1.a T_2(p_1+p_H,p_1)}{\sqrt{2}(p_1+p_H)^2}\epsilon^{+}_{\mu}(p_a) +\frac{RH_5}{\sqrt{2}\spb a.1}\epsilon^{+}_{\mu}(p_a) \right)\\ & - \frac{[1|H|a\rangle [a|H|1\rangle}{2 \spb a.1 ^2}(p_{a,\mu} RH_{10} - p_{1,\mu} RH_{12})\\ &+ \frac{\spa 1.a}{\spb a.1}\bigg(RH_1p_{1,\mu}-RH_2p_{a,\mu}+2 p_1\cdot p_H \frac{T_2(p_1+p_H, p_1)}{(p_1+p_H)^2} p_{a,\mu} \\ & \qquad- 2p_a \cdot p_H \frac{T_2(p_a, p_a-p_H)}{(p_a-p_H)^2} p_{1,\mu}+ T_1(p_a-p_1, p_a-p_1-p_H)\frac{(p_1 + p_a)_\mu}{t_1}\\ &\qquad-\frac{(p_1+p_a)\cdot p_H}{t_1} T_2(p_a-p_1, p_a-p_1-p_H)(p_1 - p_a)_\mu \bigg) \bigg]\,, \end{split} \end{equation} and implemented again in \texttt{current\_generator/include/currents.frm}.\todo{sign mismatch in line 5 and negative-helicity polarisation vectors} If we instead choose the gluon momentum in the $+$ direction, so that $p_a^- = p_{a\perp} = 0$, the corresponding currents are obtained by replacing $p_1^- \to p_1^+, p_a^- \to p_a^+, \frac{p_{1\perp}}{|p_{1\perp}|} \to -1$ in the second line of eq.~\eqref{eq:jH_same_helicity} and eq.~\eqref{eq:jH_helicity_flip}.. The form factors $H_1,H_2,H_4,H_5, H_{10}, H_{12}$ are given in~\cite{DelDuca:2003ba}, and are implemented as \lstinline!H1DD,H2DD! etc. in \texttt{src/Hjets.cc}. They reduce down to fundamental QCD integrals, which are again provided by \QCDloop. \subsubsection{Peripheral Higgs emission - Infinite top mass} \label{sec:jH_eff} To get the result with infinite top mass we could either take the limit $m_t\to \infty$ in~\eqref{eq:jH_helicity_flip} and~\eqref{eq:jH_same_helicity}, or use the \textit{impact factors} as given in~\cite{DelDuca:2003ba}. Both methods are equivalent, and lead to the same result. For the first one would find \begin{align} \lim_{m_t\to\infty} m_t^2 H_1 &= i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_2 &=-i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_4 &= i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_5 &=-i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_{10} &= 0 \\ \lim_{m_t\to\infty} m_t^2 H_{12} &= 0. \end{align} \todo{double check this, see James thesis eq. 4.33} However only the second method is implemented in the code through \lstinline!C2gHgp! and \lstinline!C2gHgm! inside \texttt{src/Hjets.cc}, each function calculates the square of eq. (4.23) and (4.22) from~\cite{DelDuca:2003ba} respectively. \subsection{Vector Boson + Jets} \label{sec:currents_W} \subsubsection{Quark+ Vector Boson} \begin{figure} \centering \begin{minipage}[b]{0.2\textwidth} \includegraphics[width=\textwidth]{Wbits.pdf} \end{minipage} \begin{minipage}[b]{0.1\textwidth} \centering{=} \vspace{0.7cm} \end{minipage} \begin{minipage}[b]{0.2\textwidth} \includegraphics[width=\textwidth]{Wbits2.pdf} \end{minipage} \begin{minipage}[b]{0.1\textwidth} \centering{+} \vspace{0.7cm} \end{minipage} \begin{minipage}[b]{0.2\textwidth} \includegraphics[width=\textwidth]{Wbits3.pdf} \end{minipage} \caption{The $j_V$ current is constructed from the two diagrams which contribute to the emission of a vector boson from a given quark line.} \label{fig:jV} \end{figure} For a $W, Z$, or photon emission we require a fermion. The current is actually a sum of two separate contributions, see figure~\ref{fig:jV}, one with a vector boson emission from the initial state, and one with the vector boson emission from the final state. This can be seen as the following two terms, given for the example of a $W$ emission~\cite{Andersen:2012gk}\todo{cite W subleading paper}: \begin{align} \label{eq:Weffcur1} j_W^\mu(p_a,p_{\ell},p_{\bar{\ell}}, p_1) =&\ \frac{g_W^2}{2}\ \frac1{p_W^2-M_W^2+i\ \Gamma_W M_W}\ \bar{u}^-(p_\ell) \gamma_\alpha v^-(p_{\bar\ell})\nonumber \\ & \cdot \left( \frac{ \bar{u}^-(p_1) \gamma^\alpha (\slashed{p}_W + \slashed{p}_1)\gamma^\mu u^-(p_a)}{(p_W+p_1)^2} + \frac{ \bar{u}^-(p_1)\gamma^\mu (\slashed{p}_a - \slashed{p}_W)\gamma^\alpha u^-(p_a)}{(p_a-p_W)^2} \right). \end{align} There are a couple of subtleties here. There is a minus sign distinction between the quark-anti-quark cases due to the fermion flow of the propagator in the current. Note that the type of $W$ emission (+ or -) will depend on the quark flavour, and that the handedness of the quark-line is given by whether its a quark or anti-quark. The coupling and propagator factor in eq.~(\ref{eq:Weffcur1}) have to be adapted depending on the emitted boson. The remaining product of currents \begin{equation} \label{eq:J_V} J_{\text{V}}^\mu(p_2,p_l,p_{\bar{l}},p_3)=\left( \frac{ \bar{u}_2 \gamma^\nu (\slashed{p}_2 + \slashed{p}_l + \slashed{p}_{\bar{l}}) \gamma^\mu u_3}{s_{2l\bar{l}}} - \frac{\bar u_2 \gamma^\mu(\slashed{p}_3 + \slashed{p}_l + \slashed{p}_{\bar{l}}) \gamma^\nu u_3}{s_{3l\bar{l}}} \right) [\bar{u}_l \gamma_\nu u_{\bar{l}}] \end{equation} with $s_{il\bar{l}} = (p_i + p_l +p_{\bar{l}})^2$ is universal. The implementation is in \texttt{include/currents.frm} inside the \texttt{current\_generator} (see section~\ref{sec:cur_gen}). To use it inside \FORM use the place-holder \lstinline!JV(h1, hl, mu, pa, p1, plbar, pl)!, where \lstinline!h1! is the helicity of the quark line and \lstinline!hl! the helicity of the lepton line. \subsubsection{Vector boson with unordered emission} \begin{figure} \centering \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno1} \caption{} \label{fig:U1diags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno2} \caption{} \label{fig:U2diags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno3} \caption{} \label{fig:Cdiags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno4} \caption{} \label{fig:Ddiags} \end{subfigure} \vspace{0.4cm} \caption{Examples of each of the four categories of Feynman diagram which contribute to at leading-order; there are twelve in total. (a) is an example where the gluon and vector boson are emitted from the same quark line and the gluon comes after the $t$-channel propagator. In (b), the gluon and vector boson are emitted from the same quark line and the gluon comes before the $t$-channel proagator. In (c) the gluon is emitted from the $t$-channel gluon and in (d) the gluon is emitted from the $b$--$3$ quark line.} \label{fig:Vunodiags} \end{figure} It is necessary to include subleading processes in vector boson + jets also. Similarly to the pure jet case, the unordered currents are not calculated separately, and only in the ME functions when required in the \texttt{src/Wjets.cc} or \texttt{src/Zjets.cc} file. The following shows the derivation of the calculation of this ME within HEJ. We start with a contraction of two currents: \begin{equation} \label{eq:SabsVuno} S_{qQ\to Vgq^\prime Q} = j_{V{\rm uno}\,\mu}^d(p_a,p_1,p_2,p_\ell,p_{\bar\ell})\ g^{\mu \nu}\ T^d_{3b}\ j^{h_b,h_3}_{\nu}(p_b,p_{3}), \end{equation} where $j_{V,{\rm uno}}$ is our new unordered current which is is only non-zero for $h_a=h_1=-$ and hence we have suppressed its helicity indices. It is derived from the 12 leading-order Feynman diagrams in the QMRK limit (see figure~\ref{fig:Vunodiags}). Using $T^m_{ij}$ represent fundamental colour matrices between quark state $i$ and $j$ with adjoint index $m$ we find \begin{align}\label{eq:wunocurrent} \begin{split} j^{d\,\mu}_{\rm V,uno}(p_a,p_1,p_2,p_\ell,p_{\bar{\ell}}) =& \ i \varepsilon_{\nu}(p_1)\ \bar{u}^-(p_\ell) \gamma_\rho v^-(p_{\bar \ell}) \\ & \quad \times\ \left(T^1_{2i} T^d_{ia} (\tilde U_1^{\nu\mu\rho}-\tilde L^{\nu\mu\rho}) + T^d_{2i} T^1_{ia} (\tilde U_2^{\nu\mu\rho}+\tilde L^{\nu\mu\rho}) \right), \end{split} \end{align} where expressions for $\tilde U_{1,2}^{\nu\mu\rho}$ and $\tilde L^{\nu\mu\rho}$ are given as: \begin{align} \label{eq:U1tensor} \begin{split} \tilde U_1^{\nu\mu\rho} ={}&\frac{\langle 2|\nu (\slashed{p}_2+ \slashed{p}_1)\mu (\slashed{p}_a - \slashed{p}_V)\rho P_L |a\rangle }{s_{12}t_{aV}} + \frac{\langle 2|\nu (\slashed{p}_2+ \slashed{p}_1)\rho P_L (\slashed{p}_2+\slashed{p}_1 + \slashed{p}_V)\mu |a\rangle }{s_{12}s_{12V}} \\ &+ \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_V) \nu (\slashed{p}_1 + \slashed{p}_2+\slashed{p}_V)\mu |a\rangle}{s_{2V}s_{12V}}\,, \end{split}\\ \label{eq:U2tensor} \begin{split} \tilde U_2^{\nu\mu\rho} ={}&\frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_V-\slashed{p}_1)\nu (\slashed{p}_a - \slashed{p}_V)\rho P_L |a\rangle }{t_{aV1}t_{aV}} + \frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_V- \slashed{p}_1)\rho P_L (\slashed{p}_a-\slashed{p}_1) \nu |a\rangle }{t_{a1V}t_{a1}} \\ &+ \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_V) \mu (\slashed{p}_a-\slashed{p}_1)\nu |a\rangle}{s_{2V}t_{a1}}\,, \end{split}\\ \label{eq:Ltensor} \begin{split} \tilde L^{\nu\mu\rho} ={}& \frac{1}{t_{aV2}}\left[ \frac{\langle 2 | \sigma (\slashed{p}_a-\slashed{p}_V)\rho|a\rangle}{t_{aV}} +\frac{\langle 2 | \rho (\slashed{p}_2+\slashed{p}_V)\sigma|a\rangle}{s_{2V}} \right]\\ &\times \left\{\left(\frac{p_b^\nu}{s_{1b}} + \frac{p_3^\nu}{s_{13}}\right)(q_1-p_1)^2g^{\mu\sigma}+(2q_1-p_1)^\nu g^{\mu\sigma} - 2p_1^\mu g^{\nu\sigma} + (2p_1-q_1)^\sigma g^{\mu\nu} \right\}\,, \end{split} \end{align} where $s_{ij\dots} = (p_i + p_j + \dots)^2, t_{ij\dots} = (p_i - p_j - \dots)^2$ and $q_1 = p_a-p_2-p_V$. This is actually calculated and used in the code in a much cleaner way as follows: \begin{align}\label{eq:spinorstringVuno} S_{qQ\to Vgq^\prime Q} = i\varepsilon_\nu (p_g) \bar{u}^-(p_2)&\gamma_\rho\nu(p_{\bar{q}})\times T^d_{3b} \bar{u}^{h_3}(p_3)\gamma_\mu u^{h_b}(p_b) \times \nonumber \\ &\left( T^1_{2i}T^d_{ia} \left( \tilde{U}_1^{\nu\mu\rho}-\tilde{L}^{\nu\mu\rho}\right)+T^d_{2i}T^1_{ia}\left(\tilde{U}_2^{\nu\mu\rho}+\tilde{L}^{\nu\mu\rho}\right) \right) \end{align} If we define the objects: \begin{align}\label{eq:VunoX} X &= \varepsilon_\nu(p_g) \left[ \bar{u}^-(p_2)\gamma_\rho\nu(p_{\bar{q}})\right] \left[ \bar{u}^{h_3}(p_3)\gamma_\mu u^{h_b}(p_b)\right] \left( \tilde{U}_1^{\nu\mu\rho}-\tilde{L}^{\nu\mu\rho}\right)\\ Y &= \varepsilon_\nu(p_g) \left[ \bar{u}^-(p_2)\gamma_\rho\nu(p_{\bar{q}})\right] \left[ \bar{u}^{h_3}(p_3)\gamma_\mu u^{h_b}(p_b)\right] \left( \tilde{U}_2^{\nu\mu\rho}+\tilde{L}^{\nu\mu\rho}\right) \label{eq:WunoY} \end{align} then we can rewrite Equation \eqref{eq:spinorstringVuno} in the much simpler form: \begin{equation} S_{qQ\to Vgq^\prime Q} = iT^d_{3b} \left( T^{1}_{2i}T^d_{ia} X + T^d_{2i}T^1_{ia} Y \right) \end{equation} then, by using: \begin{align} \sum_{\text{all indices}}& T^d_{3b}T^e_{b3}T^1_{2i}T^d_{ia}T^e_{ai}T^1_{i2} = \frac{1}{2}C_F^2C_A \\ \sum_{\text{all indices}}& T^d_{3b}T^e_{b3}T^1_{2i}T^d_{ia}T^1_{ai}T^e_{i2} = \frac{1}{2}C_F^2C_A - \frac{1}{4}C_A^2C_F = -\frac{1}{4}C_F \end{align} giving then, the spin summed and helicity averaged spinor string as: \begin{equation}\label{eq:VunoSumAveS} ||\;\bar{S}_{qQ\to Vgq^\prime Q}\;|| = \frac{1}{4N_C^2} \left( \frac{1}{2}C_F^2C_A\left(|X|^2+|Y|^2\right)-\frac{1}{4}C_F\times2\mathrm{Re}\left(XY^*\right)\right) \end{equation} -\subsubsection{\texorpdfstring{$W$}{W}+Extremal \texorpdfstring{$\mathbf{q\bar{q}}$}{qqx}} +\subsubsection{\texorpdfstring{$W$}{W}+Extremal \texorpdfstring{$\mathbf{q\bar{q}}$}{qqbar}} \todo{Update when included in $Z$ + jets} The $W$+Jet sub-leading processes containing an extremal $q\bar{q}$ are related by crossing symmetry to the $W$+Jet unordered processes. This means that one can simply perform a crossing symmetry argument on eq.~\ref{eq:wunocurrent} to arrive at the extremal $q\bar{q}$ current required.We show the basic structure of the extremal $q\bar{q}$ current in figure~\ref{fig:qgimp}, neglecting the $W$-emission for simplicity. \begin{figure} \centering \includegraphics[width=0.3\textwidth]{{qqbarex_schem}} \caption{Schematic structure of the $gq \to \bar{Q}Qq$ amplitude in the limit $y_1 \sim y_2 \ll y_3$} \label{fig:qgimp} \end{figure} \begin{figure} \centering \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarex1} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarex2} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarex4} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarex5} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarex3} \end{subfigure} \caption{The five tree-level graphs which contribute to the process $gq \to \bar{Q}Qq$.} \label{fig:qg_qQQ_graphs} \end{figure} We can obtain the current for $g\rightarrow W q \bar{q}$ by evaluating the current for $W$ plus unordered emissions with the normal arguments $p_a \leftrightarrow -p_1 $ interchanged. This is a non-trivial statement: due to the minimality of the approximations made, the crossing symmetry normally present in the full amplitude may be extended to the factorised current. We must again note that swapping $p_a \leftrightarrow -p_1$ will lead to $u$-spinors with momenta with negative energy. These are identical to $v$-spinors with momenta with positive energy, up to an overall phase which is common to all terms, and can therefore be neglected. Mathematically, this is given by: \begin{align}\label{eq:crossedJ} j^\mu_{\rm W,g\to Q\bar{Q}}(p_a,p_1,p_2,p_\ell,p_{\bar{\ell}}) =i \varepsilon_{g\nu} \langle \ell | \rho | \bar \ell \rangle_L \left(T^1_{2i} T^d_{ia} (\tilde U_{1,X}^{\nu\mu\rho}-\tilde L^{\nu\mu\rho}_X) + T^d_{2i} T^1_{ia} (\tilde U_{2,X}^{\nu\mu\rho}+\tilde L_X^{\nu\mu\rho}) \right), \end{align} where the components are now given by \begin{align} \label{eq:U1tensorX} \begin{split} \tilde U_{1,X}^{\nu\mu\rho} =&\frac{\langle 2|\nu (\slashed{p}_a- \slashed{p}_2)\mu (\slashed{p}_1 + \slashed{p}_W)\rho P_L |1\rangle }{t_{a2}s_{1W}} + \frac{\langle 2|\nu (\slashed{p}_a- \slashed{p}_2)\rho P_L (\slashed{p}_a-\slashed{p}_2 - \slashed{p}_W)\mu |1\rangle }{t_{a2}t_{a2W}} \\ &- \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_W) \nu (\slashed{p}_a - \slashed{p}_2-\slashed{p}_W)\mu |1\rangle}{s_{2W}t_{a2W}}\,, \end{split}\\ \label{eq:U2tensorX} \begin{split} \tilde U_{2,X}^{\nu\mu\rho} =&-\frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_W-\slashed{p}_1)\nu (\slashed{p}_1 + \slashed{p}_W)\rho P_L |1\rangle }{t_{aW1}s_{1W}} + \frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_W- \slashed{p}_1)\rho P_L (\slashed{p}_a-\slashed{p}_1) \nu |1\rangle }{t_{a1W}t_{a1}} \\ &+ \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_W) \mu (\slashed{p}_a-\slashed{p}_1)\nu |1\rangle}{s_{2W}t_{a1}}\,, \end{split}\\ \label{eq:LtensorX} \begin{split} \tilde L^{\nu\mu\rho}_X &= \frac{1}{s_{W12}}\left[-\frac{\langle 2 |\sigma (\slashed{p}_1 + \slashed{p}_W) \rho P_L | 1\rangle}{s_{1W}} + \frac{\langle 2 |\rho P_L (\slashed{p}_2 + \slashed{p}_W) \sigma | 1\rangle }{s_{2W}} \right] \\ &\vphantom{+\frac{1}{t_{aW2}}}\quad\cdot \left( -\left( \frac{p_b^\nu}{s_{ab}} + \frac{p_n^\nu}{s_{an}} \right) (q_1+p_a)^2 g^{\sigma\mu}+ g^{\sigma \mu} (2q_1 +p_a)^\nu - g^{\mu \nu}(2p_a+q_1)^\sigma+ 2g^{\nu \sigma}p_a^\mu \right)\,, \end{split} \end{align} where $q_1=-(p_1+p_2+p_W)$. Notice in particular the similarity to the $W$+uno scenario (from which this has been derived). -\subsubsection{Central \texorpdfstring{$\mathbf{q\bar{q}}$}{qqx} Vertex} +\subsubsection{Central \texorpdfstring{$\mathbf{q\bar{q}}$}{qqbar} Vertex} The final subleading process in the $W$+Jet case is the Central $q\bar{q}$ vertex. This subleading process does not require an altered current, but an effective vertex which is contracted with two regular \HEJ currents. This complexity is dealt with nicely by the \FORM inside the \texttt{current\_generator/j\_Wqqbar\_j.frm}, which is detailed in section~\ref{sec:contr_calc}. The $W$-emission can be from the central effective vertex (scenario -dealt with by the function \lstinline!ME_WCenqqx_qq! in the file +dealt with by the function \lstinline!ME_WCenqqbar_qq! in the file \texttt{src/Wjets.cc}); or from either of the external quark legs -(scenario dealt with by \lstinline!ME_W_Cenqqx_qq! in same file). In +(scenario dealt with by \lstinline!ME_W_Cenqqbar_qq! in same file). In the pure jets case, there are 7 separate diagrams which contribute to this, which can be seen in figure~\ref{fig:qq_qQQq_graphs}. In the $W$+Jets case, there are then 45 separate contributions. \begin{figure} \centering \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen1} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen2} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen3} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen4} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen5} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen6} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{qqbarcen7} \end{subfigure} \caption{All Feynman diagrams which contribute to $qq' \to qQ\bar{Q}q'$ at leading order.} \label{fig:qq_qQQq_graphs} \end{figure} The end result is of the effective vertex, after derivation, is: \begin{align} \label{eq:EffectiveVertexqqbar} \begin{split} V^{\mu\nu}_{\text{Eff}}=& - i \frac{C_1}{s_{23AB}}\left(X^{\mu\nu\sigma}_{1a}\hat{t_1} + X^{\mu\nu\sigma}_{4b}\hat{t_3} +V^{\mu\nu\sigma}_{3g}\right)J_{\text{V} \sigma}(p_2,p_A,p_B,p_3) \\ &\quad +iC_2X^{\mu\nu}_{Unc}+iC_3X^{\mu\nu}_{Cro}, \end{split} \end{align} where: \begin{align} \begin{split} C_1=&T^e_{1q}T^g_{qa}T^e_{23}T^g_{4b} - T^g_{1q}T^e_{qa}T^e_{23}T^g_{4b} = f^{egc}T^c_{1a}T^e_{23}T^g_{4b}, \\ C_2=&T^g_{1a}T^g_{2q}T^{g'}_{q3}T^{g'}_{4b} \\ C_3=&T^g_{1a}T^{g'}_{2q}T^g_{q3}T^{g'}_{4b} \end{split} \end{align} are the colour factors of different contributions and $J_\text{V}$ is given in equation~(\ref{eq:J_V}). The following tensor structures correspond to groupings of diagrams in figure~\ref{fig:qq_qQQq_graphs}. \begin{eqnarray} \label{eq:X_1a} X_{1a}^{\mu\nu\sigma} &= \frac{-g^{\mu\nu}}{s_{23AB}\hat{t_3}}\left(\frac{p^\sigma_a}{s_{a2} + s_{a3} + s_{aA} + s_{aB}} + \frac{p^\sigma_1}{s_{12} + s_{13} + s_{1A} + s_{1B}}\right) \\ \label{eq:X_4b} X_{4b}^{\mu\nu\sigma} &=\frac{g^{\mu\nu}}{s_{23AB}\hat{t_1}}\left(\frac{p^\sigma_b}{s_{b2} + s_{b3} + s_{bA} + s_{bB}}+ \frac{p^\sigma_4}{s_{42} + s_{43} + s_{4A} + s_{4B}}\right) \end{eqnarray} correspond to the first and second row of diagrams in figure~\ref{fig:qq_qQQq_graphs}. \begin{align} \label{eq:3GluonWEmit} \begin{split} V^{\mu\nu\sigma}_{3g}=\frac{1}{ \hat{t}_1s_{23AB}\,\hat{t}_3} \bigg[&\left(q_1+p_2+p_3+p_A+p_B\right)^\nu g^{\mu\sigma}+ \\ &\quad\left(q_3-p_2-p_3-p_A-p_B\right)^\mu g^{\sigma\nu}- \\ & \qquad\left(q_1+q_3\right)^\sigma g^{\mu\nu}\bigg]J_{\text{V} \sigma}(p_2,p_A,p_B,p_3) \end{split} \end{align} corresponds to the left diagram on the third row in figure~\ref{fig:qq_qQQq_graphs}. One notes that all of these contributions have the same colour factor, and as such we can group them together nicely before summing over helicities etc. As such, the function \lstinline!M_sym_W! returns a contraction of the above tensor containing the information from these 5 groupings of contributions (30 diagrams in total). It is available through the generated header \texttt{j\_Wqqbar\_j.hh} (see section~\ref{sec:cur_gen}). \begin{align} \label{eq:X_Unc} \begin{split} X^{\mu\nu}_{Unc}=\frac{\langle A|\sigma P_L|B\rangle}{\hat{t_1}\hat{t_3}} \bar{u}_2&\left[-\frac{ \gamma^\sigma P_L(\slashed{p}_2+\slashed{p}_A+\slashed{p}_B)\gamma^\mu (\slashed{q}_3+ \slashed{p}_3)\gamma^\nu}{(s_{2AB})(t_{unc_{2}})}\right.+ \\ &\qquad\left. \frac{\gamma^\mu(\slashed{q}_1-\slashed{p}_2)\gamma^\sigma P_L(\slashed{q}_3+\slashed{p}_3)\gamma^\nu}{(t_{unc_{1}})(t_{unc_{2}})}\right. + \\ &\qquad\qquad\left. \frac{\gamma^\mu(\slashed{q}_1-\slashed{p}_2)\gamma^\nu(\slashed{p}_3+\slashed{p}_A+\slashed{p}_B)\gamma^\sigma P_L }{(t_{unc_1})(s_{3AB})}\right]v_3 \end{split} \end{align} corresponds to the diagram on the right of row three in figure~\ref{fig:qq_qQQq_graphs}. This contribution to the current contraction can be obtained in the code with the function \lstinline!M_uncross_W!. \begin{align} \begin{split} \label{eq:X_Cro} X^{\mu\nu}_{Cro}=\frac{\langle A|\sigma P_L|B\rangle}{\hat{t_1}\hat{t_3}} \bar{u}_2&\left[-\frac{ \gamma^\nu(\slashed{q}_3+\slashed{p}_2)\gamma^\mu (\slashed{p}_3+\slashed{p}_A+\slashed{p}_B)\gamma^\sigma P_L}{(t_{cro_1})(s_{3AB})}\right.+ \\ &\qquad\left. \frac{\gamma^\nu(\slashed{q}_3+\slashed{p}_2)\gamma^\sigma P_L(\slashed{q}_1-\slashed{p}_3)\gamma^\mu}{(t_{cro_{1}})(t_{cro_{2}})}\right.+ \\ &\qquad\qquad\left . \frac{\gamma^\sigma P_L(\slashed{p}_2+\slashed{p}_A+\slashed{p}_B)\gamma^\nu(\slashed{q}_1-\slashed{p}_3)\gamma^\mu }{(s_{2AB})(t_{cro_2})}\right]v_3 \end{split} \end{align} corresponds to the last diagram in figure~\ref{fig:qq_qQQq_graphs}. This contribution to the current contraction can be obtained in the code with the function \lstinline!M_cross_W!. %%% Local Variables: %%% mode: latex %%% TeX-master: "developer_manual" %%% End: diff --git a/doc/sphinx/HEJ.rst b/doc/sphinx/HEJ.rst index 34eba56..f19fb61 100644 --- a/doc/sphinx/HEJ.rst +++ b/doc/sphinx/HEJ.rst @@ -1,443 +1,443 @@ .. _`Running HEJ 2`: Running HEJ 2 ============= Quick start ----------- In order to run HEJ 2, you need a configuration file and a file containing fixed-order events. A sample configuration is given by the :file:`config.yml` file distributed together with HEJ 2. Events in the Les Houches Event File format can be generated with standard Monte Carlo generators like `MadGraph5_aMC@NLO `_ or `Sherpa `_. If HEJ 2 was compiled with `HDF5 `_ support, it can also read and write event files in the format suggested in `arXiv:1905.05120 `_. HEJ 2 assumes that the cross section is given by the sum of the event weights. Depending on the fixed-order generator it may be necessary to adjust the weights in the Les Houches Event File accordingly. The processes supported by HEJ 2 are - Pure multijet production - Production of a Higgs boson with jets - Production of a W boson with jets - Production of jets with a charged lepton-antilepton pair, via a virtual Z boson or photon where at least two jets are required in each case. For the time being, only leading-order input events are supported. After generating an event file :file:`events.lhe` adjust the parameters under the `fixed order jets`_ setting in :file:`config.yml` to the settings in the fixed-order generation. Resummation can then be added by running:: HEJ config.yml events.lhe Using the default settings, this will produce an output event file :file:`HEJ.lhe` with events including high-energy resummation. When using the `Docker image `_, HEJ can be run with .. code-block:: bash docker run -v $PWD:$PWD -w $PWD hejdock/hej HEJ config.yml events.lhe .. _`HEJ 2 settings`: Settings -------- HEJ 2 configuration files follow the `YAML `_ format. The following configuration parameters are supported: .. _`trials`: **trials** High-energy resummation is performed by generating a number of resummation phase space configurations corresponding to an input fixed-order event. This parameter specifies how many such configurations HEJ 2 should try to generate for each input event. Typical values vary between 10 and 100. .. _`fixed order jets`: **fixed order jets** This tag collects a number of settings specifying the jet definition in the event input. The settings should correspond to the ones used in the fixed-order Monte Carlo that generated the input events. .. _`fixed order jets: min pt`: **min pt** Minimum transverse momentum in GeV of fixed-order jets. .. _`fixed order jets: algorithm`: **algorithm** The algorithm used to define jets. Allowed settings are :code:`kt`, :code:`cambridge`, :code:`antikt`, :code:`cambridge for passive`. See the `FastJet `_ documentation for a description of these algorithms. .. _`fixed order jets: R`: **R** The R parameter used in the jet algorithm, roughly corresponding to the jet radius in the plane spanned by the rapidity and the azimuthal angle. .. _`resummation jets`: **resummation jets** This tag collects a number of settings specifying the jet definition in the observed, i.e. resummed events. These settings are optional, by default the same values as for the `fixed order jets`_ are assumed. .. _`resummation jets: min pt`: **min pt** Minimum transverse momentum in GeV of resummation jets. This should be between 25% and 50% larger than the minimum transverse momentum of fixed order jets set by `fixed order jets: min pt`_. .. _`resummation jets: algorithm`: **algorithm** The algorithm used to define jets. The HEJ 2 approach to resummation relies on properties of :code:`antikt` jets, so this value is strongly recommended. For a list of possible other values, see the `fixed order jets: algorithm`_ setting. .. _`resummation jets: R`: **R** The R parameter used in the jet algorithm. .. _`event treatment`: **event treatment** Specify how to treat different event types. The different event types contribute to different orders in the high-energy limit. The possible values are :code:`reweight` to enable resummation, :code:`keep` to keep the events as they are up to a possible change of renormalisation and factorisation scale, and :code:`discard` to discard these events. The following types are implemented for the different bosons: .. csv-table:: - :header: , "FKL", "unordered", "extremal qqx", "central qqx" + :header: , "FKL", "unordered", "extremal qqbar", "central qqbar" :widths: auto :align: center :stub-columns: 1 "pure jets", "Yes", "Yes", "Yes", "Yes" "Higgs + jets", "Yes", "Yes", "No", "No" "W + jets", "Yes", "Yes", "Yes", "Yes" "Z/γ + jets", "Yes", "Yes", "No", "No" Non-implemented process will always be classified as :code:`non-resummable`. The different types are: .. _`FKL`: **FKL** Specifies how to treat events respecting FKL rapidity ordering, where all but the two partons extremal in rapidity have to be gluons, e.g. :code:`u d => u g d`. These configurations are dominant in the high-energy limit. .. _`unordered`: **unordered** Specifies how to treat events with one gluon emission that does not respect FKL ordering, e.g. :code:`u d => g u d`. In the high-energy limit, such configurations are logarithmically suppressed compared to FKL configurations. - .. _`extremal qqx`: + .. _`extremal qqbar`: - **extremal qqx** + **extremal qqbar** Specifies how to treat events with a quark-antiquark pair as extremal partons in rapidity, e.g. :code:`g d => u u_bar d`. In the high-energy limit, such configurations are logarithmically suppressed compared to FKL configurations. - .. _`central qqx`: + .. _`central qqbar`: - **central qqx** + **central qqbar** Specifies how to treat events with a quark-antiquark pair central in rapidity, e.g. :code:`g g => g u u_bar g`. In the high-energy limit, such configurations are logarithmically suppressed compared to FKL configurations. .. _`non-resummable`: **non-resummable** Specifies how to treat events that do not fall into any of the above categories or that are not yet implemented. Only :code:`keep` or :code:`discard` are valid options, *not* :code:`reweight` for obvious reasons. .. _`scales`: **scales** Specifies the renormalisation and factorisation scales for the output events. This can either be a single entry or a list :code:`[scale1, scale2, ...]`. For the case of a list the first entry defines the central scale. Possible values are fixed numbers to set the scale in GeV or the following: - :code:`H_T`: The sum of the scalar transverse momenta of all final-state particles - :code:`max jet pperp`: The maximum transverse momentum of all jets - :code:`jet invariant mass`: Sum of the invariant masses of all jets - :code:`m_j1j2`: Invariant mass between the two hardest jets. Scales can be multiplied or divided by overall factors, e.g. :code:`H_T/2`. It is also possible to import scales from an external library, see :ref:`Custom scales` .. _`scale factors`: **scale factors** A list of numeric factors by which each of the `scales`_ should be multiplied. Renormalisation and factorisation scales are varied independently. For example, a list with entries :code:`[0.5, 2]` would give the four scale choices (0.5μ\ :sub:`r`, 0.5μ\ :sub:`f`); (0.5μ\ :sub:`r`, 2μ\ :sub:`f`); (2μ\ :sub:`r`, 0.5μ\ :sub:`f`); (2μ\ :sub:`r`, 2μ\ :sub:`f`) in this order. The ordering corresponds to the order of the final event weights. .. _`max scale ratio`: **max scale ratio** Specifies the maximum factor by which renormalisation and factorisation scales may difer. For a value of :code:`2` and the example given for the `scale factors`_ the scale choices (0.5μ\ :sub:`r`, 2μ\ :sub:`f`) and (2μ\ :sub:`r`, 0.5μ\ :sub:`f`) will be discarded. .. _`log correction`: **log correction** Whether to include corrections due to the evolution of the strong coupling constant in the virtual corrections. Allowed values are :code:`true` and :code:`false`. .. _`unweight`: **unweight** Settings for unweighting events. Unweighting can greatly reduce the number of resummation events, speeding up analyses and shrinking event file sizes. .. _`type`: **type** How to unweight events. The supported settings are - :code:`weighted`: Generate weighted events. Default, if nothing else specified. - :code:`resummation`: Unweight only resummation events. Each set of resummation events coming from *a single fixed order event* are unweighted separately according to the largest weight in the current chunk of events. - :code:`partial`: Unweight only resummation events with weights below a certain threshold. The weight threshold is determined automatically in a calibration run prior to the usual event generation. .. _`unweight: trials`: **trials** Maximum number of trial resummation events generated in the calibration run for partial unweighting. This option should only be set for partial unweighting. If possible, each trial is generated from a different input fixed-order event. If there are not sufficiently many input events, more than one trial event may be generated for each of them and the actual number of trial events may be smaller than requested. Increasing the number of trials generally leads to better unweighting calibration but increases the run time. Between 1000 and 10000 trials are usually sufficient. .. _`unweight: max deviation`: **max deviation** Controls the range of events to which unweighting is applied. This option should only be set for partial unweighting. A larger value means that a larger fraction of events are unweighted. Typical values are between -1 and 1. .. _`event output`: **event output** Specifies the name of a single event output file or a list of such files. The file format is either specified explicitly or derived from the suffix. For example, :code:`events.lhe` or, equivalently :code:`Les Houches: events.lhe` generates an output event file :code:`events.lhe` in the Les Houches format. The supported formats are - :code:`file.lhe` or :code:`Les Houches: file`: The Les Houches event file format. - :code:`file.hepmc2` or :code:`HepMC2: file`: HepMC format version 2. - :code:`file.hepmc3` or :code:`HepMC3: file`: HepMC format version 3. - :code:`file.hepmc` or :code:`HepMC: file`: The latest supported version of the HepMC format, currently version 3. - :code:`file.hdf5` or :code:`HDF5: file`: The HDF5-based format of `arXiv:1905.05120 `_. .. _`random generator`: **random generator** Sets parameters for random number generation. .. _`random generator: name`: **name** Which random number generator to use. Currently, :code:`mixmax` and :code:`ranlux64` are supported. Mixmax is recommended. See the `CLHEP documentation `_ for details on the generators. .. _`random generator: seed`: **seed** The seed for random generation. This should be a single number for :code:`mixmax` and the name of a state file for :code:`ranlux64`. .. _`analyses`: **analyses** Names and settings for one or more custom and Rivet event analyses. Entries containing the :code:`rivet` key are interpreted as Rivet analyses; the values corresponding to this key should be the analyses names. In addition, there is a mandatory :code:`output` key which determines the prefix for the yoda output file. For a custom analysis the :code:`plugin` sub-entry should be set to the analysis file path. All further entries are passed on to the analysis. See :ref:`Writing custom analyses` for details. .. _`vev`: **vev** Higgs vacuum expectation value in GeV. All electro-weak constants are derived from this together with the `particle properties`_. .. _`particle properties`: **particle properties** Specifies various properties of the different particles (Higgs, W or Z). All electro-weak constants are derived from these together with the :ref:`vacuum expectation value`. .. _`particle properties: particle`: **Higgs, W or Z** The particle (Higgs, |W+| or |W-|, Z) for which the following properties are defined. .. |W+| replace:: W\ :sup:`+` .. |W-| replace:: W\ :sup:`-` .. _`particle properties: particle: mass`: **mass** The mass of the particle in GeV. .. _`particle properties: particle: width`: **width** The total decay width of the particle in GeV. .. _`Higgs coupling`: **Higgs coupling** This collects a number of settings concerning the effective coupling of the Higgs boson to gluons. This is only relevant for the production process of a Higgs boson with jets and only supported if HEJ 2 was compiled with `QCDLoop `_ support. .. _`Higgs coupling: use impact factors`: **use impact factors** Whether to use impact factors for the coupling to the most forward and most backward partons. Impact factors imply the infinite top-quark mass limit. .. _`Higgs coupling: mt`: **mt** The value of the top-quark mass in GeV. If this is not specified, the limit of an infinite mass is taken. .. _`Higgs coupling: include bottom`: **include bottom** Whether to include the Higgs coupling to bottom quarks. .. _`Higgs coupling: mb`: **mb** The value of the bottom-quark mass in GeV. Only used for the Higgs coupling, external bottom-quarks are always assumed to be massless. Advanced Settings ~~~~~~~~~~~~~~~~~ All of the following settings are optional. Please **do not set** any of the following options, unless you know exactly what you are doing. The default behaviour gives the most reliable results for a wide range of observables. .. _`soft pt regulator`: **soft pt regulator** Specifies the maximum fraction that soft radiation can contribute to the transverse momentum of each the tagging jets, i.e. any jet that affects the event classification, like the most forward and most backward jet or the jets - of the central qqx pair. This setting is needed to regulate an otherwise + of the central qqbar pair. This setting is needed to regulate an otherwise cancelled divergence. Default is 0.1 .. _`max ext soft pt fraction`: **max ext soft pt fraction** This is the same as `soft pt regulator`_ and will be removed in future versions. .. _`min extparton pt`: **min extparton pt** Specifies the minimum transverse momentum in GeV of the most forward and the most backward parton. Its value should be slightly below the minimum transverse momentum of jets specified by `resummation jets: min pt`_. This setting got superseded by `soft pt regulator`_ and will be removed in future versions. .. _`max events`: **max events** Maximal number of (input) Fixed Order events. HEJ will stop after processing `max events` many events. Default considers all events. .. _`regulator parameter`: **regulator parameter** Slicing parameter to regularise the subtraction term, called :math:`\lambda` in `arxiv:1706.01002 `_. Default is 0.2 diff --git a/doc/sphinx/HEJFOG.rst b/doc/sphinx/HEJFOG.rst index b485fe4..4959f66 100644 --- a/doc/sphinx/HEJFOG.rst +++ b/doc/sphinx/HEJFOG.rst @@ -1,299 +1,299 @@ The HEJ Fixed Order Generator ============================= For high jet multiplicities event generation with standard fixed-order generators becomes increasingly cumbersome. For example, the leading-order production of a Higgs Boson with five or more jets is computationally prohibitively expensive. To this end, HEJ 2 provides the ``HEJFOG`` fixed-order generator that allows to generate events with high jet multiplicities. To facilitate the computation the limit of Multi-Regge Kinematics with large invariant masses between all outgoing particles is assumed in the matrix elements. The typical use of the ``HEJFOG`` is to supplement low-multiplicity events from standard generators with high-multiplicity events before using the HEJ 2 program to add high-energy resummation. Installation ------------ The ``HEJFOG`` comes bundled together with HEJ 2 and the installation is very similar. After downloading HEJ 2 and installing the prerequisites as described in :ref:`Installation` the ``HEJFOG`` can be installed with:: cmake /path/to/FixedOrderGen -DCMAKE_INSTALL_PREFIX=target/directory make install where :file:`/path/to/FixedOrderGen` refers to the :file:`FixedOrderGen` subdirectory in the HEJ 2 directory. If HEJ 2 was installed to a non-standard location, it may be necessary to specify the directory containing :file:`HEJ-config.cmake`. If the base installation directory is :file:`/path/to/HEJ`, :file:`HEJ-config.cmake` should be found in :file:`/path/to/HEJ/lib/cmake/HEJ` and the commands for installing the ``HEJFOG`` would read:: cmake /path/to/FixedOrderGen -DHEJ_DIR=/path/to/HEJ/lib/cmake/HEJ -DCMAKE_INSTALL_PREFIX=target/directory make install The installation can be tested with:: make test provided that the ``CT10nlo`` PDF sets is installed. Running the fixed-order generator --------------------------------- After installing the ``HEJFOG`` you can modify the provided configuration file :file:`configFO.yml` and run the generator with:: HEJFOG configFO.yml The resulting event file, by default named :file:`HEJFO.lhe`, can then be fed into HEJ 2 like any event file generated from a standard fixed-order generator, see :ref:`Running HEJ 2`. Settings -------- Similar to HEJ 2, the ``HEJFOG`` uses a `YAML `_ configuration file. The settings are .. _`process`: **process** The scattering process for which events are being generated. The format is :code:`in1 in2 => out1 out2 ...` The incoming particles, :code:`in1`, :code:`in2` can be - quarks: :code:`u`, :code:`d`, :code:`u_bar`, and so on - gluons: :code:`g` - protons :code:`p` or antiprotons :code:`p_bar` The outgoing particles, can be - jets: :code:`j`, multiple jets can be grouped together e.g. :code:`2j` - bosons: Any gauge boson, they might further :ref:`decay` - leptons: if they can be reconstructed to a :code:`W+` or :code:`W-`, e.g. :code:`e+ nu_e` At most one of the outgoing particles can be a boson, the rest has to be partonic. At the moment only Higgs :code:`h`, :code:`W+` and :code:`W-` bosons are supported. All other outgoing particles are jets. There have to be at least two jets. Instead of the leptons decays of the bosons can be added through the :ref:`decays`. The latter is required to decay a Higgs boson. So :code:`p p => Wp j j` is the same as :code:`p p => e+ nu_e 2j`, if the decay :code:`Wp => e+ nu_e` is specified in `decays`_. .. _`events`: **events** Specifies the number of events to generate. .. _`jets`: **jets** Defines the properties of the generated jets. .. _`jets: min pt`: **min pt** Minimum jet transverse momentum in GeV. .. _`jets: peak pt`: **peak pt** Optional setting to specify the dominant jet transverse momentum in GeV. If the generated events are used as input for HEJ resummation, this should be set to the minimum transverse momentum of the resummation jets. In all cases it has to be larger than :code:`min pt`. The effect is that only a small fraction of jets will be generated with a transverse momentum below the value of this setting. .. _`jets: algorithm`: **algorithm** The algorithm used to define jets. Allowed settings are :code:`kt`, :code:`cambridge`, :code:`antikt`, :code:`cambridge for passive`. See the `FastJet `_ documentation for a description of these algorithms. .. _`jets: R`: **R** The R parameter used in the jet algorithm. .. _`jets: max rapidity`: **max rapidity** Maximum absolute value of the jet rapidity. .. _`beam`: **beam** Defines various properties of the collider beam. .. _`beam: energy`: **energy** The beam energy in GeV. For example, the 13 TeV LHC corresponds to a value of 6500. .. _`beam: particles`: **particles** A list :code:`[p1, p2]` of two beam particles. The only supported entries are protons :code:`p` and antiprotons :code:`p_bar`. .. _`pdf`: **pdf** The `LHAPDF number `_ of the PDF set. For example, 230000 corresponds to an NNPDF 2.3 NLO PDF set. .. _`subleading fraction`: **subleading fraction** This setting is related to the fraction of events that are not a FKL configuration. All possible subleading process are listed in :ref:`subleading channels`. This value must be positive and not larger than 1. It should typically be chosen between 0.01 and 0.1. Note that while this parameter influences the chance of generating subleading configurations, it generally does not correspond to the actual fraction of subleading events. .. _`subleading channels`: **subleading channels** Optional parameters to select a specific subleading process, multiple - channels can be selected at once. If multiple subleading configurations - are possible one will be selected at random for each event, thus each - final state will include at most one subleading process, e.g. either - :code:`unordered` or :code:`central qqx`. Only has an effect if - :code:`subleading fraction` is non-zero. Subleading processes requested here - also have to be available from HEJ, for a list of implemented processes see - :ref:`event treatment`. The following values are allowed: + channels can be selected at once. If multiple subleading configurations are + possible one will be selected at random for each event, thus each final state + will include at most one subleading process, e.g. either :code:`unordered` or + :code:`central qqbar`. Only has an effect if :code:`subleading fraction` is + non-zero. Subleading processes requested here also have to be available from + HEJ, for a list of implemented processes see :ref:`event treatment`. The following values are allowed: - :code:`all`: All channels allowed. This is the default. - :code:`none`: No subleading contribution, only the leading (FKL) configurations are allowed. This is equivalent to :code:`subleading fraction: 0`. - :code:`unordered`: Unordered emission allowed. Unordered emission are any rapidity ordering where exactly one gluon is emitted outside the rapidity ordering required in FKL events. More precisely, if at least one of the incoming particles is a quark or antiquark and there are more than two jets in the final state, :code:`subleading fraction` states the probability that the flavours of the outgoing particles are assigned in such a way that an unordered configuration arises. - - :code:`extremal qqx`: Production of a quark-antiquark pair as extremal - partons in rapidity. If the :code:`central qqx` channel is allowed, + - :code:`extremal qqbar`: Production of a quark-antiquark pair as extremal + partons in rapidity. If the :code:`central qqbar` channel is allowed, :code:`subleading fraction` gives the probability of emitting a quark-antiquark pair in place of two gluons adjacent in rapidity at the most forward or backwards quarks. - - :code:`central qqx`: Production of a central quark-antiquark pair. This - setting is similar to :code:`extremal qqx`, but :code:`subleading fraction` + - :code:`central qqbar`: Production of a central quark-antiquark pair. This + setting is similar to :code:`extremal qqbar`, but :code:`subleading fraction` gives the probability of emitting a quark-antiquark pair in place of two gluons adjacent in rapidity at any point *inside* the :code:`FKL` gluon chain. .. _`unweight`: **unweight** This setting defines the parameters for the partial unweighting of events. You can disable unweighting by removing this entry from the configuration file. .. _`unweight: sample size`: **sample size** The number of weighted events used to calibrate the unweighting. A good default is to set this to the number of target `events`_. If the number of `events`_ is large this can lead to significant memory consumption and a lower value should be chosen. Contrarily, for large multiplicities the unweighting efficiency becomes worse and the sample size should be increased. .. _`unweight: max deviation`: **max deviation** Controls the range of events to which unweighting is applied. A larger value means that a larger fraction of events are unweighted. Typical values are between -1 and 1. .. _`decays`: **decays** Optional setting specifying the decays of the particle. Only the decay into two particles is implemented. Each decay has the form :code:`boson: {into: [p1,p2], branching ratio: r}` where :code:`boson` is the corresponding boson, :code:`p1` and :code:`p2` are the particle names of the decay product (e.g. :code:`photon`) and :code:`r` is the branching ratio. The branching ratio is optional (:code:`1` by default). Decays of a Higgs boson are treated as the production and subsequent decay of an on-shell Higgs boson, so decays into e.g. Z bosons are not supported. In contrast W bosons are decayed off-shell, thus the branching ratio should not be specified or set to :code:`1`. .. _`FOG scales`: **scales** Specifies the renormalisation and factorisation scales for the output events. For details, see the corresponding entry in the HEJ 2 :ref:`settings`. Note that this should usually be a single value, as the weights resulting from additional scale choices will simply be ignored when adding high-energy resummation with HEJ 2. .. _`FOG event output`: **event output** Specifies the name of a single event output file or a list of such files. See the corresponding entry in the HEJ 2 :ref:`settings` for details. .. _`RanLux init`: .. _`FOG random generator`: **random generator** Sets parameters for random number generation. See the HEJ 2 :ref:`settings` for details. .. _`FOG analyses`: **analyses** Specifies the name and settings for one or more analyses library. This can be useful to specify cuts at the fixed-order level. See the corresponding entry in the HEJ 2 :ref:`settings` for details. .. _`FOG vev`: **vev** Higgs vacuum expectation value in GeV. All electro-weak constants are derived from this together with the :ref:`particle properties `. .. _`FOG particle properties`: **particle properties** Specifies various properties of the different particles (Higgs, W or Z). These have to be specified for all bosons. See the corresponding entry in the HEJ 2 :ref:`settings` for details. .. _`FOG Higgs coupling`: **Higgs coupling** This collects a number of settings concerning the effective coupling of the Higgs boson to gluons. See the corresponding entry in the HEJ 2 :ref:`settings` for details. diff --git a/include/HEJ/Config.hh b/include/HEJ/Config.hh index 6867e0b..d5aa15d 100644 --- a/include/HEJ/Config.hh +++ b/include/HEJ/Config.hh @@ -1,254 +1,254 @@ /** \file * \brief HEJ 2 configuration parameters * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include "fastjet/JetDefinition.hh" #include "yaml-cpp/yaml.h" #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" #include "HEJ/Fraction.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/event_types.hh" #include "HEJ/optional.hh" #include "HEJ/output_formats.hh" namespace HEJ { //! Jet parameters struct JetParameters{ fastjet::JetDefinition def; /**< Jet Definition */ double min_pt{}; /**< Minimum Jet Transverse Momentum */ }; //! Settings for scale variation struct ScaleConfig{ //! Base scale choices std::vector base; //! Factors for multiplicative scale variation std::vector factors; //! Maximum ratio between renormalisation and factorisation scale double max_ratio{}; }; //! Settings for random number generator struct RNGConfig { //! Random number generator name std::string name; //! Optional initial seed optional seed; }; //! Settings for partial unweighting struct PartialUnweightConfig { //! Number of trials for training size_t trials; //! Maximum distance in standard deviations from mean logarithmic weight double max_dev; }; /**! Possible treatments for fixed-order input events. * * The program will decide on how to treat an event based on * the value of this enumeration. */ enum class EventTreatment{ reweight, /**< Perform resummation */ keep, /**< Keep the event */ discard, /**< Discard the event */ }; //! Container to store the treatments for various event types using EventTreatMap = std::map; //! Possible setting for the event weight enum class WeightType{ weighted, //!< weighted events unweighted_resum, //!< unweighted only resummation part partially_unweighted //!< mixed weighted and unweighted }; /**! Input parameters. * * This struct handles stores all configuration parameters * needed in a HEJ 2 run. * * \internal To add a new option: * 1. Add a member to the Config struct. * 2. Inside "src/YAMLreader.cc": * - Add the option name to the "supported" Node in * get_supported_options. * - Initialise the new Config member in to_Config. * The functions set_from_yaml (for mandatory options) and * set_from_yaml_if_defined (non-mandatory) may be helpful. * 3. Add a new entry (with short description) to config.yaml * 4. Update the user documentation in "doc/Sphinx/" */ struct Config { //! %Parameters for scale variation ScaleConfig scales; //! Resummation jet properties JetParameters resummation_jets; //! Fixed-order jet properties JetParameters fixed_order_jets; //! Minimum transverse momentum for extremal partons //! \deprecated This will be removed in future versions. //! Use \ref soft_pt_regulator instead. double min_extparton_pt = 0.; //! \deprecated This is equivalent to\ref soft_pt_regulator //! and will be removed in future versions. optional> max_ext_soft_pt_fraction{}; //! @brief Maximum transverse momentum fraction from soft radiation in any - //! tagging jet (e.g. extremal or qqx jet) + //! tagging jet (e.g. extremal or qqbar jet) Fraction soft_pt_regulator{ DEFAULT_SOFT_PT_REGULATOR }; //! The regulator lambda for the subtraction terms double regulator_lambda = CLAMBDA; //! Number of resummation configurations to generate per fixed-order event size_t trials{}; //! Maximal number of events optional max_events; //! Whether to include the logarithmic correction from \f$\alpha_s\f$ running bool log_correction{}; //! Event output files names and formats std::vector output; //! Parameters for random number generation RNGConfig rng; //! Map to decide what to do for different event types EventTreatMap treat; //! %Parameters for custom analysis //! @deprecated use analyses_parameters instead YAML::Node analysis_parameters; //! %Parameters for custom analyses std::vector analyses_parameters; //! Settings for effective Higgs-gluon coupling HiggsCouplingSettings Higgs_coupling; //! elector weak parameters EWConstants ew_parameters; //! Type of event weight e.g. (un)weighted WeightType weight_type; //! Settings for partial unweighting optional unweight_config; }; //! Configuration options for the PhaseSpacePoint class struct PhaseSpacePointConfig { PhaseSpacePointConfig() = default; PhaseSpacePointConfig( JetParameters jet_param, double min_extparton_pt = 0., Fraction soft_pt_regulator = Fraction{DEFAULT_SOFT_PT_REGULATOR} ): jet_param{std::move(jet_param)}, min_extparton_pt{min_extparton_pt}, soft_pt_regulator{std::move(soft_pt_regulator)} {} //! Properties of resummation jets JetParameters jet_param; //! Minimum transverse momentum for extremal partons //! \deprecated This will be removed in future versions. //! Use \ref soft_pt_regulator instead. double min_extparton_pt = 0.; //! \deprecated This is equivalent to\ref soft_pt_regulator //! and will be removed in future versions. optional> max_ext_soft_pt_fraction{}; //! @brief Maximum transverse momentum fraction from soft radiation in any - //! tagging jet (e.g. extremal or qqx jet) + //! tagging jet (e.g. extremal or qqbar jet) Fraction soft_pt_regulator{ DEFAULT_SOFT_PT_REGULATOR }; }; //! Configuration options for the MatrixElement class struct MatrixElementConfig { MatrixElementConfig() = default; MatrixElementConfig( bool log_correction, HiggsCouplingSettings Higgs_coupling, EWConstants ew_parameters, double regulator_lambda = CLAMBDA ): log_correction{log_correction}, Higgs_coupling{std::move(Higgs_coupling)}, ew_parameters{std::move(ew_parameters)}, regulator_lambda{regulator_lambda} {} //! Whether to include the logarithmic correction from \f$\alpha_s\f$ running bool log_correction{}; //! Settings for effective Higgs-gluon coupling HiggsCouplingSettings Higgs_coupling; //! elector weak parameters EWConstants ew_parameters; //! The regulator lambda for the subtraction terms double regulator_lambda = CLAMBDA; }; //! Configuration options for the EventReweighter class struct EventReweighterConfig { //! Settings for phase space point generation PhaseSpacePointConfig psp_config; //! Settings for matrix element calculation MatrixElementConfig ME_config; //! Access properties of resummation jets JetParameters & jet_param() { return psp_config.jet_param;} //! Access properties of resummation jets (const version) JetParameters const & jet_param() const { return psp_config.jet_param;} //! Treatment of the various event types EventTreatMap treat; }; /**! Extract PhaseSpacePointConfig from Config * * \internal We do not provide a PhaseSpacePointConfig constructor from Config * so that PhaseSpacePointConfig remains an aggregate. * This faciliates writing client code (e.g. the HEJ fixed-order generator) * that creates a PhaseSpacePointConfig *without* a Config object. * * @see to_MatrixElementConfig, to_EventReweighterConfig */ inline PhaseSpacePointConfig to_PhaseSpacePointConfig(Config const & conf) { return { conf.resummation_jets, conf.min_extparton_pt, conf.max_ext_soft_pt_fraction?*conf.max_ext_soft_pt_fraction :conf.soft_pt_regulator }; } /**! Extract MatrixElementConfig from Config * * @see to_PhaseSpacePointConfig, to_EventReweighterConfig */ inline MatrixElementConfig to_MatrixElementConfig(Config const & conf) { return {conf.log_correction, conf.Higgs_coupling, conf.ew_parameters, conf.regulator_lambda}; } /**! Extract EventReweighterConfig from Config * * @see to_PhaseSpacePointConfig, to_MatrixElementConfig */ inline EventReweighterConfig to_EventReweighterConfig(Config const & conf) { return { to_PhaseSpacePointConfig(conf), to_MatrixElementConfig(conf), conf.treat }; } } // namespace HEJ diff --git a/include/HEJ/PhaseSpacePoint.hh b/include/HEJ/PhaseSpacePoint.hh index 85fd82b..6466d42 100644 --- a/include/HEJ/PhaseSpacePoint.hh +++ b/include/HEJ/PhaseSpacePoint.hh @@ -1,204 +1,205 @@ /** \file * \brief Contains the PhaseSpacePoint Class * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include #include "fastjet/PseudoJet.hh" #include "HEJ/Config.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "HEJ/StatusCode.hh" namespace HEJ { struct RNG; //! Generated point in resummation phase space class PhaseSpacePoint{ public: //! No default PhaseSpacePoint Constructor PhaseSpacePoint() = delete; //! PhaseSpacePoint Constructor /** * @param ev Clustered Jet Event * @param conf Configuration parameters * @param ran Random number generator */ PhaseSpacePoint( Event const & ev, PhaseSpacePointConfig conf, RNG & ran ); //! Get phase space point weight double weight() const{ return weight_; } //! Access incoming particles std::array const & incoming() const{ return incoming_; } //! Access outgoing particles std::vector const & outgoing() const{ return outgoing_; } //! Particle decays /** * The key in the returned map corresponds to the index in the * vector returned by outgoing() */ std::unordered_map> const & decays() const{ return decays_; } //! Status code of generation StatusCode status() const{ return status_; } static constexpr int NG_MAX = 1000; //!< maximum number of extra gluons private: friend Event::EventData to_EventData(PhaseSpacePoint psp); //! /internal returns the clustered jets sorted in rapidity std::vector cluster_jets( std::vector const & partons ) const; bool pass_resummation_cuts( std::vector const & jets ) const; bool pass_extremal_cuts( fastjet::PseudoJet const & ext_parton, fastjet::PseudoJet const & jet ) const; double estimate_emission_rapidity_range(Event const & event) const; double estimate_ng_mean(Event const & event) const; int sample_ng(Event const & event, RNG & ran); int sample_ng_jets(Event const & event, int ng, RNG & ran); double probability_in_jet(Event const & event) const; std::vector gen_non_jet( int ng_non_jet, double ptmin, double ptmax, RNG & ran ); - void rescale_qqx_rapidities( + void rescale_qqbar_rapidities( std::vector & out_partons, std::vector const & jets, double ymin1, double ymax2, - int qqxbackjet + int qqbar_backjet ); void rescale_rapidities( std::vector & partons, double ymin, double ymax ); //! @return jets & Bosons std::pair< std::vector, optional > reshuffle( Event const & ev, fastjet::PseudoJet const & q ); /** \interal final jet test * - number of jets must match Born kinematics * - no partons designated as nonjet may end up inside jets * - all other outgoing partons *must* end up inside jets * - the extremal (in rapidity) partons must be inside the extremal jets * - rapidities must be the same (by construction) */ bool jets_ok( std::vector const & Born_jets, std::vector const & partons ) const; void reconstruct_incoming(std::array const & Born_incoming); /** \interal Distribute gluon in jet - * @param jets jets to distribute gluon in - * @param ng_jets number of gluons - * @param qqxbackjet position of first (backwards) qqx jet + * @param jets jets to distribute gluon in + * @param ng_jets number of gluons + * @param qqbar_backjet position of first (backwards) qqbar jet * * relies on JetSplitter */ std::vector split( std::vector const & jets, - int ng_jets, std::size_t qqxbackjet, RNG & ran + int ng_jets, std::size_t qqbar_backjet, RNG & ran ); std::vector distribute_jet_partons( int ng_jets, std::vector const & jets, RNG & ran ); std::vector split( std::vector const & jets, std::vector const & np_in_jet, - std::size_t qqxbackjet, + std::size_t qqbar_backjet, RNG & ran ); bool split_preserved_jets( std::vector const & jets, std::vector const & jet_partons ) const; template Particle const & most_backward_FKL( std::vector const & partons ) const; template Particle const & most_forward_FKL( std::vector const & partons ) const; template Particle & most_backward_FKL(std::vector & partons) const; template Particle & most_forward_FKL(std::vector & partons) const; bool extremal_ok( std::vector const & partons ) const; /** \internal * Assigns PDG IDs to outgoing partons, i.e. labels them as quarks * * \note This function assumes outgoing_ to be pure partonic when called, * i.e. A/W/Z/h bosons should _not be set_ at this stage */ void label_quarks(Event const & event); /** \internal - * This function will label the qqx pair in a qqx event back to their + * This function will label the qqbar pair in a qqbar event back to their * original types from the input event. */ - void label_qqx(Event const & event); + void label_qqbar(Event const & event); void boost_AWZH_boson_from( fastjet::PseudoJet const & boosted_boson, Event const & event ); bool momentum_conserved() const; bool contains_idx( fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton ) const; - bool unob_, unof_, qqxb_, qqxf_, qqxmid_; + //! @TODO replace this with an type + bool unob_, unof_, qqbarb_, qqbarf_, qqbar_mid_; double weight_; PhaseSpacePointConfig param_; std::array incoming_; std::vector outgoing_; //! \internal Particle decays in the format {outgoing index, decay products} std::unordered_map> decays_; StatusCode status_; }; //! Extract Event::EventData from PhaseSpacePoint Event::EventData to_EventData(PhaseSpacePoint psp); } // namespace HEJ diff --git a/include/HEJ/StatusCode.hh b/include/HEJ/StatusCode.hh index bfeff84..f1855ab 100644 --- a/include/HEJ/StatusCode.hh +++ b/include/HEJ/StatusCode.hh @@ -1,47 +1,47 @@ /** \file * \brief Header file for status codes of event generation * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include "HEJ/exceptions.hh" namespace HEJ { //! Possible status codes from the event generation enum StatusCode{ good, discard, empty_jets, failed_reshuffle, failed_resummation_cuts, failed_split, too_much_energy, - gluon_in_qqx, + gluon_in_qqbar, wrong_jets, unspecified // should never appear }; //! Get name of StatusCode //! @TODO better names inline std::string to_string(StatusCode s){ switch(s){ case good: return "good"; case discard: return "discard"; case empty_jets: return "empty jets"; case failed_reshuffle: return "failed reshuffle"; case failed_resummation_cuts: return "below cuts"; case failed_split: return "failed split"; case too_much_energy: return "too much energy"; - case gluon_in_qqx: return "gluon inside qqx"; + case gluon_in_qqbar: return "gluon inside qqbar"; case wrong_jets: return "wrong jets"; case unspecified: return "unspecified"; default:{} } throw std::logic_error{"unreachable"}; } } // namespace HEJ diff --git a/include/HEJ/Wjets.hh b/include/HEJ/Wjets.hh index 62217f7..5ef1517 100644 --- a/include/HEJ/Wjets.hh +++ b/include/HEJ/Wjets.hh @@ -1,509 +1,510 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ /** \file * \brief Functions computing the square of current contractions in W+Jets. * * This file contains all the W+Jet specific components to compute * the current contractions for valid HEJ processes, to form a full * W+Jets ME, currently one would have to use functions from the * jets.hh header also. We can calculate all subleading processes for * W+Jets. */ #pragma once #include #include "CLHEP/Vector/LorentzVector.h" namespace HEJ { struct ParticleProperties; namespace currents { using HLV = CLHEP::HepLorentzVector; //! Square of qQ->qenuQ W+Jets Scattering Current /** * @param p1out Momentum of final state quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param p1in Momentum of initial state quark * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qQ->qenuQ * * This returns the square of the current contractions in qQ->qenuQ scattering * with an emission of a W Boson. */ double ME_W_qQ(HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop); //! Square of qbarQ->qbarenuQ W+Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qbarQ->qbarenuQ * * This returns the square of the current contractions in qbarQ->qbarenuQ * scattering with an emission of a W Boson. */ double ME_W_qbarQ(HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop); //! Square of qQbar->qenuQbar W+Jets Scattering Current /** * @param p1out Momentum of final state quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param p1in Momentum of initial state quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qQbar->qenuQbar * * This returns the square of the current contractions in qQbar->qenuQbar * scattering with an emission of a W Boson. */ double ME_W_qQbar(HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop); //! Square of qbarQbar->qbarenuQbar W+Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param wprop Mass and width of the W boson * @returns Square of the current contractions for * qbarQbar->qbarenuQbar * * This returns the square of the current contractions in * qbarQbar->qbarenuQbar scattering with an emission of a W Boson. */ double ME_W_qbarQbar(HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop); //! Square of qg->qenug W+Jets Scattering Current /** * @param p1out Momentum of final state quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param p1in Momentum of initial state quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qg->qenug * * This returns the square of the current contractions in qg->qenug scattering * with an emission of a W Boson. */ double ME_W_qg(HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop); //! Square of qbarg->qbarenug W+Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qbarg->qbarenug * * This returns the square of the current contractions in qbarg->qbarenug * scattering with an emission of a W Boson. */ double ME_W_qbarg(HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop); //! qQg Wjets Unordered backwards opposite leg to W /** * @param p1out Momentum of final state quark a * @param p1in Momentum of initial state quark a * @param p2out Momentum of final state quark b * @param p2in Momentum of intial state quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qQ->qQg Scattering * * This returns the square of the current contractions in qQg->qQg scattering * with an emission of a W Boson. */ double ME_W_unob_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! qbarQg Wjets Unordered backwards opposite leg to W /** * @param p1out Momentum of final state anti-quark a * @param p1in Momentum of initial state anti-quark a * @param p2out Momentum of final state quark b * @param p2in Momentum of intial state quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qbarQ->qbarQg * * This returns the square of the current contractions in qbarQg->qbarQg * scattering with an emission of a W Boson. */ double ME_W_unob_qbarQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! qQbarg Wjets Unordered backwards opposite leg to W /** * @param p1out Momentum of final state quark a * @param p1in Momentum of initial state quark a * @param p2out Momentum of final state anti-quark b * @param p2in Momentum of intial state anti-quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qQbar->qQbarg * * This returns the square of the current contractions in qQbarg->qQbarg * scattering with an emission of a W Boson. */ double ME_W_unob_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! qbarQbarg Wjets Unordered backwards opposite leg to W /** * @param p1out Momentum of final state anti-quark a * @param p1in Momentum of initial state anti-quark a * @param p2out Momentum of final state anti-quark b * @param p2in Momentum of intial state anti-quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qbarQbar->qbarQbarg * * This returns the square of the current contractions in qbarQbarg->qbarQbarg * scattering with an emission of a W Boson. */ double ME_W_unob_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! W+uno same leg /** * @param p1out Momentum of final state quark a * @param p1in Momentum of initial state quark a * @param p2out Momentum of final state quark b * @param p2in Momentum of intial state quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qQ->qQg * * This returns the square of the current contractions in gqQ->gqQ scattering * with an emission of a W Boson. */ double ME_Wuno_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! W+uno same leg. quark anti-quark /** * @param p1out Momentum of final state quark a * @param p1in Momentum of initial state quark a * @param p2out Momentum of final state anti-quark b * @param p2in Momentum of intial state anti-quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qQbar->gqQbar * * This returns the square of the current contractions in gqQbar->gqQbar * scattering with an emission of a W Boson. (Unordered Same Leg) */ double ME_Wuno_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! W+uno same leg. quark gluon /** * @param p1out Momentum of final state quark a * @param p1in Momentum of initial state quark a * @param p2out Momentum of final state gluon b * @param p2in Momentum of intial state gluon b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qg->gqg * * This returns the square of the current contractions in qg->gqg scattering * with an emission of a W Boson. */ double ME_Wuno_qg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! W+uno same leg. anti-quark quark /** * @param p1out Momentum of final state anti-quark a * @param p1in Momentum of initial state anti-quark a * @param p2out Momentum of final state quark b * @param p2in Momentum of intial state quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qbarQ->gqbarQ * * This returns the square of the current contractions in qbarQ->gqbarQ * scattering with an emission of a W Boson. */ double ME_Wuno_qbarQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! W+uno same leg. anti-quark anti-quark /** * @param p1out Momentum of final state anti-quark a * @param p1in Momentum of initial state anti-quark a * @param p2out Momentum of final state anti-quark b * @param p2in Momentum of intial state anti-quark b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qbarQbar->gqbarQbar * * This returns the square of the current contractions in gqbarQbar->qbarQbar * scattering with an emission of a W Boson. */ double ME_Wuno_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); //! W+uno same leg. anti-quark gluon /** * @param p1out Momentum of final state anti-quark a * @param p1in Momentum of initial state anti-quark a * @param p2out Momentum of final state gluon b * @param p2in Momentum of intial state gluon b * @param pg Momentum of final state unordered gluon * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param wprop Mass and width of the W boson * @returns Square of the current contractions for ->gqbarg * * This returns the square of the current contractions in qbarg->gqbarg * scattering with an emission of a W Boson. */ double ME_Wuno_qbarg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop); - //! W+Extremal qqx. qxqQ + //! W+Extremal qqbar. qbarq+Q /** * @param pgin Momentum of initial state gluon * @param pqout Momentum of final state quark a * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param pqbarout Momentum of final state anti-quark a * @param p2out Momentum of initial state anti-quark b * @param p2in Momentum of final state gluon b * @param wprop Mass and width of the W boson * @returns Square of the current contractions for ->qbarqQ * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated through the use of crossing symmetry. */ - double ME_WExqqx_qbarqQ(HLV const & pgin, HLV const & pqout, - HLV const & plbar, HLV const & pl, - HLV const & pqbarout, HLV const & p2out, - HLV const & p2in, - ParticleProperties const & wprop); + double ME_WExqqbar_qbarqQ(HLV const & pgin, HLV const & pqout, + HLV const & plbar, HLV const & pl, + HLV const & pqbarout, HLV const & p2out, + HLV const & p2in, + ParticleProperties const & wprop); - //! W+Extremal qqx. qqxQ + //! W+Extremal qqbar. qqbar+Q /** * @param pgin Momentum of initial state gluon * @param pqbarout Momentum of final state anti-quark a * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param pqout Momentum of final state quark a * @param p2out Momentum of initial state anti-quark b * @param p2in Momentum of final state gluon b * @param wprop Mass and width of the W boson * @returns Square of the current contractions for ->qqbarQ * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated through the use of crossing symmetry. */ - double ME_WExqqx_qqbarQ(HLV const & pgin, HLV const & pqbarout, - HLV const & plbar, HLV const & pl, - HLV const & pqout, HLV const & p2out, - HLV const & p2in, - ParticleProperties const & wprop); + double ME_WExqqbar_qqbarQ(HLV const & pgin, HLV const & pqbarout, + HLV const & plbar, HLV const & pl, + HLV const & pqout, HLV const & p2out, + HLV const & p2in, + ParticleProperties const & wprop); - //! W+Extremal qqx. gg->qxqg + //! W+Extremal qqbar. gg->qbarq+g /** * @param pgin Momentum of initial state gluon * @param pqout Momentum of final state quark a * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param pqbarout Momentum of final state anti-quark a * @param p2out Momentum of initial state gluon b * @param p2in Momentum of final state gluon b * @param wprop Mass and width of the W boson * @returns Square of the current contractions for gg->qbarqg * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated through the use of crossing symmetry. */ - double ME_WExqqx_qbarqg(HLV const & pgin, HLV const & pqout, - HLV const & plbar, HLV const & pl, - HLV const & pqbarout, HLV const & p2out, - HLV const & p2in, - ParticleProperties const & wprop); + double ME_WExqqbar_qbarqg(HLV const & pgin, HLV const & pqout, + HLV const & plbar, HLV const & pl, + HLV const & pqbarout, HLV const & p2out, + HLV const & p2in, + ParticleProperties const & wprop); - //! W+Extremal qqx. gg->qqxg + //! W+Extremal qqbar. gg->qqbar+g /** * @param pgin Momentum of initial state gluon * @param pqout Momentum of final state quark a * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton * @param pqbarout Momentum of final state anti-quark a * @param p2out Momentum of initial state gluon a * @param p2in Momentum of final state gluon b * @param wprop Mass and width of the W boson * @returns Square of the current contractions for gg->qqbarg * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated through the use of crossing symmetry. */ - double ME_WExqqx_qqbarg(HLV const & pgin, HLV const & pqbarout, - HLV const & plbar, HLV const & pl, - HLV const & pqout, HLV const & p2out, - HLV const & p2in, - ParticleProperties const & wprop); + double ME_WExqqbar_qqbarg(HLV const & pgin, HLV const & pqbarout, + HLV const & plbar, HLV const & pl, + HLV const & pqout, HLV const & p2out, + HLV const & p2in, + ParticleProperties const & wprop); - //! W+Extremal qqx. gg->qqxg. qqx on forwards leg, W emission backwards leg. + //! W+Extremal qqbar. gg->qqbarg. qqbar on forwards leg, W emission backwards leg. /** * @param pa Momentum of initial state (anti-)quark * @param pb Momentum of initial state gluon * @param p1 Momentum of final state (anti-)quark (after W emission) * @param p2 Momentum of final state anti-quark * @param p3 Momentum of final state quark * @param plbar Momentum of final state anti-lepton * @param pl Momentum of final state lepton - * @param aqlinepa Is opposite extremal leg to qqx a quark or antiquark line + * @param aqlinepa Is opposite extremal leg to qqbar a quark or antiquark line * @param wprop Mass and width of the W boson * @returns Square of the current contractions for gq->qqbarqW * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated via current contraction of existing - * currents. Assumes qqx split from forwards leg, W emission from backwards - * leg. Switch input (pa<->pb, p1<->pn) if calculating forwards qqx. + * currents. Assumes qqbar split from forwards leg, W emission from backwards + * leg. Switch input (pa<->pb, p1<->pn) if calculating forwards qqbar. */ - double ME_W_Exqqx_QQq(HLV const & pa, HLV const & pb, HLV const & p1, - HLV const & p2, HLV const & p3, - HLV const & plbar, HLV const & pl, bool aqlinepa, - ParticleProperties const & wprop); + double ME_W_Exqqbar_QQq(HLV const & pa, HLV const & pb, HLV const & p1, + HLV const & p2, HLV const & p3, + HLV const & plbar, HLV const & pl, bool aqlinepa, + ParticleProperties const & wprop); - //! W+Jets qqxCentral. qqx W emission. + //! W+Jets qqbarCentral. qqbar W emission. /** * @param pa Momentum of initial state particle a * @param pb Momentum of initial state particle b * @param pl Momentum of final state lepton * @param plbar Momentum of final state anti-lepton * @param partons Vector of outgoing parton momenta * @param aqlinepa True= pa is anti-quark * @param aqlinepb True= pb is anti-quark - * @param qqxmarker Ordering of the qqbar pair produced (qqx vs qxq) + * @param qqbar_marker Ordering of the qqbar pair produced (q-qbar vs qbar-q) * @param nabove Number of lipatov vertices "above" qqbar pair * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qq>qQQbarWq * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated through the use of crossing symmetry. */ - double ME_WCenqqx_qq(HLV const & pa, HLV const & pb, - HLV const & pl, HLV const & plbar, - std::vector const & partons, - bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove, - ParticleProperties const & wprop); + double ME_WCenqqbar_qq(HLV const & pa, HLV const & pb, + HLV const & pl, HLV const & plbar, + std::vector const & partons, + bool aqlinepa, bool aqlinepb, bool qqbar_marker, + int nabove, + ParticleProperties const & wprop); - //! W+Jets qqxCentral. W emission from backwards leg. + //! W+Jets qqbarCentral. W emission from backwards leg. /** * @param pa Momentum of initial state particle a * @param pb Momentum of initial state particle b * @param pl Momentum of final state lepton * @param plbar Momentum of final state anti-lepton * @param partons outgoing parton momenta * @param aqlinepa True= pa is anti-quark * @param aqlinepb True= pb is anti-quark - * @param qqxmarker Ordering of the qqbar pair produced (qqx vs qxq) + * @param qqbar_marker Ordering of the qqbar pair produced (q-qbar vs qbar-q) * @param nabove Number of lipatov vertices "above" qqbar pair * @param nbelow Number of lipatov vertices "below" qqbar pair * @param forwards Swap to emission off front leg * TODO: remove so args can be const * @param wprop Mass and width of the W boson * @returns Square of the current contractions for qq>qQQbarWq * * Calculates the square of the current contractions with extremal qqbar pair * production. This is calculated through the use of crossing symmetry. */ - double ME_W_Cenqqx_qq(HLV pa, HLV pb, - HLV pl, HLV plbar, - std::vector partons, - bool aqlinepa, bool aqlinepb, bool qqxmarker, - int nabove, int nbelow, bool forwards, - ParticleProperties const & wprop); + double ME_W_Cenqqbar_qq(HLV pa, HLV pb, + HLV pl, HLV plbar, + std::vector partons, + bool aqlinepa, bool aqlinepb, bool qqbar_marker, + int nabove, int nbelow, bool forwards, + ParticleProperties const & wprop); } // namespace currents } // namespace HEJ diff --git a/include/HEJ/event_types.hh b/include/HEJ/event_types.hh index b53ba36..c354b3d 100644 --- a/include/HEJ/event_types.hh +++ b/include/HEJ/event_types.hh @@ -1,109 +1,109 @@ /** \file * \brief Define different types of events. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include "HEJ/exceptions.hh" namespace HEJ { //! Namespace for event types namespace event_type { //! Possible event types enum EventType: std::size_t { non_resummable = 0, //!< event configuration not covered by All Order resummation bad_final_state = 1, //!< event with an unsupported final state no_2_jets = 2, //!< event with less than two jets FKL = 4, //!< FKL-type event unordered_backward = 8, //!< event with unordered backward emission unordered_forward = 16, //!< event with unordered forward emission - extremal_qqxb = 32, //!< event with a backward extremal qqbar - extremal_qqxf = 64, //!< event with a forward extremal qqbar - central_qqx = 128, //!< event with a central qqbar - unob = unordered_backward, //!< alias for unordered_backward - unof = unordered_forward, //!< alias for unordered_forward - qqxexb = extremal_qqxb, //!< alias for extremal_qqxb - qqxexf = extremal_qqxf, //!< alias for extremal_qqxf - qqxmid = central_qqx, //!< alias for central_qqx - first_type = non_resummable, //!< alias for non_resummable - last_type = central_qqx //!< alias for central_qqx + extremal_qqbar_backward = 32, //!< event with a backward extremal qqbar + extremal_qqbar_forward = 64, //!< event with a forward extremal qqbar + central_qqbar = 128, //!< event with a central qqbar + unob = unordered_backward, //!< alias for unordered_backward + unof = unordered_forward, //!< alias for unordered_forward + qqbar_exb = extremal_qqbar_backward, //!< alias for extremal_qqbar_backward + qqbar_exf = extremal_qqbar_forward, //!< alias for extremal_qqbar_forward + qqbar_mid = central_qqbar, //!< alias for central_qqbar + first_type = non_resummable, //!< alias for non_resummable + last_type = central_qqbar //!< alias for central_qqbar }; //! Event type names /** * For example, name(FKL) is the string "FKL" */ inline std::string name(EventType type) { switch(type) { case FKL: return "FKL"; case unordered_backward: return "unordered backward"; case unordered_forward: return "unordered forward"; - case extremal_qqxb: + case extremal_qqbar_backward: return "extremal qqbar backward"; - case extremal_qqxf: + case extremal_qqbar_forward: return "extremal qqbar forward"; - case central_qqx: + case central_qqbar: return "central qqbar"; case non_resummable: return "non-resummable"; case no_2_jets: return "no 2 jets"; case bad_final_state: return "bad final state"; default: throw std::logic_error{"Unreachable"}; } } //! Returns True for a HEJ \ref event_type::EventType "EventType" inline constexpr bool is_resummable(EventType type) { switch(type) { case FKL: case unordered_backward: case unordered_forward: - case extremal_qqxb: - case extremal_qqxf: - case central_qqx: + case extremal_qqbar_backward: + case extremal_qqbar_forward: + case central_qqbar: return true; default: return false; } } //! Returns True for an unordered \ref event_type::EventType "EventType" inline constexpr bool is_uno(EventType type) { return type == unordered_backward || type == unordered_forward; } - //! Returns True for an extremal_qqx \ref event_type::EventType "EventType" + //! Returns True for an extremal_qqbar \ref event_type::EventType "EventType" inline - constexpr bool is_ex_qqx(EventType type) { - return type == extremal_qqxb || type == extremal_qqxf; + constexpr bool is_ex_qqbar(EventType type) { + return type == extremal_qqbar_backward || type == extremal_qqbar_forward; } - //! Returns True for an central_qqx \ref event_type::EventType "EventType" + //! Returns True for an central_qqbar \ref event_type::EventType "EventType" inline - constexpr bool is_mid_qqx(EventType type) { - return type == central_qqx; + constexpr bool is_mid_qqbar(EventType type) { + return type == central_qqbar; } - //! Returns True for any qqx event \ref event_type::EventType "EventType" + //! Returns True for any qqbar event \ref event_type::EventType "EventType" inline - constexpr bool is_qqx(EventType type) { - return is_ex_qqx(type) || is_mid_qqx(type); + constexpr bool is_qqbar(EventType type) { + return is_ex_qqbar(type) || is_mid_qqbar(type); } } // namespace event_type } // namespace HEJ diff --git a/include/HEJ/jets.hh b/include/HEJ/jets.hh index 08c1f22..d7012ec 100644 --- a/include/HEJ/jets.hh +++ b/include/HEJ/jets.hh @@ -1,283 +1,283 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ /** \file * \brief Functions computing the square of current contractions in pure jets. * * This file contains all the necessary functions to compute the * current contractions for all valid pure jet HEJ processes, which * so far is FKL and unordered processes. It will also contain some * pure jet ME components used in other process ME calculations * * @TODO add a namespace */ #pragma once #include #include #include #include #include #include "CLHEP/Vector/LorentzVector.h" namespace HEJ { namespace currents { using COM = std::complex; using current = std::array; using HLV = CLHEP::HepLorentzVector; //! Square of qQ->qQ Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qQ->qQ Scattering */ double ME_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qQbar->qQbar Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qQbar->qQbar * * @note this can be used for qbarQ->qbarQ Scattering by inputting arguments * appropriately. */ double ME_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qbarQbar->qbarQbar Pure Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qbarQbar->qbarQbar */ double ME_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qg->qg Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @returns Square of the current contractions for qg->qg * * @note this can be used for gq->gq Scattering by inputting arguments * appropriately. */ double ME_qg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qbarg->qbarg Pure Jets Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @returns Square of the current contractions for qbarg->qbarg * * @note this can be used for gqbar->gqbar Scattering by inputting arguments * appropriately. */ double ME_qbarg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of gg->gg Pure Jets Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param p2out Momentum of final state gluon * @param p2in Momentum of intial state gluon * @returns Square of the current contractions for gg->gg */ double ME_gg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); // Unordered Backwards contributions: //! Square of qQ->qQ Pure Jets Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qQ->qQ */ double ME_unob_qQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qbarQ->qbarQ Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qbarQ->qbarQ * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qbarQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qQbar->qQbar Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qQbar->qQbar * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qbarQbar->qbarQbar Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark * @param pg Momentum of unordered gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qbarQbar->qbarQbar * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qbarQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qg->qg Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param pg Momentum of unordered gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @returns Square of the current contractions for qg->qg * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qg(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); //! Square of qbarg->qbarg Pure Jets Unordered backwards Scattering Current /** * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon * @param pg Momentum of unordered gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @returns Square of the current contractions for qbarg->qbarg * * @note this can be used for unof contributions by inputting * arguments appropriately. */ double ME_unob_qbarg(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in); - //! Square of gQ->qbarqQ Pure Jets Extremal qqx backwards Scattering Current + //! Square of gQ->qbarqQ Pure Jets Extremal qqbar backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg * @returns Square of the current contractions for gQ->qbarqg * - * @note this can be used for Exqqxf contributions by inputting + * @note this can be used for Exqqbarf contributions by inputting * arguments appropriately. */ - double ME_Exqqx_qbarqQ(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, - HLV const & p2in); + double ME_Exqqbar_qbarqQ(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, + HLV const & p2in); - //! Square of gQ->qqbarQ Pure Jets Extremal qqx backwards Scattering Current + //! Square of gQ->qqbarQ Pure Jets Extremal qqbar backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg - * @returns Square of the current contractions for gQ->qqbarg + * @returns Square of the current contractions for gQ->qqbar+g * - * @note this can be used for Exqqxf contributions by inputting + * @note this can be used for Exqqbarf contributions by inputting * arguments appropriately. */ - double ME_Exqqx_qqbarQ(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, - HLV const & p2in); + double ME_Exqqbar_qqbarQ(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, + HLV const & p2in); - //! Square of gg->qbarqg Pure Jets Extremal qqx backwards Scattering Current + //! Square of gg->qbarqg Pure Jets Extremal qqbar backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg - * @returns Square of the current contractions for gg->qbarqg + * @returns Square of the current contractions for gg->qbarq+g * - * @note this can be used for Exqqxf contributions by inputting + * @note this can be used for Exqqbarf contributions by inputting * arguments appropriately. */ - double ME_Exqqx_qbarqg(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, - HLV const & p2in); + double ME_Exqqbar_qbarqg(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, + HLV const & p2in); - //! Square of gg->qqbarg Pure Jets Extremal qqx backwards Scattering Current + //! Square of gg->qqbarg Pure Jets Extremal qqbar backwards Scattering Current /** * @param pgin Momentum of incoming gluon * @param pqout Momentum of Quark from split * @param pqbarout Momentum of Anti-quark from split * @param p2out Momentum of Outgoing forwards leg * @param p2in Momentum of Incoming forwards leg - * @returns Square of the current contractions for gg->qqbarg + * @returns Square of the current contractions for gg->qqbar+g * - * @note this can be used for Exqqxf contributions by inputting + * @note this can be used for Exqqbarf contributions by inputting * arguments appropriately. */ - double ME_Exqqx_qqbarg(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, - HLV const & p2in); + double ME_Exqqbar_qqbarg(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, + HLV const & p2in); - //! Square of qq->qQQbarq Pure Jets Central qqx Scattering Current + //! Square of qq->qQQbarq Pure Jets Central qqbar Scattering Current /** * @param ka Momentum of incoming leg a * @param kb Momentum of incoming leg b * @param partons vector of outgoing partons * @param aqlinepa Is leg a an anti-quark? * @param aqlinepb Is leg b an anti-quark? - * @param qqxmarker Is anti-quark further back in rapidity than quark - * (qqx pair) - * @param nabove Number of gluons emitted above qqx pair (back in rap) - * @returns Square of the current contractions for qq->qQQxq + * @param qqbarmarker Is anti-quark further back in rapidity than quark + * (qqbar pair) + * @param nabove Number of gluons emitted above qqbar pair (back in rap) + * @returns Square of the current contractions for qq->q+QQbar+q */ - double ME_Cenqqx_qq( + double ME_Cenqqbar_qq( HLV const & pa, HLV const & pb, std::vector const & partons, bool aqlinepa, bool aqlinepb, - bool qqxmarker, std::size_t nabove + bool qqbarmarker, std::size_t nabove ); //! @TODO These are not currents and should be moved elsewhere. double K_g(double p1minus, double paminus); double K_g(HLV const & pout, HLV const & pin); } // namespace currents } // namespace HEJ diff --git a/src/Event.cc b/src/Event.cc index cac0fa1..5404b8d 100644 --- a/src/Event.cc +++ b/src/Event.cc @@ -1,1175 +1,1175 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/Event.hh" #include #include #include #include #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "fastjet/JetDefinition.hh" #include "fastjet/PseudoJet.hh" #include "LHEF/LHEF.h" #include "HEJ/Constants.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/RNG.hh" #include "HEJ/exceptions.hh" #include "HEJ/optional.hh" namespace HEJ { namespace { using std::size_t; //! LHE status codes namespace lhe_status { enum Status: int { in = -1, decay = 2, out = 1, }; } using LHE_Status = lhe_status::Status; //! true if leptonic W decay bool valid_W_decay( int const w_type, // sign of W std::vector const & decays ){ if(decays.size() != 2) // no 1->2 decay return false; const int pidsum = decays[0].type + decays[1].type; if( std::abs(pidsum) != 1 || pidsum != w_type ) // correct charge return false; // leptonic decay (only check first, second follows from pidsum) if( w_type == 1 ) // W+ return is_antilepton(decays[0]) || is_neutrino(decays[0]); // W- return is_lepton(decays[0]) || is_antineutrino(decays[0]); } //! true for Z decay to charged leptons bool valid_Z_decay(std::vector const & decays){ if(decays.size() != 2) // no 1->2 decay return false; const int pidsum = decays[0].type + decays[1].type; if( std::abs(pidsum) != 0 ) // correct charge return false; // leptonic decay (only check first, second follows from pidsum) return is_anylepton(decays[0]) && !is_anyneutrino(decays[0]); } /// @name helper functions to determine event type //@{ /** * \brief check if final state valid for HEJ * * check if there is at most one photon, W, H, Z in the final state * and all the rest are quarks or gluons */ bool final_state_ok(Event const & ev){ std::vector const & outgoing = ev.outgoing(); if(ev.decays().size() > 1) // at most one decay return false; bool has_AWZH_boson = false; for( size_t i=0; ifirst != i ) return false; if( !valid_W_decay(out.type>0?+1:-1, ev.decays().cbegin()->second) ) return false; } // valid decay for Z if(out.type == ParticleID::Z_photon_mix){ // exactly 1 decay if( ev.decays().size() != 1 || ev.decays().cbegin()->first != i ) return false; if( !valid_Z_decay(ev.decays().cbegin()->second) ) return false; } } else if(! is_parton(out.type)) return false; } return true; } /** * returns all EventTypes implemented in HEJ */ size_t implemented_types(std::vector const & bosons){ using namespace event_type; - if(bosons.empty()) return FKL | unob | unof | qqxexb | qqxexf | qqxmid; + if(bosons.empty()) return FKL | unob | unof | qqbar_exb | qqbar_exf | qqbar_mid; if(bosons.size()>1) return non_resummable; // multi boson switch (bosons[0].type) { case ParticleID::Wp: case ParticleID::Wm: - return FKL | unob | unof | qqxexb | qqxexf | qqxmid; + return FKL | unob | unof | qqbar_exb | qqbar_exf | qqbar_mid; case ParticleID::Z_photon_mix: return FKL | unob | unof; case ParticleID::h: return FKL | unob | unof; default: return non_resummable; } } /** * \brief function which determines if type change is consistent with Wp emission. * @param in incoming Particle id * @param out outgoing Particle id - * @param qqx Current both incoming/both outgoing? + * @param is_qqbar Current both incoming/both outgoing? * * \see is_Wm_Change */ - bool is_Wp_Change(ParticleID in, ParticleID out, bool qqx){ + bool is_Wp_Change(ParticleID in, ParticleID out, bool is_qqbar){ using namespace pid; - if(!qqx && (in==d_bar || in==u || in==s_bar || in==c)) + if(!is_qqbar && (in==d_bar || in==u || in==s_bar || in==c)) return out == (in-1); - if( qqx && (in==d || in==u_bar || in==s || in==c_bar)) + if( is_qqbar && (in==d || in==u_bar || in==s || in==c_bar)) return out == -(in+1); return false; } /** * \brief function which determines if type change is consistent with Wm emission. * @param in incoming Particle id * @param out outgoing Particle id - * @param qqx Current both incoming/both outgoing? + * @param is_qqbar Current both incoming/both outgoing? * * Ensures that change type of quark line is possible by a flavour changing - * Wm emission. Allows checking of qqx currents also. + * Wm emission. Allows checking of is_qqbar currents also. */ - bool is_Wm_Change(ParticleID in, ParticleID out, bool qqx){ + bool is_Wm_Change(ParticleID in, ParticleID out, bool is_qqbar){ using namespace pid; - if(!qqx && (in==d || in==u_bar || in==s || in==c_bar)) + if(!is_qqbar && (in==d || in==u_bar || in==s || in==c_bar)) return out == (in+1); - if( qqx && (in==d_bar || in==u || in==s_bar || in==c)) + if( is_qqbar && (in==d_bar || in==u || in==s_bar || in==c)) return out == -(in-1); return false; } /** * \brief checks if particle type remains same from incoming to outgoing * @param in incoming Particle * @param out outgoing Particle - * @param qqx Current both incoming/outgoing? + * @param is_qqbar Current both incoming/outgoing? */ - bool no_flavour_change(ParticleID in, ParticleID out, bool qqx){ - const int qqxCurrent = qqx?-1:1; + bool no_flavour_change(ParticleID in, ParticleID out, bool is_qqbar){ + const int qqbarCurrent = is_qqbar?-1:1; if(std::abs(in)<=pid::top || in==pid::gluon) - return (in==out*qqxCurrent); + return (in==out*qqbarCurrent); return false; } bool has_2_jets(Event const & event){ return event.jets().size() >= 2; } /** * \brief check if we have a valid Impact factor * @param in incoming Particle * @param out outgoing Particle - * @param qqx Current both incoming/outgoing? + * @param is_qqbar Current both incoming/outgoing? * @param W_change returns +1 if Wp, -1 if Wm, else 0 */ bool is_valid_impact_factor( - ParticleID in, ParticleID out, bool qqx, int & W_change + ParticleID in, ParticleID out, bool is_qqbar, int & W_change ){ - if( no_flavour_change(in, out, qqx) ){ + if( no_flavour_change(in, out, is_qqbar) ){ return true; } - if( is_Wp_Change(in, out, qqx) ) { + if( is_Wp_Change(in, out, is_qqbar) ) { W_change+=1; return true; } - if( is_Wm_Change(in, out, qqx) ) { + if( is_Wm_Change(in, out, is_qqbar) ) { W_change-=1; return true; } return false; } //! Returns all possible classifications from the impact factors // the beginning points are changed s.t. after the the classification they // point to the beginning of the (potential) FKL chain // sets W_change: + if Wp change // 0 if no change // - if Wm change // This function can be used with forward & backwards iterators template size_t possible_impact_factors( ParticleID incoming_id, // incoming OutIterator & begin_out, OutIterator const & end_out, // outgoing int & W_change, std::vector const & boson, bool const backward // backward? ){ using namespace event_type; assert(boson.size() < 2); // keep track of all states that we don't test - size_t not_tested = qqxmid; + size_t not_tested = qqbar_mid; if(backward) - not_tested |= unof | qqxexf; + not_tested |= unof | qqbar_exf; else - not_tested |= unob | qqxexb; + not_tested |= unob | qqbar_exb; // Is this LL current? if( is_valid_impact_factor(incoming_id, begin_out->type, false, W_change) ){ ++begin_out; return not_tested | FKL; } // or NLL current? // -> needs two partons in two different jets if( std::distance(begin_out, end_out)>=2 ){ auto next = std::next(begin_out); // Is this unordered emisson? if( incoming_id!=pid::gluon && begin_out->type==pid::gluon ){ if( is_valid_impact_factor( incoming_id, next->type, false, W_change ) ){ // veto Higgs inside uno assert(next!=end_out); if( !boson.empty() && boson.front().type == ParticleID::h ){ if( (backward && boson.front().rapidity() < next->rapidity()) ||(!backward && boson.front().rapidity() > next->rapidity())) return non_resummable; } begin_out = std::next(next); return not_tested | (backward?unob:unof); } } // Is this QQbar? else if( incoming_id==pid::gluon ){ if( is_valid_impact_factor( begin_out->type, next->type, true, W_change ) ){ - // veto Higgs inside qqx + // veto Higgs inside qqbar assert(next!=end_out); if( !boson.empty() && boson.front().type == ParticleID::h ){ if( (backward && boson.front().rapidity() < next->rapidity()) ||(!backward && boson.front().rapidity() > next->rapidity())) return non_resummable; } begin_out = std::next(next); - return not_tested | (backward?qqxexb:qqxexf); + return not_tested | (backward?qqbar_exb:qqbar_exf); } } } return non_resummable; } //! Returns all possible classifications from central emissions // the beginning points are changed s.t. after the the classification they // point to the end of the emission chain // sets W_change: + if Wp change // 0 if no change // - if Wm change template size_t possible_central( OutIterator & begin_out, OutIterator const & end_out, int & W_change, std::vector const & boson ){ using namespace event_type; assert(boson.size() < 2); // if we already passed the central chain, // then it is not a valid all-order state if(std::distance(begin_out, end_out) < 0) return non_resummable; // keep track of all states that we don't test size_t possible = unob | unof - | qqxexb | qqxexf; + | qqbar_exb | qqbar_exf; // Find the first non-gluon/non-FKL while( (begin_out->type==pid::gluon) && (begin_out!=end_out) ){ ++begin_out; } // end of chain -> FKL if( begin_out==end_out ){ return possible | FKL; } // is this a qqbar-pair? // needs two partons in two separate jets auto next = std::next(begin_out); if( is_valid_impact_factor( begin_out->type, next->type, true, W_change ) ){ - // veto Higgs inside qqx + // veto Higgs inside qqbar if( !boson.empty() && boson.front().type == ParticleID::h && boson.front().rapidity() > begin_out->rapidity() && boson.front().rapidity() < next->rapidity() ){ return non_resummable; } begin_out = std::next(next); // remaining chain should be pure gluon/FKL for(; begin_out!=end_out; ++begin_out){ if(begin_out->type != pid::gluon) return non_resummable; } - return possible | qqxmid; + return possible | qqbar_mid; } return non_resummable; } /** * \brief Checks for all event types * @param ev Event * @returns Event Type * */ event_type::EventType classify(Event const & ev){ using namespace event_type; if(! has_2_jets(ev)) return no_2_jets; // currently we can't handle multiple boson states in the ME. So they are // considered "bad_final_state" even though the "classify" could work with // them. if(! final_state_ok(ev)) return bad_final_state; // initialise variables auto const & in = ev.incoming(); // range for current checks auto begin_out{ev.cbegin_partons()}; auto end_out{ev.crbegin_partons()}; assert(std::distance(begin(in), end(in)) == 2); assert(std::distance(begin_out, end_out.base()) >= 2); assert(std::is_sorted(begin_out, end_out.base(), rapidity_less{})); auto const boson{ filter_AWZH_bosons(ev.outgoing()) }; // we only allow one boson through final_state_ok assert(boson.size()<=1); // keep track of potential W couplings, at the end the sum should be 0 int remaining_Wp = 0; int remaining_Wm = 0; if(!boson.empty() && std::abs(boson.front().type) == ParticleID::Wp ){ if(boson.front().type>0) ++remaining_Wp; else ++remaining_Wm; } int W_change = 0; size_t final_type = ~(no_2_jets | bad_final_state); // check forward impact factor final_type &= possible_impact_factors( in.front().type, begin_out, end_out.base(), W_change, boson, true ); if( final_type == non_resummable ) return non_resummable; if(W_change>0) remaining_Wp-=W_change; else if(W_change<0) remaining_Wm+=W_change; W_change = 0; // check backward impact factor final_type &= possible_impact_factors( in.back().type, end_out, std::make_reverse_iterator(begin_out), W_change, boson, false ); if( final_type == non_resummable ) return non_resummable; if(W_change>0) remaining_Wp-=W_change; else if(W_change<0) remaining_Wm+=W_change; W_change = 0; // check central emissions final_type &= possible_central( begin_out, end_out.base(), W_change, boson ); if( final_type == non_resummable ) return non_resummable; if(W_change>0) remaining_Wp-=W_change; else if(W_change<0) remaining_Wm+=W_change; // Check whether the right number of Ws are present if( remaining_Wp != 0 || remaining_Wm != 0 ) return non_resummable; // result has to be unique if( (final_type & (final_type-1)) != 0) return non_resummable; // check that each sub processes is implemented // (has to be done at the end) if( (final_type & ~implemented_types(boson)) != 0 ) return non_resummable; return static_cast(final_type); } //@} Particle extract_particle(LHEF::HEPEUP const & hepeup, size_t i){ auto id = static_cast(hepeup.IDUP[i]); auto colour = is_parton(id)?hepeup.ICOLUP[i]:optional(); return { id, { hepeup.PUP[i][0], hepeup.PUP[i][1], hepeup.PUP[i][2], hepeup.PUP[i][3] }, colour }; } bool is_decay_product(std::pair const & mothers){ if(mothers.first == 0) return false; return mothers.second == 0 || mothers.first == mothers.second; } } // namespace Event::EventData::EventData(LHEF::HEPEUP const & hepeup){ parameters.central = EventParameters{ hepeup.scales.mur, hepeup.scales.muf, hepeup.XWGTUP }; size_t in_idx = 0; for (int i = 0; i < hepeup.NUP; ++i) { // skip decay products // we will add them later on, but we have to ensure that // the decayed particle is added before if(is_decay_product(hepeup.MOTHUP[i])) continue; auto particle = extract_particle(hepeup, i); // needed to identify mother particles for decay products particle.p.set_user_index(i+1); if(hepeup.ISTUP[i] == LHE_Status::in){ if(in_idx > incoming.size()) { throw std::invalid_argument{ "Event has too many incoming particles" }; } incoming[in_idx++] = std::move(particle); } else outgoing.emplace_back(std::move(particle)); } // add decay products for (int i = 0; i < hepeup.NUP; ++i) { if(!is_decay_product(hepeup.MOTHUP[i])) continue; const int mother_id = hepeup.MOTHUP[i].first; const auto mother = std::find_if( begin(outgoing), end(outgoing), [mother_id](Particle const & particle){ return particle.p.user_index() == mother_id; } ); if(mother == end(outgoing)){ throw std::invalid_argument{"invalid decay product parent"}; } const int mother_idx = std::distance(begin(outgoing), mother); assert(mother_idx >= 0); decays[mother_idx].emplace_back(extract_particle(hepeup, i)); } } Event::Event( UnclusteredEvent const & ev, fastjet::JetDefinition const & jet_def, double const min_jet_pt ): Event( Event::EventData{ ev.incoming, ev.outgoing, ev.decays, Parameters{ev.central, ev.variations} }.cluster(jet_def, min_jet_pt) ) {} //! @TODO remove in HEJ 2.2.0 UnclusteredEvent::UnclusteredEvent(LHEF::HEPEUP const & hepeup){ Event::EventData const evData{hepeup}; incoming = evData.incoming; outgoing = evData.outgoing; decays = evData.decays; central = evData.parameters.central; variations = evData.parameters.variations; } void Event::EventData::sort(){ // sort particles std::sort( begin(incoming), end(incoming), [](Particle const & o1, Particle const & o2){return o1.p.pz() idx(old_outgoing.size()); std::iota(idx.begin(), idx.end(), 0); std::sort(idx.begin(), idx.end(), [&old_outgoing](size_t i, size_t j){ return old_outgoing[i].rapidity() < old_outgoing[j].rapidity(); }); outgoing.clear(); outgoing.reserve(old_outgoing.size()); for(size_t i: idx) { outgoing.emplace_back(std::move(old_outgoing[i])); } // find decays again if(!decays.empty()){ auto old_decays = std::move(decays); decays.clear(); for(size_t i=0; isecond)); } assert(old_decays.size() == decays.size()); } } namespace { Particle reconstruct_boson(std::vector const & leptons) { Particle decayed_boson; decayed_boson.p = leptons[0].p + leptons[1].p; const int pidsum = leptons[0].type + leptons[1].type; if(pidsum == +1) { assert(is_antilepton(leptons[0])); if(is_antineutrino(leptons[0])) { throw not_implemented{"lepton-flavour violating final state"}; } assert(is_neutrino(leptons[1])); // charged antilepton + neutrino means we had a W+ decayed_boson.type = pid::Wp; } else if(pidsum == -1) { assert(is_antilepton(leptons[0])); if(is_neutrino(leptons[1])) { throw not_implemented{"lepton-flavour violating final state"}; } assert(is_antineutrino(leptons[0])); // charged lepton + antineutrino means we had a W- decayed_boson.type = pid::Wm; } else if(pidsum == 0) { assert(is_anylepton(leptons[0])); if(is_anyneutrino(leptons[0])) { throw not_implemented{"final state with two neutrinos"}; } // charged lepton-antilepton pair means we had a Z/photon decayed_boson.type = pid::Z_photon_mix; } else { throw not_implemented{ "final state with leptons " + name(leptons[0].type) + " and " + name(leptons[1].type) }; } return decayed_boson; } } // namespace void Event::EventData::reconstruct_intermediate() { const auto begin_leptons = std::partition( begin(outgoing), end(outgoing), [](Particle const & p) {return !is_anylepton(p);} ); // We can only reconstruct FS with 2 leptons if(std::distance(begin_leptons, end(outgoing)) != 2) return; std::vector leptons(begin_leptons, end(outgoing)); std::sort( begin(leptons), end(leptons), [](Particle const & p0, Particle const & p1) { assert(is_anylepton(p0) && is_anylepton(p1)); return p0.type < p1.type; } ); // `reconstruct_boson` can throw, it should therefore be called before // changing `outgoing` to allow the user to recover the original EventData auto boson = reconstruct_boson(leptons); outgoing.erase(begin_leptons, end(outgoing)); outgoing.emplace_back(std::move(boson)); decays.emplace(outgoing.size()-1, std::move(leptons)); } Event Event::EventData::cluster( fastjet::JetDefinition const & jet_def, double const min_jet_pt ){ sort(); return Event{ std::move(incoming), std::move(outgoing), std::move(decays), std::move(parameters), jet_def, min_jet_pt }; } Event::Event( std::array && incoming, std::vector && outgoing, std::unordered_map> && decays, Parameters && parameters, fastjet::JetDefinition const & jet_def, double const min_jet_pt ): incoming_{std::move(incoming)}, outgoing_{std::move(outgoing)}, decays_{std::move(decays)}, parameters_{std::move(parameters)}, cs_{ to_PseudoJet( filter_partons(outgoing_) ), jet_def }, min_jet_pt_{min_jet_pt} { jets_ = sorted_by_rapidity(cs_.inclusive_jets(min_jet_pt_)); assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{})); type_ = classify(*this); } namespace { //! check that Particles have a reasonable colour bool correct_colour(Particle const & part){ ParticleID id{ part.type }; if(!is_parton(id)) return !part.colour; if(!part.colour) return false; Colour const & col{ *part.colour }; if(is_quark(id)) return col.first != 0 && col.second == 0; if(is_antiquark(id)) return col.first == 0 && col.second != 0; assert(id==ParticleID::gluon); return col.first != 0 && col.second != 0 && col.first != col.second; } //! Connect parton to t-channel colour line & update the line //! returns false if connection not possible template bool try_connect_t(OutIterator const & it_part, Colour & line_colour){ if( line_colour.first == it_part->colour->second ){ line_colour.first = it_part->colour->first; return true; } if( line_colour.second == it_part->colour->first ){ line_colour.second = it_part->colour->second; return true; } return false; } //! Connect parton to u-channel colour line & update the line //! returns false if connection not possible template bool try_connect_u(OutIterator & it_part, Colour & line_colour){ auto it_next = std::next(it_part); if( try_connect_t(it_next, line_colour) && try_connect_t(it_part, line_colour) ){ it_part=it_next; return true; } return false; } } // namespace bool Event::is_leading_colour() const { if( !correct_colour(incoming()[0]) || !correct_colour(incoming()[1]) ) return false; Colour line_colour = *incoming()[0].colour; std::swap(line_colour.first, line_colour.second); // reasonable colour if(!std::all_of(outgoing().cbegin(), outgoing().cend(), correct_colour)) return false; for(auto it_part = cbegin_partons(); it_part!=cend_partons(); ++it_part){ switch (type()) { case event_type::FKL: if( !try_connect_t(it_part, line_colour) ) return false; break; case event_type::unob: - case event_type::qqxexb: { + case event_type::qqbar_exb: { if( !try_connect_t(it_part, line_colour) // u-channel only allowed at impact factor && (std::distance(cbegin_partons(), it_part)!=0 || !try_connect_u(it_part, line_colour))) return false; break; } case event_type::unof: - case event_type::qqxexf: { + case event_type::qqbar_exf: { if( !try_connect_t(it_part, line_colour) // u-channel only allowed at impact factor && (std::distance(it_part, cend_partons())!=2 || !try_connect_u(it_part, line_colour))) return false; break; } - case event_type::qqxmid:{ + case event_type::qqbar_mid:{ auto it_next = std::next(it_part); if( !try_connect_t(it_part, line_colour) - // u-channel only allowed at qqx/qxq pair + // u-channel only allowed at q-qbar/qbar-q pair && ( ( !(is_quark(*it_part) && is_antiquark(*it_next)) && !(is_antiquark(*it_part) && is_quark(*it_next))) || !try_connect_u(it_part, line_colour)) ) return false; break; } default: throw std::logic_error{"unreachable"}; } // no colour singlet exchange/disconnected diagram if(line_colour.first == line_colour.second) return false; } return (incoming()[1].colour->first == line_colour.first) && (incoming()[1].colour->second == line_colour.second); } namespace { //! connect incoming Particle to colour flow void connect_incoming(Particle & in, int & colour, int & anti_colour){ in.colour = std::make_pair(anti_colour, colour); // gluon if(in.type == pid::gluon) return; if(in.type > 0){ // quark assert(is_quark(in)); in.colour->second = 0; colour*=-1; return; } // anti-quark assert(is_antiquark(in)); in.colour->first = 0; anti_colour*=-1; } //! connect outgoing Particle to t-channel colour flow template void connect_tchannel( OutIterator & it_part, int & colour, int & anti_colour, RNG & ran ){ assert(colour>0 || anti_colour>0); if(it_part->type == ParticleID::gluon){ // gluon if(colour>0 && anti_colour>0){ // on g line => connect to colour OR anti-colour (random) if(ran.flat() < 0.5){ it_part->colour = std::make_pair(colour+2,colour); colour+=2; } else { it_part->colour = std::make_pair(anti_colour, anti_colour+2); anti_colour+=2; } } else if(colour > 0){ // on q line => connect to available colour it_part->colour = std::make_pair(colour+2, colour); colour+=2; } else { assert(colour<0 && anti_colour>0); - // on qx line => connect to available anti-colour + // on qbar line => connect to available anti-colour it_part->colour = std::make_pair(anti_colour, anti_colour+2); anti_colour+=2; } } else if(is_quark(*it_part)) { // quark assert(anti_colour>0); if(colour>0){ // on g line => connect and remove anti-colour it_part->colour = std::make_pair(anti_colour, 0); anti_colour+=2; anti_colour*=-1; } else { - // on qx line => new colour + // on qbar line => new colour colour*=-1; it_part->colour = std::make_pair(colour, 0); } } else if(is_antiquark(*it_part)) { // anti-quark assert(colour>0); if(anti_colour>0){ // on g line => connect and remove colour it_part->colour = std::make_pair(0, colour); colour+=2; colour*=-1; } else { // on q line => new anti-colour anti_colour*=-1; it_part->colour = std::make_pair(0, anti_colour); } } else { // not a parton assert(!is_parton(*it_part)); it_part->colour = {}; } } //! connect to t- or u-channel colour flow template void connect_utchannel( OutIterator & it_part, int & colour, int & anti_colour, RNG & ran ){ OutIterator it_first = it_part++; if(ran.flat()<.5) {// t-channel connect_tchannel(it_first, colour, anti_colour, ran); connect_tchannel(it_part, colour, anti_colour, ran); } else { // u-channel connect_tchannel(it_part, colour, anti_colour, ran); connect_tchannel(it_first, colour, anti_colour, ran); } } } // namespace bool Event::generate_colours(RNG & ran){ // generate only for HEJ events if(!event_type::is_resummable(type())) return false; assert(std::is_sorted( begin(outgoing()), end(outgoing()), rapidity_less{})); assert(incoming()[0].pz() < incoming()[1].pz()); // positive (anti-)colour -> can connect // negative (anti-)colour -> not available/used up by (anti-)quark int colour = COLOUR_OFFSET; int anti_colour = colour+1; // initialise first connect_incoming(incoming_[0], colour, anti_colour); // reset outgoing colours std::for_each(outgoing_.begin(), outgoing_.end(), [](Particle & part){ part.colour = {};}); for(auto it_part = begin_partons(); it_part!=end_partons(); ++it_part){ switch (type()) { // subleading can connect to t- or u-channel case event_type::unob: - case event_type::qqxexb: { + case event_type::qqbar_exb: { if( std::distance(begin_partons(), it_part)==0) connect_utchannel(it_part, colour, anti_colour, ran); else connect_tchannel(it_part, colour, anti_colour, ran); break; } case event_type::unof: - case event_type::qqxexf: { + case event_type::qqbar_exf: { if( std::distance(it_part, end_partons())==2) connect_utchannel(it_part, colour, anti_colour, ran); else connect_tchannel(it_part, colour, anti_colour, ran); break; } - case event_type::qqxmid:{ + case event_type::qqbar_mid:{ auto it_next = std::next(it_part); if( std::distance(begin_partons(), it_part)>0 && std::distance(it_part, end_partons())>2 && ( (is_quark(*it_part) && is_antiquark(*it_next)) || (is_antiquark(*it_part) && is_quark(*it_next)) ) ) connect_utchannel(it_part, colour, anti_colour, ran); else connect_tchannel(it_part, colour, anti_colour, ran); break; } default: // rest has to be t-channel connect_tchannel(it_part, colour, anti_colour, ran); } } // Connect last connect_incoming(incoming_[1], anti_colour, colour); assert(is_leading_colour()); return true; } // generate_colours namespace { bool valid_parton( std::vector const & jets, Particle const & parton, int const idx, double const soft_pt_regulator, double const min_extparton_pt ){ // TODO code overlap with PhaseSpacePoint::pass_extremal_cuts if(min_extparton_pt > parton.pt()) return false; if(idx<0) return false; assert(static_cast(jets.size())>=idx); auto const & jet{ jets[idx] }; return (parton.p - jet).pt()/jet.pt() <= soft_pt_regulator; } } // namespace // this should work with multiple types bool Event::valid_hej_state(double const soft_pt_regulator, double const min_pt ) const { using namespace event_type; if(!is_resummable(type())) return false; auto const & jet_idx{ particle_jet_indices() }; auto idx_begin{ jet_idx.cbegin() }; auto idx_end{ jet_idx.crbegin() }; auto part_begin{ cbegin_partons() }; auto part_end{ crbegin_partons() }; // always seperate extremal jets if( !valid_parton(jets(), *part_begin, *idx_begin, soft_pt_regulator, min_pt) ) return false; ++part_begin; ++idx_begin; if( !valid_parton(jets(), *part_end, *idx_end, soft_pt_regulator, min_pt) ) return false; ++part_end; ++idx_end; // unob -> second parton in own jet - if( type() & (unob | qqxexb) ){ + if( type() & (unob | qqbar_exb) ){ if( !valid_parton(jets(), *part_begin, *idx_begin, soft_pt_regulator, min_pt) ) return false; ++part_begin; ++idx_begin; } - if( type() & (unof | qqxexf) ){ + if( type() & (unof | qqbar_exf) ){ if( !valid_parton(jets(), *part_end, *idx_end, soft_pt_regulator, min_pt) ) return false; ++part_end; // ++idx_end; // last check, we don't need idx_end afterwards } - if( type() & qqxmid ){ - // find qqx pair - auto begin_qqx{ std::find_if( part_begin, part_end.base(), + if( type() & qqbar_mid ){ + // find qqbar pair + auto begin_qqbar{ std::find_if( part_begin, part_end.base(), [](Particle const & part) -> bool { return part.type != ParticleID::gluon; } )}; - assert(begin_qqx != part_end.base()); - long int qqx_pos{ std::distance(part_begin, begin_qqx) }; - assert(qqx_pos >= 0); - idx_begin+=qqx_pos; - if( !( valid_parton(jets(), *begin_qqx, *idx_begin, + assert(begin_qqbar != part_end.base()); + long int qqbar_pos{ std::distance(part_begin, begin_qqbar) }; + assert(qqbar_pos >= 0); + idx_begin+=qqbar_pos; + if( !( valid_parton(jets(), *begin_qqbar, *idx_begin, soft_pt_regulator, min_pt) - && valid_parton(jets(), *std::next(begin_qqx), *std::next(idx_begin), + && valid_parton(jets(), *std::next(begin_qqbar), *std::next(idx_begin), soft_pt_regulator, min_pt) )) return false; } return true; } Event::ConstPartonIterator Event::begin_partons() const { return cbegin_partons(); } Event::ConstPartonIterator Event::cbegin_partons() const { return {HEJ::is_parton, cbegin(outgoing()), cend(outgoing())}; } Event::ConstPartonIterator Event::end_partons() const { return cend_partons(); } Event::ConstPartonIterator Event::cend_partons() const { return {HEJ::is_parton, cend(outgoing()), cend(outgoing())}; } Event::ConstReversePartonIterator Event::rbegin_partons() const { return crbegin_partons(); } Event::ConstReversePartonIterator Event::crbegin_partons() const { return std::reverse_iterator( cend_partons() ); } Event::ConstReversePartonIterator Event::rend_partons() const { return crend_partons(); } Event::ConstReversePartonIterator Event::crend_partons() const { return std::reverse_iterator( cbegin_partons() ); } Event::PartonIterator Event::begin_partons() { return {HEJ::is_parton, begin(outgoing_), end(outgoing_)}; } Event::PartonIterator Event::end_partons() { return {HEJ::is_parton, end(outgoing_), end(outgoing_)}; } Event::ReversePartonIterator Event::rbegin_partons() { return std::reverse_iterator( end_partons() ); } Event::ReversePartonIterator Event::rend_partons() { return std::reverse_iterator( begin_partons() ); } namespace { void print_momentum(std::ostream & os, fastjet::PseudoJet const & part){ constexpr int prec = 6; const std::streamsize orig_prec = os.precision(); os < const & col){ constexpr int width = 3; if(!col) os << "(no color)"; // American spelling for better alignment else os << "(" <first << ", " <second << ")"; } } // namespace std::ostream& operator<<(std::ostream & os, Event const & ev){ constexpr int prec = 4; constexpr int wtype = 3; // width for types const std::streamsize orig_prec = os.precision(); os < rapidity=" < rapidity=" < rapidity=" < incoming{ event.incoming() }; // First incoming should be positive pz according to LHE standard // (or at least most (everyone?) do it this way, and Pythia assumes it) if(incoming[0].pz() < incoming[1].pz()) std::swap(incoming[0], incoming[1]); for(Particle const & in: incoming){ result.IDUP.emplace_back(in.type); result.ISTUP.emplace_back(LHE_Status::in); result.PUP.push_back({in.p[0], in.p[1], in.p[2], in.p[3], in.p.m()}); result.MOTHUP.emplace_back(0, 0); assert(in.colour); result.ICOLUP.emplace_back(*in.colour); } // outgoing for(size_t i = 0; i < event.outgoing().size(); ++i){ Particle const & out = event.outgoing()[i]; result.IDUP.emplace_back(out.type); const int status = event.decays().count(i) != 0u ?LHE_Status::decay :LHE_Status::out; result.ISTUP.emplace_back(status); result.PUP.push_back({out.p[0], out.p[1], out.p[2], out.p[3], out.p.m()}); result.MOTHUP.emplace_back(1, 2); if(out.colour) result.ICOLUP.emplace_back(*out.colour); else{ result.ICOLUP.emplace_back(std::make_pair(0,0)); } } // decays for(auto const & decay: event.decays()){ for(auto const & out: decay.second){ result.IDUP.emplace_back(out.type); result.ISTUP.emplace_back(LHE_Status::out); result.PUP.push_back({out.p[0], out.p[1], out.p[2], out.p[3], out.p.m()}); const size_t mother_idx = 1 + event.incoming().size() + decay.first; result.MOTHUP.emplace_back(mother_idx, mother_idx); result.ICOLUP.emplace_back(0,0); } } assert(result.ICOLUP.size() == num_particles); static constexpr double unknown_spin = 9.; //per Les Houches accord result.VTIMUP = std::vector(num_particles, unknown_spin); result.SPINUP = result.VTIMUP; return result; } } // namespace HEJ diff --git a/src/Hjets.cc b/src/Hjets.cc index 62e0b6c..c120121 100644 --- a/src/Hjets.cc +++ b/src/Hjets.cc @@ -1,973 +1,965 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/jets.hh" #include "HEJ/Hjets.hh" #include #include #include #include #include #include "HEJ/ConfigFlags.hh" #include "HEJ/Constants.hh" #include "HEJ/LorentzVector.hh" #include "HEJ/utility.hh" // generated headers #include "HEJ/currents/j_h_j.hh" #include "HEJ/currents/juno_h_j.hh" #ifdef HEJ_BUILD_WITH_QCDLOOP #include "qcdloop/qcdloop.h" + #include "HEJ/currents/jh_j.hh" #else #include "HEJ/exceptions.hh" #endif namespace HEJ { namespace currents { namespace { // short hand for math functions using std::norm; using std::abs; using std::conj; using std::pow; using std::sqrt; constexpr double infinity = std::numeric_limits::infinity(); // NOLINT // Loop integrals #ifdef HEJ_BUILD_WITH_QCDLOOP const COM LOOPRWFACTOR = (COM(0.,1.)*M_PI*M_PI)/pow((2.*M_PI),4); COM B0DD(HLV const & q, double mq) { static std::vector> result(3); static auto ql_B0 = [](){ ql::Bubble,double,double> ql_B0; ql_B0.setCacheSize(100); return ql_B0; }(); static std::vector masses(2); static std::vector momenta(1); for(auto & m: masses) m = mq*mq; momenta.front() = q.m2(); ql_B0.integral(result, 1, masses, momenta); return result[0]; } COM C0DD(HLV const & q1, HLV const & q2, double mq) { static std::vector> result(3); static auto ql_C0 = [](){ ql::Triangle,double,double> ql_C0; ql_C0.setCacheSize(100); return ql_C0; }(); static std::vector masses(3); static std::vector momenta(3); for(auto & m: masses) m = mq*mq; momenta[0] = q1.m2(); momenta[1] = q2.m2(); momenta[2] = (q1+q2).m2(); ql_C0.integral(result, 1, masses, momenta); return result[0]; } - COM D0DD(HLV const & q1, HLV const & q2, HLV q3, double mq) + COM D0DD(HLV const & q1, HLV const & q2, HLV const & q3, double mq) { static std::vector> result(3); static auto ql_D0 = [](){ ql::Box,double,double> ql_D0; ql_D0.setCacheSize(100); return ql_D0; }(); static std::vector masses(4); static std::vector momenta(6); for(auto & m: masses) m = mq*mq; momenta[0] = q1.m2(); momenta[1] = q2.m2(); momenta[2] = q3.m2(); momenta[3] = (q1+q2+q3).m2(); momenta[4] = (q1+q2).m2(); momenta[5] = (q2+q3).m2(); ql_D0.integral(result, 1, masses, momenta); return result[0]; } // Kallen lambda functions, see eq:lambda in developer manual double lambda(const double s1, const double s2, const double s3) { return s1*s1 + s2*s2 + s3*s3 - 2*s1*s2 - 2*s1*s3 - 2*s2*s3; } // eq: T_1 in developer manual COM T1(HLV const & q1, HLV const & q2, const double m) { const double q12 = q1.m2(); const double q22 = q2.m2(); const HLV ph = q1 - q2; const double ph2 = ph.m2(); const double lam = lambda(q12, q22, ph2); assert(m > 0.); const double m2 = m*m; return - C0DD(q1, -q2, m)*(2.*m2 + 1./2.*(q12 + q22 - ph2) + 2.*q12*q22*ph2/lam) - (B0DD(q2, m) - B0DD(ph, m))*(q22 - q12 - ph2)*q22/lam - (B0DD(q1, m) - B0DD(ph, m))*(q12 - q22 - ph2)*q12/lam - 1.; } // eq: T_2 in developer manual COM T2(HLV const & q1, HLV const & q2, const double m) { const double q12 = q1.m2(); const double q22 = q2.m2(); const HLV ph = q1 - q2; const double ph2 = ph.m2(); const double lam = lambda(q12, q22, ph2); assert(m > 0.); const double m2 = m*m; return C0DD(q1, -q2, m)*( 4.*m2/lam*(ph2 - q12 - q22) - 1. - 4.*q12*q22/lam*( 1 + 3.*ph2*(q12 + q22 - ph2)/lam ) ) - (B0DD(q2, m) - B0DD(ph, m))*(1. + 6.*q12/lam*(q22 - q12 + ph2))*2.*q22/lam - (B0DD(q1, m) - B0DD(ph, m))*(1. + 6.*q22/lam*(q12 - q22 + ph2))*2.*q12/lam - 2.*(q12 + q22 - ph2)/lam; } #else // no QCDloop COM T1(HLV const & /*q1*/, HLV const & /*q2*/, double /*mt*/){ throw std::logic_error{"T1 called without QCDloop support"}; } COM T2(HLV const & /*q1*/, HLV const & /*q2*/, double /*mt*/){ throw std::logic_error{"T2 called without QCDloop support"}; } #endif // prefactors of g^{\mu \nu} and q_2^\mu q_1^\nu in Higgs boson emission vertex // see eq:VH in developer manual, but *without* global factor \alpha_s std::array TT( HLV const & qH1, HLV const & qH2, const double mt, const bool inc_bottom, const double mb, const double vev ) { if(mt == infinity) { std::array res = {qH1.dot(qH2), 1.}; for(auto & tt: res) tt /= (3.*M_PI*vev); return res; } std::array res = {T1(qH1, qH2, mt), T2(qH1, qH2, mt)}; for(auto & tt: res) tt *= mt*mt; if(inc_bottom) { res[0] += mb*mb*T1(qH1, qH2, mb); res[1] += mb*mb*T2(qH1, qH2, mb); } for(auto & tt: res) tt /= M_PI*vev; return res; } /** * @brief Higgs+Jets FKL Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1 * @param p1in Incoming Particle 1 * @param p2out Outgoing Particle 2 * @param p2in Incoming Particle 2 * @param qH1 t-channel momenta into higgs vertex * @param qH2 t-channel momenta out of higgs vertex * @param mt top mass (inf or value) * @param inc_bottom whether to include bottom mass effects (true) or not (false) * @param mb bottom mass (value) * @param vev Higgs vacuum expectation value * * Calculates j^\mu H j_\mu. FKL with higgs vertex somewhere in the FKL chain. * Handles all possible incoming states. */ double j_h_j( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, const double mt, const bool inc_bottom, const double mb, const double vev ){ using helicity::plus; using helicity::minus; const auto qqH1 = split_into_lightlike(qH1); const HLV qH11 = qqH1.first; const HLV qH12 = -qqH1.second; const auto qqH2 = split_into_lightlike(qH2); const HLV qH21 = qqH2.first; const HLV qH22 = -qqH2.second; // since qH1.m2(), qH2.m2() < 0 the following assertions are always true assert(qH11.e() > 0); assert(qH12.e() > 0); assert(qH21.e() > 0); assert(qH22.e() > 0); const auto T_pref = TT(qH1, qH2, mt, inc_bottom, mb, vev); const COM amp_mm = HEJ::j_h_j( p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1] ); const COM amp_mp = HEJ::j_h_j( p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1] ); const COM amp_pm = HEJ::j_h_j( p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1] ); const COM amp_pp = HEJ::j_h_j( p1out, p1in, p2out, p2in, qH11, qH12, qH21, qH22, T_pref[0], T_pref[1] ); static constexpr double num_hel = 4.; // square of amplitudes, averaged over helicities const double amp2 = (norm(amp_mm) + norm(amp_mp) + norm(amp_pm) + norm(amp_pp))/num_hel; return amp2/((p1in-p1out).m2()*(p2in-p2out).m2()*qH1.m2()*qH2.m2()); } // } } // namespace double ME_H_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_qbarQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_qg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev) * K_g(p2out,p2in)/C_A; } double ME_H_qbarg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev) * K_g(p2out,p2in)/C_A; } double ME_H_gg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return j_h_j(p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev) * K_g(p2out,p2in)/C_A * K_g(p1out,p1in)/C_A; } //@} namespace { template double amp_juno_h_j( HLV const & pa, HLV const & pb, HLV const & pg, HLV const & p1, HLV const & p2, HLV const & qH11, HLV const & qH12, HLV const & qH21, HLV const & qH22, std::array const & T_pref ) { // TODO: code duplication with Wjets and pure jets const COM u1 = U1_h_j(pa,p1,pb,p2,pg,qH11,qH12,qH21,qH22,T_pref[0],T_pref[1]); const COM u2 = U2_h_j(pa,p1,pb,p2,pg,qH11,qH12,qH21,qH22,T_pref[0],T_pref[1]); const COM l = L_h_j(pa,p1,pb,p2,pg,qH11,qH12,qH21,qH22,T_pref[0],T_pref[1]); return 2.*C_F*std::real((l-u1)*std::conj(l+u2)) + 2.*C_F*C_F/3.*std::norm(u1+u2) ; } //@{ /** * @brief Higgs+Jets Unordered Contributions, function to handle all incoming types. * @param pg Unordered Gluon momenta * @param p1out Outgoing Particle 1 * @param p1in Incoming Particle 1 * @param p2out Outgoing Particle 2 * @param p2in Incoming Particle 2 * @param qH1 t-channel momenta into higgs vertex * @param qH2 t-channel momenta out of higgs vertex * @param mt top mass (inf or value) * @param inc_bottom whether to include bottom mass effects (true) or not (false) * @param mb bottom mass (value) * * Calculates j_{uno}^\mu H j_\mu. Unordered with higgs vertex * somewhere in the FKL chain. Handles all possible incoming states. */ double juno_h_j( HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, const double mt, const bool incBot, const double mb, const double vev ){ using helicity::plus; using helicity::minus; const auto qqH1 = split_into_lightlike(qH1); const HLV qH11 = qqH1.first; const HLV qH12 = -qqH1.second; const auto qqH2 = split_into_lightlike(qH2); const HLV qH21 = qqH2.first; const HLV qH22 = -qqH2.second; // since qH1.m2(), qH2.m2() < 0 the following assertions are always true assert(qH11.e() > 0); assert(qH12.e() > 0); assert(qH21.e() > 0); assert(qH22.e() > 0); const auto T_pref = TT(qH1, qH2, mt, incBot, mb, vev); // only 4 out of the 8 helicity amplitudes are independent // we still compute all of them for better numerical stability (mirror check) - MultiArray amp; + MultiArray amp{}; // NOLINTNEXTLINE #define ASSIGN_HEL(RES, J, H1, H2, HG) \ RES[H1][H2][HG] = J( \ p1in, p2in, pg, p1out, p2out, qH11, qH12, qH21, qH22, T_pref \ ) ASSIGN_HEL(amp, amp_juno_h_j, minus, minus, minus); ASSIGN_HEL(amp, amp_juno_h_j, minus, minus, plus); ASSIGN_HEL(amp, amp_juno_h_j, minus, plus, minus); ASSIGN_HEL(amp, amp_juno_h_j, minus, plus, plus); ASSIGN_HEL(amp, amp_juno_h_j, plus, minus, minus); ASSIGN_HEL(amp, amp_juno_h_j, plus, minus, plus); ASSIGN_HEL(amp, amp_juno_h_j, plus, plus, minus); ASSIGN_HEL(amp, amp_juno_h_j, plus, plus, plus); #undef ASSIGN_HEL const HLV q1 = p1in-p1out; // Top End const HLV q2 = p2out-p2in; // Bottom End const HLV qg = p1in-p1out-pg; // Extra bit post-gluon double ampsq = 0.; for(Helicity h1: {minus, plus}) { for(Helicity h2: {minus, plus}) { for(Helicity hg: {minus, plus}) { ampsq += amp[h1][h2][hg]; } } } ampsq /= 16.*qg.m2()*qH1.m2()*qH2.m2()*q2.m2(); // Factor of (Cf/Ca) for each quark to match ME_H_qQ. ampsq*=C_F*C_F/C_A/C_A; return ampsq; } } // namespace double ME_H_unob_qQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_unob_qbarQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_unob_qQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_unob_qbarQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev); } double ME_H_unob_gQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev) *K_g(p2out,p2in)/C_F; } double ME_H_unob_gQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, include_bottom, mb, vev) *K_g(p2out,p2in)/C_F; } //@} // Begin finite mass stuff #ifdef HEJ_BUILD_WITH_QCDLOOP namespace { // All the stuff needed for the box functions in qg->qgH now... COM E1(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2=-(k1+k2+kh); - double Delta, Sigma, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; - Sigma = 4.*s12*s34 - pow(S1+S2,2); + HLV const q2=-(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; + double const Sigma = 4.*s12*s34 - pow(S1+S2,2); return LOOPRWFACTOR*(-s12*D0DD(k2, k1, q2, mq)*(1 - 8.*mq*mq/s12 + S2/(2.*s12) + S2*(s12 - 8.*mq*mq)*(s34 + S1)/(2.*s12*Delta) + 2.*(s34 + S1)*(s34 + S1)/Delta + S2*pow((s34 + S1),3)/Delta/Delta) - ((s12 + S2)*C0DD(k2, k1 + q2, mq) - s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) - S1*C0DD(k1, q2, mq))*(S2*(s12 - 4.*mq*mq)/(2.*s12*Delta) + 2.*(s34 + S1)/Delta + S2*pow((s34 + S1),2)/Delta/Delta) + (C0DD(k1, q2, mq) - C0DD(k1 + k2, q2, mq))*(1. - 4.*mq*mq/s12) - C0DD(k1 + k2, q2, mq)*2.*s34/ S1 - (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*s34*(s34 + S1)/(S1*Delta) + (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*(2.*s34*(s34 + S1)*(S1 - S2)/(Delta*Sigma) + 2.*s34*(s34 + S1)/(S1*Delta)) + (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*2.*(s34 + S1)*(2.*s12*s34 - S2*(S1 + S2))/(Delta*Sigma)); } COM F1(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, Sigma, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; - Sigma = 4.*s12*s34 - pow(S1+S2,2); + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; + double const Sigma = 4.*s12*s34 - pow(S1+S2,2); return LOOPRWFACTOR*(-S2*D0DD(k1, k2, q2, mq)*(0.5 - (s12 - 8.*mq*mq)*(s34 + S2)/(2.*Delta) - s12*pow((s34 + S2),3)/Delta/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) - S2*C0DD(k2, q2, mq))*(S2*(s12 - 4.*mq*mq)/(2.*s12*Delta) + S2*pow((s34 + S2),2)/Delta/Delta) - (C0DD(k1 + k2, q2, mq) - C0DD(k1, k2 + q2, mq))*(1. - 4.*mq*mq/s12) - C0DD(k1, k2 + q2, mq) + (B0DD(k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*pow((s34 + S2),2)/((s12 + S1)*Delta) - (B0DD( q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*2.*s34*(s34 + S2)*(S2 - S1)/(Delta*Sigma) + (B0DD( k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*2.*(s34 + S2)*(2.*s12*s34 - S2*(S1 + S2))/(Delta*Sigma)); } COM G1(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; return LOOPRWFACTOR*(S2*D0DD(k1, q2, k2, mq)*(Delta/s12/s12 - 4.*mq*mq/s12) - S2*((s12 + S1)*C0DD(k1, k2 + q2, mq) - S1*C0DD(k1, q2, mq))*(1./ s12/s12 - (s12 - 4.*mq*mq)/(2.*s12*Delta)) - S2*((s12 + S2)*C0DD(k1 + q2, k2, mq) - S2*C0DD(k2, q2, mq))*(1./ s12/s12 + (s12 - 4.*mq*mq)/(2.*s12*Delta)) - C0DD(k1, q2, mq) - (C0DD(k1, k2 + q2, mq) - C0DD(k1, q2, mq))*4.*mq*mq/ s12 + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2./ s12 + (B0DD(k1 + q2, mq) - B0DD(q2, mq))*2.*s34/(s12*S1) + (B0DD(k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*(s34 + S2)/(s12*(s12 + S1))); } COM E4(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, Sigma, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; - Sigma = 4.*s12*s34 - pow(S1+S2,2); + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; + double const Sigma = 4.*s12*s34 - pow(S1+S2,2); return LOOPRWFACTOR* (-s12*D0DD(k2, k1, q2, mq)*(0.5 - (S1 - 8.*mq*mq)*(s34 + S1)/(2.*Delta) - s12*pow((s34 + S1),3)/Delta/Delta) + ((s12 + S2)*C0DD(k2, k1 + q2, mq) - s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) - S1*C0DD(k1, q2, mq))*((S1 - 4.*mq*mq)/(2.*Delta) + s12*pow((s34 + S1),2)/Delta/Delta) - C0DD(k1 + k2, q2, mq) + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*(2.*s34/Delta + 2.*s12*(s34 + S1)/((s12 + S2)*Delta)) - (B0DD( q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*((2.*s34*(2.*s12*s34 - S2*(S1 + S2) + s12*(S1 - S2)))/(Delta*Sigma)) + (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq)) *((2.*s12*(2.*s12*s34 - S1*(S1 + S2) + s34*(S2 - S1)))/(Delta*Sigma))); } COM F4(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, Sigma, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; - Sigma = 4.*s12*s34 - pow(S1+S2,2); + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; + double const Sigma = 4.*s12*s34 - pow(S1+S2,2); return LOOPRWFACTOR* (-s12*D0DD(k1, k2, q2, mq)*(0.5 + (S1 - 8.*mq*mq)*(s34 + S2)/(2.*Delta) + s12*pow((s34 + S2),3)/Delta/Delta) - ((s12 + S1)*C0DD(k1, k2 + q2, mq) - s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) - S2*C0DD(k2, q2, mq))*((S1 - 4.*mq*mq)/(2.*Delta) + s12*pow((s34 + S2),2)/Delta/Delta) - C0DD(k1 + k2, q2, mq) - (B0DD(k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*2.*(s34 + S2)/Delta + (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*2.*s34*(2.*s12*s34 - S1*(S1 + S2) + s12*(S2 - S1))/(Delta*Sigma) - (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq)) *(2.*s12*(2.*s12*s34 - S2*(S1 + S2) + s34*(S1 - S2))/(Delta*Sigma))); } COM G4(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; return LOOPRWFACTOR* (-D0DD(k1, q2, k2, mq)*(Delta/s12 + (s12 + S1)/2. - 4.*mq*mq) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - S1*C0DD(k1, q2, mq))*(1./ s12 - (S1 - 4.*mq*mq)/(2.*Delta)) + ((s12 + S2)*C0DD( k1 + q2, k2, mq) - S2*C0DD(k2, q2, mq))*(1./ s12 + (S1 - 4.*mq*mq)/(2.*Delta)) + (B0DD( k1 + k2 + q2, mq) - B0DD(k1 + q2, mq))*2./(s12 + S2)); } COM E10(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, Sigma, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; - Sigma = 4.*s12*s34 - pow(S1+S2,2); + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; + double const Sigma = 4.*s12*s34 - pow(S1+S2,2); return LOOPRWFACTOR*(-s12*D0DD(k2, k1, q2, mq)*((s34 + S1)/Delta + 12.*mq*mq*S1*(s34 + S1)/Delta/Delta - 4.*s12*S1*pow((s34 + S1),3)/Delta/Delta/Delta) - ((s12 + S2)*C0DD(k2, k1 + q2, mq) - s12*C0DD(k1, k2, mq) + (S1 - S2)*C0DD(k1 + k2, q2, mq) - S1*C0DD(k1, q2, mq))*(1./Delta + 4.*mq*mq*S1/Delta/Delta - 4.*s12*S1*pow((s34 + S1),2)/Delta/Delta/Delta) + C0DD(k1 + k2, q2, mq)*(4.*s12*s34*(S1 - S2)/(Delta*Sigma) - 4.*(s12 - 2.*mq*mq)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)) + (B0DD(k1 + q2, mq) - B0DD(k1 + k2 + q2, mq))*(4.*(s34 + S1)/((s12 + S2)*Delta) + 8.*S1*(s34 + S1)/Delta/Delta) + (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*(12.*s34*(2.*s12 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) - 4.*s34*(4.*s12 + 3.*S1 + S2)/(Delta*Sigma) + 8.*s12*s34*(s34*(s12 + S2) - S1*(s34 + S1))/(Delta*Delta*Sigma)) + (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*(12.*s12*(2.*s34 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) + 8.*s12*S1*(s34*(s12 + S2) - S1*(s34 + S1))/(Delta*Delta*Sigma))) + (COM(0.,1.)/(4.*M_PI*M_PI))*((2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)); } COM F10(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, Sigma, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; - Sigma = 4.*s12*s34 - pow(S1+S2,2); + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; + double const Sigma = 4.*s12*s34 - pow(S1+S2,2); return LOOPRWFACTOR* (s12*D0DD(k1, k2, q2, mq)*((s34 + S2)/Delta - 4.*mq*mq/Delta + 12.*mq*mq*s34*(s12 + S1)/Delta/Delta - 4.*s12*pow((s34 + S2),2)/Delta/Delta - 4.*s12*S1*pow((s34 + S2),3)/Delta/Delta/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - s12*C0DD(k1, k2, mq) - (S1 - S2)*C0DD(k1 + k2, q2, mq) - S2*C0DD(k2, q2, mq))*(1./Delta + 4.*mq*mq*S1/Delta/Delta - 4.*s12*(s34 + S2)/Delta/Delta - 4.*s12*S1*pow((s34 + S2),2)/Delta/Delta/Delta) - C0DD(k1 + k2, q2, mq)*(4.*s12*s34/(S2*Delta) + 4.*s12*s34*(S2 - S1)/(Delta*Sigma) + 4.*(s12 - 2.*mq*mq)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)) - (B0DD( k2 + q2, mq) - B0DD(k1 + k2 + q2, mq))*(4.*s34/(S2*Delta) + 8.*s34*(s12 + S1)/Delta/Delta) - (B0DD(q2, mq) - B0DD(k1 + k2 + q2, mq) + s12*C0DD(k1 + k2, q2, mq))*(-12*s34*(2*s12 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) - 4.*s12*s34*s34/(S2*Delta*Delta) + 4.*s34*S1/(Delta*Sigma) - 4.*s34*(s12*s34*(2.*s12 + S2) - S1*S1*(2.*s12 + S1))/(Delta*Delta*Sigma)) - (B0DD(k1 + k2, mq) - B0DD(k1 + k2 + q2, mq) - (s34 + S1 + S2)*C0DD(k1 + k2, q2, mq))*(-12.*s12*(2.*s34 + S1 + S2)*(2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma*Sigma) + 8.*s12*(2.*s34 + S1)/(Delta*Sigma) - 8.*s12*s34*(2.*s12*s34 - S1*(S1 + S2) + s12*(S2 - S1))/(Delta*Delta*Sigma))) + (COM(0.,1.)/(4.*M_PI*M_PI))*((2.*s12*s34 - S1*(S1 + S2))/(Delta*Sigma)); } COM G10(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ - HLV q2 = -(k1+k2+kh); - double Delta, S1, S2, s12, s34; - S1 = 2.*k1.dot(q2); - S2 = 2.*k2.dot(q2); - s12 = 2.*k1.dot(k2); - s34 = q2.m2(); - Delta = s12*s34 - S1*S2; + HLV const q2 = -(k1+k2+kh); + double const S1 = 2.*k1.dot(q2); + double const S2 = 2.*k2.dot(q2); + double const s12 = 2.*k1.dot(k2); + double const s34 = q2.m2(); + double const Delta = s12*s34 - S1*S2; return LOOPRWFACTOR* (-D0DD(k1, q2, k2, mq)*(1. + 4.*S1*mq*mq/Delta) + ((s12 + S1)*C0DD(k1, k2 + q2, mq) - S1*C0DD(k1, q2, mq))*(1./Delta + 4.*S1*mq*mq/Delta/Delta) - ((s12 + S2)*C0DD(k1 + q2, k2, mq) - S2*C0DD(k2, q2, mq))*(1./Delta + 4.*S1*mq*mq/Delta/Delta) + (B0DD(k1 + k2 + q2, mq) - B0DD(k1 + q2, mq))*4.*(s34 + S1)/(Delta*(s12 + S2)) + (B0DD(q2, mq) - B0DD(k2 + q2, mq))*4.*s34/(Delta*S2)); } COM H1DD(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return E1(k1,k2,kh,mq)+F1(k1,k2,kh,mq)+G1(k1,k2,kh,mq); } COM H4DD(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return E4(k1,k2,kh,mq)+F4(k1,k2,kh,mq)+G4(k1,k2,kh,mq); } COM H10DD(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return E10(k1,k2,kh,mq)+F10(k1,k2,kh,mq)+G10(k1,k2,kh,mq); } COM H2DD(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return -1.*H1DD(k2,k1,kh,mq); } COM H5DD(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return -1.*H4DD(k2,k1,kh,mq); } COM H12DD(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return -1.*H10DD(k2,k1,kh,mq); } HLV parity_flip(HLV const & p){ HLV flippedVector; flippedVector.setE(p.e()); flippedVector.setX(-p.x()); flippedVector.setY(-p.y()); flippedVector.setZ(-p.z()); return flippedVector; } template COM jh_j( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & p2, HLV const & ph1, HLV const & ph2, double const mq ) { return (pa.z() > 0)? jh_j_forward(pa, p1, pb, p2, ph1, ph2, mq): jh_j_backward(pa, p1, pb, p2, ph1, ph2, mq) ; } template COM amp_jh_j( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & p2, HLV const & ph1, HLV const & ph2, double const mq, bool const include_bottom, double const mq2, double const vev ) { COM res = 4.*mq*mq/vev*jh_j(pa, p1, pb, p2, ph1, ph2, mq); if(include_bottom) { res += 4.*mq2*mq2/vev*jh_j(pa, p1, pb, p2, ph1, ph2, mq2); } return res; } // sum over jh_j helicity amplitudes squared with + incoming gluon double ampsq_sum_jh_j( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & p2, HLV const & ph1, HLV const & ph2, double const mq, bool const include_bottom, double const mq2, double const vev ) { using helicity::plus; using helicity::minus; using std::norm; const COM appp = amp_jh_j( pa, p1, pb, p2, ph1, ph2, mq, include_bottom, mq2, vev ); const COM appm = amp_jh_j( pa, p1, pb, p2, ph1, ph2, mq, include_bottom, mq2, vev ); const COM apmp = amp_jh_j( pa, p1, pb, p2, ph1, ph2, mq, include_bottom, mq2, vev ); const COM apmm = amp_jh_j( pa, p1, pb, p2, ph1, ph2, mq, include_bottom, mq2, vev ); return norm(appp) + norm(appm) + norm(apmp) + norm(apmm); } } // namespace // Higgs emitted close to gluon with full mt effects. double ME_Houtside_gq( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pH, - const double mq, const bool include_bottom, const double mq2, const double vev + const double mt, const bool include_bottom, const double mb, const double vev ){ using helicity::plus; using helicity::minus; const auto ph = split_into_lightlike(pH); assert(ph.first.e() > 0); assert(ph.second.e() > 0); // incoming gluon with + helicity const double ME_plus = ampsq_sum_jh_j( p1in, p1out, p2in, p2out, ph.first, ph.second, - mq, include_bottom, mq2, vev + mt, include_bottom, mb, vev ); // incoming gluon with - helicity const double ME_minus = ampsq_sum_jh_j( parity_flip(p1in), parity_flip(p1out), parity_flip(p2in), parity_flip(p2out), parity_flip(ph.first), parity_flip(ph.second), - mq, include_bottom, mq2, vev + mt, include_bottom, mb, vev ); const double prop = m2(p1in - p1out - pH); return (ME_plus + ME_minus)/(prop*prop); } #endif // HEJ_BUILD_WITH_QCDLOOP double C2gHgm(HLV const & p2, HLV const & p1, HLV const & pH, double vev){ const double A=1./(3.*M_PI*vev); // Implements Eq. (4.22) in hep-ph/0301013 with modifications to incoming plus momenta double s12 = NAN; double p1p = NAN; double p2p = NAN; COM p1perp; COM p3perp; COM phperp; // Determine first whether this is the case p1p\sim php>>p3p or the opposite s12=p1.invariantMass2(-p2); if (p2.pz()>0.) { // case considered in hep-ph/0301013 p1p=p1.plus(); p2p=p2.plus(); } else { // opposite case p1p=p1.minus(); p2p=p2.minus(); } p1perp=p1.px()+COM(0,1)*p1.py(); phperp=pH.px()+COM(0,1)*pH.py(); p3perp=-(p1perp+phperp); COM temp=COM(0,1)*A/(2.*s12)*(p2p/p1p*conj(p1perp)*p3perp +p1p/p2p*p1perp*conj(p3perp)); temp=temp*conj(temp); return temp.real(); } double C2gHgp(HLV const & p2, HLV const & p1, HLV const & pH, double vev){ const double A=1./(3.*M_PI*vev); // Implements Eq. (4.23) in hep-ph/0301013 double s12 = NAN; double php = NAN; double p1p = NAN; double phm = NAN; COM p1perp; COM p3perp; COM phperp; // Determine first whether this is the case p1p\sim php>>p3p or the opposite s12=p1.invariantMass2(-p2); if (p2.pz()>0.) { // case considered in hep-ph/0301013 php=pH.plus(); phm=pH.minus(); p1p=p1.plus(); } else { // opposite case php=pH.minus(); phm=pH.plus(); p1p=p1.minus(); } p1perp=p1.px()+COM(0,1)*p1.py(); phperp=pH.px()+COM(0,1)*pH.py(); p3perp=-(p1perp+phperp); COM temp=-COM(0,1)*A/(2.*s12)*( conj(p1perp*p3perp)*pow(php/p1p,2)/(1.+php/p1p) +s12*(pow(conj(phperp),2)/(pow(abs(phperp),2)+p1p*phm) -pow(conj(p3perp)+(1.+php/p1p)*conj(p1perp),2) /((1.+php/p1p)*(pH.m2()+2.*p1.dot(pH)))) ); temp=temp*conj(temp); return temp.real(); } double C2qHqm(HLV const & p2, HLV const & p1, HLV const & pH, double vev){ const double A=1./(3.*M_PI*vev); // Implements Eq. (4.21) in hep-ph/0301013 double s12 = NAN; double p2p = NAN; double p1p = NAN; COM p1perp; COM p3perp; COM phperp; // Determine first whether this is the case p1p\sim php>>p3p or the opposite s12=p1.invariantMass2(-p2); if (p2.pz()>0.) { // case considered in hep-ph/0301013 p2p=p2.plus(); p1p=p1.plus(); } else { // opposite case p2p=p2.minus(); p1p=p1.minus(); } p1perp=p1.px()+COM(0,1)*p1.py(); phperp=pH.px()+COM(0,1)*pH.py(); p3perp=-(p1perp+phperp); COM temp=A/(2.*s12)*( sqrt(p2p/p1p)*p3perp*conj(p1perp) +sqrt(p1p/p2p)*p1perp*conj(p3perp) ); temp=temp*conj(temp); return temp.real(); } } // namespace currents } // namespace HEJ diff --git a/src/MatrixElement.cc b/src/MatrixElement.cc index 39dc861..8e78b38 100644 --- a/src/MatrixElement.cc +++ b/src/MatrixElement.cc @@ -1,2136 +1,2135 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/MatrixElement.hh" #include #include #include #include #include #include #include #include #include #include "fastjet/PseudoJet.hh" #include "HEJ/ConfigFlags.hh" #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" #include "HEJ/Event.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/Hjets.hh" #include "HEJ/LorentzVector.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/Particle.hh" #include "HEJ/Wjets.hh" #include "HEJ/Zjets.hh" #include "HEJ/event_types.hh" #include "HEJ/exceptions.hh" #include "HEJ/jets.hh" #include "HEJ/utility.hh" namespace HEJ { double MatrixElement::omega0( double alpha_s, double mur, fastjet::PseudoJet const & q_j ) const { const double lambda = param_.regulator_lambda; const double result = - alpha_s*N_C/M_PI*std::log(q_j.perp2()/(lambda*lambda)); if(! param_.log_correction) return result; return ( 1. + alpha_s/(4.*M_PI)*BETA0*std::log(mur*mur/(q_j.perp()*lambda)) )*result; } Weights MatrixElement::operator()(Event const & event) const { std::vector tree_kin_part=tree_kin(event); std::vector virtual_part=virtual_corrections(event); if(tree_kin_part.size() != virtual_part.size()) { throw std::logic_error("tree and virtuals have different sizes"); } Weights sum = Weights{0., std::vector(event.variations().size(), 0.)}; for(size_t i=0; i tree_kin_part=tree_kin(event); double sum = 0.; for(double i : tree_kin_part) { sum += i; } return tree_param(event)*sum; } Weights MatrixElement::tree_param(Event const & event) const { if(! is_resummable(event.type())) { return Weights{0., std::vector(event.variations().size(), 0.)}; } Weights result; // only compute once for each renormalisation scale std::unordered_map known; result.central = tree_param(event, event.central().mur); known.emplace(event.central().mur, result.central); for(auto const & var: event.variations()) { const auto ME_it = known.find(var.mur); if(ME_it == end(known)) { const double wt = tree_param(event, var.mur); result.variations.emplace_back(wt); known.emplace(var.mur, wt); } else { result.variations.emplace_back(ME_it->second); } } return result; } std::vector MatrixElement::virtual_corrections(Event const & event) const { if(! is_resummable(event.type())) { return {Weights{0., std::vector(event.variations().size(), 0.)}}; } // only compute once for each renormalisation scale std::unordered_map > known_vec; std::vector central_vec=virtual_corrections(event, event.central().mur); known_vec.emplace(event.central().mur, central_vec); for(auto const & var: event.variations()) { const auto ME_it = known_vec.find(var.mur); if(ME_it == end(known_vec)) { known_vec.emplace(var.mur, virtual_corrections(event, var.mur)); } } // At this stage known_vec contains one vector of virtual corrections for each mur value // Now put this into a vector of Weights std::vector result_vec; for(size_t i=0; isecond.at(i)); } result_vec.emplace_back(result); } return result_vec; } double MatrixElement::virtual_corrections_W( Event const & event, const double mur, Particle const & WBoson ) const{ auto const & in = event.incoming(); const auto partons = filter_partons(event.outgoing()); fastjet::PseudoJet const & pa = in.front().p; #ifndef NDEBUG fastjet::PseudoJet const & pb = in.back().p; double const norm = (in.front().p + in.back().p).E(); #endif assert(std::is_sorted(partons.begin(), partons.end(), rapidity_less{})); assert(partons.size() >= 2); assert(pa.pz() < pb.pz()); fastjet::PseudoJet q = pa - partons[0].p; std::size_t first_idx = 0; std::size_t last_idx = partons.size() - 1; #ifndef NDEBUG bool wc = true; #endif bool wqq = false; - // With extremal qqx or unordered gluon outside the extremal + // With extremal qqbar or unordered gluon outside the extremal // partons then it is not part of the FKL ladder and does not // contribute to the virtual corrections. W emitted from the // most backward leg must be taken into account in t-channel if (event.type() == event_type::unob) { q -= partons[1].p; ++first_idx; if (in[0].type != partons[1].type ){ q -= WBoson.p; #ifndef NDEBUG wc=false; #endif } } - else if (event.type() == event_type::qqxexb) { + else if (event.type() == event_type::qqbar_exb) { q -= partons[1].p; ++first_idx; if (std::abs(partons[0].type) != std::abs(partons[1].type)){ q -= WBoson.p; #ifndef NDEBUG wc=false; #endif } } else { if(event.type() == event_type::unof - || event.type() == event_type::qqxexf){ + || event.type() == event_type::qqbar_exf){ --last_idx; } if (in[0].type != partons[0].type ){ q -= WBoson.p; #ifndef NDEBUG wc=false; #endif } } - std::size_t first_idx_qqx = last_idx; - std::size_t last_idx_qqx = last_idx; + std::size_t first_idx_qqbar = last_idx; + std::size_t last_idx_qqbar = last_idx; - //if qqxMid event, virtual correction do not occur between - //qqx pair. - if(event.type() == event_type::qqxmid){ + //if qqbarMid event, virtual correction do not occur between qqbar pair. + if(event.type() == event_type::qqbar_mid){ const auto backquark = std::find_if( begin(partons) + 1, end(partons) - 1 , [](Particle const & s){ return (s.type != pid::gluon); } ); if(backquark == end(partons) || (backquark+1)->type==pid::gluon) return 0; if(std::abs(backquark->type) != std::abs((backquark+1)->type)) { wqq=true; #ifndef NDEBUG wc=false; #endif } last_idx = std::distance(begin(partons), backquark); - first_idx_qqx = last_idx+1; + first_idx_qqbar = last_idx+1; } double exponent = 0; const double alpha_s = alpha_s_(mur); for(std::size_t j = first_idx; j < last_idx; ++j){ exponent += omega0(alpha_s, mur, q)*( partons[j+1].rapidity() - partons[j].rapidity() ); q -=partons[j+1].p; } // End Loop one - if (last_idx != first_idx_qqx) q -= partons[last_idx+1].p; + if (last_idx != first_idx_qqbar) q -= partons[last_idx+1].p; if (wqq) q -= WBoson.p; - for(std::size_t j = first_idx_qqx; j < last_idx_qqx; ++j){ + for(std::size_t j = first_idx_qqbar; j < last_idx_qqbar; ++j){ exponent += omega0(alpha_s, mur, q)*( partons[j+1].rapidity() - partons[j].rapidity() ); q -= partons[j+1].p; } #ifndef NDEBUG if (wc) q -= WBoson.p; assert( nearby(q, -1*pb, norm) || is_AWZH_boson(partons.back().type) || event.type() == event_type::unof - || event.type() == event_type::qqxexf + || event.type() == event_type::qqbar_exf ); #endif return std::exp(exponent); } std::vector MatrixElement::virtual_corrections_Z_qq( Event const & event, const double mur, Particle const & ZBoson ) const{ auto const & in = event.incoming(); const auto partons = filter_partons(event.outgoing()); fastjet::PseudoJet const & pa = in.front().p; #ifndef NDEBUG fastjet::PseudoJet const & pb = in.back().p; #endif assert(std::is_sorted(partons.begin(), partons.end(), rapidity_less{})); assert(partons.size() >= 2); assert(pa.pz() < pb.pz()); fastjet::PseudoJet q_t = pa - partons[0].p - ZBoson.p; fastjet::PseudoJet q_b = pa - partons[0].p; size_t first_idx = 0; size_t last_idx = partons.size() - 1; // Unordered gluon does not contribute to the virtual corrections if (event.type() == event_type::unob) { // Gluon is partons[0] and is already subtracted // partons[1] is the backward quark q_t -= partons[1].p; q_b -= partons[1].p; ++first_idx; } else if (event.type() == event_type::unof) { // End sum at forward quark --last_idx; } double sum_top=0.; double sum_bot=0.; double sum_mix=0.; const double alpha_s = alpha_s_(mur); for(size_t j = first_idx; j < last_idx; ++j){ const double dy = partons[j+1].rapidity() - partons[j].rapidity(); const double tmp_top = omega0(alpha_s, mur, q_t)*dy; const double tmp_bot = omega0(alpha_s, mur, q_b)*dy; sum_top += tmp_top; sum_bot += tmp_bot; sum_mix += (tmp_top + tmp_bot) / 2.; q_t -= partons[j+1].p; q_b -= partons[j+1].p; } return {exp(sum_top), exp(sum_bot), exp(sum_mix)}; } double MatrixElement::virtual_corrections_Z_qg( Event const & event, const double mur, Particle const & ZBoson, const bool is_gq_event ) const{ auto const & in = event.incoming(); const auto partons = filter_partons(event.outgoing()); fastjet::PseudoJet const & pa = in.front().p; #ifndef NDEBUG fastjet::PseudoJet const & pb = in.back().p; #endif assert(std::is_sorted(partons.begin(), partons.end(), rapidity_less{})); assert(partons.size() >= 2); assert(pa.pz() < pb.pz()); // If this is a gq event, don't subtract the Z momentum from first q fastjet::PseudoJet q = (is_gq_event ? pa - partons[0].p : pa - partons[0].p - ZBoson.p); size_t first_idx = 0; size_t last_idx = partons.size() - 1; // Unordered gluon does not contribute to the virtual corrections if (event.type() == event_type::unob) { // Gluon is partons[0] and is already subtracted // partons[1] is the backward quark q -= partons[1].p; ++first_idx; } else if (event.type() == event_type::unof) { // End sum at forward quark --last_idx; } double sum=0.; const double alpha_s = alpha_s_(mur); for(size_t j = first_idx; j < last_idx; ++j){ sum += omega0(alpha_s, mur, q)*(partons[j+1].rapidity() - partons[j].rapidity()); q -= partons[j+1].p; } return exp(sum); } std::vector MatrixElement::virtual_corrections( Event const & event, const double mur ) const{ auto const & in = event.incoming(); auto const & out = event.outgoing(); fastjet::PseudoJet const & pa = in.front().p; #ifndef NDEBUG fastjet::PseudoJet const & pb = in.back().p; double const norm = (in.front().p + in.back().p).E(); #endif const auto AWZH_boson = std::find_if( begin(out), end(out), [](Particle const & p){ return is_AWZH_boson(p); } ); if(AWZH_boson != end(out) && std::abs(AWZH_boson->type) == pid::Wp){ return {virtual_corrections_W(event, mur, *AWZH_boson)}; } if(AWZH_boson != end(out) && AWZH_boson->type == pid::Z_photon_mix){ if(is_gluon(in.back().type)){ // This is a qg event return {virtual_corrections_Z_qg(event, mur, *AWZH_boson, false)}; } if(is_gluon(in.front().type)){ // This is a gq event return {virtual_corrections_Z_qg(event, mur, *AWZH_boson, true)}; } // This is a qq event return virtual_corrections_Z_qq(event, mur, *AWZH_boson); } assert(std::is_sorted(out.begin(), out.end(), rapidity_less{})); assert(out.size() >= 2); assert(pa.pz() < pb.pz()); fastjet::PseudoJet q = pa - out[0].p; std::size_t first_idx = 0; std::size_t last_idx = out.size() - 1; - // if there is a Higgs boson, extremal qqx or unordered gluon + // if there is a Higgs boson, extremal qqbar or unordered gluon // outside the extremal partons then it is not part of the FKL // ladder and does not contribute to the virtual corrections if((out.front().type == pid::Higgs) || event.type() == event_type::unob - || event.type() == event_type::qqxexb){ + || event.type() == event_type::qqbar_exb){ q -= out[1].p; ++first_idx; } if((out.back().type == pid::Higgs) || event.type() == event_type::unof - || event.type() == event_type::qqxexf){ + || event.type() == event_type::qqbar_exf){ --last_idx; } - std::size_t first_idx_qqx = last_idx; - std::size_t last_idx_qqx = last_idx; + std::size_t first_idx_qqbar = last_idx; + std::size_t last_idx_qqbar = last_idx; - //if qqxMid event, virtual correction do not occur between - //qqx pair. - if(event.type() == event_type::qqxmid){ + //if central qqbar event, virtual correction do not occur between q-qbar. + if(event.type() == event_type::qqbar_mid){ const auto backquark = std::find_if( begin(out) + 1, end(out) - 1 , [](Particle const & s){ return (s.type != pid::gluon && is_parton(s.type)); } ); if(backquark == end(out) || (backquark+1)->type==pid::gluon) return {0.}; last_idx = std::distance(begin(out), backquark); - first_idx_qqx = last_idx+1; + first_idx_qqbar = last_idx+1; } double exponent = 0; const double alpha_s = alpha_s_(mur); for(std::size_t j = first_idx; j < last_idx; ++j){ exponent += omega0(alpha_s, mur, q)*( out[j+1].rapidity() - out[j].rapidity() ); q -= out[j+1].p; } - if (last_idx != first_idx_qqx) q -= out[last_idx+1].p; + if (last_idx != first_idx_qqbar) q -= out[last_idx+1].p; - for(std::size_t j = first_idx_qqx; j < last_idx_qqx; ++j){ + for(std::size_t j = first_idx_qqbar; j < last_idx_qqbar; ++j){ exponent += omega0(alpha_s, mur, q)*( out[j+1].rapidity() - out[j].rapidity() ); q -= out[j+1].p; } assert( nearby(q, -1*pb, norm) || out.back().type == pid::Higgs || event.type() == event_type::unof - || event.type() == event_type::qqxexf + || event.type() == event_type::qqbar_exf ); return {std::exp(exponent)}; } namespace { //! Lipatov vertex for partons emitted into extremal jets CLHEP::HepLorentzVector CLipatov( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2 ) { const CLHEP::HepLorentzVector p5 = qav-qbv; const CLHEP::HepLorentzVector CL = -(qav+qbv) + p1*(qav.m2()/p5.dot(p1) + 2.*p5.dot(p2)/p1.dot(p2)) - p2*(qbv.m2()/p5.dot(p2) + 2.*p5.dot(p1)/p1.dot(p2)); return CL; } double C2Lipatov( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2 ){ const CLHEP::HepLorentzVector CL = CLipatov(qav, qbv, p1, p2); return -CL.dot(CL); } //! Lipatov vertex with soft subtraction for partons emitted into extremal jets double C2Lipatovots( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2, const double lambda ) { const double Cls=(C2Lipatov(qav, qbv, p1, p2)/(qav.m2()*qbv.m2())); const double kperp=(qav-qbv).perp(); if (kperp>lambda) return Cls; return Cls-4./(kperp*kperp); } double C2Lipatov_Mix( CLHEP::HepLorentzVector const & qav_t, CLHEP::HepLorentzVector const & qbv_t, CLHEP::HepLorentzVector const & qav_b, CLHEP::HepLorentzVector const & qbv_b, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2 ) { const CLHEP::HepLorentzVector CL_t = CLipatov(qav_t, qbv_t, p1, p2); const CLHEP::HepLorentzVector CL_b = CLipatov(qav_b, qbv_b, p1, p2); return -CL_t.dot(CL_b); } double C2Lipatovots_Mix( CLHEP::HepLorentzVector const & qav_t, CLHEP::HepLorentzVector const & qbv_t, CLHEP::HepLorentzVector const & qav_b, CLHEP::HepLorentzVector const & qbv_b, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2, const double lambda ) { const double Cls = C2Lipatov_Mix(qav_t, qbv_t, qav_b, qbv_b, p1, p2) / sqrt(qav_t.m2() * qbv_t.m2() * qav_b.m2() * qbv_b.m2()); const double kperp = (qav_t - qbv_t).perp(); if (kperp > lambda){ return Cls; } return Cls - 4.0 / (kperp * kperp); } CLHEP::HepLorentzVector CLipatov( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & pim, CLHEP::HepLorentzVector const & pip, CLHEP::HepLorentzVector const & pom, CLHEP::HepLorentzVector const & pop ){ const CLHEP::HepLorentzVector p5 = qav-qbv; const CLHEP::HepLorentzVector CL = -(qav+qbv) + qav.m2()*(1./p5.dot(pip)*pip + 1./p5.dot(pop)*pop)/2. - qbv.m2()*(1./p5.dot(pim)*pim + 1./p5.dot(pom)*pom)/2. + ( pip*(p5.dot(pim)/pip.dot(pim) + p5.dot(pom)/pip.dot(pom)) + pop*(p5.dot(pim)/pop.dot(pim) + p5.dot(pom)/pop.dot(pom)) - pim*(p5.dot(pip)/pip.dot(pim) + p5.dot(pop)/pop.dot(pim)) - pom*(p5.dot(pip)/pip.dot(pom) + p5.dot(pop)/pop.dot(pom)) )/2.; return CL; } //! Lipatov vertex double C2Lipatov( // B CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & pim, CLHEP::HepLorentzVector const & pip, CLHEP::HepLorentzVector const & pom, CLHEP::HepLorentzVector const & pop ){ const CLHEP::HepLorentzVector CL = CLipatov(qav, qbv, pim, pip, pom, pop); return -CL.dot(CL); } //! Lipatov vertex with soft subtraction double C2Lipatovots( CLHEP::HepLorentzVector const & qav, CLHEP::HepLorentzVector const & qbv, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2, const double lambda ) { const double Cls=(C2Lipatov(qav, qbv, pa, pb, p1, p2)/(qav.m2()*qbv.m2())); const double kperp=(qav-qbv).perp(); if (kperp>lambda) return Cls; return Cls-4./(kperp*kperp); } double C2Lipatov_Mix( CLHEP::HepLorentzVector const & qav_t, CLHEP::HepLorentzVector const & qbv_t, CLHEP::HepLorentzVector const & qav_b, CLHEP::HepLorentzVector const & qbv_b, CLHEP::HepLorentzVector const & pim, CLHEP::HepLorentzVector const & pip, CLHEP::HepLorentzVector const & pom, CLHEP::HepLorentzVector const & pop ) { const CLHEP::HepLorentzVector CL_t = CLipatov(qav_t, qbv_t, pim, pip, pom, pop); const CLHEP::HepLorentzVector CL_b = CLipatov(qav_b, qbv_b, pim, pip, pom, pop); return -CL_t.dot(CL_b); } double C2Lipatovots_Mix( CLHEP::HepLorentzVector const & qav_t, CLHEP::HepLorentzVector const & qbv_t, CLHEP::HepLorentzVector const & qav_b, CLHEP::HepLorentzVector const & qbv_b, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & p2, const double lambda ) { const double Cls = C2Lipatov_Mix(qav_t, qbv_t, qav_b, qbv_b, pa, pb, p1, p2) / sqrt(qav_t.m2() * qbv_t.m2() * qav_b.m2() * qbv_b.m2()); const double kperp = (qav_t - qbv_t).perp(); if (kperp > lambda) { return Cls; } return Cls - 4.0 / (kperp * kperp); } /** Matrix element squared for tree-level current-current scattering * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pg Unordered gluon momentum * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @returns ME Squared for Tree-Level Current-Current Scattering * * @note The unof contribution can be calculated by reversing the argument ordering. */ double ME_uno_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa ){ using namespace currents; assert(aptype!=pid::gluon); // aptype cannot be gluon if (bptype==pid::gluon) { if (is_quark(aptype)) return ME_unob_qg(pg,p1,pa,pn,pb); return ME_unob_qbarg(pg,p1,pa,pn,pb); } if (is_antiquark(bptype)) { if (is_quark(aptype)) return ME_unob_qQbar(pg,p1,pa,pn,pb); return ME_unob_qbarQbar(pg,p1,pa,pn,pb); } //bptype == quark if (is_quark(aptype)) return ME_unob_qQ(pg,p1,pa,pn,pb); return ME_unob_qbarQ(pg,p1,pa,pn,pb); } /** Matrix element squared for tree-level current-current scattering * @param bptype Particle b PDG ID * @param pgin Incoming gluon momentum * @param pq Quark from splitting Momentum * @param pqbar Anti-quark from splitting Momentum * @param pn Particle n Momentum * @param pb Particle b Momentum - * @param swap_q_qx Boolean. Ordering of qqbar pair. False: pqbar extremal. + * @param swap_qqbar Ordering of qqbar pair. False: pqbar extremal. * @returns ME Squared for Tree-Level Current-Current Scattering * - * @note The qqxf contribution can be calculated by reversing the argument ordering. + * @note The forward qqbar contribution can be calculated by reversing the + * argument ordering. */ - double ME_qqx_current( + double ME_qqbar_current( ParticleID bptype, CLHEP::HepLorentzVector const & pgin, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, - bool const swap_q_qx + bool const swap_qqbar ){ using namespace currents; if (bptype==pid::gluon) { - if (swap_q_qx) // pq extremal - return ME_Exqqx_qqbarg(pgin,pq,pqbar,pn,pb); + if (swap_qqbar) // pq extremal + return ME_Exqqbar_qqbarg(pgin,pq,pqbar,pn,pb); // pqbar extremal - return ME_Exqqx_qbarqg(pgin,pq,pqbar,pn,pb); + return ME_Exqqbar_qbarqg(pgin,pq,pqbar,pn,pb); } // b leg quark line - if (swap_q_qx) //extremal pq - return ME_Exqqx_qqbarQ(pgin,pq,pqbar,pn,pb); - return ME_Exqqx_qbarqQ(pgin,pq,pqbar,pn,pb); + if (swap_qqbar) //extremal pq + return ME_Exqqbar_qqbarQ(pgin,pq,pqbar,pn,pb); + return ME_Exqqbar_qbarqQ(pgin,pq,pqbar,pn,pb); } - /* \brief Matrix element squared for central qqx tree-level current-current + /* \brief Matrix element squared for central qqbar tree-level current-current * scattering * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID - * @param nabove Number of gluons emitted before central qqxpair - * @param nbelow Number of gluons emitted after central qqxpair + * @param nabove Number of gluons emitted before central qqbarpair + * @param nbelow Number of gluons emitted after central qqbarpair * @param pa Initial state a Momentum * @param pb Initial state b Momentum * @param pq Final state qbar Momentum * @param pqbar Final state q Momentum * @param partons Vector of all outgoing partons - * @returns ME Squared for qqxmid Tree-Level Current-Current Scattering + * @returns ME Squared for qqbar_mid Tree-Level Current-Current Scattering */ - double ME_qqxmid_current( + double ME_qqbar_mid_current( ParticleID aptype, ParticleID bptype, int nabove, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, std::vector const & partons ){ using namespace currents; - // CAM factors for the qqx amps, and qqbar ordering (default, pq backwards) - const bool swap_q_qx=pqbar.rapidity() < pq.rapidity(); + // CAM factors for the qqbar amps, and qqbar ordering (default, pq backwards) + const bool swap_qqbar=pqbar.rapidity() < pq.rapidity(); double wt=1.; if (aptype==pid::gluon) wt*=K_g(partons.front(),pa)/C_F; if (bptype==pid::gluon) wt*=K_g(partons.back(),pb)/C_F; - return wt*ME_Cenqqx_qq(pa, pb, partons, is_antiquark(bptype), - is_antiquark(aptype), swap_q_qx, nabove); + return wt*ME_Cenqqbar_qq(pa, pb, partons, is_antiquark(bptype), + is_antiquark(aptype), swap_qqbar, nabove); } /** Matrix element squared for tree-level current-current scattering * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @returns ME Squared for Tree-Level Current-Current Scattering */ double ME_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa ){ using namespace currents; if (aptype==pid::gluon && bptype==pid::gluon) { return ME_gg(pn,pb,p1,pa); } if (aptype==pid::gluon && bptype!=pid::gluon) { if (is_quark(bptype)) return ME_qg(pn,pb,p1,pa); return ME_qbarg(pn,pb,p1,pa); } if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_qg(p1,pa,pn,pb); return ME_qbarg(p1,pa,pn,pb); } // they are both quark if (is_quark(bptype)) { if (is_quark(aptype)) return ME_qQ(pn,pb,p1,pa); return ME_qQbar(pn,pb,p1,pa); } if (is_quark(aptype)) return ME_qQbar(p1,pa,pn,pb); return ME_qbarQbar(pn,pb,p1,pa); } /** Matrix element squared for tree-level current-current scattering With W+Jets * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param wc Boolean. True->W Emitted from b. Else; emitted from leg a * @returns ME Squared for Tree-Level Current-Current Scattering */ double ME_W_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const wc, ParticleProperties const & Wprop ){ using namespace currents; // We know it cannot be gg incoming. assert(!(aptype==pid::gluon && bptype==pid::gluon)); if (aptype==pid::gluon && bptype!=pid::gluon) { if (is_quark(bptype)) return ME_W_qg(pn,plbar,pl,pb,p1,pa,Wprop); return ME_W_qbarg(pn,plbar,pl,pb,p1,pa,Wprop); } if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_W_qg(p1,plbar,pl,pa,pn,pb,Wprop); return ME_W_qbarg(p1,plbar,pl,pa,pn,pb,Wprop); } // they are both quark if (wc){ // emission off b, (first argument pbout) if (is_quark(bptype)) { if (is_quark(aptype)) return ME_W_qQ(pn,plbar,pl,pb,p1,pa,Wprop); return ME_W_qQbar(pn,plbar,pl,pb,p1,pa,Wprop); } if (is_quark(aptype)) return ME_W_qbarQ(pn,plbar,pl,pb,p1,pa,Wprop); return ME_W_qbarQbar(pn,plbar,pl,pb,p1,pa,Wprop); } // emission off a, (first argument paout) if (is_quark(aptype)) { if (is_quark(bptype)) return ME_W_qQ(p1,plbar,pl,pa,pn,pb,Wprop); return ME_W_qQbar(p1,plbar,pl,pa,pn,pb,Wprop); } // a is anti-quark if (is_quark(bptype)) return ME_W_qbarQ(p1,plbar,pl,pa,pn,pb,Wprop); return ME_W_qbarQbar(p1,plbar,pl,pa,pn,pb,Wprop); } /** Matrix element squared for backwards uno tree-level current-current * scattering With W+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param pg Unordered gluon momentum * @param wc Boolean. True->W Emitted from b. Else; emitted from leg a * @returns ME Squared for unob Tree-Level Current-Current Scattering * * @note The unof contribution can be calculated by reversing the argument ordering. */ double ME_W_uno_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const wc, ParticleProperties const & Wprop ){ using namespace currents; // we know they are not both gluons assert(bptype != pid::gluon || aptype != pid::gluon); if (bptype == pid::gluon && aptype != pid::gluon) { // b gluon => W emission off a if (is_quark(aptype)) return ME_Wuno_qg(p1,pa,pn,pb,pg,plbar,pl,Wprop); return ME_Wuno_qbarg(p1,pa,pn,pb,pg,plbar,pl,Wprop); } // they are both quark if (wc) {// emission off b, i.e. b is first current if (is_quark(bptype)){ if (is_quark(aptype)) return ME_W_unob_qQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); return ME_W_unob_qQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } if (is_quark(aptype)) return ME_W_unob_qbarQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); return ME_W_unob_qbarQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } // wc == false, emission off a, i.e. a is first current if (is_quark(aptype)) { if (is_quark(bptype)) //qq return ME_Wuno_qQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); //qqbar return ME_Wuno_qQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } // a is anti-quark if (is_quark(bptype)) //qbarq return ME_Wuno_qbarQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); //qbarqbar return ME_Wuno_qbarQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); } - /** \brief Matrix element squared for backward qqx tree-level current-current + /** \brief Matrix element squared for backward qqbar tree-level current-current * scattering With W+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pa Initial state a Momentum * @param pb Initial state b Momentum * @param pq Final state q Momentum * @param pqbar Final state qbar Momentum * @param pn Final state n Momentum * @param plbar Final state anti-lepton momentum * @param pl Final state lepton momentum - * @param swap_q_qx Boolean. Ordering of qqbar pair. False: pqbar extremal. + * @param swap_qqbar Ordering of qqbar pair. False: pqbar extremal. * @param wc Boolean. True->W Emitted from b. Else; emitted from leg a - * @returns ME Squared for qqxb Tree-Level Current-Current Scattering + * @returns ME Squared for qqbarb Tree-Level Current-Current Scattering * - * @note calculate forwards qqx contribution by reversing argument ordering. + * @note calculate forwards qqbar contribution by reversing argument ordering. */ - double ME_W_qqx_current( + double ME_W_qqbar_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, - bool const swap_q_qx, bool const wc, + bool const swap_qqbar, bool const wc, ParticleProperties const & Wprop ){ using namespace currents; // With qqbar we could have 2 incoming gluons and W Emission if (aptype==pid::gluon && bptype==pid::gluon) { //a gluon, b gluon gg->qqbarWg - // This will be a wqqx emission as there is no other possible W Emission + // This will be a wqqbar emission as there is no other possible W Emission // Site. - if (swap_q_qx) - return ME_WExqqx_qqbarg(pa, pqbar, plbar, pl, pq, pn, pb, Wprop); - return ME_WExqqx_qbarqg(pa, pq, plbar, pl, pqbar, pn, pb, Wprop); + if (swap_qqbar) + return ME_WExqqbar_qqbarg(pa, pqbar, plbar, pl, pq, pn, pb, Wprop); + return ME_WExqqbar_qbarqg(pa, pq, plbar, pl, pqbar, pn, pb, Wprop); } assert(aptype==pid::gluon && bptype!=pid::gluon ); - //a gluon => W emission off b leg or qqx - if (!wc){ // W Emitted from backwards qqx - if (swap_q_qx) - return ME_WExqqx_qqbarQ(pa, pqbar, plbar, pl, pq, pn, pb, Wprop); - return ME_WExqqx_qbarqQ(pa, pq, plbar, pl, pqbar, pn, pb, Wprop); + //a gluon => W emission off b leg or qqbar + if (!wc){ // W Emitted from backwards qqbar + if (swap_qqbar) + return ME_WExqqbar_qqbarQ(pa, pqbar, plbar, pl, pq, pn, pb, Wprop); + return ME_WExqqbar_qbarqQ(pa, pq, plbar, pl, pqbar, pn, pb, Wprop); } // W Must be emitted from forwards leg. - return ME_W_Exqqx_QQq(pb, pa, pn, pq, pqbar, plbar, pl, is_antiquark(bptype), Wprop); + return ME_W_Exqqbar_QQq(pb, pa, pn, pq, pqbar, plbar, pl, is_antiquark(bptype), Wprop); } - /* \brief Matrix element squared for central qqx tree-level current-current + /* \brief Matrix element squared for central qqbar tree-level current-current * scattering With W+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID - * @param nabove Number of gluons emitted before central qqxpair - * @param nbelow Number of gluons emitted after central qqxpair + * @param nabove Number of gluons emitted before central qqbarpair + * @param nbelow Number of gluons emitted after central qqbarpair * @param pa Initial state a Momentum * @param pb Initial state b Momentum\ * @param pq Final state qbar Momentum * @param pqbar Final state q Momentum * @param partons Vector of all outgoing partons * @param plbar Final state anti-lepton momentum * @param pl Final state lepton momentum - * @param wqq Boolean. True siginfies W boson is emitted from Central qqx + * @param wqq Boolean. True siginfies W boson is emitted from Central qqbar * @param wc Boolean. wc=true signifies w boson emitted from leg b; if wqq=false. - * @returns ME Squared for qqxmid Tree-Level Current-Current Scattering + * @returns ME Squared for qqbar_mid Tree-Level Current-Current Scattering */ - double ME_W_qqxmid_current( + double ME_W_qqbar_mid_current( ParticleID aptype, ParticleID bptype, int nabove, int nbelow, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & pq, CLHEP::HepLorentzVector const & pqbar, std::vector const & partons, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, bool const wqq, bool const wc, ParticleProperties const & Wprop ){ using namespace currents; - // CAM factors for the qqx amps, and qqbar ordering (default, pq backwards) - const bool swap_q_qx=pqbar.rapidity() < pq.rapidity(); + // CAM factors for the qqbar amps, and qqbar ordering (default, pq backwards) + const bool swap_qqbar=pqbar.rapidity() < pq.rapidity(); double wt=1.; if (aptype==pid::gluon) wt*=K_g(partons.front(),pa)/C_F; if (bptype==pid::gluon) wt*=K_g(partons.back(),pb)/C_F; if(wqq) - return wt*ME_WCenqqx_qq(pa, pb, pl, plbar, partons, + return wt*ME_WCenqqbar_qq(pa, pb, pl, plbar, partons, is_antiquark(aptype),is_antiquark(bptype), - swap_q_qx, nabove, Wprop); - return wt*ME_W_Cenqqx_qq(pa, pb, pl, plbar, partons, + swap_qqbar, nabove, Wprop); + return wt*ME_W_Cenqqbar_qq(pa, pb, pl, plbar, partons, is_antiquark(aptype), is_antiquark(bptype), - swap_q_qx, nabove, nbelow, wc, Wprop); + swap_qqbar, nabove, nbelow, wc, Wprop); } /** Matrix element squared for tree-level current-current scattering With Z+Jets * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param plbar Final state positron momentum * @param pl Final state electron momentum * @param Zprop Z properties * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) * @returns ME Squared for Tree-Level Current-Current Scattering */ std::vector ME_Z_current( const ParticleID aptype, const ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, ParticleProperties const & Zprop, const double stw2, const double ctw ){ using namespace currents; // we know they are not both gluons assert(!is_gluon(aptype) || !is_gluon(bptype)); if(is_anyquark(aptype) && is_gluon(bptype)){ // This is a qg event return { ME_Z_qg(pa,pb,p1,pn,plbar,pl,aptype,bptype,Zprop,stw2,ctw) }; } if(is_gluon(aptype) && is_anyquark(bptype)){ // This is a gq event return { ME_Z_qg(pb,pa,pn,p1,plbar,pl,bptype,aptype,Zprop,stw2,ctw) }; } assert(is_anyquark(aptype) && is_anyquark(bptype)); // This is a qq event return ME_Z_qQ(pa,pb,p1,pn,plbar,pl,aptype,bptype,Zprop,stw2,ctw); } /** Matrix element squared for backwards uno tree-level current-current * scattering With Z+Jets * * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param pg Unordered gluon momentum * @param plbar Final state positron momentum * @param pl Final state electron momentum * @param Zprop Z properties * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) * @returns ME Squared for unob Tree-Level Current-Current Scattering * * @note The unof contribution can be calculated by reversing the argument ordering. */ std::vector ME_Z_uno_current( const ParticleID aptype, const ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, ParticleProperties const & Zprop, const double stw2, const double ctw ){ using namespace currents; // we know they are not both gluons assert(!is_gluon(aptype) || !is_gluon(bptype)); if (is_anyquark(aptype) && is_gluon(bptype)) { // This is a qg event return { ME_Zuno_qg(pa,pb,pg,p1,pn,plbar,pl,aptype,bptype,Zprop,stw2,ctw) }; } if (is_gluon(aptype) && is_anyquark(bptype)) { // This is a gq event return { ME_Zuno_qg(pb,pa,pg,pn,p1,plbar,pl,bptype,aptype,Zprop,stw2,ctw) }; } assert(is_anyquark(aptype) && is_anyquark(bptype)); // This is a qq event return ME_Zuno_qQ(pa,pb,pg,p1,pn,plbar,pl,aptype,bptype,Zprop,stw2,ctw); } /** \brief Matrix element squared for tree-level current-current scattering with Higgs * @param aptype Particle a PDG ID * @param bptype Particle b PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param qH t-channel momentum before Higgs * @param qHp1 t-channel momentum after Higgs * @returns ME Squared for Tree-Level Current-Current Scattering with Higgs */ double ME_Higgs_current( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & qH, // t-channel momentum before Higgs CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs double mt, bool include_bottom, double mb, double vev ){ using namespace currents; if (aptype==pid::gluon && bptype==pid::gluon) // gg initial state return ME_H_gg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev); if (aptype==pid::gluon&&bptype!=pid::gluon) { if (is_quark(bptype)) return ME_H_qg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4./9.; return ME_H_qbarg(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4./9.; } if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_H_qg(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev)*4./9.; return ME_H_qbarg(p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev)*4./9.; } // they are both quark if (is_quark(bptype)) { if (is_quark(aptype)) return ME_H_qQ(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); return ME_H_qQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); } if (is_quark(aptype)) return ME_H_qbarQ(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); return ME_H_qbarQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); } /** \brief Current matrix element squared with Higgs and unordered backward emission * @param aptype Particle A PDG ID * @param bptype Particle B PDG ID * @param pn Particle n Momentum * @param pb Particle b Momentum * @param pg Unordered back Particle Momentum * @param p1 Particle 1 Momentum * @param pa Particle a Momentum * @param qH t-channel momentum before Higgs * @param qHp1 t-channel momentum after Higgs * @returns ME Squared with Higgs and unordered backward emission * * @note This function assumes unordered gluon backwards from pa-p1 current. * For unof, reverse call order */ double ME_Higgs_current_uno( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pg, CLHEP::HepLorentzVector const & pn, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & qH, // t-channel momentum before Higgs CLHEP::HepLorentzVector const & qHp1, // t-channel momentum after Higgs double mt, bool include_bottom, double mb, double vev ){ using namespace currents; if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_H_unob_gQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); return ME_H_unob_gQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); } // they are both quark if (is_quark(aptype)) { if (is_quark(bptype)) return ME_H_unob_qQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); return ME_H_unob_qbarQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); } if (is_quark(bptype)) return ME_H_unob_qQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); return ME_H_unob_qbarQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); } void validate(MatrixElementConfig const & config) { #ifndef HEJ_BUILD_WITH_QCDLOOP if(!config.Higgs_coupling.use_impact_factors) { throw std::invalid_argument{ "Invalid Higgs coupling settings.\n" "HEJ without QCDloop support can only use impact factors.\n" "Set use_impact_factors to true or recompile HEJ.\n" }; } #endif if(config.Higgs_coupling.use_impact_factors && config.Higgs_coupling.mt != std::numeric_limits::infinity()) { throw std::invalid_argument{ "Conflicting settings: " "impact factors may only be used in the infinite top mass limit" }; } } } // namespace MatrixElement::MatrixElement( std::function alpha_s, MatrixElementConfig conf ): alpha_s_{std::move(alpha_s)}, param_{std::move(conf)} { validate(param_); } std::vector MatrixElement::tree_kin( Event const & ev ) const { if(! is_resummable(ev.type())) return {0.}; auto AWZH_boson = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](Particle const & p){return is_AWZH_boson(p);} ); if(AWZH_boson == end(ev.outgoing())) return {tree_kin_jets(ev)}; switch(AWZH_boson->type){ case pid::Higgs: return {tree_kin_Higgs(ev)}; case pid::Wp: case pid::Wm: return {tree_kin_W(ev)}; case pid::Z_photon_mix: return tree_kin_Z(ev); // TODO case pid::photon: case pid::Z: default: throw not_implemented("Emission of boson of unsupported type"); } } namespace { constexpr int EXTREMAL_JET_IDX = 1; constexpr int NO_EXTREMAL_JET_IDX = 0; bool treat_as_extremal(Particle const & parton){ return parton.p.user_index() == EXTREMAL_JET_IDX; } template double FKL_ladder_weight( InputIterator begin_gluon, InputIterator end_gluon, CLHEP::HepLorentzVector const & q0, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pn, double lambda ){ double wt = 1; auto qi = q0; for(auto gluon_it = begin_gluon; gluon_it != end_gluon; ++gluon_it){ assert(gluon_it->type == pid::gluon); const auto g = to_HepLorentzVector(*gluon_it); const auto qip1 = qi - g; if(treat_as_extremal(*gluon_it)){ wt *= C2Lipatovots(qip1, qi, pa, pb, lambda)*C_A; } else{ wt *= C2Lipatovots(qip1, qi, pa, pb, p1, pn, lambda)*C_A; } qi = qip1; } return wt; } template std::vector FKL_ladder_weight_mix( InputIterator begin_gluon, InputIterator end_gluon, CLHEP::HepLorentzVector const & q0_t, CLHEP::HepLorentzVector const & q0_b, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, CLHEP::HepLorentzVector const & p1, CLHEP::HepLorentzVector const & pn, const double lambda ){ double wt_top = 1; double wt_bot = 1; double wt_mix = 1; auto qi_t = q0_t; auto qi_b = q0_b; for(auto gluon_it = begin_gluon; gluon_it != end_gluon; ++gluon_it){ assert(gluon_it->type == pid::gluon); const auto g = to_HepLorentzVector(*gluon_it); const auto qip1_t = qi_t - g; const auto qip1_b = qi_b - g; if(treat_as_extremal(*gluon_it)){ wt_top *= C2Lipatovots(qip1_t, qi_t, pa, pb, lambda)*C_A; wt_bot *= C2Lipatovots(qip1_b, qi_b, pa, pb, lambda)*C_A; wt_mix *= C2Lipatovots_Mix(qip1_t, qi_t, qip1_b, qi_b, pa, pb, lambda)*C_A; } else{ wt_top *= C2Lipatovots(qip1_t, qi_t, pa, pb, p1, pn, lambda)*C_A; wt_bot *= C2Lipatovots(qip1_b, qi_b, pa, pb, p1, pn, lambda)*C_A; wt_mix *= C2Lipatovots_Mix(qip1_t, qi_t, qip1_b, qi_b, pa, pb, p1, pn, lambda)*C_A; } qi_t = qip1_t; qi_b = qip1_b; } return {wt_top, wt_bot, wt_mix}; } std::vector tag_extremal_jet_partons( Event const & ev ){ auto out_partons = filter_partons(ev.outgoing()); if(out_partons.size() == ev.jets().size()){ // no additional emissions in extremal jets, don't need to tag anything for(auto & parton: out_partons){ parton.p.set_user_index(NO_EXTREMAL_JET_IDX); } return out_partons; } auto const & jets = ev.jets(); assert(jets.size() >= 2); auto most_backward = begin(jets); auto most_forward = end(jets) - 1; - // skip jets caused by unordered emission or qqx - if(ev.type() == event_type::unob || ev.type() == event_type::qqxexb){ + // skip jets caused by unordered emission or qqbar + if(ev.type() == event_type::unob || ev.type() == event_type::qqbar_exb){ assert(jets.size() >= 3); ++most_backward; } - else if(ev.type() == event_type::unof || ev.type() == event_type::qqxexf){ + else if(ev.type() == event_type::unof || ev.type() == event_type::qqbar_exf){ assert(jets.size() >= 3); --most_forward; } const auto extremal_jet_indices = ev.particle_jet_indices( {*most_backward, *most_forward} ); assert(extremal_jet_indices.size() == out_partons.size()); for(std::size_t i = 0; i < out_partons.size(); ++i){ assert(is_parton(out_partons[i])); const int idx = (extremal_jet_indices[i]>=0)? EXTREMAL_JET_IDX: NO_EXTREMAL_JET_IDX; out_partons[i].p.set_user_index(idx); } return out_partons; } - double tree_kin_jets_qqxmid( + double tree_kin_jets_qqbar_mid( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, std::vector const & partons, double lambda ){ CLHEP::HepLorentzVector pq; CLHEP::HepLorentzVector pqbar; const auto backmidquark = std::find_if( begin(partons)+1, end(partons)-1, [](Particle const & s){ return s.type != pid::gluon; } ); assert(backmidquark!=end(partons)-1); if (is_quark(backmidquark->type)){ pq = to_HepLorentzVector(*backmidquark); pqbar = to_HepLorentzVector(*(backmidquark+1)); } else { pqbar = to_HepLorentzVector(*backmidquark); pq = to_HepLorentzVector(*(backmidquark+1)); } auto p1 = to_HepLorentzVector(partons[0]); auto pn = to_HepLorentzVector(partons[partons.size() - 1]); auto q0 = pa - p1; - // t-channel momentum after qqx - auto qqxt = q0; + // t-channel momentum after qqbar + auto qqbart = q0; const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder_1 = (backmidquark); const auto begin_ladder_2 = (backmidquark+2); const auto end_ladder = cend(partons) - 1; for(auto parton_it = begin_ladder; parton_it < begin_ladder_2; ++parton_it){ - qqxt -= to_HepLorentzVector(*parton_it); + qqbart -= to_HepLorentzVector(*parton_it); } const int nabove = std::distance(begin_ladder, backmidquark); std::vector partonsHLV; partonsHLV.reserve(partons.size()); for (std::size_t i = 0; i != partons.size(); ++i) { partonsHLV.push_back(to_HepLorentzVector(partons[i])); } - const double current_factor = ME_qqxmid_current( + const double current_factor = ME_qqbar_mid_current( aptype, bptype, nabove, pa, pb, pq, pqbar, partonsHLV ); const double ladder_factor = FKL_ladder_weight( begin_ladder, end_ladder_1, q0, pa, pb, p1, pn, lambda )*FKL_ladder_weight( begin_ladder_2, end_ladder, - qqxt, pa, pb, p1, pn, + qqbart, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template - double tree_kin_jets_qqx(InIter BeginIn, InIter EndIn, partIter BeginPart, - partIter EndPart, double lambda){ - const bool swap_q_qx = is_quark(*BeginPart); + double tree_kin_jets_qqbar(InIter BeginIn, InIter EndIn, partIter BeginPart, + partIter EndPart, double lambda){ + const bool swap_qqbar = is_quark(*BeginPart); const auto pgin = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(EndIn-1)); - const auto pq = to_HepLorentzVector(*(BeginPart+(swap_q_qx?0:1))); - const auto pqbar = to_HepLorentzVector(*(BeginPart+(swap_q_qx?1:0))); + const auto pq = to_HepLorentzVector(*(BeginPart+(swap_qqbar?0:1))); + const auto pqbar = to_HepLorentzVector(*(BeginPart+(swap_qqbar?1:0))); const auto p1 = to_HepLorentzVector(*(BeginPart)); const auto pn = to_HepLorentzVector(*(EndPart-1)); assert((BeginIn)->type==pid::gluon); // Incoming a must be gluon. - const double current_factor = ME_qqx_current( - (EndIn-1)->type, pgin, pq, pqbar, pn, pb, swap_q_qx + const double current_factor = ME_qqbar_current( + (EndIn-1)->type, pgin, pq, pqbar, pn, pb, swap_qqbar )/(4.*(N_C*N_C - 1.)); const double ladder_factor = FKL_ladder_weight( (BeginPart+2), (EndPart-1), pgin-pq-pqbar, pgin, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template double tree_kin_jets_uno(InIter BeginIn, InIter EndIn, partIter BeginPart, partIter EndPart, double lambda ){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(EndIn-1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); const double current_factor = ME_uno_current( (BeginIn)->type, (EndIn-1)->type, pg, pn, pb, p1, pa ); const double ladder_factor = FKL_ladder_weight( (BeginPart+2), (EndPart-1), pa-p1-pg, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } } // namespace double MatrixElement::tree_kin_jets(Event const & ev) const { auto const & incoming = ev.incoming(); const auto partons = tag_extremal_jet_partons(ev); if (ev.type()==event_type::FKL){ const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto p1 = to_HepLorentzVector(partons.front()); const auto pn = to_HepLorentzVector(partons.back()); return ME_current( incoming[0].type, incoming[1].type, pn, pb, p1, pa )/(4.*(N_C*N_C - 1.))*FKL_ladder_weight( begin(partons) + 1, end(partons) - 1, pa - p1, pa, pb, p1, pn, param_.regulator_lambda ); } if (ev.type()==event_type::unordered_backward){ return tree_kin_jets_uno(incoming.begin(), incoming.end(), partons.begin(), partons.end(), param_.regulator_lambda); } if (ev.type()==event_type::unordered_forward){ return tree_kin_jets_uno(incoming.rbegin(), incoming.rend(), partons.rbegin(), partons.rend(), param_.regulator_lambda); } - if (ev.type()==event_type::extremal_qqxb){ - return tree_kin_jets_qqx(incoming.begin(), incoming.end(), - partons.begin(), partons.end(), - param_.regulator_lambda); - } - if (ev.type()==event_type::extremal_qqxf){ - return tree_kin_jets_qqx(incoming.rbegin(), incoming.rend(), - partons.rbegin(), partons.rend(), - param_.regulator_lambda); - } - if (ev.type()==event_type::central_qqx){ - return tree_kin_jets_qqxmid(incoming[0].type, incoming[1].type, - to_HepLorentzVector(incoming[0]), - to_HepLorentzVector(incoming[1]), - partons, param_.regulator_lambda); + if (ev.type()==event_type::extremal_qqbar_backward){ + return tree_kin_jets_qqbar(incoming.begin(), incoming.end(), + partons.begin(), partons.end(), + param_.regulator_lambda); + } + if (ev.type()==event_type::extremal_qqbar_forward){ + return tree_kin_jets_qqbar(incoming.rbegin(), incoming.rend(), + partons.rbegin(), partons.rend(), + param_.regulator_lambda); + } + if (ev.type()==event_type::central_qqbar){ + return tree_kin_jets_qqbar_mid(incoming[0].type, incoming[1].type, + to_HepLorentzVector(incoming[0]), + to_HepLorentzVector(incoming[1]), + partons, param_.regulator_lambda); } throw std::logic_error("Cannot reweight non-resummable processes in Pure Jets"); } namespace { double tree_kin_W_FKL( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, std::vector const & partons, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, double lambda, ParticleProperties const & Wprop ){ auto p1 = to_HepLorentzVector(partons[0]); auto pn = to_HepLorentzVector(partons[partons.size() - 1]); const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder = cend(partons) - 1; bool wc = aptype==partons[0].type; //leg b emits w auto q0 = pa - p1; if(!wc) q0 -= pl + plbar; const double current_factor = ME_W_current( aptype, bptype, pn, pb, p1, pa, plbar, pl, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( begin_ladder, end_ladder, q0, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template double tree_kin_W_uno(InIter BeginIn, partIter BeginPart, partIter EndPart, const CLHEP::HepLorentzVector & plbar, const CLHEP::HepLorentzVector & pl, double lambda, ParticleProperties const & Wprop ){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(BeginIn+1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); bool wc = (BeginIn)->type==(BeginPart+1)->type; //leg b emits w auto q0 = pa - p1 - pg; if(!wc) q0 -= pl + plbar; const double current_factor = ME_W_uno_current( (BeginIn)->type, (BeginIn+1)->type, pn, pb, p1, pa, pg, plbar, pl, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } template - double tree_kin_W_qqx(InIter BeginIn, partIter BeginPart, - partIter EndPart, - const CLHEP::HepLorentzVector & plbar, - const CLHEP::HepLorentzVector & pl, - double lambda, ParticleProperties const & Wprop + double tree_kin_W_qqbar(InIter BeginIn, partIter BeginPart, + partIter EndPart, + const CLHEP::HepLorentzVector & plbar, + const CLHEP::HepLorentzVector & pl, + double lambda, ParticleProperties const & Wprop ){ - const bool swap_q_qx=is_quark(*BeginPart); + const bool swap_qqbar=is_quark(*BeginPart); const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(BeginIn+1)); - const auto pq = to_HepLorentzVector(*(BeginPart+(swap_q_qx?0:1))); - const auto pqbar = to_HepLorentzVector(*(BeginPart+(swap_q_qx?1:0))); + const auto pq = to_HepLorentzVector(*(BeginPart+(swap_qqbar?0:1))); + const auto pqbar = to_HepLorentzVector(*(BeginPart+(swap_qqbar?1:0))); const auto p1 = to_HepLorentzVector(*(BeginPart)); const auto pn = to_HepLorentzVector(*(EndPart-1)); const bool wc = (BeginIn+1)->type!=(EndPart-1)->type; //leg b emits w auto q0 = pa - pq - pqbar; if(!wc) q0 -= pl + plbar; - const double current_factor = ME_W_qqx_current( + const double current_factor = ME_W_qqbar_current( (BeginIn)->type, (BeginIn+1)->type, pa, pb, - pq, pqbar, pn, plbar, pl, swap_q_qx, wc, Wprop + pq, pqbar, pn, plbar, pl, swap_qqbar, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda ); return current_factor*ladder_factor; } - double tree_kin_W_qqxmid( + double tree_kin_W_qqbar_mid( ParticleID aptype, ParticleID bptype, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, std::vector const & partons, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, double lambda, ParticleProperties const & Wprop ){ CLHEP::HepLorentzVector pq; CLHEP::HepLorentzVector pqbar; const auto backmidquark = std::find_if( begin(partons)+1, end(partons)-1, [](Particle const & s){ return s.type != pid::gluon; } ); assert(backmidquark!=end(partons)-1); if (is_quark(backmidquark->type)){ pq = to_HepLorentzVector(*backmidquark); pqbar = to_HepLorentzVector(*(backmidquark+1)); } else { pqbar = to_HepLorentzVector(*backmidquark); pq = to_HepLorentzVector(*(backmidquark+1)); } auto p1 = to_HepLorentzVector(partons.front()); auto pn = to_HepLorentzVector(partons.back()); auto q0 = pa - p1; - // t-channel momentum after qqx - auto qqxt = q0; + // t-channel momentum after qqbar + auto qqbart = q0; - bool wqq = backmidquark->type != -(backmidquark+1)->type; // qqx emit W + bool wqq = backmidquark->type != -(backmidquark+1)->type; // qqbar emit W bool wc = !wqq && (aptype==partons.front().type); //leg b emits w assert(!wqq || !wc); - if(wqq){ // emission from qqx - qqxt -= pl + plbar; + if(wqq){ // emission from qqbar + qqbart -= pl + plbar; } else if(!wc) { // emission from leg a q0 -= pl + plbar; - qqxt -= pl + plbar; + qqbart -= pl + plbar; } const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder_1 = (backmidquark); const auto begin_ladder_2 = (backmidquark+2); const auto end_ladder = cend(partons) - 1; for(auto parton_it = begin_ladder; parton_it < begin_ladder_2; ++parton_it){ - qqxt -= to_HepLorentzVector(*parton_it); + qqbart -= to_HepLorentzVector(*parton_it); } const int nabove = std::distance(begin_ladder, backmidquark); const int nbelow = std::distance(begin_ladder_2, end_ladder); std::vector partonsHLV; partonsHLV.reserve(partons.size()); for (std::size_t i = 0; i != partons.size(); ++i) { partonsHLV.push_back(to_HepLorentzVector(partons[i])); } - const double current_factor = ME_W_qqxmid_current( + const double current_factor = ME_W_qqbar_mid_current( aptype, bptype, nabove, nbelow, pa, pb, pq, pqbar, partonsHLV, plbar, pl, wqq, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( begin_ladder, end_ladder_1, q0, pa, pb, p1, pn, lambda )*FKL_ladder_weight( begin_ladder_2, end_ladder, - qqxt, pa, pb, p1, pn, + qqbart, pa, pb, p1, pn, lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } } // namespace double MatrixElement::tree_kin_W(Event const & ev) const { using namespace event_type; auto const & incoming(ev.incoming()); #ifndef NDEBUG // assert that there is exactly one decay corresponding to the W assert(ev.decays().size() == 1); auto const & w_boson{ std::find_if(ev.outgoing().cbegin(), ev.outgoing().cend(), [] (Particle const & p) -> bool { return std::abs(p.type) == ParticleID::Wp; }) }; assert(w_boson != ev.outgoing().cend()); assert( static_cast(ev.decays().cbegin()->first) == std::distance(ev.outgoing().cbegin(), w_boson) ); #endif // find decay products of W auto const & decay{ ev.decays().cbegin()->second }; assert(decay.size() == 2); assert( ( is_anylepton(decay.at(0)) && is_anyneutrino(decay.at(1)) ) || ( is_anylepton(decay.at(1)) && is_anyneutrino(decay.at(0)) ) ); // get lepton & neutrino CLHEP::HepLorentzVector plbar; CLHEP::HepLorentzVector pl; if (decay.at(0).type < 0){ plbar = to_HepLorentzVector(decay.at(0)); pl = to_HepLorentzVector(decay.at(1)); } else{ pl = to_HepLorentzVector(decay.at(0)); plbar = to_HepLorentzVector(decay.at(1)); } const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto partons = tag_extremal_jet_partons(ev); if(ev.type() == FKL){ return tree_kin_W_FKL(incoming[0].type, incoming[1].type, pa, pb, partons, plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } if(ev.type() == unordered_backward){ return tree_kin_W_uno(cbegin(incoming), cbegin(partons), cend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } if(ev.type() == unordered_forward){ return tree_kin_W_uno(crbegin(incoming), crbegin(partons), crend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Wprop()); } - if(ev.type() == extremal_qqxb){ - return tree_kin_W_qqx(cbegin(incoming), cbegin(partons), - cend(partons), plbar, pl, - param_.regulator_lambda, - param_.ew_parameters.Wprop()); - } - if(ev.type() == extremal_qqxf){ - return tree_kin_W_qqx(crbegin(incoming), crbegin(partons), - crend(partons), plbar, pl, - param_.regulator_lambda, - param_.ew_parameters.Wprop()); - } - assert(ev.type() == central_qqx); - return tree_kin_W_qqxmid(incoming[0].type, incoming[1].type, - pa, pb, partons, plbar, pl, - param_.regulator_lambda, - param_.ew_parameters.Wprop()); + if(ev.type() == extremal_qqbar_backward){ + return tree_kin_W_qqbar(cbegin(incoming), cbegin(partons), + cend(partons), plbar, pl, + param_.regulator_lambda, + param_.ew_parameters.Wprop()); + } + if(ev.type() == extremal_qqbar_forward){ + return tree_kin_W_qqbar(crbegin(incoming), crbegin(partons), + crend(partons), plbar, pl, + param_.regulator_lambda, + param_.ew_parameters.Wprop()); + } + assert(ev.type() == central_qqbar); + return tree_kin_W_qqbar_mid(incoming[0].type, incoming[1].type, + pa, pb, partons, plbar, pl, + param_.regulator_lambda, + param_.ew_parameters.Wprop()); } namespace { std::vector tree_kin_Z_FKL( const ParticleID aptype, const ParticleID bptype, CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, std::vector const & partons, CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, const double lambda, ParticleProperties const & Zprop, const double stw2, const double ctw ){ const auto p1 = to_HepLorentzVector(partons[0]); const auto pn = to_HepLorentzVector(partons[partons.size() - 1]); const auto begin_ladder = cbegin(partons) + 1; const auto end_ladder = cend(partons) - 1; const std::vector current_factor = ME_Z_current( aptype, bptype, pn, pb, p1, pa, plbar, pl, Zprop, stw2, ctw ); std::vector ladder_factor; if(is_gluon(bptype)){ // This is a qg event const auto q0 = pa-p1-plbar-pl; ladder_factor.push_back(FKL_ladder_weight(begin_ladder, end_ladder, q0, pa, pb, p1, pn, lambda)); } else if(is_gluon(aptype)){ // This is a gq event const auto q0 = pa-p1; ladder_factor.push_back(FKL_ladder_weight(begin_ladder, end_ladder, q0, pa, pb, p1, pn, lambda)); } else { // This is a qq event const auto q0 = pa-p1-plbar-pl; const auto q1 = pa-p1; ladder_factor=FKL_ladder_weight_mix(begin_ladder, end_ladder, q0, q1, pa, pb, p1, pn, lambda); } std::vector result; for(size_t i=0; i std::vector tree_kin_Z_uno(InIter BeginIn, partIter BeginPart, partIter EndPart, const CLHEP::HepLorentzVector & plbar, const CLHEP::HepLorentzVector & pl, const double lambda, ParticleProperties const & Zprop, const double stw2, const double ctw){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(BeginIn+1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); const ParticleID aptype = (BeginIn)->type; const ParticleID bptype = (BeginIn+1)->type; const std::vector current_factor = ME_Z_uno_current( aptype, bptype, pn, pb, p1, pa, pg, plbar, pl, Zprop, stw2, ctw ); std::vector ladder_factor; if(is_gluon(bptype)){ // This is a qg event const auto q0 = pa-pg-p1-plbar-pl; ladder_factor.push_back(FKL_ladder_weight(BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda)); }else if(is_gluon(aptype)){ // This is a gq event const auto q0 = pa-pg-p1; ladder_factor.push_back(FKL_ladder_weight(BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda)); }else{ // This is a qq event const auto q0 = pa-pg-p1-plbar-pl; const auto q1 = pa-pg-p1; ladder_factor=FKL_ladder_weight_mix(BeginPart+2, EndPart-1, q0, q1, pa, pb, p1, pn, lambda); } std::vector result; for(size_t i=0; i MatrixElement::tree_kin_Z(Event const & ev) const { using namespace event_type; auto const & incoming(ev.incoming()); // find decay products of Z auto const & decay{ ev.decays().cbegin()->second }; assert(decay.size() == 2); assert(is_anylepton(decay.at(0)) && !is_anyneutrino(decay.at(0)) && decay.at(0).type==-decay.at(1).type); // get leptons CLHEP::HepLorentzVector plbar; CLHEP::HepLorentzVector pl; if (decay.at(0).type < 0){ plbar = to_HepLorentzVector(decay.at(0)); pl = to_HepLorentzVector(decay.at(1)); } else{ pl = to_HepLorentzVector(decay.at(0)); plbar = to_HepLorentzVector(decay.at(1)); } const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto partons = tag_extremal_jet_partons(ev); const double stw2 = param_.ew_parameters.sin2_tw(); const double ctw = param_.ew_parameters.cos_tw(); if(ev.type() == FKL){ return tree_kin_Z_FKL(incoming[0].type, incoming[1].type, pa, pb, partons, plbar, pl, param_.regulator_lambda, param_.ew_parameters.Zprop(), stw2, ctw); } if(ev.type() == unordered_backward){ return tree_kin_Z_uno(cbegin(incoming), cbegin(partons), cend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Zprop(), stw2, ctw); } if(ev.type() == unordered_forward){ return tree_kin_Z_uno(crbegin(incoming), crbegin(partons), crend(partons), plbar, pl, param_.regulator_lambda, param_.ew_parameters.Zprop(), stw2, ctw); } throw std::logic_error("Can only reweight FKL or uno processes in Z+Jets"); } double MatrixElement::tree_kin_Higgs(Event const & ev) const { if(is_uno(ev.type())){ return tree_kin_Higgs_between(ev); } if(ev.outgoing().front().type == pid::Higgs){ return tree_kin_Higgs_first(ev); } if(ev.outgoing().back().type == pid::Higgs){ return tree_kin_Higgs_last(ev); } return tree_kin_Higgs_between(ev); } namespace { // Colour acceleration multipliers, for gluons see eq. (7) in arXiv:0910.5113 #ifdef HEJ_BUILD_WITH_QCDLOOP double K( ParticleID type, CLHEP::HepLorentzVector const & pout, CLHEP::HepLorentzVector const & pin ){ if(type == pid::gluon) return currents::K_g(pout, pin); return C_F; } #endif // Colour factor in strict MRK limit double K_MRK(ParticleID type) { return (type == pid::gluon)?C_A:C_F; } } // namespace double MatrixElement::MH2_forwardH( CLHEP::HepLorentzVector const & p1out, CLHEP::HepLorentzVector const & p1in, ParticleID type2, CLHEP::HepLorentzVector const & p2out, CLHEP::HepLorentzVector const & p2in, CLHEP::HepLorentzVector const & pH, double t1, double t2 ) const{ using namespace currents; ignore(p2out, p2in); const double shat = p1in.invariantMass2(p2in); const double vev = param_.ew_parameters.vev(); // gluon case #ifdef HEJ_BUILD_WITH_QCDLOOP if(!param_.Higgs_coupling.use_impact_factors){ return K(type2, p2out, p2in)*C_A*1./(16*M_PI*M_PI)*t1/t2*ME_Houtside_gq( p1out, p1in, p2out, p2in, pH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, vev )/(4*(N_C*N_C - 1)); } #endif return K_MRK(type2)/C_A*9./2.*shat*shat*( C2gHgp(p1in,p1out,pH,vev) + C2gHgm(p1in,p1out,pH,vev) )/(t1*t2); } double MatrixElement::tree_kin_Higgs_first(Event const & ev) const { auto const & incoming = ev.incoming(); auto const & outgoing = ev.outgoing(); assert(outgoing.front().type == pid::Higgs); if(outgoing[1].type != pid::gluon) { assert(incoming.front().type == outgoing[1].type); return tree_kin_Higgs_between(ev); } const auto pH = to_HepLorentzVector(outgoing.front()); const auto partons = tag_extremal_jet_partons( ev ); const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); const auto p1 = to_HepLorentzVector(partons.front()); const auto pn = to_HepLorentzVector(partons.back()); const auto q0 = pa - p1 - pH; const double t1 = q0.m2(); const double t2 = (pn - pb).m2(); return MH2_forwardH( p1, pa, incoming[1].type, pn, pb, pH, t1, t2 )*FKL_ladder_weight( begin(partons) + 1, end(partons) - 1, q0, pa, pb, p1, pn, param_.regulator_lambda ); } double MatrixElement::tree_kin_Higgs_last(Event const & ev) const { auto const & incoming = ev.incoming(); auto const & outgoing = ev.outgoing(); assert(outgoing.back().type == pid::Higgs); if(outgoing[outgoing.size()-2].type != pid::gluon) { assert(incoming.back().type == outgoing[outgoing.size()-2].type); return tree_kin_Higgs_between(ev); } const auto pH = to_HepLorentzVector(outgoing.back()); const auto partons = tag_extremal_jet_partons( ev ); const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); auto p1 = to_HepLorentzVector(partons.front()); const auto pn = to_HepLorentzVector(partons.back()); auto q0 = pa - p1; const double t1 = q0.m2(); const double t2 = (pn + pH - pb).m2(); return MH2_forwardH( pn, pb, incoming[0].type, p1, pa, pH, t2, t1 )*FKL_ladder_weight( begin(partons) + 1, end(partons) - 1, q0, pa, pb, p1, pn, param_.regulator_lambda ); } namespace { template double tree_kin_Higgs_uno(InIter BeginIn, InIter EndIn, partIter BeginPart, partIter EndPart, CLHEP::HepLorentzVector const & qH, CLHEP::HepLorentzVector const & qHp1, double mt, bool inc_bot, double mb, double vev ){ const auto pa = to_HepLorentzVector(*BeginIn); const auto pb = to_HepLorentzVector(*(EndIn-1)); const auto pg = to_HepLorentzVector(*BeginPart); const auto p1 = to_HepLorentzVector(*(BeginPart+1)); const auto pn = to_HepLorentzVector(*(EndPart-1)); return ME_Higgs_current_uno( (BeginIn)->type, (EndIn-1)->type, pg, pn, pb, p1, pa, qH, qHp1, mt, inc_bot, mb, vev ); } } // namespace double MatrixElement::tree_kin_Higgs_between(Event const & ev) const { using namespace event_type; auto const & incoming = ev.incoming(); auto const & outgoing = ev.outgoing(); const auto the_Higgs = std::find_if( begin(outgoing), end(outgoing), [](Particle const & s){ return s.type == pid::Higgs; } ); assert(the_Higgs != end(outgoing)); const auto pH = to_HepLorentzVector(*the_Higgs); const auto partons = tag_extremal_jet_partons(ev); const auto pa = to_HepLorentzVector(incoming[0]); const auto pb = to_HepLorentzVector(incoming[1]); auto p1 = to_HepLorentzVector( partons[(ev.type() == unob)?1:0] ); auto pn = to_HepLorentzVector( partons[partons.size() - ((ev.type() == unof)?2:1)] ); auto first_after_Higgs = begin(partons) + (the_Higgs-begin(outgoing)); assert( (first_after_Higgs == end(partons) && ( (ev.type() == unob) || partons.back().type != pid::gluon )) || first_after_Higgs->rapidity() >= the_Higgs->rapidity() ); assert( (first_after_Higgs == begin(partons) && ( (ev.type() == unof) || partons.front().type != pid::gluon )) || (first_after_Higgs-1)->rapidity() <= the_Higgs->rapidity() ); // always treat the Higgs as if it were in between the extremal FKL partons if(first_after_Higgs == begin(partons)) ++first_after_Higgs; else if(first_after_Higgs == end(partons)) --first_after_Higgs; // t-channel momentum before Higgs auto qH = pa; for(auto parton_it = begin(partons); parton_it != first_after_Higgs; ++parton_it){ qH -= to_HepLorentzVector(*parton_it); } auto q0 = pa - p1; auto begin_ladder = begin(partons) + 1; auto end_ladder = end(partons) - 1; double current_factor = NAN; if(ev.type() == FKL){ current_factor = ME_Higgs_current( incoming[0].type, incoming[1].type, pn, pb, p1, pa, qH, qH - pH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, param_.ew_parameters.vev() ); } else if(ev.type() == unob){ current_factor = C_A*C_A/2*tree_kin_Higgs_uno( begin(incoming), end(incoming), begin(partons), end(partons), qH, qH-pH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, param_.ew_parameters.vev() ); const auto p_unob = to_HepLorentzVector(partons.front()); q0 -= p_unob; p1 += p_unob; ++begin_ladder; } else if(ev.type() == unof){ current_factor = C_A*C_A/2*tree_kin_Higgs_uno( rbegin(incoming), rend(incoming), rbegin(partons), rend(partons), qH-pH, qH, param_.Higgs_coupling.mt, param_.Higgs_coupling.include_bottom, param_.Higgs_coupling.mb, param_.ew_parameters.vev() ); pn += to_HepLorentzVector(partons.back()); --end_ladder; } else{ throw std::logic_error("Can only reweight FKL or uno processes in H+Jets"); } const double ladder_factor = FKL_ladder_weight( begin_ladder, first_after_Higgs, q0, pa, pb, p1, pn, param_.regulator_lambda )*FKL_ladder_weight( first_after_Higgs, end_ladder, qH - pH, pa, pb, p1, pn, param_.regulator_lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } namespace { double get_AWZH_coupling(Event const & ev, double alpha_s, double alpha_w) { const auto AWZH_boson = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](auto const & p){return is_AWZH_boson(p);} ); if(AWZH_boson == end(ev.outgoing())) return 1.; switch(AWZH_boson->type){ case pid::Higgs: return alpha_s*alpha_s; case pid::Wp: case pid::Wm: case pid::Z_photon_mix: return alpha_w*alpha_w; // TODO case pid::photon: case pid::Z: default: throw not_implemented("Emission of boson of unsupported type"); } } } // namespace double MatrixElement::tree_param(Event const & ev, double mur) const { assert(is_resummable(ev.type())); const auto begin_partons = ev.begin_partons(); const auto end_partons = ev.end_partons(); const auto num_partons = std::distance(begin_partons, end_partons); const double alpha_s = alpha_s_(mur); const double gs2 = 4.*M_PI*alpha_s; double res = std::pow(gs2, num_partons); if(param_.log_correction){ // use alpha_s(q_perp), evolved to mur assert(num_partons >= 2); const auto first_emission = std::next(begin_partons); const auto last_emission = std::prev(end_partons); for(auto parton = first_emission; parton != last_emission; ++parton){ res *= 1. + alpha_s/(2.*M_PI)*BETA0*std::log(mur/parton->perp()); } } return get_AWZH_coupling(ev, alpha_s, param_.ew_parameters.alpha_w())*res; } } // namespace HEJ diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc index 0f29081..7282592 100644 --- a/src/PhaseSpacePoint.cc +++ b/src/PhaseSpacePoint.cc @@ -1,929 +1,925 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/PhaseSpacePoint.hh" #include #include #include #include #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "fastjet/JetDefinition.hh" #include "HEJ/Constants.hh" #include "HEJ/Event.hh" #include "HEJ/JetSplitter.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/RNG.hh" #include "HEJ/event_types.hh" #include "HEJ/kinematics.hh" #include "HEJ/resummation_jet.hh" #include "HEJ/utility.hh" namespace HEJ { namespace { constexpr int MAX_JET_USER_IDX = PhaseSpacePoint::NG_MAX; bool is_nonjet_parton(fastjet::PseudoJet const & parton){ assert(parton.user_index() != -1); return parton.user_index() > MAX_JET_USER_IDX; } bool is_jet_parton(fastjet::PseudoJet const & parton){ assert(parton.user_index() != -1); return parton.user_index() <= MAX_JET_USER_IDX; } namespace user_idx { //! user indices for partons with extremal rapidity enum ID: int { - qqxmid1 = -9, - qqxmid2 = -8, - qqxb = -7, - qqxf = -6, + qqbar_mid1 = -9, + qqbar_mid2 = -8, + qqbarb = -7, + qqbarf = -6, unob = -5, unof = -4, backward_fkl = -3, forward_fkl = -2, }; } // namespace user_idx using UID = user_idx::ID; double phase_space_normalisation( int num_Born_jets, int num_out_partons ){ return std::pow(16.*std::pow(M_PI,3), num_Born_jets - num_out_partons); } } // namespace Event::EventData to_EventData(PhaseSpacePoint psp){ Event::EventData result; result.incoming = std::move(psp).incoming_; // NOLINT(bugprone-use-after-move) result.outgoing = std::move(psp).outgoing_; // NOLINT(bugprone-use-after-move) // technically Event::EventData doesn't have to be sorted, // but PhaseSpacePoint should be anyway assert( std::is_sorted( begin(result.outgoing), end(result.outgoing), rapidity_less{} ) ); assert(result.outgoing.size() >= 2); static_assert( std::numeric_limits::has_quiet_NaN, "no quiet NaN for double" ); constexpr double nan = std::numeric_limits::quiet_NaN(); result.decays = std::move(psp).decays_; // NOLINT(bugprone-use-after-move) result.parameters.central = {nan, nan, psp.weight()}; // NOLINT(bugprone-use-after-move) return result; } std::vector PhaseSpacePoint::cluster_jets( std::vector const & partons ) const{ fastjet::ClusterSequence cs(partons, param_.jet_param.def); return sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt)); } bool PhaseSpacePoint::pass_resummation_cuts( std::vector const & jets ) const{ return cluster_jets(jets).size() == jets.size(); } namespace { // find iterators to central qqbar emission auto get_central_qqbar(Event const & ev) { // find born quarks (ignore extremal partons) auto const firstquark = std::find_if( std::next(ev.begin_partons()), std::prev(ev.end_partons(), 2), [](Particle const & s){ return (is_anyquark(s)); } ); // assert that it is a q-q_bar pair. assert(std::distance(firstquark, ev.end_partons()) != 2); assert( ( is_quark(*firstquark) && is_antiquark(*std::next(firstquark)) ) || ( is_antiquark(*firstquark) && is_quark(*std::next(firstquark)) ) ); return std::make_pair(firstquark, std::next(firstquark)); } //! returns index of most backward q-qbar jet template int get_back_quark_jet(Event const & ev, Iterator firstquark){ // find jets at FO corresponding to the quarks // technically this isn't necessary for LO std::vector const born_indices{ ev.particle_jet_indices() }; const auto firstquark_idx = std::distance(ev.begin_partons(), firstquark); int const firstjet_idx = born_indices[firstquark_idx]; assert(firstjet_idx>0); assert( born_indices[firstquark_idx+1] == firstjet_idx+1 ); return firstjet_idx; } //! returns index of most backward q-qbar jet int getBackQuarkJet(Event const & ev){ const auto firstquark = get_central_qqbar(ev).first; return get_back_quark_jet(ev, firstquark); } } // namespace double PhaseSpacePoint::estimate_emission_rapidity_range( Event const & ev ) const { assert(std::is_sorted(begin(ev.jets()), end(ev.jets()), rapidity_less{})); double delta_y = most_forward_FKL(ev.jets()).rapidity() - most_backward_FKL(ev.jets()).rapidity(); // neglect tiny probability for emission between central qqbar pair - if(ev.type() == event_type::central_qqx) { + if(ev.type() == event_type::central_qqbar) { const int qjet = getBackQuarkJet(ev); delta_y -= ev.jets()[qjet+1].rapidity() - ev.jets()[qjet].rapidity(); } assert(delta_y >= 0); return delta_y; } double PhaseSpacePoint::estimate_ng_mean(Event const & ev) const { // Formula derived from fit in arXiv:1805.04446 (see Fig. 2) constexpr double GLUONS_PER_RAPIDITY = 0.975052; return GLUONS_PER_RAPIDITY*estimate_emission_rapidity_range(ev); } int PhaseSpacePoint::sample_ng(Event const & event, RNG & ran){ const double ng_mean = estimate_ng_mean(event); std::poisson_distribution dist(ng_mean); const int ng = dist(ran); assert(ng >= 0); assert(ng < NG_MAX); weight_ *= std::tgamma(ng + 1)*std::exp(ng_mean)*std::pow(ng_mean, -ng); return ng; } void PhaseSpacePoint::boost_AWZH_boson_from( fastjet::PseudoJet const & boosted_boson, Event const & event ){ auto const & from = event.outgoing(); const auto original_boson = std::find_if( begin(from), end(from), [](Particle const & p){ return is_AWZH_boson(p); } ); if(original_boson == end(from)) return; auto insertion_point = std::lower_bound( begin(outgoing_), end(outgoing_), *original_boson, rapidity_less{} ); // copy AWZH particle outgoing_.insert(insertion_point, {original_boson->type, boosted_boson, original_boson->colour} ); assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{})); // copy & boost decay products const int idx = std::distance(begin(from), original_boson); assert(idx >= 0); const auto decay_it = event.decays().find(idx); if(decay_it == end(event.decays())) return; const int new_idx = std::distance(begin(outgoing_), insertion_point); assert(new_idx >= 0); assert(outgoing_[new_idx].type == original_boson->type); auto decayparticles=decay_it->second; // change the momenta of the decay products. for(auto & particle: decayparticles){ auto & p = particle.p; // boost _to_ rest frame of input boson p.unboost(original_boson->p); // then boost _from_ rest frame of shuffled boson p.boost(boosted_boson); } decays_.emplace(new_idx, decayparticles); } namespace { template - void label_extremal_qqx( + void label_extremal_qqbar( ConstIterator born_begin, ConstIterator born_end, Iterator first_out ){ // find born quarks const auto firstquark = std::find_if( born_begin, born_end-1, [](Particle const & s){ return (is_anyquark(s)); } ); assert(firstquark != born_end-1); const auto secondquark = std::find_if( firstquark+1, born_end, [](Particle const & s){ return (is_anyquark(s)); } ); assert(secondquark != born_end); assert( ( is_quark(*firstquark) && is_antiquark(*secondquark) ) || ( is_antiquark(*firstquark) && is_quark(*secondquark) )); assert(first_out->type == ParticleID::gluon); assert((first_out+1)->type == ParticleID::gluon); // copy type from born first_out->type = firstquark->type; (first_out+1)->type = secondquark->type; } } // namespace - void PhaseSpacePoint::label_qqx(Event const & event){ + void PhaseSpacePoint::label_qqbar(Event const & event){ assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{})); assert(filter_partons(outgoing_).size() == outgoing_.size()); - if(qqxb_){ - label_extremal_qqx( event.outgoing().cbegin(), event.outgoing().cend(), - outgoing_.begin() - ); + if(qqbarb_){ + label_extremal_qqbar(event.outgoing().cbegin(), event.outgoing().cend(), + outgoing_.begin() ); return; } - if(qqxf_){ // same as qqxb with reversed order - label_extremal_qqx( event.outgoing().crbegin(), event.outgoing().crend(), - outgoing_.rbegin() - ); + if(qqbarf_){ // same as qqbarb with reversed order + label_extremal_qqbar( event.outgoing().crbegin(), event.outgoing().crend(), + outgoing_.rbegin() ); return; } - // central qqx + // central qqbar const auto firstquark = get_central_qqbar(event).first; // find jets at FO corresponding to the quarks // technically this isn't necessary for LO const auto firstjet_idx = get_back_quark_jet(event, firstquark); // find corresponding jets after resummation fastjet::ClusterSequence cs{to_PseudoJet(outgoing_), param_.jet_param.def}; auto const jets = fastjet::sorted_by_rapidity( cs.inclusive_jets( param_.jet_param.min_pt )); std::vector const resum_indices{ cs.particle_jet_indices({jets}) }; // assert that jets didn't move assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx )->rapidity(), jets[ firstjet_idx ].rapidity(), 1e-2) ); assert(nearby_ep( ( event.jets().cbegin()+firstjet_idx+1 )->rapidity(), jets[ firstjet_idx+1 ].rapidity(), 1e-2) ); // find last partons in first (central) jet size_t idx_out = 0; for(size_t i=resum_indices.size()-2; i>0; --i) if(resum_indices[i] == firstjet_idx){ idx_out = i; break; } assert(idx_out != 0); // check that there is sufficient pt in jets from the quarks const double minpartonjetpt = 1. - param_.soft_pt_regulator; if (outgoing_[idx_out].p.pt()pt()){ weight_=0.; status_ = StatusCode::wrong_jets; return; } if (outgoing_[idx_out+1].p.pt()pt()){ weight_=0.; status_ = StatusCode::wrong_jets; return; } // check that no additional emission between jets // such configurations are possible if we have an gluon gets generated - // inside the rapidities of the qqx chain, but clusted to a + // inside the rapidities of the qqbar chain, but clusted to a // differnet/outside jet. Changing this is non trivial if(resum_indices[idx_out+1] != resum_indices[idx_out]+1){ weight_=0.; - status_ = StatusCode::gluon_in_qqx; + status_ = StatusCode::gluon_in_qqbar; return; } outgoing_[idx_out].type = firstquark->type; outgoing_[idx_out+1].type = std::next(firstquark)->type; } void PhaseSpacePoint::label_quarks(Event const & ev){ const auto WZEmit = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](Particle const & s){ return (std::abs(s.type) == pid::Wp || s.type == pid::Z_photon_mix); } ); if (WZEmit != end(ev.outgoing())){ - if(!qqxb_) { + if(!qqbarb_) { const size_t backward_FKL_idx = unob_?1:0; const auto backward_FKL = std::next(ev.begin_partons(), backward_FKL_idx); outgoing_[backward_FKL_idx].type = backward_FKL->type; } - if(!qqxf_) { + if(!qqbarf_) { const size_t forward_FKL_idx = unof_?1:0; const auto forward_FKL = std::prev(ev.end_partons(), 1+forward_FKL_idx); outgoing_.rbegin()[unof_].type = forward_FKL->type; // NOLINT } } else { most_backward_FKL(outgoing_).type = ev.incoming().front().type; most_forward_FKL(outgoing_).type = ev.incoming().back().type; } - if(qqxmid_||qqxb_||qqxf_){ - label_qqx(ev); + if(qqbar_mid_||qqbarb_||qqbarf_){ + label_qqbar(ev); } } PhaseSpacePoint::PhaseSpacePoint( Event const & ev, PhaseSpacePointConfig conf, RNG & ran ): unob_{ev.type() == event_type::unob}, unof_{ev.type() == event_type::unof}, - qqxb_{ev.type() == event_type::qqxexb}, - qqxf_{ev.type() == event_type::qqxexf}, - qqxmid_{ev.type() == event_type::qqxmid}, + qqbarb_{ev.type() == event_type::qqbar_exb}, + qqbarf_{ev.type() == event_type::qqbar_exf}, + qqbar_mid_{ev.type() == event_type::qqbar_mid}, param_{std::move(conf)}, status_{unspecified} { // legacy code: override new variable with old if(param_.max_ext_soft_pt_fraction){ param_.soft_pt_regulator = *param_.max_ext_soft_pt_fraction; param_.max_ext_soft_pt_fraction = {}; } weight_ = 1; auto const & Born_jets = ev.jets(); const int ng = sample_ng(ev, ran); weight_ /= std::tgamma(ng + 1); const int ng_jets = sample_ng_jets(ev, ng, ran); std::vector out_partons = gen_non_jet( ng - ng_jets, CMINPT, param_.jet_param.min_pt, ran ); - int qqxbackjet(-1); - if(qqxmid_){ - qqxbackjet = getBackQuarkJet(ev); + int qqbar_backjet(-1); + if(qqbar_mid_){ + qqbar_backjet = getBackQuarkJet(ev); } // reshuffle soft momenta const auto qperp = std::accumulate( begin(out_partons), end(out_partons), fastjet::PseudoJet{} ); std::vector jets; optional boson; std::tie(jets, boson) = reshuffle(ev, qperp); if(weight_ == 0.) { status_ = failed_reshuffle; return; } if(! pass_resummation_cuts(jets)){ status_ = failed_resummation_cuts; weight_ = 0.; return; } // split jets in multiple partons std::vector jet_partons = split( - jets, ng_jets, qqxbackjet, ran + jets, ng_jets, qqbar_backjet, ran ); if(weight_ == 0.) { status_ = StatusCode::failed_split; return; } - - // rescale rapidity interval - if(qqxmid_){ - rescale_qqx_rapidities( + if(qqbar_mid_){ + rescale_qqbar_rapidities( out_partons, jets, most_backward_FKL(jet_partons).rapidity(), most_forward_FKL(jet_partons).rapidity(), - qqxbackjet + qqbar_backjet ); } else{ rescale_rapidities( out_partons, most_backward_FKL(jet_partons).rapidity(), most_forward_FKL(jet_partons).rapidity() ); } if(! cluster_jets(out_partons).empty()){ weight_ = 0.; status_ = StatusCode::empty_jets; return; } std::sort(begin(out_partons), end(out_partons), rapidity_less{}); assert( std::is_sorted(begin(jet_partons), end(jet_partons), rapidity_less{}) ); const auto first_jet_parton = out_partons.insert( end(out_partons), begin(jet_partons), end(jet_partons) ); std::inplace_merge( begin(out_partons), first_jet_parton, end(out_partons), rapidity_less{} ); if(! jets_ok(Born_jets, out_partons)){ weight_ = 0.; status_ = StatusCode::wrong_jets; return; } weight_ *= phase_space_normalisation(Born_jets.size(), out_partons.size()); outgoing_.reserve(out_partons.size() + 1); // one slot for possible A, W, Z, H for( auto it = std::make_move_iterator(out_partons.begin()); it != std::make_move_iterator(out_partons.end()); ++it ){ outgoing_.emplace_back( Particle{pid::gluon, *it, {}}); } assert(!outgoing_.empty()); label_quarks(ev); if(weight_ == 0.) { //! @TODO optimise s.t. this is not possible // status is handled internally return; } // reattach boson & decays if(boson){ boost_AWZH_boson_from(*boson, ev); } reconstruct_incoming(ev.incoming()); status_ = StatusCode::good; } std::vector PhaseSpacePoint::gen_non_jet( int const ng_non_jet, double const ptmin, double const ptmax, RNG & ran ){ // heuristic parameters for pt sampling const double ptpar = 1.3 + ng_non_jet/5.; const double temp1 = std::atan((ptmax - ptmin)/ptpar); std::vector partons(ng_non_jet); for(int i = 0; i < ng_non_jet; ++i){ const double r1 = ran.flat(); const double pt = ptmin + ptpar*std::tan(r1*temp1); const double temp2 = std::cos(r1*temp1); const double phi = 2*M_PI*ran.flat(); weight_ *= 2.0*M_PI*pt*ptpar*temp1/(temp2*temp2); // we don't know the allowed rapidity span yet, // set a random value to be rescaled later on const double y = ran.flat(); partons[i].reset_PtYPhiM(pt, y, phi); // Set user index higher than any jet-parton index // in order to assert that these are not inside jets partons[i].set_user_index(i + 1 + NG_MAX); assert(ptmin-1e-5 <= partons[i].pt() && partons[i].pt() <= ptmax+1e-5); } assert(std::all_of(partons.cbegin(), partons.cend(), is_nonjet_parton)); return sorted_by_rapidity(partons); } - void PhaseSpacePoint::rescale_qqx_rapidities( + void PhaseSpacePoint::rescale_qqbar_rapidities( std::vector & out_partons, std::vector const & jets, const double ymin1, const double ymax2, - const int qqxbackjet + const int qqbar_backjet ){ - const double ymax1 = jets[qqxbackjet].rapidity(); - const double ymin2 = jets[qqxbackjet+1].rapidity(); + const double ymax1 = jets[qqbar_backjet].rapidity(); + const double ymin2 = jets[qqbar_backjet+1].rapidity(); constexpr double ep = 1e-7; const double tot_y = ymax1 - ymin1 + ymax2 - ymin2; std::vector> refpart( out_partons.begin(), out_partons.end()); double ratio = (ymax1 - ymin1)/tot_y; const auto gap{ std::find_if(refpart.begin(), refpart.end(), [ratio](fastjet::PseudoJet const & p){ return (p.rapidity()>=ratio);} ) }; double ymin = ymin1; double ymax = ymax1; double dy = ymax - ymin - 2*ep; double offset = 0.; for(auto it_part=refpart.begin(); it_part & partons, double ymin, double ymax ){ constexpr double ep = 1e-7; for(auto & parton: partons){ assert(0 <= parton.rapidity() && parton.rapidity() <= 1); const double dy = ymax - ymin - 2*ep; const double y = ymin + ep + dy*parton.rapidity(); parton.reset_momentum_PtYPhiM(parton.pt(), y, parton.phi()); weight_ *= dy; assert(ymin <= parton.rapidity() && parton.rapidity() <= ymax); } } namespace { template auto min(T const & a, T const & b, Rest&&... r) { using std::min; return min(a, min(b, std::forward(r)...)); } } double PhaseSpacePoint::probability_in_jet(Event const & ev) const{ const double dy = estimate_emission_rapidity_range(ev); const double R = param_.jet_param.def.R(); // jets into which we predominantly emit - const auto njets = ev.jets().size() - unof_ - unob_ - qqxb_ - qqxf_; //NOLINT + const auto njets = ev.jets().size() - unof_ - unob_ - qqbarb_ - qqbarf_; //NOLINT assert(njets >= 2); const double p_J_y_large = (njets-1.)*R*R/(2.*dy); const double p_J_y0 = njets*R/M_PI; return min(p_J_y_large, p_J_y0, 1.); } int PhaseSpacePoint::sample_ng_jets(Event const & event, int ng, RNG & ran){ const double p_J = probability_in_jet(event); std::binomial_distribution<> bin_dist(ng, p_J); const int ng_J = bin_dist(ran); weight_ *= std::pow(p_J, -ng_J)*std::pow(1 - p_J, ng_J - ng); return ng_J; } std::pair< std::vector, optional > PhaseSpacePoint::reshuffle( Event const & ev, fastjet::PseudoJet const & q ){ // Create a copy of the outgoing momenta not containing decay products std::vector born_momenta; born_momenta.reserve(ev.jets().size()); std::transform(ev.jets().cbegin(), ev.jets().cend(), back_inserter(born_momenta), [](fastjet::PseudoJet const & t) { return &t; }); // check if there is one (or more) bosons in the event. const auto AWZH_boson = std::find_if( begin(ev.outgoing()), end(ev.outgoing()), [](Particle const & p){ return is_AWZH_boson(p); } ); optional boson; if(AWZH_boson != end(ev.outgoing())){ boson = AWZH_boson->p; } // reshuffle all momenta if(q == fastjet::PseudoJet{0, 0, 0, 0}) return {ev.jets(), boson}; // add boson to reshuffling if(boson) { born_momenta.push_back(&*boson); } auto shuffle_momenta = resummation_jet_momenta(born_momenta, q); if(shuffle_momenta.empty()){ weight_ = 0; return {}; } // additional Jacobian to ensure Born integration over delta gives 1 weight_ *= resummation_jet_weight(born_momenta, q); // take out boson again optional shuffle_boson; if(boson){ shuffle_boson = std::move(shuffle_momenta.back()); shuffle_momenta.pop_back(); } return {shuffle_momenta, shuffle_boson}; } std::vector PhaseSpacePoint::distribute_jet_partons( int ng_jets, std::vector const & jets, RNG & ran ){ size_t first_valid_jet = 0; size_t num_valid_jets = jets.size(); const double R_eff = 5./3.*param_.jet_param.def.R(); // if there is an unordered jet too far away from the FKL jets // then extra gluon constituents of the unordered jet would // violate the FKL rapidity ordering - if((unob_||qqxb_) && jets[0].delta_R(jets[1]) > R_eff){ + if((unob_||qqbarb_) && jets[0].delta_R(jets[1]) > R_eff){ ++first_valid_jet; --num_valid_jets; } - else if((unof_||qqxf_) && jets[jets.size()-1].delta_R(jets[jets.size()-2]) > R_eff){ + else if((unof_||qqbarf_) && jets[jets.size()-1].delta_R(jets[jets.size()-2]) > R_eff){ --num_valid_jets; } std::vector np(jets.size(), 1); for(int i = 0; i < ng_jets; ++i){ ++np[first_valid_jet + ran.flat() * num_valid_jets]; } weight_ *= std::pow(num_valid_jets, ng_jets); return np; } #ifndef NDEBUG namespace { bool tagged_FKL_backward( std::vector const & jet_partons ){ return std::find_if( begin(jet_partons), end(jet_partons), [](fastjet::PseudoJet const & p){ return p.user_index() == UID::backward_fkl; } ) != end(jet_partons); } bool tagged_FKL_forward( std::vector const & jet_partons ){ // the most forward FKL parton is most likely near the end of jet_partons; // start search from there return std::find_if( jet_partons.rbegin(), jet_partons.rend(), [](fastjet::PseudoJet const & p){ return p.user_index() == UID::forward_fkl; } ) != jet_partons.rend(); } bool tagged_FKL_extremal( std::vector const & jet_partons ){ return tagged_FKL_backward(jet_partons) && tagged_FKL_forward(jet_partons); } } // namespace #endif std::vector PhaseSpacePoint::split( std::vector const & jets, - int ng_jets, size_t qqxbackjet, RNG & ran + int ng_jets, size_t qqbar_backjet, RNG & ran ){ return split( - jets, distribute_jet_partons(ng_jets, jets, ran), qqxbackjet, ran); + jets, distribute_jet_partons(ng_jets, jets, ran), qqbar_backjet, ran); } bool PhaseSpacePoint::pass_extremal_cuts( fastjet::PseudoJet const & ext_parton, fastjet::PseudoJet const & jet ) const{ if(ext_parton.pt() < param_.min_extparton_pt) return false; return (ext_parton - jet).pt()/jet.pt() < param_.soft_pt_regulator; } std::vector PhaseSpacePoint::split( std::vector const & jets, std::vector const & np, - size_t qqxbackjet, + size_t qqbar_backjet, RNG & ran ){ assert(! jets.empty()); assert(jets.size() == np.size()); assert(pass_resummation_cuts(jets)); - const size_t most_backward_FKL_idx = 0 + unob_ + qqxb_; // NOLINT - const size_t most_forward_FKL_idx = jets.size() - 1 - unof_ - qqxf_; // NOLINT + const size_t most_backward_FKL_idx = 0 + unob_ + qqbarb_; // NOLINT + const size_t most_forward_FKL_idx = jets.size() - 1 - unof_ - qqbarf_; // NOLINT auto const & jet = param_.jet_param; const JetSplitter jet_splitter{jet.def, jet.min_pt}; std::vector jet_partons; // randomly distribute jet gluons among jets for(size_t i = 0; i < jets.size(); ++i){ auto split_res = jet_splitter.split(jets[i], np[i], ran); weight_ *= split_res.weight; if(weight_ == 0) return {}; assert( std::all_of( begin(split_res.constituents), end(split_res.constituents), is_jet_parton ) ); const auto first_new_parton = jet_partons.insert( end(jet_partons), begin(split_res.constituents), end(split_res.constituents) ); // mark uno and extremal FKL emissions here so we can check // their position once all emissions are generated - // also mark qqxmid partons, and apply appropriate pt cut. + // also mark qqbar_mid partons, and apply appropriate pt cut. auto extremal = end(jet_partons); if (i == most_backward_FKL_idx){ //FKL backward emission extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index(UID::backward_fkl); } - else if(((unob_ || qqxb_) && i == 0)){ - // unordered/qqxb + else if(((unob_ || qqbarb_) && i == 0)){ + // unordered/qqbarb extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index((unob_)?UID::unob:UID::qqxb); + extremal->set_user_index((unob_)?UID::unob:UID::qqbarb); } else if (i == most_forward_FKL_idx){ extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); extremal->set_user_index(UID::forward_fkl); } - else if(((unof_ || qqxf_) && i == jets.size() - 1)){ - // unordered/qqxf + else if(((unof_ || qqbarf_) && i == jets.size() - 1)){ + // unordered/qqbarf extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index((unof_)?UID::unof:UID::qqxf); + extremal->set_user_index((unof_)?UID::unof:UID::qqbarf); } - else if((qqxmid_ && i == qqxbackjet)){ + else if((qqbar_mid_ && i == qqbar_backjet)){ extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index(UID::qqxmid1); + extremal->set_user_index(UID::qqbar_mid1); } - else if((qqxmid_ && i == qqxbackjet+1)){ + else if((qqbar_mid_ && i == qqbar_backjet+1)){ extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index(UID::qqxmid2); + extremal->set_user_index(UID::qqbar_mid2); } if( extremal != end(jet_partons) && !pass_extremal_cuts(*extremal, jets[i]) ){ weight_ = 0; return {}; } } assert(tagged_FKL_extremal(jet_partons)); std::sort(begin(jet_partons), end(jet_partons), rapidity_less{}); if( !extremal_ok(jet_partons) || !split_preserved_jets(jets, jet_partons) ){ weight_ = 0.; return {}; } return jet_partons; } bool PhaseSpacePoint::extremal_ok( std::vector const & partons ) const{ assert(std::is_sorted(begin(partons), end(partons), rapidity_less{})); if(unob_ && partons.front().user_index() != UID::unob) return false; if(unof_ && partons.back().user_index() != UID::unof) return false; - if(qqxb_ && partons.front().user_index() != UID::qqxb) return false; - if(qqxf_ && partons.back().user_index() != UID::qqxf) return false; + if(qqbarb_ && partons.front().user_index() != UID::qqbarb) return false; + if(qqbarf_ && partons.back().user_index() != UID::qqbarf) return false; return most_backward_FKL(partons).user_index() == UID::backward_fkl && most_forward_FKL(partons).user_index() == UID::forward_fkl; } bool PhaseSpacePoint::split_preserved_jets( std::vector const & jets, std::vector const & jet_partons ) const{ assert(std::is_sorted(begin(jets), end(jets), rapidity_less{})); const auto split_jets = cluster_jets(jet_partons); // this can happen if two overlapping jets // are both split into more than one parton if(split_jets.size() != jets.size()) return false; for(size_t i = 0; i < split_jets.size(); ++i){ // this can happen if there are two overlapping jets // and a parton is assigned to the "wrong" jet if(!nearby_ep(jets[i].rapidity(), split_jets[i].rapidity(), 1e-2)){ return false; } } return true; } template Particle const & PhaseSpacePoint::most_backward_FKL( std::vector const & partons ) const{ - return partons[0 + unob_ + qqxb_]; + return partons[0 + unob_ + qqbarb_]; } template Particle const & PhaseSpacePoint::most_forward_FKL( std::vector const & partons ) const{ - const size_t idx = partons.size() - 1 - unof_ - qqxf_; + const size_t idx = partons.size() - 1 - unof_ - qqbarf_; assert(idx < partons.size()); return partons[idx]; } template Particle & PhaseSpacePoint::most_backward_FKL( std::vector & partons ) const{ - return partons[0 + unob_ + qqxb_]; + return partons[0 + unob_ + qqbarb_]; } template Particle & PhaseSpacePoint::most_forward_FKL( std::vector & partons ) const{ - const size_t idx = partons.size() - 1 - unof_ - qqxf_; + const size_t idx = partons.size() - 1 - unof_ - qqbarf_; assert(idx < partons.size()); return partons[idx]; } bool PhaseSpacePoint::contains_idx( fastjet::PseudoJet const & jet, fastjet::PseudoJet const & parton ) const { auto const & constituents = jet.constituents(); const int idx = parton.user_index(); const bool injet = std::find_if( begin(constituents), end(constituents), [idx](fastjet::PseudoJet const & con){return con.user_index() == idx;} ) != end(constituents); const double minpartonjetpt = 1. - param_.soft_pt_regulator; return ((parton.pt()>minpartonjetpt*jet.pt())&&injet); } bool PhaseSpacePoint::jets_ok( std::vector const & Born_jets, std::vector const & partons ) const{ fastjet::ClusterSequence cs(partons, param_.jet_param.def); const auto jets = sorted_by_rapidity(cs.inclusive_jets(param_.jet_param.min_pt)); if(jets.size() != Born_jets.size()) return false; int in_jet = 0; for(auto const & jet : jets){ assert(jet.has_constituents()); for(auto && parton: jet.constituents()){ if(is_nonjet_parton(parton)) return false; } in_jet += jet.constituents().size(); } const int expect_in_jet = std::count_if( partons.cbegin(), partons.cend(), is_jet_parton ); if(in_jet != expect_in_jet) return false; // note that PseudoJet::contains does not work here if(! ( contains_idx(most_backward_FKL(jets), most_backward_FKL(partons)) && contains_idx(most_forward_FKL(jets), most_forward_FKL(partons)) )) return false; if(unob_ && !contains_idx(jets.front(), partons.front())) return false; - if(qqxb_ && !contains_idx(jets.front(), partons.front())) return false; + if(qqbarb_ && !contains_idx(jets.front(), partons.front())) return false; if(unof_ && !contains_idx(jets.back(), partons.back())) return false; - if(qqxf_ && !contains_idx(jets.back(), partons.back())) return false; + if(qqbarf_ && !contains_idx(jets.back(), partons.back())) return false; #ifndef NDEBUG for(size_t i = 0; i < jets.size(); ++i){ assert(nearby_ep(jets[i].rapidity(), Born_jets[i].rapidity(), 1e-2)); } #endif return true; } void PhaseSpacePoint::reconstruct_incoming( std::array const & Born_incoming ){ std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_); for(size_t i = 0; i < incoming_.size(); ++i){ incoming_[i].type = Born_incoming[i].type; } assert(momentum_conserved()); } bool PhaseSpacePoint::momentum_conserved() const{ fastjet::PseudoJet diff; for(auto const & in: incoming()) diff += in.p; const double norm = diff.E(); for(auto const & out: outgoing()) diff -= out.p; return nearby(diff, fastjet::PseudoJet{}, norm); } } //namespace HEJ diff --git a/src/Wjets.cc b/src/Wjets.cc index 3a439a3..6103303 100644 --- a/src/Wjets.cc +++ b/src/Wjets.cc @@ -1,805 +1,805 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include "HEJ/Wjets.hh" #include #include #include #include #include #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" #include "HEJ/LorentzVector.hh" #include "HEJ/exceptions.hh" #include "HEJ/jets.hh" // generated headers #include "HEJ/currents/jV_j.hh" #include "HEJ/currents/jV_juno.hh" #include "HEJ/currents/jVuno_j.hh" #include "HEJ/currents/jW_jqqbar.hh" #include "HEJ/currents/jW_qqbar_j.hh" #include "HEJ/currents/jWqqbar_j.hh" #include "HEJ/currents/j_Wqqbar_j.hh" namespace HEJ { namespace currents { namespace { // short hands using std::conj; // --- Helper Functions --- // FKL W Helper Functions double WProp(const HLV & plbar, const HLV & pl, ParticleProperties const & wprop ){ COM propW = COM(0.,-1.)/( (pl+plbar).m2() - wprop.mass*wprop.mass + COM(0.,1.)*wprop.mass*wprop.width); double PropFactor=(propW*conj(propW)).real(); return PropFactor; } /** * @brief Contraction of W + unordered jet current with FKL current * * See eq:wunocurrent in the developer manual for the definition * of the W + unordered jet current */ template double jM2Wuno( HLV const & pg, HLV const & p1, HLV const & plbar, HLV const & pl, HLV const & pa, HLV const & p2, HLV const & pb, ParticleProperties const & wprop ){ using helicity::minus; const COM u1 = U1(p1, p2, pa, pb, pg, pl, plbar); const COM u2 = U2(p1, p2, pa, pb, pg, pl, plbar); const COM l = L(p1, p2, pa, pb, pg, pl, plbar); const COM x = u1 - l; const COM y = u2 + l; double amp = C_A*C_F*C_F/2.*(norm(x)+norm(y)) - C_F/2.*(x*conj(y)).real(); amp *= WProp(plbar, pl, wprop); const HLV q1g = pa-pl-plbar-p1-pg; const HLV q2 = p2-pb; const double t1 = q1g.m2(); const double t2 = q2.m2(); amp /= (t1*t2); return amp; } /** * @brief Contraction of W + extremal qqbar current with FKL current * * See eq:crossed in the developer manual for the definition * of the W + extremal qqbar current. * */ template double jM2_W_extremal_qqbar( HLV const & pg, HLV const & pq, HLV const & plbar, HLV const & pl, HLV const & pqbar, HLV const & p3, HLV const & pb, ParticleProperties const & wprop ){ const COM u1Xcontr = U1X(pg, pq, plbar, pl, pqbar, p3, pb); const COM u2Xcontr = U2X(pg, pq, plbar, pl, pqbar, p3, pb); const COM lXcontr = LX(pg, pq, plbar, pl, pqbar, p3, pb); const COM x = u1Xcontr - lXcontr; const COM y = u2Xcontr + lXcontr; double amp = C_A*C_F*C_F/2.*(norm(x)+norm(y)) - C_F/2.*(x*conj(y)).real(); amp *= WProp(plbar, pl, wprop); const HLV q1 = pg-pl-plbar-pq-pqbar; const HLV q2 = p3-pb; const double t1 = q1.m2(); const double t2 = q2.m2(); amp /= (t1*t2); return amp; } //! W+Jets FKL Contributions /** * @brief W+Jets FKL Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1. (W emission) * @param plbar Outgoing election momenta * @param pl Outgoing neutrino momenta * @param p1in Incoming Particle 1. (W emission) * @param p2out Outgoing Particle 2 * @param p2in Incoming Particle 2 * @param aqlineb Bool. Is Backwards quark line an anti-quark line? * @param aqlinef Bool. Is Forwards quark line an anti-quark line? * * Calculates j_W ^\mu j_\mu. * Handles all possible incoming states. */ double jW_j(HLV const & p1out, HLV plbar, HLV pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, bool aqlineb, bool /* aqlinef */, ParticleProperties const & wprop ){ using helicity::minus; using helicity::plus; const HLV q1=p1in-p1out-plbar-pl; const HLV q2=-(p2in-p2out); const double wPropfact = WProp(plbar, pl, wprop); // we assume that the W is emitted off a quark line // if this is not the case, we have to apply CP conjugation, // which is equivalent to swapping lepton and antilepton momenta if(aqlineb) std::swap(pl, plbar); const COM ampm = jV_j(p1in,p1out,p2in,p2out,pl,plbar); const COM ampp = jV_j(p1in,p1out,p2in,p2out,pl,plbar); const double Msqr = std::norm(ampm) + std::norm(ampp); // Division by colour and Helicity average (Nc2-1)(4) // Multiply by Cf^2 return C_F*C_F*wPropfact*Msqr/(q1.m2()*q2.m2()*(N_C*N_C - 1)*4); } } // Anonymous Namespace double ME_W_qQ( HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ return jW_j(p1out, plbar, pl, p1in, p2out, p2in, false, false, wprop); } double ME_W_qQbar( HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ return jW_j(p1out, plbar, pl, p1in, p2out, p2in, false, true, wprop); } double ME_W_qbarQ( HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ return jW_j(p1out, plbar, pl, p1in, p2out, p2in, true, false, wprop); } double ME_W_qbarQbar( HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ return jW_j(p1out, plbar, pl, p1in, p2out, p2in, true, true, wprop); } double ME_W_qg( HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ return jW_j(p1out, plbar, pl, p1in, p2out, p2in, false, false, wprop) *K_g(p2out, p2in)/C_F; } double ME_W_qbarg( HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ return jW_j(p1out, plbar, pl, p1in, p2out, p2in, true, false, wprop) *K_g(p2out, p2in)/C_F; } namespace { // helicity amplitude squares for contraction of W current with unordered // current template double ampsq_jW_juno( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & p2, HLV const & pg, HLV const & pl, HLV const & plbar ){ using helicity::minus; const COM u1 = U1_jV(pa,p1,pb,p2,pg,pl,plbar); const COM u2 = U2_jV(pa,p1,pb,p2,pg,pl,plbar); const COM l = L_jV(pa,p1,pb,p2,pg,pl,plbar); return 2.*C_F*std::real((l-u1)*std::conj(l+u2)) + 2.*C_F*C_F/3.*std::norm(u1+u2) ; } /** * @brief W+Jets Unordered Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1. (W emission) * @param plbar Outgoing election momenta * @param pl Outgoing neutrino momenta * @param p1in Incoming Particle 1. (W emission) * @param p2out Outgoing Particle 2 (Quark, unordered emission this side.) * @param p2in Incoming Particle 2 (Quark, unordered emission this side.) * @param pg Unordered Gluon momenta * @param aqlineb Bool. Is Backwards quark line an anti-quark line? * @param aqlinef Bool. Is Forwards quark line an anti-quark line? * * Calculates j_W ^\mu j_{uno}_\mu. Ie, unordered with W emission * opposite side. Handles all possible incoming states. * * @TODO use const & for pl plbar */ double jW_juno( HLV const & p1out, HLV plbar, HLV pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, bool aqlineb, bool /* aqlinef */, ParticleProperties const & wprop ){ using helicity::minus; using helicity::plus; // we assume that the W is emitted off a quark line // if this is not the case, we have to apply CP conjugation, // which is equivalent to swapping lepton and antilepton momenta if(aqlineb) std::swap(pl, plbar); // helicity assignments assume aqlinef == false // in the end, this is irrelevant since we sum over all helicities const double ampsq = + ampsq_jW_juno(p1in,p1out,p2in,p2out,pg,pl,plbar) + ampsq_jW_juno(p1in,p1out,p2in,p2out,pg,pl,plbar) + ampsq_jW_juno(p1in,p1out,p2in,p2out,pg,pl,plbar) + ampsq_jW_juno(p1in,p1out,p2in,p2out,pg,pl,plbar) ; const HLV q1=p1in-p1out-plbar-pl; const HLV q2=-(p2in-p2out-pg); //! @TODO explain magic 16 return WProp(plbar, pl, wprop)*ampsq/(16.*q2.m2()*q1.m2()); } } // Anonymous Namespace double ME_W_unob_qQ( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jW_juno(p2out, plbar, pl, p2in, p1out, p1in, pg, false, false, wprop); } double ME_W_unob_qQbar( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jW_juno(p2out, plbar, pl, p2in, p1out, p1in, pg, false, true, wprop); } double ME_W_unob_qbarQ( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jW_juno(p2out, plbar, pl, p2in, p1out, p1in, pg, true, false, wprop); } double ME_W_unob_qbarQbar( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jW_juno(p2out, plbar, pl, p2in, p1out, p1in, pg, true, true, wprop); } namespace { // helicity sum helper for jWuno_j(...) template double jWuno_j_helsum( HLV const & pg, HLV const & p1, HLV const & plbar, HLV const & pl, HLV const & pa, HLV const & p2, HLV const & pb, ParticleProperties const & wprop ){ using helicity::minus; using helicity::plus; const double ME2h1pp = jM2Wuno( pg, p1, plbar, pl, pa, p2, pb, wprop ); const double ME2h1pm = jM2Wuno( pg, p1, plbar, pl, pa, p2, pb, wprop ); const double ME2h1mp = jM2Wuno( pg, p1, plbar, pl, pa, p2, pb, wprop ); const double ME2h1mm = jM2Wuno( pg, p1, plbar, pl, pa, p2, pb, wprop ); return ME2h1pp + ME2h1pm + ME2h1mp + ME2h1mm; } /** * @brief W+Jets Unordered Contributions, function to handle all incoming * @types. * @param pg Unordered Gluon momenta * @param p1out Outgoing Particle 1. (Quark - W and Uno emission) * @param plbar Outgoing election momenta * @param pl Outgoing neutrino momenta * @param p1in Incoming Particle 1. (Quark - W and Uno emission) * @param p2out Outgoing Particle 2 * @param p2in Incoming Particle 2 * @param aqlineb Bool. Is Backwards quark line an anti-quark line? * * Calculates j_W_{uno} ^\mu j_\mu. Ie, unordered with W emission same * side. Handles all possible incoming states. Note this handles both forward * and back- -ward Wuno emission. For forward, ensure p1out is the uno and W * emission parton. * @TODO: Include separate wrapper functions for forward and backward to clean up * ME_W_unof_current in `MatrixElement.cc`. */ double jWuno_j( HLV const & pg, HLV const & p1out, HLV const & plbar, HLV const & pl, HLV const & p1in, HLV const & p2out, HLV const & p2in, bool aqlineb, ParticleProperties const & wprop ){ const auto helsum = aqlineb? jWuno_j_helsum: jWuno_j_helsum; //Helicity sum and average over initial states return helsum(pg, p1out,plbar,pl,p1in,p2out,p2in, wprop)/(4.*C_A*C_A); } } // Anonymous Namespace double ME_Wuno_qQ( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jWuno_j(pg, p1out, plbar, pl, p1in, p2out, p2in, false, wprop); } double ME_Wuno_qQbar( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jWuno_j(pg, p1out, plbar, pl, p1in, p2out, p2in, false, wprop); } double ME_Wuno_qbarQ( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jWuno_j(pg, p1out, plbar, pl, p1in, p2out, p2in, true, wprop); } double ME_Wuno_qbarQbar( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jWuno_j(pg, p1out, plbar, pl, p1in, p2out, p2in, true, wprop); } double ME_Wuno_qg( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jWuno_j(pg, p1out, plbar, pl, p1in, p2out, p2in, false, wprop) *K_g(p2out, p2in)/C_F; } double ME_Wuno_qbarg( HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pg, HLV const & plbar, HLV const & pl, ParticleProperties const & wprop ){ return jWuno_j(pg, p1out, plbar, pl, p1in, p2out, p2in, true, wprop) *K_g(p2out, p2in)/C_F; } - // helicity sum helper for jWqqx_j(...) + // helicity sum helper for jWqqbar_j(...) template - double jWqqx_j_helsum( + double jWqqbar_j_helsum( HLV const & pg, HLV const & pq, HLV const & plbar, HLV const & pl, HLV const & pqbar, HLV const & p3, HLV const & pb, ParticleProperties const & wprop ){ using helicity::minus; using helicity::plus; const double amp_h1pp = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); const double amp_h1pm = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); const double amp_h1mp = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); const double amp_h1mm = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); return amp_h1pp + amp_h1pm + amp_h1mp + amp_h1mm; } /** - * @brief W+Jets Extremal qqx Contributions, function to handle all incoming + * @brief W+Jets Extremal qqbar Contributions, function to handle all incoming * @types. - * @param pgin Incoming gluon which will split into qqx. - * @param pqout Quark of extremal qqx outgoing (W-Emission). + * @param pgin Incoming gluon which will split into qqbar. + * @param pqout Quark of extremal qqbar outgoing (W-Emission). * @param plbar Outgoing anti-lepton momenta * @param pl Outgoing lepton momenta - * @param pqbarout Anti-quark of extremal qqx pair. (W-Emission) + * @param pqbarout Anti-quark of extremal qqbar pair. (W-Emission) * @param pout Outgoing Particle 2 (end of FKL chain) * @param p2in Incoming Particle 2 * @param aqlinef Bool. Is Forwards quark line an anti-quark line? * - * Calculates j_W_{qqx} ^\mu j_\mu. Ie, Ex-QQX with W emission same side. + * Calculates j_W_{qqbar} ^\mu j_\mu. Ie, Ex-QQbar with W emission same side. * Handles all possible incoming states. Calculated via crossing symmetry from * jWuno_j. */ - double jWqqx_j( + double jWqqbar_j( HLV const & pgin, HLV const & pqout, HLV const & plbar, HLV const & pl, HLV const & pqbarout, HLV const & p2out, HLV const & p2in, bool aqlinef, ParticleProperties const & wprop ){ const auto helsum = aqlinef? - jWqqx_j_helsum: - jWqqx_j_helsum; + jWqqbar_j_helsum: + jWqqbar_j_helsum; //Helicity sum and average over initial states. double ME2 = helsum(pgin, pqout, plbar, pl, pqbarout, p2out, p2in, wprop)/ (4.*C_A*C_A); //Correct colour averaging after crossing: ME2*=(3.0/8.0); return ME2; } - double ME_WExqqx_qbarqQ( + double ME_WExqqbar_qbarqQ( HLV const & pgin, HLV const & pqout, HLV const & plbar, HLV const & pl, HLV const & pqbarout, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ - return jWqqx_j(pgin, pqout, plbar, pl, pqbarout, p2out, p2in, false, wprop); + return jWqqbar_j(pgin, pqout, plbar, pl, pqbarout, p2out, p2in, false, wprop); } - double ME_WExqqx_qqbarQ( + double ME_WExqqbar_qqbarQ( HLV const & pgin, HLV const & pqbarout, HLV const & plbar, HLV const & pl, HLV const & pqout, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ - return jWqqx_j(pgin, pqbarout, plbar, pl, pqout, p2out, p2in, true, wprop); + return jWqqbar_j(pgin, pqbarout, plbar, pl, pqout, p2out, p2in, true, wprop); } - double ME_WExqqx_qbarqg( + double ME_WExqqbar_qbarqg( HLV const & pgin, HLV const & pqout, HLV const & plbar, HLV const & pl, HLV const & pqbarout, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ - return jWqqx_j(pgin, pqout, plbar, pl, pqbarout, p2out, p2in, false, wprop) + return jWqqbar_j(pgin, pqout, plbar, pl, pqbarout, p2out, p2in, false, wprop) *K_g(p2out,p2in)/C_F; } - double ME_WExqqx_qqbarg( + double ME_WExqqbar_qqbarg( HLV const & pgin, HLV const & pqbarout, HLV const & plbar, HLV const & pl, HLV const & pqout, HLV const & p2out, HLV const & p2in, ParticleProperties const & wprop ){ - return jWqqx_j(pgin, pqbarout, plbar, pl, pqout, p2out, p2in, true, wprop) + return jWqqbar_j(pgin, pqbarout, plbar, pl, pqout, p2out, p2in, true, wprop) *K_g(p2out,p2in)/C_F; } namespace { template double jW_jqqbar( HLV const & pb, HLV const & p2, HLV const & p3, HLV const & pa, HLV const & p1, HLV const & pl, HLV const & plbar ){ using std::norm; static constexpr double cm1m1 = 8./3.; static constexpr double cm2m2 = 8./3.; static constexpr double cm3m3 = 6.; static constexpr double cm1m2 =-1./3.; static constexpr COM cm1m3 = COM{0.,-3.}; static constexpr COM cm2m3 = COM{0.,3.}; const COM m1 = jW_qggm1(pb,p2,p3,pa,p1,pl,plbar); const COM m2 = jW_qggm2(pb,p2,p3,pa,p1,pl,plbar); const COM m3 = jW_qggm3(pb,p2,p3,pa,p1,pl,plbar); return + cm1m1*norm(m1) + cm2m2*norm(m2) + cm3m3*norm(m3) + 2.*real(cm1m2*m1*conj(m2)) + 2.*real(cm1m3*m1*conj(m3)) + 2.*real(cm2m3*m2*conj(m3)) ; } } // Anonymous Namespace // contraction of W current with extremal qqbar on the other side - double ME_W_Exqqx_QQq( + double ME_W_Exqqbar_QQq( HLV const & pa, HLV const & pb, HLV const & p1, HLV const & p2, HLV const & p3, HLV const & plbar, HLV const & pl, bool aqlinepa, ParticleProperties const & wprop ){ using helicity::minus; using helicity::plus; const double wPropfact = WProp(plbar, pl, wprop); const double prefactor = 2.*wPropfact /24./4./((pa-p1-pl-plbar).m2()*(p2+p3-pb).m2()); if(aqlinepa) { return prefactor*( jW_jqqbar(pb,p2,p3,pa,p1,pl,plbar) + jW_jqqbar(pb,p2,p3,pa,p1,pl,plbar) ); } return prefactor*( jW_jqqbar(pb,p2,p3,pa,p1,pl,plbar) + jW_jqqbar(pb,p2,p3,pa,p1,pl,plbar) ); } namespace { // helper function for matrix element for W + Jets with central qqbar template - double amp_WCenqqx_qq( + double amp_WCenqqbar_qq( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & p4, HLV const & pq, HLV const & pqbar, HLV const & pl, HLV const & plbar, HLV const & q11, HLV const & q12 ){ using std::norm; const COM sym = M_sym_W( pa, p1, pb, p4, pq, pqbar, pl, plbar, q11, q12 ); const COM cross = M_cross_W( pa, p1, pb, p4, pq, pqbar, pl, plbar, q11, q12 ); const COM uncross = M_uncross_W( pa, p1, pb, p4, pq, pqbar, pl, plbar, q11, q12 ); // Colour factors static constexpr double cmsms = 3.; static constexpr double cmumu = 4./3.; static constexpr double cmcmc = 4./3.; static constexpr COM cmsmu = COM{0., 3./2.}; static constexpr COM cmsmc = COM{0.,-3./2.}; static constexpr double cmumc = -1./6.; return +cmsms*norm(sym) +cmumu*norm(uncross) +cmcmc*norm(cross) +2.*real(cmsmu*sym*conj(uncross)) +2.*real(cmsmc*sym*conj(cross)) +2.*real(cmumc*uncross*conj(cross)) ; } } // Anonymous Namespace // matrix element for W + Jets with W emission off central qqbar - double ME_WCenqqx_qq( + double ME_WCenqqbar_qq( HLV const & pa, HLV const & pb, HLV const & pl, HLV const & plbar, - std::vector const & partons, bool /* aqlinepa */, bool /* aqlinepb */, bool - qqxmarker, int nabove, ParticleProperties const & wprop + std::vector const & partons, bool /* aqlinepa */, bool /* aqlinepb */, + bool qqbar_marker, int nabove, ParticleProperties const & wprop ){ using helicity::plus; using helicity::minus; CLHEP::HepLorentzVector q1 = pa; for(int i = 0; i <= nabove; ++i){ q1 -= partons[i]; } const auto qq = split_into_lightlike(q1); // since q1.m2() < 0 the following assertion is always true // see documentation for split_into_lightlike assert(qq.second.e() < 0); HLV pq; HLV pqbar; - if (qqxmarker){ + if (qqbar_marker){ pqbar = partons[nabove+1]; pq = partons[nabove+2];} else{ pq = partons[nabove+1]; pqbar = partons[nabove+2]; } const HLV p1 = partons.front(); const HLV p4 = partons.back(); // 4 Different Helicity Choices (Differs from Pure Jet Case, where there is // also the choice in qqbar helicity. // the first helicity label is for aqlinepa == true, // the second one for aqlinepb == true // In principle the corresponding helicity should be flipped // if either of them is false, but we sum up all helicities, // so this has no effect in the end. - const double amp_mm = amp_WCenqqx_qq( + const double amp_mm = amp_WCenqqbar_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); - const double amp_mp = amp_WCenqqx_qq( + const double amp_mp = amp_WCenqqbar_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); - const double amp_pm = amp_WCenqqx_qq( + const double amp_pm = amp_WCenqqbar_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); - const double amp_pp = amp_WCenqqx_qq( + const double amp_pp = amp_WCenqqbar_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); // Divide by t channels, extremal + adjacent central vertex const double ta = (pa-p1).m2(); const double t1 = q1.m2(); const double t3 = (q1-pl-plbar-pq-pqbar).m2(); const double tb = (p4-pb).m2(); const double amp = WProp(plbar, pl, wprop)*( amp_mm+amp_mp+amp_pm+amp_pp )/(9.*4.*ta*t1*t3*tb); return amp; } // helper function for matrix element for W + Jets with central qqbar // W emitted off extremal parton - // @TODO: code duplication with amp_WCenqqx_qq + // @TODO: code duplication with amp_WCenqqbar_qq template - double amp_W_Cenqqx_qq( + double amp_W_Cenqqbar_qq( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & pn, HLV const & pq, HLV const & pqbar, HLV const & pl, HLV const & plbar, HLV const & q11, HLV const & q12 ){ using std::norm; const COM crossed = M_cross( pa, p1, pb, pn, pq, pqbar, pl, plbar, q11, q12 ); const COM uncrossed = M_qbar( pa, p1, pb, pn, pq, pqbar, pl, plbar, q11, q12 ); const COM sym = M_sym( pa, p1, pb, pn, pq, pqbar, pl, plbar, q11, q12 ); //Colour factors: static constexpr double cmsms = 3.; static constexpr double cmumu = 4./3.; static constexpr double cmcmc = 4./3.; static constexpr COM cmsmu = COM{0.,3./2.}; static constexpr COM cmsmc = COM{0.,-3./2.}; static constexpr double cmumc = -1./6.; return +cmsms*norm(sym) +cmumu*norm(uncrossed) +cmcmc*norm(crossed) +2.*real(cmsmu*sym*conj(uncrossed)) +2.*real(cmsmc*sym*conj(crossed)) +2.*real(cmumc*uncrossed*conj(crossed)) ; } // matrix element for W + Jets with W emission *not* off central qqbar - double ME_W_Cenqqx_qq( + double ME_W_Cenqqbar_qq( HLV pa, HLV pb, HLV pl,HLV plbar, std::vector partons, bool aqlinepa, bool aqlinepb, - bool qqxmarker, int nabove, int nbelow, bool forwards, + bool qqbar_marker, int nabove, int nbelow, bool forwards, ParticleProperties const & wprop ){ using helicity::minus; using helicity::plus; if (!forwards){ //If Emission from Leg a instead, flip process. std::swap(pa, pb); std::reverse(partons.begin(),partons.end()); std::swap(aqlinepa, aqlinepb); - qqxmarker = !qqxmarker; + qqbar_marker = !qqbar_marker; std::swap(nabove,nbelow); } HLV q1=pa; for(int i=0;i( + const double amp_mm = amp_W_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); - const double amp_mp = amp_W_Cenqqx_qq( + const double amp_mp = amp_W_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); - const double amp_pm = amp_W_Cenqqx_qq( + const double amp_pm = amp_W_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); - const double amp_pp = amp_W_Cenqqx_qq( + const double amp_pp = amp_W_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); // Divide by t channels, extremal + adjacent central vertex const double ta = (pa-p1).m2(); const double t1 = q1.m2(); const double t3 = (q1 - pq - pqbar).m2(); const double tb = (pn+pl+plbar-pb).m2(); const double amp= WProp(plbar, pl, wprop)*( amp_mm+amp_mp+amp_pm+amp_pp )/(9.*4.*ta*t1*t3*tb); return amp; } } // namespace currents } // namespace HEJ diff --git a/src/YAMLreader.cc b/src/YAMLreader.cc index 2896389..4e66832 100644 --- a/src/YAMLreader.cc +++ b/src/YAMLreader.cc @@ -1,574 +1,574 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/YAMLreader.hh" #include #include #include #include #include #include #include #include #include "HEJ/ConfigFlags.hh" #include "HEJ/Constants.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/event_types.hh" #include "HEJ/output_formats.hh" namespace HEJ { class Event; namespace { //! Get YAML tree of supported options /** * The configuration file is checked against this tree of options * in assert_all_options_known. */ YAML::Node const & get_supported_options(){ const static YAML::Node supported = [](){ YAML::Node supported; static const auto opts = { "trials", "min extparton pt", "max ext soft pt fraction", "soft pt regulator", "scales", "scale factors", "max scale ratio", "import scales", "log correction", "event output", "analysis", "analyses", "vev", "regulator parameter", "max events" }; // add subnodes to "supported" - the assigned value is irrelevant for(auto && opt: opts) supported[opt] = ""; for(auto && jet_opt: {"min pt", "algorithm", "R"}){ supported["resummation jets"][jet_opt] = ""; supported["fixed order jets"][jet_opt] = ""; } for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){ supported["Higgs coupling"][opt] = ""; } for(auto && opt: {"name", "seed"}){ supported["random generator"][opt] = ""; } - for(auto && opt: {"FKL", "unordered", "extremal qqx", "central qqx", "non-resummable"}){ + for(auto && opt: {"FKL", "unordered", "extremal qqbar", "central qqbar", "non-resummable"}){ supported["event treatment"][opt] = ""; } for(auto && particle_type: {"Higgs", "W", "Z"}){ for(auto && particle_opt: {"mass", "width"}){ supported["particle properties"][particle_type][particle_opt] = ""; } } for(auto && opt: {"type", "trials", "max deviation"}){ supported["unweight"][opt] = ""; } return supported; }(); return supported; } fastjet::JetAlgorithm to_JetAlgorithm(std::string const & algo){ using namespace fastjet; static const std::map known = { {"kt", kt_algorithm}, {"cambridge", cambridge_algorithm}, {"antikt", antikt_algorithm}, {"cambridge for passive", cambridge_for_passive_algorithm}, {"plugin", plugin_algorithm} }; const auto res = known.find(algo); if(res == known.end()){ throw std::invalid_argument("Unknown jet algorithm \"" + algo + "\""); } return res->second; } EventTreatment to_EventTreatment(std::string const & name){ static const std::map known = { {"reweight", EventTreatment::reweight}, {"keep", EventTreatment::keep}, {"discard", EventTreatment::discard} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown event treatment \"" + name + "\""); } return res->second; } WeightType to_weight_type(std::string const & setting){ if(setting == "weighted") return WeightType::weighted; if(setting =="resummation") return WeightType::unweighted_resum; if(setting =="partial") return WeightType::partially_unweighted; throw std::invalid_argument{"Unknown weight type \"" + setting + "\""}; } } // namespace namespace detail{ void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml){ setting = to_JetAlgorithm(yaml.as()); } void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml){ setting = to_EventTreatment(yaml.as()); } void set_from_yaml(ParticleID & setting, YAML::Node const & yaml){ setting = to_ParticleID(yaml.as()); } void set_from_yaml(WeightType & setting, YAML::Node const & yaml){ setting = to_weight_type(yaml.as()); } } // namespace detail JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ){ assert(node); JetParameters result; fastjet::JetAlgorithm jet_algo = fastjet::antikt_algorithm; double R = NAN; set_from_yaml_if_defined(jet_algo, node, entry, "algorithm"); set_from_yaml(R, node, entry, "R"); result.def = fastjet::JetDefinition{jet_algo, R}; set_from_yaml(result.min_pt, node, entry, "min pt"); return result; } RNGConfig to_RNGConfig( YAML::Node const & node, std::string const & entry ){ assert(node); RNGConfig result; set_from_yaml(result.name, node, entry, "name"); set_from_yaml_if_defined(result.seed, node, entry, "seed"); return result; } ParticleProperties get_particle_properties( YAML::Node const & node, std::string const & entry, std::string const & boson ){ ParticleProperties result{}; set_from_yaml(result.mass, node, entry, boson, "mass"); set_from_yaml(result.width, node, entry, boson, "width"); return result; } EWConstants get_ew_parameters(YAML::Node const & node){ EWConstants result; double vev = NAN; set_from_yaml(vev, node, "vev"); result.set_vevWZH(vev, get_particle_properties(node, "particle properties", "W"), get_particle_properties(node, "particle properties", "Z"), get_particle_properties(node, "particle properties", "Higgs") ); return result; } HiggsCouplingSettings get_Higgs_coupling( YAML::Node const & node, std::string const & entry ){ assert(node); static constexpr double mt_max = 2e4; #ifndef HEJ_BUILD_WITH_QCDLOOP if(node[entry].IsDefined()){ throw std::invalid_argument{ "Higgs coupling settings require building HEJ 2 " "with QCDloop support" }; } #endif HiggsCouplingSettings settings; set_from_yaml_if_defined(settings.mt, node, entry, "mt"); set_from_yaml_if_defined(settings.mb, node, entry, "mb"); set_from_yaml_if_defined(settings.include_bottom, node, entry, "include bottom"); set_from_yaml_if_defined(settings.use_impact_factors, node, entry, "use impact factors"); if(settings.use_impact_factors){ if(settings.mt != std::numeric_limits::infinity()){ throw std::invalid_argument{ "Conflicting settings: " "impact factors may only be used in the infinite top mass limit" }; } } else{ // huge values of the top mass are numerically unstable settings.mt = std::min(settings.mt, mt_max); } return settings; } FileFormat to_FileFormat(std::string const & name){ static const std::map known = { {"Les Houches", FileFormat::Les_Houches}, {"HepMC", FileFormat::HepMC}, {"HepMC2", FileFormat::HepMC2}, {"HepMC3", FileFormat::HepMC3}, {"HDF5", FileFormat::HDF5} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown file format \"" + name + "\""); } return res->second; } std::string extract_suffix(std::string const & filename){ size_t separator = filename.rfind('.'); if(separator == std::string::npos) return {}; return filename.substr(separator + 1); } FileFormat format_from_suffix(std::string const & filename){ const std::string suffix = extract_suffix(filename); if(suffix == "lhe") return FileFormat::Les_Houches; if(suffix == "hepmc") return FileFormat::HepMC; if(suffix == "hepmc3") return FileFormat::HepMC3; if(suffix == "hepmc2") return FileFormat::HepMC2; if(suffix == "hdf5") return FileFormat::HDF5; throw std::invalid_argument{ "Can't determine format for output file \"" + filename + "\"" }; } void assert_all_options_known( YAML::Node const & conf, YAML::Node const & supported ){ if(!conf.IsMap()) return; if(!supported.IsMap()) throw invalid_type{"must not have sub-entries"}; for(auto const & entry: conf){ const auto name = entry.first.as(); if(! supported[name]) throw unknown_option{name}; /* check sub-options, e.g. 'resummation jets: min pt' * we don't check analyses sub-options * those depend on the analysis being used and should be checked there * similar for "import scales" */ if(name != "analyses" && name != "analysis" && name != "import scales"){ try{ assert_all_options_known(conf[name], supported[name]); } catch(unknown_option const & ex){ throw unknown_option{name + ": " + ex.what()}; } catch(invalid_type const & ex){ throw invalid_type{name + ": " + ex.what()}; } } } } } // namespace HEJ namespace YAML { Node convert::encode(HEJ::OutputFile const & outfile) { Node node; node[to_string(outfile.format)] = outfile.name; return node; } bool convert::decode(Node const & node, HEJ::OutputFile & out) { switch(node.Type()){ case NodeType::Map: { YAML::const_iterator it = node.begin(); out.format = HEJ::to_FileFormat(it->first.as()); out.name = it->second.as(); return true; } case NodeType::Scalar: out.name = node.as(); out.format = HEJ::format_from_suffix(out.name); return true; default: return false; } } } // namespace YAML namespace HEJ { namespace detail{ void set_from_yaml(OutputFile & setting, YAML::Node const & yaml){ setting = yaml.as(); } } namespace { void update_fixed_order_jet_parameters( JetParameters & fixed_order_jets, YAML::Node const & yaml ){ if(!yaml["fixed order jets"]) return; set_from_yaml_if_defined( fixed_order_jets.min_pt, yaml, "fixed order jets", "min pt" ); fastjet::JetAlgorithm algo = fixed_order_jets.def.jet_algorithm(); set_from_yaml_if_defined(algo, yaml, "fixed order jets", "algorithm"); double R = fixed_order_jets.def.R(); set_from_yaml_if_defined(R, yaml, "fixed order jets", "R"); fixed_order_jets.def = fastjet::JetDefinition{algo, R}; } // like std::stod, but throw if not the whole string can be converted double to_double(std::string const & str){ std::size_t pos = 0; const double result = std::stod(str, &pos); if(pos < str.size()){ throw std::invalid_argument(str + " is not a valid double value"); } return result; } using EventScale = double (*)(Event const &); void import_scale_functions( std::string const & file, std::vector const & scale_names, std::unordered_map & known ) { void * handle = dlopen(file.c_str(), RTLD_NOW); char * error = dlerror(); if(error != nullptr) throw std::runtime_error{error}; for(auto const & scale: scale_names) { void * sym = dlsym(handle, scale.c_str()); error = dlerror(); if(error != nullptr) throw std::runtime_error{error}; known.emplace(scale, reinterpret_cast(sym)); // NOLINT } } auto get_scale_map( YAML::Node const & yaml ) { std::unordered_map scale_map; scale_map.emplace("H_T", H_T); scale_map.emplace("max jet pperp", max_jet_pt); scale_map.emplace("jet invariant mass", jet_invariant_mass); scale_map.emplace("m_j1j2", m_j1j2); if(yaml["import scales"].IsDefined()) { if(! yaml["import scales"].IsMap()) { throw invalid_type{"Entry 'import scales' is not a map"}; } for(auto const & import: yaml["import scales"]) { const auto file = import.first.as(); const auto scale_names = import.second.IsSequence() ?import.second.as>() :std::vector{import.second.as()}; import_scale_functions(file, scale_names, scale_map); } } return scale_map; } // simple (as in non-composite) scale functions /** * An example for a simple scale function would be H_T, * H_T/2 is then composite (take H_T and then divide by 2) */ ScaleFunction parse_simple_ScaleFunction( std::string const & scale_fun, std::unordered_map const & known ) { assert( scale_fun.empty() || (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back())) ); const auto it = known.find(scale_fun); if(it != end(known)) return {it->first, it->second}; try{ const double scale = to_double(scale_fun); return {scale_fun, FixedScale{scale}}; } catch(std::invalid_argument const &){} throw std::invalid_argument{"Unknown scale choice: \"" + scale_fun + "\""}; } std::string trim_front(std::string const & str){ const auto new_begin = std::find_if( begin(str), end(str), [](char c){ return std::isspace(c) == 0; } ); return std::string(new_begin, end(str)); } std::string trim_back(std::string str){ size_t pos = str.size() - 1; // use guaranteed wrap-around behaviour to check whether we have // traversed the whole string for(; pos < str.size() && std::isspace(str[pos]); --pos) {} str.resize(pos + 1); // note that pos + 1 can be 0 return str; } ScaleFunction parse_ScaleFunction( std::string const & scale_fun, std::unordered_map const & known ){ assert( scale_fun.empty() || (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back())) ); // parse from right to left => a/b/c gives (a/b)/c const size_t delim = scale_fun.find_last_of("*/"); if(delim == std::string::npos){ return parse_simple_ScaleFunction(scale_fun, known); } const std::string first = trim_back(std::string{scale_fun, 0, delim}); const std::string second = trim_front(std::string{scale_fun, delim+1}); if(scale_fun[delim] == '/'){ return parse_ScaleFunction(first, known) / parse_ScaleFunction(second, known); } assert(scale_fun[delim] == '*'); return parse_ScaleFunction(first, known) * parse_ScaleFunction(second, known); } EventTreatMap get_event_treatment( YAML::Node const & node, std::string const & entry ){ using namespace event_type; EventTreatMap treat { {no_2_jets, EventTreatment::discard}, {bad_final_state, EventTreatment::discard}, {FKL, EventTreatment::discard}, {unob, EventTreatment::discard}, {unof, EventTreatment::discard}, - {qqxexb, EventTreatment::discard}, - {qqxexf, EventTreatment::discard}, - {qqxmid, EventTreatment::discard}, + {qqbar_exb, EventTreatment::discard}, + {qqbar_exf, EventTreatment::discard}, + {qqbar_mid, EventTreatment::discard}, {non_resummable, EventTreatment::discard} }; set_from_yaml(treat.at(FKL), node, entry, "FKL"); set_from_yaml(treat.at(unob), node, entry, "unordered"); treat.at(unof) = treat.at(unob); - set_from_yaml(treat.at(qqxexb), node, entry, "extremal qqx"); - treat.at(qqxexf) = treat.at(qqxexb); - set_from_yaml(treat.at(qqxmid), node, entry, "central qqx"); + set_from_yaml(treat.at(qqbar_exb), node, entry, "extremal qqbar"); + treat.at(qqbar_exf) = treat.at(qqbar_exb); + set_from_yaml(treat.at(qqbar_mid), node, entry, "central qqbar"); set_from_yaml(treat.at(non_resummable), node, entry, "non-resummable"); if(treat[non_resummable] == EventTreatment::reweight){ throw std::invalid_argument{"Cannot reweight non-resummable events"}; } return treat; } Config to_Config(YAML::Node const & yaml){ try{ assert_all_options_known(yaml, get_supported_options()); } catch(unknown_option const & ex){ throw unknown_option{std::string{"Unknown option '"} + ex.what() + "'"}; } Config config; config.resummation_jets = get_jet_parameters(yaml, "resummation jets"); config.fixed_order_jets = config.resummation_jets; update_fixed_order_jet_parameters(config.fixed_order_jets, yaml); set_from_yaml_if_defined(config.min_extparton_pt, yaml, "min extparton pt"); if(config.min_extparton_pt!=0) std::cerr << "WARNING: \"min extparton pt\" is deprecated." << " Please remove this entry or set \"soft pt regulator\" instead.\n"; set_from_yaml_if_defined( config.max_ext_soft_pt_fraction, yaml, "max ext soft pt fraction" ); if(config.max_ext_soft_pt_fraction){ std::cerr << "WARNING: \"max ext soft pt fraction\" is deprecated." << " Please remove this entry or set \"soft pt regulator\" instead.\n"; config.soft_pt_regulator = *config.max_ext_soft_pt_fraction; } else { set_from_yaml_if_defined( config.soft_pt_regulator, yaml, "soft pt regulator" ); } // Sets the standard value, then changes this if defined config.regulator_lambda=CLAMBDA; set_from_yaml_if_defined(config.regulator_lambda, yaml, "regulator parameter"); set_from_yaml_if_defined(config.max_events, yaml, "max events"); set_from_yaml(config.trials, yaml, "trials"); config.weight_type = WeightType::weighted; set_from_yaml_if_defined(config.weight_type, yaml, "unweight", "type"); if(config.weight_type == WeightType::partially_unweighted) { config.unweight_config = PartialUnweightConfig{}; set_from_yaml( config.unweight_config->trials, yaml, "unweight", "trials" ); set_from_yaml( config.unweight_config->max_dev, yaml, "unweight", "max deviation" ); } else if(yaml["unweight"].IsDefined()) { for(auto && opt: {"trials", "max deviation"}) { if(yaml["unweight"][opt].IsDefined()) { throw std::invalid_argument{ "'unweight: " + std::string{opt} + "' " "is only supported if 'unweight: type' is set to 'partial'" }; } } } set_from_yaml(config.log_correction, yaml, "log correction"); config.treat = get_event_treatment(yaml, "event treatment"); set_from_yaml_if_defined(config.output, yaml, "event output"); config.rng = to_RNGConfig(yaml, "random generator"); set_from_yaml_if_defined(config.analyses_parameters, yaml, "analyses"); if(yaml["analysis"].IsDefined()){ std::cerr << "WARNING: Configuration entry 'analysis' is deprecated. " " Use 'analyses' instead.\n"; set_from_yaml(config.analysis_parameters, yaml, "analysis"); if(!config.analysis_parameters.IsNull()){ config.analyses_parameters.push_back(config.analysis_parameters); } } config.scales = to_ScaleConfig(yaml); config.ew_parameters = get_ew_parameters(yaml); config.Higgs_coupling = get_Higgs_coupling(yaml, "Higgs coupling"); return config; } } // namespace ScaleConfig to_ScaleConfig(YAML::Node const & yaml){ ScaleConfig config; auto scale_funs = get_scale_map(yaml); std::vector scales; set_from_yaml(scales, yaml, "scales"); config.base.reserve(scales.size()); std::transform( begin(scales), end(scales), std::back_inserter(config.base), [scale_funs](auto const & entry){ return parse_ScaleFunction(entry, scale_funs); } ); set_from_yaml_if_defined(config.factors, yaml, "scale factors"); config.max_ratio = std::numeric_limits::infinity(); set_from_yaml_if_defined(config.max_ratio, yaml, "max scale ratio"); return config; } Config load_config(std::string const & config_file){ try{ return to_Config(YAML::LoadFile(config_file)); } catch(...){ std::cerr << "Error reading " << config_file << ":\n "; throw; } } } // namespace HEJ diff --git a/src/jets.cc b/src/jets.cc index 88efa02..cef204e 100644 --- a/src/jets.cc +++ b/src/jets.cc @@ -1,355 +1,355 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/jets.hh" #include #include #include #include "HEJ/Constants.hh" // generated headers #include "HEJ/currents/j_j.hh" #include "HEJ/currents/j_jqqbar.hh" #include "HEJ/currents/j_qqbar_j.hh" #include "HEJ/currents/juno_j.hh" namespace { // short hand for math functions using std::conj; } // Anonymous Namespace namespace HEJ { namespace currents { // Colour acceleration multiplier for gluons see eq. (7) in arXiv:0910.5113 // @TODO: this is not a current and should be moved somewhere else double K_g(double p1minus, double paminus) { return 1./2.*(p1minus/paminus + paminus/p1minus)*(C_A - 1./C_A) + 1./C_A; } double K_g( HLV const & pout, HLV const & pin ) { if(pin.z() > 0) return K_g(pout.plus(), pin.plus()); return K_g(pout.minus(), pin.minus()); } namespace { //@{ /** * @brief Pure Jet FKL Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1. * @param p1in Incoming Particle 1. * @param p2out Outgoing Particle 2 * @param p2in Incoming Particle 2 * * Calculates j_\mu j^\mu. * Handles all possible incoming states. Helicity doesn't matter since we sum * over all of them. In addition, we only have to distinguish between the two * possibilities of contracting same-helicity or opposite-helicity currents. */ double j_j(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ using helicity::plus; using helicity::minus; COM const Mp = HEJ::j_j(p1in, p1out, p2in, p2out); COM const Mm = HEJ::j_j(p1in, p1out, p2in, p2out); double const sst = std::norm(Mm) + std::norm(Mp); HLV const q1=p1in-p1out; HLV const q2=-(p2in-p2out); // Multiply by Cf^2 (colour) * 2 (helicities) return 2.*C_F*C_F*(sst)/(q1.m2()*q2.m2()); } } // Anonymous Namespace double ME_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return j_j(p1out, p1in, p2out, p2in); } double ME_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return j_j(p1out, p1in, p2out, p2in); } double ME_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return j_j(p1out, p1in, p2out, p2in); } double ME_qg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return j_j(p1out, p1in, p2out, p2in)*K_g(p2out, p2in)/C_F; } double ME_qbarg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return j_j(p1out, p1in, p2out, p2in)*K_g(p2out, p2in)/(C_F); } double ME_gg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return j_j(p1out, p1in, p2out, p2in)*K_g(p1out, p1in)*K_g(p2out, p2in)/(C_F*C_F); } //@} namespace { template double amp_juno_j( HLV const & pa, HLV const & pb, HLV const & pg, HLV const & p1, HLV const & p2 ) { // TODO: code duplication with Wjets const COM u1 = U1_j(pa,p1,pb,p2,pg); const COM u2 = U2_j(pa,p1,pb,p2,pg); const COM l = L_j(pa,p1,pb,p2,pg); const COM x = u1 - l; const COM y = u2 + l; return C_A*C_F*C_F/2.*(norm(x)+norm(y)) - C_F/2.*(x*conj(y)).real(); } double juno_j(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ using helicity::minus; using helicity::plus; const HLV q2 = p2out - p2in; // Bottom End const HLV qg = p1in - p1out - pg; // Extra bit post-gluon // only calculate half of the helicity amplitudes, // the remaining ones follow from CP symmetry const double amm = amp_juno_j(p1in, p2in, pg, p1out, p2out); const double amp = amp_juno_j(p1in, p2in, pg, p1out, p2out); const double apm = amp_juno_j< plus, minus>(p1in, p2in, pg, p1out, p2out); const double app = amp_juno_j< plus, plus>(p1in, p2in, pg, p1out, p2out); constexpr double hel_fac = 2.; return hel_fac/(4.*C_A*C_A)*(amm + amp + apm + app)/(q2.m2()*qg.m2()); } } // Anonymous Namespace //Unordered bits for pure jet double ME_unob_qQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qbarQ(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qbarQbar(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return juno_j(pg, p1out, p1in, p2out, p2in); } double ME_unob_qg( HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return juno_j(pg, p1out, p1in, p2out, p2in)*K_g(p2out,p2in)/C_F; } double ME_unob_qbarg(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ return juno_j(pg, p1out, p1in, p2out, p2in)*K_g(p2out,p2in)/C_F; } namespace { // helicity amplitudes for j jqqbar contraction template double amp_j_jqqbar( HLV const & pa, HLV const & pb, HLV const & p1, HLV const & p2, HLV const & p3 ) { // TODO: code duplication with Wjets.cc using std::norm; static constexpr double cm1m1 = 8./3.; static constexpr double cm2m2 = 8./3.; static constexpr double cm3m3 = 6.; static constexpr double cm1m2 =-1./3.; static constexpr COM cm1m3 = COM{0.,-3.}; static constexpr COM cm2m3 = COM{0.,3.}; const COM m1 = j_qggm1(pb,p2,p3,pa,p1); const COM m2 = j_qggm2(pb,p2,p3,pa,p1); const COM m3 = j_qggm3(pb,p2,p3,pa,p1); return + cm1m1*norm(m1) + cm2m2*norm(m2) + cm3m3*norm(m3) + 2.*real(cm1m2*m1*conj(m2)) + 2.*real(cm1m3*m1*conj(m3)) + 2.*real(cm2m3*m2*conj(m3)) ; } //Now the function to give helicity/colour sum/average double MqgtqQQ(HLV const & pa, HLV const & pb, HLV const & p1, HLV const & p2, HLV const & p3 ){ using helicity::minus; using helicity::plus; const double Mmmm = amp_j_jqqbar(pa, pb, p1, p2, p3); const double Mmmp = amp_j_jqqbar(pa, pb, p1, p2, p3); const double Mpmm = amp_j_jqqbar< plus, minus>(pa, pb, p1, p2, p3); const double Mpmp = amp_j_jqqbar< plus, plus>(pa, pb, p1, p2, p3); // Factor of 2 for the helicity for conjugate configurations return (2.*(Mmmm+Mmmp+Mpmm+Mpmp)/3.)/(pa-p1).m2()/(p2+p3-pb).m2(); } } // Anonymous Namespace - // Extremal qqx - double ME_Exqqx_qbarqQ(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, HLV const & p2in + // Extremal qqbar + double ME_Exqqbar_qbarqQ(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, HLV const & p2in ){ return MqgtqQQ(p2in, pgin, p2out, pqout, pqbarout); } - double ME_Exqqx_qqbarQ(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, HLV const & p2in + double ME_Exqqbar_qqbarQ(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, HLV const & p2in ){ return MqgtqQQ(p2in, pgin, p2out, pqbarout, pqout); } - double ME_Exqqx_qbarqg(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, HLV const & p2in + double ME_Exqqbar_qbarqg(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, HLV const & p2in ){ return MqgtqQQ(p2in, pgin, p2out, pqout, pqbarout)*K_g(p2out,p2in)/C_F; } - double ME_Exqqx_qqbarg(HLV const & pgin, HLV const & pqout, - HLV const & pqbarout, HLV const & p2out, HLV const & p2in + double ME_Exqqbar_qqbarg(HLV const & pgin, HLV const & pqout, + HLV const & pqbarout, HLV const & p2out, HLV const & p2in ){ return MqgtqQQ(p2in, pgin, p2out, pqbarout, pqout)*K_g(p2out,p2in)/C_F; } namespace { // helicity amplitudes for matrix element with central qqbar template - double amp_Cenqqx_qq( + double amp_Cenqqbar_qq( HLV const & pa, HLV const & p1, HLV const & pb, HLV const & p4, HLV const & pq, HLV const & pqbar, HLV const & q11, HLV const & q12 ){ using std::norm; const COM sym = M_sym( pa, p1, pb, p4, pq, pqbar, q11, q12 ); const COM cross = M_cross( pa, p1, pb, p4, pq, pqbar, q11, q12 ); const COM uncross = M_qbar( pa, p1, pb, p4, pq, pqbar, q11, q12 ); // Colour factors static constexpr double cmsms = 3.; static constexpr double cmumu = 4./3.; static constexpr double cmcmc = 4./3.; static constexpr COM cmsmu = COM{0., 3./2.}; static constexpr COM cmsmc = COM{0.,-3./2.}; static constexpr double cmumc = -1./6.; return +cmsms*norm(sym) +cmumu*norm(uncross) +cmcmc*norm(cross) +2.*real(cmsmu*sym*conj(uncross)) +2.*real(cmsmc*sym*conj(cross)) +2.*real(cmumc*uncross*conj(cross)) ; } } // Anonymous Namespace - double ME_Cenqqx_qq( + double ME_Cenqqbar_qq( HLV const & pa, HLV const & pb, std::vector const & partons, bool /* aqlinepa */, bool /* aqlinepb */, - const bool qqxmarker, const std::size_t nabove + const bool qqbarmarker, const std::size_t nabove ){ using helicity::plus; using helicity::minus; CLHEP::HepLorentzVector q1 = pa; for(std::size_t i = 0; i <= nabove; ++i){ q1 -= partons[i]; } const auto qq = split_into_lightlike(q1); // since q1.m2() < 0 the following assertion is always true // see documentation for split_into_lightlike assert(qq.second.e() < 0); HLV pq = partons[nabove+1]; HLV pqbar = partons[nabove+2]; - if(qqxmarker) std::swap(pq, pqbar); + if(qqbarmarker) std::swap(pq, pqbar); const HLV p1 = partons.front(); const HLV pn = partons.back(); // 8 helicity choices, but only 4 independent ones //(complex conjugation related). // In principle, the proper helicity labeling depends on // whether we have antiquark lines (aqlinea and aqlineb). // However, this only corresponds to a relabeling, // which doesn't change the sum over all helicities below. - const double amp_mm = amp_Cenqqx_qq( + const double amp_mm = amp_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, qq.first, -qq.second ); - const double amp_mp = amp_Cenqqx_qq( + const double amp_mp = amp_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, qq.first, -qq.second ); - const double amp_pm = amp_Cenqqx_qq( + const double amp_pm = amp_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, qq.first, -qq.second ); - const double amp_pp = amp_Cenqqx_qq( + const double amp_pp = amp_Cenqqbar_qq( pa, p1, pb, pn, pq, pqbar, qq.first, -qq.second ); //Result (averaged, without coupling or t-channel props). Factor of //2 for the 4 helicity configurations I didn't work out explicitly const HLV q3 = q1 - pq - pqbar; return (2.*(amp_mm+amp_mp+amp_pm+amp_pp)/9./4.) / ((pa-p1).m2()*(pb-pn).m2()*q1.m2()*q3.m2()); } } // namespace currents } // namespace HEJ diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index 60c8e55..53cdf2f 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -1,505 +1,505 @@ set(tst_dir "${CMAKE_CURRENT_SOURCE_DIR}") set(tst_ME_data_dir "${tst_dir}/ME_data") # small library for common test functions add_library(hej_test SHARED hej_test.cc) target_include_directories(hej_test PUBLIC ${tst_dir}) target_link_libraries(hej_test HEJ) # test event classification # test explicit configurations add_executable(test_classify ${tst_dir}/test_classify.cc) target_compile_options(test_classify PRIVATE "-O0") # avoid compiler optimisation target_link_libraries(test_classify HEJ hej_test) add_test( NAME classify COMMAND test_classify ) # test against reference data add_executable(test_classify_ref ${tst_dir}/test_classify_ref.cc) target_link_libraries(test_classify_ref HEJ hej_test) add_test( NAME classify_ref COMMAND test_classify_ref ${tst_dir}/classify_ref ${tst_dir}/classify.lhe.gz ) add_test( NAME classify_ref_4j COMMAND test_classify_ref ${tst_dir}/classify_ref_4j ${tst_dir}/4j.lhe.gz ) add_test( NAME classify_ref_W4j COMMAND test_classify_ref ${tst_dir}/classify_ref_W4j ${tst_dir}/MGemnubar4j_unweighted.lhe.gz ) add_test( NAME t_classify_ref_Z4j COMMAND test_classify_ref ${tst_dir}/classify_ref_Z4j ${tst_dir}/Z_4j.lhe.gz ) # test for valid W decays add_executable(test_decay ${tst_dir}/test_decay.cc) target_link_libraries(test_decay HEJ hej_test) add_test( NAME valid_decay COMMAND test_decay ) # test valid jet cuts on tagging jets add_executable(test_jet_cuts ${tst_dir}/test_jet_cuts.cc) target_link_libraries(test_jet_cuts HEJ hej_test) add_test( NAME jet_cuts COMMAND test_jet_cuts ) # test phase space point add_executable(test_psp ${tst_dir}/test_psp.cc) target_link_libraries(test_psp HEJ hej_test) add_test( NAME PhaseSpace COMMAND test_psp ${tst_dir}/psp_gen.lhe.gz ) # test importing analyses file(COPY "${tst_dir}/analysis_config.yml" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") set(test_config "${CMAKE_CURRENT_BINARY_DIR}/analysis_config.yml") get_target_property(ANALYSIS_PATH AnalysisTemplate_lib BINARY_DIR) get_target_property(ANALYSIS_LIB AnalysisTemplate_lib OUTPUT_NAME) set(ANALYSIS_PARAMETERS "") file(READ ${test_config} config) file(WRITE ${test_config} "${config} - plugin: ${ANALYSIS_PATH}/lib${ANALYSIS_LIB}.so\n ${ANALYSIS_PARAMETERS}\n") configure_file( ${tst_dir}/analysis_config_single.yml.in analysis_config_simple.yml @ONLY ) add_test( NAME analysis_simple COMMAND $ analysis_config_simple.yml ${tst_dir}/2j.lhe.gz ) get_target_property(ANALYSIS_PATH AnalysisPrint_lib BINARY_DIR) get_target_property(ANALYSIS_LIB AnalysisPrint_lib OUTPUT_NAME) set(ANALYSIS_PARAMETERS " output: ana_output") file(READ ${test_config} config) file(WRITE ${test_config} "${config} - plugin: ${ANALYSIS_PATH}/lib${ANALYSIS_LIB}.so\n ${ANALYSIS_PARAMETERS}\n") configure_file( ${tst_dir}/analysis_config_single.yml.in analysis_config_print.yml @ONLY ) add_test( NAME analysis_print COMMAND $ analysis_config_print.yml ${tst_dir}/2j.lhe.gz ) if(ROOT_FOUND) get_target_property(ANALYSIS_PATH AnalysisROOT_lib BINARY_DIR) get_target_property(ANALYSIS_LIB AnalysisROOT_lib OUTPUT_NAME) set(ANALYSIS_PARAMETERS "") file(READ ${test_config} config) file(WRITE ${test_config} "${config} - plugin: ${ANALYSIS_PATH}/lib${ANALYSIS_LIB}.so\n ${ANALYSIS_PARAMETERS}\n") configure_file( ${tst_dir}/analysis_config_single.yml.in analysis_config_root.yml @ONLY ) add_test( NAME analysis_root COMMAND $ analysis_config_root.yml ${tst_dir}/2j.lhe.gz ) endif() if(RIVET_FOUND) file(READ ${test_config} config) file(WRITE ${test_config} "${config} - rivet: MC_XS\n output: ana_rivet\n") endif() add_test( NAME analysis_all COMMAND $ ${test_config} ${tst_dir}/2j.lhe.gz ) # test importing scales (from examples/softestptScale) add_executable(test_scale_import ${tst_dir}/test_scale_import) target_link_libraries(test_scale_import HEJ) get_target_property(SCALE_PATH softestptScale_lib BINARY_DIR) get_target_property(SCALE_LIB softestptScale_lib OUTPUT_NAME) set(SCALE_NAME "softest_jet_pt") configure_file( ${tst_dir}/jet_config_with_import.yml.in jet_config_with_import.yml @ONLY ) add_test( NAME scale_import COMMAND test_scale_import jet_config_with_import.yml ) # test scale arithmetic (e.g. 2*H_T/4) add_executable(test_scale_arithmetics ${tst_dir}/test_scale_arithmetics) target_link_libraries(test_scale_arithmetics HEJ hej_test) add_test( NAME scale_arithmetics COMMAND test_scale_arithmetics ${tst_dir}/jet_config.yml ${tst_dir}/2j.lhe.gz ) # test "ParameterDescription" add_executable(test_descriptions ${tst_dir}/test_descriptions) target_link_libraries(test_descriptions HEJ hej_test) add_test( NAME descriptions COMMAND test_descriptions ) # test "EventParameters*Weight" add_executable(test_parameters ${tst_dir}/test_parameters) target_link_libraries(test_parameters HEJ hej_test) add_test( NAME parameters COMMAND test_parameters ) # test Particle IDs add_executable(test_pid ${tst_dir}/test_pid) target_link_libraries(test_pid HEJ hej_test) add_test( NAME pid COMMAND test_pid ) # test EW constants add_executable(test_EWConstants ${tst_dir}/test_EWConstants) target_link_libraries(test_EWConstants HEJ hej_test) add_test( NAME EWConstants COMMAND test_EWConstants ) # test unweighting add_executable(test_unweighter ${tst_dir}/test_unweighter) target_link_libraries(test_unweighter HEJ hej_test) add_test( NAME unweighter COMMAND test_unweighter ${tst_dir}/4j.lhe.gz ) # test colour generation add_executable(test_colours ${tst_dir}/test_colours) target_link_libraries(test_colours HEJ hej_test) add_test( NAME colour_flow COMMAND test_colours ) add_executable(test_colours2 ${tst_dir}/test_colours2) target_link_libraries(test_colours2 HEJ hej_test) add_test( NAME colour_flow2 COMMAND test_colours2 ) # test matrix elements add_executable(test_ME_generic ${tst_dir}/test_ME_generic.cc) target_link_libraries(test_ME_generic HEJ hej_test) add_test( NAME ME_j COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtinf.yml ${tst_ME_data_dir}/ME_jets_tree.dat ${tst_ME_data_dir}/PSP_jets.lhe.gz ) add_test( NAME ME_j_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtinf.yml ${tst_ME_data_dir}/ME_jets_tree_virt.dat ${tst_ME_data_dir}/PSP_jets.lhe.gz ) add_test( NAME ME_h COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtinf.yml ${tst_ME_data_dir}/ME_h_mtinf_tree.dat ${tst_ME_data_dir}/PSP_h.lhe.gz ) add_test( NAME ME_h_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtinf.yml ${tst_ME_data_dir}/ME_h_mtinf_virt.dat ${tst_ME_data_dir}/PSP_h.lhe.gz ) if(QCDloop_FOUND) add_test( NAME ME_h_mt COMMAND test_ME_generic ${tst_ME_data_dir}/config_mt.yml ${tst_ME_data_dir}/ME_h_mt_tree.dat ${tst_ME_data_dir}/PSP_h.lhe.gz ) add_test( NAME ME_h_mtmb COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtmb.yml ${tst_ME_data_dir}/ME_h_mtmb_tree.dat ${tst_ME_data_dir}/PSP_h.lhe.gz ) endif() add_test( NAME ME_j_subl COMMAND test_ME_generic ${tst_ME_data_dir}/config_pure.yml ${tst_ME_data_dir}/ME_jets_subl_tree.dat ${tst_ME_data_dir}/PSP_jets_subl.lhe.gz ) add_test( NAME ME_j_subl_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_pure.yml ${tst_ME_data_dir}/ME_jets_subl_tree_virt.dat ${tst_ME_data_dir}/PSP_jets_subl.lhe.gz ) add_test( NAME ME_j_subl_4j COMMAND test_ME_generic ${tst_ME_data_dir}/config_pure.yml ${tst_ME_data_dir}/ME_jets_subl_tree_new.dat ${tst_dir}/4j.lhe.gz ) add_test( NAME ME_j_subl_4j_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_pure.yml ${tst_ME_data_dir}/ME_jets_subl_tree_new_virt.dat ${tst_dir}/4j.lhe.gz ) add_test( NAME ME_w_FKL COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_w_FKL_tree.dat ${tst_ME_data_dir}/PSP_w_FKL.lhe.gz ) add_test( NAME ME_w_FKL_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_w_FKL_virt.dat ${tst_ME_data_dir}/PSP_w_FKL.lhe.gz ) add_test( NAME ME_Wp COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_Wp.dat ${tst_ME_data_dir}/PSP_Wp.lhe.gz ) add_test( NAME ME_Wp_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_Wp_virt.dat ${tst_ME_data_dir}/PSP_Wp.lhe.gz ) add_test( NAME ME_Wm COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_Wm.dat ${tst_ME_data_dir}/PSP_Wm.lhe.gz ) add_test( NAME ME_Wm_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_Wm_virt.dat ${tst_ME_data_dir}/PSP_Wm.lhe.gz ) add_test( NAME t_ME_Z_FKL COMMAND test_ME_generic ${tst_ME_data_dir}/config_Z_ME.yml ${tst_ME_data_dir}/ME_Z_FKL_tree.dat ${tst_ME_data_dir}/PSP_Z_FKL.lhe.gz ) add_test( NAME t_ME_Z_FKL_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_Z_ME.yml ${tst_ME_data_dir}/ME_Z_FKL_virt.dat ${tst_ME_data_dir}/PSP_Z_FKL.lhe.gz ) add_test( NAME t_ME_Z_uno COMMAND test_ME_generic ${tst_ME_data_dir}/config_Z_ME.yml ${tst_ME_data_dir}/ME_Z_uno_tree.dat ${tst_ME_data_dir}/PSP_Z_uno.lhe.gz ) add_test( NAME t_ME_Z_uno_virt COMMAND test_ME_generic ${tst_ME_data_dir}/config_Z_ME.yml ${tst_ME_data_dir}/ME_Z_uno_virt.dat ${tst_ME_data_dir}/PSP_Z_uno.lhe.gz ) # test main executable file(COPY "${tst_dir}/jet_config.yml" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") set(test_config "${CMAKE_CURRENT_BINARY_DIR}/jet_config.yml") if(HighFive_FOUND) file(READ ${test_config} config) file(WRITE ${test_config} "${config} - tst.hdf5\n") endif() if(HepMC3_FOUND) file(READ ${test_config} config) file(WRITE ${test_config} "${config} - tst.hepmc\n") endif() if(HepMC_FOUND) file(READ ${test_config} config) file(WRITE ${test_config} "${config} - tst.hepmc2\n") endif() if(rivet_FOUND) file(READ ${test_config} config) file(WRITE ${test_config} "${config}\nanalysis:\n rivet: MC_XS\n output: tst\n") endif() set(test_cmd_main "$\\\;${test_config}\\\;${tst_dir}/2j.lhe.gz") # check that HepMC3 output is correct if(HepMC3_FOUND) add_executable(check_hepmc ${tst_dir}/check_hepmc.cc) target_link_libraries(check_hepmc HEJ ${HEPMC3_LIBRARIES}) target_include_directories(check_hepmc PRIVATE ${HEPMC3_INCLUDE_DIR}) set(test_cmd_hepmc "$\\\;tst.hepmc") else() set(test_cmd_hepmc "") endif() # check that LHEF output is correct add_executable(check_lhe ${tst_dir}/check_lhe.cc) target_link_libraries(check_lhe HEJ hej_test) set(test_cmd_lhe "$\\\;tst.lhe") # check that rivet interface is consistent with naive rivet if(rivet_FOUND) # this assumes "rivet" and "yodadiff" are found in PATH if(rivet_USE_HEPMC3) set(hepmc_file "tst.hepmc") else() set(hepmc_file "tst.hepmc2") endif() if(rivet_USE_HEPMC3 OR (rivet_VERSION VERSION_LESS 3)) set(histo_exclude "") else() # rivet 3 with HepMC 2 is inconsistent in order of weights # -> interface != direct call (by permutation) # REQUIRES Yoda 1.7.5 set(histo_exclude "-M\\\;\\\\d") endif() set(test_cmd_rivet "rivet\\\;-a\\\;MC_XS\\\;${hepmc_file}\\\;-o\\\;tst_direct.yoda\ \;yodadiff\\\;${histo_exclude}\\\;tst.yoda\\\;tst_direct.yoda") else() set(test_cmd_rivet "") endif() # Run dependent tests in one command to ensure correct execution order # Note: The commands are concatenated with "\;" to escape CMake lists. # Thus arguments have to be escaped twice "\\\;". # e.g. "cmd1\;cmd2\\\;arg1\\\;arg2" is executed like "cmd1 && cmd2 arg1 arg2" add_test( NAME main COMMAND ${CMAKE_COMMAND} -DCMDS=${test_cmd_main}\;${test_cmd_hepmc}\;${test_cmd_lhe}\;${test_cmd_rivet} -P ${PROJECT_SOURCE_DIR}/cmake/run_multiple_tests.cmake ) add_test( NAME main_example COMMAND $ ${PROJECT_SOURCE_DIR}/config.yml ${tst_dir}/h_3j.lhe.gz ) # check that Sherpas LHE input can be read add_executable(check_lhe_sherpa ${tst_dir}/check_lhe_sherpa.cc) target_link_libraries(check_lhe_sherpa HEJ hej_test) add_test( NAME sherpa_reader COMMAND check_lhe_sherpa ${tst_dir}/SherpaLHE.lhe 1.62624e+08 ) # check HDF5 reader & writer if(HighFive_FOUND) add_executable(test_hdf5 ${tst_dir}/test_hdf5.cc) target_link_libraries(test_hdf5 HEJ) add_test( NAME hdf5_read COMMAND test_hdf5 ${tst_dir}/Wm9-g4-repack.hdf5 ) add_executable(test_hdf5_write ${tst_dir}/test_hdf5_write.cc) target_link_libraries(test_hdf5_write HEJ hej_test) add_test( NAME hdf5_write COMMAND test_hdf5_write ${tst_dir}/Wm9-g4-repack.hdf5 output.hdf5 ) endif() # check rivet interface if(RIVET_FOUND) add_executable(check_rivet ${tst_dir}/check_rivet.cc) target_link_libraries(check_rivet HEJ rivet::rivet) add_test( NAME rivet COMMAND check_rivet ) endif() # test boson reconstruction add_executable(cmp_events ${tst_dir}/cmp_events.cc) target_link_libraries(cmp_events HEJ) add_test( NAME reconstruct_W COMMAND cmp_events ${tst_dir}/epnu2jLOFKL_unweight.lhe.tar.gz ${tst_dir}/epnu2jLOFKL_unweight_noW.lhe.gz ) # test resummed result add_executable(check_res ${tst_dir}/check_res.cc) target_link_libraries(check_res HEJ hej_test) if(TEST_ALL) # deactivate long tests by default add_test( NAME xs_2j COMMAND check_res ${tst_dir}/2j.lhe.gz 3.49391e+07 419684 ) add_test( NAME xs_3j COMMAND check_res ${tst_dir}/3j.lhe.gz 2.37902e+06 25746.6 ) add_test( NAME xs_3j_unof COMMAND check_res ${tst_dir}/3j.lhe.gz 133399 4688.83 unof ) add_test( NAME xs_3j_unob COMMAND check_res ${tst_dir}/3j.lhe.gz 105247 3449.45 unob ) add_test( NAME xs_3j_splitf COMMAND check_res ${tst_dir}/3j.lhe.gz 97659.9 2748.65 splitf ) add_test( NAME xs_3j_splitb COMMAND check_res ${tst_dir}/3j.lhe.gz 107150 2799.8 splitb ) add_test( NAME xs_4j COMMAND check_res ${tst_dir}/4j.lhe.gz 603713 72822.6 ) add_test( - NAME xs_4j_qqxmid - COMMAND check_res ${tst_dir}/4j.lhe.gz 21866.7 1716.96 qqxmid + NAME xs_4j_qqbar_mid + COMMAND check_res ${tst_dir}/4j.lhe.gz 21866.7 1716.96 qqbar_mid ) add_test( NAME xs_h_3j COMMAND check_res ${tst_dir}/h_3j.lhe.gz 0.821622 0.0220334 ) add_test( NAME xs_h_3j_unof COMMAND check_res ${tst_dir}/h_3j_uno.lhe.gz 0.0127362 0.000271555 unof ) add_test( NAME xs_h_3j_unob COMMAND check_res ${tst_dir}/h_3j_uno.lhe.gz 0.0130615 0.000224793 unob ) add_test( NAME xs_epnu_2j COMMAND check_res ${tst_dir}/epnu2jLOFKL_unweight.lhe.tar.gz 262.7 3 ) add_test( NAME xs_epnu_3j COMMAND check_res ${tst_dir}/MGepnu3j_unweighted.lhe.gz 38.9512 1 ) add_test( NAME xs_emnubar_3j COMMAND check_res ${tst_dir}/MGemnubar3j_unweighted.lhe.gz 24.1575 1 ) add_test( NAME xs_epnu_3j_unof COMMAND check_res ${tst_dir}/MGepnu3j_unweighted.lhe.gz 9.63702 0.128355 unof ) add_test( NAME xs_epnu_3j_unob COMMAND check_res ${tst_dir}/MGepnu3j_unweighted.lhe.gz 9.70119 0.108436 unob ) add_test( NAME xs_epnu_3j_splitf COMMAND check_res ${tst_dir}/MGepnu3j_unweighted.lhe.gz 2.91995 0.0463182 splitf ) add_test( NAME xs_epnu_3j_splitb COMMAND check_res ${tst_dir}/MGepnu3j_unweighted.lhe.gz 3.40708 0.0550975 splitb ) add_test( NAME xs_epnu_4j COMMAND check_res ${tst_dir}/MGepnu4j_unweighted.lhe.gz 10.2542 0.135106 ) add_test( NAME xs_emnubar_4j COMMAND check_res ${tst_dir}/MGemnubar4j_unweighted.lhe.gz 5.73282 0.0583738 ) add_test( - NAME xs_epnu_4j_qqxmid - COMMAND check_res ${tst_dir}/MGepnu4j_unweighted.lhe.gz 0.802603 0.0126908 qqxmid + NAME xs_epnu_4j_qqbar_mid + COMMAND check_res ${tst_dir}/MGepnu4j_unweighted.lhe.gz 0.802603 0.0126908 qqbar_mid ) endif() diff --git a/t/ME_data/config_Z_ME.yml b/t/ME_data/config_Z_ME.yml index 68919c6..a219afc 100644 --- a/t/ME_data/config_Z_ME.yml +++ b/t/ME_data/config_Z_ME.yml @@ -1,37 +1,37 @@ trials: 1 resummation jets: min pt: 30 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: reweight - extremal qqx: reweight - central qqx: reweight + extremal qqbar: reweight + central qqbar: reweight non-resummable: discard scales: H_T/2 log correction: false vev: 246.219650794137 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.1876 width: 2.495 random generator: name: mixmax seed: 1 diff --git a/t/ME_data/config_mt.yml b/t/ME_data/config_mt.yml index bf7ec12..ae9abd1 100644 --- a/t/ME_data/config_mt.yml +++ b/t/ME_data/config_mt.yml @@ -1,42 +1,42 @@ trials: 1 resummation jets: min pt: 30 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: reweight - extremal qqx: discard - central qqx: discard + extremal qqbar: discard + central qqbar: discard non-resummable: discard scales: 125 log correction: false random generator: name: mixmax seed: 1 vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 Higgs coupling: use impact factors: false mt: 174 include bottom: false diff --git a/t/ME_data/config_mtinf.yml b/t/ME_data/config_mtinf.yml index 633b85a..1fb2269 100644 --- a/t/ME_data/config_mtinf.yml +++ b/t/ME_data/config_mtinf.yml @@ -1,37 +1,37 @@ trials: 1 resummation jets: min pt: 30 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: reweight - extremal qqx: discard - central qqx: discard + extremal qqbar: discard + central qqbar: discard non-resummable: discard scales: 125 log correction: false vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 random generator: name: mixmax seed: 1 diff --git a/t/ME_data/config_mtmb.yml b/t/ME_data/config_mtmb.yml index 5a19a50..27a003c 100644 --- a/t/ME_data/config_mtmb.yml +++ b/t/ME_data/config_mtmb.yml @@ -1,43 +1,43 @@ trials: 1 resummation jets: min pt: 30 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: reweight - extremal qqx: discard - central qqx: discard + extremal qqbar: discard + central qqbar: discard non-resummable: discard scales: 125 log correction: false random generator: name: mixmax seed: 1 vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 Higgs coupling: use impact factors: false mt: 174 include bottom: true mb: 4.7 diff --git a/t/ME_data/config_pure.yml b/t/ME_data/config_pure.yml index 92fe72b..327f679 100644 --- a/t/ME_data/config_pure.yml +++ b/t/ME_data/config_pure.yml @@ -1,37 +1,37 @@ trials: 1 resummation jets: min pt: 30 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: reweight - extremal qqx: reweight - central qqx: discard + extremal qqbar: reweight + central qqbar: discard non-resummable: discard scales: 125 log correction: false vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.419 width: 2.0476 Z: mass: 91.187 width: 2.495 random generator: name: mixmax seed: 1 diff --git a/t/ME_data/config_w_ME.yml b/t/ME_data/config_w_ME.yml index 425d5cb..fcf064d 100644 --- a/t/ME_data/config_w_ME.yml +++ b/t/ME_data/config_w_ME.yml @@ -1,37 +1,37 @@ trials: 1 resummation jets: min pt: 30 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: reweight - extremal qqx: reweight - central qqx: reweight + extremal qqbar: reweight + central qqbar: reweight non-resummable: discard scales: 125 log correction: false vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.419 width: 2.0476 Z: mass: 91.187 width: 2.495 random generator: name: mixmax seed: 1 diff --git a/t/analysis_config.yml b/t/analysis_config.yml index 99f1fa4..ac8e282 100644 --- a/t/analysis_config.yml +++ b/t/analysis_config.yml @@ -1,41 +1,41 @@ trials: 10 resummation jets: min pt: 35 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: discard - extremal qqx: reweight - central qqx: discard + extremal qqbar: reweight + central qqbar: discard non-resummable: keep log correction: false scales: 125 random generator: name: mixmax seed: 11 vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 max events: 100 analyses: diff --git a/t/analysis_config_single.yml.in b/t/analysis_config_single.yml.in index 3ac7d26..0c34b58 100644 --- a/t/analysis_config_single.yml.in +++ b/t/analysis_config_single.yml.in @@ -1,43 +1,43 @@ trials: 10 resummation jets: min pt: 35 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: discard - extremal qqx: reweight - central qqx: discard + extremal qqbar: reweight + central qqbar: discard non-resummable: keep log correction: false scales: 125 random generator: name: mixmax seed: 11 vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 analysis: plugin: @ANALYSIS_PATH@/lib@ANALYSIS_LIB@.so @ANALYSIS_PARAMETERS@ max events: 100 diff --git a/t/check_res.cc b/t/check_res.cc index 33b0103..aee30d1 100644 --- a/t/check_res.cc +++ b/t/check_res.cc @@ -1,164 +1,164 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "hej_test.hh" #include #include #include #include #include #include #include #include #include "HEJ/Config.hh" #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/EventReweighter.hh" #include "HEJ/EWConstants.hh" #include "HEJ/Fraction.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Parameters.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/stream.hh" #include "fastjet/JetDefinition.hh" #include "LHEF/LHEF.h" namespace HEJ { struct RNG; } namespace { const fastjet::JetDefinition JET_DEF{fastjet::kt_algorithm, 0.4}; const fastjet::JetDefinition BORN_JET_DEF{JET_DEF}; constexpr double BORN_JETPTMIN = 30; constexpr double JETPTMIN = 35; constexpr bool LOG_CORR = false; constexpr std::size_t NUM_TRIES = 100; constexpr HEJ::ParticleProperties WPROP{80.385, 2.085}; constexpr HEJ::ParticleProperties ZPROP{91.187, 2.495}; constexpr HEJ::ParticleProperties HPROP{125, 0.004165}; constexpr double VEV = 246.2196508; using EventTreatment = HEJ::EventTreatment; using namespace HEJ::event_type; HEJ::EventTreatMap TREAT{ {no_2_jets, EventTreatment::discard}, {bad_final_state, EventTreatment::discard}, {non_resummable, EventTreatment::discard}, {unof, EventTreatment::discard}, {unob, EventTreatment::discard}, - {qqxexb, EventTreatment::discard}, - {qqxexf, EventTreatment::discard}, - {qqxmid, EventTreatment::discard}, + {qqbar_exb, EventTreatment::discard}, + {qqbar_exf, EventTreatment::discard}, + {qqbar_mid, EventTreatment::discard}, {FKL, EventTreatment::reweight} }; bool correct_colour(HEJ::Event const & ev){ if(!HEJ::event_type::is_resummable(ev.type())) return true; return ev.is_leading_colour(); } } // namespace int main(int argn, char** argv) { if(argn == 5 && std::string(argv[4]) == "unof"){ --argn; TREAT[unof] = EventTreatment::reweight; TREAT[unob] = EventTreatment::discard; TREAT[FKL] = EventTreatment::discard; } if(argn == 5 && std::string(argv[4]) == "unob"){ --argn; TREAT[unof] = EventTreatment::discard; TREAT[unob] = EventTreatment::reweight; TREAT[FKL] = EventTreatment::discard; } else if(argn == 5 && std::string(argv[4]) == "splitf"){ --argn; - TREAT[qqxexb] = EventTreatment::discard; - TREAT[qqxexf] = EventTreatment::reweight; + TREAT[qqbar_exb] = EventTreatment::discard; + TREAT[qqbar_exf] = EventTreatment::reweight; TREAT[FKL] = EventTreatment::discard; } else if(argn == 5 && std::string(argv[4]) == "splitb"){ --argn; - TREAT[qqxexb] = EventTreatment::reweight; - TREAT[qqxexf] = EventTreatment::discard; + TREAT[qqbar_exb] = EventTreatment::reweight; + TREAT[qqbar_exf] = EventTreatment::discard; TREAT[FKL] = EventTreatment::discard; } - else if(argn == 5 && std::string(argv[4]) == "qqxmid"){ + else if(argn == 5 && std::string(argv[4]) == "qqbar_mid"){ --argn; - TREAT[qqxmid] = EventTreatment::reweight; + TREAT[qqbar_mid] = EventTreatment::reweight; TREAT[FKL] = EventTreatment::discard; } if(argn != 4){ std::cerr << "Usage: check_res eventfile xsection tolerance [uno]"; return EXIT_FAILURE; } const double xsec_ref = std::stod(argv[2]); const double tolerance = std::stod(argv[3]); HEJ::istream in{argv[1]}; LHEF::Reader reader{in}; HEJ::PhaseSpacePointConfig psp_conf; psp_conf.jet_param = HEJ::JetParameters{JET_DEF, JETPTMIN}; HEJ::MatrixElementConfig ME_conf; ME_conf.log_correction = LOG_CORR; ME_conf.Higgs_coupling = HEJ::HiggsCouplingSettings{}; ME_conf.ew_parameters.set_vevWZH(VEV, WPROP, ZPROP, HPROP); HEJ::EventReweighterConfig conf; conf.psp_config = std::move(psp_conf); conf.ME_config = std::move(ME_conf); conf.treat = TREAT; reader.readEvent(); const bool has_Higgs = std::find( begin(reader.hepeup.IDUP), end(reader.hepeup.IDUP), 25 ) != end(reader.hepeup.IDUP); const double mu = has_Higgs?125.:91.188; HEJ::ScaleGenerator scale_gen{ {{std::to_string(mu), HEJ::FixedScale{mu}}}, {}, 1. }; std::shared_ptr ran{std::make_shared()}; HEJ::EventReweighter hej{reader.heprup, std::move(scale_gen), conf, ran}; HEJ::CrossSectionAccumulator xs; do{ auto ev_data = HEJ::Event::EventData{reader.hepeup}; shuffle_particles(ev_data); ev_data.reconstruct_intermediate(); HEJ::Event ev{ ev_data.cluster( BORN_JET_DEF, BORN_JETPTMIN ) }; auto resummed_events = hej.reweight(ev, NUM_TRIES); for(auto const & res_ev: resummed_events) { ASSERT(correct_colour(res_ev)); ASSERT(std::isfinite(res_ev.central().weight)); // we fill the xs uncorrelated since we only want to test the uncertainty // of the resummation xs.fill(res_ev); } } while(reader.readEvent()); const double xsec = xs.total().value; const double xsec_err = std::sqrt(xs.total().error); const double significance = std::abs(xsec - xsec_ref) / std::sqrt( xsec_err*xsec_err + tolerance*tolerance ); std::cout << xsec_ref << " +/- " << tolerance << " ~ " << xsec << " +- " << xsec_err << " => " << significance << " sigma\n"; if(significance > 3.){ std::cerr << "Cross section is off by over 3 sigma!\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } diff --git a/t/jet_config.yml b/t/jet_config.yml index 8cf1aee..d2920d2 100644 --- a/t/jet_config.yml +++ b/t/jet_config.yml @@ -1,49 +1,49 @@ trials: 10 resummation jets: min pt: 35 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: keep - extremal qqx: discard - central qqx: keep + extremal qqbar: discard + central qqbar: keep non-resummable: keep log correction: false scales: [125, 91.188] scale factors: [0.5, 1, 2] max scale ratio: 2.0001 random generator: name: mixmax seed: 1024 vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 max events: 500 unweight: type: partial trials: 100 max deviation: -1 event output: - tst.lhe diff --git a/t/jet_config_with_import.yml.in b/t/jet_config_with_import.yml.in index 490d373..f0cf167 100644 --- a/t/jet_config_with_import.yml.in +++ b/t/jet_config_with_import.yml.in @@ -1,42 +1,42 @@ trials: 10 resummation jets: min pt: 35 algorithm: antikt R: 0.4 fixed order jets: min pt: 30 event treatment: FKL: reweight unordered: discard - extremal qqx: discard - central qqx: discard + extremal qqbar: discard + central qqbar: discard non-resummable: discard log correction: false scales: @SCALE_NAME@ event output: - tst.lhe random generator: name: ranlux64 vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 import scales: @SCALE_PATH@/lib@SCALE_LIB@.so: @SCALE_NAME@ diff --git a/t/test_classify.cc b/t/test_classify.cc index 14aad7b..4194267 100644 --- a/t/test_classify.cc +++ b/t/test_classify.cc @@ -1,525 +1,525 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "hej_test.hh" #include #include #include #include #include #include #include "fastjet/JetDefinition.hh" #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/exceptions.hh" #include "HEJ/PDG_codes.hh" namespace { const fastjet::JetDefinition JET_DEF{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double MIN_JET_PT{30.}; const std::vector ALL_QUARKS{"-4","-1","1","2","3","4"}; const std::vector ALL_PARTONS{"g","-2","-1","1","2","3","4"}; const std::vector ALL_BOSONS{"h", "Wp", "Wm", "Z_photon_mix"}; const std::vector ALL_G_Z{"photon", "Z"}; const std::vector ALL_W{"W+", "W-"}; const std::size_t MAX_MULTI = 6; std::mt19937_64 RAN{0}; bool couple_quark(std::string const & boson, std::string & quark){ if(std::abs(HEJ::to_ParticleID(boson)) == HEJ::ParticleID::Wp){ auto qflav{ HEJ::to_ParticleID(quark) }; if(!HEJ::is_anyquark(qflav)) return false; const int W_charge = HEJ::to_ParticleID(boson)>0?1:-1; if(W_charge*qflav < 0 && !(std::abs(qflav)%2)) return false; // not anti-down if(W_charge*qflav > 0 && (std::abs(qflav)%2)) return false; // not up quark=std::to_string(qflav-W_charge); } if(HEJ::to_ParticleID(boson) == HEJ::ParticleID::Z_photon_mix){ auto qflav{ HEJ::to_ParticleID(quark) }; if(!HEJ::is_anyquark(qflav)) return false; } return true; } bool match_expectation( HEJ::event_type::EventType expected, std::array const & in, std::vector const & out, int const overwrite_boson = 0 ){ HEJ::Event ev{ parse_configuration( in,out,overwrite_boson ).cluster(JET_DEF, MIN_JET_PT)}; if(ev.type() != expected){ std::cerr << "Expected type " << name(expected) << " but found " << name(ev.type()) << "\n" << ev; auto jet_idx{ ev.particle_jet_indices() }; std::cout << "Particle Jet indices: "; for(int const i: jet_idx) std::cout << i << " "; std::cout << std::endl; return false; } return true; } //! test FKL configurations //! if implemented==false : check processes that are not in HEJ yet bool check_fkl( bool const implemented=true ){ using namespace HEJ; auto const type{ implemented?event_type::FKL:event_type::non_resummable }; std::vector bosons; if(implemented) bosons = ALL_BOSONS; else { bosons = ALL_G_Z; } for(std::string const & first: ALL_PARTONS) // all quark flavours for(std::string const & last: ALL_PARTONS){ for(std::size_t njet=2; njet<=MAX_MULTI; ++njet){ // all multiplicities if(njet==5) continue; std::array base_in{first,last}; std::vector base_out(njet, "g"); base_out.front() = first; base_out.back() = last; if(implemented && !match_expectation(type, base_in, base_out)) return false; for(auto const & boson: bosons) // any boson for(std::size_t pos=0; pos<=njet; ++pos){ // at any position auto const & in{base_in}; auto out{base_out}; // change quark flavours for W const bool couple_idx = std::uniform_int_distribution{0,1}(RAN) != 0; if(!couple_quark(boson, couple_idx?out.back():out.front())) continue; out.insert(out.begin()+pos, boson); if(!match_expectation(type, in, out)) return false; } } } return true; } //! test unordered configurations //! if implemented==false : check processes that are not in HEJ yet bool check_uno( bool const implemented=true ){ using namespace HEJ; auto const b{ implemented?event_type::unob:event_type::non_resummable }; auto const f{ implemented?event_type::unof:event_type::non_resummable }; std::vector bosons; if(implemented) { bosons = ALL_BOSONS; } else { bosons = ALL_G_Z; } for(std::string const & uno: ALL_QUARKS) // all quark flavours for(std::string const & fkl: ALL_PARTONS){ for(std::size_t njet=3; njet<=MAX_MULTI; ++njet){ // all multiplicities >2 if(njet==5) continue; for(std::size_t i=0; i<2; ++i){ // forward & backwards std::array base_in; std::vector base_out(njet, "g"); const std::size_t uno_pos = i?1:(njet-2); const std::size_t fkl_pos = i?(njet-1):0; base_in[i?0:1] = uno; base_in[i?1:0] = fkl; base_out[uno_pos] = uno; base_out[fkl_pos] = fkl; auto expectation{ i?b:f }; if( implemented && !match_expectation(expectation, base_in, base_out) ) return false; for(auto const & boson: bosons){ // any boson // at any position (higgs only inside FKL chain) std::size_t start = 0; std::size_t end = njet; if(to_ParticleID(boson) == pid::higgs){ start = i?(uno_pos+1):fkl_pos; end = i?(fkl_pos+1):uno_pos; } for(std::size_t pos=start; pos<=end; ++pos){ auto const & in{base_in}; auto out{base_out}; // change quark flavours for W const bool couple_idx = std::uniform_int_distribution{0,1}(RAN) != 0; if(!couple_quark(boson, couple_idx?out[fkl_pos]:out[uno_pos])) continue; out.insert(out.begin()+pos, boson); if(!match_expectation(expectation, in, out)) return false; } } } } } return true; } - //! test extremal qqx configurations + //! test extremal qqbar configurations //! if implemented==false : check processes that are not in HEJ yet - bool check_extremal_qqx( bool const implemented=true ){ + bool check_extremal_qqbar( bool const implemented=true ){ using namespace HEJ; - auto const b{ implemented?event_type::qqxexb:event_type::non_resummable }; - auto const f{ implemented?event_type::qqxexf:event_type::non_resummable }; + auto const b{ implemented?event_type::qqbar_exb:event_type::non_resummable }; + auto const f{ implemented?event_type::qqbar_exf:event_type::non_resummable }; std::vector bosons; if(implemented) bosons = ALL_W; else { bosons = ALL_G_Z; bosons.emplace_back("h"); bosons.emplace_back("Z_photon_mix"); } - for(std::string const & qqx: ALL_QUARKS) // all quark flavours + for(std::string const & qqbar: ALL_QUARKS) // all quark flavours for(std::string const & fkl: ALL_PARTONS){ - std::string const qqx2{ std::to_string(HEJ::to_ParticleID(qqx)*-1) }; + std::string const qqbar2{ std::to_string(HEJ::to_ParticleID(qqbar)*-1) }; for(std::size_t njet=3; njet<=MAX_MULTI; ++njet){ // all multiplicities >2 if(njet==5) continue; for(std::size_t i=0; i<2; ++i){ // forward & backwards std::array base_in; std::vector base_out(njet, "g"); - const std::size_t qqx_pos = i?0:(njet-2); + const std::size_t qqbar_pos = i?0:(njet-2); const std::size_t fkl_pos = i?(njet-1):0; base_in[i?0:1] = "g"; base_in[i?1:0] = fkl; base_out[fkl_pos] = fkl; - base_out[qqx_pos] = qqx; - base_out[qqx_pos+1] = qqx2; + base_out[qqbar_pos] = qqbar; + base_out[qqbar_pos+1] = qqbar2; auto expectation{ i?b:f }; if( implemented && !match_expectation(expectation, base_in, base_out) ) return false; for(auto const & boson: bosons){ // all bosons // at any position (higgs only inside FKL chain) std::size_t start = 0; std::size_t end = njet; if(to_ParticleID(boson) == pid::higgs){ - start = i?(qqx_pos+2):fkl_pos; - end = i?(fkl_pos+1):qqx_pos; + start = i?(qqbar_pos+2):fkl_pos; + end = i?(fkl_pos+1):qqbar_pos; } for(std::size_t pos=start; pos<=end; ++pos){ auto const & in{base_in}; auto out{base_out}; // change quark flavours for W const bool couple_idx = std::uniform_int_distribution{0,1}(RAN) != 0; if(couple_idx || !couple_quark(boson, out[fkl_pos]) ){ - // (randomly) try couple to FKL, else fall-back to qqx - if(!couple_quark(boson, out[qqx_pos])) - couple_quark(boson, out[qqx_pos+1]); + // (randomly) try couple to FKL, else fall-back to qqbar + if(!couple_quark(boson, out[qqbar_pos])) + couple_quark(boson, out[qqbar_pos+1]); } out.insert(out.begin()+pos, boson); if(!match_expectation(expectation, in, out)) return false; } } } } // test allowed jet configurations if( implemented){ - if( !( match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -3) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -4) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -5) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -5) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -6) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -7) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -7) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -8) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -8) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -9) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -10) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -11) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -11) - && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqx,qqx2}, -12) - && match_expectation(b,{"g",fkl},{qqx,qqx2,"g","g","g","g",fkl}, -12) + if( !( match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -3) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -4) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -5) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -5) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -6) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -7) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -7) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -8) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -8) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -9) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -10) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -11) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -11) + && match_expectation(f,{fkl,"g"},{fkl,"g","g","g","g",qqbar,qqbar2}, -12) + && match_expectation(b,{"g",fkl},{qqbar,qqbar2,"g","g","g","g",fkl}, -12) )) return false; if (fkl == "2") { - if( !( match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqx,qqx2}, -3) - && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -4) - && match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqx,qqx2}, -5) - && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -5) - && match_expectation(f,{"2","g"},{"1","g","Wp","g","g",qqx,qqx2}, -6) - && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -7) - && match_expectation(b,{"g","2"},{qqx,qqx2,"g","g","g","Wp","1"}, -7) - && match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqx,qqx2}, -8) - && match_expectation(b,{"g","2"},{qqx,qqx2,"Wp","g","g","g","1"}, -8) - && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -9) - && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -10) - && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -11) - && match_expectation(b,{"g","2"},{qqx,qqx2,"g","g","g","Wp","1"}, -11) - && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqx,qqx2}, -12) - && match_expectation(b,{"g","2"},{qqx,qqx2,"g","Wp","g","g","1"}, -12) + if( !( match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqbar,qqbar2}, -3) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"g","Wp","g","g","1"}, -4) + && match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqbar,qqbar2}, -5) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"g","Wp","g","g","1"}, -5) + && match_expectation(f,{"2","g"},{"1","g","Wp","g","g",qqbar,qqbar2}, -6) + && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqbar,qqbar2}, -7) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"g","g","g","Wp","1"}, -7) + && match_expectation(f,{"2","g"},{"1","Wp","g","g","g",qqbar,qqbar2}, -8) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"Wp","g","g","g","1"}, -8) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"g","Wp","g","g","1"}, -9) + && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqbar,qqbar2}, -10) + && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqbar,qqbar2}, -11) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"g","g","g","Wp","1"}, -11) + && match_expectation(f,{"2","g"},{"1","g","g","g","Wp",qqbar,qqbar2}, -12) + && match_expectation(b,{"g","2"},{qqbar,qqbar2,"g","Wp","g","g","1"}, -12) )) return false; } } } return true; } - //! test central qqx configurations + //! test central qqbar configurations //! if implemented==false : check processes that are not in HEJ yet - bool check_central_qqx(bool const implemented=true){ + bool check_central_qqbar(bool const implemented=true){ using namespace HEJ; - auto const t{ implemented?event_type::qqxmid:event_type::non_resummable }; + auto const t{ implemented?event_type::qqbar_mid:event_type::non_resummable }; std::vector bosons; if(implemented) bosons = ALL_W; else { bosons = ALL_G_Z; bosons.emplace_back("h"); bosons.emplace_back("Z_photon_mix"); } - for(std::string const & qqx: ALL_QUARKS) // all quark flavours + for(std::string const & qqbar: ALL_QUARKS) // all quark flavours for(std::string const & fkl1: ALL_PARTONS) for(std::string const & fkl2: ALL_PARTONS){ - std::string const qqx2{ std::to_string(HEJ::to_ParticleID(qqx)*-1) }; + std::string const qqbar2{ std::to_string(HEJ::to_ParticleID(qqbar)*-1) }; for(std::size_t njet=4; njet<=MAX_MULTI; ++njet){ // all multiplicities >3 if(njet==5) continue; - for(std::size_t qqx_pos=1; qqx_pos base_in; std::vector base_out(njet, "g"); base_in[0] = fkl1; base_in[1] = fkl2; base_out.front() = fkl1; base_out.back() = fkl2; - base_out[qqx_pos] = qqx; - base_out[qqx_pos+1] = qqx2; + base_out[qqbar_pos] = qqbar; + base_out[qqbar_pos+1] = qqbar2; if( implemented && !match_expectation(t, base_in, base_out) ) return false; for(auto const & boson: bosons) // any boson for(std::size_t pos=0; pos<=njet; ++pos){ // at any position if( to_ParticleID(boson) == pid::higgs - && (pos==qqx_pos || pos==qqx_pos+1) ) + && (pos==qqbar_pos || pos==qqbar_pos+1) ) continue; auto const & in{base_in}; auto out{base_out}; // change quark flavours for W const int couple_idx{ std::uniform_int_distribution{0,2}(RAN) }; - // (randomly) try couple to FKL, else fall-back to qqx + // (randomly) try couple to FKL, else fall-back to qqbar if( couple_idx == 0 && couple_quark(boson, out.front()) ){} else if( couple_idx == 1 && couple_quark(boson, out.back()) ){} else { - if(!couple_quark(boson, out[qqx_pos])) - couple_quark(boson, out[qqx_pos+1]); + if(!couple_quark(boson, out[qqbar_pos])) + couple_quark(boson, out[qqbar_pos+1]); } out.insert(out.begin()+pos, boson); if(!match_expectation(t, in, out)) return false; } } } } return true; } // this checks a (non excessive) list of non-resummable states bool check_non_resummable(){ auto type{ HEJ::event_type::non_resummable}; return // 2j - crossing lines match_expectation(type, {"g","2"}, {"2","g"}) && match_expectation(type, {"-1","g"}, {"g","-1"}) && match_expectation(type, {"1","-1"}, {"-1","1"}) && match_expectation(type, {"g","2"}, {"2","g","h"}) && match_expectation(type, {"1","2"}, {"2","h","1"}) && match_expectation(type, {"1","-1"}, {"h","-1","1"}) && match_expectation(type, {"g","2"}, {"Wp","1","g"}) && match_expectation(type, {"1","-1"}, {"-2","Wp","1"}) && match_expectation(type, {"4","g"}, {"g","3","Wp"}) && match_expectation(type, {"1","-2"}, {"-1","Wm","1"}) && match_expectation(type, {"g","3"}, {"4","g","Wm"}) && match_expectation(type, {"1","3"}, {"Wm","4","1"}) && match_expectation(type, {"g","2"}, {"Z_photon_mix","2","g"}) && match_expectation(type, {"1","-1"}, {"-1","Z_photon_mix","1"}) && match_expectation(type, {"4","g"}, {"g","4","Z_photon_mix"}) - // 2j - qqx + // 2j - qqbar && match_expectation(type, {"g","g"}, {"1","-1"}) && match_expectation(type, {"g","g"}, {"-2","2","h"}) && match_expectation(type, {"g","g"}, {"-4","Wp","3"}) && match_expectation(type, {"g","g"}, {"Wm","-1","2"}) && match_expectation(type, {"g","g"}, {"-3","Z_photon_mix","3"}) // 3j - crossing lines && match_expectation(type, {"g","4"}, {"4","g","g"}) && match_expectation(type, {"-1","g"}, {"g","g","-1"}) && match_expectation(type, {"1","3"}, {"3","g","1"}) && match_expectation(type, {"-2","2"}, {"2","g","-2","h"}) && match_expectation(type, {"-3","g"}, {"g","g","Wp","-4"}) && match_expectation(type, {"1","-2"}, {"Wm","-1","g","1"}) && match_expectation(type, {"-1","g"}, {"1","-1","-1"}) && match_expectation(type, {"1","-4"}, {"Z_photon_mix","-4","g","1"}) // higgs inside uno && match_expectation(type, {"-1","g"}, {"g","h","-1","g"}) && match_expectation(type, {"-1","1"}, {"g","h","-1","1"}) && match_expectation(type, {"g","2"}, {"g","2","h","g"}) && match_expectation(type, {"-1","1"}, {"-1","1","h","g"}) // higgs outside uno && match_expectation(type, {"-1","g"}, {"h","g","-1","g"}) && match_expectation(type, {"-1","1"}, {"-1","1","g","h"}) - // higgs inside qqx + // higgs inside qqbar && match_expectation(type, {"g","g"}, {"-1","h","1","g","g"}) && match_expectation(type, {"g","g"}, {"g","-1","h","1","g"}) && match_expectation(type, {"g","g"}, {"g","g","2","h","-2"}) - // higgs outside qqx + // higgs outside qqbar && match_expectation(type, {"g","g"}, {"h","-1","1","g","g"}) && match_expectation(type, {"g","g"}, {"g","g","2","-2","h"}) // 4j - two uno && match_expectation(type, {"-2","2"}, {"g","-2","2","g"}) && match_expectation(type, {"1","3"}, {"g","1","h","3","g"}) && match_expectation(type, {"1","2"}, {"g","1","3","Wp","g"}) && match_expectation(type, {"1","-2"}, {"g","Wm","1","-1","g"}) && match_expectation(type, {"3","2"}, {"g","3","Z_photon_mix","2","g"}) // 4j - two gluon outside && match_expectation(type, {"g","4"}, {"g","4","g","g"}) && match_expectation(type, {"1","3"}, {"1","3","h","g","g"}) && match_expectation(type, {"1","2"}, {"1","3","g","Wp","g"}) && match_expectation(type, {"1","-2"}, {"1","Wm","-1","g","g"}) && match_expectation(type, {"-1","g"}, {"g","g","-1","g"}) && match_expectation(type, {"1","3"}, {"g","g","1","3","h"}) && match_expectation(type, {"1","2"}, {"g","g","1","Wp","3"}) && match_expectation(type, {"1","-2"}, {"Wm","g","g","1","-1"}) && match_expectation(type, {"-1","2"}, {"g","g","-1","Z_photon_mix","2"}) // 4j - ggx+uno && match_expectation(type, {"g","4"}, {"1","-1","4","g"}) && match_expectation(type, {"2","g"}, {"g","2","-3","3"}) && match_expectation(type, {"g","4"}, {"1","-1","h","4","g"}) && match_expectation(type, {"2","g"}, {"g","2","-3","3","h"}) && match_expectation(type, {"g","4"}, {"Wp","1","-1","3","g"}) && match_expectation(type, {"2","g"}, {"g","2","-4","Wp","3"}) && match_expectation(type, {"g","4"}, {"2","Wm","-1","4","g"}) && match_expectation(type, {"2","g"}, {"g","2","Wp","-3","4"}) && match_expectation(type, {"-4","g"}, {"g","-4","-3","3","Z_photon_mix"}) // 3j - crossing+uno && match_expectation(type, {"1","4"}, {"g","4","1"}) && match_expectation(type, {"1","4"}, {"4","1","g"}) && match_expectation(type, {"1","4"}, {"g","h","4","1"}) && match_expectation(type, {"-1","-3"},{"Wm","g","-4","-1"}) && match_expectation(type, {"1","4"}, {"3","1","Wp","g"}) && match_expectation(type, {"1","4"}, {"3","1","g","h"}) && match_expectation(type, {"2","3"}, {"3","2","Z_photon_mix","g"}) - // 3j - crossing+qqx + // 3j - crossing+qqbar && match_expectation(type, {"1","g"}, {"-1","1","g","1"}) && match_expectation(type, {"1","g"}, {"-1","1","1","g"}) && match_expectation(type, {"g","1"}, {"1","g","1","-1"}) && match_expectation(type, {"g","1"}, {"g","1","1","-1"}) && match_expectation(type, {"1","g"}, {"2","-2","g","1"}) && match_expectation(type, {"1","g"}, {"2","-2","1","g"}) && match_expectation(type, {"g","1"}, {"1","g","-2","2"}) && match_expectation(type, {"g","1"}, {"g","1","-2","2"}) && match_expectation(type, {"1","g"}, {"-1","1","h","g","1"}) && match_expectation(type, {"1","g"}, {"-1","h","1","1","g"}) && match_expectation(type, {"g","1"}, {"1","g","1","h","-1"}) && match_expectation(type, {"g","1"}, {"h","g","1","1","-1"}) && match_expectation(type, {"1","g"}, {"2","-2","1","g","h"}) && match_expectation(type, {"g","1"}, {"g","h","1","-2","2"}) && match_expectation(type, {"1","g"}, {"Wp","3","-4","g","1"}) && match_expectation(type, {"3","g"}, {"-2","Wm","1","3","g"}) && match_expectation(type, {"g","1"}, {"1","g","Wm","-3","4"}) && match_expectation(type, {"g","-3"}, {"g","-3","-1","Wp","2"}) && match_expectation(type, {"g","2"}, {"2","g","Z_photon_mix","4","-4"}) - // 4j- gluon in qqx + // 4j- gluon in qqbar && match_expectation(type, {"g","1"}, {"1","g","-1","1"}) && match_expectation(type, {"1","g"}, {"1","-1","g","1"}) && match_expectation(type, {"g","1"}, {"1","g","Wm","-2","1"}) && match_expectation(type, {"2","g"}, {"2","-2","g","Wp","1"}) && match_expectation(type, {"g","g"}, {"Wp","3","g","-4","g"}) && match_expectation(type, {"1","g"}, {"1","h","-1","g","1"}) && match_expectation(type, {"3","g"}, {"3","1","g","Z_photon_mix","-1"}) - // 6j - two qqx + // 6j - two qqbar && match_expectation(type, {"g","g"}, {"1","-1","g","g","1","-1"}) && match_expectation(type, {"g","g"}, {"1","-1","g","1","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","-1","g","1","-1"}) && match_expectation(type, {"g","g"}, {"g","1","-1","1","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","1","-1","-1","g"}) && match_expectation(type, {"g","g"}, {"h","1","-1","g","g","1","-1"}) && match_expectation(type, {"g","g"}, {"1","Wp","-2","g","1","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","Wp","-1","g","1","-2"}) && match_expectation(type, {"g","g"}, {"g","1","-1","Wm","2","-1","g"}) && match_expectation(type, {"g","g"}, {"g","1","2","-1","Wm","-1","g"}) && match_expectation(type, {"g","g"}, {"2","-2","g","-1","1","Z_photon_mix","g"}) // random stuff (can be non-physical) - && match_expectation(type, {"g","g"}, {"1","-2","2","-1"}) // != 2 qqx - && match_expectation(type, {"g","g"}, {"1","-2","2","g"}) // could be qqx + && match_expectation(type, {"g","g"}, {"1","-2","2","-1"}) // != 2 qqbar + && match_expectation(type, {"g","g"}, {"1","-2","2","g"}) // could be qqbar && match_expectation(type, {"e+","e-"},{"1","-1"}) // bad initial state && match_expectation(type, {"1","e-"}, {"g","1","Wm"}) // bad initial state && match_expectation(type, {"h","g"}, {"g","g"}) // bad initial state - && match_expectation(type, {"-1","g"}, {"-1","1","1"}) // bad qqx - && match_expectation(type, {"-1","g"}, {"1","1","-1"}) // crossing in bad qqx - && match_expectation(type, {"-1","g"}, {"-2","1","1","Wp"}) // bad qqx - && match_expectation(type, {"1","2"}, {"1","-1","g","g","g","2"}) // bad qqx - && match_expectation(type, {"1","2"}, {"1","-1","-2","g","g","2"}) // gluon in bad qqx - && match_expectation(type, {"g","g"}, {"-1","2","g","g"}) // wrong back qqx - && match_expectation(type, {"g","g"}, {"g","g","2","1"}) // wrong forward qqx - && match_expectation(type, {"g","g"}, {"g","-2","1","g"}) // wrong central qqx + && match_expectation(type, {"-1","g"}, {"-1","1","1"}) // bad qqbar + && match_expectation(type, {"-1","g"}, {"1","1","-1"}) // crossing in bad qqbar + && match_expectation(type, {"-1","g"}, {"-2","1","1","Wp"}) // bad qqbar + && match_expectation(type, {"1","2"}, {"1","-1","g","g","g","2"}) // bad qqbar + && match_expectation(type, {"1","2"}, {"1","-1","-2","g","g","2"}) // gluon in bad qqbar + && match_expectation(type, {"g","g"}, {"-1","2","g","g"}) // wrong back qqbar + && match_expectation(type, {"g","g"}, {"g","g","2","1"}) // wrong forward qqbar + && match_expectation(type, {"g","g"}, {"g","-2","1","g"}) // wrong central qqbar && match_expectation(type, {"1","g"}, {"1","-2","g","g","Wp"}) // extra quark && match_expectation(type, {"g","1"}, {"g","g","-2","1","Wp"}) // extra quark && match_expectation(type, {"g","1"}, {"g","g","Wp","-2","1"}) // extra quark && match_expectation(type, {"g","1"}, {"g","-2","1","g","Wp"}) // extra quark && match_expectation(type, {"g","g"}, {"g","g","g","-2","1","-1","Wp"}) // extra quark && match_expectation(type, {"1","g"}, {"g","Wp","1","-2","g"}) // extra quark && match_expectation(type, {"g","g"}, {"1","-1","-2","g","g","g","Wp"}) // extra quark ; } // Two boson states, that are currently not implemented bool check_bad_FS(){ auto type{ HEJ::event_type::bad_final_state}; return match_expectation(type, {"g","g"}, {"g","h","h","g"}) && match_expectation(type, {"g","g"}, {"h","g","h","g"}) && match_expectation(type, {"g","-1"}, {"g","h","Wp","-2"}) && match_expectation(type, {"-3","-1"},{"-4","g","Wp","Wp","-2"}) && match_expectation(type, {"-4","-1"},{"-3","Wp","g","Wm","-2"}) && match_expectation(type, {"-4","-1"},{"g","-3","Wp","Wm","-2"}) && match_expectation(type, {"-4","-1"},{"-4","g","11","-11","-2"}) && match_expectation(type, {"-4","-1"},{"-4","g","-13","g","-2"}) && match_expectation(type, {"3","-2"}, {"Wp","3","Wm","g","g","g","-2"}, -13) && match_expectation(type, {"1","2"},{"1","g","Z_photon_mix","Z_photon_mix","2"}) && match_expectation(type, {"-4","g"},{"-4","Z_photon_mix","g","Z_photon_mix","g"}) && match_expectation(type, {"g","-2"}, {"g","Z_photon_mix","Wm","-1"}) && match_expectation(type, {"3","1"},{"3","g","h","Z_photon_mix","1"}) ; } // not 2 jets bool check_not_2_jets(){ auto type{ HEJ::event_type::no_2_jets}; return match_expectation(type, {"g","g"}, {}) && match_expectation(type, {"1","-1"}, {}) && match_expectation(type, {"g","-1"}, {"-1"}) && match_expectation(type, {"g","g"}, {"g"}) ; } // not implemented processes bool check_not_implemented(){ return check_fkl(false) && check_uno(false) - && check_extremal_qqx(false) - && check_central_qqx(false); + && check_extremal_qqbar(false) + && check_central_qqbar(false); } } // namespace int main() { // tests for "no false negatives" // i.e. all HEJ-configurations get classified correctly if(!check_fkl()) return EXIT_FAILURE; if(!check_uno()) return EXIT_FAILURE; - if(!check_extremal_qqx()) return EXIT_FAILURE; - if(!check_central_qqx()) return EXIT_FAILURE; + if(!check_extremal_qqbar()) return EXIT_FAILURE; + if(!check_central_qqbar()) return EXIT_FAILURE; // test for "no false positive" // i.e. non-resummable gives non-resummable if(!check_non_resummable()) return EXIT_FAILURE; if(!check_bad_FS()) return EXIT_FAILURE; if(!check_not_2_jets()) return EXIT_FAILURE; if(!check_not_implemented()) return EXIT_FAILURE; return EXIT_SUCCESS; } diff --git a/t/test_colours.cc b/t/test_colours.cc index 5af4bf4..7947eed 100644 --- a/t/test_colours.cc +++ b/t/test_colours.cc @@ -1,362 +1,362 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "hej_test.hh" #include #include #include #include #include "HEJ/Constants.hh" #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/exceptions.hh" #include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/RNG.hh" #include "fastjet/JetDefinition.hh" namespace { /// biased RNG to connect always to colour class dum_rnd: public HEJ::DefaultRNG { public: dum_rnd() = default; double flat() override { return 0.; } }; HEJ::Event::EventData decay_boson( HEJ::Event::EventData ev ){ for( std::size_t i=0; ifirst; int const anti_colour = part.colour->second; if(part.type == HEJ::ParticleID::gluon) return colour != anti_colour && colour >= HEJ::COLOUR_OFFSET && anti_colour >= HEJ::COLOUR_OFFSET; if(HEJ::is_quark(part)) return anti_colour == 0 && colour >= HEJ::COLOUR_OFFSET; return colour == 0 && anti_colour >= HEJ::COLOUR_OFFSET; } bool correct_colour(HEJ::Event const & ev){ if(!ev.is_leading_colour()) return false; // some of these additional checks are also in ev.is_leading_colour() for(auto const & part: ev.incoming()){ if(!correct_colour(part)) return false; } for(auto const & part: ev.outgoing()){ if(!correct_colour(part)) return false; } return true; } bool match_expected( HEJ::Event const & ev, std::vector const & expected ){ ASSERT(ev.outgoing().size()+2==expected.size()); for(std::size_t i=0; i const & expected_colours ){ unc_ev = decay_boson(std::move(unc_ev)); shuffle_particles(unc_ev); // make sure incoming order doesn't matter HEJ::Event ev{unc_ev.cluster( fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30.) }; ASSERT(HEJ::event_type::is_resummable(ev.type())); dum_rnd rng; ASSERT(!ev.is_leading_colour()); ASSERT(ev.generate_colours(rng)); if(!correct_colour(ev)){ std::cerr << "Found illegal colours for event\n"; dump_event(ev); throw std::invalid_argument("Illegal colour set"); } if(!match_expected(ev, expected_colours)){ std::cerr << "Colours didn't match expectation. Found\n"; dump_event(ev); std::cerr << "but expected\n"; for(auto const & col: expected_colours){ std::cerr << "colour={" << col.first << ", " << col.second << "}\n"; } throw std::logic_error("Colours did not match expectation"); } } HEJ::Event::EventData reset_colour( HEJ::Event::EventData ev, std::vector const & goal ){ for(std::size_t i=0; i<2; ++i){ ev.incoming[i].colour = goal[i]; } for(std::size_t i=0; i{}; else ev.outgoing[i].colour = col_goal; } return ev; } } // namespace int main() { HEJ::Event::EventData ev; std::vector expected_colours(7); /// pure gluon (they all have a mass of 4 GeV to allow decays) ev.incoming[0] = { HEJ::ParticleID::gluon, { 0, 0, -205, 205}, {}}; ev.incoming[1] = { HEJ::ParticleID::gluon, { 0, 0, 279, 279}, {}}; ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-15, -82, -82, 117}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::gluon, { 68, 93, 20, 117}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::higgs, {-30, -65, 22, 75}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-12, 92, 76, 120}, {}}); ev.outgoing.push_back({ HEJ::ParticleID::gluon, {-11, -38, 38, 55}, {}}); expected_colours[0] = {502, 501}; expected_colours[1] = {509, 502}; expected_colours[2] = {503, 501}; expected_colours[3] = {505, 503}; expected_colours[4] = { 0, 0}; expected_colours[5] = {507, 505}; expected_colours[6] = {509, 507}; // default colours is always forbidden! // default: swap last two (anti-)colour -> crossing ev=reset_colour(ev, expected_colours); std::swap(ev.outgoing[4].colour, ev.outgoing[3].colour); check_event(ev, expected_colours); - /// last g to Qx (=> gQx -> g ... Qx) + /// last g to Qbar (=> gQbar -> g ... Qbar) ev.incoming[1].type = HEJ::ParticleID::d_bar; ev.outgoing[4].type = HEJ::ParticleID::d_bar; // => only end changes expected_colours[1].first = 0; expected_colours[6].first = 0; // default: swap last two anti-colours -> last gluon colour singlet ev=reset_colour(ev, expected_colours); std::swap(ev.outgoing[4].colour->second, ev.outgoing[3].colour->second); check_event(ev, expected_colours); { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; - /// uno forward (=> gQx -> g ... Qx g) + /// uno forward (=> gQbar -> g ... Qbar g) std::swap(new_ev.outgoing[3].type, new_ev.outgoing[4].type); // => uno quarks eats colour and gluon connects to anti-colour new_expected[5] = {0, expected_colours[3].first}; new_expected[6] = {expected_colours[0].first, expected_colours[0].first+2}; new_expected[1].second += 2; // one more anti-colour in line // default: swap last two anti-colours -> crossing new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.outgoing[4].colour->second, new_ev.outgoing[3].colour->second); check_event(new_ev, new_expected); } - /// swap Qx <-> Q (=> gQ -> g ... Q) + /// swap Qbar <-> Q (=> gQ -> g ... Q) ev.incoming[1].type = HEJ::ParticleID::d; ev.outgoing[4].type = HEJ::ParticleID::d; // => swap: colour<->anti && initial<->final std::swap(expected_colours[1], expected_colours[6]); std::swap(expected_colours[1].first, expected_colours[1].second); std::swap(expected_colours[6].first, expected_colours[6].second); // default: swap incoming <-> outgoing ev=reset_colour(ev, expected_colours); std::swap(ev.incoming[0].colour, ev.outgoing[0].colour); check_event(ev, expected_colours); - /// first g to qx (=> qxQ -> qx ... Q) + /// first g to qbar (=> qbarQ -> qbar ... Q) ev.incoming[0].type = HEJ::ParticleID::u_bar; ev.outgoing[0].type = HEJ::ParticleID::u_bar; expected_colours[0] = { 0, 501}; // => shift anti-colour index one up expected_colours[1].first -= 2; expected_colours[5] = expected_colours[3]; expected_colours[3] = expected_colours[2]; expected_colours[2] = { 0, 502}; - // default: closed qx->qx g + // default: closed qbar->qbar g ev=reset_colour(ev, expected_colours); ev.outgoing[1].colour->first = ev.outgoing[0].colour->second; ev.outgoing[1].colour->second = ev.incoming[0].colour->second; ev.outgoing[4].colour->first = ev.outgoing[3].colour->second; check_event(ev, expected_colours); { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; - /// uno backward (=> qxQ -> g qx ... Q) + /// uno backward (=> qbarQ -> g qbar ... Q) std::swap(new_ev.outgoing[0].type, new_ev.outgoing[1].type); // => uno gluon connects to quark colour new_expected[3] = expected_colours[2]; new_expected[2] = {expected_colours[0].second+2, expected_colours[0].second}; // default: Colourful Higgs new_ev=reset_colour(new_ev, new_expected); new_ev.outgoing[2].colour = std::make_pair(1,1); check_event(new_ev, new_expected); - /// swap qx <-> q (=> qQ -> g q ... Q) + /// swap qbar <-> q (=> qQ -> g q ... Q) new_ev.incoming[0].type = HEJ::ParticleID::u; new_ev.outgoing[1].type = HEJ::ParticleID::u; // => swap: colour<->anti && inital<->final std::swap(new_expected[0], new_expected[3]); std::swap(new_expected[0].first, new_expected[0].second); std::swap(new_expected[3].first, new_expected[3].second); // => & connect first gluon with remaining anti-colour new_expected[2] = {new_expected[0].first, new_expected[0].first+2}; // shift colour line one down new_expected[1].first-=2; new_expected[5].first-=2; new_expected[5].second-=2; // shift anti-colour line one up new_expected[6].first+=2; // default: swap 2 quarks -> disconnected new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.outgoing[1].colour, new_ev.outgoing[4].colour); check_event(new_ev, new_expected); } { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; - /// uno forward (=> qxQ -> qx ... Q g) + /// uno forward (=> qbarQ -> qbar ... Q g) std::swap(new_ev.outgoing[3].type, new_ev.outgoing[4].type); // => uno gluon connects to remaining colour new_expected[5] = expected_colours[6]; new_expected[6] = {expected_colours[3].first+2, expected_colours[3].first}; // default: no colour on last gluon new_ev=reset_colour(new_ev, new_expected); new_ev.incoming[1].colour->first = new_ev.outgoing[4].colour->second; new_ev.outgoing[4].colour = {}; check_event(new_ev, new_expected); } { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; - /// qqx backward (=> gQ -> qx q ... Q) with Wp + /// qqbar backward (=> gQ -> qbar q ... Q) with Wp // => swap: incoming q <-> outgoing gluon std::swap(new_ev.incoming[0].type, new_ev.outgoing[1].type); new_ev.outgoing[1].type=static_cast( -(new_ev.outgoing[1].type+1) ); new_ev.outgoing[2].type = HEJ::ParticleID::Wp; // incoming q -> outgoing q (colour<->anti) std::swap(new_expected[0], new_expected[3]); std::swap(new_expected[3].first, new_expected[3].second); new_expected[3].first+=2; new_expected[0].first-=1; // skip one index // couple first in to first out new_expected[2].second=new_expected[0].second; - // default: swap qqx <-> first g + // default: swap qqbar <-> first g new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.outgoing[0].colour->second, new_ev.outgoing[3].colour->second); std::swap(new_ev.outgoing[1].colour->first, new_ev.outgoing[3].colour->first); check_event(new_ev, new_expected); } { // don't overwrite auto new_expected = expected_colours; auto new_ev = ev; - /// qqx forward (=> qx g -> qx ... Qx Q) with Wp + /// qqbar forward (=> qbar g -> qbar ... Qbar Q) with Wp // => swap: incoming Q <-> outgoing gluon std::swap(new_ev.incoming[1].type, new_ev.outgoing[3].type); new_ev.outgoing[3].type=static_cast( -(new_ev.outgoing[3].type+1)); new_ev.outgoing[2].type = HEJ::ParticleID::Wp; // incoming q -> outgoing q (colour<->anti) std::swap(new_expected[1], new_expected[5]); std::swap(new_expected[5].first, new_expected[5].second); new_expected[5].second-=2; new_expected[1].second-=1; // skip one index // couple last in to last out new_expected[6].first=new_expected[1].first; // default: uncoloured quark new_ev=reset_colour(new_ev, new_expected); new_ev.outgoing[0].colour = {}; check_event(new_ev, new_expected); - // move Higgs to position 1 (=> qx g -> qx h g Qx Q) + // move Higgs to position 1 (=> qbar g -> qbar h g Qbar Q) std::swap(new_ev.outgoing[1].type, new_ev.outgoing[2].type); std::swap(new_expected[3], new_expected[4]); // trivial - // default: incoming qx wrong colour + // default: incoming qbar wrong colour new_ev=reset_colour(new_ev, new_expected); new_ev.incoming[0].colour->first = 1; check_event(new_ev, new_expected); - // central qqx (=> qx g -> qx h Q Qx g) + // central qqbar (=> qbar g -> qbar h Q Qbar g) // => swap: Q <-> g std::swap(new_ev.outgoing[2].type, new_ev.outgoing[4].type); std::swap(new_expected[4], new_expected[6]); - // gluon was connected on left side, i.e. doesn't matter for QQx - // => couple Q to out qx + // gluon was connected on left side, i.e. doesn't matter for QQbar + // => couple Q to out qbar new_expected[4].first = new_expected[2].second; - // Qx next in line + // Qbar next in line new_expected[5].second = new_expected[4].first+2; // incoming g shifted by one position in line new_expected[1].first-=2; new_expected[1].second+=2; // default: wrong colour in last incoming new_ev=reset_colour(new_ev, new_expected); std::swap(new_ev.incoming[1].colour->first, new_ev.incoming[1].colour->second); check_event(new_ev, new_expected); } return EXIT_SUCCESS; } diff --git a/t/test_colours2.cc b/t/test_colours2.cc index 676de86..caeab8c 100644 --- a/t/test_colours2.cc +++ b/t/test_colours2.cc @@ -1,352 +1,352 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include "hej_test.hh" #include #include #include #include #include #include #include #include "HEJ/Constants.hh" #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Particle.hh" #include "fastjet/JetDefinition.hh" namespace { const fastjet::JetDefinition JET_DEF{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double MIN_JET_PT{30.}; HEJ::Particle & get_particle( HEJ::Event::EventData & event, char const name ){ if(name == 'a') return event.incoming[0]; if(name == 'b') return event.incoming[1]; std::size_t idx = name-'0'; ASSERT(idx incoming; std::vector outgoing; std::string colour; HEJ::Event to_event(){ using namespace HEJ; auto event = parse_configuration(incoming, outgoing); if(colour.empty()) // no colour -> only parse return event.cluster(JET_DEF,MIN_JET_PT); ASSERT(colour.front()=='a' && colour.back()=='a'); // only closed loops event.sort(); // fill colours with dummy value for(auto & p: event.incoming) p.colour = Colour{-1, -1}; for(auto & p: event.outgoing) p.colour = Colour{-1, -1}; int current_colour = COLOUR_OFFSET; int backup_colour = current_colour; Particle* last_part = &event.incoming.front(); // loop connections for(auto const & entry: colour){ if(entry == '_'){ // '_' -> skip connection backup_colour = current_colour; current_colour = 0; continue; } auto & part = get_particle(event, entry); part.colour->first = last_part->colour->second = current_colour; current_colour = ++backup_colour; last_part = ∂ } for(auto & in: event.incoming) std::swap(in.colour->first, in.colour->second); // reset untouched colours for(auto & out: event.outgoing) if(out.colour->first == -1 && out.colour->second ==-1) out.colour = {}; shuffle_particles(event); return event.cluster(JET_DEF,MIN_JET_PT); } }; const colour_flow FKL_GGG{{"g","g"},{"g","g","g"},{}}; const colour_flow FKL_QGQ{{"1","2"},{"1","g","2"},{}}; - const colour_flow FKL_QXGQX{{"-1","-2"},{"-1","g","-2"},{}}; + const colour_flow FKL_QBARGQBAR{{"-1","-2"},{"-1","g","-2"},{}}; const colour_flow FKL_QHGQ{{"1","2"},{"1","h","g","2"},{}}; const colour_flow UNO_GQGQ{{"1","2"},{"g","1","g","2"},{}}; const colour_flow UNO_QGQG{{"1","2"},{"1","g","2","g"},{}}; const colour_flow UNO_WGQQ{{"2","2"},{"W+","g","1","2"},{}}; const colour_flow UNO_GWQQ{{"2","2"},{"g","W+","1","2"},{}}; const colour_flow UNO_GQWQ{{"2","2"},{"g","1","W+","2"},{}}; const colour_flow UNO_QWQG{{"2","2"},{"1","W+","2","g"},{}}; const colour_flow UNO_QQWG{{"2","2"},{"1","2","W+","g"},{}}; const colour_flow UNO_QQGW{{"2","2"},{"1","2","g","W+"},{}}; - const colour_flow QQX_QXQGQ{{"g","2"},{"-1","1","g","2"},{}}; - const colour_flow QQX_QGQQX{{"1","g"},{"1","g","2","-2"},{}}; - const colour_flow QQX_QGQXQ{{"1","g"},{"1","g","-2","2"},{}}; - const colour_flow QQX_WQXQQ{{"g","2"},{"W+","-2","1","2"},{}}; - const colour_flow QQX_QXWQQ{{"g","2"},{"-2","W+","1","2"},{}}; - const colour_flow QQX_QXQWQ{{"g","2"},{"-2","1","W+","2"},{}}; - const colour_flow QQX_QWQQX{{"2","g"},{"1","W+","2","-2"},{}}; - const colour_flow QQX_QQWQX{{"2","g"},{"1","2","W+","-2"},{}}; - const colour_flow QQX_QQQXW{{"2","g"},{"1","2","-2","W+"},{}}; + const colour_flow QQBAR_QBARQGQ{{"g","2"},{"-1","1","g","2"},{}}; + const colour_flow QQBAR_QGQQBAR{{"1","g"},{"1","g","2","-2"},{}}; + const colour_flow QQBAR_QGQBARQ{{"1","g"},{"1","g","-2","2"},{}}; + const colour_flow QQBAR_WQBARQQ{{"g","2"},{"W+","-2","1","2"},{}}; + const colour_flow QQBAR_QBARWQQ{{"g","2"},{"-2","W+","1","2"},{}}; + const colour_flow QQBAR_QBARQWQ{{"g","2"},{"-2","1","W+","2"},{}}; + const colour_flow QQBAR_QWQQBAR{{"2","g"},{"1","W+","2","-2"},{}}; + const colour_flow QQBAR_QQWQBAR{{"2","g"},{"1","2","W+","-2"},{}}; + const colour_flow QQBAR_QQQBARW{{"2","g"},{"1","2","-2","W+"},{}}; - const colour_flow QQX_GQQXQ{{"g","2"},{"g","3","-3","2"},{}}; - const colour_flow QQX_QQXQG{{"1","g"},{"1","-3","3","g"},{}}; - const colour_flow QQX_QQXQQX{{"1","-1"},{"1","-1","1","-1"},{}}; - const colour_flow QQX_QWQXQQX{{"2","-1"},{"1","W+","-1","1","-1"},{}}; - const colour_flow QQX_QQXWQQX{{"2","-1"},{"1","-1","W+","1","-1"},{}}; - const colour_flow QQX_QQXQWQX{{"2","-1"},{"1","-1","1","W+","-1"},{}}; + const colour_flow QQBAR_GQQBARQ{{"g","2"},{"g","3","-3","2"},{}}; + const colour_flow QQBAR_QQBARQG{{"1","g"},{"1","-3","3","g"},{}}; + const colour_flow QQBAR_QQBARQQBAR{{"1","-1"},{"1","-1","1","-1"},{}}; + const colour_flow QQBAR_QWQBARQQBAR{{"2","-1"},{"1","W+","-1","1","-1"},{}}; + const colour_flow QQBAR_QQBARWQQBAR{{"2","-1"},{"1","-1","W+","1","-1"},{}}; + const colour_flow QQBAR_QQBARQWQBAR{{"2","-1"},{"1","-1","1","W+","-1"},{}}; HEJ::Mixmax RNG; void verify_colour(colour_flow configuration, std::string line, bool const expectation = true ){ configuration.colour = std::move(line); auto const event = configuration.to_event(); if(event.is_leading_colour() != expectation){ std::cerr << "Expected "<< (expectation?"":"non-") <<"leading colour\n" << event << "\nwith connection: " << configuration.colour << "\n"; throw std::logic_error("Colour verification failed"); } } int find_colour(HEJ::Event const & evt, int const colour){ if(evt.incoming()[0].colour->second==colour) return -1; if(evt.incoming()[1].colour->second==colour) return -2; for(std::size_t i=0;ifirst == colour) return i; } return -3; } int find_anticolour(HEJ::Event const & evt, int const anticolour){ if(evt.incoming()[0].colour->first==anticolour) return -1; if(evt.incoming()[1].colour->first==anticolour) return -2; for(std::size_t i=0;isecond == anticolour) return i; } return -3; } bool matching_colours(HEJ::Event const & evt1, HEJ::Event const & evt2){ ASSERT(evt1.outgoing().size()==evt2.outgoing().size()); for(std::size_t i=0; i<2;++i){ HEJ::optional col1 = evt1.incoming()[i].colour; HEJ::optional col2 = evt2.incoming()[i].colour; ASSERT(col1); ASSERT(col2); int idx1 = find_colour(evt1, col1->first); int idx2 = find_colour(evt2, col2->first); if(idx1 == -3 || idx2 == -3) return false; if(idx1!=idx2){ return false; } idx1 = find_anticolour(evt1, col1->second); idx2 = find_anticolour(evt2, col2->second); if(idx1 == -3 || idx2 == -3) return false; if(idx1!=idx2) return false; } for(std::size_t i=0; i col1 = evt1.outgoing()[i].colour; HEJ::optional col2 = evt2.outgoing()[i].colour; if(!col1){ if(!col2) continue; return false; } if(!col2) return false; int idx1 = find_colour(evt1, col1->second); int idx2 = find_colour(evt2, col2->second); if(idx1 == -3 || idx2 == -3) return false; if(idx1!=idx2) return false; idx1 = find_anticolour(evt1, col1->first); idx2 = find_anticolour(evt2, col2->first); if(idx1 == -3 || idx2 == -3) return false; if(idx1!=idx2) return false; } return true; } void all_colours_possible( colour_flow momenta, std::vector allowed ){ std::vector possible; for(auto const & line: allowed){ momenta.colour = line; possible.push_back(momenta.to_event()); if(!possible.back().is_leading_colour()){ std::cerr << "Expected leading colour\n" << possible.back() << "\nwith connection: " << momenta.colour << "\n"; throw std::logic_error("Colour verification failed"); } } momenta.colour = ""; ASSERT(possible.size()<16); // sooo magic std::bitset<16> missing = (1<<(possible.size()))-1; std::size_t max_tries = possible.size()*10; // brute force generation of specific colour flow while(missing != 0 && max_tries>0){ --max_tries; HEJ::Event test_evt = momenta.to_event(); test_evt.generate_colours(RNG); std::size_t i=0; for(; i #include #include #include #include #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "fastjet/JetDefinition.hh" namespace { const fastjet::JetDefinition JET_DEF{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double MIN_JET_PT{30.}; const double SOFT_PT_REGULATOR{0.4}; bool correct( bool const expected, std::array const & in, std::vector const & out, int const overwrite_boson ){ HEJ::Event ev{ parse_configuration( in,out,overwrite_boson ).cluster(JET_DEF, MIN_JET_PT)}; ASSERT(is_resummable(ev.type())); // only test jet configurations if(ev.valid_hej_state(SOFT_PT_REGULATOR) != expected){ std::cerr << "Expected " << (expected?"valid":"invalid") << " but found " << (ev.valid_hej_state(SOFT_PT_REGULATOR)?"valid":"invalid") << " jets (" << overwrite_boson << ")\n" << ev; auto jet_idx{ ev.particle_jet_indices() }; std::cout << "Particle Jet indices: "; for(int const i: jet_idx) std::cout << i << " "; std::cout << std::endl; return false; } return true; } // valid jet configurations bool valid_jets(){ // FKL // extremal parton inside jet for(int i=-3; i>-13;--i){ std::array base_in{"g","g"}; std::vector base_out(7, "g"); if(!correct(true, base_in, base_out, i)) return false; } // replace one of the extremal with a boson if( !( correct(true,{"g","d"},{"g","g","g","g","g","d","h"}, -13) && correct(true,{"d","d"},{"d","g","g","g","g","d","h"}, -13) && correct(true,{"2","2"},{"1","g","g","g","g","2","Wp"},-14) )) return false; // uno if( !( correct(true,{"g","3"},{"h","g","g","g","g","3","g"}, -1) && correct(true,{"3","2"},{"Wp","g","3","g","g","g","1"}, -1) && correct(true,{"1","2"},{"Wm","g","2","g","g","g","2"}, -1) && correct(true,{"1","3"},{"1","g","g","g","g","3","g"}, -3) && correct(true,{"-1","3"},{"-1","g","g","g","g","3","g"},-5) && correct(true,{"3","-2"},{"g","3","g","g","g","g","-2"},-5) && correct(true,{"-3","3"},{"g","-3","g","g","g","g","3"},-6) && correct(true,{"-4","3"},{"-4","g","g","g","g","3","g"},-7) && correct(true,{"3","-5"},{"g","3","g","g","g","g","-5"},-7) && correct(true,{"g","3"},{"g","g","g","g","g","3","g"}, -8) && correct(true,{"3","g"},{"g","3","g","g","g","g","g"}, -8) && correct(true,{"2","3"},{"g","1","g","Wp","g","g","3"}, -9) && correct(true,{"2","3"},{"1","g","g","g","3","Wp","g"}, -9) && correct(true,{"2","3"},{"1","g","Wp","g","g","3","g"}, -10) && correct(true,{"3","g"},{"g","3","g","g","g","g","g"}, -11) && correct(true,{"1","3"},{"1","g","g","g","g","3","g"}, -12) && correct(true,{"3","2"},{"g","3","g","g","g","g","2"}, -12) && correct(true,{"3","g"},{"g","3","g","g","g","g","h"}, -13) && correct(true,{"2","3"},{"1","g","g","g","3","g","Wp"}, -13) && correct(true,{"2","3"},{"g","1","g","g","g","3","Wp"}, -14) )) return false; - // extremal qqx + // extremal qqbar if( !( correct(true,{"2","g"},{"1","Wp","g","g","g","1","-1"}, -3) && correct(true,{"2","g"},{"1","Wp","g","g","g","1","-1"}, -5) && correct(true,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -5) && correct(true,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -7) && correct(true,{"g","2"},{"1","-1","g","g","g","Wp","1"}, -7) && correct(true,{"2","g"},{"1","Wp","g","g","g","1","-1"}, -8) && correct(true,{"g","2"},{"1","-1","Wp","g","g","g","1"}, -8) && correct(true,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -9) && correct(true,{"g","3"},{"-2","1","g","Wp","g","g","3"}, -9) && correct(true,{"2","g"},{"1","g","g","g","3","Wp","-3"}, -9) && correct(true,{"2","g"},{"1","g","Wp","g","g","-3","3"}, -10) && correct(true,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -12) && correct(true,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -12) && correct(true,{"2","g"},{"1","g","g","g","-3","3","Wp"}, -13) && correct(true,{"g","g"},{"-2","1","g","g","g","g","Wp"}, -14) // pure jets && correct(true,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -3) && correct(true,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -5) && correct(true,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -5) && correct(true,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -6) && correct(true,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -7) && correct(true,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -7) && correct(true,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -8) && correct(true,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -8) && correct(true,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -11) && correct(true,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -12) && correct(true,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -12) )) return false; - // central qqx + // central qqbar if( !( correct(true,{"1","g"},{"2","g","-2","Wm","2","g","g"}, -3) && correct(true,{"1","g"},{"2","g","g","-2","2","Wm","g"}, -4) && correct(true,{"1","g"},{"2","g","g","Wm","-2","2","g"}, -5) && correct(true,{"1","g"},{"2","g","g","Wm","-2","2","g"}, -8) && correct(true,{"1","g"},{"2","Wm","g","-2","2","g","g"}, -9) && correct(true,{"1","g"},{"2","Wm","-2","2","g","g","g"}, -9) && correct(true,{"1","g"},{"2","g","g","-2","2","Wm","g"}, -10) && correct(true,{"1","g"},{"2","g","Wm","2","-2","g","g"}, -11) && correct(true,{"1","g"},{"2","g","g","-2","2","g","Wm"}, -12) && correct(true,{"1","g"},{"2","2","-2","g","g","g","Wm"}, -13) && correct(true,{"1","g"},{"2","2","-2","g","g","g","Wm"}, -14) // pure jets && correct(true,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -4) && correct(true,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -10) )) return false; return true; } // invalid jet configurations bool invalid_jets(){ // processes failing jet cuts // FKL if( !( true && correct(false,{"1","g"}, {"g","1","g","g","g","g","g"}, -1) // FKL not in own jet && correct(false,{"1","1"}, {"1","g","g","g","g","g","1"}, -1) && correct(false,{"g","g"}, {"g","g","g","g","g","g","g"}, -2) && correct(false,{"1","-3"},{"1","g","g","g","g","-3","g"}, -1) && correct(false,{"2","g"}, {"g","2","g","g","g","g","g"}, -2) // FKL below pt && correct(false,{"1","1"}, {"1","g","g","g","g","g","1"}, -13) && correct(false,{"g","3"},{"h","g","g","g","g","3","g"}, -13) && correct(false,{"g","1"},{"Wm","g","g","g","g","2","g"}, -13) && correct(false,{"3","1"},{"Wm","g","3","g","g","g","2"}, -13) // uno in same jet as FKL && correct(false,{"-1","1"},{"-1","g","g","g","g","1","g"}, -9) && correct(false,{"3","3"}, {"g","3","g","g","g","g","3"}, -9) && correct(false,{"-1","1"},{"g","-1","g","g","g","g","1"}, -10) && correct(false,{"-1","1"},{"-1","g","g","g","g","1","g"}, -13) && correct(false,{"-1","1"},{"g","-1","g","g","g","g","1"}, -13) // FKL not in jet & uno other side && correct(false,{"2","3"},{"Wp","1","g","g","g","3","g"}, -15) )) return false; // uno if( !( true // uno backward not in jet && correct(false,{"1","2"}, {"g","1","g","g","g","g","2"}, -1) // uno forward not in jet && correct(false,{"3","3"}, {"3","g","g","g","g","3","g"}, -2) // uno backward in same jet && correct(false,{"1","g"}, {"g","1","g","g","g","g","g"}, -3) // uno forward in same jet && correct(false,{"3","2"}, {"3","g","g","g","g","2","g"}, -4) // uno backward below pt && correct(false,{"3","g"}, {"g","3","g","g","g","g","g"}, -4) // uno forward below pt && correct(false,{"4","3"}, {"4","g","g","g","g","3","g"}, -10) && correct(false,{"2","3"}, {"1","g","g","g","3","g","Wp"}, -14) // uno forward not in jet && correct(false,{"5","3"}, {"5","g","g","g","g","3","g"}, -11) )) return false; - // extremal qqx + // extremal qqbar if( !( true - // qqxf in same jet + // qqbarf in same jet && correct(false,{"g","g"}, {"Wm","g","g","g","g","-1","2"}, -4) - // qqxb below pt + // qqbarb below pt && correct(false,{"g","2"},{"1","-1","g","Wp","g","g","1"}, -4) - // central qqx not in jet + // central qqbar not in jet && correct(false,{"1","2"}, {"1","g","-2","2","Wp","g","1"}, -5) - // central qqx in same jet + // central qqbar in same jet && correct(false,{"1","2"}, {"1","-2","2","g","Wp","g","1"}, -6) - // central qqx in same jet + // central qqbar in same jet && correct(false,{"1","2"}, {"1","Wm","g","g","2","-1","2"}, -6) - // qqxf below pt + // qqbarf below pt && correct(false,{"2","g"},{"1","g","Wp","g","g","1","-1"}, -6) - // central qqx in same jet + // central qqbar in same jet && correct(false,{"1","2"}, {"1","-1","1","g","g","Wp","1"}, -7) - // central qqx in same jet + // central qqbar in same jet && correct(false,{"1","2"}, {"1","Wp","g","3","-3","g","1"}, -7) - // central qqx in same jet + // central qqbar in same jet && correct(false,{"g","3"}, {"g","Wp","-2","1","g","g","3"}, -8) - // central qqx in same jet + // central qqbar in same jet && correct(false,{"g","-2"},{"Wm","g","g","2","-1","g","-2"}, -8) - // qqxf below pt + // qqbarf below pt && correct(false,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -10) - // qqxf outside jet + // qqbarf outside jet && correct(false,{"2","g"},{"1","g","g","g","Wp","1","-1"}, -11) - // extremal qqx in same jet as FKL + // extremal qqbar in same jet as FKL && correct(false,{"-1","g"},{"-1","g","Wm","g","g","2","-1"},-9) && correct(false,{"g","1"}, {"2","-1","g","g","g","Wm","1"}, -10) - // extraml qqx below pt + // extraml qqbar below pt && correct(false,{"-1","g"},{"-1","Wm","g","g","g","-1","2"},-13) && correct(false,{"g","g"}, {"g","g","g","g","-1","Wm","2"}, -14) && correct(false,{"g","g"}, {"-1","2","g","g","g","g","Wm"}, -15) // pure jets && correct(false,{"2","g"}, {"2","g","g","g","g","2","-2"}, -1) && correct(false,{"g","2"}, {"2","-2","g","g","g","g","2"}, -1) && correct(false,{"g","-2"},{"4","-4","g","g","g","g","-2"}, -2) - // qqx backward not in jet + // qqbar backward not in jet && correct(false,{"g","g"}, {"g","g","g","g","g","-2","2"}, -2) - // qqx backward in same jet + // qqbar backward in same jet && correct(false,{"g","2"}, {"-4","4","g","g","g","g","2"}, -3) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -4) && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -4) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -6) && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -9) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -9) && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -10) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -10) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -11) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -13) && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -13) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -14) && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -14) && correct(false,{"-3","g"},{"-3","g","g","g","g","-3","3"}, -15) && correct(false,{"g","-3"},{"-3","3","g","g","g","g","-3"}, -15) )) return false; - // central qqx + // central qqbar if( !( true && correct(false,{"1","g"}, {"2","Wm","-2","2","g","g","g"}, -3) && correct(false,{"1","g"}, {"2","g","Wm","-2","2","g","g"}, -3) && correct(false,{"1","g"}, {"2","-2","2","g","Wm","g","g"}, -4) && correct(false,{"-1","g"},{"-1","g","g","Wp","1","-2","g"}, -4) && correct(false,{"-1","g"},{"-1","1","-2","g","g","Wp","g"}, -5) && correct(false,{"-1","g"},{"-1","g","1","-2","g","Wp","g"}, -5) && correct(false,{"-1","g"},{"-1","g","g","1","-2","Wp","g"}, -5) && correct(false,{"1","g"}, {"2","g","g","Wm","2","-2","g"}, -6) && correct(false,{"1","g"}, {"2","g","Wm","-2","2","g","g"}, -6) && correct(false,{"g","4"}, {"g","g","-4","4","g","Wp","3"}, -6) && correct(false,{"1","g"}, {"2","g","Wm","g","-2","2","g"}, -7) && correct(false,{"g","4"}, {"g","g","-4","4","g","Wp","3"}, -7) && correct(false,{"g","4"}, {"g","Wp","g","-4","4","g","3"}, -7) && correct(false,{"1","g"}, {"2","-2","2","Wm","g","g","g"}, -8) && correct(false,{"g","4"}, {"g","-4","4","g","Wp","g","3"}, -8) && correct(false,{"g","g"}, {"g","g","g","g","-2","1","Wp"}, -9) && correct(false,{"1","g"}, {"2","-2","2","g","Wm","g","g"}, -9) && correct(false,{"1","g"}, {"2","g","-2","2","g","Wm","g"}, -9) && correct(false,{"1","g"}, {"2","g","g","Wm","-2","2","g"}, -10) && correct(false,{"g","g"}, {"g","1","-2","Wp","g","g","g"}, -10) && correct(false,{"g","1"}, {"g","g","Wp","g","-2","1","1"}, -11) && correct(false,{"1","g"}, {"2","g","Wm","g","-2","2","g"}, -11) && correct(false,{"1","g"}, {"2","Wm","g","-2","2","g","g"}, -12) && correct(false,{"3","2"}, {"3","g","-1","2","Wm","g","2"}, -12) && correct(false,{"3","-2"},{"3","-1","2","g","g","Wm","-2"}, -13) && correct(false,{"3","-2"},{"3","-1","2","g","g","Wm","-2"}, -14) && correct(false,{"3","-2"},{"3","g","g","-1","2","Wm","-2"}, -14) && correct(false,{"1","g"}, {"Wm","2","2","-2","g","g","g"}, -15) && correct(false,{"3","-2"},{"3","-1","2","g","g","-2","Wm"}, -15) // pure jets && correct(false,{"-1","g"},{"-1","1","-1","g","g","g","g"}, -1) && correct(false,{"4","-3"},{"4","g","g","1","-1","g","-3"}, -2) && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -3) && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -3) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -3) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -4) && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -4) && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -5) && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -5) && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -5) && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -6) && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -7) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -7) && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -8) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -8) && correct(false,{"1","-1"},{"1","-1","1","g","g","g","-1"}, -9) && correct(false,{"1","-1"},{"1","g","-1","1","g","g","-1"}, -9) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -10) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -11) && correct(false,{"1","-1"},{"1","g","g","-1","1","g","-1"}, -12) && correct(false,{"1","-1"},{"1","g","g","g","-1","1","-1"}, -12) )) return false; return true; } } // namespace int main() { if(!valid_jets()) return EXIT_FAILURE; if(!invalid_jets()) return EXIT_FAILURE; return EXIT_SUCCESS; }