diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..0dc056c --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,38 @@ +Checks: ' + *, + -cert-dcl21-cpp, + -clang-analyzer-optin.cplusplus.VirtualCall, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-constant-array-index, + -fuchsia*, + -google-build-using-namespace, + -google-explicit-constructor, + -google-readability*, + -google-runtime-int, + -google-runtime-references, + -hicpp*, + -llvm-header-guard, + -modernize-use-trailing-return-type, + -readability-braces-around-statements, + -readability-uppercase-literal-suffix, + ' +CheckOptions: + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: '1' + - key: llvm-namespace-comment.ShortNamespaceLines + value: '10' + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: '0' + - key: readability-identifier-naming.GlobalConstantCase + value: UPPER_CASE + - key: readability-identifier-naming.GlobalVariableCase + value: UPPER_CASE + - key: readability-identifier-naming.PrivateMemberSuffix + value: '_' + - key: readability-implicit-bool-conversion.AllowIntegerConditions + value: '1' + - key: readability-magic-numbers.IgnoreAllFloatingPointValues + value: '1' diff --git a/AUTHORS b/AUTHORS index 27ce9b2..841162b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,17 +1,18 @@ ## Authors of the HEJ collaboration (sorted by first name) # Current contributors Andreas Maier +Bertrand Ducloué Helen Brooks James Black Jennifer Smillie Jeppe R. Andersen Marian Heil # Former contributors James Cockburn Tuomas Hapola Jack J. Medley diff --git a/CMakeLists.txt b/CMakeLists.txt index fa6bef6..b40d944 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,257 +1,274 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(CMAKE_LEGACY_CYGWIN_WIN32 0) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project("HEJ" VERSION 2.0.6 LANGUAGES C CXX) ## User settings include(CMakeDependentOption) option(EXCLUDE_QCDloop "Do not include QCDloop" FALSE) option(EXCLUDE_HighFive "Do not include HighFive" FALSE) option(EXCLUDE_HepMC "Do not include HepMC version 2" FALSE) option(EXCLUDE_HepMC3 "Do not include HepMC version 3" FALSE) option(EXCLUDE_ROOT "Do not include ROOT" TRUE) # Only skip rivet if we both HepMC 2 & 3 are excluded, since we don't know which # HepMC version rivet needs CMAKE_DEPENDENT_OPTION(EXCLUDE_rivet "Do not include rivet" FALSE "NOT EXCLUDE_HepMC OR NOT EXCLUDE_HepMC3" TRUE) option(BUILD_EXAMPLES "Build examples" FALSE) option(TEST_ALL "Run additional (longer) tests" FALSE) option(TEST_COVERAGE "Generate test coverage with \"gcovr\"" FALSE) +option(RUN_CLANG_TIDY "Run clang tidy" FALSE) # Set a default build type if none was specified set(default_build_type "RelWithDebInfo") if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${default_build_type}' as none was specified.") set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() ## Global flags for the compiler (all warnings) if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") elseif (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX /EHsc") endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 14) ## Create Version set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") # Get the latest abbreviated commit hash of the working branch execute_process( COMMAND git rev-parse HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE PROJECT_GIT_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE ) # Get the current working branch execute_process( COMMAND git rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE PROJECT_GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE ) ## target directories for install set(INSTALL_INCLUDE_DIR_BASE include) set(INSTALL_INCLUDE_DIR ${INSTALL_INCLUDE_DIR_BASE}/HEJ) set(INSTALL_BIN_DIR bin) set(INSTALL_LIB_DIR lib) set(INSTALL_CONFIG_DIR lib/cmake/HEJ) ## find dependencies -find_package(fastjet REQUIRED) find_package(CLHEP 2.3 REQUIRED) -find_package(LHAPDF REQUIRED) +find_package(fastjet REQUIRED) +find_package(LHAPDF 6 REQUIRED) ## Amend unintuitive behaviour of FindBoost.cmake ## Priority of BOOST_ROOT over BOOSTROOT matches FindBoost.cmake ## at least for cmake 3.12 if(DEFINED BOOST_ROOT) message("BOOST_ROOT set - only looking for Boost in ${BOOST_ROOT}") set(Boost_NO_BOOST_CMAKE ON) elseif(DEFINED BOOSTROOT) message("BOOSTROOT set - only looking for Boost in ${BOOSTROOT}") set(Boost_NO_BOOST_CMAKE ON) endif() find_package(Boost REQUIRED COMPONENTS iostreams) find_package(yaml-cpp) # requiring yaml does not work with fedora if(EXCLUDE_HepMC) message(STATUS "Skipping HepMC") else() find_package(HepMC 2 EXACT) endif() if(EXCLUDE_HepMC3) message(STATUS "Skipping HepMC3") else() ## version 3.1: Finding version not possible in 3.1.1, see HepMC3 git 29e7a6c3 find_package(HepMC3 QUIET) # suppress CMake warning if HepMC3 not available ## print standard message find_package_handle_standard_args( HepMC3 FOUND_VAR HepMC3_FOUND REQUIRED_VARS HEPMC3_INCLUDE_DIR HEPMC3_LIBRARIES # VERSION_VAR # HEPMC3_VERSION ) endif() if(EXCLUDE_rivet) message(STATUS "Skipping rivet") else() find_package(rivet) if(rivet_FOUND) if(rivet_USE_HEPMC3 AND NOT HepMC3_FOUND) message(FATAL_ERROR "Rivet was compiled with HepMC version 3, " "but HepMC3 was not found!") elseif(NOT rivet_USE_HEPMC3 AND NOT HepMC_FOUND) message(FATAL_ERROR "Rivet was compiled with HepMC version 2, " "but HepMC2 was not found!") endif() endif() endif() if(EXCLUDE_QCDloop) message(STATUS "Skipping QCDloop") else() find_package(QCDloop 2) endif() if(NOT EXCLUDE_ROOT) # Don't print "Skipping ROOT" for default option (EXCLUDE_ROOT=TRUE) find_package(ROOT 6.14) # root targets are broken before 6.14 find_package_handle_standard_args( ROOT FOUND_VAR ROOT_FOUND REQUIRED_VARS ROOT_INCLUDE_DIRS ROOT_LIBRARIES VERSION_VAR ROOT_VERSION ) endif() if(EXCLUDE_HighFive) message(STATUS "Skipping HighFive") else() find_package(HighFive QUIET) find_package_handle_standard_args( HighFive CONFIG_MODE ) endif() include(RepairTargets) # more reliable cmake targets ## Save dependencies for templates set(HEJ_BUILD_WITH_HepMC2 ${HepMC_FOUND}) set(HEJ_BUILD_WITH_HepMC3 ${HepMC3_FOUND}) set(HEJ_BUILD_WITH_RIVET ${rivet_FOUND}) if(rivet_FOUND AND rivet_VERSION VERSION_LESS 3) set(HEJ_USE_RIVET2 TRUE) endif() set(HEJ_BUILD_WITH_QCDLOOP ${QCDloop_FOUND}) set(HEJ_BUILD_WITH_HDF5 ${HighFive_FOUND}) ## Template files configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Templates/Version.hh.in include/HEJ/Version.hh @ONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Templates/ConfigFlags.hh.in include/HEJ/ConfigFlags.hh @ONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Templates/HEJ-config.cc.in src/bin/HEJ-config.cc @ONLY ) # Generate CMake config file include(CMakePackageConfigHelpers) configure_package_config_file( cmake/Templates/hej-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${INSTALL_CONFIG_DIR}/hej-config.cmake INSTALL_DESTINATION ${INSTALL_CONFIG_DIR} PATH_VARS INSTALL_INCLUDE_DIR_BASE INSTALL_LIB_DIR ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/${INSTALL_CONFIG_DIR}/hej-config-version.cmake COMPATIBILITY SameMajorVersion ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${INSTALL_CONFIG_DIR}/hej-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${INSTALL_CONFIG_DIR}/hej-config-version.cmake DESTINATION ${INSTALL_CONFIG_DIR}) ## shortcut for HEJ specific includes set(HEJ_INCLUDE_DIR $ $ $ ) if (TEST_COVERAGE AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) ) include(CodeCoverage) APPEND_COVERAGE_COMPILER_FLAGS() set(COVERAGE_GCOVR_EXCLUDES "${PROJECT_SOURCE_DIR}/include/LHEF/*" "${PROJECT_SOURCE_DIR}/include/cxxopts.hpp" "${PROJECT_SOURCE_DIR}/t/*" "${PROJECT_BINARY_DIR}/*") SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML( NAME ctest_coverage EXECUTABLE ctest ) endif() add_subdirectory(current_generator) ## create main HEJ library add_subdirectory(src) ## define executable add_executable(HEJ_main src/bin/HEJ.cc) set_target_properties(HEJ_main PROPERTIES OUTPUT_NAME "HEJ") ## link libraries target_link_libraries(HEJ_main HEJ) add_executable(HEJ-config src/bin/HEJ-config.cc) target_include_directories(HEJ-config PRIVATE ${HEJ_INCLUDE_DIR}) file(GLOB hej_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/HEJ/*.hh ${PROJECT_BINARY_DIR}/include/HEJ/*.hh ) file(GLOB hej_detail_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/HEJ/detail/*.hh) file(GLOB lhef_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/LHEF/*.h) install(FILES ${hej_headers} DESTINATION ${INSTALL_INCLUDE_DIR}) install(FILES ${hej_detail_headers} DESTINATION ${INSTALL_INCLUDE_DIR}/detail/) install(FILES ${lhef_headers} DESTINATION ${INSTALL_INCLUDE_DIR_BASE}/LHEF/) install(TARGETS HEJ_main HEJ-config DESTINATION ${INSTALL_BIN_DIR}) ## tests (prevent testing if current project is a subproject) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) ## advanced version of enable_testing() ## adds "BUILD_TESTING" option to deactivate testing include(CTest) if(BUILD_TESTING) set(BUILD_EXAMPLES TRUE) # examples needed for testing set(include_tests TRUE) endif() endif() ## build examples if(BUILD_EXAMPLES) message(STATUS "Adding examples") add_subdirectory(examples) endif() ## build tests if(include_tests) message(STATUS "Adding tests") add_subdirectory(t) endif() add_subdirectory(doc) + +## Clang-tidy +if(RUN_CLANG_TIDY) + find_program( + CLANG_TIDY_EXE + NAMES "clang-tidy" + DOC "Path to clang-tidy executable" ) + if(NOT CLANG_TIDY_EXE) + message(FATAL_ERROR "clang-tidy not found, but requested. Please deactivate RUN_CLANG_TIDY" ) + else() + set_target_properties( + HEJ + PROPERTIES CXX_CLANG_TIDY + "${CLANG_TIDY_EXE};-header-filter=${CMAKE_SOURCE_DIR}/include/HEJ;-fix;--fix-errors" ) + endif() +endif() diff --git a/Changes-API.md b/Changes-API.md index 6525817..316ad8b 100644 --- a/Changes-API.md +++ b/Changes-API.md @@ -1,107 +1,110 @@ # Changelog for HEJ API This log lists only changes on the HEJ API. These are primarily code changes relevant for calling HEJ as an API. This file should only be read as an addition to [`Changes.md`](Changes.md), where the main features are documented. ## Version 2.1 ### 2.1.0 #### Changes to Event class * Restructured `Event` class - `Event` can now only be build from a (new) `Event::EventData` class - Removed default constructor for `Event` - `Event::EventData` replaces the old `UnclusteredEvent` struct. - `UnclusteredEvent` is now deprecated, and will be removed in HEJ Version 2.3.0 - Removed `Event.unclustered()` function - Added new member function `Events.parameters()`, to directly access (underlying) `Parameters` - New member functions `begin_partons`, `end_partons` (`rbegin_partons`, `rend_partons`) with aliases `cbegin_partons`, `cend_partons` (`crbegin_partons`, `crend_partons`) for constant (reversed) iterators over outgoing partons. * New function `Event::EventData.reconstruct_intermediate()` to reconstruct bosons from decays, e.g. `positron + nu_e => Wp` * Added optional Colour charges to particles (`Particle.colour`) - Colour connection in the HEJ limit can be generated via `Event.generate_colours` (automatically done in the resummation) - Colour configuration of input events can be checked with `Event.is_leading_colour` * Added function `Event.valid_hej_state` to check if event _could have_ been produced by `HEJ` according to the `max ext soft pt fraction` cut on the jets #### New and updated functions * Renamed `EventType::nonHEJ` to `EventType::non_resummable` and `is_HEJ()` to `is_resummable()` such that run card is consistent with internal workings * Made `MatrixElement.tree_kin(...)` and `MatrixElement.tree_param(...)` public * New `EventReweighter` member function `treatment` to query the treatment with respect to resummation for the various event types. -* Added auxiliary functions to obtain a `std::string` from and - `EventDescription` (`to_string` for human readable, and `to_simple_string` - for easy parsable string) +* Added auxiliary functions to obtain a `std::string` from `EventDescription` + (`to_string` for human readable, and `to_simple_string` for easy parsable + string) * New `get_analyses` function to read in multiple `HEJ::Analysis` at once, similar to `get_analysis` * New `get_ew_parameters` function to extract electroweak parameters from YAML configuration. #### New classes * New class `Unweighter` to do unweighting * New class `CrossSectionAccumulator` to keep track of Cross Section of the different subprocess * New template struct `Parameters` similar to old `Weights` - `Weights` are now an alias for `Parameters`. Calling `Weights` did not change - `Weights.hh` was replaced by `Parameters.hh`. The old `Weights.hh` header will be removed in HEJ Version 2.2.0 * Function to multiplication and division of `EventParameters.weight` by double - This can be combined with `Parameters`, e.g. `Parameters*Weights`, see also `Events.parameters()` - Moved `EventParameters` to `Parameters.hh` header * new class `EWConstants` replaces previously hard coded `vev` - `EWConstants` have to be set in the general `Config` and the `MatrixElementConfig` #### Input/Output * New abstract `EventReader` class, as base for reading events from files - Moved LHE file reader to `HEJ::LesHouchesReader` +* New (optional) function `finish()` in abstract class `EventWriter`. `finish()` + is called _after_ all events are written. * Support reading (`HDF5Reader`) and writing (`HDF5Writer`) `hdf5` files * New `BufferedEventReader` class that allows to put events back into the reader. * New `SherpaLHEReader` to read Sherpa LHE files with correct weights * `get_analysis` now requires `YAML::Node` and `LHEF::HEPRUP` as arguments * Replaced `HepMCInterface` and `HepMCWriter` by `HepMCInterfaceX` and `HepMCWriterX` respectively, with `X` being the major version of HepMC (2 or 3) - Renamed `HepMCInterfaceX::init_kinematics` to `HepMCInterfaceX::init_event` and protected it, use `HepMCInterfaceX::operator()` instead - Removed redundant `HepMCInterfaceX::add_variation` function #### Linking * Export cmake target `HEJ::HEJ` to link directly against `libHEJ` * Preprocessor flags (`HEJ_BUILD_WITH_XYZ`) for enabled/disabled dependencies are now written to `ConfigFlags.hh` * Provide links to version specific object files, e.g. `libHEJ.so -> libHEJ.so.2.1 (soname) -> libHEJ.so.2.1.0` +* Removed `LHAPDF` from public interface #### Miscellaneous * Capitalisation of `Config.hh` now matches class `Config` (was `config.hh`) -* Replaced redundant member `EventReweighterConfig::jet_param` getter function - `EventReweighter.jet_param()` (`JetParameters` are already in +* Replaced redundant member `EventReweighterConfig::jet_param` with getter + function `EventReweighter.jet_param()` (`JetParameters` are already in `PhaseSpacePointConfig`) * Avoid storing reference to the Random Number Generator inside classes - Constructors of `EventReweighter` now expect `std::shared_ptr` (was reference) - Moved reference to `HEJ::RNG` from constructor of `JetSplitter` to `JetSplitter.split` ## Version 2.0 ### 2.0.4 * Fixed wrong path of `HEJ_INCLUDE_DIR` in `hej-config.cmake` ### 2.0.0 * First release diff --git a/Changes.md b/Changes.md index b417c6b..e6d2ade 100644 --- a/Changes.md +++ b/Changes.md @@ -1,129 +1,140 @@ # 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 -Implemented W boson with jets for complete set of first subleading processes -(unordered gluon, central and extremal quark-antiquark pair), see -[arXiv:TODO](https://arxiv.org/abs/TODO). Ported unordered gluon emissions for -pure jets from HEJ 1. This release include many changes to the code, which -affect users of HEJ as a library (see [`Changes-API.md`](Changes-API.md)). +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 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 extremal jets (`max ext soft pt fraction`). This supersedes `min extparton pt`, which is now deprecated and will be removed in future versions. #### 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 - FOG: 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 -* 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` * 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`. -* Use `git-lfs` for raw data in test (`make test` now requires `git-lfs`) * 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/include/Beam.hh b/FixedOrderGen/include/Beam.hh index 73f7380..4054083 100644 --- a/FixedOrderGen/include/Beam.hh +++ b/FixedOrderGen/include/Beam.hh @@ -1,19 +1,19 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include "HEJ/PDG_codes.hh" -namespace HEJFOG{ +namespace HEJFOG { struct Beam{ double energy; std::array particles{{ HEJ::pid::proton, HEJ::pid::proton }}; }; } diff --git a/FixedOrderGen/include/Decay.hh b/FixedOrderGen/include/Decay.hh index f6d3ec8..2c92262 100644 --- a/FixedOrderGen/include/Decay.hh +++ b/FixedOrderGen/include/Decay.hh @@ -1,27 +1,27 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/PDG_codes.hh" -namespace HEJFOG{ +namespace HEJFOG { struct Decay{ std::vector products; double branching_ratio; }; #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 ParticlesDecayMap = std::unordered_map, std::hash>; #else using ParticlesDecayMap = std::unordered_map>; #endif } diff --git a/FixedOrderGen/include/EventGenerator.hh b/FixedOrderGen/include/EventGenerator.hh index 4ac94b5..680deec 100644 --- a/FixedOrderGen/include/EventGenerator.hh +++ b/FixedOrderGen/include/EventGenerator.hh @@ -1,67 +1,67 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include "HEJ/EWConstants.hh" #include "HEJ/MatrixElement.hh" #include "HEJ/optional.hh" #include "HEJ/PDF.hh" #include "HEJ/ScaleFunction.hh" #include "Beam.hh" #include "Decay.hh" #include "JetParameters.hh" #include "Process.hh" #include "Status.hh" -namespace HEJ{ +namespace HEJ { class Event; class HiggsCouplingSettings; class RNG; } //! Namespace for HEJ Fixed Order Generator -namespace HEJFOG{ +namespace HEJFOG { class EventGenerator{ public: EventGenerator( Process process, Beam beam, HEJ::ScaleGenerator scale_gen, JetParameters jets, int pdf_id, double subl_change, unsigned int subl_channels, ParticlesDecayMap particle_decays, HEJ::HiggsCouplingSettings Higgs_coupling, HEJ::EWConstants ew_parameters, std::shared_ptr ran ); HEJ::optional gen_event(); Status status() const { return status_; } private: HEJ::PDF pdf_; HEJ::MatrixElement ME_; HEJ::ScaleGenerator scale_gen_; Process process_; JetParameters jets_; Beam beam_; Status status_; double subl_change_; unsigned int subl_channels_; ParticlesDecayMap particle_decays_; HEJ::EWConstants ew_parameters_; std::shared_ptr ran_; }; } diff --git a/FixedOrderGen/include/JetParameters.hh b/FixedOrderGen/include/JetParameters.hh index 8649fff..6ea2711 100644 --- a/FixedOrderGen/include/JetParameters.hh +++ b/FixedOrderGen/include/JetParameters.hh @@ -1,19 +1,19 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include "fastjet/JetDefinition.hh" #include "HEJ/optional.hh" -namespace HEJFOG{ +namespace HEJFOG { struct JetParameters{ fastjet::JetDefinition def; double min_pt; double max_y; HEJ::optional peak_pt; }; } diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh index f3e1910..e88e82a 100644 --- a/FixedOrderGen/include/PhaseSpacePoint.hh +++ b/FixedOrderGen/include/PhaseSpacePoint.hh @@ -1,238 +1,238 @@ /** \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 "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" #include "fastjet/PseudoJet.hh" #include "Decay.hh" #include "Status.hh" -namespace HEJ{ +namespace HEJ { class EWConstants; class PDF; class RNG; } -namespace HEJFOG{ +namespace HEJFOG { class JetParameters; class Process; //! A point in resummation phase space class PhaseSpacePoint{ public: //! Default PhaseSpacePoint Constructor PhaseSpacePoint() = delete; //! PhaseSpacePoint Constructor /** * @param proc The process to generate * @param jet_properties 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 change 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_properties, HEJ::PDF & pdf, double E_beam, double subl_chance, unsigned int 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_; } private: friend HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); /** * @internal * @brief Generate LO parton momentum * * @param count 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 count, 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, unsigned int subl_channels, HEJ::PDF & pdf, double E_beam, double uf, HEJ::RNG & ran ); /** * @internal * @brief Returns list of all allowed initial states partons */ std::array,2> filter_partons( Process const & proc, unsigned int const subl_channels, HEJ::RNG & ran ); HEJ::ParticleID generate_incoming_id( std::size_t beam_idx, double x, double uf, HEJ::PDF & pdf, std::bitset<11> allowed_partons, HEJ::RNG & ran ); bool momentum_conserved(double ep) const; HEJ::Particle const & most_backward_FKL( std::vector const & partons ) const; HEJ::Particle const & most_forward_FKL( std::vector const & partons ) const; HEJ::Particle & most_backward_FKL(std::vector & partons) const; HEJ::Particle & most_forward_FKL(std::vector & partons) 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 Change 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, unsigned int channels, Process const & proc, HEJ::RNG & ran); void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward, HEJ::RNG & ran); void turn_to_qqx(bool allow_strange, HEJ::RNG & ran); //! decay where we select the decay channel std::vector decay_boson( HEJ::Particle const & parent, std::vector const & decays, HEJ::RNG & ran ); //! generate decay products of a boson std::vector decay_boson( 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 ptmax, HEJ::RNG & ran); double gen_parton_pt( int count, JetParameters const & jet_param, double ptmax, double y, HEJ::RNG & ran ); 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); } diff --git a/FixedOrderGen/include/Process.hh b/FixedOrderGen/include/Process.hh index aef9426..aebea2f 100644 --- a/FixedOrderGen/include/Process.hh +++ b/FixedOrderGen/include/Process.hh @@ -1,22 +1,22 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/PDG_codes.hh" #include "HEJ/optional.hh" -namespace HEJFOG{ +namespace HEJFOG { struct Process{ std::array incoming; int njets; HEJ::optional boson; std::vector boson_decay; }; } diff --git a/FixedOrderGen/include/Status.hh b/FixedOrderGen/include/Status.hh index 5639b49..0b5e926 100644 --- a/FixedOrderGen/include/Status.hh +++ b/FixedOrderGen/include/Status.hh @@ -1,27 +1,27 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include -namespace HEJFOG{ +namespace HEJFOG { enum Status{ good, not_enough_jets, too_much_energy }; inline std::string to_string(Status s){ switch(s){ case good: return "good"; case not_enough_jets: return "not enough jets"; case too_much_energy: return "too much energy"; default:; } throw std::logic_error{"unreachable"}; } } diff --git a/FixedOrderGen/include/Subleading.hh b/FixedOrderGen/include/Subleading.hh index 36f2976..ef76017 100644 --- a/FixedOrderGen/include/Subleading.hh +++ b/FixedOrderGen/include/Subleading.hh @@ -1,24 +1,24 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once -namespace HEJFOG{ +namespace HEJFOG { namespace channels{ /** * Bit position of different subleading channels * e.g. (unsigned int) 1 => only unordered */ enum Subleading: unsigned { none = 0u, all = ~0u, uno = 1u, unordered = uno, qqx = 2u }; } using Subleading = channels::Subleading; } diff --git a/FixedOrderGen/include/config.hh b/FixedOrderGen/include/config.hh index 4ac0350..212f059 100644 --- a/FixedOrderGen/include/config.hh +++ b/FixedOrderGen/include/config.hh @@ -1,49 +1,49 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "HEJ/Config.hh" #include "HEJ/EWConstants.hh" #include "HEJ/Fraction.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/optional.hh" #include "HEJ/output_formats.hh" #include "yaml-cpp/yaml.h" #include "Beam.hh" #include "Decay.hh" #include "JetParameters.hh" #include "Process.hh" #include "UnweightSettings.hh" -namespace HEJFOG{ +namespace HEJFOG { struct Config{ Process process; std::size_t events; JetParameters jets; Beam beam; int pdf_id; HEJ::Fraction subleading_fraction; unsigned int subleading_channels; //! < see HEJFOG::Subleading ParticlesDecayMap particle_decays; std::vector analyses_parameters; HEJ::ScaleConfig scales; std::vector output; HEJ::RNGConfig rng; HEJ::HiggsCouplingSettings Higgs_coupling; HEJ::EWConstants ew_parameters; HEJ::optional unweight; }; Config load_config(std::string const & config_file); } diff --git a/FixedOrderGen/src/EventGenerator.cc b/FixedOrderGen/src/EventGenerator.cc index 811b9f3..84b03c7 100644 --- a/FixedOrderGen/src/EventGenerator.cc +++ b/FixedOrderGen/src/EventGenerator.cc @@ -1,99 +1,99 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "EventGenerator.hh" #include #include #include "HEJ/Config.hh" #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/EWConstants.hh" #include "HEJ/exceptions.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "Process.hh" #include "Beam.hh" #include "JetParameters.hh" #include "PhaseSpacePoint.hh" -namespace HEJFOG{ +namespace HEJFOG { EventGenerator::EventGenerator( Process process, Beam beam, HEJ::ScaleGenerator scale_gen, JetParameters jets, int pdf_id, double subl_change, unsigned int subl_channels, ParticlesDecayMap particle_decays, HEJ::HiggsCouplingSettings Higgs_coupling, HEJ::EWConstants ew_parameters, std::shared_ptr ran ): pdf_{pdf_id, beam.particles[0], beam.particles[1]}, ME_{ [this](double mu){ return pdf_.Halphas(mu); }, HEJ::MatrixElementConfig{ false, std::move(Higgs_coupling), ew_parameters } }, scale_gen_{std::move(scale_gen)}, process_{std::move(process)}, jets_{std::move(jets)}, beam_{std::move(beam)}, subl_change_{subl_change}, subl_channels_{subl_channels}, particle_decays_{std::move(particle_decays)}, ew_parameters_{ew_parameters}, ran_{std::move(ran)} { } HEJ::optional EventGenerator::gen_event(){ HEJFOG::PhaseSpacePoint psp{ process_, jets_, pdf_, beam_.energy, subl_change_, subl_channels_, particle_decays_, ew_parameters_, *ran_ }; status_ = psp.status(); if(status_ != good) return {}; HEJ::Event ev = scale_gen_( HEJ::Event{ to_EventData( std::move(psp) ).cluster( jets_.def, jets_.min_pt) } ); if(!is_resummable(ev.type())) throw HEJ::not_implemented("Tried to generate a event type, " "which is not yet implemented in HEJ."); ev.generate_colours(*ran_); const double shat = HEJ::shat(ev); const double xa = (ev.incoming()[0].E()-ev.incoming()[0].pz())/(2.*beam_.energy); const double xb = (ev.incoming()[1].E()+ev.incoming()[1].pz())/(2.*beam_.energy); // evaluate matrix element ev.parameters() *= ME_.tree(ev)/(shat*shat); // and PDFs ev.central().weight *= pdf_.pdfpt(0,xa,ev.central().muf, ev.incoming()[0].type); ev.central().weight *= pdf_.pdfpt(0,xb,ev.central().muf, ev.incoming()[1].type); for(std::size_t i = 0; i < ev.variations().size(); ++i){ auto & var = ev.variations(i); var.weight *= pdf_.pdfpt(0,xa,var.muf, ev.incoming()[0].type); var.weight *= pdf_.pdfpt(0,xb,var.muf, ev.incoming()[1].type); } return ev; } } diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc index 19cff80..6169c52 100644 --- a/FixedOrderGen/src/PhaseSpacePoint.cc +++ b/FixedOrderGen/src/PhaseSpacePoint.cc @@ -1,710 +1,701 @@ /** * \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/exceptions.hh" #include "HEJ/kinematics.hh" #include "HEJ/Particle.hh" #include "HEJ/PDF.hh" #include "HEJ/RNG.hh" #include "HEJ/utility.hh" #include "JetParameters.hh" #include "Process.hh" #include "Subleading.hh" namespace { using namespace HEJ; static_assert( std::numeric_limits::has_quiet_NaN, "no quiet NaN for double" ); constexpr double NaN = std::numeric_limits::quiet_NaN(); } // namespace anonymous -namespace HEJFOG{ +namespace HEJFOG { Event::EventData to_EventData(PhaseSpacePoint psp){ //! @TODO Same function already in HEJ Event::EventData result; result.incoming = std::move(psp).incoming_; assert(result.incoming.size() == 2); result.outgoing = std::move(psp).outgoing_; // 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); result.decays = std::move(psp).decays_; result.parameters.central = {NaN, NaN, psp.weight()}; return result; } namespace { bool can_swap_to_uno( Particle const & p1, Particle const & p2 ){ return is_parton(p1) && p1.type != pid::gluon && p2.type == pid::gluon; } size_t count_gluons(std::vector::const_iterator first, std::vector::const_iterator last ){ return std::count_if(first, last, [](Particle const & p) {return p.type == 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 */ bool can_change_to_qqx( std::vector::const_iterator first, std::vector::const_iterator last ){ return 1 < count_gluons(first,last); } bool is_AWZ_proccess(Process const & proc){ return proc.boson && is_AWZ_boson(*proc.boson); } bool is_up_type(Particle const & part){ return is_anyquark(part) && !(std::abs(part.type)%2); } bool is_down_type(Particle const & part){ return is_anyquark(part) && std::abs(part.type)%2; } bool can_couple_to_W(Particle const & part, pid::ParticleID const W_id){ const int W_charge = W_id>0?1:-1; return std::abs(part.type)<5 && ( (W_charge*part.type > 0 && is_up_type(part)) || (W_charge*part.type < 0 && is_down_type(part)) ); } } void PhaseSpacePoint::maybe_turn_to_subl( double chance, unsigned int const channels, Process const & proc, RNG & ran ){ if(proc.njets <= 2) return; assert(outgoing_.size() >= 2); // decide what kind of subleading process is allowed bool allow_uno = false; bool allow_strange = true; const size_t nout = outgoing_.size(); const bool can_be_uno_backward = (channels&Subleading::uno) && can_swap_to_uno(outgoing_[0], outgoing_[1]); const bool can_be_uno_forward = (channels&Subleading::uno) && can_swap_to_uno(outgoing_[nout-1], outgoing_[nout-2]); allow_uno = can_be_uno_backward || can_be_uno_forward; bool allow_qqx = (channels&Subleading::qqx) && can_change_to_qqx(outgoing_.cbegin(), outgoing_.cend()); if(is_AWZ_proccess(proc)) { if(std::none_of(outgoing_.cbegin(), outgoing_.cend(), [&proc](Particle const & p){ return can_couple_to_W(p, *proc.boson);})) { // enforce qqx if A/W/Z can't couple somewhere else assert(allow_qqx); allow_uno = false; chance = 1.; // strange not allowed for W if(std::abs(*proc.boson)== pid::Wp) allow_strange = false; } } if(!allow_uno && !allow_qqx) return; if(ran.flat() < chance){ weight_ /= chance; if(allow_uno && !allow_qqx){ turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran); } else if (!allow_uno && allow_qqx) { turn_to_qqx(allow_strange, ran); } else { assert( allow_uno && allow_qqx); if(ran.flat() < 0.5) turn_to_uno(can_be_uno_backward, can_be_uno_forward, ran); else turn_to_qqx(allow_strange, ran); weight_ *= 2.; } } else weight_ /= 1 - chance; } void PhaseSpacePoint::turn_to_uno( const bool can_be_uno_backward, const bool can_be_uno_forward, RNG & ran ){ if(!can_be_uno_backward && !can_be_uno_forward) return; const size_t nout = outgoing_.size(); if(can_be_uno_backward && can_be_uno_forward){ if(ran.flat() < 0.5){ std::swap(outgoing_[0].type, outgoing_[1].type); } else { std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type); } weight_ *= 2.; } else if(can_be_uno_backward){ std::swap(outgoing_[0].type, outgoing_[1].type); } else { assert(can_be_uno_forward); std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type); } } void PhaseSpacePoint::turn_to_qqx(const bool allow_strange, RNG & ran){ /// find first and last gluon in FKL chain auto first = std::find_if(outgoing_.begin(), outgoing_.end(), [](Particle const & p){return p.type == pid::gluon;}); std::vector FKL_gluons; for(auto p = first; p!=outgoing_.end(); ++p){ if(p->type == pid::gluon) FKL_gluons.push_back(&*p); else if(is_anyquark(*p)) break; } const size_t ng = FKL_gluons.size(); if(ng < 2) throw std::logic_error("not enough gluons to create qqx"); // select flavour of quark const double r1 = 2.*ran.flat()-1.; - const double max_flavour = allow_strange?n_f:n_f-1; + const double max_flavour = allow_strange?N_F:N_F-1; weight_ *= max_flavour*2; int flavour = pid::down + std::floor(std::abs(r1)*max_flavour); flavour*=r1<0.?-1:1; // select gluon for switch const size_t idx = std::floor((ng-1) * ran.flat()); weight_ *= (ng-1); FKL_gluons[idx]->type = ParticleID(flavour); FKL_gluons[idx+1]->type = ParticleID(-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}; } namespace { //! adds a particle to target (in correct rapidity ordering) //! @returns positon of insertion auto insert_particle(std::vector & target, Particle && particle ){ const auto pos = std::upper_bound( begin(target),end(target),particle,rapidity_less{} ); target.insert(pos, std::move(particle)); return pos; } } PhaseSpacePoint::PhaseSpacePoint( Process const & proc, JetParameters const & jet_param, PDF & pdf, double E_beam, double const subl_chance, unsigned int const subl_channels, ParticlesDecayMap const & particle_decays, EWConstants const & ew_parameters, RNG & ran - ) - { + ){ assert(proc.njets >= 2); status_ = good; weight_ = 1; const int 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&& p_out: partons) { outgoing_.emplace_back(Particle{pid::gluon, std::move(p_out), {}}); } if(status_ != good) return; if(proc.boson){ // decay boson - const auto & boson_prop = ew_parameters.prop(*proc.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); - const auto & boson_decay = particle_decays.find(*proc.boson); + 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_boson(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_channels, pdf, E_beam, jet_param.min_pt, ran); if(status_ != good) return; // set outgoing states most_backward_FKL(outgoing_).type = incoming_[0].type; most_forward_FKL(outgoing_).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( - int np , double ptmin, double ptmax, double y, - RNG & ran + const int np , const double ptmin, const double ptmax, const double /* y */, + RNG & ran ) { - // heuristic parameters for pt sampling + // heuristic parameter for pt sampling, see eq:pt_par in developer manual const double ptpar = ptmin + np/5.; - const double arg_small_y = std::atan((ptmax - ptmin)/ptpar); - const double y_cut = 3.; - - const double r1 = ran.flat(); - if(y < y_cut){ - const double pt = ptmin + ptpar*std::tan(r1*arg_small_y); - const double temp = std::cos(r1*arg_small_y); - weight_ *= pt*ptpar*arg_small_y/(temp*temp); - return pt; - } - const double ptpar2 = ptpar/(1 + 5*(y-y_cut)); - const double temp = 1. - std::exp((ptmin-ptmax)/ptpar2); - const double pt = ptmin - ptpar2*std::log(1-r1*temp); - weight_ *= pt*ptpar2*temp/(1-r1*temp); + 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, 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, 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_ = 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, 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_ = not_enough_jets; return {}; } std::sort(begin(partons), end(partons), rapidity_less{}); return partons; } Particle PhaseSpacePoint::gen_boson( ParticleID bosonid, double mass, double width, 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) == pid::Wp){ weight_/=M_PI*M_PI*8.; weight_*= mass*width*( M_PI+2.*std::atan(mass/width) ) / ( 1. + std::cos( M_PI*r1 + 2.*(r1-1.)*std::atan(mass/width) ) ); } auto p = gen_last_momentum(outgoing_, s_boson, y); return Particle{bosonid, std::move(p), {}}; } Particle const & PhaseSpacePoint::most_backward_FKL( std::vector const & partons ) const{ if(!is_parton(partons[0])) return partons[1]; return partons[0]; } Particle const & PhaseSpacePoint::most_forward_FKL( std::vector const & partons ) const{ const size_t last_idx = partons.size() - 1; if(!is_parton(partons[last_idx])) return partons[last_idx-1]; return partons[last_idx]; } Particle & PhaseSpacePoint::most_backward_FKL( std::vector & partons ) const{ if(!is_parton(partons[0])) return partons[1]; return partons[0]; } Particle & PhaseSpacePoint::most_forward_FKL( std::vector & partons ) const{ const size_t last_idx = partons.size() - 1; if(!is_parton(partons[last_idx])) return partons[last_idx-1]; return partons[last_idx]; } namespace { /// partons are ordered: even = anti, 0 = gluon ParticleID index_to_pid(size_t i){ if(!i) return pid::gluon; return static_cast( i%2 ? (i+1)/2 : -i/2 ); } /// partons are ordered: even = anti, 0 = gluon size_t pid_to_index(ParticleID id){ if(id==pid::gluon) return 0; return id>0 ? id*2-1 : std::abs(id)*2; } std::bitset<11> init_allowed(ParticleID const id){ if(std::abs(id) == pid::proton) return ~0; std::bitset<11> out{0}; if(is_parton(id)) out[pid_to_index(id)] = 1; return out; } /// decides which "index" (see index_to_pid) are allowed for process std::bitset<11> allowed_quarks(ParticleID const boson){ std::bitset<11> allowed = ~0; if(std::abs(boson) == pid::Wp){ // special case W: // Wp: anti-down or up-type quark, no b/t // Wm: down or anti-up-type quark, no b/t allowed = boson>0? 0b00011001101 :0b00100110011; } return allowed; } } /** * 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 * b) 2j => no incoming gluon * c) 3j => can couple OR is gluon => 2 gluons become qqx later */ std::array,2> PhaseSpacePoint::filter_partons( Process const & proc, unsigned int const subl_channels, RNG & ran ){ std::array,2> allowed_partons{ init_allowed(proc.incoming[0]), init_allowed(proc.incoming[1]) }; bool const allow_qqx = subl_channels&Subleading::qqx; // special case A/W/Z if(is_AWZ_proccess(proc) && ((proc.njets < 4) || !allow_qqx)){ // all possible incoming states auto allowed(allowed_quarks(*proc.boson)); if(proc.njets == 2 || !allow_qqx) allowed[0]=0; // possible states per leg std::array,2> const maybe_partons{ allowed_partons[0]&allowed, allowed_partons[1]&allowed}; if(maybe_partons[0].any() && maybe_partons[1].any()){ // two options to get allowed initial state => choose one at random const size_t idx = ran.flat() < 0.5; allowed_partons[idx] = maybe_partons[idx]; // else choose the possible } else if(maybe_partons[0].any()) { allowed_partons[0] = maybe_partons[0]; } else if(maybe_partons[1].any()) { allowed_partons[1] = maybe_partons[1]; } else{ throw std::invalid_argument{"Incoming state not allowed."}; } } return allowed_partons; } void PhaseSpacePoint::reconstruct_incoming( Process const & proc, unsigned int const subl_channels, PDF & pdf, double E_beam, double uf, 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_ = too_much_energy; return; } auto const & ids = proc.incoming; std::array,2> allowed_partons( filter_partons(proc, subl_channels, ran)); for(size_t i = 0; i < 2; ++i){ if(ids[i] == pid::proton || ids[i] == 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)); } ParticleID PhaseSpacePoint::generate_incoming_id( size_t const beam_idx, double const x, double const uf, PDF & pdf, std::bitset<11> allowed_partons, RNG & ran ){ std::array pdf_wt; pdf_wt[0] = allowed_partons[0]? std::fabs(pdf.pdfpt(beam_idx,x,uf,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: outgoing_){ // Wp -> up OR anti-down, Wm -> anti-up OR down, no bottom if ( can_couple_to_W(part, boson) ) allowed_parts.push_back(&part); } if(allowed_parts.size() == 0){ 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, 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 nearby_ep(diff, fastjet::PseudoJet{}, ep); } Decay PhaseSpacePoint::select_decay_channel( std::vector const & decays, 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"}; } std::vector PhaseSpacePoint::decay_boson( Particle const & parent, std::vector const & decays, RNG & ran ){ const auto channel = select_decay_channel(decays, ran); return decay_boson(parent, channel.products, ran); } std::vector PhaseSpacePoint::decay_boson( Particle const & parent, std::vector const & decays, RNG & ran ){ if(decays.size() != 2){ throw 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.; 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; } } diff --git a/FixedOrderGen/src/config.cc b/FixedOrderGen/src/config.cc index 5c367f7..4d869b4 100644 --- a/FixedOrderGen/src/config.cc +++ b/FixedOrderGen/src/config.cc @@ -1,439 +1,439 @@ /** * \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 "HEJ/exceptions.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/YAMLreader.hh" #include "Subleading.hh" -namespace HEJFOG{ +namespace HEJFOG { using HEJ::set_from_yaml; using HEJ::set_from_yaml_if_defined; using HEJ::pid::ParticleID; - namespace{ + 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, end = 0; end != str.npos;){ begin = str.find_first_not_of(delims, end); if(begin == str.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::channels; set_from_yaml(name, yaml); if(name == "none") return none; if(name == "all") return all; if(name == "unordered" || name == "uno") return uno; if(name == "qqx") return qqx; throw HEJ::unknown_option("Unknown subleading channel '"+name+"'"); } unsigned int get_subleading_channels(YAML::Node const & node){ using YAML::NodeType; using namespace HEJFOG::channels; // 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: unsigned int channels = HEJFOG::Subleading::none; 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; } HEJ::ParticleProperties get_particle_properties( YAML::Node const & node, std::string const & entry, std::string const & boson ){ HEJ::ParticleProperties result; set_from_yaml(result.mass, node, entry, boson, "mass"); set_from_yaml(result.width, node, entry, boson, "width"); return result; } HEJ::EWConstants get_ew_parameters(YAML::Node const & node){ HEJ::EWConstants result; double vev; 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; } 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){ - const auto & in = config.process.incoming[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 = Subleading::none; else config.subleading_channels = get_subleading_channels(yaml["subleading channels"]); config.ew_parameters = 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"]){ 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"]) config.unweight = get_unweight(yaml, "unweight"); return config; } } // namespace anonymous 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; } } } diff --git a/FixedOrderGen/src/main.cc b/FixedOrderGen/src/main.cc index 51e9a1a..c9b7fec 100644 --- a/FixedOrderGen/src/main.cc +++ b/FixedOrderGen/src/main.cc @@ -1,292 +1,293 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "fastjet/PseudoJet.hh" #include "HEJ/Analysis.hh" #include "HEJ/CombinedEventWriter.hh" #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #include "HEJ/get_analysis.hh" #include "HEJ/make_RNG.hh" #include "HEJ/ProgressBar.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/Unweighter.hh" #include "LHEF/LHEF.h" #include "config.hh" #include "EventGenerator.hh" #include "Status.hh" #include "Version.hh" namespace YAML { class Node; } namespace { constexpr auto banner = " __ ___ __ ______ __ " " __ \n / / / (_)___ _/ /_ / ____/___ " " ___ _________ ___ __ / /__ / /______ \n " " / /_/ / / __ `/ __ \\ / __/ / __ \\/ _ \\/ ___/ __ `/ / / / __ / / _" " \\/ __/ ___/ \n / __ / / /_/ / / / / / /___/ /" " / / __/ / / /_/ / /_/ / / /_/ / __/ /_(__ ) " " \n /_/ /_/_/\\__, /_/ /_/ /_____/_/ /_/\\___/_/ \\__, /\\__, / \\___" "_/\\___/\\__/____/ \n ____///__/ " "__ ____ ///__//____/ ______ __ " " \n / ____(_) _____ ____/ / / __ \\_________/ /__ _____ / " "____/__ ____ ___ _________ _/ /_____ _____\n / /_ / / |/_/ _ \\/ __" " / / / / / ___/ __ / _ \\/ ___/ / / __/ _ \\/ __ \\/ _ \\/ ___/ __ `/ " "__/ __ \\/ ___/\n / __/ / /> > get_analyses( std::vector const & parameters, LHEF::HEPRUP const & heprup ){ try{ return HEJ::get_analyses(parameters, heprup); } catch(std::exception const & exc){ std::cerr << "Failed to load analysis: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } template auto make_lowpt_filter(Iterator begin, Iterator end, HEJ::optional peak_pt){ return boost::make_filter_iterator( [peak_pt](HEJ::Event const & ev){ assert(! ev.jets().empty()); double min_pt = peak_pt?(*peak_pt):0.; const auto softest_jet = fastjet::sorted_by_pt(ev.jets()).back(); return softest_jet.pt() > min_pt; }, begin, end ); } int main(int argn, char** argv) { using namespace std::string_literals; if (argn < 2) { std::cerr << "\n# Usage:\n." << argv[0] << " config_file\n"; return EXIT_FAILURE; } std::cout << banner; std::cout << "Version " << HEJFOG::Version::String() << ", revision " << HEJFOG::Version::revision() << std::endl; fastjet::ClusterSequence::print_banner(); using clock = std::chrono::system_clock; const auto start_time = clock::now(); // read configuration auto config = load_config(argv[1]); std::shared_ptr ran{ HEJ::make_RNG(config.rng.name, config.rng.seed)}; assert(ran != nullptr); HEJ::ScaleGenerator scale_gen{ config.scales.base, config.scales.factors, config.scales.max_ratio }; HEJFOG::EventGenerator generator{ config.process, config.beam, std::move(scale_gen), config.jets, config.pdf_id, config.subleading_fraction, config.subleading_channels, config.particle_decays, config.Higgs_coupling, config.ew_parameters, ran }; // prepare process information for output LHEF::HEPRUP heprup; heprup.IDBMUP=std::pair(config.beam.particles[0], config.beam.particles[1]); heprup.EBMUP=std::make_pair(config.beam.energy, config.beam.energy); heprup.PDFGUP=std::make_pair(0,0); heprup.PDFSUP=std::make_pair(config.pdf_id,config.pdf_id); heprup.NPRUP=1; heprup.XSECUP=std::vector(1.); heprup.XERRUP=std::vector(1.); heprup.LPRUP=std::vector{1}; heprup.generators.emplace_back(LHEF::XMLTag{}); heprup.generators.back().name = HEJFOG::Version::package_name(); heprup.generators.back().version = HEJFOG::Version::String(); HEJ::CombinedEventWriter writer{config.output, heprup}; std::vector> analyses = get_analyses( config.analyses_parameters, heprup ); assert(analyses.empty() || analyses.front() != nullptr); // warm-up phase to train unweighter HEJ::optional unweighter{}; std::map status_counter; std::vector events; std::uint64_t trials = 0; if(config.unweight) { std::cout << "Calibrating unweighting ...\n"; const auto warmup_start = clock::now(); const std::size_t warmup_events = config.unweight->sample_size; HEJ::ProgressBar warmup_progress{std::cout, warmup_events}; for(; events.size() < warmup_events; ++trials){ auto ev = generator.gen_event(); ++status_counter[generator.status()]; assert( (generator.status() == HEJFOG::good) == bool(ev) ); if(generator.status() != HEJFOG::good) continue; const bool pass_cuts = analyses.empty() || std::any_of( begin(analyses), end(analyses), [&ev](auto const & analysis) { return analysis->pass_cuts(*ev, *ev); } ); if(pass_cuts) { events.emplace_back(std::move(*ev)); ++warmup_progress; } } std::cout << std::endl; unweighter = HEJ::Unweighter(); unweighter->set_cut_to_peakwt( make_lowpt_filter(events.cbegin(), events.cend(), config.jets.peak_pt), make_lowpt_filter(events.cend(), events.cend(), config.jets.peak_pt), config.unweight->max_dev ); std::vector unweighted_events; for(auto && ev: events) { auto unweighted = unweighter->unweight(std::move(ev), *ran); if(unweighted) { unweighted_events.emplace_back(std::move(*unweighted)); } } events = std::move(unweighted_events); if(events.empty()) { std::cerr << "Failed to generate events. Please increase \"unweight: sample size\"" " or reduce \"unweight: max deviation\"\n"; return EXIT_FAILURE; } const auto warmup_end = clock::now(); const double completion = static_cast(events.size())/config.events; const std::chrono::duration remaining_time = (warmup_end- warmup_start)*(1./completion - 1); const auto finish = clock::to_time_t( std::chrono::time_point_cast(warmup_end + remaining_time) ); std::cout << "Generated " << events.size() << "/" << config.events << " events (" << static_cast(std::round(100*completion)) << "%)\n" << "Estimated remaining generation time: " << remaining_time.count() << " seconds (" << std::put_time(std::localtime(&finish), "%c") << ")\n\n"; } // end unweighting warm-up // main generation loop // event weight is wrong, need to divide by "total number of trials" afterwards HEJ::ProgressBar progress{std::cout, config.events}; progress.increment(events.size()); events.reserve(config.events); for(; events.size() < config.events; ++trials){ auto ev = generator.gen_event(); ++status_counter[generator.status()]; assert( (generator.status() == HEJFOG::good) == bool(ev) ); if(generator.status() != HEJFOG::good) continue; const bool pass_cuts = analyses.empty() || std::any_of( begin(analyses), end(analyses), [&ev](auto const & analysis) { return analysis->pass_cuts(*ev, *ev); } ); if(pass_cuts) { if(unweighter) { auto unweighted = unweighter->unweight(std::move(*ev), *ran); if(! unweighted) continue; ev = std::move(unweighted); } events.emplace_back(std::move(*ev)); ++progress; } } std::cout << std::endl; // final run though events with correct weight HEJ::CrossSectionAccumulator xs; for(auto & ev: events){ ev.parameters() *= invGeV2_to_pb/trials; for(auto const & analysis: analyses) { if(analysis->pass_cuts(ev, ev)) { analysis->fill(ev, ev); } } writer.write(ev); xs.fill(ev); } for(auto const & analysis: analyses) { analysis->finalise(); } + writer.finish(); // Print final informations const std::chrono::duration run_time = (clock::now() - start_time); std::cout << "\nTask Runtime: " << run_time.count() << " seconds for " << events.size() << " Events (" << events.size()/run_time.count() << " evts/s)\n" << std::endl; std::cout << xs << "\n"; for(auto && entry: status_counter){ const double fraction = static_cast(entry.second)/trials; const int percent = std::round(100*fraction); std::cout << "status " << std::left << std::setw(16) << (to_string(entry.first) + ":") << " ["; for(int i = 0; i < percent/2; ++i) std::cout << '#'; for(int i = percent/2; i < 50; ++i) std::cout << ' '; std::cout << "] " << percent << "%" << std::endl; } return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_2j_decay.cc b/FixedOrderGen/t/h_2j_decay.cc index 049c205..69e609d 100644 --- a/FixedOrderGen/t/h_2j_decay.cc +++ b/FixedOrderGen/t/h_2j_decay.cc @@ -1,101 +1,101 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #ifdef NDEBUG #undef NDEBUG #endif #include #include #include #include #include #include #include "fastjet/PseudoJet.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/Ranlux64.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/utility.hh" #include "config.hh" #include "EventGenerator.hh" #include "Status.hh" namespace { using namespace HEJFOG; bool pass_dR_cut( std::vector const & jets, std::vector const & photons ){ constexpr double delta_R_min = 0.7; for(auto const & jet: jets){ for(auto const & photon: photons){ if(jet.delta_R(photon.p) < delta_R_min) return false; } } return true; } } int main(){ constexpr double invGeV2_to_pb = 389379292.; constexpr double xs_ref = 0.00429198; // +- 1.0488e-05 //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 auto config = load_config("config_h_2j_decay.yml"); std::shared_ptr ran{std::make_shared()}; HEJFOG::EventGenerator generator{ 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 }; double xs = 0., xs_err = 0.; for (std::size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); if(generator.status() != good) continue; assert(ev); assert(ev->decays().size() == 1); const auto decay = ev->decays().begin(); assert(ev->outgoing().size() > decay->first); - const auto & the_Higgs = ev->outgoing()[decay->first]; + auto const & the_Higgs = ev->outgoing()[decay->first]; assert(the_Higgs.type == HEJ::pid::Higgs); assert(decay->second.size() == 2); auto const & gamma = decay->second; assert(gamma[0].type == HEJ::pid::photon); assert(gamma[1].type == HEJ::pid::photon); assert(HEJ::nearby_ep(gamma[0].p + gamma[1].p, the_Higgs.p, 1e-6)); if(!pass_dR_cut(ev->jets(), gamma)) continue; ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; xs_err += ev->central().weight*ev->central().weight; } xs_err = std::sqrt(xs_err); std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << std::endl; assert(std::abs(xs - xs_ref) < 3*xs_err); assert(xs_err < 0.012*xs); return EXIT_SUCCESS; } diff --git a/cmake/Templates/hej-config.cmake.in b/cmake/Templates/hej-config.cmake.in index 41755d8..958b070 100644 --- a/cmake/Templates/hej-config.cmake.in +++ b/cmake/Templates/hej-config.cmake.in @@ -1,29 +1,28 @@ @PACKAGE_INIT@ # - Config file for the HEJ package # It defines the following variables # HEJ_INCLUDE_DIR - include directory # HEJ_LIBRARIES - libraries to link against # # And the following `IMPORTED` target: # # HEJ::HEJ - The main hej library set_and_check(HEJ_INCLUDE_DIR @PACKAGE_INSTALL_INCLUDE_DIR_BASE@) find_library(HEJ_LIB NAMES HEJ HINTS @PACKAGE_INSTALL_LIB_DIR@) set(HEJ_LIBRARIES ${HEJ_LIB}) get_filename_component(HEJ_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include(CMakeFindDependencyMacro) find_dependency(Boost REQUIRED COMPONENTS iostreams) -find_dependency(yaml-cpp REQUIRED) find_dependency(CLHEP 2.3 REQUIRED) -find_dependency(LHAPDF REQUIRED) find_dependency(fastjet REQUIRED) +find_dependency(yaml-cpp REQUIRED) if(NOT TARGET HEJ::HEJ) include("${HEJ_CMAKE_DIR}/hejTargets.cmake") endif() diff --git a/current_generator/include/currents.frm b/current_generator/include/currents.frm index 9ba8931..a8d213e 100644 --- a/current_generator/include/currents.frm +++ b/current_generator/include/currents.frm @@ -1,63 +1,51 @@ */** * \brief Predefined current functions and polarisation vectors * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #ifndef `$HEJCurrentsIncluded' #$HEJCurrentsIncluded = 1; -* W FKL current -cfunction JW; -* Z FKL current -cfunction JZ; +* FKL current for vector boson +cfunction JV; * Polarisation vectors cfunction Eps; cfunction m2,sqrt; * internal symbols are all uppercase symbols H, H1, HL; indices MU1,...,MU50; vectors PA,P1,PLBAR,PL,PG,PR; #procedure InsSpecialCurrents - #call InsJW - #call InsJZ + #call InsJV #call InsEps #endprocedure - -* Replace W FKL current using eq:Weffcur1 in developer manual -#procedure InsJW() - - id JW(H?, MU1?, PA?, P1?, PLBAR?, PL?) = JZ(H, -1, MU1, PA, P1, PLBAR, PL); - #call InsJZ - -#endprocedure - -* Replace Z FKL current -#procedure InsJZ() +* Replace vector boson FKL current +#procedure InsJV() * we use a loop here to introduce a new dummy index for each occurence repeat; - once JZ(H1?, HL?, MU1?, PA?, P1?, PLBAR?, PL?) = Current(HL, PL, MU2, PLBAR)*( + once JV(H1?, HL?, MU1?, PA?, P1?, PLBAR?, PL?) = Current(HL, PL, MU2, PLBAR)*( + Current(H1, P1, MU2, P1+PLBAR+PL, MU1, PA)/m2(P1+PLBAR+PL) + Current(H1, P1, MU1, PA-PLBAR-PL, MU2, PA)/m2(PA-PLBAR-PL) ); sum MU2; endrepeat; #endprocedure * Replace polarisation vectors by currents #procedure InsEps() id Eps(-1, MU1?, PG?, PR?) = sqrt(2)/2*SpinorChain(PR, MU1, PG)/AngleChain(PG,PR); id Eps(+1, MU1?, PG?, PR?) = sqrt(2)/2*SpinorChain(PG, MU1, PR)/SquareChain(PG,PR); #endprocedure #endif diff --git a/current_generator/include/write.frm b/current_generator/include/write.frm index aadb6a8..78cb2cd 100644 --- a/current_generator/include/write.frm +++ b/current_generator/include/write.frm @@ -1,165 +1,166 @@ */** * \brief Procedures for writing current contraction headers * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #ifndef `$HEJWriteIncluded' #$HEJWriteIncluded = 1; * Write start of C++ header to `OUTPUT' #procedure WriteHeader(OUTPUT) #write <`OUTPUT'> "#pragma once" #write <`OUTPUT'> "" #write <`OUTPUT'> "#include " #write <`OUTPUT'> "#include \"HEJ/helicity.hh\"" #write <`OUTPUT'> "#include \"HEJ/LorentzVector.hh\"" + #write <`OUTPUT'> "#include \"HEJ/utility.hh\"" #write <`OUTPUT'> "" #write <`OUTPUT'> "namespace HEJ {" #endprocedure * Write optimised expression to C++ header `OUTPUT' #procedure WriteOptimised(OUTPUT,EXPRESSION,NUMHELICITIES,?MOMENTA) #write <`OUTPUT'> " template<%" #define FIRST "1" #do i=1,`NUMHELICITIES' #ifdef `FIRST' #undefine FIRST #write <`OUTPUT'> "Helicity%" #else #write <`OUTPUT'> ", Helicity%" #endif #enddo #write <`OUTPUT'> ">" #write <`OUTPUT'> " std::complex `EXPRESSION'(" #call WriteMomenta(`?MOMENTA') #write <`OUTPUT'> "\n );\n" #call WriteOptimisedHelper(`OUTPUT',`EXPRESSION',`NUMHELICITIES',`?MOMENTA') #endprocedure *INTERNAL PROCEDURE #procedure WriteOptimisedHelper(OUTPUT,EXPRESSION,NUMHELICITIES,?REST) #if `NUMHELICITIES' > 0 #do H={+,-} #call WriteOptimisedHelper(`OUTPUT',`EXPRESSION',{`NUMHELICITIES'-1},`?REST',`H') #enddo #else #define HELSTRING "" #define TEMPLATEARGS "" #define MOMENTA "" #do ARG={`?REST'} #if ("`ARG'" == "+") || ("`ARG'" == "-") * arguments that define helicities #redefine HELSTRING "`HELSTRING'`ARG'" #if "`TEMPLATEARGS'" != "" #redefine TEMPLATEARGS "`TEMPLATEARGS'," #endif #if "`ARG'" == "+" #redefine TEMPLATEARGS "`TEMPLATEARGS'Helicity::plus" #else #redefine TEMPLATEARGS "`TEMPLATEARGS'Helicity::minus" #endif #else * arguments that define momenta #if "`MOMENTA'" != "" #redefine MOMENTA "`MOMENTA'," #endif #redefine MOMENTA "`MOMENTA'`ARG'" #endif #enddo #optimize [`EXPRESSION'`HELSTRING'] #write "operations in [`EXPRESSION'`HELSTRING']: `optimvalue_'" #write <`OUTPUT'> " template<>" #write <`OUTPUT'> " inline std::complex `EXPRESSION'<%" * we use a loop here because otherwise FORM will insert line breaks * if the string is too large #define FIRST "1" #do TEMPLATEARG={`TEMPLATEARGS'} #ifdef `FIRST' #undefine FIRST #else #write <`OUTPUT'> ", %" #endif #write <`OUTPUT'> "`TEMPLATEARG'%" #enddo #write <`OUTPUT'> ">(" #if termsin([`EXPRESSION'`HELSTRING']) > 0 #call WriteMomenta(`MOMENTA') #else #call WriteMomentaCommentedOut(`MOMENTA') #endif #write <`OUTPUT'> "\n ) {" #write <`OUTPUT'> " static constexpr std::complex i_{0., 1.};" - #write <`OUTPUT'> " (void) i_; //potentially unused" + #write <`OUTPUT'> " ignore(i_); //potentially unused" #if `optimmaxvar_' > 0 #write <`OUTPUT'> " std::complex %" #define FIRST "1" #do i=1,`optimmaxvar_' #ifdef `FIRST' #undefine FIRST #write <`OUTPUT'> "Z`i'_%" #else #write <`OUTPUT'> ", Z`i'_%" #endif #enddo #write <`OUTPUT'> ";" #endif #write <`OUTPUT'> " %O" #write <`OUTPUT'> " return %E;" [`EXPRESSION'`HELSTRING'] #write <`OUTPUT'> " }\n" [`EXPRESSION'`HELSTRING'] #clearoptimize #endif #endprocedure *INTERNAL PROCEDURE * Write momenta as C++ function arguments to `OUTPUT' #procedure WriteMomenta(?MOMENTA) #define FIRST "1" #do P={`?MOMENTA'} #ifdef `FIRST' #undefine FIRST #write <`OUTPUT'> " CLHEP::HepLorentzVector const & `P'%" #else #write <`OUTPUT'> ",\n CLHEP::HepLorentzVector const & `P'%" #endif #enddo #endprocedure *INTERNAL PROCEDURE * Write momenta as C++ function arguments to `OUTPUT' * with momentum names commented out #procedure WriteMomentaCommentedOut(?MOMENTA) #define FIRST "1" #do P={`?MOMENTA'} #ifdef `FIRST' #undefine FIRST #write <`OUTPUT'> " CLHEP::HepLorentzVector const & /* `P' */%" #else #write <`OUTPUT'> ",\n CLHEP::HepLorentzVector const & /* `P' */%" #endif #enddo #endprocedure * Write end of C++ header to `OUTPUT' #procedure WriteFooter(OUTPUT) #write <`OUTPUT'> "}" #endprocedure #endif diff --git a/current_generator/jZ_j.frm b/current_generator/jV_j.frm similarity index 70% rename from current_generator/jZ_j.frm rename to current_generator/jV_j.frm index 3cca4cd..c4c374a 100644 --- a/current_generator/jZ_j.frm +++ b/current_generator/jV_j.frm @@ -1,29 +1,29 @@ */** -* \brief Contraction of Z current with FKL current +* \brief Contraction of vector boson current with FKL current * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm v pb,p2,pa,p1,pl,plbar; i mu; #do h1={+,-} #do hl={+,-} #do h2={+,-} - l [jZ_j `h1'`hl'`h2'] = JZ(`h1'1, `hl'1, mu, pa, p1, plbar, pl)*Current(`h2'1, p2, mu, pb); + l [jV_j `h1'`hl'`h2'] = JV(`h1'1, `hl'1, mu, pa, p1, plbar, pl)*Current(`h2'1, p2, mu, pb); #enddo #enddo #enddo #call ContractCurrents .sort format O4; format c; #call WriteHeader(`OUTPUT') -#call WriteOptimised(`OUTPUT',jZ_j,3,pa,p1,pb,p2,pl,plbar) +#call WriteOptimised(`OUTPUT',jV_j,3,pa,p1,pb,p2,pl,plbar) #call WriteFooter(`OUTPUT') .end diff --git a/current_generator/jZ_juno.frm b/current_generator/jV_juno.frm similarity index 75% rename from current_generator/jZ_juno.frm rename to current_generator/jV_juno.frm index eb8a88f..dcd4cc0 100644 --- a/current_generator/jZ_juno.frm +++ b/current_generator/jV_juno.frm @@ -1,70 +1,70 @@ */** -* \brief Contraction of Z current with unordered current +* \brief Contraction of vector boson current with unordered current * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm -v pb,p2,pa,p1,pg,pl,plbar,pZ,q2,q3,p,pr; +v pb,p2,pa,p1,pg,pl,plbar,q2,q3,p,pr; i mu,nu,sigma; s h; #do h2={+,-} * eq:juno in developer manual with pa -> pb, p1 -> pg - l [U1_jZ `h2'] = ( + l [U1_jV `h2'] = ( + Current(`h2'1, p2, nu, pg)*Current(`h2'1, pg, mu, pb) + 2*p2(nu)*Current(`h2'1, p2, mu, pb) )/m2(p2 + pg); - l [U2_jZ `h2'] = ( + l [U2_jV `h2'] = ( + 2*Current(`h2'1, p2, mu, pb)*pb(nu) - Current(`h2'1, p2, mu, pg)*Current(`h2'1, pg, nu, pb) )/m2(pb - pg); - l [L_jZ `h2'] = ( + l [L_jV `h2'] = ( - (q2(nu) + q3(nu))*Current(`h2'1, p2, mu, pb) - 2*pg(mu)*Current(`h2'1, p2, nu, pb) + 2*Current(`h2'1, p2, pg, pb)*d_(mu, nu) + m2(q2)*Current(`h2'1, p2, mu, pb)*(p1(nu)/m2(p1+pg) + pa(nu)/m2(pa+pg)) )/m2(q3); #enddo .sort drop; * multiply with polarisation vector and other currents #do h1={+,-} #do hl={+,-} #do h2={+,-} #do hg={+,-} - #do TENSOR={U1_jZ,U2_jZ,L_jZ} + #do TENSOR={U1_jV,U2_jV,L_jV} l [`TENSOR' `h1'`hl'`h2'`hg'] = ( [`TENSOR' `h2'] - *JZ(`h1'1, `hl'1, mu, pa, p1, plbar, pl) + *JV(`h1'1, `hl'1, mu, pa, p1, plbar, pl) *Eps(`hg'1, nu, pr) ); #enddo #enddo #enddo #enddo #enddo * choice of auxiliary vector id Eps(h?, mu?, pr)*Current(h?, ?a) = Eps(h, mu, pg, p2)*Current(h, ?a); also Eps(h?, mu?, pr) = Eps(h, mu, pg, pb); multiply replace_( q2,p2+pg-pb, q3,p2-pb ); .sort #call ContractCurrents .sort format O4; format c; #call WriteHeader(`OUTPUT') -#call WriteOptimised(`OUTPUT',U1_jZ,4,pa,p1,pb,p2,pg,pl,plbar) -#call WriteOptimised(`OUTPUT',U2_jZ,4,pa,p1,pb,p2,pg,pl,plbar) -#call WriteOptimised(`OUTPUT',L_jZ,4,pa,p1,pb,p2,pg,pl,plbar) +#call WriteOptimised(`OUTPUT',U1_jV,4,pa,p1,pb,p2,pg,pl,plbar) +#call WriteOptimised(`OUTPUT',U2_jV,4,pa,p1,pb,p2,pg,pl,plbar) +#call WriteOptimised(`OUTPUT',L_jV,4,pa,p1,pb,p2,pg,pl,plbar) #call WriteFooter(`OUTPUT') .end diff --git a/current_generator/jZuno_j.frm b/current_generator/jVuno_j.frm similarity index 73% rename from current_generator/jZuno_j.frm rename to current_generator/jVuno_j.frm index 200adee..bb66160 100644 --- a/current_generator/jZuno_j.frm +++ b/current_generator/jVuno_j.frm @@ -1,103 +1,103 @@ */** -* \brief Contraction of Z unordered current with FKL current +* \brief Contraction of vector boson unordered current with FKL current * * TODO: unify conventions with developer manual * the current dictionary is as follows: * * code | manual * pg | p_1 * p1 | p_2 * pa | p_a * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm -s h,s2g,sbg,taZ1; -v p,p1,p2,pa,pb,pg,pl,plbar,pZ,pr,q1,q1g,q2; +s h,s2g,sbg,taV1; +v p,p1,p2,pa,pb,pg,pl,plbar,pV,pr,q1,q1g,q2; i mu,nu,rho,sigma; cf m2inv; #do h1={+,-} * eq:U1tensor in developer manual, up to factors 1/sij, 1/tij l [U1 `h1'] = ( - + Current(`h1'1, p1, nu, p1+pg, mu, pa-pZ, rho, pa) - + Current(`h1'1, p1, nu, p1+pg, rho, p1+pg+pZ, mu, pa) - + Current(`h1'1, p1, rho, p1+pZ, nu, p1+pg+pZ, mu, pa) + + Current(`h1'1, p1, nu, p1+pg, mu, pa-pV, rho, pa) + + Current(`h1'1, p1, nu, p1+pg, rho, p1+pg+pV, mu, pa) + + Current(`h1'1, p1, rho, p1+pV, nu, p1+pg+pV, mu, pa) ); * eq:U2tensor in developer manual, up to factors 1/sij, 1/tij l [U2 `h1'] = ( - + Current(`h1'1, p1, mu, pa - pZ - pg, nu, pa - pZ, rho, pa) - + Current(`h1'1, p1, mu, pa - pZ - pg, rho, pa - pg, nu, pa) - + Current(`h1'1, p1, rho, p1 + pZ, mu, pa - pg, nu, pa) + + Current(`h1'1, p1, mu, pa - pV - pg, nu, pa - pV, rho, pa) + + Current(`h1'1, p1, mu, pa - pV - pg, rho, pa - pg, nu, pa) + + Current(`h1'1, p1, rho, p1 + pV, mu, pa - pg, nu, pa) ); * eq:Ltensor in developer manual, up to factors 1/sij, 1/tij l [L `h1'] = ( - Current(`h1'1, p1, sigma, pa - pZ, rho, pa) + - Current(`h1'1, p1, rho, p1 + pZ, sigma, pa) + Current(`h1'1, p1, sigma, pa - pV, rho, pa) + + Current(`h1'1, p1, rho, p1 + pV, sigma, pa) )*( ((pb(nu)/sbg + p2(nu)/s2g)*m2(q1g) + 2*q1(nu) - pg(nu))*d_(mu, sigma) - 2*pg(mu)*d_(nu, sigma) + (2*pg(sigma) - q1(sigma))*d_(mu, nu) - )/taZ1; + )/taV1; #enddo .sort * restore kinematic factors id Current(h?, p1, mu?, q1?, nu?, q2?, rho?, pa) = ( Current(h, p1, mu, q1, nu, q2, rho, pa)*m2inv(q1)*m2inv(q2) ); id Current(h?, p1, mu?, q1?, nu?, pa) = ( Current(h, p1, mu, q1, nu, pa)*m2inv(q1) ); .sort drop; * multiply with polarisation vector and other currents #do h1={+,-} #do hl={+,-} #do h2={+,-} #do hg={+,-} #do TENSOR={U1,U2,L} l [`TENSOR' `h1'`hl'`h2'`hg'] = ( [`TENSOR' `h1'] *Eps(`hg'1, nu) *Current(`h2'1, p2, mu, pb) *Current(`hl'1, pl, rho, plbar) ); #enddo #enddo #enddo #enddo #enddo * choice of best reference vector (p2 or pb) id Eps(h?, nu?)*Current(h?, p2, mu?, pb) = Eps(h, nu, pg, p2)*Current(h, p2, mu, pb); also Eps(h?, mu?) = Eps(h, mu, pg, pb); multiply replace_(q1g,q1-pg); -multiply replace_(q1,pa-p1-pZ); -multiply replace_(pZ,pl+plbar); +multiply replace_(q1,pa-p1-pV); +multiply replace_(pV,pl+plbar); .sort #call ContractCurrents multiply replace_( s2g,m2(p2+pg), sbg,m2(pb+pg), - taZ1,m2(pa-pl-plbar-p1) + taV1,m2(pa-pl-plbar-p1) ); id m2inv(q1?) = 1/m2(q1); -multiply replace_(pZ,pl+plbar); +multiply replace_(pV,pl+plbar); .sort format O4; format c; #call WriteHeader(`OUTPUT') #call WriteOptimised(`OUTPUT',U1,4,p1,p2,pa,pb,pg,pl,plbar) #call WriteOptimised(`OUTPUT',U2,4,p1,p2,pa,pb,pg,pl,plbar) #call WriteOptimised(`OUTPUT',L,4,p1,p2,pa,pb,pg,pl,plbar) #call WriteFooter(`OUTPUT') .end diff --git a/current_generator/jW_j.frm b/current_generator/jW_j.frm deleted file mode 100644 index 7004769..0000000 --- a/current_generator/jW_j.frm +++ /dev/null @@ -1,25 +0,0 @@ -*/** -* \brief Contraction of W current with FKL current -* -* \authors The HEJ collaboration (see AUTHORS for details) -* \date 2020 -* \copyright GPLv2 or later -*/ -#include- include/helspin.frm -#include- include/write.frm - -v pb,p2,pa,p1,pl,plbar; -i mu; - -#do h2={+,-} - l [jW_j `h2'] = JW(-1, mu, pa, p1, plbar, pl)*Current(`h2'1, p2, mu, pb); -#enddo - -#call ContractCurrents -.sort -format O4; -format c; -#call WriteHeader(`OUTPUT') -#call WriteOptimised(`OUTPUT',jW_j,1,pa,p1,pb,p2,pl,plbar) -#call WriteFooter(`OUTPUT') -.end diff --git a/current_generator/jW_jqqbar.frm b/current_generator/jW_jqqbar.frm index dafe5d4..7f6a9a3 100644 --- a/current_generator/jW_jqqbar.frm +++ b/current_generator/jW_jqqbar.frm @@ -1,48 +1,48 @@ */** * \brief Contraction of W current with extremal qqbar emission current * * TODO: transcribe formulas to developer manual * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm v pb,p2,p3,pa,p1,pl,plbar,pr,p; i mu,nu,sigma,alpha; s h; * Equation 3.23 in James Cockburn's Thesis. l qggm1 = Current(-1, p2, mu, p3-pb, nu, p3); l qggm2 = -Current(-1, p2, nu, p2-pb, mu, p3); l qggm3 = -2*i_/m2(p2+p3)*( (p2(nu) + p3(nu))*d_(mu, sigma) - pb(mu)*d_(nu, sigma) + pb(sigma)*d_(mu, nu) )*Current(-1, p2, sigma, p3); .sort drop; #do i=1,3 #do h1={+,-} #do hg={+,-} - l [jW_qggm`i' `h1'`hg'] = qggm`i'*Eps(`hg'1, nu, pa, pb)*JW( - `h1'1, mu, pa, p1, plbar, pl + l [jW_qggm`i' `h1'`hg'] = qggm`i'*Eps(`hg'1, nu, pa, pb)*JV( + `h1'1, -1, mu, pa, p1, plbar, pl ); #enddo #enddo #enddo id Current(h?, p1?, mu?, p?, nu?, p2?) = Current(h, p1, mu, p, nu, p2)/m2(p); #call ContractCurrents .sort format O4; format c; #call WriteHeader(`OUTPUT') #call WriteOptimised(`OUTPUT',jW_qggm1,2,pb,p2,p3,pa,p1,pl,plbar) #call WriteOptimised(`OUTPUT',jW_qggm2,2,pb,p2,p3,pa,p1,pl,plbar) #call WriteOptimised(`OUTPUT',jW_qggm3,2,pb,p2,p3,pa,p1,pl,plbar) #call WriteFooter(`OUTPUT') .end diff --git a/current_generator/jW_juno.frm b/current_generator/jW_juno.frm deleted file mode 100644 index 0aa4f40..0000000 --- a/current_generator/jW_juno.frm +++ /dev/null @@ -1,66 +0,0 @@ -*/** -* \brief Contraction of W current with unordered current -* -* \authors The HEJ collaboration (see AUTHORS for details) -* \date 2020 -* \copyright GPLv2 or later -*/ -#include- include/helspin.frm -#include- include/write.frm - -v pb,p2,pa,p1,pg,pl,plbar,pW,q2,q3,p,pr; -i mu,nu,sigma; -s h; - -#do h2={+,-} -* eq:juno in developer manual with pa -> pb, p1 -> pg - l [U1_jW `h2'] = ( - + Current(`h2'1, p2, nu, pg)*Current(`h2'1, pg, mu, pb) - + 2*p2(nu)*Current(`h2'1, p2, mu, pb) - )/m2(p2 + pg); - l [U2_jW `h2'] = ( - + 2*Current(`h2'1, p2, mu, pb)*pb(nu) - - Current(`h2'1, p2, mu, pg)*Current(`h2'1, pg, nu, pb) - )/m2(pb - pg); - l [L_jW `h2'] = ( - - (q2(nu) + q3(nu))*Current(`h2'1, p2, mu, pb) - - 2*pg(mu)*Current(`h2'1, p2, nu, pb) - + 2*Current(`h2'1, p2, pg, pb)*d_(mu, nu) - + m2(q2)*Current(`h2'1, p2, mu, pb)*(p1(nu)/m2(p1+pg) + pa(nu)/m2(pa+pg)) - )/m2(q3); -#enddo -.sort -drop; - -* multiply with polarisation vector and other currents -#do h2={+,-} - #do hg={+,-} - #do TENSOR={U1_jW,U2_jW,L_jW} - l [`TENSOR' `h2'`hg'] = ( - [`TENSOR' `h2'] - *JW(-1, mu, pa, p1, plbar, pl) - *Eps(`hg'1, nu, pr) - ); - #enddo - #enddo -#enddo - -* choice of auxiliary vector -id Eps(h?, mu?, pr)*Current(h?, ?a) = Eps(h, mu, pg, p2)*Current(h, ?a); -also Eps(h?, mu?, pr) = Eps(h, mu, pg, pb); - -multiply replace_( - q2,p2+pg-pb, - q3,p2-pb -); -.sort -#call ContractCurrents -.sort -format O4; -format c; -#call WriteHeader(`OUTPUT') -#call WriteOptimised(`OUTPUT',U1_jW,2,pa,p1,pb,p2,pg,pl,plbar) -#call WriteOptimised(`OUTPUT',U2_jW,2,pa,p1,pb,p2,pg,pl,plbar) -#call WriteOptimised(`OUTPUT',L_jW,2,pa,p1,pb,p2,pg,pl,plbar) -#call WriteFooter(`OUTPUT') -.end diff --git a/current_generator/jW_qqbar_j.frm b/current_generator/jW_qqbar_j.frm index 665c4de..77cc3bc 100644 --- a/current_generator/jW_qqbar_j.frm +++ b/current_generator/jW_qqbar_j.frm @@ -1,73 +1,73 @@ */** * \brief Contraction of W current with central qqbar emission and FKL current * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm cf Xsym,V3g; i mu,nu,sigma; s h,sqqbar,saq,saqbar,s1q,s1qbar,sbq,sbqbar,s4q,s4qbar; v p,pa,p1,pb,pn,pq,pqbar,pl,plbar,q1,q3,q11,q12; * cf. eq:Xcen in developer manual #do h1={+,-} #do h2={+,-} * eq:Xcrossed in developer manual, normalisation added later on l [M_cross `h1'`h2'] = -Current(`h1'1, p1, mu, pa)*Current( `h2'1, pq, nu, q1 - pqbar, mu, pqbar ); * eq:Xqprop in developer manual, normalisation added later on l [M_qbar `h1'`h2'] = Current(`h1'1, p1, mu, pa)*Current( `h2'1, pq, mu, q1 - pq, nu, pqbar ); l [M_sym `h1'`h2'] = -i_*Current(`h1'1, p1, mu, pa)*( d_(mu,nu)*Xsym(sigma) + V3g(mu, nu, sigma) )/sqqbar*Current(`h2'1, pq, sigma, pqbar); #enddo #enddo -multiply JW(-1, nu, pb, pn, plbar, pl); +multiply JV(-1, -1, nu, pb, pn, plbar, pl); id Current(h?, pq?, mu?, p?, nu?, pqbar?) = Current(h, pq, mu, p, nu, pqbar)/m2(p); * eq:Xsym in developer manual id Xsym(sigma?) = m2(q1)*( pa(sigma)/(saq + saqbar) + p1(sigma)/(s1q + s1qbar) ) - m2(q3)*( pb(sigma)/(sbq + sbqbar) + pn(sigma)/(s4q + s4qbar) ); * eq:V3g in developer manual id V3g(mu?, nu?, sigma?) = ( (q1(nu) + pq(nu) + pqbar(nu))*d_(mu, sigma) + (q3(mu) - pq(mu) - pqbar(mu))*d_(nu, sigma) - (q1(sigma) + q3(sigma))*d_(mu, nu) ); multiply replace_(q3, q1-pq-pqbar); * replace q1 by sum of lightlike momenta multiply replace_(q1, q11-q12); .sort #call ContractCurrents() multiply replace_( sqqbar, m2(pq+pqbar), saq, m2(pa+pq), saqbar, m2(pa+pqbar), s1q, m2(p1+pq), s1qbar, m2(p1+pqbar), sbq, m2(pb+pq), sbqbar, m2(pb+pqbar), s4q, m2(pn+pq), s4qbar, m2(pn+pqbar) ); .sort format O4; format c; #call WriteHeader(`OUTPUT') #call WriteOptimised(`OUTPUT',M_qbar,2,pa,p1,pb,pn,pq,pqbar,pl,plbar,q11,q12) #call WriteOptimised(`OUTPUT',M_cross,2,pa,p1,pb,pn,pq,pqbar,pl,plbar,q11,q12) #call WriteOptimised(`OUTPUT',M_sym,2,pa,p1,pb,pn,pq,pqbar,pl,plbar,q11,q12) #call WriteFooter(`OUTPUT') .end diff --git a/current_generator/jWuno_j.frm b/current_generator/jWuno_j.frm deleted file mode 100644 index 13e4198..0000000 --- a/current_generator/jWuno_j.frm +++ /dev/null @@ -1,101 +0,0 @@ -*/** -* \brief Contraction of W unordered current with FKL current -* -* TODO: unify conventions with developer manual -* the current dictionary is as follows: -* -* code | manual -* pg | p_1 -* p1 | p_2 -* pa | p_a -* -* \authors The HEJ collaboration (see AUTHORS for details) -* \date 2019-2020 -* \copyright GPLv2 or later -*/ -#include- include/helspin.frm -#include- include/write.frm - -s h,s2g,sbg,taW1; -v p,p1,p2,pa,pb,pg,pl,plbar,pW,pr,q1,q1g,q2; -i mu,nu,rho,sigma; -cf m2inv; - -#do h1={+,-} -* eq:U1tensor in developer manual, up to factors 1/sij, 1/tij - l [U1 `h1'] = ( - + Current(`h1'1, p1, nu, p1+pg, mu, pa-pW, rho, pa) - + Current(`h1'1, p1, nu, p1+pg, rho, p1+pg+pW, mu, pa) - + Current(`h1'1, p1, rho, p1+pW, nu, p1+pg+pW, mu, pa) - ); - -* eq:U2tensor in developer manual, up to factors 1/sij, 1/tij - l [U2 `h1'] = ( - + Current(`h1'1, p1, mu, pa - pW - pg, nu, pa - pW, rho, pa) - + Current(`h1'1, p1, mu, pa - pW - pg, rho, pa - pg, nu, pa) - + Current(`h1'1, p1, rho, p1 + pW, mu, pa - pg, nu, pa) - ); - -* eq:Ltensor in developer manual, up to factors 1/sij, 1/tij - l [L `h1'] = ( - Current(`h1'1, p1, sigma, pa - pW, rho, pa) + - Current(`h1'1, p1, rho, p1 + pW, sigma, pa) - )*( - ((pb(nu)/sbg + p2(nu)/s2g)*m2(q1g) + 2*q1(nu) - pg(nu))*d_(mu, sigma) - - 2*pg(mu)*d_(nu, sigma) - + (2*pg(sigma) - q1(sigma))*d_(mu, nu) - )/taW1; -#enddo -.sort -* restore kinematic factors -id Current(h?, p1, mu?, q1?, nu?, q2?, rho?, pa) = ( - Current(h, p1, mu, q1, nu, q2, rho, pa)*m2inv(q1)*m2inv(q2) -); -id Current(h?, p1, mu?, q1?, nu?, pa) = ( - Current(h, p1, mu, q1, nu, pa)*m2inv(q1) -); -.sort -drop; - -* multiply with polarisation vector and other currents -#do h1={+,-} - #do h2={+,-} - #do hg={+,-} - #do TENSOR={U1,U2,L} - l [`TENSOR' `h1'`h2'`hg'] = ( - [`TENSOR' `h1'] - *Eps(`hg'1, nu) - *Current(`h2'1, p2, mu, pb) - *Current(-1, pl, rho, plbar) - ); - #enddo - #enddo - #enddo -#enddo - -* choice of best reference vector (p2 or pb) -id Eps(h?, nu?)*Current(h?, p2, mu?, pb) = Eps(h, nu, pg, p2)*Current(h, p2, mu, pb); -also Eps(h?, mu?) = Eps(h, mu, pg, pb); - -multiply replace_(q1g,q1-pg); -multiply replace_(q1,pa-p1-pW); -multiply replace_(pW,pl+plbar); - -.sort -#call ContractCurrents -multiply replace_( - s2g,m2(p2+pg), - sbg,m2(pb+pg), - taW1,m2(pa-pl-plbar-p1) -); -id m2inv(q1?) = 1/m2(q1); -multiply replace_(pW,pl+plbar); -.sort -format O4; -format c; -#call WriteHeader(`OUTPUT') -#call WriteOptimised(`OUTPUT',U1,3,p1,p2,pa,pb,pg,pl,plbar) -#call WriteOptimised(`OUTPUT',U2,3,p1,p2,pa,pb,pg,pl,plbar) -#call WriteOptimised(`OUTPUT',L,3,p1,p2,pa,pb,pg,pl,plbar) -#call WriteFooter(`OUTPUT') -.end diff --git a/current_generator/j_Wqqbar_j.frm b/current_generator/j_Wqqbar_j.frm index afe808c..e91fa3b 100644 --- a/current_generator/j_Wqqbar_j.frm +++ b/current_generator/j_Wqqbar_j.frm @@ -1,116 +1,116 @@ */** * \brief Contraction of FKL current with central qqbar+W emission and FKL current * * TODO: unify conventions with developer manual * the current dictionary is as follows: * * code | manual * pq | p_2 * pqbar | p_3 * pl | p_A * plbar | p_B * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include- include/helspin.frm #include- include/write.frm i mu,nu,rho,sigma; cf T1a, T4b, Xunc, Xcro, X1a, X4b, V3g, jv; v p1,pa,p4,pb,pq,pqbar,pl,plbar,q1,q3,q11,q12; s h; s tunc1,tunc2,tcro1,tcro2,s2AB,s3AB,s23AB; s t1,t3,s12,s13,s1A,s1B,sa2,sa3,saA,saB,s42,s43,s4A,s4B,sb2,sb3,sbA,sbB; #do h1={+,-} #do h2={+,-} l [M_uncross_W `h1'`h2'] = T1a(`h1'1, mu)*T4b(`h2'1, nu)*Xunc(mu, nu); l [M_cross_W `h1'`h2'] = T1a(`h1'1, mu)*T4b(`h2'1, nu)*Xcro(mu, nu); l [M_sym_W `h1'`h2'] = T1a(`h1'1, mu)*T4b(`h2'1, nu)*( X1a(mu, nu, sigma) - X4b(mu, nu, sigma) + V3g(mu, nu, sigma) )/s23AB * jv(sigma); #enddo #enddo id T1a(h?, mu?) = Current(h, p1, mu, pa); id T4b(h?, mu?) = Current(h, p4, mu, pb); * see eq:X_Unc in developer manual, multiplied by -t1*t3 id Xunc(mu, nu) = -Current(-1, pl, sigma, plbar)*( - Current(-1, pq, sigma, pq + pl + plbar, mu, q3 + pqbar, nu, pqbar)/s2AB/tunc2 + Current(-1, pq, mu, q1 - pq, sigma, q3 + pqbar, nu, pqbar)/tunc1/tunc2 + Current(-1, pq, mu, q1 - pq, nu, pqbar + pl + plbar, sigma, pqbar)/tunc1/s3AB ); * see eq:X_Cro in developer manual, multiplied by -t1*t3 id Xcro(mu, nu) = -Current(-1, pl, sigma, plbar)*( - Current(-1, pq, nu, q3 + pq, mu, pqbar + pl + plbar, sigma, pqbar)/tcro1/s3AB + Current(-1, pq, nu, q3 + pq, sigma, q1 - pqbar, mu, pqbar)/tcro1/tcro2 + Current(-1, pq, sigma, pq + pl + plbar, nu, q1 - pqbar, mu, pqbar)/s2AB/tcro2 ); * see eq:X_1a in developer manual, multiplied by -t1*s23AB*t3 id X1a(mu, nu, sigma) = t1*d_(mu, nu)*( + p1(sigma)/(s12 + s13 + s1A + s1B) + pa(sigma)/(sa2 + sa3 + saA + saB) ); * see eq:X_4b in developer manual, multiplied by t1*s23AB*t3 id X4b(mu, nu, sigma) = t3*d_(mu, nu)*( + p4(sigma)/(s42 + s43 + s4A + s4B) + pb(sigma)/(sb2 + sb3 + sbA + sbB) ); * see eq:3GluonWEmit in developer manual, multiplied by t1*s23AB*t3 id V3g(mu, nu, sigma) = ( + (q1(nu) + pq(nu) + pqbar(nu) + pl(nu) + plbar(nu))*d_(mu, sigma) + (q3(mu) - pq(mu) - pqbar(mu) - pl(mu) - plbar(mu))*d_(nu, sigma) - (q1(sigma) + q3(sigma))*d_(mu, nu) ); * see eq:J_V in developer manual -* this is actually JW (eq:Weffcur1), but we can't use the built-in +* this is actually JV (eq:Weffcur1), but we can't use the built-in * because it cannot deal with two outgoing momenta, i.e. negative-energy spinors id jv(sigma?) = -i_*( + Current(-1, pq, rho, pq + pl + plbar, sigma, pqbar)/s2AB - Current(-1, pq, sigma, pqbar + pl + plbar, rho, pqbar)/s3AB )*Current(-1, pl, rho, plbar); multiply replace_( s2AB, m2(pl+plbar+pq), s3AB, m2(pl+plbar+pqbar), s23AB, m2(pl+plbar+pq+pqbar), tunc1, m2(q1-pq), tunc2, m2(q3+pqbar), tcro1, m2(q3+pq), tcro2, m2(q1-pqbar), t1, m2(q1), t3, m2(q3), s12, m2(p1+pq), s13, m2(p1+pqbar), s1A, m2(p1+pl), s1B, m2(p1+plbar), sa2, m2(pa+pq), sa3, m2(pa+pqbar), saA, m2(pa+pl), saB, m2(pa+plbar), s42, m2(p4+pq), s43, m2(p4+pqbar), s4A, m2(p4+pl), s4B, m2(p4+plbar), sb2, m2(pb+pq), sb3, m2(pb+pqbar), sbA, m2(pb+pl), sbB, m2(pb+plbar) ); multiply replace_(q3, q1-pl-plbar-pq-pqbar); * replace q1 by sum of lightlike momenta * @TODO: choose q11 as collinear to some other vector multiply replace_(q1, q11-q12); .sort #call ContractCurrents() .sort format O4; format c; #call WriteHeader(`OUTPUT') #call WriteOptimised(`OUTPUT',M_uncross_W,2,pa,p1,pb,p4,pq,pqbar,pl,plbar,q11,q12) #call WriteOptimised(`OUTPUT',M_cross_W,2,pa,p1,pb,p4,pq,pqbar,pl,plbar,q11,q12) #call WriteOptimised(`OUTPUT',M_sym_W,2,pa,p1,pb,p4,pq,pqbar,pl,plbar,q11,q12) #call WriteFooter(`OUTPUT') .end diff --git a/doc/developer_manual/Makefile b/doc/developer_manual/Makefile index 585fb71..1a22481 100644 --- a/doc/developer_manual/Makefile +++ b/doc/developer_manual/Makefile @@ -1,22 +1,25 @@ SHELL=/bin/bash SRCFILES=$(wildcard src/*.asy) FIGURES=$(patsubst src/%.asy,build/figures/%.pdf,$(SRCFILES)) .SECONDARY: .SUFFIXES: developer_manual.pdf: developer_manual.tex currents.tex $(FIGURES) latexmk -pdf -pdflatex='pdflatex -synctex=1' $< build/figures/%.pdf: src/%.asy mkdir -p $(@D) - asy -nosafe -f pdf -o $@ $< +# workaround: depending on the ghostscript version the `-nosafe` +# flag can be either required or forbidden (e.g. 9.50) for +# building figures with opacity + asy -f pdf -o $@ $< || asy -nosafe -f pdf -o $@ $< .PHONY: clean clean: rm -f developer_manual.{pdf,aux,dvi,fdb_latexmk,fls,nav,out,snm,synctex.gz,toc,vrb} rm -f *.log rm -fr build diff --git a/doc/developer_manual/currents.tex b/doc/developer_manual/currents.tex index a6796aa..a9768c3 100644 --- a/doc/developer_manual/currents.tex +++ b/doc/developer_manual/currents.tex @@ -1,692 +1,687 @@ \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}, 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} and -\texttt{include/HEJ/jets.hh} respectively. +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 for depends on the helicity and direction (forward/backwards) for the quarks. Currently all different contractions of incoming and outgoing states are defined in \lstinline!joi!, \lstinline!jio! and \lstinline!joo!. \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}} 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}} 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) = \frac{j_\mu^\pm(p_1, p_a)}{\sqrt{2} \bar{u}^\pm(p_a)u^\mp(p_1)}\,, \quad \epsilon_\mu^{\pm,*}(p_1) = -\frac{j_\mu^\pm(p_1, p_a)}{\sqrt{2} \bar{u}^\mp(p_1)u^\pm(p_a)}\,. \end{equation} 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} We also employ the usual short-hand notation \begin{equation} \label{eq:spinor_helicity} \spa i.j = \bar{u}^-(p_i)u^+(p_j)\,,\qquad \spb i.j = \bar{u}^+(p_i)u^-(p_j)\,, \qquad[ i | H | j\rangle = j_\mu^+(p_i, p_j)p_H^\mu\,. \end{equation} 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$. Eq.~\eqref{eq:jH_same_helicity} is implemented by \lstinline!g_gH_HC! in \texttt{src/Hjets.cc} \footnote{\lstinline!g_gH_HC! and \lstinline!g_gH_HNC! includes an additional $1/t_2$ factor, which should be in the Matrix element instead.}. 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 by \lstinline!g_gH_HNC! again in \texttt{src/Hjets.cc}. 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} (see variables \lstinline!ang1a! and \lstinline!sqa1! in the implementation). The form factors $H_1,H_2,H_4,H_5, H_{10}, H_{12}$ are given in~\cite{DelDuca:2003ba}, and are implemented under their name 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{\texorpdfstring{$W$}{W}+Jets} +\subsection{Vector Boson + Jets} \label{sec:currents_W} -\subsubsection{Quark+\texorpdfstring{$W$}{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_W$ current is constructed from the two diagrams which - contribute to the emission of a $W$-boson from a given quark line.} - \label{fig:jW} + \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$ emission we require a fermion. The $j_W$ current is actually a sum of -two separate contributions, see figure~\ref{fig:jW}, one with a $W$-emission -from the initial state, and one with the $W$-emission from the final state. -Mathematically this can be seen as the following two -terms~\cite{Andersen:2012gk}\todo{cite W subleading paper}: +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 FKL $W$ current is -\begin{align} - \label{eq:jW-} - j^-_{W}(p_a, p_1, p_{\bar{l}}, p_{l}) ={}& - \frac{2 \spa 1.l}{(p_1+p_W)^2}\*\Big( - \spb 1.{\bar{l}} j^-(p_1, p_a) + \spb l.{\bar{l}} - j^-(p_{l}, p_a)\Big)\notag\\ - & + \frac{2\spb a.{\bar{l}}}{(p_a-p_W)^2}\Big( - \spa l.{\bar{l}} j^-(p_1, p_{\bar{l}}) + \spa a.l - j^-(p_1, p_a) - \Big)\,,\\ - \label{eq:jW+} - j^+_{W}(p_a, p_1, p_{\bar{l}}, p_{l}) =& \big[j^-_{W}(p_a, p_1, p_l, p_{\bar{l}})\big]^*\,, -\end{align} -where the negative-helicity current is used for emission off a quark -line and the positive-helicity current for emissions off antiquark. The +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!JW(h, mu, pa, p1, plbar, pl)!. +\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{\texorpdfstring{$W$}{W}+uno} +\subsubsection{Vector boson with unordered emission} \begin{figure} \centering \begin{subfigure}{0.45\textwidth} \centering - \includegraphics{wuno1} + \includegraphics{vuno1} \caption{} \label{fig:U1diags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering -\includegraphics{wuno2} +\includegraphics{vuno2} \caption{} \label{fig:U2diags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering - \includegraphics{wuno3} + \includegraphics{vuno3} \caption{} \label{fig:Cdiags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering -\includegraphics{wuno4} +\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 $W$ + 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 $W$ boson are emitted from + $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:Wunodiags} + \label{fig:Vunodiags} \end{figure} -It is necessary to include subleading processes in $W$+Jets also.Similarly to -the pure jet case, the uno currents are not calculated separately, and only in -the ME functions when required in the \texttt{src/Wjets.cc} file. For unordered -emissions a new current is required, $j_{W,{\rm uno}}$, it is only non-zero for -$h_a=h_1=-$ and hence we have suppressed its helicity indices. It is derived +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. For unordered +emissions a new current is required, $j_{V,{\rm uno}}$. It is derived from the 12 leading-order Feynman diagrams in the QMRK limit (see -figure~\ref{fig:Wunodiags}). Using $T^m_{ij}$ represent fundamental colour +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 W,uno}(p_a,p_1,p_2,p_\ell,p_{\bar{\ell}}) =& \ i \varepsilon_{\nu}(p_1)\ + 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}_W)\rho P_L |a\rangle }{s_{12}t_{aW}} + \frac{\langle 2|\nu (\slashed{p}_2+ \slashed{p}_1)\rho P_L (\slashed{p}_2+\slashed{p}_1 + \slashed{p}_W)\mu |a\rangle }{s_{12}s_{12W}} \\ - &+ \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_W) \nu - (\slashed{p}_1 + \slashed{p}_2+\slashed{p}_W)\mu |a\rangle}{s_{2W}s_{12W}}\,, + \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}_W-\slashed{p}_1)\nu (\slashed{p}_a - \slashed{p}_W)\rho P_L |a\rangle }{t_{aW1}t_{aW}} + \frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_W- \slashed{p}_1)\rho P_L (\slashed{p}_a-\slashed{p}_1) \nu |a\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 |a\rangle}{s_{2W}t_{a1}}\,, + \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_{aW2}}\left[ - \frac{\langle 2 | \sigma (\slashed{p}_a-\slashed{p}_W)\rho|a\rangle}{t_{aW}} - +\frac{\langle 2 | \rho (\slashed{p}_2+\slashed{p}_W)\sigma|a\rangle}{s_{2W}} + \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_W$. +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$. \subsubsection{\texorpdfstring{$W$}{W}+Extremal \texorpdfstring{$\mathbf{q\bar{q}}$}{qqx}} +\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} 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 \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 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}}=& - \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) + - 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 -\todo{Different convention for $J_V$} -\begin{equation} - \label{eq:J_V} - J_V^\mu(p_2,p_l,p_{\bar{l}},p_3)=-i\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} +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/developer_manual/developer_manual.tex b/doc/developer_manual/developer_manual.tex index 1fb614b..968593c 100644 --- a/doc/developer_manual/developer_manual.tex +++ b/doc/developer_manual/developer_manual.tex @@ -1,1981 +1,2004 @@ \documentclass[a4paper,11pt]{article} \usepackage{fourier} \usepackage[T1]{fontenc} \usepackage{microtype} \usepackage{geometry} \usepackage{enumitem} \setlist[description]{leftmargin=\parindent,labelindent=\parindent} \usepackage{amsmath} \usepackage{amssymb} \usepackage[utf8x]{inputenc} \usepackage{graphicx} \usepackage{xcolor} \usepackage{todonotes} \usepackage{listings} \usepackage{xspace} \usepackage{tikz} \usepackage{slashed} \usepackage{subcaption} \usetikzlibrary{arrows.meta} \usetikzlibrary{shapes} \usetikzlibrary{calc} \usepackage[colorlinks,linkcolor={blue!50!black}]{hyperref} \graphicspath{{build/figures/}{figures/}} \usepackage[left]{showlabels} \renewcommand{\showlabelfont}{\footnotesize\color{darkgreen}} \emergencystretch \hsize \setlength{\marginparwidth}{2cm} \newcommand{\HEJ}{{\tt HEJ}\xspace} \newcommand{\HIGHEJ}{\emph{High Energy Jets}\xspace} \newcommand{\cmake}{\href{https://cmake.org/}{cmake}\xspace} \newcommand{\html}{\href{https://www.w3.org/html/}{html}\xspace} \newcommand{\YAML}{\href{http://yaml.org/}{YAML}\xspace} \newcommand{\QCDloop}{\href{https://github.com/scarrazza/qcdloop}{QCDloop}\xspace} \newcommand{\FORM}{{\tt FORM}\xspace} \newcommand\matel[4][]{\mathinner{\langle#2\vert#3\vert#4\rangle}_{#1}} \newcommand{\as}{\alpha_s} \DeclareRobustCommand{\mathgraphics}[1]{\vcenter{\hbox{\includegraphics{#1}}}} \def\spa#1.#2{\left\langle#1\,#2\right\rangle} \def\spb#1.#2{\left[#1\,#2\right]} \def\spaa#1.#2.#3{\langle\mskip-1mu{#1} | #2 | {#3}\mskip-1mu\rangle} \def\spbb#1.#2.#3{[\mskip-1mu{#1} | #2 | {#3}\mskip-1mu]} \def\spab#1.#2.#3{\langle\mskip-1mu{#1} | #2 | {#3}\mskip-1mu\rangle} \def\spba#1.#2.#3{\langle\mskip-1mu{#1}^+ | #2 | {#3}^+\mskip-1mu\rangle} \def\spav#1.#2.#3{\|\mskip-1mu{#1} | #2 | {#3}\mskip-1mu\|^2} \def\jc#1.#2.#3{j^{#1}_{#2#3}} % expectation value \newcommand{\ev}[1]{\text{E}\left[#1\right]} \definecolor{darkgreen}{rgb}{0,0.4,0} \lstset{ % backgroundcolor=\color{lightgray}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor} basicstyle=\footnotesize\usefont{T1}{DejaVuSansMono-TLF}{m}{n}, % the size of the fonts that are used for the code breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace breaklines=false, % sets automatic line breaking captionpos=t, % sets the caption-position to bottom commentstyle=\color{red}, % comment style deletekeywords={...}, % if you want to delete keywords from the given language escapeinside={\%*}{*)}, % if you want to add LaTeX within your code extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 frame=false, % adds a frame around the code keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) keywordstyle=\color{blue}, % keyword style otherkeywords={}, % if you want to add more keywords to the set numbers=none, % where to put the line-numbers; possible values are (none, left, right) numbersep=5pt, % how far the line-numbers are from the code rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' showstringspaces=false, % underline spaces within strings only showtabs=false, % show tabs within strings adding particular underscores stepnumber=2, % the step between two line-numbers. If it's 1, each line will be numbered stringstyle=\color{gray}, % string literal style tabsize=2, % sets default tabsize to 2 spaces title=\lstname, emph={}, emphstyle=\color{darkgreen} } \begin{document} \tikzstyle{mynode}=[rectangle split,rectangle split parts=2, draw,rectangle split part fill={lightgray, none}] \title{\HEJ 2 developer manual} \author{} \maketitle \tableofcontents \newpage \section{Overview} \label{sec:overview} \HEJ 2 is a C++ program and library implementing an algorithm to apply \HIGHEJ resummation~\cite{Andersen:2008ue,Andersen:2008gc} to pre-generated fixed-order events. This document is intended to give an overview over the concepts and structure of this implementation. \subsection{Project structure} \label{sec:project} \HEJ 2 is developed under the \href{https://git-scm.com/}{git} version control system. The main repository is on the IPPP \href{https://gitlab.com/}{gitlab} server under \url{https://gitlab.dur.scotgrid.ac.uk/hej/hej}. To get a local copy, get an account on the gitlab server and use \begin{lstlisting}[language=sh,caption={}] git clone git@gitlab.dur.scotgrid.ac.uk:hej/hej.git \end{lstlisting} This should create a directory \texttt{hej} with the following contents: \begin{description} \item[README.md:] Basic information concerning \HEJ. \item[doc:] Contains additional documentation, see section~\ref{sec:doc}. \item[include:] Contains the C++ header files. \item[src:] Contains the C++ source files. \item[examples:] Example analyses and scale setting code. \item[t:] Contains the source code for the automated tests. \item[CMakeLists.txt:] Configuration file for the \cmake build system. See section~\ref{sec:cmake}. \item[cmake:] Auxiliary files for \cmake. This includes modules for finding installed software in \texttt{cmake/Modules} and templates for code generation during the build process in \texttt{cmake/Templates}. \item[config.yml:] Sample configuration file for running \HEJ 2. \item[current\_generator:] Contains the code for the current generator, see section~\ref{sec:cur_gen}. \item[FixedOrderGen:] Contains the code for the fixed-order generator, see section~\ref{sec:HEJFOG}. \item[COPYING:] License file. \item[AUTHORS:] List of \HEJ authors. \item[Changes-API.md:] Change history for the \HEJ API (application programming interface). \item[Changes.md:] History of changes relevant for \HEJ users. \end{description} In the following all paths are given relative to the \texttt{hej} directory. \subsection{Documentation} \label{sec:doc} The \texttt{doc} directory contains user documentation in \texttt{doc/sphinx} and the configuration to generate source code documentation in \texttt{doc/doxygen}. The user documentation explains how to install and run \HEJ 2. The format is \href{http://docutils.sourceforge.net/rst.html}{reStructuredText}, which is mostly human-readable. Other formats, like \html, can be generated with the \href{http://www.sphinx-doc.org/en/master/}{sphinx} generator with \begin{lstlisting}[language=sh,caption={}] make sphinx \end{lstlisting} To document the source code we use \href{https://www.stack.nl/~dimitri/doxygen/}{doxygen}. To generate \html documentation, use the command \begin{lstlisting}[language=sh,caption={}] make doxygen \end{lstlisting} in your \texttt{build/} directory. \subsection{Build system} \label{sec:cmake} For the most part, \HEJ 2 is a library providing classes and functions that can be used to add resummation to fixed-order events. In addition, there is a relatively small executable program leveraging this library to read in events from an input file and produce resummation events. Both the library and the program are built and installed with the help of \cmake. Debug information can be turned on by using \begin{lstlisting}[language=sh,caption={}] cmake base/directory -DCMAKE_BUILD_TYPE=Debug make install \end{lstlisting} This facilitates the use of debuggers like \href{https://www.gnu.org/software/gdb/}{gdb}. The main \cmake configuration file is \texttt{CMakeLists.txt}. It defines the compiler flags, software prerequisites, header and source files used to build \HEJ 2, and the automated tests. \texttt{cmake/Modules} contains module files that help with the detection of the software prerequisites and \texttt{cmake/Templates} template files for the automatic generation of header and source files. For example, this allows to only keep the version information in one central location (\texttt{CMakeLists.txt}) and automatically generate a header file from the template \texttt{Version.hh.in} to propagate this to the C++ code. \subsection{General coding guidelines} \label{sec:notes} The goal is to make the \HEJ 2 code well-structured and readable. Here are a number of guidelines to this end. \begin{description} \item[Observe the boy scout rule.] Always leave the code cleaner than how you found it. Ugly hacks can be useful for testing, but shouldn't make their way into the main branch. \item[Ask if something is unclear.] Often there is a good reason why code is written the way it is. Sometimes that reason is only obvious to the original author (use \lstinline!git blame! to find them), in which case they should be poked to add a comment. Sometimes there is no good reason, but nobody has had the time to come up with something better, yet. In some places the code might just be bad. \item[Don't break tests.] There are a number of tests in the \texttt{t} directory, which can be run with \lstinline!make test!. Ideally, all tests should run successfully in each git revision. If your latest commit broke a test and you haven't pushed to the central repository yet, you can fix it with \lstinline!git commit --amend!. If an earlier local commit broke a test, you can use \lstinline!git rebase -i! if you feel confident. Additionally each \lstinline!git push! is also automatically tested via the GitLab CI (see appendix~\ref{sec:CI}). \item[Test your new code.] When you add some new functionality, also add an automated test. This can be useful even if you don't know the ``correct'' result because it prevents the code from changing its behaviour silently in the future. \href{http://www.valgrind.org/}{valgrind} is a very useful tool to detect potential memory leaks. The code coverage of all tests can be generated with \href{https://gcovr.com/en/stable/}{gcovr}. Therefore add the flag \lstinline!-DTEST_COVERAGE=True! to cmake and run \lstinline!make ctest_coverage!. \item[Stick to the coding style.] It is somewhat easier to read code that has a uniform coding and indentation style. We don't have a strict style, but it helps if your code looks similar to what is already there. \end{description} \section{Program flow} \label{sec:flow} A run of the \HEJ 2 program has three stages: initialisation, event processing, and cleanup. The following sections outline these stages and their relations to the various classes and functions in the code. Unless denoted otherwise, all classes and functions are part of the \lstinline!HEJ! namespace. The code for the \HEJ 2 program is in \texttt{src/bin/HEJ.cc}, all other code comprises the \HEJ 2 library. Classes and free functions are usually implemented in header and source files with a corresponding name, i.e. the code for \lstinline!MyClass! can usually be found in \texttt{include/HEJ/MyClass.hh} and \texttt{src/MyClass.cc}. \subsection{Initialisation} \label{sec:init} The first step is to load and parse the \YAML configuration file. The entry point for this is the \lstinline!load_config! function and the related code can be found in \texttt{include/HEJ/YAMLreader.hh}, \texttt{include/HEJ/config.hh} and the corresponding \texttt{.cc} files in the \texttt{src} directory. The implementation is based on the \href{https://github.com/jbeder/yaml-cpp}{yaml-cpp} library. The \lstinline!load_config! function returns a \lstinline!Config! object containing all settings. To detect potential mistakes as early as possible, we throw an exception whenever one of the following errors occurs: \begin{itemize} \item There is an unknown option in the \YAML file. \item A setting is invalid, for example a string is given where a number would be expected. \item An option value is not set. \end{itemize} The third rule is sometimes relaxed for ``advanced'' settings with an obvious default, like for importing custom scales or analyses. The information stored in the \lstinline!Config! object is then used to initialise various objects required for the event processing stage described in section~\ref{sec:processing}. First, the \lstinline!get_analyses! function creates a number objects that inherits from the \lstinline!Analysis! interface.\footnote{In the context of C++ the proper technical expression is ``pure abstract class''.} Using an interface allows us to decide the concrete type of the analysis at run time instead of having to make a compile-time decision. Depending on the settings, \lstinline!get_analyses! creates a number of user-defined analyses loaded from one or more external libraries and \textsc{rivet} analyses (see the user documentation \url{https://hej.web.cern.ch/HEJ/doc/current/user/}) Together with a number of further objects, whose roles are described in section~\ref{sec:processing}, we also initialise the global random number generator. We again use an interface to defer deciding the concrete type until the program is actually run. Currently, we support the \href{https://mixmax.hepforge.org/}{MIXMAX} (\texttt{include/HEJ/Mixmax.hh}) and Ranlux64 (\texttt{include/HEJ/Ranlux64.hh}) random number generators, both are provided by \href{http://proj-clhep.web.cern.ch/}{CLHEP}. We also set up a \lstinline!HEJ::EventReader! object for reading events either in the the Les Houches event file format~\cite{Alwall:2006yp} or an \href{https://www.hdfgroup.org/}{HDF5}-based format~\cite{Hoeche:2019rti}. To allow making the decision at run time, \lstinline!HEJ::EventReader! is an abstract base class defined in \texttt{include/HEJ/EventReader.hh} and the implementations of the derived classes are in \texttt{include/HEJ/LesHouchesReader.hh}, \texttt{include/HEJ/HDF5Reader.hh} and the corresponding \texttt{.cc} source files in the \texttt{src} directory. The \lstinline!LesHouchesReader! leverages \href{http://home.thep.lu.se/~leif/LHEF/}{\texttt{include/LHEF/LHEF.h}}. A small wrapper around the \href{https://www.boost.org/doc/libs/1_67_0/libs/iostreams/doc/index.html}{boost iostreams} library allows us to also read event files compressed with \href{https://www.gnu.org/software/gzip/}{gzip}. The wrapper code is in \texttt{include/HEJ/stream.hh} and the \texttt{src/stream.cc}. If unweighting is enabled, we also initialise an unweighter as defined in \texttt{include/HEJ/Unweighter.hh}. The unweighting strategies are explained in section~\ref{sec:unweight}. \subsection{Event processing} \label{sec:processing} In the second stage events are continuously read from the event file. After jet clustering, a number of corresponding resummation events are generated for each input event and fed into the analyses and a number of output files. The roles of various classes and functions are illustrated in the following flow chart: \begin{center} \begin{tikzpicture}[node distance=2cm and 5mm] \node (reader) [mynode] {\lstinline!EventReader::read_event!\nodepart{second}{read event}}; \node (data) [mynode,below=of reader] {\lstinline!Event::EventData! constructor\nodepart{second}{convert to \HEJ object}}; \node (cluster) [mynode,below=of data] {\lstinline!Event::EventData::cluster!\nodepart{second}{cluster jets \& classify \lstinline!EventType!}}; \node (resum) [mynode,below=of cluster] {\lstinline!EventReweighter::reweight!\nodepart{second}{perform resummation}}; \node (cut) [mynode,below=of resum] {\lstinline!Analysis::pass_cuts!\nodepart{second}{apply cuts}}; \node (cut) [mynode,below=of resum] {\lstinline!Analysis::pass_cuts!\nodepart{second}{apply cuts}}; \node (unweight) [mynode,below=of cut] {\lstinline!Unweighter::unweight!\nodepart{second}{unweight (optional)}}; \node (fill) [mynode,below left=of unweight] {\lstinline!Analysis::fill!\nodepart{second}{analyse event}}; \node (write) [mynode,below right=of unweight] {\lstinline!CombinedEventWriter::write!\nodepart{second}{write out event}}; \node (control) [below=of unweight] {}; \draw[-{Latex[length=3mm, width=1.5mm]}] (reader.south) -- node[left] {\lstinline!LHEF::HEPEUP!} (data.north); \draw[-{Latex[length=3mm, width=1.5mm]}] (data.south) -- node[left] {\lstinline!Event::EventData!} (cluster.north); \draw[-{Latex[length=3mm, width=1.5mm]}] (cluster.south) -- node[left] {\lstinline!Event!} (resum.north); \draw[-{Latex[length=3mm, width=1.5mm]}] (resum.south) -- (cut.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(resum.south)+(10mm, 0cm)$) -- ($(cut.north)+(10mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(resum.south)+(5mm, 0cm)$) -- ($(cut.north)+(5mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(resum.south)-(5mm, 0cm)$) -- ($(cut.north)-(5mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(resum.south)-(10mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(cut.north)-(10mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] (cut.south) -- (unweight.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(cut.south)+(7mm, 0cm)$) -- ($(unweight.north)+(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(cut.south)-(7mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(unweight.north)-(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(unweight.south)-(3mm,0mm)$) .. controls ($(control)-(3mm,0mm)$) ..node[left] {\lstinline!Event!} (fill.east); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(unweight.south)-(3mm,0mm)$) .. controls ($(control)-(3mm,0mm)$) .. (write.west); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(unweight.south)+(3mm,0mm)$) .. controls ($(control)+(3mm,0mm)$) .. (fill.east); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(unweight.south)+(3mm,0mm)$) .. controls ($(control)+(3mm,0mm)$) ..node[right] {\lstinline!Event!} (write.west); \end{tikzpicture} \end{center} \lstinline!EventData! is an intermediate container, its members are completely accessible. In contrast after jet clustering and classification the phase space inside \lstinline!Event! can not be changed any more (\href{https://wikipedia.org/wiki/Builder_pattern}{Builder design pattern}). The resummation is performed by the \lstinline!EventReweighter! class, which is described in more detail in section~\ref{sec:resum}. The \lstinline!CombinedEventWriter! writes events to zero or more output files. To this end, it contains a number of objects implementing the \lstinline!EventWriter! interface. These event writers typically write the events to a file in a given format. We currently have the \lstinline!LesHouchesWriter! for event files in the Les Houches Event File format, the \lstinline!HDF5Writer! for \href{https://www.hdfgroup.org/}{HDF5}~\cite{Hoeche:2019rti} and the \lstinline!HepMC2Writer! or \lstinline!HepMC3Writer! for the \href{https://hepmc.web.cern.ch/hepmc/}{HepMC} format (Version 2 and 3). \subsection{Resummation} \label{sec:resum} In the \lstinline!EventReweighter::reweight! member function, we first classify the input fixed-order event (FKL, unordered, non-resummable, \dots) and decide according to the user settings whether to discard, keep, or resum the event. If we perform resummation for the given event, we generate a number of trial \lstinline!PhaseSpacePoint! objects. Phase space generation is discussed in more detail in section~\ref{sec:pspgen}. We then perform jet clustering according to the settings for the resummation jets on each \lstinline!PhaseSpacePoint!, update the factorisation and renormalisation scale in the resulting \lstinline!Event! and reweight it according to the ratio of pdf factors and \HEJ matrix elements between resummation and original fixed-order event: \begin{center} \begin{tikzpicture}[node distance=1.5cm and 5mm] \node (in) {}; \node (treat) [diamond,draw,below=of in,minimum size=3.5cm, label={[anchor=west, inner sep=8pt]west:discard}, label={[anchor=east, inner sep=14pt]east:keep}, label={[anchor=south, inner sep=20pt]south:reweight} ] {}; \draw (treat.north west) -- (treat.south east); \draw (treat.north east) -- (treat.south west); \node (psp) [mynode,below=of treat] {\lstinline!PhaseSpacePoint! constructor}; \node (cluster) [mynode,below=of psp] {\lstinline!Event::EventData::cluster!\nodepart{second}{cluster jets}}; \node (colour) [mynode,below=of cluster] {\lstinline!Event::generate_colours()!\nodepart{second}{generate particle colour}}; \node (gen_scales) [mynode,below=of colour] {\lstinline!ScaleGenerator::operator()!\nodepart{second}{update scales}}; \node (rescale) [mynode,below=of gen_scales] {\lstinline!PDF::pdfpt!, \lstinline!MatrixElement!\nodepart{second}{reweight}}; \node (out) [below of=rescale] {}; \draw[-{Latex[length=3mm, width=1.5mm]}] (in.south) -- node[left] {\lstinline!Event!} (treat.north); \draw[-{Latex[length=3mm, width=1.5mm]}] (treat.south) -- node[left] {\lstinline!Event!} (psp.north); \draw[-{Latex[length=3mm, width=1.5mm]}] (psp.south) -- (cluster.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(psp.south)+(7mm, 0cm)$) -- ($(cluster.north)+(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(psp.south)-(7mm, 0cm)$) -- node[left] {\lstinline!PhaseSpacePoint!} ($(cluster.north)-(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] (cluster.south) -- (colour.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(cluster.south)+(7mm, 0cm)$) -- ($(colour.north)+(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(cluster.south)-(7mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(colour.north)-(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] (colour.south) -- (gen_scales.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(colour.south)+(7mm, 0cm)$) -- ($(gen_scales.north)+(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(colour.south)-(7mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(gen_scales.north)-(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] (gen_scales.south) -- (rescale.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(gen_scales.south)+(7mm, 0cm)$) -- ($(rescale.north)+(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(gen_scales.south)-(7mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(rescale.north)-(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] (rescale.south) -- (out.north); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(rescale.south)+(7mm, 0cm)$) -- ($(out.north)+(7mm, 0cm)$); \draw[-{Latex[length=3mm, width=1.5mm]}] ($(rescale.south)-(7mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(out.north)-(7mm, 0cm)$); \node (helper) at ($(treat.east) + (15mm,0cm)$) {}; \draw[-{Latex[length=3mm, width=1.5mm]}] (treat.east) -- ($(treat.east) + (15mm,0cm)$) -- node[left] {\lstinline!Event!} (helper |- gen_scales.east) -- (gen_scales.east) ; \end{tikzpicture} \end{center} \subsection{Phase space point generation} \label{sec:pspgen} The resummed and matched \HEJ cross section for pure jet production of FKL configurations is given by (cf. eq. (3) of~\cite{Andersen:2018tnm}) \begin{align} \label{eq:resumdijetFKLmatched2} % \begin{split} \sigma&_{2j}^\mathrm{resum, match}=\sum_{f_1, f_2}\ \sum_m \prod_{j=1}^m\left( \int_{p_{j\perp}^B=0}^{p_{j\perp}^B=\infty} \frac{\mathrm{d}^2\mathbf{p}_{j\perp}^B}{(2\pi)^3}\ \int \frac{\mathrm{d} y_j^B}{2} \right) \ (2\pi)^4\ \delta^{(2)}\!\!\left(\sum_{k=1}^{m} \mathbf{p}_{k\perp}^B\right)\nonumber\\ &\times\ x_a^B\ f_{a, f_1}(x_a^B, Q_a^B)\ x_b^B\ f_{b, f_2}(x_b^B, Q_b^B)\ \frac{\overline{\left|\mathcal{M}_\text{LO}^{f_1f_2\to f_1g\cdots gf_2}\big(\big\{p^B_j\big\}\big)\right|}^2}{(\hat {s}^B)^2}\nonumber\\ & \times (2\pi)^{-4+3m}\ 2^m \nonumber\\ &\times\ \sum_{n=2}^\infty\ \int_{p_{1\perp}=p_{\perp,\mathrm{min}} }^{p_{1\perp}=\infty} \frac{\mathrm{d}^2\mathbf{p}_{1\perp}}{(2\pi)^3}\ \int_{p_{n\perp}=p_{\perp,\mathrm{min}}}^{p_{n\perp}=\infty} \frac{\mathrm{d}^2\mathbf{p}_{n\perp}}{(2\pi)^3}\ \prod_{i=2}^{n-1}\int_{p_{i\perp}=\lambda}^{p_{i\perp}=\infty} \frac{\mathrm{d}^2\mathbf{p}_{i\perp}}{(2\pi)^3}\ (2\pi)^4\ \delta^{(2)}\!\!\left(\sum_{k=1}^n \mathbf{p}_{k\perp}\right )\\ &\times \ \mathbf{T}_y \prod_{i=1}^n \left(\int \frac{\mathrm{d} y_i}{2}\right)\ \mathcal{O}_{mj}^e\ \left(\prod_{l=1}^{m-1}\delta^{(2)}(\mathbf{p}_{\mathcal{J}_{l}\perp}^B - \mathbf{j}_{l\perp})\right)\ \left(\prod_{l=1}^m\delta(y^B_{\mathcal{J}_l}-y_{\mathcal{J}_l})\right) \ \mathcal{O}_{2j}(\{p_i\})\nonumber\\ &\times \frac{(\hat{s}^B)^2}{\hat{s}^2}\ \frac{x_a f_{a,f_1}(x_a, Q_a)\ x_b f_{b,f_2}(x_b, Q_b)}{x_a^B\ f_{a,f_1}(x_a^B, Q_a^B)\ x_b^B\ f_{b,f_2}(x_b^B, Q_b^B)}\ \frac{\overline{\left|\mathcal{M}_{\mathrm{HEJ}}^{f_1 f_2\to f_1 g\cdots gf_2}(\{ p_i\})\right|}^2}{\overline{\left|\mathcal{M}_\text{LO, HEJ}^{f_1f_2\to f_1g\cdots gf_2}\big(\big\{p^B_j\big\}\big)\right|}^{2}} \,.\nonumber % \end{split} \end{align} The first two lines correspond to the generation of the fixed-order input events with incoming partons $f_1, f_2$ and outgoing momenta $p_j^B$, where $\mathbf{p}_{j\perp}^B$ and $y_j^B$ denote the respective transverse momentum and rapidity. Note that, at leading order, these coincide with the fixed-order jet momenta $p_{\mathcal{J}_j}^B$. $f_{a,f_1}(x_a, Q_a),f_{b,f_2}(x_b, Q_b)$ are the pdf factors for the incoming partons with momentum fractions $x_a$ and $x_b$. The square of the partonic centre-of-mass energy is denoted by $\hat{s}^B$ and $\mathcal{M}_\text{LO}^{f_1f_2\to f_1g\cdots gf_2}$ is the leading-order matrix element. The third line is a factor accounting for the different multiplicities between fixed-order and resummation events. Lines four and five are the integration over the resummation phase space described in this section. $p_i$ are the momenta of the outgoing partons in resummation phase space. $\mathbf{T}_y$ denotes rapidity ordering and $\mathcal{O}_{mj}^e$ projects out the exclusive $m$-jet component. The relation between resummation and fixed-order momenta is fixed by the $\delta$ functions. The first sets each transverse fixed-order jet momentum to some function $\mathbf{j_{l\perp}}$ of the resummation momenta. The exact form is described in section~\ref{sec:ptj_res}. The second $\delta$ forces the rapidities of resummation and fixed-order jets to be the same. Finally, the last line is the reweighting of pdf and matrix element factors already shown in section~\ref{sec:resum}. There are two kinds of cut-off in the integration over the resummation partons. $\lambda$ is a technical cut-off connected to the cancellation of infrared divergencies between real and virtual corrections. Its numerical value is set in \texttt{include/HEJ/Constants.h}. $p_{\perp,\mathrm{min}}$ regulates and \emph{uncancelled} divergence in the extremal parton momenta. Its size is set by the user configuration \url{https://hej.web.cern.ch/HEJ/doc/current/user/HEJ.html#settings}. It is straightforward to generalise eq.~(\ref{eq:resumdijetFKLmatched2}) to unordered configurations and processes with additional colourless emissions, for example a Higgs or electroweak boson. In the latter case only the fixed-order integration and the matrix elements change. \subsubsection{Gluon Multiplicity} \label{sec:psp_ng} The first step in evaluating the resummation phase space in eq.~(\ref{eq:resumdijetFKLmatched2}) is to randomly pick terms in the sum over the number of emissions. This sampling of the gluon multiplicity is done in the \lstinline!PhaseSpacePoint::sample_ng! function in \texttt{src/PhaseSpacePoint.cc}. The typical number of extra emissions depends strongly on the rapidity span of the underlying fixed-order event. Let us, for example, consider a fixed-order FKL-type multi-jet configuration with rapidities $y_{j_f},\,y_{j_b}$ of the most forward and backward jets, respectively. By eq.~(\ref{eq:resumdijetFKLmatched2}), the jet multiplicity and the rapidity of each jet are conserved when adding resummation. This implies that additional hard radiation is restricted to rapidities $y$ within a region $y_{j_b} \lesssim y \lesssim y_{j_f}$. Within \HEJ, we require the most forward and most backward emissions to be hard \todo{specify how hard} in order to avoid divergences, so this constraint in fact applies to \emph{all} additional radiation. To simplify the remaining discussion, let us remove the FKL rapidity ordering \begin{equation} \label{eq:remove_y_order} \mathbf{T}_y \prod_{i=1}^n\int \frac{\mathrm{d}y_i}{2} = \frac{1}{n!}\prod_{i=1}^n\int \frac{\mathrm{d}y_i}{2}\,, \end{equation} where all rapidity integrals now cover a region which is approximately bounded by $y_{j_b}$ and $y_{j_f}$. Each of the $m$ jets has to contain at least one parton; selecting random emissions we can rewrite the phase space integrals as \begin{equation} \label{eq:select_jets} \frac{1}{n!}\prod_{i=1}^n\int [\mathrm{d}p_i] = \left(\prod_{i=1}^{m}\int [\mathrm{d}p_i]\ {\cal J}_i(p_i)\right) \frac{1}{n_g!}\prod_{i=m+1}^{m+n_g}\int [\mathrm{d}p_i] \end{equation} with jet selection functions \begin{equation} \label{eq:def_jet_selection} {\cal J}_i(p) = \begin{cases} 1 &p\text{ clustered into jet }i\\ 0 & \text{otherwise} \end{cases} \end{equation} and $n_g \equiv n - m$. Here and in the following we use the short-hand notation $[\mathrm{d}p_i]$ to denote the phase-space measure for parton $i$. As is evident from eq.~\eqref{eq:select_jets}, adding an extra emission $n_g+1$ introduces a suppression factor $\tfrac{1}{n_g+1}$. However, the additional phase space integral also results in an enhancement proportional to $\Delta y_{j_f j_b} = y_{j_f} - y_{j_b}$. This is a result of the rapidity-independence of the MRK limit of the integrand, consisting of the matrix elements divided by the flux factor. Indeed, we observe that the typical number of gluon emissions is to a good approximation proportional to the rapidity separation and the phase space integral is dominated by events with $n_g \approx \Delta y_{j_f j_b}$. For the actual phase space sampling, we assume a Poisson distribution and extract the mean number of gluon emissions in different rapidity bins and fit the results to a linear function in $\Delta y_{j_f j_b}$, finding a coefficient of $0.975$ for the inclusive production of a Higgs boson with two jets. Here are the observed and fitted average gluon multiplicities as a function of $\Delta y_{j_f j_b}$: \begin{center} \includegraphics[width=.75\textwidth]{ng_mean} \end{center} As shown for two rapidity slices the assumption of a Poisson distribution is also a good approximation: \begin{center} \includegraphics[width=.49\textwidth]{{ng_1.5}.pdf}\hfill \includegraphics[width=.49\textwidth]{{ng_5.5}.pdf} \end{center} \subsubsection{Number of Gluons inside Jets} \label{sec:psp_ng_jet} For each of the $n_g$ gluon emissions we can split the phase-space integral into a (disconnected) region inside the jets and a remainder: \begin{equation} \label{eq:psp_split} \int [\mathrm{d}p_i] = \int [\mathrm{d}p_i]\, \theta\bigg(\sum_{j=1}^{m}{\cal J}_j(p_i)\bigg) + \int [\mathrm{d}p_i]\, \bigg[1-\theta\bigg(\sum_{j=1}^{m}{\cal J}_j(p_i)\bigg)\bigg]\,. \end{equation} The next step is to decide how many of the gluons will form part of a jet. This is done in the \lstinline!PhaseSpacePoint::sample_ng_jets! function. We choose an importance sampling which is flat in the plane spanned by the azimuthal angle $\phi$ and the rapidity $y$. This is observed in BFKL and valid in the limit of Multi-Regge-Kinematics (MRK). Furthermore, we assume anti-$k_t$ jets, which cover an area of $\pi R^2$. In principle, the total accessible area in the $y$-$\phi$ plane is given by $2\pi \Delta y_{fb}$, where $\Delta y_{fb}\geq \Delta y_{j_f j_b}$ is the a priori unknown rapidity separation between the most forward and backward partons. In most cases the extremal jets consist of single partons, so that $\Delta y_{fb} = \Delta y_{j_f j_b}$. For the less common case of two partons forming a jet we observe a maximum distance of $R$ between the constituents and the jet centre. In rare cases jets have more than two constituents. Empirically, they are always within a distance of $\tfrac{5}{3}R$ to the centre of the jet, so $\Delta y_{fb} \leq \Delta y_{j_f j_b} + \tfrac{10}{3} R$. In practice, the extremal partons are required to carry a large fraction of the jet transverse momentum and will therefore be much closer to the jet axis. In summary, for sufficiently large rapidity separations we can use the approximation $\Delta y_{fb} \approx \Delta y_{j_f j_b}$. This scenario is depicted here: \begin{center} \includegraphics[width=0.5\linewidth]{ps_large_y} \end{center} If there is no overlap between jets, the probability $p_{\cal J, >}$ for an extra gluon to end up inside a jet is then given by \begin{equation} \label{eq:p_J_large} p_{\cal J, >} = \frac{(m - 1)\*R^2}{2\Delta y_{j_f j_b}}\,. \end{equation} For a very small rapidity separation, eq.~\eqref{eq:p_J_large} obviously overestimates the true probability. The maximum phase space covered by jets in the limit of a vanishing rapidity distance between all partons is $2mR \Delta y_{fb}$: \begin{center} \includegraphics[width=0.5\linewidth]{ps_small_y} \end{center} We therefore estimate the probability for a parton to end up inside a jet as \begin{equation} \label{eq:p_J} p_{\cal J} = \min\bigg(\frac{(m - 1)\*R^2}{2\Delta y_{j_f j_b}}, \frac{mR}{\pi}\bigg)\,. \end{equation} Here we compare this estimate with the actually observed fraction of additional emissions into jets as a function of the rapidity separation: \begin{center} \includegraphics[width=0.75\linewidth]{pJ} \end{center} \subsubsection{Gluons outside Jets} \label{sec:gluons_nonjet} Using our estimate for the probability of a gluon to be a jet constituent, we choose a number $n_{g,{\cal J}}$ of gluons inside jets, which also fixes the number $n_g - n_{g,{\cal J}}$ of gluons outside jets. As explained later on, we need to generate the momenta of the gluons outside jets first. This is done in \lstinline!PhaseSpacePoint::gen_non_jet!. The azimuthal angle $\phi$ is generated flat within $0\leq \phi \leq 2 \pi$. The allowed rapidity interval is set by the most forward and backward partons, which are necessarily inside jets. Since these parton rapidities are not known at this point, we also have to postpone the rapidity generation for the gluons outside jets. For the scalar transverse momentum $p_\perp = |\mathbf{p}_\perp|$ of a gluon outside jets we use the parametrisation \begin{equation} \label{eq:p_nonjet} p_\perp = \lambda + \tilde{p}_\perp\*\tan(\tau\*r)\,, \qquad \tau = \arctan\bigg(\frac{p_{\perp{\cal J}_\text{min}} - \lambda}{\tilde{p}_\perp}\bigg)\,. \end{equation} For $r \in [0,1)$, $p_\perp$ is always less than the minimum momentum $p_{\perp{\cal J}_\text{min}}$ required for a jet. $\tilde{p}_\perp$ is a free parameter, a good empirical value is $\tilde{p}_\perp = [1.3 + 0.2\*(n_g - n_{g,\cal J})]\,$GeV \subsubsection{Resummation jet momenta} \label{sec:ptj_res} On the one hand, each jet momentum is given by the sum of its constituent momenta. On the other hand, the resummation jet momenta are fixed by the constraints in line five of the master equation~\eqref{eq:resumdijetFKLmatched2}. We therefore have to calculate the resummation jet momenta from these constraints before generating the momenta of the gluons inside jets. This is done in \lstinline!PhaseSpacePoint::reshuffle! and in the free \lstinline!resummation_jet_momenta! function (declared in \texttt{resummation\_jet.hh}). The resummation jet momenta are determined by the $\delta$ functions in line five of eq.~(\ref{eq:resumdijetFKLmatched2}). The rapidities are fixed to the rapidities of the jets in the input fixed-order events, so that the FKL ordering is guaranteed to be preserved. In traditional \HEJ reshuffling the transverse momentum are given through \begin{equation} \label{eq:ptreassign_old} \mathbf{p}^B_{\mathcal{J}_{l\perp}} = \mathbf{j}_{l\perp} \equiv \mathbf{p}_{\mathcal{J}_{l}\perp} + \mathbf{q}_\perp \,\frac{|\mathbf{p}_{\mathcal{J}_{l}\perp}|}{P_\perp}, \end{equation} where $\mathbf{q}_\perp = \sum_{j=1}^n \mathbf{p}_{i\perp} \bigg[1-\theta\bigg(\sum_{j=1}^{m}{\cal J}_j(p_i)\bigg)\bigg] $ is the total transverse momentum of all partons \emph{outside} jets and $P_\perp = \sum_{j=1}^m |\mathbf{p}_{\mathcal{J}_{j}\perp}|$. Since the total transverse momentum of an event vanishes, we can also use $\mathbf{q}_\perp = - \sum_{j=1}^m \mathbf{p}_{\mathcal{J}_{j}\perp}$. Eq.~(\ref{eq:ptreassign}) is a non-linear system of equations in the resummation jet momenta $\mathbf{p}_{\mathcal{J}_{l}\perp}$. Hence we would have to solve \begin{equation} \label{eq:ptreassign_eq} \mathbf{p}_{\mathcal{J}_{l}\perp}=\mathbf{j}^B_{l\perp} \equiv\mathbf{j}_{l\perp}^{-1} \left(\mathbf{p}^B_{\mathcal{J}_{l\perp}}\right) \end{equation} numerically. Since solving such a system is computationally expensive, we instead change the reshuffling around to be linear in the resummation jet momenta. Hence~\eqref{eq:ptreassign_eq} gets replaces by \begin{equation} \label{eq:ptreassign} \mathbf{p}_{\mathcal{J}_{l\perp}} = \mathbf{j}^B_{l\perp} \equiv \mathbf{p}^B_{\mathcal{J}_{l}\perp} - \mathbf{q}_\perp \,\frac{|\mathbf{p}^B_{\mathcal{J}_{l}\perp}|}{P^B_\perp}, \end{equation} which is linear in the resummation momentum. Consequently the equivalent of~\eqref{eq:ptreassign_old} is non-linear in the Born momentum. However the exact form of~\eqref{eq:ptreassign_old} is not relevant for the resummation. Both methods have been tested for two and three jets with the \textsc{rivet} standard analysis \texttt{MC\_JETS}. They didn't show any differences even after $10^9$ events. The reshuffling relation~\eqref{eq:ptreassign} allows the transverse momenta $p^B_{\mathcal{J}_{l\perp}}$ of the fixed-order jets to be somewhat below the minimum transverse momentum of resummation jets. It is crucial that this difference does not become too large, as the fixed-order cross section diverges for vanishing transverse momenta. In the production of a Higgs boson with resummation jets above $30\,$GeV we observe that the contribution from fixed-order events with jets softer than about $20\,$GeV can be safely neglected. This is shown in the following plot of the differential cross section over the transverse momentum of the softest fixed-order jet: \begin{center} \includegraphics[width=.75\textwidth]{ptBMin} \end{center} Finally, we have to account for the fact that the reshuffling relation~\eqref{eq:ptreassign} is non-linear in the Born momenta. To arrive at the master formula~\eqref{eq:resumdijetFKLmatched2} for the cross section, we have introduced unity in the form of an integral over the Born momenta with $\delta$ functions in the integrand, that is \begin{equation} \label{eq:delta_intro} 1 = \int_{p_{j\perp}^B=0}^{p_{j\perp}^B=\infty} \mathrm{d}^2\mathbf{p}_{j\perp}^B\delta^{(2)}(\mathbf{p}_{\mathcal{J}_{j\perp}}^B - \mathbf{j}_{j\perp})\,. \end{equation} If the arguments of the $\delta$ functions are not linear in the Born momenta, we have to compensate with additional Jacobians as factors. Explicitly, for the reshuffling relation~\eqref{eq:ptreassign} we have \begin{equation} \label{eq:delta_rewrite} \prod_{l=1}^m \delta^{(2)}(\mathbf{p}_{\mathcal{J}_{l\perp}}^B - \mathbf{j}_{l\perp}) = \Delta \prod_{l=1}^m \delta^{(2)}(\mathbf{p}_{\mathcal{J}_{l\perp}} - \mathbf{j}_{l\perp}^B)\,, \end{equation} where $\mathbf{j}_{l\perp}^B$ is given by~\eqref{eq:ptreassign_eq} and only depends on the Born momenta. We have extended the product to run to $m$ instead of $m-1$ by eliminating the last $\delta$ function $\delta^{(2)}\!\!\left(\sum_{k=1}^n \mathbf{p}_{k\perp}\right )$. The Jacobian $\Delta$ is the determinant of a $2m \times 2m$ matrix with $l, l' = 1,\dots,m$ and $X, X' = x,y$. \begin{equation} \label{eq:jacobian} \Delta = \left|\frac{\partial\,\mathbf{j}^B_{l'\perp}}{\partial\, \mathbf{p}^B_{{\cal J}_l \perp}} \right| = \left| \delta_{l l'} \delta_{X X'} - \frac{q_X\, p^B_{{\cal J}_{l'}X'}}{\left|\mathbf{p}^B_{{\cal J}_{l'} \perp}\right| P^B_\perp}\left(\delta_{l l'} - \frac{\left|\mathbf{p}^B_{{\cal J}_l \perp}\right|}{P^B_\perp}\right)\right|\,. \end{equation} The determinant is calculated in \lstinline!resummation_jet_weight!, again coming from the \texttt{resummation\_jet.hh} header. Having to introduce this Jacobian is not a disadvantage specific to the new reshuffling. If we instead use the old reshuffling relation~\eqref{eq:ptreassign_old} we \emph{also} have to introduce a similar Jacobian since we actually want to integrate over the resummation phase space and need to transform the argument of the $\delta$ function to be linear in the resummation momenta for this. \subsubsection{Gluons inside Jets} \label{sec:gluons_jet} After the steps outlined in section~\ref{sec:psp_ng_jet}, we have a total number of $m + n_{g,{\cal J}}$ constituents. In \lstinline!PhaseSpacePoint::distribute_jet_partons! we distribute them randomly among the jets such that each jet has at least one constituent. We then generate their momenta in \lstinline!PhaseSpacePoint::split! using the \lstinline!Splitter! class. The phase space integral for a jet ${\cal J}$ is given by \begin{equation} \label{eq:ps_jetparton} \prod_{i\text{ in }{\cal J}} \bigg(\int \mathrm{d}\mathbf{p}_{i\perp}\ \int \mathrm{d} y_i \bigg)\delta^{(2)}\Big(\sum_{i\text{ in }{\cal J}} \mathbf{p}_{i\perp} - \mathbf{j}_{\perp}^B\Big)\delta(y_{\mathcal{J}}-y^B_{\mathcal{J}})\,. \end{equation} For jets with a single constituent, the parton momentum is obiously equal to the jet momentum. In the case of two constituents, we observe that the partons are always inside the jet cone with radius $R$ and often very close to the jet centre. The following plots show the typical relative distance $\Delta R/R$ for this scenario: \begin{center} \includegraphics[width=0.45\linewidth]{dR_2} \includegraphics[width=0.45\linewidth]{dR_2_small} \end{center} According to this preference for small values of $\Delta R$, we parametrise the $\Delta R$ integrals as \begin{equation} \label{eq:dR_sampling} \frac{\Delta R}{R} = \begin{cases} 0.25\,x_R & x_R < 0.4 \\ 1.5\,x_R - 0.5 & x_R \geq 0.4 \end{cases}\,. \end{equation} Next, we generate $\Theta_1 \equiv \Theta$ and use the constraint $\Theta_2 = \Theta \pm \pi$. The transverse momentum of the first parton is then given by \begin{equation} \label{eq:delta_constraints} p_{1\perp} = \frac{p_{\mathcal{J} y} - \tan(\phi_2) p_{\mathcal{J} x}}{\sin(\phi_1) - \tan(\phi_2)\cos(\phi_1)}\,. \end{equation} We get $p_{2\perp}$ by exchanging $1 \leftrightarrow 2$ in the indices. To obtain the Jacobian of the transformation, we start from the single jet phase space eq.~(\ref{eq:ps_jetparton}) with the rapidity delta function already rewritten to be linear in the rapidity of the last parton, i.e. \begin{equation} \label{eq:jet_2p} \prod_{i=1,2} \bigg(\int \mathrm{d}\mathbf{p}_{i\perp}\ \int \mathrm{d} y_i \bigg)\delta^{(2)}\Big(\mathbf{p}_{1\perp} + \mathbf{p}_{2\perp} - \mathbf{j}_{\perp}^B\Big)\delta(y_2- \dots)\,. \end{equation} The integral over the second parton momentum is now trivial; we can just replace the integral over $y_2$ with the equivalent constraint \begin{equation} \label{eq:R2} \int \mathrm{d}R_2 \ \delta\bigg(R_2 - \bigg[\phi_{\cal J} - \arctan \bigg(\frac{p_{{\cal J}y} - p_{1y}}{p_{{\cal J}x} - p_{1x}}\bigg)\bigg]/\cos \Theta\bigg) \,. \end{equation} In order to fix the integral over $p_{1\perp}$ instead, we rewrite this $\delta$ function. This introduces the Jacobian \begin{equation} \label{eq:jac_pt1} \bigg|\frac{\partial p_{1\perp}}{\partial R_2} \bigg| = \frac{\cos(\Theta)\mathbf{p}_{2\perp}^2}{p_{{\cal J}\perp}\sin(\phi_{\cal J}-\phi_1)}\,. \end{equation} The final form of the integral over the two parton momenta is then \begin{equation} \label{eq:ps_jet_2p} \int \mathrm{d}R_1\ R_1 \int \mathrm{d}R_2 \int \mathrm{d}x_\Theta\ 2\pi \int \mathrm{d}p_{1\perp}\ p_{1\perp} \int \mathrm{d}p_{2\perp} \ \bigg|\frac{\partial p_{1\perp}}{\partial R_2} \bigg|\delta(p_{1\perp} -\dots) \delta(p_{2\perp} - \dots)\,. \end{equation} As is evident from section~\ref{sec:psp_ng_jet}, jets with three or more constituents are rare and an efficient phase-space sampling is less important. For such jets, we exploit the observation that partons with a distance larger than $R_{\text{max}} = \tfrac{5}{3} R$ to the jet centre are never clustered into the jet. Assuming $N$ constituents, we generate all components for the first $N-1$ partons and fix the remaining parton with the $\delta$-functional. In order to end up inside the jet, we use the parametrisation \begin{align} \label{eq:ps_jet_param} \phi_i ={}& \phi_{\cal J} + \Delta \phi_i\,, & \Delta \phi_i ={}& \Delta R_i \cos(\Theta_i)\,, \\ y_i ={}& y_{\cal J} + \Delta y_i\,, & \Delta y_i ={}& \Delta R_i \sin(\Theta_i)\,, \end{align} and generate $\Theta_i$ and $\Delta R_i$ randomly with $\Delta R_i \leq R_{\text{max}}$ and the empiric value $R_{\text{max}} = 5\*R/3$. We can then write the phase space integral for a single parton as $(p_\perp = |\mathbf{p}_\perp|)$ \begin{equation} \label{eq:ps_jetparton_x} \int \mathrm{d}\mathbf{p}_{\perp}\ \int \mathrm{d} y \approx \int_{\Box} \mathrm{d}x_{\perp} \mathrm{d}x_{ R} \mathrm{d}x_{\theta}\ 2\*\pi\,\*R_{\text{max}}^2\,\*x_{R}\,\*p_{\perp}\,\*(p_{\perp,\text{max}} - p_{\perp,\text{min}}) \end{equation} with \begin{align} \label{eq:ps_jetparton_parameters} \Delta \phi ={}& R_{\text{max}}\*x_{R}\*\cos(2\*\pi\*x_\theta)\,,& \Delta y ={}& R_{\text{max}}\*x_{R}\*\sin(2\*\pi\*x_\theta)\,, \\ p_{\perp} ={}& (p_{\perp,\text{max}} - p_{\perp,\text{min}})\*x_\perp + p_{\perp,\text{min}}\,. \end{align} $p_{\perp,\text{max}}$ is determined from the requirement that the total contribution from the first $n-1$ partons --- i.e. the projection onto the jet $p_{\perp}$ axis --- must never exceed the jet $p_\perp$. This gives \todo{This bound is too high} \begin{equation} \label{eq:pt_max} p_{i\perp,\text{max}} = \frac{p_{{\cal J}\perp} - \sum_{j.frm} inside \texttt{current\_generator} it generates a corresponding header \texttt{include/HEJ/currents/.hh} inside the build directory. The header can be included with \begin{lstlisting}[language=C++,caption={}] #include "HEJ/currents/.hh" \end{lstlisting} The naming scheme is \begin{itemize} \item \texttt{j1\_j2.frm} for the contraction of current \texttt{j1} with current \texttt{j2}. \item \texttt{j1\_vx\_j2.frm} for the contraction of current \texttt{j1} with the vertex \texttt{vx} and the current \texttt{j2}. \end{itemize} For instance, \texttt{juno\_qqbarW\_j.frm} would indicate the contraction of an unordered current with a (central) quark-antiquark-W emission vertex and a standard FKL current. \subsection{Implementing new current contractions} \label{sec:cur_gen_new} To implement a new current contraction \lstinline!jcontr! create a new file \texttt{jcontr.frm} inside the \texttt{current\_generator} directory. \texttt{jcontr.frm} should contain \FORM code, see \url{https://www.nikhef.nl/~form/} for more information on \FORM. Here is a small example: \begin{lstlisting}[caption={}] * FORM comments are started by an asterisk * at the beginning of a line * First include the relevant headers #include- include/helspin.frm #include- include/write.frm * Define the symbols that appear. * UPPERCASE symbols are reserved for internal use vectors p1,...,p10; indices mu1,...,mu10; * Define local expressions of the form [NAME HELICITIES] * for the current contractions for all relevant helicity configurations #do HELICITY1={+,-} #do HELICITY2={+,-} * We use the Current function * Current(h, p1, mu1, ..., muX, p2) = * u(p1) \gamma_{mu1} ... \gamma_{muX} u(p2) * where h=+1 or h=-1 is the spinor helicity. * All momenta appearing as arguments have to be *lightlike* local [jcontr `HELICITY1'`HELICITY2'] = Current(`HELICITY1'1, p1, mu1, p2, mu2, p3) *Current(`HELICITY2'1, p4, mu2, p2, mu1, p1); #enddo #enddo .sort * Main procedure that calculates the contraction #call ContractCurrents .sort * Optimise expression format O4; * Format in a (mostly) c compatible way format c; * Write start of C++ header file #call WriteHeader(`OUTPUT') * Write a template function jcontr * taking as template arguments two helicities * and as arguments the momenta p1,...,p4 * returning the contractions [jcontr HELICITIES] defined above #call WriteOptimised(`OUTPUT',jcontr,2,p1,p2,p3,p4) * Wrap up #call WriteFooter(`OUTPUT') .end \end{lstlisting} \subsection{Calculation of contractions} \label{sec:contr_calc} In order to describe the algorithm for the calculation of current contractions we first have to define the currents and establish some useful relations. \subsubsection{Massive momenta} \label{sec:p_massive} We want to use relations for lightlike momenta. Momenta $P$ that are \emph{not} lightlike can be written as the sum of two lightlike momenta: \begin{equation} \label{eq:P_massive} P^\mu = p^\mu + q^\mu\,, \qquad p^2 = q^2 = 0 \,. \end{equation} This decomposition is not unique. If we impose the arbitrary constraint $q_\perp = 0$ and require real-valued momentum components we can use the ansatz \begin{align} \label{eq:P_massive_p} p_\mu ={}& P_\perp\*(\cosh y, \cos \phi, \sin \phi, \sinh y)\,,\\ \label{eq:P_massive_q} q_\mu ={}& (E, 0, 0, s\,E)\,, \end{align} where $P_\perp$ is the transverse momentum of $P$ and $\phi$ the corresponding azimuthal angle. For the remaining parameters we obtain \begin{align} \label{eq:P_massive_plus} P^+ > 0:& & y ={}& \log \frac{P^+}{P_\perp}\,,\qquad E = \frac{P^2}{2P^+}\,,\qquad s = -1\,,\\ \label{eq:P_massive_minus} P^- > 0:& & y ={}& \log \frac{P_\perp}{P^-}\,,\qquad E = \frac{P^2}{2P^-}\,,\qquad s = +1\,. \end{align} \subsubsection{Currents and current relations} \label{sec:current_relations} Our starting point are generalised currents \begin{equation} \label{eq:j_gen} j^{\pm}(p, \mu_1,\dots,\mu_{2N-1},q) = \bar{u}^{\pm}(p)\gamma_{\mu_1} \dots \gamma_{\mu_{2N-1}} u^\pm(q)\,. \end{equation} Since there are no masses, we can consider two-component chiral spinors \begin{align} \label{eq:u_plus} u^+(p)={}& \left(\sqrt{p^+}, \sqrt{p^-} \hat{p}_\perp \right) \,,\\ \label{eq:u_minus} u^-(p)={}& \left(\sqrt{p^-} \hat{p}^*_\perp, -\sqrt{p^+}\right)\,, \end{align} with $p^\pm = E\pm p_z,\, \hat{p}_\perp = \tfrac{p_\perp}{|p_\perp|},\, p_\perp = p_x + i p_y$. The spinors for vanishing transverse momentum are obtained by replacing $\hat{p}_\perp \to -1$. This gives \begin{equation} \label{eq:jminus_gen} j^-(p,\mu_1,\dots,\mu_{2N-1},q) = u^{-,\dagger}(p)\ \sigma^-_{\mu_1}\ \sigma^+_{\mu_2}\dots\sigma^-_{\mu_{2N-1}}\ u^{-}(q)\,. \end{equation} where $\sigma_\mu^\pm = (1, \pm \sigma_i)$ and $\sigma_i$ are the Pauli matrices \begin{equation} \label{eq:Pauli_matrices} \sigma_1 = \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix} \,, \qquad \sigma_2 = \begin{pmatrix} 0 & -i\\ i & 0 \end{pmatrix} \,, \qquad \sigma_3 = \begin{pmatrix} 1 & 0\\ 0 & -1 \end{pmatrix} \,. \end{equation} For positive-helicity currents we can either flip all helicities in eq.~(\ref{eq:jminus_gen}) or reverse the order of the arguments, i.e. \begin{equation} \label{eq:jplus_gen} j^+(p,\mu_1,\dots,\mu_{2N-1},q) = \big(j^-(p,\mu_1,\dots,\mu_{2N-1},q)\big)^* = j^-(q,\mu_{2N-1},\dots,\mu_1,p) \,. \end{equation} Using the standard spinor-helicity notation we have \begin{gather} \label{eq:spinors_spinhel} u^+(p) = | p \rangle\,, \qquad u^-(p) = | p ]\,, \qquad u^{+,\dagger}(p) = [ p |\,, \qquad u^{-,\dagger}(p) = \langle p |\,,\\ \label{eq:current_spinhel} j^-(p,\mu_1,\dots,\mu_{2N-1},q) = \langle p |\ \mu_1\ \dots\ \mu_{2N-1}\ | q ] \,.\\ \label{eq:contraction_spinhel} P_{\mu_i} j^-(p,\mu_1,\dots,\mu_{2N-1},q) = \langle p |\ \mu_1\ \dots\ \mu_{i-1}\ P\ \mu_{i+1}\ \dots\ \mu_{2N-1}\ | q ] \,. \end{gather} Lightlike momenta $p$ can be decomposed into spinor products: \begin{equation} \label{eq:p_decomp} \slashed{p} = |p\rangle [p| + |p] \langle p |\,. \end{equation} Taking into account helicity conservation this gives the following relations: \begingroup \addtolength{\jot}{1em} \begin{align} \label{eq:p_in_current} \langle p |\ \mu_1\ \dots\ \mu_i\ P\ \mu_{i+1}\ \dots\ \mu_{2N-1}\ | q ] ={}& \begin{cases} \langle p |\ \mu_1\ \dots\ \mu_i\ |P]\ \langle P|\ \mu_{i+1}\ \dots\ \mu_{2N-1}\ | q ]& i \text{ even}\\ \langle p |\ \mu_1\ \dots\ \mu_i\ |P\rangle\ [ P|\ \mu_{i+1}\ \dots\ \mu_{2N-1}\ | q ]& i \text{ odd} \end{cases}\,,\\ \label{eq:p_in_angle} \langle p |\ \mu_1\ \dots\ \mu_i\ P\ \mu_{i+1}\ \dots\ \mu_{2N}\ | q \rangle ={}& \begin{cases} \langle p |\ \mu_1\ \dots\ \mu_i\ |P]\ \langle P| \mu_{i+1}\ \dots\ \mu_{2N}\ | q \rangle & i \text{ even}\\ \langle p |\ \mu_1\ \dots\ \mu_i\ |P\rangle\ \big(\langle P| \mu_{i+1}\ \dots\ \mu_{2N}\ | q ]\big)^* & i \text{ odd} \end{cases}\,,\\ \label{eq:p_in_square} [ p |\ \mu_1\ \dots\ \mu_i\ P\ \mu_{i+1}\ \dots\ \mu_{2N}\ | q ] ={}& \begin{cases} \big(\langle p |\ \mu_1\ \dots\ \mu_i\ |P]\big)^* \ [ P| \mu_{i+1}\ \dots\ \mu_{2N}\ | q ] & i \text{ even}\\ [ p |\ \mu_1\ \dots\ \mu_i\ |P]\ \langle P| \mu_{i+1}\ \dots\ \mu_{2N}\ | q ] & i \text{ odd} \end{cases}\,. \end{align} \endgroup For contractions of vector currents we can use the Fierz identity \begin{equation} \label{eq:Fierz} \langle p|\ \mu\ |q]\ \langle k|\ \mu\ |l] = 2 \spa p.k \spb l.q\,. \end{equation} The scalar angle and square products are given by \begin{align} \label{eq:angle_product} \spa p.q ={}& {\big(u^-(p)\big)}^\dagger u^+(q) = \sqrt{p^-q^+}\hat{p}_{i,\perp} - \sqrt{p^+q^-}\hat{q}_{j,\perp} = - \spa q.p\,,\\ \label{eq:square_product} \spb p.q ={}& {\big(u^+(p)\big)}^\dagger u^-(q) = -\spa p.q ^* = - \spb q.p\,. \end{align} \subsubsection{Contraction algorithm} \label{sec:contr_calc_algo} The contractions are now calculated as follows: \begin{enumerate} \item Use equations \eqref{eq:jplus_gen}, \eqref{eq:current_spinhel} to write all currents in a canonical form. \item Assume that all momenta are lightlike and use the relations \eqref{eq:p_in_current}, \eqref{eq:p_in_angle}, \eqref{eq:p_in_square} to split up currents that are contracted with momenta. \item Apply the Fierz transformation~\eqref{eq:Fierz} to eliminate contractions between vector currents. \item Write the arguments of the antisymmetric angle and scalar products in canonical order, see equations~\eqref{eq:angle_product} ,\eqref{eq:square_product}. \end{enumerate} The corresponding \lstinline!ContractCurrents! procedure is implemented in \texttt{include/helspin.fm}. \section{The fixed-order generator} \label{sec:HEJFOG} Even at leading order, standard fixed-order generators can only generate events with a limited number of final-state particles within reasonable CPU time. The purpose of the fixed-order generator is to supplement this with high-multiplicity input events according to the first two lines of eq.~\eqref{eq:resumdijetFKLmatched2} with the \HEJ approximation $\mathcal{M}_\text{LO, \HEJ}^{f_1f_2\to f_1g\cdots gf_2}$ instead of the full fixed-order matrix element $\mathcal{M}_\text{LO}^{f_1f_2\to f_1g\cdots gf_2}$. Its usage is described in the user documentation \url{https://hej.web.cern.ch/HEJ/doc/current/user/HEJFOG.html}. \subsection{File structure} \label{sec:HEJFOG_structure} The code for the fixed-order generator is in the \texttt{FixedOrderGen} directory, which contains the following: \begin{description} \item[include:] Contains the C++ header files. \item[src:] Contains the C++ source files. \item[t:] Contains the source code for the automated tests. \item[CMakeLists.txt:] Configuration file for the \cmake build system. \item[configFO.yml:] Sample configuration file for the fixed-order generator. \end{description} The code is generally in the \lstinline!HEJFOG! namespace. Functions and classes \lstinline!MyClass! are usually declared in \texttt{include/MyClass.hh} and implemented in \texttt{src/MyClass.cc}. \subsection{Program flow} \label{sec:prog_flow} A single run of the fixed-order generator consists of three or four stages. First, we perform initialisation similar to \HEJ 2, see section~\ref{sec:init}. Since there is a lot of overlap we frequently reuse classes and functions from \HEJ 2, i.e. from the \lstinline!HEJ! namespace. The code for parsing the configuration file is in \texttt{include/config.hh} and implemented in \texttt{src/config.cc}. If partial unweighting is requested in the user settings \url{https://hej.web.cern.ch/HEJ/doc/current/user/HEJFOG.html#settings}, the initialisation is followed by a calibration phase. We use a \lstinline!EventGenerator! to produce a number of trial events. We use these to calibrate the \lstinline!Unweighter! in its constructor and produce a first batch of partially unweighted events. This also allows us to estimate our unweighting efficiency. In the next step, we continue to generate events and potentially unweight them. Once the user-defined target number of events is reached, we adjust their weights according to the number of required trials. As in \HEJ 2 (see section~\ref{sec:processing}), we pass the final events to a number of \lstinline!HEJ::Analysis! objects and a \lstinline!HEJ::CombinedEventWriter!. \subsection{Event generation} \label{sec:evgen} Event generation is performed by the \lstinline!EventGenerator::gen_event! member function. We begin by generating a \lstinline!PhaseSpacePoint!. This is not to be confused with the resummation phase space points represented by \lstinline!HEJ::PhaseSpacePoint!! After jet clustering, we compute the leading-order matrix element (see section~\ref{sec:ME}) and pdf factors. The phase space point generation is performed in the \lstinline!PhaseSpacePoint! constructor. We first construct the user-defined number of $n_p$ partons (by default gluons) in \lstinline!PhaseSpacePoint::gen_LO_partons!. We use flat sampling in -rapidity and azimuthal angle. For the scalar transverse momenta, we -distinguish between two cases. By default, they are generated based on a -random variable $x_{p_\perp}$ according to +rapidity and azimuthal angle. The scalar transverse momenta is +generated based on a random variable $x_{p_\perp}$ according to \begin{equation} \label{eq:pt_sampling} -p_\perp = p_{\perp,\text{min}} + -\begin{cases} - p_{\perp,\text{par}} + p_\perp = p_{\perp,\text{min}} + + p_{\perp,\text{par}} \tan\left( x_{p_\perp} \arctan\left( \frac{p_{\perp,\text{max}} - p_{\perp,\text{min}}}{p_{\perp,\text{par}}} \right) - \right) -& y < y_\text{cut} - \\ - - \tilde{p}_{\perp,\text{par}}\log\left(1 - x_{p_\perp}\left[1 - - \exp\left(\frac{p_{\perp,\text{min}} - - p_{\perp,\text{max}}}{\tilde{p}_{\perp,\text{par}}}\right)\right]\right) -& y \geq y_\text{cut} -\end{cases}\,, + \right)\,, \end{equation} where $p_{\perp,\text{min}}$ is the minimum jet transverse momentum, $p_{\perp,\text{max}}$ is the maximum transverse parton momentum, -tentatively set to the beam energy, and $y_\text{cut}$, $p_{\perp,\text{par}}$ -and $\tilde{p}_{\perp,\text{par}}$ are generation parameters set to -heuristically determined values of -\begin{align} - y_\text{cut}&=3,\\ - p_{\perp,\text{par}}&=p_{\perp,\min}+\frac{n_p}{5}, \\ - \tilde{p}_{\perp,\text{par}}&=\frac{p_{\perp,\text{par}}}{1 + - 5(y-y_\text{cut})}. -\end{align} +tentatively set to the beam energy, and $p_{\perp,\text{par}}$ +is a generation parameter set to the heuristically determined value of +\begin{equation} + \label{eq:pt_par} + p_{\perp,\text{par}}=p_{\perp,\min}+\frac{n_p}{5}. +\end{equation} The problem with this generation is that the transverse momenta peak at the minimum transverse momentum required for fixed-order jets. However, if we use the generated events as input for \HEJ resummation, events with such soft transverse momenta hardly contribute, see section~\ref{sec:ptj_res}. To generate efficient input for resummation, there is the user option \texttt{peak pt}, which specifies the dominant transverse momentum for resummation jets. If this option is set, most jets will be generated as above, but with $p_{\perp,\text{min}}$ set to the peak transverse momentum $p_{\perp, \text{peak}}$. In addition, there is a small chance of around $2\%$ to generate softer jets. The heuristic ansatz for the transverse momentum distribution in the ``soft'' region is \begin{equation} \label{FO_pt_soft} \frac{\partial \sigma}{\partial p_\perp} \propto e^{n_p\frac{p_\perp- p_{\perp, \text{peak}}}{\bar{p}_\perp}}\,, \end{equation} where $n_p$ is the number of partons and $\bar{p}_\perp \approx 4\,$GeV. To achieve this distribution, we use \begin{equation} \label{eq:FO_pt_soft_sampling} p_\perp = p_{\perp, \text{peak}} + \bar{p}_\perp \frac{\log x_{p_\perp}}{n_p} \end{equation} and discard the phase space point if the parton is too soft, i.e. below the threshold for fixed-order jets. After ensuring that all partons form separate jets, we generate any potential colourless emissions. We then determine the incoming momenta and flavours in \lstinline!PhaseSpacePoint::reconstruct_incoming! and adjust the outgoing flavours to ensure an FKL configuration. Finally, we may reassign outgoing flavours to generate suppressed (for example unordered) configurations. \input{currents} \appendix \section{Continuous Integration} \label{sec:CI} Whenever you are implementing something new or fixed a bug, please also add a test for the new behaviour to \texttt{t/CMakeLists.txt} via \lstinline!add_test!. These test can be triggered by running \lstinline!make test! or \lstinline!ctest! after compiling. A typical test should be at most a few seconds, so it can be potentially run on each commit change by each developer. If you require a longer, more careful test, preferably on top of a small one, surround it with \begin{lstlisting}[caption={}] if(${TEST_ALL}) add_test( NAME t_feature COMMAND really_long_test ) endif() \end{lstlisting} Afterwards you can execute the longer tests with\footnote{No recompiling is needed, as long as only the \lstinline!add_test! command is guarded, not the compiling commands itself.} \begin{lstlisting}[language=sh,caption={}] cmake base/directory -DTEST_ALL=TRUE make test \end{lstlisting} On top of that you should add \href{https://en.cppreference.com/w/cpp/error/assert}{\lstinline!assert!s} in the code itself. They are only executed when compiled with \lstinline!CMAKE_BUILD_TYPE=Debug!, without slowing down release code. So you can use them everywhere to test \textit{expected} or \textit{assumed} behaviour, e.g. requiring a Higgs boson or relying on rapidity ordering. GitLab provides ways to directly test code via \textit{Continuous integrations}. The CI is controlled by \texttt{.gitlab-ci.yml}. For all options for the YAML file see \href{https://docs.gitlab.com/ee/ci/yaml/}{docs.gitlab.com/ee/ci/yaml/}.https://gitlab.dur.scotgrid.ac.uk/hej/docold/tree/master/Theses GitLab also provides a small tool to check that YAML syntax is correct under \lstinline!CI/CD > Pipelines > CI Lint! or \href{https://gitlab.dur.scotgrid.ac.uk/hej/HEJ/-/ci/lint}{gitlab.dur.scotgrid.ac.uk/hej/HEJ/-/ci/lint}. Currently the CI is configured to trigger a \textit{Pipeline} on each \lstinline!git push!. The corresponding \textit{GitLab runners} are configured under \lstinline!CI/CD Settings>Runners! in the GitLab UI. All runners use a \href{https://www.docker.com/}{docker} image as virtual environments\footnote{To use only Docker runners set the \lstinline!docker! tag in \texttt{.gitlab-ci.yml}.}. The specific docker images maintained separately. If you add a new dependencies, please also provide a docker image for the CI. The goal to be able to test \HEJ with all possible configurations. Each pipeline contains multiple stages (see \lstinline!stages! in \texttt{.gitlab-ci.yml}) which are executed in order from top to bottom. Additionally each stage contains multiple jobs. For example the stage \lstinline!build! contains the jobs \lstinline!build:basic!, \lstinline!build:qcdloop!, \lstinline!build:rivet!, etc., which compile \HEJ for different environments and dependencies, by using different Docker images. Jobs starting with an dot are ignored by the Runner, e.g. \lstinline!.HEJ_build! is only used as a template, but never executed directly. Only after all jobs of the previous stage was executed without any error the next stage will start. To pass information between multiple stages we use \lstinline!artifacts!. The runner will automatically load all artifacts form all \lstinline!dependencies! for each job\footnote{If no dependencies are defined \textit{all} artifacts from all previous jobs are downloaded. Thus please specify an empty dependence if you do not want to load any artifacts.}. For example the compiled \HEJ code from \lstinline!build:basic! gets loaded in \lstinline!test:basic! and \lstinline!FOG:build:basic!, without recompiling \HEJ again. Additionally artifacts can be downloaded from the GitLab web page, which could be handy for debugging. We also trigger some jobs \lstinline!only! on specific events. For example we only push the code to \href{https://phab.hepforge.org/source/hej/repository/v2.0/}{HepForge} on release branches (e.g. v2.0). Also we only execute the \textit{long} tests for merge requests, on pushes for any release or the \lstinline!master! branch, or when triggered manually from the GitLab web page. The actual commands are given in the \lstinline!before_script!, \lstinline!script! and \lstinline!after_script! \footnote{\lstinline!after_script! is always executed} sections, and are standard Linux shell commands (dependent on the docker image). Any failed command, i.e. returning not zero, stops the job and making the pipeline fail entirely. Most tests are just running \lstinline!make test! or are based on it. Thus, to emphasise it again, write tests for your code in \lstinline!cmake!. The CI is only intended to make automated testing in different environments easier. \section{Monte Carlo uncertainty} \label{sec:MC_err} Since \HEJ is reweighting each Fixed Order point with multiple resummation events, the Monte Carlo uncertainty of \HEJ is a little bit more complicated then usual. We start by defining the \HEJ cross section after $N$ FO points \begin{align} \sigma_N:=\sum_{i}^N x_i \sum_{j}^{M_i} y_{i,j}=:\sum_i^N\sum_j^{M_i} w_{i,j}, \end{align} where $x_i$ are the FO weights\footnote{In this definition $x_i$ can be zero, see the discussion in the next section.}, $y_{i,j}$ are the reweighting weights , and $M_i$ the number of resummation points. We can set $M=M_i \forall i$ by potentially adding some points with $y_{i,j}=0$, i.e. $M$ correspond to the \lstinline!trials! in \lstinline!EventReweighter!. $w_{i,j}$ are the weights as written out by \HEJ. The expectation value of $\sigma$ is then \begin{align} \ev{\sigma_N}= \sum_i \ev{x_i}\sum_j\ev{y_{i,j}}=M \mu_x\sum_i\mu_{y_i},\label{eq:true_sigma} \end{align} with $\mu_{x/y}$ being the (true) mean value of $x$ or $y$, i.e. \begin{align} \mu_{x}:=\ev{\bar{x}}=\ev{\frac{\sum_i x_i}{N}}=\ev{x}. \end{align} The true underlying standard derivation on $\sigma_N$, assuming $\delta_{x}$ and $\delta_{y_i}$ are the standard derivations of $x$ and $y_i$ is \begin{align} \delta_{\sigma_N}^2&=M^2 \delta_{x}^2 \sum_i \mu_{y_i}^2 +M \mu_x^2 \sum_i \delta_{y_i}^2. \label{eq:true_err} \end{align} Notice that each point $i$ can have an different expectation for $y_i$. Since we do not know the true distribution of $x$ and $y$ we need to estimate it. We use the standard derivation \begin{align} \tilde{\delta}_{x_i}^2&:=\left(x_i-\bar x\right)^2 =\left(\frac{N-1}{N} x_i - \frac{\sum_{j\neq i} x_j}{N}\right)^2 \label{eq:err_x}\\ \tilde{\delta}_{y_{i,j}}^2&:=\left(y_{i,j}-\bar y_i\right)^2 \label{eq:err_y}, \end{align} and the mean values $\bar x$ and $\bar y$, to get an estimator for $\delta_{\sigma_N}$ \begin{align} \tilde\delta_{\sigma_N}^2&=M^2 \sum_i \tilde\delta_{x_i}^2 \bar{y_i}^2 +\sum_{i,j} x_i^2\tilde\delta_{y_{i,j}}^2. \label{eq:esti_err} \end{align} Trough error propagation we can connect the estimated uncertainties back to the fundamental ones \begin{align} \delta_{\tilde{\delta}_{x_i}}^2=\frac{N-1}{N} \delta_x^2. \end{align} Together with $\delta_x^2=\ev{x^2}-\ev{x}^2$ and $\ev{\tilde\delta}=0$ this leads to \begin{align} \ev{\tilde{\delta}_{x_i}^2 \bar y_i^2}&=\ev{\tilde{\delta}_{x_i} \bar y_i}^2 +\delta_{\tilde{\delta}_{x_i}}^2 \mu_{y_i}^2 +\delta_{y_i}^2 \mu_{\tilde\delta}^2 \\ &=\frac{N-1}{N} \delta_x^2\mu_{y_i}^2, \end{align} and a similar results for $y$. Therefore \begin{align} \ev{\delta_{\sigma_N}}=\frac{N-1}{N} M^2 \delta_{x}^2 \sum_i \mu_{y_i}^2 +\frac{M-1}{M} M \mu_x^2 \sum_i \delta_{y_i}^2, \end{align} where we can compensate for the additional factors compared to~\eqref{eq:true_err}, by replacing \begin{align} \tilde\delta_x&\to\frac{N}{N-1}\tilde\delta_x \label{eq:xcom_bias}\\ \tilde\delta_{y_i}&\to\frac{M}{M-1}\tilde\delta_{y_i}. \label{eq:ycom_bias} \end{align} Thus~\eqref{eq:esti_err} is an unbiased estimator of $\delta_{\sigma_N}$. \subsection{Number of events vs. number of trials} Even though the above calculation is completely valid, it is unpractical. Both $x_i$ and $y_{ij}$ could be zero, but zero weight events are typically not written out. In that sense $N$ and $M$ are the \textit{number of trials} it took to generate $N'$ and $M'$ (non-zero) events. We can not naively replace all $N$ and $M$ with $N'$ and $M'$ in the above equations, since this would also change the definition of the average $\bar x$ and $\bar y$. For illustration let us consider unweighted events, with all weights equal to $x'$, without changing the cross section $\sum_i^N x_i=\sum_i^{N'} x'_i=N' x'$. Then the average trial weight is unequal to the average event weight \begin{align} \bar x = \frac{\sum_i^{N} x_i}{N} = \frac{\sum_i^{N'} x'}{N}=x'\frac{N'}{N} \neq x'=\frac{\sum_i^{N'} x'}{N'}. \end{align} $N=N'$ would correspond to an $100\%$ efficient unweighting, i.e. a perfect sampling, where we know the analytical results. In particular using $N'$ instead of $N$ in the standard derivation gives \begin{align} \sum_i \left(x_i-\frac{\sum_i^{N} x_i}{N'}\right)^2=\sum_i \left(x'-x' \frac{\sum_i^{N'}}{N'}\right)^2=0, \end{align} which is obviously not true in general for $\tilde\delta^2_x$. Hence we would have to use the number of trials $N$ everywhere. This would require an additional parameter to be passed with each events, which is not always available in practice\footnote{ \texttt{Sherpa} gives the number of trials, as an \lstinline!attribute::trials! of \lstinline!HEPEUP! in the \texttt{LHE} file, or similarly as a data member in the HDF5 format \cite{Hoeche:2019rti}. The \texttt{LHE} standard itself provides the variable \lstinline!ntries! per event (see \href{https://phystev.cnrs.fr/wiki/2017:groups:tools:lhe}{this proposal}), though I have not seen this used anywhere.}. Instead we use \begin{align} \tilde\delta_{x}'^2:=\sum_i^{N} x_i^2\geq\tilde\delta_x^2, \label{eq:err_prac} \end{align} where the bias of $\delta_x'^2$ vanishes for large $N$. Thus we can use the sum of weight squares~\eqref{eq:err_prac} instead of~\eqref{eq:err_x} and~\eqref{eq:err_y}, without worrying about the difference between trials and generated events. The total error~\eqref{eq:esti_err} becomes \begin{align} \tilde\delta_{\sigma_N}^2=\sum_i \left(\sum_j w_{i,j}\right)^2+\sum_{i,j} \left(w_{i,j}\right)^2, \end{align} which (conveniently) only dependent on the \HEJ weights $w_{i,j}$. \section{Explicit formulas for vector currents} \label{sec:j_vec} Using eqs.~\eqref{eq:u_plus}\eqref{eq:u_minus}\eqref{eq:jminus_gen}\eqref{eq:Pauli_matrices}\eqref{eq:jplus_gen}, the vector currents read \begin{align} \label{eq:j-_explicit} j^-_\mu(p, q) ={}& \begin{pmatrix} \sqrt{p^+\,q^+} + \sqrt{p^-\,q^-} \hat{p}_{\perp} \hat{q}_{\perp}^*\\ \sqrt{p^-\,q^+}\, \hat{p}_{\perp} + \sqrt{p^+\,q^-}\,\hat{q}_{\perp}^*\\ -i \sqrt{p^-\,q^+}\, \hat{p}_{\perp} + i \sqrt{p^+\,q^-}\, \hat{q}_{\perp}^*\\ \sqrt{p^+\,q^+} - \sqrt{p^-\,q^-}\, \hat{p}_{\perp}\, \hat{q}_{\perp}^* \end{pmatrix}\,,\\ j^+_\mu(p, q) ={}&\big(j^-_\mu(p, q)\big)^*\,,\\ j^\pm_\mu(q, p) ={}&\big(j^\pm_\mu(p, q)\big)^*\,. \end{align} If $q= p_{\text{in}}$ is the momentum of an incoming parton, we have $\hat{p}_{\text{in} \perp} = -1$ and either $p_{\text{in}}^+ = 0$ or $p_{\text{in}}^- = 0$. The current simplifies further:\todo{Helicities flipped w.r.t code} \begin{align} \label{eq:j_explicit} j^-_\mu(p_{\text{out}}, p_{\text{in}}) ={}& \begin{pmatrix} \sqrt{p_{\text{in}}^+\,p_{\text{out}}^+}\\ \sqrt{p_{\text{in}}^+\,p_{\text{out}}^-} \ \hat{p}_{\text{out}\,\perp}\\ -i\,j^-_1\\ j^-_0 \end{pmatrix} & p_{\text{in}\,z} > 0\,,\\ j^-_\mu(p_{\text{out}}, p_{\text{in}}) ={}& \begin{pmatrix} -\sqrt{p_{\text{in}}^-\,p_{\text{out}}^{-\phantom{+}}} \ \hat{p}_{\text{out}\,\perp}\\ - \sqrt{p_{\text{in}}^-\,p_{\text{out}}^+}\\ i\,j^-_1\\ -j^-_0 \end{pmatrix} & p_{\text{in}\,z} < 0\,. \end{align} We also employ the usual short-hand notation For the gluon polarisation vectors with gluon momentum $p_g$ and auxiliary reference vector $p_r$ we use \begin{equation} \label{eq:pol_vector} \epsilon_\mu^+(p_g, p_r) = \frac{j_\mu^+(p_r, p_g)}{\sqrt{2}\spb g.r}\,,\qquad\epsilon_\mu^-(p_g, p_r) = \frac{j_\mu^-(p_r, p_g)}{\sqrt{2}\spa g.r}\,. \end{equation} \bibliographystyle{JHEP} \bibliography{biblio} \end{document} diff --git a/doc/developer_manual/figures/wuno1.pdf b/doc/developer_manual/figures/wuno1.pdf deleted file mode 100644 index 1110954..0000000 --- a/doc/developer_manual/figures/wuno1.pdf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2a13a926a7c6c3468fb09d6a968a4e0cf321cf26db28f276c1e8e2531bc4c0f6 -size 5247 diff --git a/doc/developer_manual/figures/wuno2.pdf b/doc/developer_manual/figures/wuno2.pdf deleted file mode 100644 index 5b1d742..0000000 --- a/doc/developer_manual/figures/wuno2.pdf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:38fefdfb069a43916677f110a35aa6e65e49820d1ef664067a6499a8ae33c68b -size 5489 diff --git a/doc/developer_manual/figures/wuno3.pdf b/doc/developer_manual/figures/wuno3.pdf deleted file mode 100644 index 4aad276..0000000 --- a/doc/developer_manual/figures/wuno3.pdf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:76b070e9ae86f2b71f3f8eb90c16814123d5656fbfa8caad5e6496163cccafc5 -size 5602 diff --git a/doc/developer_manual/figures/wuno4.pdf b/doc/developer_manual/figures/wuno4.pdf deleted file mode 100644 index 7fb6e3f..0000000 --- a/doc/developer_manual/figures/wuno4.pdf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5c7984ffecd1a52ad84bd4a1b89661e724d82996d49945ad3b6de4a70ebae136 -size 5594 diff --git a/doc/developer_manual/src/vuno1.asy b/doc/developer_manual/src/vuno1.asy new file mode 100644 index 0000000..3d07dde --- /dev/null +++ b/doc/developer_manual/src/vuno1.asy @@ -0,0 +1,15 @@ +import feynman; +usepackage("fourier"); + +currentpen = fontsize(9); + +real w = 50; +real h = 25; + +draw(Label("a", BeginPoint, W), (-w,h)--(0,h), MidArrow(5)); +draw(Label("2", EndPoint, E), (0,h)--(w,h), MidArrow(5)); +draw(Label("b", BeginPoint, W), (-w,-h)--(0,-h), MidArrow(5)); +draw(Label("3", EndPoint, E), (0,-h)--(w,-h), MidArrow(5)); +draw(gluon((0,-h)--(0,h))); +draw(Label("W, Z, $\gamma$", EndPoint,N), photon((-3w/4,h)--(-w/4,3h/2))); +draw(Label("1", EndPoint, N), gluon((w/4,h)--(3w/4,3h/2))); diff --git a/doc/developer_manual/src/vuno2.asy b/doc/developer_manual/src/vuno2.asy new file mode 100644 index 0000000..1606cfe --- /dev/null +++ b/doc/developer_manual/src/vuno2.asy @@ -0,0 +1,15 @@ +import feynman; +usepackage("fourier"); + +currentpen = fontsize(9); + +real w = 50; +real h = 25; + +draw(Label("a", BeginPoint, W), (-w,h)--(0,h), MidArrow(5)); +draw(Label("2", EndPoint, E), (0,h)--(w,h), MidArrow(5)); +draw(Label("b", BeginPoint, W), (-w,-h)--(0,-h), MidArrow(5)); +draw(Label("3", EndPoint, E), (0,-h)--(w,-h), MidArrow(5)); +draw(gluon((0,-h)--(0,h))); +draw(Label("W, Z, $\gamma$", EndPoint,N), photon((-3w/4,h)--(-w/4,3h/2))); +draw(Label("1", EndPoint, N), gluon((-w/4,h)--(3w/4,3h/2))); diff --git a/doc/developer_manual/src/vuno3.asy b/doc/developer_manual/src/vuno3.asy new file mode 100644 index 0000000..ab6561b --- /dev/null +++ b/doc/developer_manual/src/vuno3.asy @@ -0,0 +1,16 @@ +import feynman; +usepackage("fourier"); + +currentpen = fontsize(9); + +real w = 50; +real h = 25; + +draw(Label("a", BeginPoint, W), (-w,h)--(0,h), MidArrow(5)); +draw(Label("2", EndPoint, E), (0,h)--(w,h), MidArrow(5)); +draw(Label("b", BeginPoint, W), (-w,-h)--(0,-h), MidArrow(5)); +draw(Label("3", EndPoint, E), (0,-h)--(w,-h), MidArrow(5)); +draw(gluon((0,-h)--(0,0))); +draw(gluon((0,0)--(0,h))); +draw(Label("W, Z, $\gamma$", EndPoint,N), photon((-3w/4,h)--(-w/4,3h/2))); +draw(Label("1", EndPoint, N), gluon((0,0)--(3w/4,3h/2))); diff --git a/doc/developer_manual/src/vuno4.asy b/doc/developer_manual/src/vuno4.asy new file mode 100644 index 0000000..dd8c298 --- /dev/null +++ b/doc/developer_manual/src/vuno4.asy @@ -0,0 +1,15 @@ +import feynman; +usepackage("fourier"); + +currentpen = fontsize(9); + +real w = 50; +real h = 25; + +draw(Label("a", BeginPoint, W), (-w,h)--(0,h), MidArrow(5)); +draw(Label("2", EndPoint, E), (0,h)--(w,h), MidArrow(5)); +draw(Label("b", BeginPoint, W), (-w,-h)--(0,-h), MidArrow(5)); +draw(Label("3", EndPoint, E), (0,-h)--(w,-h), MidArrow(5)); +draw(gluon((0,-h)--(0,h))); +draw(Label("W, Z, $\gamma$", EndPoint,N), photon((-3w/4,h)--(-w/4,3h/2))); +draw(Label("1", EndPoint, N), gluon((w/4,-h)--(3w/4,3h/2))); diff --git a/doc/sphinx/HEJ.rst b/doc/sphinx/HEJ.rst index e46aee3..445e4e7 100644 --- a/doc/sphinx/HEJ.rst +++ b/doc/sphinx/HEJ.rst @@ -1,436 +1,436 @@ .. _`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 - -.. - - *TODO* Production of a Z boson or photon 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. .. _`max ext soft pt fraction`: **max ext soft pt fraction** Specifies the maximum fraction that soft radiation can contribute to the transverse momentum of each the most forward and the most backward jet. This setting is needed to regulate an otherwise uncancelled divergence. Values between around 0.05 and 0.1 are recommended. .. _`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" :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 qqx** 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 qqx** 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. .. _`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 `max ext soft pt fraction`_ 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/installation.rst b/doc/sphinx/installation.rst index 1da3c9e..c225964 100644 --- a/doc/sphinx/installation.rst +++ b/doc/sphinx/installation.rst @@ -1,103 +1,103 @@ .. _Installation: Installation ============ Download -------- A tar archive of the HEJ 2 source code can be downloaded and decompressed with the command:: curl https://hej.web.cern.ch/HEJ/downloads/HEJ_2.0.tar.gz | tar -xz To obtain the latest stable HEJ version, `HEJ_2.0.tar.gz` should be replaced by `HEJ.tar.gz`. Alternatively, the HEJ source code can be obtained by installing the `git version control system `_. and running: .. code-block:: sh git clone https://phab.hepforge.org/source/hej.git git submodule update --init # downloads FORM We also provide a `Docker image `_ -containing a HEJ 2 installation on. This image can be pulled with:: +containing a HEJ 2 installation. This image can be pulled with:: docker pull hejdock/hej When using the Docker image the remaining installation steps can be skipped. Prerequisites ------------- Before installing HEJ 2, you need the following programs and libraries: - `CMake `_ version 3.1 - A compiler supporting the C++14 standard, for example `gcc `_ 5 or later - `FastJet `_ -- `CLHEP `_ -- `LHAPDF `_ +- `CLHEP `_ version 2.3 +- `LHAPDF `_ version 6 - The `IOStreams` and `uBLAS` `boost `_ libraries - `yaml-cpp `_ - `autoconf` and `automake` for `FORM `_ In addition, some optional features have additional dependencies: - `Version 2 of QCDLoop `_ is required to include finite top mass corrections in Higgs boson + jets production. - `HepMC versions 2 and 3 `_ enable event output in the respective format. - `Rivet `_ together with HepMC 2 or 3 allow using Rivet analyses. - `HighFive `_ has to be installed in order to read and write event files in the `HDF5 `_-based format suggested in `arXiv:1905.05120 `_. Compilation ----------- To compile and install HEJ 2 run:: cmake source/directory -DCMAKE_INSTALL_PREFIX=target/directory make install :file:`source/directory` is the directory containing the file :file:`CMakeLists.txt`. If you omit :code:`-DCMAKE_INSTALL_PREFIX=target/directory` HEJ 2 will be installed to some default location. In case some of the aforementioned prerequisites are not found by :code:`cmake` you can give a hint by adding an additional argument :code:`-Dlibname_ROOT_DIR=/directory/with/library`, where :code:`libname` should be replaced by the name of the library in question. If :code:`cmake` fails to find (the correct) boost path, try setting :code:`-DBOOST_ROOT=/path/to/boost`, this will force :code:`cmake` to search for boost only in :code:`/path/to/boost`. To not include specific packages one can add :code:`-DEXCLUDE_packagename=TRUE` to :code:`cmake`, e.g. by setting :code:`-DEXCLUDE_rivet=TRUE` HEJ 2 will not be interfaced -to `rivet` even if rivet is available on the system. +to Rivet even if it is available on the system. Testing ------- To test your installation, download the NNPDF 2.3 PDF set with:: lhapdf install NNPDF23_nlo_as_0119 and run:: make test -The test data of HEJ are sorted in a +The test data of HEJ are stored in a `Git Large File Storage `_ format. :code:`git clone` therefore requires :code:`git-lfs` to download the data correctly. diff --git a/include/HEJ/BufferedEventReader.hh b/include/HEJ/BufferedEventReader.hh index 77a6231..97209b4 100644 --- a/include/HEJ/BufferedEventReader.hh +++ b/include/HEJ/BufferedEventReader.hh @@ -1,69 +1,69 @@ /** \file * \brief Event reader with internal buffer * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once +#include #include #include -#include #include #include #include #include "LHEF/LHEF.h" #include "HEJ/EventReader.hh" #include "HEJ/optional.hh" namespace HEJ { //! Event reader with internal buffer /** * Having a buffer makes it possible to put events back into the reader * so that they are read again */ class BufferedEventReader: public EventReader { public: explicit BufferedEventReader(std::unique_ptr reader): reader_{std::move(reader)} {} //! Read an event bool read_event() override; //! Access header text std::string const & header() const override { return reader_->header(); } //! Access run information LHEF::HEPRUP const & heprup() const override { return reader_->heprup(); } //! Access last read event LHEF::HEPEUP const & hepeup() const override { return cur_event_; } //! Guess number of events from header - HEJ::optional number_events() const override { + optional number_events() const override { return reader_->number_events(); } //! Push event back into reader template< class... T> void emplace(T&&... args) { buffer_.emplace(std::forward(args)...); } private: std::stack> buffer_; std::unique_ptr reader_; LHEF::HEPEUP cur_event_; }; } // namespace HEJ diff --git a/include/HEJ/CombinedEventWriter.hh b/include/HEJ/CombinedEventWriter.hh index 411639d..66ff8e6 100644 --- a/include/HEJ/CombinedEventWriter.hh +++ b/include/HEJ/CombinedEventWriter.hh @@ -1,45 +1,50 @@ /** \file * \brief Declares the CombinedEventWriter class * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/EventWriter.hh" namespace LHEF { - struct HEPRUP; + class HEPRUP; } namespace HEJ { class Event; struct OutputFile; //! Write event output to zero or more output files. class CombinedEventWriter: public EventWriter { public: //! Constructor /** * @param outfiles Specifies files output should be written to. * Each entry in the vector contains a file name * and output format. * @param heprup General process information */ CombinedEventWriter( std::vector const & outfiles, LHEF::HEPRUP const & heprup ); + ~CombinedEventWriter() override; //! Write one event to all output files - void write(Event const &) override; + void write(Event const & /*ev*/) override; + + //! Finish writing, e.g. write out general informations at end of run + //! @warning using the writer after finishing is not guaranteed to work + void finish() override; private: std::vector> writers_; }; -} +} // namespace HEJ diff --git a/include/HEJ/Config.hh b/include/HEJ/Config.hh index 70d64de..12ac088 100644 --- a/include/HEJ/Config.hh +++ b/include/HEJ/Config.hh @@ -1,234 +1,234 @@ /** \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/event_types.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" -#include "HEJ/ScaleFunction.hh" -namespace HEJ{ +namespace HEJ { //! Jet parameters struct JetParameters{ fastjet::JetDefinition def; /**< Jet Definition */ - double min_pt; /**< Minimum Jet Transverse Momentum */ + 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; + 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 max_ext_soft_pt_fraction instead. double min_extparton_pt = 0; //! Maximum transverse momentum fraction from soft radiation in extremal jets - Fraction max_ext_soft_pt_fraction; + Fraction max_ext_soft_pt_fraction{}; //! The regulator lambda for the subtraction terms double regulator_lambda = CLAMBDA; //! Number of resummation configurations to generate per fixed-order event - size_t trials; + 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; + 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 - HEJ::optional unweight_config; + optional unweight_config; }; //! Configuration options for the PhaseSpacePoint class struct PhaseSpacePointConfig { //! Properties of resummation jets JetParameters jet_param; //! Minimum transverse momentum for extremal partons //! \deprecated This will be removed in future versions. //! Use \ref max_ext_soft_pt_fraction instead. double min_extparton_pt = 0; //! Maximum transverse momentum fraction from soft radiation in extremal jets Fraction max_ext_soft_pt_fraction; }; //! 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; + 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 }; } /**! 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/Constants.hh b/include/HEJ/Constants.hh index 6faecce..11516e8 100644 --- a/include/HEJ/Constants.hh +++ b/include/HEJ/Constants.hh @@ -1,33 +1,33 @@ /** \file * \brief Header file defining all global constants used for HEJ * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once namespace HEJ { //! @name QCD parameters //! @{ constexpr double N_C = 3.; //!< number of Colours constexpr double C_A = N_C; //!< \f$C_A\f$ constexpr double C_F = (N_C*N_C - 1.)/(2.*N_C); //!< \f$C_F\f$ - constexpr double t_f = 0.5; //!< \f$t_f\f$ - constexpr double n_f = 5.; //!< number light flavours - constexpr double beta0 = 11./3.*C_A - 4./3.*t_f*n_f; //!< \f$\beta_0\f$ + constexpr double T_F = 0.5; //!< \f$t_f\f$ + constexpr double N_F = 5.; //!< number light flavours + constexpr double BETA0 = 11./3.*C_A - 4./3.*T_F*N_F; //!< \f$\beta_0\f$ //! @} //! @name Generation Parameters //! @{ //! @brief Default scale for virtual correction //! \f$\lambda\f$ cf. eq. (20) in \cite Andersen:2011hs constexpr double CLAMBDA = 0.2; constexpr double CMINPT = 0.2; //!< minimal \f$p_t\f$ of all partons //! @} //! @name Conventional Parameters //! @{ //! Value of first colour for colour dressing, according to LHE convention //! \cite Boos:2001cv constexpr int COLOUR_OFFSET = 501; //! @} -} +} // namespace HEJ diff --git a/include/HEJ/CrossSectionAccumulator.hh b/include/HEJ/CrossSectionAccumulator.hh index c5ca18f..a8589ea 100644 --- a/include/HEJ/CrossSectionAccumulator.hh +++ b/include/HEJ/CrossSectionAccumulator.hh @@ -1,99 +1,99 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include "HEJ/event_types.hh" namespace HEJ { class Event; //! Collection of Cross Section with its uncertainty template struct XSWithError { T value = T{}; //!< Cross Section T error = T{}; //!< Error }; /** * @brief Sum of Cross Section for different subproccess */ class CrossSectionAccumulator { public: //! Fill with single event //! @note for multiple resummation events use fill_correlated() instead void fill(Event const & ev); //! Fill by weight and type //! @note for multiple resummation events use fill_correlated() instead - void fill(double weight, event_type::EventType type); + void fill(double wt, event_type::EventType type); //! Fill by weight, error and type //! @note The error will be _added_ to the current error - void fill(double weight, double delta_error, event_type::EventType type); + void fill(double wt, double err, event_type::EventType type); /** * @brief Fill with multiple correlated weights * @details This should be used to fill multiple reweighted events, * coming from the same fixed order point. * Total error for \f$N\f$ fixed order points each giving \f$M_i\f$ * resummation events is: * \f[ * \delta^2=\sum_i \left(\sum_j w_{i,j}\right)^2 * +\sum_{i,j} \left(w_{i,j}\right)^2, * \f] * @note This is equivalent to fill() for only one reweighted event * coming from each fixed order point (\f$M_i=1\f$) */ void fill_correlated(std::vector const & evts); //! iterator implementation of fill_correlated() template void fill_correlated(ConstIt begin, ConstIt end); //! explicit version of fill_correlated() by giving sum(wt) and sum(wt^2) void fill_correlated(double sum, double sum2, event_type::EventType type); //! begin of Cross Section and error for subprocesses auto begin() const { return std::begin(xs_); } //! end of Cross Section and error for subprocesses auto end() const { return std::end(xs_); } //! total Cross Section and error XSWithError const & total() const { return total_; } private: - std::map> xs_; + std::map> xs_; XSWithError total_; }; //! Print CrossSectionAccumulator to stream std::ostream& operator<<(std::ostream& os, const CrossSectionAccumulator& xs); // ------------ Implementation ------------ template void CrossSectionAccumulator::fill_correlated(ConstIt begin, ConstIt end){ if(std::distance(begin, end) < 2){ // only one event fill(*begin); return; } double sum = 0.; double sum2 = 0.; const auto type = begin->type(); for(; begin != end; ++begin){ double const wt = begin->central().weight; sum += wt; sum2 += wt*wt; } fill_correlated(sum, sum2, type); } } // namespace HEJ diff --git a/include/HEJ/EWConstants.hh b/include/HEJ/EWConstants.hh index 91f3703..2542658 100644 --- a/include/HEJ/EWConstants.hh +++ b/include/HEJ/EWConstants.hh @@ -1,90 +1,90 @@ /** \file * \brief Defines the electro weak parameters * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include -#include "HEJ/exceptions.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/exceptions.hh" namespace HEJ { //! collection of basic particle properties struct ParticleProperties { double mass; //!< Mass double width; //!< Decay width }; //! Collection of electro-weak constants class EWConstants { public: EWConstants() = default; //! initialise by Vacuum expectation value & boson properties EWConstants( double vev, //!< vacuum expectation value ParticleProperties const & Wprop, //!< W boson mass & width ParticleProperties const & Zprop, //!< Z boson mass & width ParticleProperties const & Hprop //!< Higgs boson mass & width - ): set{true}, vev_{vev}, Wprop_{Wprop}, Zprop_{Zprop}, Hprop_{Hprop} {} + ): set_{true}, vev_{vev}, Wprop_{Wprop}, Zprop_{Zprop}, Hprop_{Hprop} {} //! set constants by Vacuum expectation value & boson properties void set_vevWZH( double vev, //!< vacuum expectation value ParticleProperties const & Wprop, //!< W boson mass & width ParticleProperties const & Zprop, //!< Z boson mass & width ParticleProperties const & Hprop //!< Higgs boson mass & width ){ - set = true; vev_= vev; Wprop_= Wprop; Zprop_= Zprop; Hprop_= Hprop; + set_ = true; vev_= vev; Wprop_= Wprop; Zprop_= Zprop; Hprop_= Hprop; } //! vacuum expectation value double vev() const {check_set(); return vev_;} //! Properties of the W boson ParticleProperties const & Wprop() const {check_set(); return Wprop_;} //! Properties of the Z boson ParticleProperties const & Zprop() const {check_set(); return Zprop_;} //! Properties of the Higgs boson ParticleProperties const & Hprop() const {check_set(); return Hprop_;} //! access Properties by boson id ParticleProperties const & prop(ParticleID const id) const { using namespace pid; switch(id){ case Wp: case Wm: return Wprop(); case Z: return Zprop(); case h: return Hprop(); default: throw std::invalid_argument("No properties available for particle "+name(id)); } } //! cosine of Weinberg angle double cos_tw() const {return Wprop().mass/Zprop().mass;} //! cosine square of Weinberg angle double cos2_tw() const {return cos_tw()*cos_tw();} //! sinus Weinberg angle double sin_tw() const {return std::sqrt(sin2_tw());} //! sinus square of Weinberg angle double sin2_tw() const {return 1. - cos2_tw();} //! elector magnetic coupling double alpha_em() const {return e2()/4./M_PI;} //! weak coupling double alpha_w() const {return gw2()/2.;} private: double gw2() const {return 4*Wprop().mass/vev()*Wprop().mass/vev();} double e2() const {return gw2()*sin2_tw();} void check_set() const { - if(!set) throw std::invalid_argument("EW constants not specified"); + if(!set_) throw std::invalid_argument("EW constants not specified"); } - bool set{false}; - double vev_; - ParticleProperties Wprop_; - ParticleProperties Zprop_; - ParticleProperties Hprop_; + bool set_{false}; + double vev_{}; + ParticleProperties Wprop_{}; + ParticleProperties Zprop_{}; + ParticleProperties Hprop_{}; }; } // namespace HEJ diff --git a/include/HEJ/EmptyAnalysis.hh b/include/HEJ/EmptyAnalysis.hh index 5cdd8e3..e40d1e3 100644 --- a/include/HEJ/EmptyAnalysis.hh +++ b/include/HEJ/EmptyAnalysis.hh @@ -1,53 +1,53 @@ /** \file * \brief Declaration of the trivial (empty) analysis * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include "HEJ/Analysis.hh" namespace YAML { class Node; } namespace LHEF { class HEPRUP; } namespace HEJ { class Event; /** An analysis that does nothing * * This analysis is used by default if no user analysis is specified. * The member functions don't do anything and events passed to the * analysis are simply ignored. */ struct EmptyAnalysis: Analysis{ //! Create EmptyAnalysis static std::unique_ptr create( - YAML::Node const & parameters, LHEF::HEPRUP const &); + YAML::Node const & parameters, LHEF::HEPRUP const & /*unused*/); //! Fill event into analysis (e.g. to histograms) /** * This function does nothing */ - void fill(Event const &, Event const &) override; + void fill(Event const & /*res_event*/, Event const & /*FO_event*/) override; //! Whether a resummation event passes all cuts /** * There are no cuts, so all events pass */ - bool pass_cuts(Event const &, Event const &) override; + bool pass_cuts(Event const & /*res_event*/, Event const & /*FO_event*/) override; //! Finalise analysis /** * This function does nothing */ void finalise() override; ~EmptyAnalysis() override = default; }; } // namespace HEJ diff --git a/include/HEJ/Event.hh b/include/HEJ/Event.hh index 6f9e353..336f49b 100644 --- a/include/HEJ/Event.hh +++ b/include/HEJ/Event.hh @@ -1,373 +1,372 @@ /** \file * \brief Declares the Event class and helpers * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include #include #include #include "boost/iterator/filter_iterator.hpp" #include "fastjet/ClusterSequence.hh" #include "fastjet/PseudoJet.hh" #include "HEJ/EWConstants.hh" -#include "HEJ/event_types.hh" #include "HEJ/Parameters.hh" #include "HEJ/Particle.hh" +#include "HEJ/event_types.hh" namespace LHEF { class HEPEUP; class HEPRUP; } namespace fastjet { class JetDefinition; } namespace HEJ { struct RNG; struct UnclusteredEvent; /** @brief An event with clustered jets * * This is the main HEJ 2 event class. * It contains kinematic information including jet clustering, * parameter (e.g. scale) settings and the event weight. */ class Event { public: class EventData; //! Iterator over partons using ConstPartonIterator = boost::filter_iterator< bool (*)(Particle const &), std::vector::const_iterator >; //! Reverse Iterator over partons using ConstReversePartonIterator = std::reverse_iterator< ConstPartonIterator>; //! No default Constructor Event() = delete; //! Event Constructor adding jet clustering to an unclustered event //! @deprecated UnclusteredEvent will be replaced by EventData in HEJ 2.2.0 [[deprecated("UnclusteredEvent will be replaced by EventData")]] Event( UnclusteredEvent const & ev, fastjet::JetDefinition const & jet_def, double min_jet_pt ); //! @name Particle Access //! @{ //! Incoming particles std::array const & incoming() const{ return incoming_; } //! Outgoing particles std::vector const & outgoing() const{ return outgoing_; } //! 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; //! 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_; } //! The jets formed by the outgoing partons, sorted in rapidity std::vector const & jets() const{ return jets_; } //! @} //! @name Weight variations //! @{ //! All chosen parameter, i.e. scale choices (const version) Parameters const & parameters() const{ return parameters_; } //! All chosen parameter, i.e. scale choices Parameters & parameters(){ return parameters_; } //! Central parameter choice (const version) EventParameters const & central() const{ return parameters_.central; } //! Central parameter choice EventParameters & central(){ return parameters_.central; } //! Parameter (scale) variations (const version) std::vector const & variations() const{ return parameters_.variations; } //! Parameter (scale) variations std::vector & variations(){ return parameters_.variations; } //! Parameter (scale) variation (const version) /** * @param i Index of the requested variation */ EventParameters const & variations(std::size_t i) const{ return parameters_.variations.at(i); } //! Parameter (scale) variation /** * @param i Index of the requested variation */ EventParameters & variations(std::size_t i){ return parameters_.variations.at(i); } //! @} //! Indices of the jets the outgoing partons belong to /** * @param jets Jets to be tested * @returns A vector containing, for each outgoing parton, * the index in the vector of jets the considered parton * belongs to. If the parton is not inside any of the * passed jets, the corresponding index is set to -1. */ std::vector particle_jet_indices( std::vector const & jets ) const { return cs_.particle_jet_indices(jets); } //! particle_jet_indices() of the Event jets() std::vector particle_jet_indices() const { return particle_jet_indices(jets()); } //! Jet definition used for clustering fastjet::JetDefinition const & jet_def() const{ return cs_.jet_def(); } //! Minimum jet transverse momentum double min_jet_pt() const{ return min_jet_pt_; } //! Event type event_type::EventType type() const{ return type_; } //! Give colours to each particle /** * @returns true if new colours are generated, i.e. same as is_resummable() * @details Colour ordering is done according to leading colour in the MRK * limit, see \cite Andersen:2011zd. This only affects \ref * is_resummable() "HEJ" configurations, all other \ref event_type * "EventTypes" will be ignored. * @note This overwrites all previously set colours. */ - bool generate_colours(HEJ::RNG &); + bool generate_colours(RNG & /*ran*/); //! Check that current colours are leading in the high energy limit /** * @details Checks that the colour configuration can be split up in * multiple, rapidity ordered, non-overlapping ladders. Such * configurations are leading in the MRK limit, see * \cite Andersen:2011zd * * @note This is _not_ to be confused with \ref is_resummable(), however * for all resummable states it is possible to create a leading colour * configuration, see generate_colours() */ bool is_leading_colour() const; /** * @brief Check if given event could have been produced by HEJ * @details A HEJ state has to fulfil: * 1. type() has to be \ref is_resummable() "resummable" * 2. Soft radiation in the tagging jets contributes at most to * `max_ext_soft_pt_fraction` of the total jet \f$ p_\perp \f$ * * @note This is true for any resummed stated produced by the * EventReweighter or any \ref is_resummable() "resummable" Leading * Order state. * - * @param max_ext_soft_pt_fraction Maximum transverse momentum fraction from - * soft radiation in extremal jets - * @param min_extparton_pt Absolute minimal \f$ p_\perp \f$, - * \b deprecated use max_ext_soft_pt_fraction - * instead + * @param max_frac Maximum transverse momentum fraction from soft radiation + * in extremal jets + * @param min_pt Absolute minimal \f$ p_\perp \f$, + * \b deprecated use max_frac instead * @return True if this state could have been produced by HEJ */ bool valid_hej_state( - double max_ext_soft_pt_fraction, double min_extparton_pt = 0.) const; + double max_frac, double min_pt = 0.) const; private: //! \internal //! @brief Construct Event explicitly from input. /** This is only intended to be called from EventData. * * \warning The input is taken _as is_, sorting and classification has to be * done externally, i.e. by EventData */ Event( std::array && incoming, std::vector && outgoing, std::unordered_map> && decays, Parameters && parameters, fastjet::JetDefinition const & jet_def, - double const min_jet_pt + double min_jet_pt ); //! Iterator over partons (non-const) using PartonIterator = boost::filter_iterator< bool (*)(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(); std::array incoming_; std::vector outgoing_; std::unordered_map> decays_; std::vector jets_; Parameters parameters_; fastjet::ClusterSequence cs_; double min_jet_pt_; event_type::EventType type_; }; // end class Event //! Class to store general Event setup, i.e. Phase space and weights class Event::EventData { public: //! Default Constructor EventData() = default; //! Constructor from LesHouches event information EventData(LHEF::HEPEUP const & hepeup); //! Constructor with all values given EventData( std::array incoming, std::vector outgoing, std::unordered_map> decays, Parameters parameters ): incoming(std::move(incoming)), outgoing(std::move(outgoing)), decays(std::move(decays)), parameters(std::move(parameters)) {} //! Generate an Event from the stored EventData. /** * @details Do jet clustering and classification. * Use this to generate an Event. * * @note Calling this function destroys EventData * * @param jet_def Jet definition * @param min_jet_pt minimal \f$p_T\f$ for each jet * * @returns Full clustered and classified event. */ Event cluster( - fastjet::JetDefinition const & jet_def, double const min_jet_pt); + fastjet::JetDefinition const & jet_def, double min_jet_pt); //! Alias for cluster() Event operator()( fastjet::JetDefinition const & jet_def, double const min_jet_pt){ return cluster(jet_def, min_jet_pt); } //! Sort particles in rapidity void sort(); //! Reconstruct intermediate particles from final-state leptons /** * Final-state leptons are created from virtual photons, W, or Z bosons. * This function tries to reconstruct such intermediate bosons if they * are not part of the event record. */ void reconstruct_intermediate(EWConstants const & ew_parameters); //! Incoming particles std::array incoming; //! Outcoing particles std::vector outgoing; //! Particle decays in the format {outgoing index, decay products} std::unordered_map> decays; //! Parameters, e.g. scale or inital weight Parameters parameters; }; // end class EventData //! Print Event std::ostream& operator<<(std::ostream & os, Event const & ev); //! Square of the partonic centre-of-mass energy \f$\hat{s}\f$ double shat(Event const & ev); //! Convert an event to a LHEF::HEPEUP - LHEF::HEPEUP to_HEPEUP(Event const & event, LHEF::HEPRUP *); + LHEF::HEPEUP to_HEPEUP(Event const & event, LHEF::HEPRUP * /*heprup*/); // put deprecated warning at the end, so don't get the warning inside Event.hh, // additionally doxygen can not identify [[deprecated]] correctly struct [[deprecated("UnclusteredEvent will be replaced by EventData")]] UnclusteredEvent; //! An event before jet clustering //! @deprecated UnclusteredEvent will be replaced by EventData in HEJ 2.2.0 struct UnclusteredEvent{ //! Default Constructor UnclusteredEvent() = default; //! Constructor from LesHouches event information UnclusteredEvent(LHEF::HEPEUP const & hepeup); std::array incoming; /**< Incoming Particles */ std::vector outgoing; /**< Outgoing Particles */ //! Particle decays in the format {outgoing index, decay products} std::unordered_map> decays; //! Central parameter (e.g. scale) choice EventParameters central; std::vector variations; /**< For parameter variation */ }; } // namespace HEJ diff --git a/include/HEJ/EventReader.hh b/include/HEJ/EventReader.hh index f4952bf..61614ae 100644 --- a/include/HEJ/EventReader.hh +++ b/include/HEJ/EventReader.hh @@ -1,60 +1,60 @@ /** \file * \brief Header file for event reader interface * * This header defines an abstract base class for reading events from files. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "HEJ/optional.hh" namespace LHEF { class HEPEUP; class HEPRUP; } namespace HEJ { //! Abstract base class for reading events from files struct EventReader { //! Read an event virtual bool read_event() = 0; //! Access header text virtual std::string const & header() const = 0; //! Access run information virtual LHEF::HEPRUP const & heprup() const = 0; //! Access last read event virtual LHEF::HEPEUP const & hepeup() const = 0; //! Guess number of events from header - virtual HEJ::optional number_events() const { + virtual optional number_events() const { std::size_t start = header().rfind("Number of Events"); start = header().find_first_of("123456789", start); if(start == std::string::npos) { return {}; } const std::size_t end = header().find_first_not_of("0123456789", start); return std::stoi(header().substr(start, end - start)); } virtual ~EventReader() = default; }; //! Factory function for event readers /** * @param filename The name of the input file * @returns A pointer to an instance of an EventReader * for the input file */ std::unique_ptr make_reader(std::string const & filename); } // namespace HEJ diff --git a/include/HEJ/EventReweighter.hh b/include/HEJ/EventReweighter.hh index 8593ffe..6b7c672 100644 --- a/include/HEJ/EventReweighter.hh +++ b/include/HEJ/EventReweighter.hh @@ -1,191 +1,191 @@ /** \file * \brief Declares the EventReweighter class * * EventReweighter is the main class used within HEJ 2. It reweights the * resummation events. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include #include "HEJ/Config.hh" -#include "HEJ/event_types.hh" #include "HEJ/MatrixElement.hh" -#include "HEJ/Parameters.hh" #include "HEJ/PDF.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/Parameters.hh" #include "HEJ/ScaleFunction.hh" #include "HEJ/StatusCode.hh" +#include "HEJ/event_types.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; - class RNG; + struct RNG; //! Beam parameters /** * Currently, only symmetric beams are supported, * so there is a single beam energy. */ struct Beam{ double E; /**< Beam energy */ std::array type; /**< Beam particles */ }; //! Main class for reweighting events in HEJ. class EventReweighter{ using EventType = event_type::EventType; public: EventReweighter( Beam const & beam, /**< Beam Energy */ int pdf_id, /**< PDF ID */ ScaleGenerator scale_gen, /**< Scale settings */ EventReweighterConfig conf, /**< Configuration parameters */ std::shared_ptr ran /**< Random number generator */ ); EventReweighter( LHEF::HEPRUP const & heprup, /**< LHEF event header */ ScaleGenerator scale_gen, /**< Scale settings */ EventReweighterConfig conf, /**< Configuration parameters */ std::shared_ptr ran /**< Random number generator */ ); //! Get the used pdf PDF const & pdf() const; //! Get event treatment EventTreatment treatment(EventType type) const; //! Generate resummation events for a given fixed-order event /** * @param ev Fixed-order event corresponding * to the resummation events * @param num_events Number of trial resummation configurations. * @returns A vector of resummation events. * * The result vector depends on the type of the input event and the * \ref EventTreatment of different types as specified in the constructor: * * - EventTreatment::reweight: The result vector contains between 0 and * num_events resummation events. * - EventTreatment::keep: If the input event passes the resummation * jet cuts the result vector contains one * event. Otherwise it is empty. * - EventTreatment::discard: The result vector is empty */ std::vector reweight( Event const & ev, std::size_t num_events ); //! Gives all StatusCodes of the last reweight() /** * Each StatusCode corresponds to one tried generation. Only good * StatusCodes generated an event. */ std::vector const & status() const { return status_; } private: /** \internal * \brief main generation/reweighting function: * generate phase space points and divide out Born factors */ std::vector gen_res_events( - Event const & ev, std::size_t num_events + Event const & ev, std::size_t phase_space_points ); std::vector rescale( Event const & Born_ev, std::vector events ) const; /** \internal * \brief Do the Jets pass the resummation Cuts? * * @param ev Event in Question * @returns 0 or 1 depending on if ev passes Jet Cuts */ bool jets_pass_resummation_cuts(Event const & ev) const; /** \internal * \brief pdf_factors Function * * @param ev Event in Question * @returns EventFactor due to PDFs * * Calculates the Central value and the variation due * to the PDF choice made. */ Weights pdf_factors(Event const & ev) const; /** \internal * \brief matrix_elements Function * * @param ev Event in question * @returns EventFactor due to MatrixElements * * Calculates the Central value and the variation due * to the Matrix Element. */ Weights matrix_elements(Event const & ev) const; /** \internal * \brief Scale-dependent part of fixed-order matrix element * * @param ev Event in question * @returns EventFactor scale variation due to FO-ME. * * This is only called to compute the scale variation for events where * we don't do resummation (e.g. non-FKL). * Since at tree level the scale dependence is just due to alpha_s, * it is enough to return the alpha_s(mur) factors in the matrix element. * The rest drops out in the ratio of (output event ME)/(input event ME), * so we never have to compute it. */ Weights fixed_order_scale_ME(Event const & ev) const; /** \internal * \brief Computes the tree level matrix element * * @param ev Event in Question * @returns HEJ approximation to Tree level Matrix Element * * This computes the HEJ approximation to the tree level FO * Matrix element which is used within the LO weighting process. */ double tree_matrix_element(Event const & ev) const; //! \internal General parameters EventReweighterConfig param_; //! \internal Beam energy double E_beam_; //! \internal PDF PDF pdf_; //! \internal Object to calculate the square of the matrix element MatrixElement MEt2_; //! \internal Object to calculate event renormalisation and factorisation scales ScaleGenerator scale_gen_; //! \internal random number generator std::shared_ptr ran_; //! \internal StatusCode of each attempt std::vector status_; }; } // namespace HEJ diff --git a/include/HEJ/EventWriter.hh b/include/HEJ/EventWriter.hh index e858de2..c4b437e 100644 --- a/include/HEJ/EventWriter.hh +++ b/include/HEJ/EventWriter.hh @@ -1,21 +1,41 @@ /** \file * \brief Header file for the EventWriter interface. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once +#include + namespace HEJ { class Event; //! Pure abstract base class for event writers - struct EventWriter { + class EventWriter { + public: //! Write an event virtual void write(Event const &) = 0; + //! Finish writing, e.g. write out general informations at end of run + //! @warning using the writer after finishing is not guaranteed to work + virtual void finish(){finished_=true;}; virtual ~EventWriter() = default; + protected: + //! @brief If writer is not finished run finish() and abort on error + //! + //! @param writer writer that is expected to finish, i.e. `this` + //! @param name name for error message + //! + //! Used in the destructor of inherited EventWriter to force finish() + //! @warning When finish() failed this will abort the whole program! + void finish_or_abort( + EventWriter* writer, std::string const & name + ) const noexcept; + bool finished() const {return finished_;}; + private: + bool finished_ = false; }; -} +} // namespace HEJ diff --git a/include/HEJ/Fraction.hh b/include/HEJ/Fraction.hh index 358420d..1a76b5a 100644 --- a/include/HEJ/Fraction.hh +++ b/include/HEJ/Fraction.hh @@ -1,45 +1,45 @@ /** \file * \brief Header file for fractions, i.e. floating point numbers between 0 and 1. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #pragma once #include #include #include namespace HEJ { //! Class for floating point numbers between 0 and 1 template< class Real, typename = std::enable_if_t::value> > class Fraction { public: Fraction() = default; explicit Fraction(Real r): f_{r} { if(f_ < 0 || f_ > 1) { throw std::invalid_argument{ std::to_string(f_) + " is not between 0 and 1" }; } } Fraction & operator=(Real r) { f_ = Fraction{r}; return *this; } operator Real() const { return f_; } private: Real f_; }; -} +} // namespace HEJ diff --git a/include/HEJ/HDF5Reader.hh b/include/HEJ/HDF5Reader.hh index 25bcc3b..4133db6 100644 --- a/include/HEJ/HDF5Reader.hh +++ b/include/HEJ/HDF5Reader.hh @@ -1,51 +1,51 @@ /** \file * \brief Header file for reading events in the HDF5 event format. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once -#include #include +#include #include "HEJ/EventReader.hh" -namespace HEJ{ +namespace HEJ { //! Class for reading events from a file in the HDF5 file format /** * @details This format is specified in \cite Hoeche:2019rti. */ class HDF5Reader : public EventReader{ public: HDF5Reader() = delete; //! Contruct object reading from the given file explicit HDF5Reader(std::string const & filename); //! Read an event bool read_event() override; //! Access header text std::string const & header() const override; //! Access run information LHEF::HEPRUP const & heprup() const override; //! Access last read event LHEF::HEPEUP const & hepeup() const override; //! Get number of events - HEJ::optional number_events() const override; + optional number_events() const override; ~HDF5Reader() override; private: struct HDF5ReaderImpl; std::unique_ptr impl_; }; } // namespace HEJ diff --git a/include/HEJ/HDF5Writer.hh b/include/HEJ/HDF5Writer.hh index 917eed2..2843eca 100644 --- a/include/HEJ/HDF5Writer.hh +++ b/include/HEJ/HDF5Writer.hh @@ -1,54 +1,57 @@ /** \file * \brief Contains the EventWriter for HDF5 Output. * * The output format is specified in arXiv:1905.05120. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/EventWriter.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This is an event writer specifically for HDF5 output. /** * \internal Implementation note: This uses the pimpl ("pointer to * implementation") idiom. HDF5 support is optional. Without pimpl, * we would have to specify whether HDF5 is available via the * preprocessor whenever this header is included. We don't want to * burden users of the HEJ library (for example the HEJ fixed-order * generator) with those details */ class HDF5Writer: public EventWriter{ public: //! Constructor /** * @param file name of the output file * @param heprup general process information */ HDF5Writer(std::string const & file, LHEF::HEPRUP heprup); HDF5Writer() = delete; //! Write an event to the output file void write(Event const & ev) override; + //! Finish writing + void finish() override; + ~HDF5Writer() override; private: struct HDF5WriterImpl; std::unique_ptr impl_; }; } // namespace HEJ diff --git a/include/HEJ/HepMC2Interface.hh b/include/HEJ/HepMC2Interface.hh index e678c40..40c918f 100644 --- a/include/HEJ/HepMC2Interface.hh +++ b/include/HEJ/HepMC2Interface.hh @@ -1,78 +1,77 @@ /** \file * \brief Header file for the HepMC2Interface * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/PDG_codes.hh" namespace HepMC { class GenCrossSection; class GenEvent; } namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This class converts the Events into HepMC::GenEvents /** * \details The output is depended on the HepMC version HEJ is compiled with, * both HepMC 2 and HepMC 3 are supported. If HEJ 2 is compiled * without HepMC calling this interface will throw an error. * * This interface will also keep track of the cross section of all the events that * being fed into it. */ class HepMC2Interface{ public: - HepMC2Interface(LHEF::HEPRUP const &); - ~HepMC2Interface(); + HepMC2Interface(LHEF::HEPRUP const & /*heprup*/); /** * \brief main function to convert an event into HepMC::GenEvent * * \param event Event to convert * \param weight_index optional selection of specific weight * (negative value gives central weight) */ HepMC::GenEvent operator()(Event const & event, int weight_index = -1); /** * \brief Sets the central value from \p event to \p out_ev * * \param out_ev HepMC::GenEvent to write to * \param event Event to convert * \param weight_index optional selection of specific weight * (negative value gives "central") * * This overwrites the central value of \p out_ev. */ void set_central(HepMC::GenEvent & out_ev, Event const & event, int weight_index = -1); protected: /** * \brief initialise generic event infomrations (not central weights) * * \param event Event to convert */ HepMC::GenEvent init_event(Event const & event) const; private: - std::array beam_particle_; - std::array beam_energy_; + std::array beam_particle_{}; + std::array beam_energy_{}; std::size_t event_count_; double tot_weight_; double tot_weight2_; HepMC::GenCrossSection cross_section() const; }; } // namespace HEJ diff --git a/include/HEJ/HepMC3Interface.hh b/include/HEJ/HepMC3Interface.hh index 9ecdca5..94f821f 100644 --- a/include/HEJ/HepMC3Interface.hh +++ b/include/HEJ/HepMC3Interface.hh @@ -1,82 +1,89 @@ /** \file * \brief Header file for the HepMC3Interface * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "HEJ/PDG_codes.hh" namespace HepMC3 { class GenCrossSection; class GenEvent; class GenRunInfo; } namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This class converts the Events into HepMC3::GenEvents /** * \details The output is depended on the HepMC3 version HEJ is compiled with, * both HepMC3 2 and HepMC3 3 are supported. If HEJ 2 is compiled * without HepMC3 calling this interface will throw an error. * * This interface will also keep track of the cross section of all the events that * being fed into it. */ class HepMC3Interface{ public: - HepMC3Interface(LHEF::HEPRUP const &); + HepMC3Interface(LHEF::HEPRUP heprup); ~HepMC3Interface(); + HepMC3Interface & operator=(HepMC3Interface const & other) = default; + HepMC3Interface(HepMC3Interface const & other) = default; + HepMC3Interface & operator=(HepMC3Interface && other) = default; + HepMC3Interface(HepMC3Interface && other) = default; /** * \brief main function to convert an event into HepMC3::GenEvent * * \param event Event to convert * \param weight_index optional selection of specific weight * (negative value gives central weight) */ HepMC3::GenEvent operator()(Event const & event, int weight_index = -1); /** * \brief Sets the central value from \p event to \p out_ev * * \param out_ev HepMC3::GenEvent to write to * \param event Event to convert * \param weight_index optional selection of specific weight * (negative value gives "central") * * This overwrites the central value of \p out_ev. */ void set_central(HepMC3::GenEvent & out_ev, Event const & event, int weight_index = -1); //! Pointer to generic run informations - std::shared_ptr run_info; + std::shared_ptr run_info(){ + return run_info_; + } protected: /** * \brief initialise generic event infomrations (not central weights) * * \param event Event to convert */ HepMC3::GenEvent init_event(Event const & event) const; private: - std::array beam_particle_; - std::array beam_energy_; + std::array beam_particle_{}; + std::array beam_energy_{}; + std::shared_ptr run_info_; std::size_t event_count_; double tot_weight_; double tot_weight2_; std::shared_ptr xs_; }; } // namespace HEJ diff --git a/include/HEJ/HepMC3Writer.hh b/include/HEJ/HepMC3Writer.hh index 9fa6b53..05a6b88 100644 --- a/include/HEJ/HepMC3Writer.hh +++ b/include/HEJ/HepMC3Writer.hh @@ -1,52 +1,55 @@ /** \file * \brief Contains the EventWriter for HepMC3 Output. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/EventWriter.hh" namespace LHEF { class HEPRUP; } namespace HEJ { class Event; //! This is an event writer specifically for HepMC3 output. /** * \internal Implementation note: * This uses the pimpl ("pointer to implementation") idiom. * HepMC3 support is optional and the implementation depends on the * HepMC3 version. Without pimpl, we would have to specify the HepMC3 version * via the preprocessor whenever this header is included. We don't want to * burden users of the HEJ library (for example the HEJ fixed-order generator) * with those details */ class HepMC3Writer: public EventWriter{ public: //! Constructor /** * @param file name of the output file * @param heprup general process information */ HepMC3Writer(std::string const & file, LHEF::HEPRUP heprup); HepMC3Writer() = delete; //! Write an event to the output file void write(Event const & ev) override; + //! Finish writing + void finish() override; + ~HepMC3Writer() override; private: struct HepMC3WriterImpl; std::unique_ptr impl_; }; } // namespace HEJ diff --git a/include/HEJ/HiggsCouplingSettings.hh b/include/HEJ/HiggsCouplingSettings.hh index 1bcabb9..e754b17 100644 --- a/include/HEJ/HiggsCouplingSettings.hh +++ b/include/HEJ/HiggsCouplingSettings.hh @@ -1,24 +1,24 @@ /** \file * \brief Defines the settings for Higgs boson coupling to gluons * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include -namespace HEJ{ +namespace HEJ { //! Settings for Higgs boson coupling to gluons struct HiggsCouplingSettings{ //! Top quark mass double mt = std::numeric_limits::infinity(); //! Bottom quark mass double mb = 4.7; //! Whether to use impact factors bool use_impact_factors = true; //! Whether to include bottom quark effects bool include_bottom = false; }; -} +} // namespace HEJ diff --git a/include/HEJ/Hjets.hh b/include/HEJ/Hjets.hh index 086520b..bc87e04 100644 --- a/include/HEJ/Hjets.hh +++ b/include/HEJ/Hjets.hh @@ -1,412 +1,412 @@ /** * \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 H+Jets. * * This file contains all the H+Jet specific components to compute * the current contractions for valid HEJ processes, to form a full * H+Jets ME, currently one would have to use functions from the * jets.hh header also. We have FKL and also unordered components for * H+Jets. */ #pragma once #include "CLHEP/Vector/LorentzVector.h" namespace HEJ { namespace currents { - typedef CLHEP::HepLorentzVector HLV; + using HLV = CLHEP::HepLorentzVector; //! Square of gg->gg Higgs+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 - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for gg->gg * * g~p1 g~p2 - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if g is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if g is backward, qH1 is forward) */ double ME_H_gg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! Square of gq->gq Higgs+Jets Scattering Current with Higgs before Gluon /** * @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 * @param pH Momentum of Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contraction */ double ME_Houtside_gq(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pH, double mt, bool include_bottom, double mb, double vev); //! Square of qg->qg Higgs+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 - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qg->qg * * q~p1 g~p2 (i.e. ALWAYS p1 for quark, p2 for gluon) - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if g is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if g is backward, qH1 is forward) */ double ME_H_qg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarg->qbarg Higgs+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 - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarg->qbarg * * qbar~p1 g~p2 (i.e. ALWAYS p1 for anti-quark, p2 for gluon) - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if g is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if g is backward, qH1 is forward) */ double ME_H_qbarg(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qQ->qQ Higgs+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 - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQ->qQ * * q~p1 Q~p2 (i.e. ALWAYS p1 for quark, p2 for quark) - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if Q is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if Q is backward, qH1 is forward) */ double ME_H_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qQbar->qQbar Higgs+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 - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQ->qQ * * q~p1 Qbar~p2 (i.e. ALWAYS p1 for quark, p2 for anti-quark) - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if Qbar is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if Qbar is backward, qH1 is forward) */ double ME_H_qQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarQ->qbarQ Higgs+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 quark * @param p2in Momentum of intial state quark - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qbarQ->qbarQ * * qbar~p1 Q~p2 (i.e. ALWAYS p1 for anti-quark, p2 for quark) - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if Q is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if Q is backward, qH1 is forward) */ double ME_H_qbarQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! Square of qbarQbar->qbarQbar Higgs+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 - * @param q1 Momentum of t-channel propagator before Higgs + * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for * qbarQbar->qbarQbar * * qbar~p1 Qbar~p2 (i.e. ALWAYS p1 for anti-quark, p2 for anti-quark) - * should be called with q1 meant to be contracted with p2 in first part of - * vertex (i.e. if Qbar is backward, q1 is forward) + * should be called with qH1 meant to be contracted with p2 in first part of + * vertex (i.e. if Qbar is backward, qH1 is forward) */ double ME_H_qbarQbar(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, - HLV const & q1, HLV const & qH2, + HLV const & qH1, HLV const & qH2, double mt, bool include_bottom, double mb, double vev); //! @name Unordered backwards //! @{ //! Square of qbarQ->qbarQg Higgs+Jets Unordered b Scattering Current /** + * @param pg Momentum of unordered b gluon * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark - * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for * qbarQ->qbarQg * * This construction is taking rapidity order: p1out >> p2out > pg */ - double ME_H_unob_qbarQ(HLV const & p1out, HLV const & p1in, - HLV const & pg, HLV const & p2out, + 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); //! Square of qQ->qQg Higgs+Jets Unordered b Scattering Current /** + * @param pg Momentum of unordered b gluon * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark - * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for qQ->qQg * * This construction is taking rapidity order: p1out >> p2out > pg */ - double ME_H_unob_qQ(HLV const & p1out, HLV const & p1in, - HLV const & pg, HLV const & p2out, + 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); //! Square of qQbar->qQbarg Higgs+Jets Unordered b Scattering Current /** + * @param pg Momentum of unordered b gluon * @param p1out Momentum of final state quark * @param p1in Momentum of initial state quark - * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for * qQbar->qQbarg * * This construction is taking rapidity order: p1out >> p2out > pg */ - double ME_H_unob_qQbar(HLV const & p1out, HLV const & p1in, - HLV const & pg, HLV const & p2out, + 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); //! Square of qbarQbar->qbarQbarg Higgs+Jets Unordered b Scattering Current /** + * @param pg Momentum of unordered b gluon * @param p1out Momentum of final state anti-quark * @param p1in Momentum of initial state anti-quark - * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for * qbarQbar->qbarQbarg * * This construction is taking rapidity order: p1out >> p2out > pg */ - double ME_H_unob_qbarQbar(HLV const & p1out, HLV const & p1in, - HLV const & pg, HLV const & p2out, + 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); //! Square of gQbar->gQbarg Higgs+Jets Unordered b Scattering Current /** + * @param pg Momentum of unordered b gluon * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon - * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state anti-quark * @param p2in Momentum of intial state anti-quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for * gQbar->gQbarg * * This construction is taking rapidity order: p1out >> p2out > pg */ - double ME_H_unob_gQbar(HLV const & p1out, HLV const & p1in, - HLV const & pg, HLV const & p2out, + 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); //! Square of gQ->gQg Higgs+Jets Unordered b Scattering Current /** + * @param pg Momentum of unordered b gluon * @param p1out Momentum of final state gluon * @param p1in Momentum of initial state gluon - * @param pg Momentum of unordered b gluon * @param p2out Momentum of final state quark * @param p2in Momentum of intial state quark * @param qH1 Momentum of t-channel propagator before Higgs * @param qH2 Momentum of t-channel propagator after Higgs * @param mt Top quark mass * @param include_bottom Specifies whether bottom corrections are included * @param mb Bottom quark mass * @param vev Vacuum expectation value * @returns Square of the current contractions for gQ->gQg * * This construction is taking rapidity order: p1out >> p2out > pg */ - double ME_H_unob_gQ(HLV const & p1out, HLV const & p1in, - HLV const & pg, HLV const & p2out, + 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); //! @} //! @name impact factors for Higgs + jet //! @{ //! @brief Implements Eq. (4.22) in \cite DelDuca:2003ba with modifications to //! incoming plus momenta /** * @param p2 Momentum of Particle 2 * @param p1 Momentum of Particle 1 * @param pH Momentum of Higgs * @param vev Vacuum expectation value * @returns impact factor * * This gives the impact factor. First it determines whether this is the * case \f$p1p\sim php\gg p3p\f$ or the opposite */ double C2gHgm(HLV const & p2, HLV const & p1, HLV const & pH, double vev); //! @brief Implements Eq. (4.23) in \cite DelDuca:2003ba with modifications to //! incoming plus momenta /** * @param p2 Momentum of Particle 2 * @param p1 Momentum of Particle 1 * @param pH Momentum of Higgs * @param vev Vacuum expectation value * @returns impact factor * * This gives the impact factor. First it determines whether this is the * case \f$p1p\sim php\gg p3p\f$ or the opposite */ double C2gHgp(HLV const & p2, HLV const & p1, HLV const & pH, double vev); //! Implements Eq. (4.21) in \cite DelDuca:2003ba /** * @param p2 Momentum of Particle 2 * @param p1 Momentum of Particle 1 * @param pH Momentum of Higgs * @param vev Vacuum expectation value * @returns impact factor * * This gives the impact factor. First it determines whether this is the * case \f$p1p\sim php\gg p3p\f$ or the opposite * * @TODO remove this function is not used */ double C2qHqm(HLV const & p2, HLV const & p1, HLV const & pH, double vev); //! @} } // namespace currents } // namespace HEJ diff --git a/include/HEJ/JetSplitter.hh b/include/HEJ/JetSplitter.hh index 54deda8..de92459 100644 --- a/include/HEJ/JetSplitter.hh +++ b/include/HEJ/JetSplitter.hh @@ -1,67 +1,57 @@ /** * \file * \brief Declaration of the JetSplitter class * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include "fastjet/JetDefinition.hh" #include "fastjet/PseudoJet.hh" namespace HEJ { - class RNG; + struct RNG; //! Class to split jets into their constituents class JetSplitter { public: //! Wrapper for return values struct SplitResult { std::vector constituents; double weight; }; //! Constructor /** * @param jet_def Jet definition * @param min_pt Minimum jet transverse momentum */ JetSplitter(fastjet::JetDefinition jet_def, double min_pt); //! Split a get into constituents /** * @param j2split Jet to be split * @param ncons Number of constituents * @param ran Random number generator * @returns The constituent momenta together with the associated * weight */ SplitResult split( fastjet::PseudoJet const & j2split, int ncons, RNG & ran ) const; //! Maximum distance of constituents to jet axis - static constexpr double R_factor = 5./3.; + static constexpr double R_FACTOR = 5./3.; private: //! \internal split jet into two partons SplitResult Split2(fastjet::PseudoJet const & j2split, RNG & ran) const; - /** \internal - * @brief sample y-phi distance to jet pt axis for a jet splitting into two - * partons - * - * @param wt Multiplied by the weight of the sampling point - * @param ran Random number generator - * @returns The distance in units of the jet radius - */ - double sample_distance_2p(double & wt, RNG & ran) const; - double min_jet_pt_; fastjet::JetDefinition jet_def_; }; } // namespace HEJ diff --git a/include/HEJ/LesHouchesReader.hh b/include/HEJ/LesHouchesReader.hh index 8f11749..f5955f1 100644 --- a/include/HEJ/LesHouchesReader.hh +++ b/include/HEJ/LesHouchesReader.hh @@ -1,82 +1,85 @@ /** \file * \brief Header file for reading events in the Les Houches Event File format. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include "LHEF/LHEF.h" #include "HEJ/EventReader.hh" #include "HEJ/stream.hh" -namespace HEJ{ +namespace HEJ { //! Class for reading events from a file in the Les Houches Event File format class LesHouchesReader : public EventReader{ public: //! Contruct object reading from the given file explicit LesHouchesReader(std::string const & filename): stream_{filename}, reader_{stream_} { // always use the newest LHE version reader_.heprup.version = LHEF::HEPRUP().version; } //! Read an event bool read_event() override { return reader_.readEvent(); } //! Access header text std::string const & header() const override { return reader_.headerBlock; } //! Access run information LHEF::HEPRUP const & heprup() const override { return reader_.heprup; } //! Access last read event LHEF::HEPEUP const & hepeup() const override { return reader_.hepeup; } private: - HEJ::istream stream_; + istream stream_; + LHEF::Reader reader_; protected: //! Underlying reader - LHEF::Reader reader_; + LHEF::Reader & reader(){ + return reader_; + } }; /** * @brief Les Houches Event file reader for LHE files created by Sherpa * @details In Sherpa the cross section is given by * sum(weights)/(number of trials). This EventReader converts the * weights such that cross section=sum(weights) * @note Reading from a pipe is not possible! */ class SherpaLHEReader : public LesHouchesReader{ public: //! Inialise Reader for a Sherpa LHE file explicit SherpaLHEReader(std::string const & filename); bool read_event() override; - HEJ::optional number_events() const override { - return num_events; + optional number_events() const override { + return num_events_; } private: - double num_trials; - size_t num_events; + double num_trials_; + size_t num_events_; }; } // namespace HEJ diff --git a/include/HEJ/LesHouchesWriter.hh b/include/HEJ/LesHouchesWriter.hh index 375af43..2137dac 100644 --- a/include/HEJ/LesHouchesWriter.hh +++ b/include/HEJ/LesHouchesWriter.hh @@ -1,60 +1,63 @@ /** \file * \brief Contains the writer for LesHouches output * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "LHEF/LHEF.h" #include "HEJ/EventWriter.hh" -namespace HEJ{ +namespace HEJ { class Event; //! Class for writing events to a file in the Les Houches Event File format class LesHouchesWriter : public EventWriter{ public: //! Constructor /** * @param file Name of output file * @param heprup General process information */ LesHouchesWriter(std::string const & file, LHEF::HEPRUP heprup); LesHouchesWriter(LesHouchesWriter const & other) = delete; LesHouchesWriter & operator=(LesHouchesWriter const & other) = delete; /** @TODO in principle, this class should be movable * but that somehow(?) breaks the write member function */ LesHouchesWriter(LesHouchesWriter && other) = delete; LesHouchesWriter & operator=(LesHouchesWriter && other) = delete; ~LesHouchesWriter() override; //! Write an event to the file specified in the constructor void write(Event const & ev) override; + //! Dump general information & finish writing + void finish() override; + private: void write_init(){ writer_->init(); } void rewrite_init(); LHEF::HEPRUP & heprup(){ return writer_->heprup; } LHEF::HEPEUP & hepeup(){ return writer_->hepeup; } std::fstream out_; std::unique_ptr writer_; }; } // namespace HEJ diff --git a/include/HEJ/LorentzVector.hh b/include/HEJ/LorentzVector.hh index bae214d..afef8fa 100644 --- a/include/HEJ/LorentzVector.hh +++ b/include/HEJ/LorentzVector.hh @@ -1,56 +1,56 @@ /** \file * \brief Auxiliary functions for Lorentz vectors * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "CLHEP/Vector/LorentzVector.h" namespace HEJ { //! "dot" product inline auto dot( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ) { return pi.dot(pj); } //! "angle" product angle(pi, pj) = \ std::complex angle( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ); //! "square" product square(pi, pj) = [i j] std::complex square( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ); //! Invariant mass inline auto m2(CLHEP::HepLorentzVector const & h1) { return h1.m2(); } //! Split a single Lorentz vector into two lightlike Lorentz vectors /** * @param P Lorentz vector to be split * @returns A pair (p, q) of Lorentz vectors with P = p + q and p^2 = q^2 = 0 * * P.perp() has to be positive. * * p.e() is guaranteed to be positive. * In addition, if either of P.plus() or P.minus() is positive, * q.e() has the same sign as P.m2() */ std::pair split_into_lightlike(CLHEP::HepLorentzVector const & P); -} +} // namespace HEJ diff --git a/include/HEJ/MatrixElement.hh b/include/HEJ/MatrixElement.hh index 972abb6..d999c18 100644 --- a/include/HEJ/MatrixElement.hh +++ b/include/HEJ/MatrixElement.hh @@ -1,203 +1,207 @@ /** \file * \brief Contains the MatrixElement Class * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "fastjet/PseudoJet.hh" #include "HEJ/Config.hh" -#include "HEJ/Parameters.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/Parameters.hh" namespace CLHEP { class HepLorentzVector; } -namespace HEJ{ +namespace HEJ { class Event; - class Particle; + struct Particle; //! Class to calculate the squares of matrix elements class MatrixElement{ public: /** \brief MatrixElement Constructor * @param alpha_s Function taking the renormalisation scale * and returning the strong coupling constant * @param conf General matrix element settings */ MatrixElement( std::function alpha_s, MatrixElementConfig conf ); /** * \brief squares of regulated HEJ matrix elements * @param event The event for which to calculate matrix elements * @returns The squares of HEJ matrix elements including virtual corrections * * This function returns one value for the central parameter choice * and one additional value for each entry in \ref Event.variations(). * See eq. (22) in \cite Andersen:2011hs for the definition of the squared * matrix element. * * \internal Relation to standard HEJ Met2: MatrixElement = Met2*shat^2/(pdfta*pdftb) */ Weights operator()(Event const & event) const; //! Squares of HEJ tree-level matrix elements /** * @param event The event for which to calculate matrix elements * @returns The squares of HEJ matrix elements without virtual corrections * * cf. eq. (22) in \cite Andersen:2011hs */ Weights tree(Event const & event) const; /** * \brief Virtual corrections to matrix element squares * @param event The event for which to calculate matrix elements * @returns The virtual corrections to the squares of the matrix elements * * The all order virtual corrections to LL in the MRK limit is * given by replacing 1/t in the scattering amplitude according to the * lipatov ansatz. * + * If there is more than one entry in the returned vector, each entry + * corresponds to the contribution from the interference of two + * channels. The order of these entries matches the one returned by + * the tree_kin member function, but is otherwise unspecified. + * * cf. second-to-last line of eq. (22) in \cite Andersen:2011hs * note that indices are off by one, i.e. out[0].p corresponds to p_1 */ std::vector virtual_corrections(Event const & event) const; /** * \brief Scale-dependent part of tree-level matrix element squares * @param event The event for which to calculate matrix elements * @returns The scale-dependent part of the squares of the * tree-level matrix elements * * The tree-level matrix elements factorises into a renormalisation-scale * dependent part, given by the strong coupling to some power, and a * scale-independent remainder. This function only returns the former parts * for the central scale choice and all \ref Event.variations(). * * @see tree, tree_kin */ Weights tree_param(Event const & event) const; /** * \brief Kinematic part of tree-level matrix element squares * @param event The event for which to calculate matrix elements * @returns The kinematic part of the squares of the * tree-level matrix elements * * The tree-level matrix elements factorises into a renormalisation-scale * dependent part, given by the strong coupling to some power, and a * scale-independent remainder. This function only returns the latter part. - * Since it does not depend on the parameter variations, only a single value - * is returned. + * + * If there is more than one entry in the returned vector, each entry + * corresponds to the contribution from the interference of two + * channels. The order of these entries matches the one returned by + * the virtual_corrections member function, but is otherwise unspecified. * * @see tree, tree_param */ std::vector tree_kin(Event const & event) const; private: double tree_param( Event const & event, double mur ) const; double virtual_corrections_W( Event const & event, double mur, Particle const & WBoson ) const; std::vector virtual_corrections_Z_qq( Event const & event, double mur, Particle const & ZBoson ) const; double virtual_corrections_Z_qg( Event const & event, double mur, Particle const & ZBoson, - bool swap_momenta + bool is_gq_event ) const; std::vector virtual_corrections( Event const & event, double mur ) const; //! \internal cf. last line of eq. (22) in \cite Andersen:2011hs double omega0( double alpha_s, double mur, fastjet::PseudoJet const & q_j ) const; double tree_kin_jets( Event const & ev ) const; double tree_kin_W( Event const & ev ) const; double tree_kin_WW( Event const & ev ) const; std::vector tree_kin_Z( Event const & ev ) const; double tree_kin_Higgs( Event const & ev ) const; double tree_kin_Higgs_first( Event const & ev ) const; double tree_kin_Higgs_last( Event const & ev ) const; /** * \internal * \brief Higgs inbetween extremal partons. * * Note that in the case of unordered emission, the Higgs is *always* * treated as if in between the extremal (FKL) partons, even if its * rapidity is outside the extremal parton rapidities */ double tree_kin_Higgs_between( Event const & ev ) const; double tree_param_partons( double alpha_s, double mur, std::vector const & partons ) const; std::vector in_extremal_jet_indices( std::vector const & partons ) const; - std::vector tag_extremal_jet_partons( - Event const & ev - ) const; - double MH2_forwardH( CLHEP::HepLorentzVector const & p1out, CLHEP::HepLorentzVector const & p1in, pid::ParticleID type2, CLHEP::HepLorentzVector const & p2out, CLHEP::HepLorentzVector const & p2in, CLHEP::HepLorentzVector const & pH, double t1, double t2 ) const; std::function alpha_s_; MatrixElementConfig param_; }; } // namespace HEJ diff --git a/include/HEJ/Mixmax.hh b/include/HEJ/Mixmax.hh index 029677e..a18abea 100644 --- a/include/HEJ/Mixmax.hh +++ b/include/HEJ/Mixmax.hh @@ -1,36 +1,36 @@ /** \file * \brief The Mixmax random number generator * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include "CLHEP/Random/MixMaxRng.h" #include "CLHEP/Random/Randomize.h" #include "HEJ/RNG.hh" namespace HEJ { //! MIXMAX random number generator /** * For details on MIXMAX, see \cite Savvidy:2014ana */ class Mixmax : public DefaultRNG { public: Mixmax() = default; //! Constructor with explicit seed Mixmax(long seed): ran_{seed} {} //! Generate pseudorandom number between 0 and 1 double flat() override { return ran_.flat(); } private: CLHEP::MixMaxRng ran_; }; -} +} // namespace HEJ diff --git a/include/HEJ/PDF.hh b/include/HEJ/PDF.hh index 57bb704..adbc025 100644 --- a/include/HEJ/PDF.hh +++ b/include/HEJ/PDF.hh @@ -1,76 +1,81 @@ /** \file * * \brief Contains all the necessary classes and functions for interaction with PDFs. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include -#include "LHAPDF/LHAPDF.h" - #include "HEJ/PDG_codes.hh" -namespace HEJ{ +namespace LHAPDF { + class PDF; +} + +namespace HEJ { //! Class for interaction with a PDF set class PDF { public: /** * \brief PDF Constructor * @param id Particle ID according to PDG * @param beam1 Particle ID of particle in beam 1 * @param beam2 Particle ID of particle in beam 2 */ PDF(int id, ParticleID beam1, ParticleID beam2); /** * \brief Calculate the pdf value x*f(x, q) * @param beam_idx Beam number (0 or 1) * @param x Momentum fraction * @param q Energy scale * @param id PDG particle id * @returns x*f(x, q) * * Returns 0 if x or q are outside the range covered by the PDF set */ double pdfpt(std::size_t beam_idx, double x, double q, ParticleID id) const; /** * \brief Value of the strong coupling \f$\alpha_s(q)\f$ at the given scale * @param q Renormalisation scale * @returns Value of the strong coupling constant */ double Halphas(double q) const; //! Check if the energy scale is within the range covered by the PDF set /** * @param q Energy Scale * @returns true if q is within the covered range, false otherwise */ bool inRangeQ(double q) const; //! Check if the momentum fraction is within the range covered by the PDF set /** * @param x Momentum Fraction * @returns true if x is within the covered range, false otherwise */ bool inRangeX(double x) const; -#if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6 //! PDF id of the current set int id() const; -#endif + + PDF(PDF const & other) = delete; //!< Copy not possible + PDF & operator=(PDF const & other) = delete; //!< Copy not possible + PDF(PDF && other) = default; //!< Moving allowed + PDF & operator=(PDF && other) = default; //!< Moving allowed + ~PDF(); private: -#if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6 - std::unique_ptr pdf; -#endif + //! @internal unique_ptr does not allow copy + std::unique_ptr pdf_; - std::array beamtype; + std::array beamtype_; }; } // namespace HEJ diff --git a/include/HEJ/PDG_codes.hh b/include/HEJ/PDG_codes.hh index cb6954e..0fd58b0 100644 --- a/include/HEJ/PDG_codes.hh +++ b/include/HEJ/PDG_codes.hh @@ -1,236 +1,238 @@ /** \file PDG_codes.hh * \brief Contains the Particle IDs of all relevant SM particles. * * Large enumeration included which has multiple entries for potential * alternative names of different particles. There are also functions * which can be used to determine if a particle is a parton or if * it is a non-gluon boson. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once -#include #include +#include namespace HEJ { //! particle ids according to PDG namespace pid { //! The possible particle identities. We use PDG IDs as standard. enum ParticleID: int{ + //! Unspecified type, should never be used!, debug only + unspecified = 0, d = 1, /*!< Down Quark */ down = d, /*!< Down Quark */ u = 2, /*!< Up Quark */ up = u, /*!< Up Quark */ s = 3, /*!< Strange Quark */ strange = s, /*!< Strange Quark */ c = 4, /*!< Charm Quark */ charm = c, /*!< Charm Quark */ b = 5, /*!< Bottom Quark */ bottom = b, /*!< Bottom Quark */ t = 6, /*!< Top Quark */ top = t, /*!< Top Quark */ e = 11, /*!< Electron */ electron = e, /*!< Electron */ nu_e = 12, /*!< Electron Neutrino */ electron_neutrino = nu_e, /*!< Electron neutrino */ mu = 13, /*!< Muon */ muon = mu, /*!< Muon */ nu_mu = 14, /*!< Muon Neutrino */ muon_neutrino = nu_mu, /*!< Muon Neutrino */ tau = 15, /*!< Tau */ nu_tau = 16, /*!< Tau Neutrino */ tau_neutrino = nu_tau, /*!< Tau Neutrino */ d_bar = -d, /*!< Anti-Down Quark */ antidown = d_bar, /*!< Anti-Down Quark */ u_bar = -u, /*!< Anti-Up quark */ antiup = -u, /*!< Anti-Up quark */ s_bar = -s, /*!< Anti-Strange Quark */ antistrange = -s, /*!< Anti-Strange Quark */ c_bar = -c, /*!< Anti-Charm Quark */ anticharm = -c, /*!< Anti-Charm Quark */ b_bar = -b, /*!< Anti-Bottom Quark */ antibottom = -b, /*!< Anti-Bottom Quark */ t_bar = -t, /*!< Anti-Top Quark */ antitop = -t, /*!< Anti-Top Quark */ e_bar = -e, /*!< Positron */ positron = e_bar, /*!< Positron */ antielectron = positron, /*!< Positron */ nu_e_bar = -nu_e, /*!< Electron Anti-Neutrino */ electron_antineutrino = nu_e_bar,/*!< Electron Anti-Neutrino */ mu_bar = -mu, /*!< Anti-Muon */ antimuon = -mu, /*!< Anti-Muon */ nu_mu_bar = -nu_mu, /*!< Muon Anti-Neutrino */ muon_antineutrino = nu_mu_bar, /*!< Muon Anti-Neutrino */ tau_bar = -tau, /*!< Anti-Tau */ antitau = tau_bar, /*!< Anti-Tau */ nu_tau_bar = -nu_tau, /*!< Tau Anti-Neutrino */ tau_antineutrino = nu_tau_bar, /*!< Tau Anti-Neutrino */ gluon = 21, /*!< Gluon */ g = gluon, /*!< Gluon */ photon = 22, /*!< Photon */ gamma = photon, /*!< Photon */ Z = 23, /*!< Z Boson */ Z_photon_mix = 81, /*!< Z/photon superposition */ Z_gamma_mix = Z_photon_mix, /*!< Z/photon superposition */ Wp = 24, /*!< W- Boson */ Wm = -Wp, /*!< W+ Boson */ h = 25, /*!< Higgs Boson */ Higgs = h, /*!< Higgs Boson */ higgs = h, /*!< Higgs Boson */ p = 2212, /*!< Proton */ proton = p, /*!< Proton */ p_bar = -p, /*!< Anti-Proton */ antiproton = p_bar, /*!< Anti-Proton */ }; //! Get the of the particle with the given PDG ID std::string name(ParticleID id); } // namespace pid using ParticleID = pid::ParticleID; //! Convert a particle name to the corresponding PDG particle ID ParticleID to_ParticleID(std::string const & name); /** * \brief Function to determine if particle is a quark * @param id PDG ID of particle * @returns true if the particle is a quark, false otherwise */ inline constexpr bool is_quark(ParticleID id){ return (id >= pid::down && id <= pid::top); } /** * \brief Function to determine if particle is an antiquark * @param id PDG ID of particle * @returns true if the particle is an antiquark, false otherwise */ inline constexpr bool is_antiquark(ParticleID id){ return (id <= pid::d_bar && id >= pid::t_bar); } /** * \brief Function to determine if particle is an (anti-)quark * @param id PDG ID of particle * @returns true if the particle is a quark or antiquark, false otherwise */ inline constexpr bool is_anyquark(ParticleID id){ return is_quark(id) || is_antiquark(id); } /** * \brief Function to determine if particle is a gluon * @param id PDG ID of particle * @returns true if the particle is a gluon, false otherwise */ inline constexpr bool is_gluon(ParticleID id){ return id == pid::gluon; } /** * \brief Function to determine if particle is a parton * @param id PDG ID of particle * @returns true if the particle is a parton, false otherwise */ inline constexpr bool is_parton(ParticleID id){ return is_gluon(id) || (is_anyquark(id) && std::abs(id) != pid::top); } /** * \brief function to determine if the particle is a photon, W or Z * @param id PDG ID of particle * @returns true if the partice is an A,W,Z, or H, false otherwise */ inline constexpr bool is_AWZ_boson(ParticleID id){ return id == pid::Wm || (id >= pid::photon && id <= pid::Wp) || id == pid::Z_photon_mix; } /** * \brief function to determine if the particle is a photon, W, Z, or Higgs * boson * @param id PDG ID of particle * @returns true if the partice is an A,W,Z, or H, false otherwise */ inline constexpr bool is_AWZH_boson(ParticleID id){ return is_AWZ_boson(id) || (id == pid::Higgs); } /** * \brief Function to determine if particle is a lepton * @param id PDG ID of particle * @returns true if the particle is a lepton, false otherwise */ inline constexpr bool is_lepton(ParticleID id){ return (id >= pid::electron && id <= pid::tau_neutrino); } /** * \brief Function to determine if particle is an antilepton * @param id PDG ID of particle * @returns true if the particle is an antilepton, false otherwise */ inline constexpr bool is_antilepton(ParticleID id){ return (id <= pid::positron && id >= pid::nu_tau_bar); } /** * \brief Function to determine if particle is an (anti-)lepton * @param id PDG ID of particle * @returns true if the particle is a lepton or antilepton, * false otherwise */ inline constexpr bool is_anylepton(ParticleID id){ return ( is_lepton(id) || is_antilepton(id)); } /** * \brief Function to determine if particle is a neutrino * @param id PDG ID of particle * @returns true if the particle is a neutrino, false otherwise */ inline constexpr bool is_neutrino(ParticleID id){ return (id == pid::nu_e || id == pid::tau_neutrino || id == pid::muon_neutrino); } /** * \brief Function to determine if particle is an antineutrino * @param id PDG ID of particle * @returns true if the particle is an antineutrino, false otherwise */ inline constexpr bool is_antineutrino(ParticleID id){ return (id == pid::nu_e_bar || id == pid::nu_tau_bar || id == pid::nu_mu_bar); } /** * \brief Function to determine if particle is an (anti-)neutrino * @param id PDG ID of particle * @returns true if the particle is a neutrino or antineutrino, * false otherwise */ inline constexpr bool is_anyneutrino(ParticleID id){ return ( is_neutrino(id) || is_antineutrino(id)); } } // namespace HEJ diff --git a/include/HEJ/Parameters.hh b/include/HEJ/Parameters.hh index f9a91f1..154f2a5 100644 --- a/include/HEJ/Parameters.hh +++ b/include/HEJ/Parameters.hh @@ -1,216 +1,216 @@ /** \file * \brief Containers for Parameter variations, e.g. different Weights * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include #include "HEJ/exceptions.hh" -namespace HEJ{ +namespace HEJ { //! Collection of parameters, e.g. Weights, assigned to a single event /** * A number of member functions of the MatrixElement class return Parameters * objects containing the squares of the matrix elements for the various * scale choices. */ template struct Parameters { - T central; + T central{}; std::vector variations; template Parameters& operator*=(Parameters const & other); Parameters& operator*=(double factor); template Parameters& operator/=(Parameters const & other); Parameters& operator/=(double factor); template Parameters& operator+=(Parameters const & other); template Parameters& operator-=(Parameters const & other); }; template inline Parameters operator*(Parameters a, Parameters const & b) { a*=b; return a; } template inline Parameters operator*(Parameters a, double b) { a*=b; return a; } template inline Parameters operator*(double b, Parameters a) { a*=b; return a; } template inline Parameters operator/(Parameters a, Parameters const & b) { a/=b; return a; } template inline Parameters operator/(Parameters a, double b) { a/=b; return a; } template inline Parameters operator+(Parameters a, Parameters const & b) { a+=b; return a; } template inline Parameters operator-(Parameters a, Parameters const & b) { a-=b; return a; } //! Alias for weight container, e.g. used by the MatrixElement using Weights = Parameters; //! Description of event parameters, see also EventParameters struct ParameterDescription { //! Name of central scale choice (e.g. "H_T/2") std::string scale_name; //! Actual renormalisation scale divided by central scale - double mur_factor; + double mur_factor{}; //! Actual factorisation scale divided by central scale - double muf_factor; + double muf_factor{}; ParameterDescription() = default; ParameterDescription( std::string scale_name, double mur_factor, double muf_factor ): scale_name{std::move(scale_name)}, mur_factor{mur_factor}, muf_factor{muf_factor} {} }; //! generate human readable string name std::string to_string(ParameterDescription const & p); //! generate simplified string, intended for easy parsing //! Format: Scale_SCALENAME_MuRxx_MuFyy std::string to_simple_string(ParameterDescription const & p); //! Event parameters struct EventParameters{ - double mur; /**< Value of the Renormalisation Scale */ - double muf; /**< Value of the Factorisation Scale */ - double weight; /**< Event Weight */ + double mur{}; /**< Value of the Renormalisation Scale */ + double muf{}; /**< Value of the Factorisation Scale */ + double weight{}; /**< Event Weight */ //! Optional description std::shared_ptr description = nullptr; //! multiply weight by factor EventParameters& operator*=(double factor){ weight*=factor; return *this; } //! divide weight by factor EventParameters& operator/=(double factor){ weight/=factor; return *this; } //! add number to weight EventParameters& operator+=(double factor){ weight+=factor; return *this; } //! add number to weight EventParameters& operator-=(double factor){ weight-=factor; return *this; } }; inline EventParameters operator*(EventParameters a, double b){ a*=b; return a; } inline EventParameters operator*(double b, EventParameters a){ a*=b; return a; } inline EventParameters operator/(EventParameters a, double b){ a/=b; return a; } //! @{ //! @internal Implementation of template functions template template Parameters& Parameters::operator*=(Parameters const & other) { if(other.variations.size() != variations.size()) { throw std::invalid_argument{"Wrong number of Parameters"}; } central *= other.central; for(std::size_t i = 0; i < variations.size(); ++i) { variations[i] *= other.variations[i]; } return *this; } template Parameters& Parameters::operator*=(double factor) { central *= factor; for(auto & wt: variations) wt *= factor; return *this; } template template Parameters& Parameters::operator/=(Parameters const & other) { if(other.variations.size() != variations.size()) { throw std::invalid_argument{"Wrong number of Parameters"}; } central /= other.central; for(std::size_t i = 0; i < variations.size(); ++i) { variations[i] /= other.variations[i]; } return *this; } template Parameters& Parameters::operator/=(double factor) { central /= factor; for(auto & wt: variations) wt /= factor; return *this; } template template Parameters& Parameters::operator+=(Parameters const & other) { if(other.variations.size() != variations.size()) { throw std::invalid_argument{"Wrong number of Parameters"}; } central += other.central; for(std::size_t i = 0; i < variations.size(); ++i) { variations[i] += other.variations[i]; } return *this; } template template Parameters& Parameters::operator-=(Parameters const & other) { if(other.variations.size() != variations.size()) { throw std::invalid_argument{"Wrong number of Parameters"}; } central -= other.central; for(std::size_t i = 0; i < variations.size(); ++i) { variations[i] -= other.variations[i]; } return *this; } //! @} } // namespace HEJ diff --git a/include/HEJ/Particle.hh b/include/HEJ/Particle.hh index 6db65e7..ade6a5b 100644 --- a/include/HEJ/Particle.hh +++ b/include/HEJ/Particle.hh @@ -1,236 +1,237 @@ /** * \file Particle.hh * \brief Contains the particle struct * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "fastjet/PseudoJet.hh" -#include "HEJ/optional.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/optional.hh" namespace HEJ { using Colour = std::pair; //! Class representing a particle struct Particle { //! particle type - ParticleID type; + ParticleID type = pid::unspecified; //! particle momentum fastjet::PseudoJet p; //! (optional) colour & anti-colour optional colour; //! get rapidity double rapidity() const{ return p.rapidity(); } //! get transverse momentum double perp() const{ return p.perp(); } //! get transverse momentum double pt() const{ return perp(); } //! get momentum in x direction double px() const{ return p.px(); } //! get momentum in y direction double py() const{ return p.py(); } //! get momentum in z direction double pz() const{ return p.pz(); } //! get energy double E() const{ return p.E(); } //! get mass double m() const{ return p.m(); } }; //! Functor to compare rapidities /** * This can be used whenever a rapidity comparison function is needed, * for example in many standard library functions. * * @see pz_less */ struct rapidity_less{ template bool operator()(FourVector const & p1, FourVector const & p2){ return p1.rapidity() < p2.rapidity(); } }; //! Functor to compare momenta in z direction /** * This can be used whenever a pz comparison function is needed, * for example in many standard library functions. * * @see rapidity_less */ struct pz_less{ template bool operator()(FourVector const & p1, FourVector const & p2){ return p1.pz() < p2.pz(); } }; //! Convert a vector of Particles to a vector of particle momenta inline std::vector to_PseudoJet( std::vector const & v ){ std::vector result; + result.reserve(v.size()); for(auto && sp: v) result.emplace_back(sp.p); return result; } //! Functor to compare particle type (PDG) /** * This can be used whenever a particle-type comparison is needed * for example in many standard library functions. */ struct type_less{ template bool operator()(Particle const & p1, Particle const & p2){ return p1.type < p2.type; } }; //! Check if a particle is a parton, i.e. quark, antiquark, or gluon inline bool is_parton(Particle const & p){ return is_parton(p.type); } //! Check if a particle is a quark inline bool is_quark(Particle const & p){ return is_quark(p.type); } //! Check if a particle is an anti-quark inline bool is_antiquark(Particle const & p){ return is_antiquark(p.type); } //! Check if a particle is a quark or anit-quark inline bool is_anyquark(Particle const & p){ return is_anyquark(p.type); } /** * \brief Function to determine if particle is a lepton * @param p the particle * @returns true if the particle is a lepton, false otherwise */ inline constexpr bool is_lepton(Particle const & p){ return is_lepton(p.type); } /** * \brief Function to determine if particle is an antilepton * @param p the particle * @returns true if the particle is an antilepton, false otherwise */ inline constexpr bool is_antilepton(Particle const & p){ return is_antilepton(p.type); } /** * \brief Function to determine if particle is an (anti-)lepton * @param p the particle * @returns true if the particle is a lepton or antilepton, false otherwise */ inline constexpr bool is_anylepton(Particle const & p){ return is_anylepton(p.type); } /** * \brief Function to determine if particle is a neutrino * @param p the particle * @returns true if the particle is a neutrino, false otherwise */ inline constexpr bool is_neutrino(Particle const & p){ return is_neutrino(p.type); } /** * \brief Function to determine if particle is an antineutrino * @param p the particle * @returns true if the particle is an antineutrino, false otherwise */ inline constexpr bool is_antineutrino(Particle const & p){ return is_antineutrino(p.type); } /** * \brief Function to determine if particle is an (anti-)neutrino * @param p the particle * @returns true if the particle is a neutrino or antineutrino, false otherwise */ inline constexpr bool is_anyneutrino(Particle const & p){ return is_anyneutrino(p.type); } //! Check if a particle is a photon, W or Z boson inline bool is_AWZ_boson(Particle const & particle){ return is_AWZ_boson(particle.type); } //! Check if a particle is a photon, W, Z, or Higgs boson inline bool is_AWZH_boson(Particle const & particle){ return is_AWZH_boson(particle.type); } //! Extract all partons from a vector of particles inline std::vector filter_partons( std::vector const & v ){ std::vector result; result.reserve(v.size()); std::copy_if( begin(v), end(v), std::back_inserter(result), [](Particle const & p){ return is_parton(p); } ); return result; } //! Extract all AWZH bosons from a vector of particles inline std::vector filter_AWZH_bosons( std::vector const & v ){ std::vector result; std::copy_if( begin(v), end(v), std::back_inserter(result), [](Particle const & p){ return is_AWZH_boson(p); } ); return result; } } // namespace HEJ diff --git a/include/HEJ/PhaseSpacePoint.hh b/include/HEJ/PhaseSpacePoint.hh index 56ecaca..58ee23c 100644 --- a/include/HEJ/PhaseSpacePoint.hh +++ b/include/HEJ/PhaseSpacePoint.hh @@ -1,206 +1,200 @@ /** \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 "fastjet/PseudoJet.hh" #include "HEJ/Config.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" #include "HEJ/StatusCode.hh" -namespace HEJ{ - class RNG; +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 + 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; int sample_ng(std::vector const & Born_jets, RNG & ran); int sample_ng_jets( int ng, std::vector const & Born_jets, RNG & ran ); double probability_in_jet( std::vector const & Born_jets ) const; std::vector gen_non_jet( - int ng_non_jet, - double ptmin, double ptmax, - RNG & ran + int ng_non_jet, double ptmin, double ptmax, RNG & ran ); void rescale_qqx_rapidities( std::vector & out_partons, std::vector const & jets, - const double ymin1, const double ymax2, - const int qqxbackjet + double ymin1, double ymax2, + int qqxbackjet ); void rescale_rapidities( std::vector & partons, double ymin, double ymax ); std::vector reshuffle( std::vector const & Born_jets, 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); - double phase_space_normalisation( - int num_Born_jets, - int num_res_partons - ) const; /** \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 * * relies on JetSplitter */ std::vector split( std::vector const & jets, int ng_jets, std::size_t qqxbackjet, 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, 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 * original types from the input event. */ void label_qqx(Event const & event); void copy_AWZH_boson_from(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_; 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/RNG.hh b/include/HEJ/RNG.hh index 63e4ceb..a696552 100644 --- a/include/HEJ/RNG.hh +++ b/include/HEJ/RNG.hh @@ -1,48 +1,48 @@ /** \file * \brief Interface for pseudorandom number generators * * We select our random number generator at runtime according to the * configuration file. This interface guarantees that we can use all * generators in the same way. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include namespace HEJ { //! Interface for random number generator struct RNG { //! Random number type, see std::RandomNumberDistribution using result_type = unsigned; //! Generate random number in [0,1) virtual double flat() = 0; //! Minimum number that can be generated virtual result_type min() const = 0; //! Maximum number that can be generated virtual result_type max() const = 0; //! Generate random number in [min(), max()] virtual result_type operator()() = 0; virtual ~RNG() = default; }; //! Helper struct with default implementations struct DefaultRNG : virtual RNG { result_type min() const override { - return 0u; + return 0U; } result_type max() const override { return std::numeric_limits::max() - 1; } result_type operator()() override { return flat()*std::numeric_limits::max(); } }; } // namespace HEJ diff --git a/include/HEJ/RivetAnalysis.hh b/include/HEJ/RivetAnalysis.hh index 9f28c78..5883b06 100644 --- a/include/HEJ/RivetAnalysis.hh +++ b/include/HEJ/RivetAnalysis.hh @@ -1,70 +1,71 @@ /** \file * \brief HEJ 2 interface to rivet analyses * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "LHEF/LHEF.h" #include "HEJ/Analysis.hh" #include "HEJ/optional.hh" namespace YAML { class Node; } namespace HEJ { /** * @brief Class representing a Rivet analysis * * This class inherits from Analysis and can therefore be used * like any other HEJ 2 analysis. */ - class RivetAnalysis: public HEJ::Analysis { + class RivetAnalysis: public Analysis { public: //! Create RivetAnalysis static std::unique_ptr create( YAML::Node const & config, LHEF::HEPRUP const & heprup); //! Constructor /** * @param config Configuration parameters * @param heprup General run informations * * config["rivet"] should be the name of a single Rivet analysis or * a list of Rivet analyses. config["output"] is the prefix for * the .yoda output files. */ - RivetAnalysis(YAML::Node const & config, LHEF::HEPRUP const & heprup); + RivetAnalysis(YAML::Node const & config, LHEF::HEPRUP heprup); ~RivetAnalysis() override; //! Pass an event to the underlying Rivet analysis - void fill(HEJ::Event const & event, HEJ::Event const &) override; - bool pass_cuts(HEJ::Event const &, HEJ::Event const &) override - {return true;} //!< no additional cuts are applied + void fill(Event const & event, Event const & /*unused*/) override; + //! no additional cuts are applied + bool pass_cuts(Event const & /*unused*/, Event const & /*unused*/ + ) override { return true; } void finalise() override; private: std::vector analyses_names_; std::string output_name_; LHEF::HEPRUP heprup_; /// struct to organise the infos per rivet run/scale setting struct rivet_info; std::vector rivet_runs_; /** * \internal * @brief Calculates the scale variation from the first event for the output * file */ - void init(HEJ::Event const & event); + void init(Event const & event); bool first_event_; }; } // namespace HEJ diff --git a/include/HEJ/ScaleFunction.hh b/include/HEJ/ScaleFunction.hh index aecbee8..da7b10d 100644 --- a/include/HEJ/ScaleFunction.hh +++ b/include/HEJ/ScaleFunction.hh @@ -1,174 +1,174 @@ /** \file * \brief Functions to calculate the (renormalisation and factorisation) scales for an event * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include -namespace HEJ{ +namespace HEJ { class Event; //! Class to calculate the scale associated with an event class ScaleFunction { public: //! Constructor /** * @param name Name of the scale choice (e.g. H_T) * @param fun Function used to calculate the scale */ ScaleFunction(std::string name, std::function fun): name_{std::move(name)}, fun_{std::move(fun)} {} //! Name of the scale choice std::string const & name() const { return name_; } //! Calculate the scale associated with an event double operator()(Event const & ev) const { return fun_(ev); } private: friend ScaleFunction operator*(double factor, ScaleFunction base_scale); friend ScaleFunction operator/(ScaleFunction base_scale, double denom); - friend ScaleFunction operator*(ScaleFunction func1, ScaleFunction func2); - friend ScaleFunction operator/(ScaleFunction func1, ScaleFunction func2); + friend ScaleFunction operator*(ScaleFunction factor, ScaleFunction base_scale); + friend ScaleFunction operator/(ScaleFunction base_scale, ScaleFunction denom); std::string name_; std::function fun_; }; //! Multiply a scale choice by a constant factor /** * For example, multiplying 0.5 and a scale function for H_T * will result in a scale function for H_T/2. */ ScaleFunction operator*(double factor, ScaleFunction base_scale); //! Multiply a scale choice by a second one /** * For example, multiplying H_T and m_j1j2 * will result in a scale function for H_T*m_j1j2. */ ScaleFunction operator*(ScaleFunction factor, ScaleFunction base_scale); //! Divide a scale choice by a constant factor /** * For example, dividing a scale function for H_T by 2 * will result in a scale function for H_T/2. */ ScaleFunction operator/(ScaleFunction base_scale, double denom); //! Divide a scale choice by a second one /** * For example, dividing a scale function for H_T by m_j1j2 * will result in a scale function for H_T/m_j1j2. */ ScaleFunction operator/(ScaleFunction base_scale, ScaleFunction denom); //! Calculate \f$H_T\f$ for the input event /** * \f$H_T\f$ is the sum of the (scalar) transverse momenta of all * final-state particles */ - double H_T(Event const &); + double H_T(Event const & /*ev*/); //! The maximum of all (scalar) jet transverse momentum - double max_jet_pt(Event const &); + double max_jet_pt(Event const & /*ev*/); //! The invariant mass of the sum of all jet momenta - double jet_invariant_mass(Event const &); + double jet_invariant_mass(Event const & /*ev*/); //! Invariant mass of the two hardest jets - double m_j1j2(Event const &); + double m_j1j2(Event const & /*ev*/); //! Functor that returns a fixed scale regardless of the input event class FixedScale { public: explicit FixedScale(double mu): mu_{mu} {} - double operator()(Event const &) const { + double operator()(Event const & /*unused*/) const { return mu_; } private: double mu_; }; - class ParameterDescription; + struct ParameterDescription; //! Generate combinations of renormalisation and factorisation scales class ScaleGenerator{ public: ScaleGenerator() = default; /** \brief Constructor * @param scale_functions_begin Iterator to first base scale * @param scale_functions_end Iterator past last base scale * @param scale_factors_begin Iterator to first scale factor * @param scale_factors_end Iterator past last scale factor * @param max_scale_ratio Maximum ratio between renormalisation * and factorisation scale */ template ScaleGenerator( ScaleFunIterator scale_functions_begin, ScaleFunIterator scale_functions_end, FactorIterator scale_factors_begin, FactorIterator scale_factors_end, double max_scale_ratio ): scales_(scale_functions_begin, scale_functions_end), scale_factors_(scale_factors_begin, scale_factors_end), max_scale_ratio_{max_scale_ratio} { gen_descriptions(); } /** \brief Constructor * @param scales Base scales * @param scale_factors Factors to multiply the base scales * @param max_scale_ratio Maximum ratio between renormalisation * and factorisation scale */ ScaleGenerator( std::vector scales, std::vector scale_factors, double max_scale_ratio ): scales_(std::move(scales)), scale_factors_(std::move(scale_factors)), max_scale_ratio_{max_scale_ratio} { gen_descriptions(); } /** \brief Adjust event parameters, adding scale variation * * The central renormalisation and factorisation scale of the returned * event is given be the first base scale passed to the constructor. * The scale variation (stored in event.variation()) is constructed as * follows: For each base scale according to the arguments of the * constructor we add one variation where both renormalisation and * factorisation scale are set according to the current base scale. * Then, all combinations where the base renormalisation and factorisation * scales are multiplied by one of the scale factors are added. * The case were both scales are multiplied by one is skipped. * Scale combinations where the ratio is larger than the maximum scale ratio * set in the constructor are likewise discarded. */ Event operator()(Event event) const; private: void gen_descriptions(); std::vector scales_; std::vector scale_factors_; std::vector> descriptions_; - double max_scale_ratio_; + double max_scale_ratio_{}; }; } // namespace HEJ diff --git a/include/HEJ/Unweighter.hh b/include/HEJ/Unweighter.hh index 67f5ce6..c62754c 100644 --- a/include/HEJ/Unweighter.hh +++ b/include/HEJ/Unweighter.hh @@ -1,84 +1,85 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/optional.hh" namespace HEJ { class Event; - class RNG; + struct RNG; /** * @brief Unweight events * @details Throws away events below with abs(weight) const & events){ set_cut_to_maxwt(events.cbegin(), events.cend()); } //! Iterator version of set_max() template void set_cut_to_maxwt(ConstIt begin, ConstIt end); //! Estimate some reasonable cut for partial unweighting /** * @param events Events used for the estimation * @param max_dev Standard derivation to include above mean weight */ void set_cut_to_peakwt(std::vector const & events, double max_dev){ set_cut_to_peakwt(events.cbegin(), events.cend(), max_dev); } //! Iterator version of set_cut_to_peakwt() template void set_cut_to_peakwt(ConstIt begin, ConstIt end, double max_dev); //! Returns current value of the cut double get_cut() const { return cut_; } //! Unweight one event, returns original event if weight > get_cut() optional unweight(Event ev, RNG & ran) const; //! Unweight for multiple events at once std::vector unweight( std::vector events, RNG & ran ) const; //! @brief Iterator implementation of unweight(), /** * Usage similar to std::remove(), i.e. use with erase() * * @return Beginning of "discarded" range */ template Iterator unweight( Iterator begin, Iterator end, RNG & ran ) const; private: - double cut_; + double cut_{-1}; //! Returns true if element can be removed/gets discarded //! directly corrects weight if is accepted (not removed) bool discard(RNG & ran, Event & ev) const; }; } // namespace HEJ // implementation of template functions #include "HEJ/detail/Unweighter_impl.hh" diff --git a/include/HEJ/Wjets.hh b/include/HEJ/Wjets.hh index dbe850e..62217f7 100644 --- a/include/HEJ/Wjets.hh +++ b/include/HEJ/Wjets.hh @@ -1,509 +1,509 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \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 { - class ParticleProperties; + struct ParticleProperties; namespace currents { - typedef CLHEP::HepLorentzVector HLV; + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + 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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Extremal qqx. qxqQ /** * @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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Extremal qqx. qqxQ /** * @param pgin Momentum of initial state gluon - * @param pqout Momentum of final state quark a + * @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 pqbarout Momentum of final state anti-quark a + * @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 & pqout, + double ME_WExqqx_qqbarQ(HLV const & pgin, HLV const & pqbarout, HLV const & plbar, HLV const & pl, - HLV const & pqbarout, HLV const & p2out, + HLV const & pqout, HLV const & p2out, HLV const & p2in, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Extremal qqx. gg->qxqg /** * @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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Extremal qqx. gg->qqxg /** * @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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Extremal qqx. gg->qqxg. qqx 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 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. */ 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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Jets qqxCentral. qqx 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 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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); //! W+Jets qqxCentral. W emission from backwards leg. /** - * @param ka Momentum of initial state particle a - * @param kb Momentum of initial state particle b + * @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 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 ka, HLV kb, + 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, - HEJ::ParticleProperties const & wprop); + ParticleProperties const & wprop); } // namespace currents } // namespace HEJ diff --git a/include/HEJ/YAMLreader.hh b/include/HEJ/YAMLreader.hh index d4748f5..5f45944 100644 --- a/include/HEJ/YAMLreader.hh +++ b/include/HEJ/YAMLreader.hh @@ -1,270 +1,270 @@ /** \file * \brief The file which handles the configuration file parameters * * The configuration files parameters are read and then stored * within this objects. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "yaml-cpp/yaml.h" #include "fastjet/JetDefinition.hh" #include "HEJ/Config.hh" -#include "HEJ/exceptions.hh" #include "HEJ/Fraction.hh" -#include "HEJ/optional.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/exceptions.hh" +#include "HEJ/optional.hh" #include "HEJ/utility.hh" -namespace HEJ{ - class OutputFile; +namespace HEJ { + struct OutputFile; //! Load configuration from file /** * @param config_file Name of the YAML configuration file * @returns The HEJ 2 configuration */ Config load_config(std::string const & config_file); //! Set option using the corresponding YAML entry /** * @param setting Option variable to be set * @param yaml Root of the YAML configuration * @param names Name of the entry * * If the entry does not exist or has the wrong type or format * an exception is thrown. * * For example * @code * set_from_yaml(foobar, yaml, "foo", "bar") * @endcode * is equivalent to * @code * foobar = yaml["foo"]["bar"].as() * @endcode * with improved diagnostics on errors. * * @see set_from_yaml_if_defined */ template void set_from_yaml( T & setting, YAML::Node const & yaml, YamlNames const & ... names ); //! Set option using the corresponding YAML entry, if present /** * @param setting Option variable to be set * @param yaml Root of the YAML configuration * @param names Name of the entry * * This function works similar to set_from_yaml, but does not * throw any exception if the requested YAML entry does not exist. * * @see set_from_yaml */ template void set_from_yaml_if_defined( T & setting, YAML::Node const & yaml, YamlNames const & ... names ); //! Extract jet parameters from YAML configuration JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ); //! Extract Higgs coupling settings from YAML configuration HiggsCouplingSettings get_Higgs_coupling( YAML::Node const & node, std::string const & entry ); //! Extract the EW parameters from YAML configuration EWConstants get_ew_parameters(YAML::Node const & node); //! Extract scale setting parameters from YAML configuration ScaleConfig to_ScaleConfig(YAML::Node const & yaml); //! Extract random number generator settings from YAML configuration RNGConfig to_RNGConfig(YAML::Node const & node, std::string const & entry); //! Check whether all options in configuration are supported /** * @param conf Configuration to be checked * @param supported Tree of supported options * * If conf contains an entry that does not appear in supported * an unknown_option exception is thrown. Sub-entries of "analysis" * (if present) are not checked. * * @see unknown_option */ void assert_all_options_known( YAML::Node const & conf, YAML::Node const & supported ); namespace detail{ void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml); void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml); void set_from_yaml(ParticleID & setting, YAML::Node const & yaml); void set_from_yaml(OutputFile & setting, YAML::Node const & yaml); void set_from_yaml(WeightType & setting, YAML::Node const & yaml); inline void set_from_yaml(YAML::Node & setting, YAML::Node const & yaml){ setting = yaml; } template void set_from_yaml(Scalar & setting, YAML::Node const & yaml){ assert(yaml); if(!yaml.IsScalar()){ throw invalid_type{"value is not a scalar"}; } try{ setting = yaml.as(); } catch(...){ throw invalid_type{ "value " + yaml.as() + " cannot be converted to a " + type_string(setting) }; } } template void set_from_yaml(optional & setting, YAML::Node const & yaml){ T tmp; set_from_yaml(tmp, yaml); setting = tmp; } template void set_from_yaml(std::vector & setting, YAML::Node const & yaml){ assert(yaml); // special case: treat a single value like a vector with one element if(yaml.IsScalar()){ setting.resize(1); return set_from_yaml(setting.front(), yaml); } if(yaml.IsSequence()){ setting.resize(yaml.size()); for(size_t i = 0; i < setting.size(); ++i){ set_from_yaml(setting[i], yaml[i]); } return; } throw invalid_type{""}; } template void set_from_yaml( T & setting, YAML::Node const & yaml, FirstName const & name, YamlNames && ... names ){ if(!yaml[name]) throw missing_option{""}; set_from_yaml( setting, yaml[name], std::forward(names)... ); } template void set_from_yaml_if_defined(T & setting, YAML::Node const & yaml){ return set_from_yaml(setting, yaml); } template void set_from_yaml_if_defined( T & setting, YAML::Node const & yaml, FirstName const & name, YamlNames && ... names ){ if(!yaml[name]) return; set_from_yaml_if_defined( setting, yaml[name], std::forward(names)... ); } } // namespace detail template void set_from_yaml( T & setting, YAML::Node const & yaml, YamlNames const & ... names ){ try{ detail::set_from_yaml(setting, yaml, names...); } catch(invalid_type const & ex){ throw invalid_type{ "In option " + join(": ", names...) + ": " + ex.what() }; } catch(missing_option const &){ throw missing_option{ "No entry for mandatory option " + join(": ", names...) }; } catch(std::invalid_argument const & ex){ throw missing_option{ "In option " + join(": ", names...) + ":" " invalid value " + ex.what() }; } } template void set_from_yaml_if_defined( T & setting, YAML::Node const & yaml, YamlNames const & ... names ){ try{ detail::set_from_yaml_if_defined(setting, yaml, names...); } catch(invalid_type const & ex){ throw invalid_type{ "In option " + join(": ", names...) + ": " + ex.what() }; } catch(std::invalid_argument const & ex){ throw missing_option{ "In option " + join(": ", names...) + ":" " invalid value " + ex.what() }; } } } // namespace HEJ namespace YAML { template<> struct convert { static Node encode(HEJ::OutputFile const & outfile); static bool decode(Node const & node, HEJ::OutputFile & out); }; template struct convert> { static Node encode(HEJ::Fraction const & f) { return encode(Real{f}); } static bool decode(Node const & node, HEJ::Fraction & f) { Real r; if(!convert::decode(node, r)) return false; f = r; return true; } }; -} +} // namespace YAML diff --git a/include/HEJ/Zjets.hh b/include/HEJ/Zjets.hh index 4ebcd48..1058e0c 100644 --- a/include/HEJ/Zjets.hh +++ b/include/HEJ/Zjets.hh @@ -1,127 +1,127 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ /** \file * \brief Functions computing the square of current contractions in Z+Jets. */ #pragma once #include #include "CLHEP/Vector/LorentzVector.h" #include "HEJ/PDG_codes.hh" namespace HEJ { - class ParticleProperties; + struct ParticleProperties; namespace currents { - typedef CLHEP::HepLorentzVector HLV; + using HLV = CLHEP::HepLorentzVector; //! Square of qQ->qQe+e- Z+Jets Scattering Current /** * @param pa Momentum of initial state quark * @param pb Momentum of initial state quark * @param p1 Momentum of final state quark * @param p2 Momentum of final state quark * @param pep Momentum of final state positron * @param pem Momentum of final state electron * @param aptype Initial particle 1 type * @param bptype Initial particle 2 type * @param zprop Mass and width of the Z boson * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) * @returns Square of the current contractions for qQ->qQe+e- Scattering * * This returns the square of the current contractions in qQ->qQ scattering * with an emission of a Z Boson. */ std::vector ME_Z_qQ(const HLV & pa, const HLV & pb, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, - const HEJ::ParticleID aptype, const HEJ::ParticleID bptype, - HEJ::ParticleProperties const & zprop, - const double stw2, const double ctw); + ParticleID aptype, ParticleID bptype, + ParticleProperties const & zprop, + double stw2, double ctw); //! Square of qg->qge+e- Z+Jets Scattering Current /** * @param pa Momentum of initial state quark * @param pb Momentum of initial state gluon * @param p1 Momentum of final state quark * @param p2 Momentum of final state gluon * @param pep Momentum of final state positron * @param pem Momentum of final state electron * @param aptype Initial particle 1 type * @param bptype Initial particle 2 type * @param zprop Mass and width of the Z boson * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) * @returns Square of the current contractions for qg->qge+e- Scattering * * This returns the square of the current contractions in qg->qg scattering * with an emission of a Z Boson. */ double ME_Z_qg(const HLV & pa, const HLV & pb, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, - const HEJ::ParticleID aptype, const HEJ::ParticleID bptype, - HEJ::ParticleProperties const & zprop, - const double stw2, const double ctw); + ParticleID aptype, ParticleID bptype, + ParticleProperties const & zprop, + double stw2, double ctw); //! Square of qQ->gqQe+e- Z+Jets Unordered Current /** * @param pa Momentum of initial state quark a * @param pb Momentum of initial state quark b * @param pg Momentum of final state unordered gluon * @param p1 Momentum of final state quark a * @param p2 Momentum of final state quark b * @param pep Momentum of final state positron * @param pem Momentum of final state electron * @param aptype Initial particle 1 type * @param bptype Initial particle 2 type * @param zprop Mass and width of the Z boson * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) * @returns Square of the current contractions for qQ->gqQe+e- Scattering * * This returns the square of the current contractions in qQ->gqQ scattering * with an emission of a Z Boson. */ std::vector ME_Zuno_qQ(const HLV & pa, const HLV & pb, const HLV & pg, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, - const HEJ::ParticleID aptype, const HEJ::ParticleID bptype, - HEJ::ParticleProperties const & zprop, - const double stw2, const double ctw); + ParticleID aptype, ParticleID bptype, + ParticleProperties const & zprop, + double stw2, double ctw); //! Square of qg->gqge+e- Z+Jets Unordered Current /** * @param pa Momentum of initial state quark * @param pb Momentum of initial state gluon * @param pg Momentum of final state unordered gluon * @param p1 Momentum of final state quark * @param p2 Momentum of final state gluon * @param pep Momentum of final state positron * @param pem Momentum of final state electron * @param aptype Initial particle 1 type * @param bptype Initial particle 2 type * @param zprop Mass and width of the Z boson * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) * @returns Square of the current contractions for qg->gqge+e- Scattering * * This returns the square of the current contractions in qg->gqg scattering * with an emission of a Z Boson. */ double ME_Zuno_qg(const HLV & pa, const HLV & pb, const HLV & pg, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, - const HEJ::ParticleID aptype, const HEJ::ParticleID bptype, - HEJ::ParticleProperties const & zprop, - const double stw2, const double ctw); + ParticleID aptype, ParticleID bptype, + ParticleProperties const & zprop, + double stw2, double ctw); } // namespace currents } // namespace HEJ diff --git a/include/HEJ/detail/HepMCInterface_common.hh b/include/HEJ/detail/HepMCInterface_common.hh index 9db9533..b41ad67 100644 --- a/include/HEJ/detail/HepMCInterface_common.hh +++ b/include/HEJ/detail/HepMCInterface_common.hh @@ -1,95 +1,97 @@ /** \file * \brief Template functions shared between HepMC2 and HepMC3 * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include "HEJ/Event.hh" #include "HEJ/Particle.hh" -namespace HEJ{ - namespace detail_HepMC{ +namespace HEJ { + namespace detail_HepMC { //! Convert Particle to HepMC 4-vector (x,y,z,E) template FourVector to_FourVector(Particle const & sp){ return {sp.px(), sp.py(), sp.pz(), sp.E()}; } - //! @name Conventional status codes in HepMC - //!@{ - constexpr int status_beam = 4; //!< Beam - constexpr int status_in = 11; //!< Incoming - constexpr int status_decayed = 2; //!< Decaying - constexpr int status_out = 1; //!< Final outgoing - - //!@} + namespace status_codes { + //! Conventional status codes in HepMC + enum Status: unsigned { + out = 1, //!< Final outgoing + decay = 2, //!< Decaying + beam = 4, //!< Beam + in = 11, //!< Incoming + }; + } + using Status = status_codes::Status; //! @name Helper classes/functions //! @brief Have to be implemented for each HepMC version V //!@{ template struct HepMCVersion; template using GenEvent = typename HepMCVersion::GenEvent; template using Beam = typename HepMCVersion::Beam; //! Make HepMC particle template auto make_particle_ptr( Particle const & sp, int status ); //! Make HepMC vetex template auto make_vx_ptr(); //!@} /** * @brief Template class to initialise the kinematics for HepMC * * @tparam V HepMC major version * @param event HEJ event * @param beam beam particles * @param out_ev new HepMC event */ template GenEvent HepMC_init_kinematics( Event const & event, Beam const & beam, GenEvent && out_ev ){ out_ev.set_beam_particles(beam[0], beam[1]); auto vx = make_vx_ptr(); for(size_t i=0; i(event.incoming()[i], status_in); + auto particle = make_particle_ptr(event.incoming()[i], Status::in); auto vx_beam = make_vx_ptr(); vx_beam->add_particle_in(beam[i]); vx_beam->add_particle_out(particle); out_ev.add_vertex(vx_beam); vx->add_particle_in(particle); } for(size_t i=0; i < event.outgoing().size(); ++i){ auto const & out = event.outgoing()[i]; - auto particle = make_particle_ptr(out, status_out); - const int status = event.decays().count(i)?status_decayed:status_out; + auto particle = make_particle_ptr(out, Status::out); + const int status = event.decays().count(i)?Status::decay:Status::out; particle->set_status(status); - if( status == status_decayed ){ + if( status == Status::decay ){ auto vx_decay = make_vx_ptr(); vx_decay->add_particle_in(particle); for( auto const & decay: event.decays().at(i) ){ vx_decay->add_particle_out( - make_particle_ptr(decay, status_out) + make_particle_ptr(decay, Status::out) ); } out_ev.add_vertex(vx_decay); } vx->add_particle_out(particle); } out_ev.add_vertex(vx); return out_ev; } } // namespace detail_HepMC } // namespace HEJ diff --git a/include/HEJ/detail/Unweighter_impl.hh b/include/HEJ/detail/Unweighter_impl.hh index 17ff22e..a79b226 100644 --- a/include/HEJ/detail/Unweighter_impl.hh +++ b/include/HEJ/detail/Unweighter_impl.hh @@ -1,149 +1,147 @@ /** \file * \brief Unweighter Class Implementation for template functions * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once -#include "HEJ/Unweighter.hh" - #include #include #include #include #include "HEJ/Event.hh" #include "HEJ/RNG.hh" namespace HEJ { namespace detail { //! primitive histogram class //! @internal we don't want to do much with this, //! so try to avoid an external dependence like YODA or ROOT class Histogram { public: Histogram(size_t nbins, double min, double max): bins_(nbins), min_{min}, width_{(max-min)/nbins} {} size_t bin_idx(double pos) { - int const idx = std::abs((pos - min_)/width_); + int const idx = std::floor((pos - min_)/width_); if(idx < 0) return 0; return std::min(static_cast(idx), bins_.size()-1); } void fill(double pos, double val) { size_t const bin = bin_idx(pos); if(bin >= bins_.size()) return; bins_[bin] += val; } double operator[](size_t i) const { return bins_[i]; } double & operator[](size_t i) { return bins_[i]; } std::pair peak_bin() const { const auto max = std::max_element(begin(bins_), end(bins_)); if(max == end(bins_)) { static constexpr double nan = std::numeric_limits::quiet_NaN(); return std::make_pair(nan, nan); } const size_t nbin = std::distance(begin(bins_), max); return std::make_pair(min_ + (nbin + 0.5)*width_, *max); } private: std::vector bins_; double min_, width_; }; //! Get minimal and maximal absolute weight template std::pair awt_range(ConstIt begin, ConstIt end) { // can't use std::minmax_element // because boost filter iterators are not assignable assert(begin != end); double min = std::numeric_limits::infinity(); double max = 0.; for(auto it = begin; it != end; ++it) { const double awt = std::abs(it->central().weight); if(awt == 0) continue; if(awt < min) { min = awt; } else if (awt > max) { max = awt; } } return std::make_pair(min, max); } - } + } // namespace detail template void Unweighter::set_cut_to_peakwt(ConstIt begin, ConstIt end, double max_dev){ if(begin == end) { throw std::invalid_argument{"Empty range of events"}; } double err = 0.; double awt_sum = 0.; const auto extremal_awts = detail::awt_range(begin, end); const double min_lwt = std::log(extremal_awts.first); const double max_lwt = std::log(extremal_awts.second); const size_t nevents = std::distance(begin, end); // heuristic value for number of bins const size_t nbins = std::sqrt(nevents); detail::Histogram wtwt{nbins, min_lwt, max_lwt}; detail::Histogram nwt{nbins, min_lwt, max_lwt}; for(auto it=begin; it != end; ++it){ const double awt = std::abs(it->central().weight); nwt.fill(std::log(awt), 1.); } const auto cut = nevents/nbins; for(; begin != end; ++begin){ const double awt = std::abs(begin->central().weight); const double log_wt = std::log(awt); const double bin_idx = nwt.bin_idx(log_wt); assert(bin_idx=0); // prune low statistics bins with a heuristic cut if(nwt[bin_idx] >= cut){ wtwt.fill(log_wt, awt); const double tmp = awt*log_wt; err += tmp*tmp; awt_sum += awt; } } const auto peak = wtwt.peak_bin(); err = std::sqrt(err)/awt_sum; set_cut(std::exp(peak.first + max_dev*err)); } template void Unweighter::set_cut_to_maxwt(ConstIt begin, ConstIt end){ set_cut(-1); for(; begin != end; ++begin){ const double awt = std::abs(begin->central().weight); if( awt > get_cut()) set_cut(awt); } } template Iterator Unweighter::unweight( Iterator begin, Iterator end, RNG & ran ) const { if(get_cut() < 0) return end; return std::remove_if(begin, end, [&](auto & ev) -> bool { return this->discard(ran, ev); }); } } // namespace HEJ diff --git a/include/HEJ/get_analysis.hh b/include/HEJ/get_analysis.hh index 4d0d727..2756cfc 100644 --- a/include/HEJ/get_analysis.hh +++ b/include/HEJ/get_analysis.hh @@ -1,43 +1,43 @@ /** \file * \brief Contains the get_analysis function * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/Analysis.hh" namespace YAML { class Node; } namespace HEJ { //! Load an analysis /** * @param parameters Analysis parameters * @param heprup General run informations * @returns A pointer to an Analysis instance * * If parameters["plugin"] exists, an analysis (deriving from the * \ref Analysis class) will be loaded from the library parameters["plugin"]. * Otherwise, if parameters["rivet"] exists, the corresponding RivetAnalysis * will be loaded. If none of these parameters are specified, a pointer to * the default EmptyAnalysis is returned. */ std::unique_ptr get_analysis( YAML::Node const & parameters, LHEF::HEPRUP const & heprup); //! Loads multiple analysis, vector version of get_analysis() /** * @param parameters Vector of Analysis parameters * @param heprup General run informations * @returns Vector of pointers to an Analysis instance */ std::vector> get_analyses( std::vector const & parameters, LHEF::HEPRUP const & heprup); -} +} // namespace HEJ diff --git a/include/HEJ/helicity.hh b/include/HEJ/helicity.hh index 0e82a6d..90f518f 100644 --- a/include/HEJ/helicity.hh +++ b/include/HEJ/helicity.hh @@ -1,27 +1,27 @@ /** \file * \brief Definition of Helicity * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once namespace HEJ { namespace helicity { enum Helicity { minus, plus }; } using helicity::Helicity; /** * \brief Flip helicity * @param h Helicity to flip * @returns plus iff the input helicity was minus and vice versa */ inline constexpr Helicity flip(Helicity h) { return (h == helicity::plus)?helicity::minus:helicity::plus; } -} +} // namespace HEJ diff --git a/include/HEJ/jets.hh b/include/HEJ/jets.hh index 80481e5..bbaf3c5 100644 --- a/include/HEJ/jets.hh +++ b/include/HEJ/jets.hh @@ -1,435 +1,436 @@ /** * \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 { - typedef std::complex COM; - typedef COM current[4]; - typedef CLHEP::HepLorentzVector HLV; + 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 /** * @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 * arguments appropriately. */ double ME_Exqqx_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 /** * @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 * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_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 /** * @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 * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_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 /** * @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 * * @note this can be used for Exqqxf contributions by inputting * arguments appropriately. */ double ME_Exqqx_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 /** * @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 */ double ME_Cenqqx_qq(HLV const & ka, HLV const & kb, std::vector const & partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove); /** \class CCurrent jets.hh "include/HEJ/jets.hh" * \brief This is the a new class structure for currents. */ class CCurrent { public: CCurrent(COM sc0, COM sc1, COM sc2, COM sc3): c0(sc0), c1(sc1), c2(sc2), c3(sc3) {} - CCurrent(const HLV p): + CCurrent(HLV const & p): c0{p.e()}, c1{p.px()}, c2{p.py()}, c3{p.pz()} {} - CCurrent() {} + CCurrent() = default; CCurrent operator+(const CCurrent& other) const; CCurrent operator-(const CCurrent& other) const; - CCurrent operator*(const double x) const; - CCurrent operator*(const COM x) const; - CCurrent operator/(const double x) const; - CCurrent operator/(const COM x) const; + CCurrent operator*(double x) const; + CCurrent operator*(COM x) const; + CCurrent operator/(double x) const; + CCurrent operator/(COM x) const; friend std::ostream& operator<<(std::ostream& os, const CCurrent& cur); COM dot(HLV const & p1) const; COM dot(CCurrent const & p1) const; COM c0, c1, c2, c3; }; /* std::ostream& operator <<(std::ostream& os, const CCurrent& cur); */ CCurrent operator*(double x, CCurrent const & m); CCurrent operator*(COM const & x, CCurrent const & m); CCurrent operator/(double x, CCurrent const & m); CCurrent operator/(COM const & x, CCurrent const & m); //! Current /** * This is a wrapper function around \see joi() note helicity flip to * give same answer. */ void jio(HLV const & pin, bool helin, HLV const & pout, bool helout, current &cur); //! Current /** * @param pi bra state momentum * @param heli helicity of pi * @param pj ket state momentum * @param helj helicity of pj. (must be same as heli) * @param cur reference to current which is saved. * * This function is for building currents. It * must be called with pi as the bra, and pj as the ket. * * @TODO Remove heli/helj and just have helicity of current as argument. */ void joo(HLV pi, bool heli, HLV pj, bool helj, current &cur); //! Current /** * @param pout bra state momentum * @param helout helicity of pout * @param pin ket state momentum * @param helin helicity of pin. (must be same as helout) * @param cur reference to current which is saved. * * This function is for building currents. It must be * called with pout as the bra, and pin as the ket. jio calls this * with flipped helicity * * @TODO Remove helout/helin and just have helicity of current as argument. */ void joi(HLV const & pout, bool helout, HLV const & pin, bool helin, current &cur); //! Current /** * This is a wrapper function around the void function of the same name. * \see joi * * @TODO This is never used */ CCurrent joi(HLV const & pout, bool helout, HLV const & pin, bool helin); //! Current /** * This is a wrapper function around the void function of the same name. * \see jio */ - CCurrent jio(HLV const & pout, bool helout, HLV const & pin, bool helin); + CCurrent jio(HLV const & pin, bool helin, HLV const & pout, bool helout); //! Current /** * This is a wrapper function around the void function of the same name. * \see joo */ - CCurrent joo(HLV const & pout, bool helout, HLV const & pin, bool helin); + CCurrent joo(HLV const & pi, bool heli, HLV const & pj, bool helj); inline COM cdot(const current & j1, const current & j2) { return j1[0]*j2[0]-j1[1]*j2[1]-j1[2]*j2[2]-j1[3]*j2[3]; } inline COM cdot(const HLV & p, const current & j1) { return j1[0]*p.e()-j1[1]*p.x()-j1[2]*p.y()-j1[3]*p.z(); } inline void cmult(const COM & factor, const current & j1, current &cur) { cur[0]=factor*j1[0]; cur[1]=factor*j1[1]; cur[2]=factor*j1[2]; cur[3]=factor*j1[3]; } // WHY!?! inline void cadd(const current & j1, const current & j2, const current & j3, const current & j4, const current & j5, current &sum ) { sum[0]=j1[0]+j2[0]+j3[0]+j4[0]+j5[0]; sum[1]=j1[1]+j2[1]+j3[1]+j4[1]+j5[1]; sum[2]=j1[2]+j2[2]+j3[2]+j4[2]+j5[2]; sum[3]=j1[3]+j2[3]+j3[3]+j4[3]+j5[3]; } inline void cadd(const current & j1, const current & j2, const current & j3, const current & j4, current &sum ) { sum[0] = j1[0] + j2[0] + j3[0] + j4[0]; sum[1] = j1[1] + j2[1] + j3[1] + j4[1]; sum[2] = j1[2] + j2[2] + j3[2] + j4[2]; sum[3] = j1[3] + j2[3] + j3[3] + j4[3]; } inline void cadd(const current & j1, const current & j2, const current & j3, current &sum ) { sum[0]=j1[0]+j2[0]+j3[0]; sum[1]=j1[1]+j2[1]+j3[1]; sum[2]=j1[2]+j2[2]+j3[2]; sum[3]=j1[3]+j2[3]+j3[3]; } inline void cadd(const current & j1, const current & j2, current &sum) { sum[0]=j1[0]+j2[0]; sum[1]=j1[1]+j2[1]; sum[2]=j1[2]+j2[2]; sum[3]=j1[3]+j2[3]; } inline double abs2(const COM & a) { return (a*std::conj(a)).real(); } inline double vabs2(const CCurrent & cur) { return abs2(cur.c0)-abs2(cur.c1)-abs2(cur.c2)-abs2(cur.c3); } inline double vre(const CCurrent & a, const CCurrent & b) { return real(a.c0*std::conj(b.c0)-a.c1*std::conj(b.c1) -a.c2*std::conj(b.c2)-a.c3*std::conj(b.c3)); } //! @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/include/HEJ/kinematics.hh b/include/HEJ/kinematics.hh index cb8aa9c..90350a0 100644 --- a/include/HEJ/kinematics.hh +++ b/include/HEJ/kinematics.hh @@ -1,25 +1,25 @@ /** \file * \brief Contains function to compute the incoming momentum from outgoing. * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include -namespace fastjet{ +namespace fastjet { class PseudoJet; } -namespace HEJ{ - class Particle; +namespace HEJ { + struct Particle; /** \brief Compute the incoming momentum from momentum conservation. */ std::tuple incoming_momenta( std::vector const & outgoing /**< Outgoing particles */ ); } diff --git a/include/HEJ/make_RNG.hh b/include/HEJ/make_RNG.hh index 64a6559..243a20c 100644 --- a/include/HEJ/make_RNG.hh +++ b/include/HEJ/make_RNG.hh @@ -1,32 +1,32 @@ /** \file * \brief Declares a factory function for random number generators * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include -#include "HEJ/optional.hh" #include "HEJ/RNG.hh" +#include "HEJ/optional.hh" namespace HEJ { //! Factory function for random number generators /** * @param name Name of the random number generator * @param seed Optional seed * @returns A pointer to an instance of a random number generator * * At present, name should be one of "ranlux64" or "mixmax" (case insensitive). * The interpretation of the seed depends on the random number generator. * For ranlux64, it is the name of a seed file. For mixmax it should be a * string convertible to a long integer. */ std::unique_ptr make_RNG( std::string const & name, optional const & seed ); -} +} // namespace HEJ diff --git a/include/HEJ/make_writer.hh b/include/HEJ/make_writer.hh index 3511f5a..ab93d08 100644 --- a/include/HEJ/make_writer.hh +++ b/include/HEJ/make_writer.hh @@ -1,36 +1,36 @@ /** \file * \brief Declares a factory function for event writers * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "HEJ/EventWriter.hh" #include "HEJ/output_formats.hh" namespace LHEF{ - struct HEPRUP; + class HEPRUP; } -namespace HEJ{ +namespace HEJ { //! Factory function for event writers /** * @param format The format of the output file * @param outfile The name of the output file * @param heprup General process information * @returns A pointer to an instance of an EventWriter * for the desired format */ std::unique_ptr make_format_writer( FileFormat format, std::string const & outfile, LHEF::HEPRUP const & heprup ); -} +} // namespace HEJ diff --git a/include/HEJ/optional.hh b/include/HEJ/optional.hh index 619cf7f..fa6cb9e 100644 --- a/include/HEJ/optional.hh +++ b/include/HEJ/optional.hh @@ -1,28 +1,28 @@ /** \file * \brief Defines the optional type * * The C++14 standard introduces the std::optional type. * If C++14 is not available, we use the optional type from boost instead. * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #if __cplusplus <= 201402L #include #else #include #endif -namespace HEJ{ +namespace HEJ { #if __cplusplus <= 201402L template using optional = boost::optional; #else template using optional = std::optional; #endif } diff --git a/include/HEJ/output_formats.hh b/include/HEJ/output_formats.hh index 9633c2e..b0cb75b 100644 --- a/include/HEJ/output_formats.hh +++ b/include/HEJ/output_formats.hh @@ -1,42 +1,42 @@ /** \file * \brief Defines formats for output to event files * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once #include #include -namespace HEJ{ +namespace HEJ { //! Supported event file formats enum class FileFormat{ Les_Houches, /*!< Les Houches Output */ HepMC3, /*!< HepMC3 Output */ HepMC2, /*!< HepMC2 Output */ HDF5, /*!< HDF5 Output */ HepMC=HepMC3 /*!< HepMC3 Output */ }; //! Convert a file format to a string inline std::string to_string(FileFormat f){ switch(f){ case FileFormat::Les_Houches: return "Les Houches"; case FileFormat::HepMC2: return "HepMC2"; case FileFormat::HepMC3: return "HepMC3"; case FileFormat::HDF5: return "HDF5"; default: throw std::logic_error("unhandled file format"); } } //! Output file specification struct OutputFile{ std::string name; /**< Output File Name */ FileFormat format; /**< Output File Format */ }; } // namespace HEJ diff --git a/include/HEJ/resummation_jet.hh b/include/HEJ/resummation_jet.hh index 815bb69..dcc1c0d 100644 --- a/include/HEJ/resummation_jet.hh +++ b/include/HEJ/resummation_jet.hh @@ -1,43 +1,43 @@ /** \file * \brief Functions to calculate the kinematics of resummation jets, * i.e. resuffling the jet momenta * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include -namespace fastjet{ - struct PseudoJet; +namespace fastjet { + class PseudoJet; } -namespace HEJ{ +namespace HEJ { /** * \brief Calculate the resummation jet momenta * @param p_born born Jet Momenta * @param qperp Sum of non-jet Parton Transverse Momenta * @returns Resummation Jet Momenta */ std::vector resummation_jet_momenta( std::vector const & p_born, fastjet::PseudoJet const & qperp ); /** * \brief Calculate additional weight from changing the jet momenta * @param p_born born Jet Momenta * @param qperp Sum of non-jet Parton Transverse Momenta * * Computes the Jacobian for changing the original delta functions * expressed in terms of jet momenta to delta functions of the * parton momenta in the resummation phase space */ double resummation_jet_weight( std::vector const & p_born, fastjet::PseudoJet const & qperp ); } // namespace HEJ diff --git a/include/HEJ/stream.hh b/include/HEJ/stream.hh index 3830154..0460bd8 100644 --- a/include/HEJ/stream.hh +++ b/include/HEJ/stream.hh @@ -1,38 +1,38 @@ /** \file * \brief Declares input streams * * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include "boost/iostreams/filtering_stream.hpp" -namespace HEJ{ +namespace HEJ { //! Small wrapper around boost's filtering_istream class istream { using boost_istream = boost::iostreams::filtering_istream; public: //! Constructor /** * @param filename Name of input file */ explicit istream(std::string const & filename); //! Conversion to boost_istream operator boost_istream& () const noexcept { return *stream_; } private: std::ifstream file_; std::unique_ptr stream_; }; -} +} // namespace HEJ diff --git a/include/HEJ/utility.hh b/include/HEJ/utility.hh index eff7a08..fd760ed 100644 --- a/include/HEJ/utility.hh +++ b/include/HEJ/utility.hh @@ -1,86 +1,86 @@ /** * \file * \brief Contains various utilities * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include "boost/core/demangle.hpp" #include "fastjet/PseudoJet.hh" -namespace HEJ{ +namespace HEJ { inline std::string join( std::string const & /* delim */ ){ return ""; } inline std::string join( std::string const & /* delim */, std::string const & str ){ return str; } //! Join strings with a delimiter /** * @param delim Delimiter to be put between consecutive strings * @param first First string * @param second Second string * @param rest Remaining strings */ template std::string join( std::string const & delim, std::string const & first, std::string const & second, Strings&&... rest ){ return join(delim, first + delim + second, std::forward(rest)...); } //! Return the name of the argument's type template - std::string type_string(T&&){ + std::string type_string(T&& /*unused*/){ return boost::core::demangle(typeid(T).name()); } //! Eliminate compiler warnings for unused variables template - constexpr void ignore(T&&...) {} + constexpr void ignore(T&&... /*unused*/) {} //! Check whether two doubles are closer than ep > 0 to each other inline bool nearby_ep(double a, double b, double ep){ assert(ep > 0); return std::abs(a-b) < ep; } //! Check whether all components of two PseudoJets are closer than ep to each other inline bool nearby_ep( fastjet::PseudoJet const & pa, fastjet::PseudoJet const & pb, double ep ){ assert(ep > 0); for(size_t i = 0; i < 4; ++i){ if(!nearby_ep(pa[i], pb[i], ep)) return false; } return true; } inline bool nearby( fastjet::PseudoJet const & pa, fastjet::PseudoJet const & pb, double const norm = 1. ){ return nearby_ep(pa, pb, 1e-7*norm); } } // namespace HEJ diff --git a/include/LHEF/LHEF.h b/include/LHEF/LHEF.h index 9b0c317..bdebb66 100644 --- a/include/LHEF/LHEF.h +++ b/include/LHEF/LHEF.h @@ -1,3407 +1,3410 @@ // -*- C++ -*- #ifndef HEPMC3_LHEF_H #define HEPMC3_LHEF_H // // This is the declaration of the Les Houches Event File classes, // implementing a simple C++ parser/writer for Les Houches Event files. // Copyright (C) 2009-2013 Leif Lonnblad // // The code is licenced under version 2 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846264338327950288 #endif /** * @brief Les Houches event file classes. * * The namespace containing helper classes and Reader and Writer * classes for handling Les Houches event files. * * @ingroup LHEF */ namespace LHEF { /** * Helper struct for output of attributes. */ template struct OAttr { /** * Constructor */ OAttr(std::string n, const T & v): name(n), val(v) {} /** * The name of the attribute being printed. */ std::string name; /** * The value of the attribute being printed. */ T val; }; /** * Output manipulator for writing attributes. */ template OAttr oattr(std::string name, const T & value) { return OAttr(name, value); } /** * Output operator for attributes. */ template std::ostream & operator<<(std::ostream & os, const OAttr & oa) { os << " " << oa.name << "=\"" << oa.val << "\""; return os; } /** * The XMLTag struct is used to represent all information within an * XML tag. It contains the attributes as a map, any sub-tags as a * vector of pointers to other XMLTag objects, and any other * information as a single string. */ struct XMLTag { /** * Convenient typdef. */ typedef std::string::size_type pos_t; /** * Convenient typdef. */ typedef std::map AttributeMap; /** * Convenient alias for npos. */ static const pos_t end = std::string::npos; /** * Default constructor. */ XMLTag() {} /** * The destructor also destroys any sub-tags. */ ~XMLTag() { for ( int i = 0, N = tags.size(); i < N; ++i ) delete tags[i]; } /** * The name of this tag. */ std::string name; /** * The attributes of this tag. */ AttributeMap attr; /** * A vector of sub-tags. */ std::vector tags; /** * The contents of this tag. */ std::string contents; /** * Find an attribute named \a n and set the double variable \a v to * the corresponding value. @return false if no attribute was found. */ bool getattr(std::string n, double & v) const { AttributeMap::const_iterator it = attr.find(n); if ( it == attr.end() ) return false; v = std::atof(it->second.c_str()); return true; } /** * Find an attribute named \a n and set the bool variable \a v to * true if the corresponding value is "yes". @return false if no * attribute was found. */ bool getattr(std::string n, bool & v) const { AttributeMap::const_iterator it = attr.find(n); if ( it == attr.end() ) return false; if ( it->second == "yes" ) v = true; return true; } /** * Find an attribute named \a n and set the long variable \a v to * the corresponding value. @return false if no attribute was found. */ bool getattr(std::string n, long & v) const { AttributeMap::const_iterator it = attr.find(n); if ( it == attr.end() ) return false; v = std::atoi(it->second.c_str()); return true; } /** * Find an attribute named \a n and set the long variable \a v to * the corresponding value. @return false if no attribute was found. */ bool getattr(std::string n, int & v) const { AttributeMap::const_iterator it = attr.find(n); if ( it == attr.end() ) return false; v = int(std::atoi(it->second.c_str())); return true; } /** * Find an attribute named \a n and set the string variable \a v to * the corresponding value. @return false if no attribute was found. */ bool getattr(std::string n, std::string & v) const { AttributeMap::const_iterator it = attr.find(n); if ( it == attr.end() ) return false; v = it->second; return true; } /** * Scan the given string and return all XML tags found as a vector * of pointers to XMLTag objects. Text which does not belong to any * tag is stored in tags without name and in the string pointed to * by leftover (if not null). */ static std::vector findXMLTags(std::string str, std::string * leftover = 0) { std::vector tags; pos_t curr = 0; while ( curr != end ) { // Find the first tag pos_t begin = str.find("<", curr); // Check for comments if ( begin != end && str.find("", begin); tags.push_back(new XMLTag()); if ( endcom == end ) { tags.back()->contents = str.substr(curr); if ( leftover ) *leftover += str.substr(curr); return tags; } tags.back()->contents = str.substr(curr, endcom - curr); if ( leftover ) *leftover += str.substr(curr, endcom - curr); curr = endcom; continue; } if ( begin != curr ) { tags.push_back(new XMLTag()); tags.back()->contents = str.substr(curr, begin - curr); if ( leftover ) *leftover += str.substr(curr, begin - curr); } if ( begin == end || begin > str.length() - 3 || str[begin + 1] == '/' ) return tags; pos_t close = str.find(">", curr); if ( close == end ) return tags; // find the tag name. curr = str.find_first_of(" \t\n/>", begin); tags.push_back(new XMLTag()); tags.back()->name = str.substr(begin + 1, curr - begin - 1); while ( true ) { // Now skip some white space to see if we can find an attribute. curr = str.find_first_not_of(" \t\n", curr); if ( curr == end || curr >= close ) break; pos_t tend = str.find_first_of("= \t\n", curr); if ( tend == end || tend >= close ) break; std::string name = str.substr(curr, tend - curr); curr = str.find("=", curr) + 1; // OK now find the beginning and end of the atribute. curr = str.find_first_of("\"'", curr); if ( curr == end || curr >= close ) break; char quote = str[curr]; pos_t bega = ++curr; curr = str.find(quote, curr); while ( curr != end && str[curr - 1] == '\\' ) curr = str.find(quote, curr + 1); std::string value = str.substr(bega, curr == end? end: curr - bega); tags.back()->attr[name] = value; ++curr; } curr = close + 1; if ( str[close - 1] == '/' ) continue; pos_t endtag = str.find("name + ">", curr); if ( endtag == end ) { tags.back()->contents = str.substr(curr); curr = endtag; } else { tags.back()->contents = str.substr(curr, endtag - curr); curr = endtag + tags.back()->name.length() + 3; } std::string leftovers; tags.back()->tags = findXMLTags(tags.back()->contents, &leftovers); if ( leftovers.find_first_not_of(" \t\n") == end ) leftovers=""; tags.back()->contents = leftovers; } return tags; } /** * Delete all tags in a vector. */ static void deleteAll(std::vector & tags) { while ( tags.size() && tags.back() ) { delete tags.back(); tags.pop_back(); } } /** * Print out this tag to a stream. */ void print(std::ostream & os) const { if ( name.empty() ) { os << contents; return; } os << "<" << name; for ( AttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it ) os << oattr(it->first, it->second); if ( contents.empty() && tags.empty() ) { os << "/>" << std::endl; return; } os << ">"; for ( int i = 0, N = tags.size(); i < N; ++i ) tags[i]->print(os); os << contents << "" << std::endl; } }; /** * Helper function to make sure that each line in the string \a s starts with a * #-character and that the string ends with a new-line. */ inline std::string hashline(std::string s) { std::string ret; std::istringstream is(s); std::string ss; while ( getline(is, ss) ) { if ( ss.empty() ) continue; if ( ss.find_first_not_of(" \t") == std::string::npos ) continue; if ( ss.find('#') == std::string::npos || ss.find('#') != ss.find_first_not_of(" \t") ) ss = "# " + ss; ret += ss + '\n'; } return ret; } /** * This is the base class of all classes representing xml tags. */ struct TagBase { /** * Convenient typedef. */ typedef XMLTag::AttributeMap AttributeMap; /** * Default constructor does nothing. */ TagBase() {} /** * Main constructor stores the attributes and contents of a tag. */ TagBase(const AttributeMap & attr, std::string conts = std::string()): attributes(attr), contents(conts) {} /** * Find an attribute named \a n and set the double variable \a v to * the corresponding value. Remove the correspondig attribute from * the list if found and \a erase is true. @return false if no * attribute was found. */ bool getattr(std::string n, double & v, bool erase = true) { AttributeMap::iterator it = attributes.find(n); if ( it == attributes.end() ) return false; v = std::atof(it->second.c_str()); if ( erase) attributes.erase(it); return true; } /** * Find an attribute named \a n and set the bool variable \a v to * true if the corresponding value is "yes". Remove the correspondig * attribute from the list if found and \a erase is true. @return * false if no attribute was found. */ bool getattr(std::string n, bool & v, bool erase = true) { AttributeMap::iterator it = attributes.find(n); if ( it == attributes.end() ) return false; if ( it->second == "yes" ) v = true; if ( erase) attributes.erase(it); return true; } /** * Find an attribute named \a n and set the long variable \a v to * the corresponding value. Remove the correspondig attribute from * the list if found and \a erase is true. @return false if no * attribute was found. */ bool getattr(std::string n, long & v, bool erase = true) { AttributeMap::iterator it = attributes.find(n); if ( it == attributes.end() ) return false; v = std::atoi(it->second.c_str()); if ( erase) attributes.erase(it); return true; } /** * Find an attribute named \a n and set the long variable \a v to * the corresponding value. Remove the correspondig attribute from * the list if found and \a erase is true. @return false if no * attribute was found. */ bool getattr(std::string n, int & v, bool erase = true) { AttributeMap::iterator it = attributes.find(n); if ( it == attributes.end() ) return false; v = int(std::atoi(it->second.c_str())); if ( erase) attributes.erase(it); return true; } /** * Find an attribute named \a n and set the string variable \a v to * the corresponding value. Remove the correspondig attribute from * the list if found and \a erase is true. @return false if no * attribute was found. */ bool getattr(std::string n, std::string & v, bool erase = true) { AttributeMap::iterator it = attributes.find(n); if ( it == attributes.end() ) return false; v = it->second; if ( erase) attributes.erase(it); return true; } /** * print out ' name="value"' for all unparsed attributes. */ void printattrs(std::ostream & file) const { for ( AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); ++ it ) file << oattr(it->first, it->second); } /** * Print out end of tag marker. Print contents if not empty else * print simple close tag. */ void closetag(std::ostream & file, std::string tag) const { if ( contents.empty() ) file << "/>\n"; else if ( contents.find('\n') != std::string::npos ) file << ">\n" << contents << "\n\n"; else file << ">" << contents << "\n"; } /** * The attributes of this tag; */ XMLTag::AttributeMap attributes; /** * The contents of this tag. */ mutable std::string contents; /** * Static string token for truth values. */ static std::string yes() { return "yes"; } }; /** * The Generator class contains information about a generator used in a run. */ struct Generator : public TagBase { /** * Construct from XML tag. */ Generator(const XMLTag & tag) : TagBase(tag.attr, tag.contents) { getattr("name", name); getattr("version", version); } /** * Print the object as a generator tag. */ void print(std::ostream & file) const { file << " neve ) file << oattr("ntries", ntries); if ( xsecerr > 0.0 ) file << oattr("xsecerr", xsecerr); if ( !weightname.empty() ) file << oattr("weightname", weightname); if ( negweights ) file << oattr("negweights", yes()); if ( varweights ) file << oattr("varweights", yes()); printattrs(file); closetag(file, "xsecinfo"); } /** * The number of events. */ long neve; /** * The number of attempte that was needed to produce the neve events. */ long ntries; /** * The total cross section in pb. */ double totxsec; /** * The estimated statistical error on totxsec. */ double xsecerr; /** * The maximum weight. */ double maxweight; /** * The average weight. */ double meanweight; /** * Does the file contain negative weights? */ bool negweights; /** * Does the file contain varying weights? */ bool varweights; /** * The named weight to which this object belongs. */ std::string weightname; }; /** * Convenient Alias. */ typedef std::map XSecInfos; /** * Simple struct to store information about separate eventfiles to be * loaded. */ struct EventFile : public TagBase { /** * Intitialize default values. */ EventFile(): filename(""), neve(-1), ntries(-1) {} /** * Create from XML tag */ EventFile(const XMLTag & tag) : TagBase(tag.attr, tag.contents), filename(""), neve(-1), ntries(-1) { if ( !getattr("name", filename) ) throw std::runtime_error("Found eventfile tag without name attribute " "in Les Houches Event File."); getattr("neve", neve); ntries = neve; getattr("ntries", ntries); } /** * Print out an XML tag. */ void print(std::ostream & file) const { if ( filename.empty() ) return; file << " 0 ) file << oattr("neve", neve); if ( ntries > neve ) file << oattr("ntries", ntries); printattrs(file); closetag(file, "eventfile"); } /** * The name of the file. */ std::string filename; /** * The number of events. */ long neve; /** * The number of attempte that was needed to produce the neve events. */ long ntries; }; /** * The Cut class represents a cut used by the Matrix Element generator. */ struct Cut : public TagBase { /** * Intitialize default values. */ Cut(): min(-0.99*std::numeric_limits::max()), max(0.99*std::numeric_limits::max()) {} /** * Create from XML tag. */ Cut(const XMLTag & tag, const std::map >& ptypes) : TagBase(tag.attr), min(-0.99*std::numeric_limits::max()), max(0.99*std::numeric_limits::max()) { if ( !getattr("type", type) ) throw std::runtime_error("Found cut tag without type attribute " "in Les Houches file"); long tmp; if ( tag.getattr("p1", np1) ) { if ( ptypes.find(np1) != ptypes.end() ) { p1 = ptypes.find(np1)->second; attributes.erase("p1"); } else { getattr("p1", tmp); p1.insert(tmp); np1 = ""; } } if ( tag.getattr("p2", np2) ) { if ( ptypes.find(np2) != ptypes.end() ) { p2 = ptypes.find(np2)->second; attributes.erase("p2"); } else { getattr("p2", tmp); p2.insert(tmp); np2 = ""; } } std::istringstream iss(tag.contents); iss >> min; if ( iss >> max ) { if ( min >= max ) min = -0.99*std::numeric_limits::max(); } else max = 0.99*std::numeric_limits::max(); } /** * Print out an XML tag. */ void print(std::ostream & file) const { file << ""; if ( min > -0.9*std::numeric_limits::max() ) file << min; else file << max; if ( max < 0.9*std::numeric_limits::max() ) file << " " << max; if ( !contents.empty() ) file << std::endl << contents << std::endl; file << "" << std::endl; } /** * Check if a \a id1 matches p1 and \a id2 matches p2. Only non-zero * values are considered. */ bool match(long id1, long id2 = 0) const { std::pair ret(false, false); if ( !id2 ) ret.second = true; if ( !id1 ) ret.first = true; if ( p1.find(0) != p1.end() ) ret.first = true; if ( p1.find(id1) != p1.end() ) ret.first = true; if ( p2.find(0) != p2.end() ) ret.second = true; if ( p2.find(id2) != p2.end() ) ret.second = true; return ret.first && ret.second; } /** * Check if the particles given as a vector of PDG \a id numbers, * and a vector of vectors of momentum components, \a p, will pass * the cut defined in this event. */ bool passCuts(const std::vector & id, const std::vector< std::vector >& p ) const { if ( ( type == "m" && !p2.size() ) || type == "kt" || type == "eta" || type == "y" || type == "E" ) { for ( int i = 0, N = id.size(); i < N; ++i ) if ( match(id[i]) ) { if ( type == "m" ) { double v = p[i][4]*p[i][4] - p[i][3]*p[i][3] - p[i][2]*p[i][2] - p[i][1]*p[i][1]; v = v >= 0.0? std::sqrt(v): -std::sqrt(-v); if ( outside(v) ) return false; } else if ( type == "kt" ) { if ( outside(std::sqrt(p[i][2]*p[i][2] + p[i][1]*p[i][1])) ) return false; } else if ( type == "E" ) { if ( outside(p[i][4]) ) return false; } else if ( type == "eta" ) { if ( outside(eta(p[i])) ) return false; } else if ( type == "y" ) { if ( outside(rap(p[i])) ) return false; } } } else if ( type == "m" || type == "deltaR" ) { for ( int i = 1, N = id.size(); i < N; ++i ) for ( int j = 0; j < i; ++j ) if ( match(id[i], id[j]) || match(id[j], id[i]) ) { if ( type == "m" ) { double v = (p[i][4] + p[j][4])*(p[i][4] + p[j][4]) - (p[i][3] + p[j][3])*(p[i][3] + p[j][3]) - (p[i][2] + p[j][2])*(p[i][2] + p[j][2]) - (p[i][1] + p[j][1])*(p[i][1] + p[j][1]); v = v >= 0.0? std::sqrt(v): -std::sqrt(-v); if ( outside(v) ) return false; } else if ( type == "deltaR" ) { if ( outside(deltaR(p[i], p[j])) ) return false; } } } else if ( type == "ETmiss" ) { double x = 0.0; double y = 0.0; for ( int i = 0, N = id.size(); i < N; ++i ) if ( match(id[i]) && !match(0, id[i]) ) { x += p[i][1]; y += p[i][2]; } if ( outside(std::sqrt(x*x + y*y)) ) return false; } else if ( type == "HT" ) { double pt = 0.0; for ( int i = 0, N = id.size(); i < N; ++i ) if ( match(id[i]) && !match(0, id[i]) ) pt += std::sqrt(p[i][1]*p[i][1] + p[i][2]*p[i][2]); if ( outside(pt) ) return false; } return true; } /** * Return the pseudorapidity of a particle with momentum \a p. */ static double eta(const std::vector & p) { double pt2 = p[2]*p[2] + p[1]*p[1]; if ( pt2 != 0.0 ) { double dum = std::sqrt(pt2 + p[3]*p[3]) + p[3]; if ( dum != 0.0 ) return std::log(dum/std::sqrt(pt2)); } return p[3] < 0.0? -std::numeric_limits::max(): std::numeric_limits::max(); } /** * Return the true rapidity of a particle with momentum \a p. */ static double rap(const std::vector & p) { double pt2 = p[5]*p[5] + p[2]*p[2] + p[1]*p[1]; if ( pt2 != 0.0 ) { double dum = std::sqrt(pt2 + p[3]*p[3]) + p[3]; if ( dum != 0.0 ) return std::log(dum/std::sqrt(pt2)); } return p[3] < 0.0? -std::numeric_limits::max(): std::numeric_limits::max(); } /** * Return the delta-R of a particle pair with momenta \a p1 and \a p2. */ static double deltaR(const std::vector & p1, const std::vector & p2) { double deta = eta(p1) - eta(p2); double dphi = std::atan2(p1[1], p1[2]) - std::atan2(p2[1], p2[2]); if ( dphi > M_PI ) dphi -= 2.0*M_PI; if ( dphi < -M_PI ) dphi += 2.0*M_PI; return std::sqrt(dphi*dphi + deta*deta); } /** * Return true if the given \a value is outside limits. */ bool outside(double value) const { return value < min || value >= max; } /** * The variable in which to cut. */ std::string type; /** * The first types particle types for which this cut applies. */ std::set p1; /** * Symbolic name for p1. */ std::string np1; /** * The second type of particles for which this cut applies. */ std::set p2; /** * Symbolic name for p1. */ std::string np2; /** * The minimum value of the variable */ double min; /** * The maximum value of the variable */ double max; }; /** * The ProcInfo class represents the information in a procinfo tag. */ struct ProcInfo : public TagBase { /** * Intitialize default values. */ ProcInfo(): iproc(0), loops(0), qcdorder(-1), eworder(-1) {} /** * Create from XML tag. */ ProcInfo(const XMLTag & tag) : TagBase(tag.attr, tag.contents), iproc(0), loops(0), qcdorder(-1), eworder(-1) { getattr("iproc", iproc); getattr("loops", loops); getattr("qcdorder", qcdorder); getattr("eworder", eworder); getattr("rscheme", rscheme); getattr("fscheme", fscheme); getattr("scheme", scheme); } /** * Print out an XML tag. */ void print(std::ostream & file) const { file << "= 0 ) file << oattr("loops", loops); if ( qcdorder >= 0 ) file << oattr("qcdorder", qcdorder); if ( eworder >= 0 ) file<< oattr("eworder", eworder); if ( !rscheme.empty() ) file << oattr("rscheme", rscheme); if ( !fscheme.empty() ) file << oattr("fscheme", fscheme); if ( !scheme.empty() ) file << oattr("scheme", scheme); printattrs(file); closetag(file, "procinfo"); } /** * The id number for the process. */ int iproc; /** * The number of loops */ int loops; /** * The number of QCD vertices. */ int qcdorder; /** * The number of electro-weak vertices. */ int eworder; /** * The factorization scheme used. */ std::string fscheme; /** * The renormalization scheme used. */ std::string rscheme; /** * The NLO scheme used. */ std::string scheme; }; /** * The MergeInfo class represents the information in a mergeinfo tag. */ struct MergeInfo : public TagBase { /** * Intitialize default values. */ MergeInfo(): iproc(0), mergingscale(0.0), maxmult(false) {} /** * Create from XML tag. */ MergeInfo(const XMLTag & tag) : TagBase(tag.attr, tag.contents), iproc(0), mergingscale(0.0), maxmult(false) { getattr("iproc", iproc); getattr("mergingscale", mergingscale); getattr("maxmult", maxmult); } /** * Print out an XML tag. */ void print(std::ostream & file) const { file << " 0.0 ) file << oattr("mergingscale", mergingscale); if ( maxmult ) file << oattr("maxmult", yes()); printattrs(file); closetag(file, "mergeinfo"); } /** * The id number for the process. */ int iproc; /** * The merging scale used if different from the cut definitions. */ double mergingscale; /** * Is this event reweighted as if it was the maximum multiplicity. */ bool maxmult; }; /** * The WeightInfo class encodes the description of a given weight * present for all events. */ struct WeightInfo : public TagBase { /** * Constructors */ WeightInfo(): inGroup(-1), isrwgt(false), muf(1.0), mur(1.0), pdf(0), pdf2(0) {} /** * Construct from the XML tag */ WeightInfo(const XMLTag & tag) : TagBase(tag.attr, tag.contents), inGroup(-1), isrwgt(tag.name == "weight"), muf(1.0), mur(1.0), pdf(0), pdf2(0) { getattr("mur", mur); getattr("muf", muf); getattr("pdf", pdf); getattr("pdf2", pdf2); if ( isrwgt ) getattr("id", name); else getattr("name", name); } /** * Print out an XML tag. */ void print(std::ostream & file) const { if ( isrwgt ) file << " & wiv) : TagBase(tag.attr) { getattr("type", type); getattr("combine", combine); for ( int i = 0, N = tag.tags.size(); i < N; ++i ) { if ( tag.tags[i]->name == "weight" || tag.tags[i]->name == "weightinfo" ) { WeightInfo wi(*tag.tags[i]); wi.inGroup = groupIndex; wiv.push_back(wi); } } } /** * The type. */ std::string type; /** * The way in which these weights should be combined. */ std::string combine; }; /** * The Weight class represents the information in a weight tag. */ struct Weight : public TagBase { /** * Initialize default values. */ Weight(): iswgt(false), born(0.0), sudakov(0.0) {} /** * Create from XML tag */ Weight(const XMLTag & tag) : TagBase(tag.attr, tag.contents), iswgt(tag.name == "wgt"), born(0.0), sudakov(0.0) { if ( iswgt ) getattr("id", name); else getattr("name", name); getattr("born", born); getattr("sudakov", sudakov); std::istringstream iss(tag.contents); double w; while ( iss >> w ) weights.push_back(w); indices.resize(weights.size(), 0.0); } /** * Print out an XML tag. */ void print(std::ostream & file) const { if ( iswgt ) file << ""; for ( int j = 0, M = weights.size(); j < M; ++j ) file << " " << weights[j]; if ( iswgt ) file << "" << std::endl; else file << "" << std::endl; } /** * The identifyer for this set of weights. */ std::string name; /** * Is this a wgt or a weight tag */ bool iswgt; /** * The relative size of the born cross section of this event. */ double born; /** * The relative size of the sudakov applied to this event. */ double sudakov; /** * The weights of this event. */ mutable std::vector weights; /** * The indices where the weights are stored. */ std::vector indices; }; /** * The Clus class represents a clustering of two particle entries into * one as defined in a clustering tag. */ struct Clus : public TagBase { /** * Initialize default values. */ Clus(): scale(-1.0), alphas(-1.0) {} /** * Initialize default values. */ Clus(const XMLTag & tag) : TagBase(tag.attr, tag.contents), scale(-1.0), alphas(-1.0) { getattr("scale", scale); getattr("alphas", alphas); std::istringstream iss(tag.contents); iss >> p1 >> p2; if ( !( iss >> p0 ) ) p0 = p1; } /** * Print out an XML tag. */ void print(std::ostream & file) const { file << " 0.0 ) file << oattr("scale", scale); if ( alphas > 0.0 ) file << oattr("alphas", alphas); file << ">" << p1 << " " << p2; if ( p1 != p0 ) file << " " << p0; file << "" << std::endl; } /** * The first particle entry that has been clustered. */ int p1; /** * The second particle entry that has been clustered. */ int p2; /** * The particle entry corresponding to the clustered particles. */ int p0; /** * The scale in GeV associated with the clustering. */ double scale; /** * The alpha_s used in the corresponding vertex, if this was used in * the cross section. */ double alphas; }; /** * Store special scales from within a scales tag. */ struct Scale : public TagBase { /** * Empty constructor */ Scale(std::string st = "veto", int emtr = 0, double sc = 0.0) : stype(st), emitter(emtr), scale(sc) {} /** * Construct from an XML-tag. */ Scale(const XMLTag & tag) : TagBase(tag.attr, tag.contents),stype("veto"), emitter(0) { if ( !getattr("stype", stype) ) throw std::runtime_error("Found scale tag without stype attribute " "in Les Houches Event File."); std::string pattr; if ( getattr("pos", pattr) ) { std::istringstream pis(pattr); if ( !(pis >> emitter) ) emitter = 0; else { int rec = 0; while ( pis >> rec ) recoilers.insert(rec); } } std::string eattr; if ( getattr("etype", eattr) ) { if ( eattr == "QCD" ) eattr = "-5 -4 -3 -2 -1 1 2 3 4 5 21"; if ( eattr == "EW" ) eattr = "-13 -12 -11 11 12 13 22 23 24"; std::istringstream eis(eattr); int pdg = 0; while ( eis >> pdg ) emitted.insert(pdg); } std::istringstream cis(tag.contents); cis >> scale; } /** * Print out an XML tag. */ void print(std::ostream & file) const { file << " 0 ) { std::ostringstream pos; pos << emitter; for ( std::set::iterator it = recoilers.begin(); it != recoilers.end(); ++it ) pos << " " << *it; file << oattr("pos", pos.str()); } if ( emitted.size() > 0 ) { std::set::iterator it = emitted.begin(); std::ostringstream eos; eos << *it; while ( ++it != emitted.end() ) eos << " " << *it; if ( eos.str() == "-5 -4 -3 -2 -1 1 2 3 4 5 21" ) file << oattr("etype", std::string("QCD")); else if ( eos.str() == "-13 -12 -11 11 12 13 22 23 24" ) file << oattr("etype", std::string("EW")); else file << oattr("etype", eos.str()); } std::ostringstream os; os << scale; contents = os.str(); closetag(file, "scale"); } /** * The type of scale this represents. Predefined values are "veto" * and "start". */ std::string stype; /** * The emitter this scale applies to. This is the index of a * particle in HEPEUP (starting at 1). Zero corresponds to any * particle in HEPEUP. */ int emitter; /** * The set of recoilers for which this scale applies. */ std::set recoilers; /** * The set of emitted particles (PDG id) this applies to. */ std::set emitted; /** * The actual scale given. */ double scale; }; /** * Collect different scales relevant for an event. */ struct Scales : public TagBase { /** * Empty constructor. */ Scales(double defscale = -1.0, int npart = 0) : muf(defscale), mur(defscale), mups(defscale), SCALUP(defscale) { (void) npart; // avoid "unused variable" compiler warning } /** * Construct from an XML-tag */ Scales(const XMLTag & tag, double defscale = -1.0, int npart = 0) : TagBase(tag.attr, tag.contents), muf(defscale), mur(defscale), mups(defscale), SCALUP(defscale) { getattr("muf", muf); getattr("mur", mur); getattr("mups", mups); for ( int i = 0, N = tag.tags.size(); i < N; ++i ) if ( tag.tags[i]->name == "scale" ) scales.push_back(Scale(*tag.tags[i])); for ( int i = 0; i < npart; ++i ) { std::ostringstream pttag; pttag << "pt_start_" << i + 1; double sc = 0.0; if ( getattr(pttag.str(), sc) ) scales.push_back(Scale("start", i + 1, sc)); } } /** * Check if this object contains useful information besides SCALUP. */ bool hasInfo() const { return muf != SCALUP || mur != SCALUP || mups != SCALUP || !scales.empty(); } /** * Print out the corresponding XML-tag. */ void print(std::ostream & file) const { if ( !hasInfo() ) return; file << " scales; }; /** * The PDFInfo class represents the information in a pdfinto tag. */ struct PDFInfo : public TagBase { /** * Initialize default values. */ PDFInfo(double defscale = -1.0): p1(0), p2(0), x1(-1.0), x2(-1.0), xf1(-1.0), xf2(-1.0), scale(defscale), SCALUP(defscale) {} /** * Create from XML tag. */ PDFInfo(const XMLTag & tag, double defscale = -1.0) : TagBase(tag.attr, tag.contents), p1(0), p2(0), x1(-1.0), x2(-1.0), xf1(-1.0), xf2(-1.0), scale(defscale), SCALUP(defscale) { getattr("scale", scale); getattr("p1", p1); getattr("p2", p2); getattr("x1", x1); getattr("x2", x2); } /** * Print out an XML tag. */ void print(std::ostream & file) const { if ( xf1 <= 0 ) return; file << " 0 ) file << oattr("x1", x1); if ( x2 > 0 ) file << oattr("x2", x2); if ( scale != SCALUP ) file << oattr("scale", scale); printattrs(file); file << ">" << xf1 << " " << xf2 << "" << std::endl; } /** * The type of the incoming particle 1. */ long p1; /** * The type of the incoming particle 2. */ long p2; /** * The x-value used for the incoming particle 1. */ double x1; /** * The x-value used for the incoming particle 2. */ double x2; /** * The value of the pdf for the incoming particle 1. */ double xf1; /** * The value of the pdf for the incoming particle 2. */ double xf2; /** * The scale used in the PDF:s */ double scale; /** * THe default scale in the event. */ double SCALUP; }; /** * The HEPRUP class is a simple container corresponding to the Les Houches * accord (hep-ph/0109068) * common block with the same name. The members are named in the same * way as in the common block. However, fortran arrays are represented * by vectors, except for the arrays of length two which are * represented by pair objects. */ class HEPRUP : public TagBase { public: /** @name Standard constructors and destructors. */ //@{ /** * Default constructor. */ HEPRUP() : IDWTUP(0), NPRUP(0), version(3), dprec(std::numeric_limits::digits10) {} /** * Copy constructor */ HEPRUP(const HEPRUP &) = default; - /** * Assignment operator. */ HEPRUP & operator=(const HEPRUP & x) = default; + /** + * Move constructor + */ + HEPRUP(HEPRUP &&) = default; + /** + * Move assignment operator. + */ + HEPRUP & operator=(HEPRUP && x) = default; /** * Construct from a given init tag. */ HEPRUP(const XMLTag & tagin, int versin) : TagBase(tagin.attr, tagin.contents), version(versin), dprec(std::numeric_limits::digits10) { std::vector tags = tagin.tags; // The first (anonymous) tag should just be the init block. std::istringstream iss(tags[0]->contents); if ( !( iss >> IDBMUP.first >> IDBMUP.second >> EBMUP.first >> EBMUP.second >> PDFGUP.first >> PDFGUP.second >> PDFSUP.first >> PDFSUP.second >> IDWTUP >> NPRUP ) ) { throw std::runtime_error("Could not parse init block " "in Les Houches Event File."); } resize(); for ( int i = 0; i < NPRUP; ++i ) { if ( !( iss >> XSECUP[i] >> XERRUP[i] >> XMAXUP[i] >> LPRUP[i] ) ) { throw std::runtime_error("Could not parse processes in init block " "in Les Houches Event File."); } } for ( int i = 1, N = tags.size(); i < N; ++i ) { const XMLTag & tag = *tags[i]; if ( tag.name.empty() ) junk += tag.contents; if ( tag.name == "initrwgt" ) { for ( int j = 0, M = tag.tags.size(); j < M; ++j ) { if ( tag.tags[j]->name == "weightgroup" ) weightgroup.push_back(WeightGroup(*tag.tags[j], weightgroup.size(), weightinfo)); if ( tag.tags[j]->name == "weight" ) weightinfo.push_back(WeightInfo(*tag.tags[j])); } } if ( tag.name == "weightinfo" ) { weightinfo.push_back(WeightInfo(tag)); } if ( tag.name == "weightgroup" ) { weightgroup.push_back(WeightGroup(tag, weightgroup.size(), weightinfo)); } if ( tag.name == "eventfiles" ) { for ( int j = 0, M = tag.tags.size(); j < M; ++j ) { XMLTag & eftag = *tag.tags[j]; if ( eftag.name == "eventfile" ) eventfiles.push_back(EventFile(eftag)); } } if ( tag.name == "xsecinfo" ) { XSecInfo xsecinfo = XSecInfo(tag); xsecinfos[xsecinfo.weightname] = xsecinfo; } if ( tag.name == "generator" ) { generators.push_back(Generator(tag)); } else if ( tag.name == "cutsinfo" ) { for ( int j = 0, M = tag.tags.size(); j < M; ++j ) { XMLTag & ctag = *tag.tags[j]; if ( ctag.name == "ptype" ) { std::string tname = ctag.attr["name"]; long id; std::istringstream isss(ctag.contents); while ( isss >> id ) ptypes[tname].insert(id); } else if ( ctag.name == "cut" ) cuts.push_back(Cut(ctag, ptypes)); } } else if ( tag.name == "procinfo" ) { ProcInfo proc(tag); procinfo[proc.iproc] = proc; } else if ( tag.name == "mergeinfo" ) { MergeInfo merge(tag); mergeinfo[merge.iproc] = merge; } } weightmap.clear(); for ( int i = 0, N = weightinfo.size(); i < N; ++i ) weightmap[weightinfo[i].name] = i + 1; } //@} public: /** * Return the name of the weight with given index suitable to ne * used for HepMC3 output. */ std::string weightNameHepMC(int i) const { std::string name; if ( i < 0 || i >= (int)weightinfo.size() ) return name; if ( weightinfo[i].inGroup >= 0 ) name = weightgroup[weightinfo[i].inGroup].type + "/" + weightgroup[weightinfo[i].inGroup].combine + "/"; name += weightinfo[i].name; return name; } /** * Print out the corresponding XML tag to a stream. */ void print(std::ostream & file) const { using std::setw; file << std::setprecision(dprec); file << "\n" << " " << setw(8) << IDBMUP.first << " " << setw(8) << IDBMUP.second << " " << setw(14) << EBMUP.first << " " << setw(14) << EBMUP.second << " " << setw(4) << PDFGUP.first << " " << setw(4) << PDFGUP.second << " " << setw(4) << PDFSUP.first << " " << setw(4) << PDFSUP.second << " " << setw(4) << IDWTUP << " " << setw(4) << NPRUP << std::endl; for ( int i = 0; i < NPRUP; ++i ) file << " " << setw(14) << XSECUP[i] << " " << setw(14) << XERRUP[i] << " " << setw(14) << XMAXUP[i] << " " << setw(6) << LPRUP[i] << std::endl; for ( int i = 0, N = generators.size(); i < N; ++i ) generators[i].print(file); if ( !eventfiles.empty() ) { file << "\n"; for ( int i = 0, N = eventfiles.size(); i < N; ++i ) eventfiles[i].print(file); file << "\n"; } //AV if ( !xsecinfos.empty() > 0 ) if ( !xsecinfos.empty()) for ( XSecInfos::const_iterator it = xsecinfos.begin(); it != xsecinfos.end(); ++it ) if ( it->second.neve > 0 ) it->second.print(file); if ( cuts.size() > 0 ) { file << "" << std::endl; for ( std::map >::const_iterator ptit = ptypes.begin(); ptit != ptypes.end(); ++ptit ) { file << "first) << ">"; for ( std::set::const_iterator it = ptit->second.begin(); it != ptit->second.end(); ++it ) file << " " << *it; file << "" << std::endl; } for ( int i = 0, N = cuts.size(); i < N; ++i ) cuts[i].print(file); file << "" << std::endl; } for ( std::map::const_iterator it = procinfo.begin(); it != procinfo.end(); ++it ) it->second.print(file); for ( std::map::const_iterator it = mergeinfo.begin(); it != mergeinfo.end(); ++it ) it->second.print(file); bool isrwgt = false; int ingroup = -1; for ( int i = 0, N = weightinfo.size(); i < N; ++i ) { if ( weightinfo[i].isrwgt ) { if ( !isrwgt ) file << "\n"; isrwgt = true; } else { if ( isrwgt ) file << "\n"; isrwgt = false; } int group = weightinfo[i].inGroup; if ( group != ingroup ) { if ( ingroup != -1 ) file << "\n"; if ( group != -1 ) { file << "\n"; } ingroup = group; } weightinfo[i].print(file); } if ( ingroup != -1 ) file << "\n"; if ( isrwgt ) file << "\n"; file << hashline(junk) << "" << std::endl; } /** * Clear all information. */ void clear() { procinfo.clear(); mergeinfo.clear(); weightinfo.clear(); weightgroup.clear(); cuts.clear(); ptypes.clear(); junk.clear(); } /** * Set the NPRUP variable, corresponding to the number of * sub-processes, to \a nrup, and resize all relevant vectors * accordingly. */ void resize(int nrup) { NPRUP = nrup; resize(); } /** * Assuming the NPRUP variable, corresponding to the number of * sub-processes, is correctly set, resize the relevant vectors * accordingly. */ void resize() { XSECUP.resize(NPRUP); XERRUP.resize(NPRUP); XMAXUP.resize(NPRUP); LPRUP.resize(NPRUP); } /** * @return the index of the weight with the given \a name */ int weightIndex(std::string name) const { std::map::const_iterator it = weightmap.find(name); if ( it != weightmap.end() ) return it->second; return 0; } /** * @return the number of weights (including the nominial one). */ int nWeights() const { return weightmap.size() + 1; } /** * @return the XSecInfo object corresponding to the named weight \a * weithname. If no such object exists, it will be created. */ XSecInfo & getXSecInfo(std::string weightname = "") { XSecInfo & xi = xsecinfos[weightname]; xi.weightname = weightname; return xi; } /** * @return the XSecInfo object corresponding to the named weight \a * weithname. If no such object exists, an empty XSecInfo will be * returned.. */ const XSecInfo & getXSecInfo(std::string weightname = "") const { static XSecInfo noinfo; XSecInfos::const_iterator it = xsecinfos.find(weightname); if ( it != xsecinfos.end() ) return it->second; else return noinfo; } public: /** * PDG id's of beam particles. (first/second is in +/-z direction). */ std::pair IDBMUP; /** * Energy of beam particles given in GeV. */ std::pair EBMUP; /** * The author group for the PDF used for the beams according to the * PDFLib specification. */ std::pair PDFGUP; /** * The id number the PDF used for the beams according to the * PDFLib specification. */ std::pair PDFSUP; /** * Master switch indicating how the ME generator envisages the * events weights should be interpreted according to the Les Houches * accord. */ int IDWTUP; /** * The number of different subprocesses in this file. */ int NPRUP; /** * The cross sections for the different subprocesses in pb. */ std::vector XSECUP; /** * The statistical error in the cross sections for the different * subprocesses in pb. */ std::vector XERRUP; /** * The maximum event weights (in HEPEUP::XWGTUP) for different * subprocesses. */ std::vector XMAXUP; /** * The subprocess code for the different subprocesses. */ std::vector LPRUP; /** * Contents of the xsecinfo tags. */ XSecInfos xsecinfos; /** * A vector of EventFiles where the events are stored separate fron * the init block. */ std::vector eventfiles; /** * Contents of the cuts tag. */ std::vector cuts; /** * A map of codes for different particle types. */ std::map > ptypes; /** * Contents of the procinfo tags */ std::map procinfo; /** * Contents of the mergeinfo tags */ std::map mergeinfo; /** * The names of the programs and their version information used to * create this file. */ std::vector generators; /** * The vector of WeightInfo objects for this file. */ std::vector weightinfo; /** * A map relating names of weights to indices of the weightinfo vector. */ std::map weightmap; /** * The vector of WeightGroup objects in this file. */ std::vector weightgroup; /** * Just to be on the safe side we save any junk inside the init-tag. */ std::string junk; /** * The main version of the information stored. */ int version; /** * The precision used for outputing real numbers. */ int dprec; }; /** * Forward declaration of the HEPEUP class. */ class HEPEUP; /** * The EventGroup represents a set of events which are to be * considered together. */ struct EventGroup: public std::vector { /** * Initialize default values. */ inline EventGroup(): nreal(-1), ncounter(-1) {} /** * The copy constructor also copies the included HEPEUP object. */ inline EventGroup(const EventGroup &); /** * The assignment also copies the included HEPEUP object. */ inline EventGroup & operator=(const EventGroup &); /** * Remove all subevents. */ inline void clear(); /** * The destructor deletes the included HEPEUP objects. */ inline ~EventGroup(); /** * The number of real events in this event group. */ int nreal; /** * The number of counter events in this event group. */ int ncounter; }; /** * The HEPEUP class is a simple container corresponding to the Les Houches accord * (hep-ph/0109068) * common block with the same name. The members are named in the same * way as in the common block. However, fortran arrays are represented * by vectors, except for the arrays of length two which are * represented by pair objects. */ class HEPEUP : public TagBase { public: /** @name Standard constructors and destructors. */ //@{ /** * Default constructor. */ HEPEUP() : NUP(0), IDPRUP(0), XWGTUP(0.0), XPDWUP(0.0, 0.0), SCALUP(0.0), AQEDUP(0.0), AQCDUP(0.0), heprup(0), currentWeight(0), ntries(1), isGroup(false) {} /** * Copy constructor */ - HEPEUP(const HEPEUP & x) - : TagBase(x), isGroup(false) { - operator=(x); - } + HEPEUP(const HEPEUP & x) = default; + /** + * Move constructor + */ + HEPEUP(HEPEUP && x) = default; + /** + * Assignment operator. + */ + HEPEUP & operator=(const HEPEUP & x) = default; + /** + * Move Assignment operator. + */ + HEPEUP & operator=(HEPEUP && x) = default; /** * Copy information from the given HEPEUP. Sub event information is * left untouched. */ HEPEUP & setEvent(const HEPEUP & x) { NUP = x.NUP; IDPRUP = x.IDPRUP; XWGTUP = x.XWGTUP; XPDWUP = x.XPDWUP; SCALUP = x.SCALUP; AQEDUP = x.AQEDUP; AQCDUP = x.AQCDUP; IDUP = x.IDUP; ISTUP = x.ISTUP; MOTHUP = x.MOTHUP; ICOLUP = x.ICOLUP; PUP = x.PUP; VTIMUP = x.VTIMUP; SPINUP = x.SPINUP; heprup = x.heprup; namedweights = x.namedweights; weights = x.weights; pdfinfo = x.pdfinfo; PDFGUPsave = x.PDFGUPsave; PDFSUPsave = x.PDFSUPsave; clustering = x.clustering; scales = x.scales; junk = x.junk; currentWeight = x.currentWeight; ntries = x.ntries; return *this; } /** - * Assignment operator. - */ - HEPEUP & operator=(const HEPEUP & x) { - if ( &x == this ) return *this; - TagBase::operator=(x); - clear(); - setEvent(x); - subevents = x.subevents; - isGroup = x.isGroup; - return *this; - } - - /** * Destructor. */ ~HEPEUP() { clear(); }; //@} public: /** * Constructor from an event or eventgroup tag. */ HEPEUP(const XMLTag & tagin, HEPRUP & heprupin) : TagBase(tagin.attr), NUP(0), IDPRUP(0), XWGTUP(0.0), XPDWUP(0.0, 0.0), SCALUP(0.0), AQEDUP(0.0), AQCDUP(0.0), heprup(&heprupin), currentWeight(0), ntries(1), isGroup(tagin.name == "eventgroup") { if ( heprup->NPRUP < 0 ) throw std::runtime_error("Tried to read events but no processes defined " "in init block of Les Houches file."); std::vector tags = tagin.tags; if ( isGroup ) { getattr("nreal", subevents.nreal); getattr("ncounter", subevents.ncounter); for ( int i = 0, N = tags.size(); i < N; ++i ) if ( tags[i]->name == "event" ) subevents.push_back(new HEPEUP(*tags[i], heprupin)); return; } else getattr("ntries", ntries); // The event information should be in the first (anonymous) tag std::istringstream iss(tags[0]->contents); if ( !( iss >> NUP >> IDPRUP >> XWGTUP >> SCALUP >> AQEDUP >> AQCDUP ) ) throw std::runtime_error("Failed to parse event in Les Houches file."); resize(); // Read all particle lines. for ( int i = 0; i < NUP; ++i ) { if ( !( iss >> IDUP[i] >> ISTUP[i] >> MOTHUP[i].first >> MOTHUP[i].second >> ICOLUP[i].first >> ICOLUP[i].second >> PUP[i][0] >> PUP[i][1] >> PUP[i][2] >> PUP[i][3] >> PUP[i][4] >> VTIMUP[i] >> SPINUP[i] ) ) throw std::runtime_error("Failed to parse event in Les Houches file."); } junk.clear(); std::string ss; while ( getline(iss, ss) ) junk += ss + '\n'; scales = Scales(SCALUP, NUP); pdfinfo = PDFInfo(SCALUP); namedweights.clear(); weights.clear(); weights.resize(heprup->nWeights(), std::make_pair(XWGTUP, (WeightInfo*)(0))); weights.front().first = XWGTUP; for ( int i = 1, N = weights.size(); i < N; ++i ) weights[i].second = &heprup->weightinfo[i - 1]; for ( int i = 1, N = tags.size(); i < N; ++i ) { XMLTag & tag = *tags[i]; if ( tag.name.empty() ) junk += tag.contents; if ( tag.name == "weights" ) { weights.resize(heprup->nWeights(), std::make_pair(XWGTUP, (WeightInfo*)(0))); weights.front().first = XWGTUP; for ( int ii = 1, NN = weights.size(); ii < NN; ++ii ) weights[ii].second = &heprup->weightinfo[ii - 1]; double w = 0.0; int iii = 0; std::istringstream isss(tag.contents); while ( isss >> w ) if ( ++iii < int(weights.size()) ) weights[iii].first = w; else weights.push_back(std::make_pair(w, (WeightInfo*)(0))); } if ( tag.name == "weight" ) { namedweights.push_back(Weight(tag)); } if ( tag.name == "rwgt" ) { for ( int j = 0, M = tag.tags.size(); j < M; ++j ) { if ( tag.tags[j]->name == "wgt" ) { namedweights.push_back(Weight(*tag.tags[j])); } } } else if ( tag.name == "clustering" ) { for ( int j = 0, M= tag.tags.size(); j < M; ++j ) { if ( tag.tags[j]->name == "clus" ) clustering.push_back(Clus(*tag.tags[j])); } } else if ( tag.name == "pdfinfo" ) { pdfinfo = PDFInfo(tag, SCALUP); } else if ( tag.name == "scales" ) { scales = Scales(tag, SCALUP, NUP); } } for ( int i = 0, N = namedweights.size(); i < N; ++i ) { int indx = heprup->weightIndex(namedweights[i].name); if ( indx > 0 ) { weights[indx].first = namedweights[i].weights[0]; namedweights[i].indices[0] = indx; } else { weights.push_back(std::make_pair(namedweights[i].weights[0], (WeightInfo*)(0))); namedweights[i].indices[0] = weights.size() - 1; } for ( int j = 1, M = namedweights[i].weights.size(); j < M; ++j ) { weights.push_back(std::make_pair(namedweights[i].weights[j], (WeightInfo*)(0))); namedweights[i].indices[j] = weights.size() - 1; } } } /** * Print out the event (group) as an XML tag. */ void print(std::ostream & file) const { file << std::setprecision(heprup->dprec); using std::setw; if ( isGroup ) { file << " 0 ) file << oattr("nreal", subevents.nreal); if ( subevents.ncounter > 0 ) file << oattr("ncounter", subevents.ncounter); printattrs(file); file << ">\n"; for ( int i = 0, N = subevents.size(); i < N; ++i ) subevents[i]->print(file); file << "\n"; return; } file << " 1 ) file << oattr("ntries", ntries); printattrs(file); file << ">\n"; file << " " << setw(4) << NUP << " " << setw(6) << IDPRUP << " " << setw(14) << XWGTUP << " " << setw(14) << SCALUP << " " << setw(14) << AQEDUP << " " << setw(14) << AQCDUP << "\n"; for ( int i = 0; i < NUP; ++i ) file << " " << setw(8) << IDUP[i] << " " << setw(2) << ISTUP[i] << " " << setw(4) << MOTHUP[i].first << " " << setw(4) << MOTHUP[i].second << " " << setw(4) << ICOLUP[i].first << " " << setw(4) << ICOLUP[i].second << " " << setw(14) << PUP[i][0] << " " << setw(14) << PUP[i][1] << " " << setw(14) << PUP[i][2] << " " << setw(14) << PUP[i][3] << " " << setw(14) << PUP[i][4] << " " << setw(1) << VTIMUP[i] << " " << setw(1) << SPINUP[i] << std::endl; if ( weights.size() > 0 ) { file << ""; for ( int i = 1, N = weights.size(); i < N; ++i ) file << " " << weights[i].first; file << "\n"; } bool iswgt = false; for ( int i = 0, N = namedweights.size(); i < N; ++i ) { if ( namedweights[i].iswgt ) { if ( !iswgt ) file << "\n"; iswgt = true; } else { if ( iswgt ) file << "\n"; iswgt = false; } for ( int j = 0, M = namedweights[i].indices.size(); j < M; ++j ) namedweights[i].weights[j] = weight(namedweights[i].indices[j]); namedweights[i].print(file); } if ( iswgt ) file << "\n"; if ( !clustering.empty() ) { file << "" << std::endl; for ( int i = 0, N = clustering.size(); i < N; ++i ) clustering[i].print(file); file << "" << std::endl; } pdfinfo.print(file); scales.print(file); file << hashline(junk) << "\n"; } /** * Reset the HEPEUP object (does not touch the sub events). */ void reset() { setWeightInfo(0); NUP = 0; clustering.clear(); weights.clear(); } /** * Clear the HEPEUP object. */ void clear() { reset(); subevents.clear(); } /** * Set the NUP variable, corresponding to the number of particles in * the current event, to \a nup, and resize all relevant vectors * accordingly. */ void resize(int nup) { NUP = nup; resize(); } /** * Return the total weight for this event (including all sub * evenets) for the given index. */ double totalWeight(int i = 0) const { if ( subevents.empty() ) return weight(i); double w = 0.0; for ( int ii = 0, N = subevents.size(); ii < N; ++ii ) w += subevents[ii]->weight(i); return w; } /** * Return the total weight for this event (including all sub * evenets) for the given weight name. */ double totalWeight(std::string name) const { return totalWeight(heprup->weightIndex(name)); } /** * Return the weight for the given index. */ double weight(int i = 0) const { return weights[i].first; } /** * Return the weight for the given weight name. */ double weight(std::string name) const { return weight(heprup->weightIndex(name)); } /** * Set the weight with the given index. */ void setWeight(int i, double w) { weights[i].first = w; } /** * Set the weight with the given name. */ bool setWeight(std::string name, double w) { int i = heprup->weightIndex(name); if ( i >= int(weights.size()) ) return false; setWeight(i, w); return true; } /** * Assuming the NUP variable, corresponding to the number of * particles in the current event, is correctly set, resize the * relevant vectors accordingly. */ void resize() { IDUP.resize(NUP); ISTUP.resize(NUP); MOTHUP.resize(NUP); ICOLUP.resize(NUP); PUP.resize(NUP, std::vector(5)); VTIMUP.resize(NUP); SPINUP.resize(NUP); } /** * Setup the current event to use weight i. If zero, the default * weight will be used. */ bool setWeightInfo(unsigned int i) { if ( i >= weights.size() ) return false; if ( currentWeight ) { scales.mur /= currentWeight->mur; scales.muf /= currentWeight->muf; heprup->PDFGUP = PDFGUPsave; heprup->PDFSUP = PDFSUPsave; } XWGTUP = weights[i].first; currentWeight = weights[i].second; if ( currentWeight ) { scales.mur *= currentWeight->mur; scales.muf *= currentWeight->muf; PDFGUPsave = heprup->PDFGUP; PDFSUPsave = heprup->PDFSUP; if ( currentWeight->pdf ) { heprup->PDFGUP.first = heprup->PDFGUP.second = 0; heprup->PDFSUP.first = heprup->PDFSUP.second = currentWeight->pdf; } if ( currentWeight->pdf2 ) { heprup->PDFSUP.second = currentWeight->pdf2; } } return true; } /** * Setup the current event to use sub event i. If zero, no sub event * will be chsen. */ bool setSubEvent(unsigned int i) { if ( i > subevents.size() || subevents.empty() ) return false; if ( i == 0 ) { reset(); weights = subevents[0]->weights; for ( int ii = 1, N = subevents.size(); ii < N; ++ii ) for ( int j = 0, M = weights.size(); j < M; ++j ) weights[j].first += subevents[ii]->weights[j].first; currentWeight = 0; } else { setEvent(*subevents[i - 1]); } return true; } public: /** * The number of particle entries in the current event. */ int NUP; /** * The subprocess code for this event (as given in LPRUP). */ int IDPRUP; /** * The weight for this event. */ double XWGTUP; /** * The PDF weights for the two incoming partons. Note that this * variable is not present in the current LesHouches accord * (hep-ph/0109068), * hopefully it will be present in a future accord. */ std::pair XPDWUP; /** * The scale in GeV used in the calculation of the PDF's in this * event. */ double SCALUP; /** * The value of the QED coupling used in this event. */ double AQEDUP; /** * The value of the QCD coupling used in this event. */ double AQCDUP; /** * The PDG id's for the particle entries in this event. */ std::vector IDUP; /** * The status codes for the particle entries in this event. */ std::vector ISTUP; /** * Indices for the first and last mother for the particle entries in * this event. */ std::vector< std::pair > MOTHUP; /** * The colour-line indices (first(second) is (anti)colour) for the * particle entries in this event. */ std::vector< std::pair > ICOLUP; /** * Lab frame momentum (Px, Py, Pz, E and M in GeV) for the particle * entries in this event. */ std::vector< std::vector > PUP; /** * Invariant lifetime (c*tau, distance from production to decay in * mm) for the particle entries in this event. */ std::vector VTIMUP; /** * Spin info for the particle entries in this event given as the * cosine of the angle between the spin vector of a particle and the * 3-momentum of the decaying particle, specified in the lab frame. */ std::vector SPINUP; /** * A pointer to the current HEPRUP object. */ HEPRUP * heprup; /** * The current weight info object. */ const WeightInfo * currentWeight; /** * The weights associated with this event */ std::vector namedweights; /** * The weights for this event and their corresponding WeightInfo object. */ std::vector< std::pair > weights; /** * Contents of the clustering tag. */ std::vector clustering; /** * Contents of the pdfinfo tag. */ PDFInfo pdfinfo; /** * Saved information about pdfs if different in a selected weight. */ std::pair PDFGUPsave; /** * Saved information about pdfs if different in a selected weight. */ std::pair PDFSUPsave; /** * Contents of the scales tag */ Scales scales; /** * The number of attempts the ME generator did before accepting this * event. */ int ntries; /** * Is this an event or an event group? */ bool isGroup; /** * If this is not a single event, but an event group, the events * included in the group are in this vector; */ EventGroup subevents; /** * Save junk stuff in events just to be on the safe side */ std::string junk; }; // Destructor implemented here. inline void EventGroup::clear() { while ( size() > 0 ) { delete back(); pop_back(); } } inline EventGroup::~EventGroup() { clear(); } inline EventGroup::EventGroup(const EventGroup & eg) : std::vector(eg.size()) { for ( int i = 0, N = eg.size(); i < N; ++i ) at(i) = new HEPEUP(*eg.at(i)); } inline EventGroup & EventGroup::operator=(const EventGroup & x) { if ( &x == this ) return *this; clear(); nreal = x.nreal; ncounter = x.ncounter; for ( int i = 0, N = x.size(); i < N; ++i ) push_back(new HEPEUP(*x.at(i))); return *this; } /** * The Reader class is initialized with a stream from which to read a * version 1/2 Les Houches Accord event file. In the constructor of * the Reader object the optional header information is read and then * the mandatory init is read. After this the whole header block * including the enclosing lines with tags are available in the public * headerBlock member variable. Also the information from the init * block is available in the heprup member variable and any additional * comment lines are available in initComments. After each successful * call to the readEvent() function the standard Les Houches Accord * information about the event is available in the hepeup member * variable and any additional comments in the eventComments * variable. A typical reading sequence would look as follows: * * */ class Reader { public: /** * Initialize the Reader with a stream from which to read an event * file. After the constructor is called the whole header block * including the enclosing lines with tags are available in the * public headerBlock member variable. Also the information from the * init block is available in the heprup member variable and any * additional comment lines are available in initComments. * * @param is the stream to read from. */ Reader(std::istream & is) : file(&is), currevent(-1), curreventfile(-1), currfileevent(-1), dirpath("") { init(); } /** * Initialize the Reader with a filename from which to read an event * file. After the constructor is called the whole header block * including the enclosing lines with tags are available in the * public headerBlock member variable. Also the information from the * init block is available in the heprup member variable and any * additional comment lines are available in initComments. * * @param filename the name of the file to read from. */ Reader(std::string filename) : intstream(filename.c_str()), file(&intstream), currevent(-1), curreventfile(-1), currfileevent(-1), dirpath("") { size_t slash = filename.find_last_of('/'); if ( slash != std::string::npos ) dirpath = filename.substr(0, slash + 1); init(); } private: /** * Used internally in the constructors to read header and init * blocks. */ void init() { // initialize reading from multi-file runs. initfile = file; bool readingHeader = false; bool readingInit = false; // Make sure we are reading a LHEF file: getline(); if ( !currentFind(" tag. while ( getline() && !currentFind("") ) { if ( currentFind("") ) { // We have hit the init block readingInit = true; initComments = currentLine + "\n"; } else if ( currentFind("") ) { // The end of the header block. Dump this line as well to the // headerBlock and we're done. readingHeader = false; headerBlock += currentLine + "\n"; } else if ( readingHeader ) { // We are in the process of reading the header block. Dump the // line to haderBlock. headerBlock += currentLine + "\n"; } else if ( readingInit ) { // Here we found a comment line. Dump it to initComments. initComments += currentLine + "\n"; } else { // We found some other stuff outside the standard tags. outsideBlock += currentLine + "\n"; } } if ( !currentFind("") ) throw std::runtime_error("Found incomplete init tag in " "Les Houches file."); initComments += currentLine + "\n"; std::vector tags = XMLTag::findXMLTags(initComments); for ( int i = 0, N = tags.size(); i < N; ++i ) if ( tags[i]->name == "init" ) { heprup = HEPRUP(*tags[i], version); break; } XMLTag::deleteAll(tags); if ( !heprup.eventfiles.empty() ) openeventfile(0); } public: /** * Read an event from the file and store it in the hepeup * object. Optional comment lines are stored i the eventComments * member variable. * @return true if the read sas successful. */ bool readEvent() { // Check if the initialization was successful. Otherwise we will // not read any events. if ( heprup.NPRUP < 0 ) return false; std::string eventLines; int inEvent = 0; // Keep reading lines until we hit the end of an event or event group. while ( getline() ) { if ( inEvent ) { eventLines += currentLine + "\n"; if ( inEvent == 1 && currentFind("") ) break; if ( inEvent == 2 && currentFind("") ) break; } else if ( currentFind("") ) || ( inEvent == 2 && !currentFind("") ) ) { if ( heprup.eventfiles.empty() || ++curreventfile >= int(heprup.eventfiles.size()) ) return false; openeventfile(curreventfile); return readEvent(); } std::vector tags = XMLTag::findXMLTags(eventLines); for ( int i = 0, N = tags.size(); i < N ; ++i ) { if ( tags[i]->name == "event" || tags[i]->name == "eventgroup" ) { hepeup = HEPEUP(*tags[i], heprup); XMLTag::deleteAll(tags); ++currevent; if ( curreventfile >= 0 ) ++currfileevent; return true; } } if ( !heprup.eventfiles.empty() && ++curreventfile < int(heprup.eventfiles.size()) ) { openeventfile(curreventfile); return readEvent(); } XMLTag::deleteAll(tags); return false; } /** * Open the efentfile with index ifile. If another eventfile is * being read, its remaining contents is discarded. This is a noop * if current read session is not a multi-file run. */ void openeventfile(int ifile) { std::cerr << "opening file " << ifile << std::endl; efile.close(); std::string fname = heprup.eventfiles[ifile].filename; if ( fname[0] != '/' ) fname = dirpath + fname; efile.open(fname.c_str()); if ( !efile ) throw std::runtime_error("Could not open event file " + fname); file = &efile; curreventfile = ifile; currfileevent = 0; } protected: /** * Used internally to read a single line from the stream. */ bool getline() { return ( (bool)std::getline(*file, currentLine) ); } /** * @return true if the current line contains the given string. */ bool currentFind(std::string str) const { return currentLine.find(str) != std::string::npos; } protected: /** * A local stream which is unused if a stream is supplied from the * outside. */ std::ifstream intstream; /** * The stream we are reading from. This may be a pointer to an * external stream or the internal intstream, or a separate event * file from a multi-file run */ std::istream * file; /** * The original stream from where we read the init block. */ std::istream * initfile; /** * A separate stream for reading multi-file runs. */ std::ifstream efile; /** * The last line read in from the stream in getline(). */ std::string currentLine; public: /** * XML file version */ int version; /** * All lines (since the last readEvent()) outside the header, init * and event tags. */ std::string outsideBlock; /** * All lines from the header block. */ std::string headerBlock; /** * The standard init information. */ HEPRUP heprup; /** * Additional comments found in the init block. */ std::string initComments; /** * The standard information about the last read event. */ HEPEUP hepeup; /** * Additional comments found with the last read event. */ std::string eventComments; /** * The number of the current event (starting from 1). */ int currevent; /** * The current event file being read from (-1 means there are no * separate event files). */ int curreventfile; /** * The number of the current event in the current event file. */ int currfileevent; /** * The directory from where we are reading files. */ std::string dirpath; private: /** * The default constructor should never be used. */ Reader(); /** * The copy constructor should never be used. */ Reader(const Reader &); /** * The Reader cannot be assigned to. */ Reader & operator=(const Reader &); }; /** * The Writer class is initialized with a stream to which to write a * version 1.0 Les Houches Accord event file. In the constructor of * the Writer object the main XML tag is written out, with the * corresponding end tag is written in the destructor. After a Writer * object has been created, it is possible to assign standard init * information in the heprup member variable. In addition any XML * formatted information can be added to the headerBlock member * variable (directly or via the addHeader() function). Further * comment line (beginning with a # character) can be * added to the initComments variable (directly or with the * addInitComment() function). After this information is set, it * should be written out to the file with the init() function. * * Before each event is written out with the writeEvent() function, * the standard event information can then be assigned to the hepeup * variable and optional comment lines (beginning with a * # character) may be given to the eventComments * variable (directly or with the addEventComment() function). * */ class Writer { public: #ifndef HEPMC3_PYTHON_BINDINGS /** * Create a Writer object giving a stream to write to. * @param os the stream where the event file is written. */ Writer(std::ostream & os) : file(&os), initfile(&os), dirpath("") { } #endif /** * Create a Writer object giving a filename to write to. * @param filename the name of the event file to be written. */ Writer(std::string filename) : intstream(filename.c_str()), file(&intstream), initfile(&intstream), dirpath("") { size_t slash = filename.find_last_of('/'); if ( slash != std::string::npos ) dirpath = filename.substr(0, slash + 1); } /** * The destructor writes out the final XML end-tag. */ ~Writer() { file = initfile; if ( !heprup.eventfiles.empty() ) { if ( curreventfile >= 0 && curreventfile < int(heprup.eventfiles.size()) && heprup.eventfiles[curreventfile].neve < 0 ) heprup.eventfiles[curreventfile].neve = currfileevent; writeinit(); } *file << "" << std::endl; } #ifndef HEPMC3_PYTHON_BINDINGS /** * Add header lines consisting of XML code with this stream. */ std::ostream & headerBlock() { return headerStream; } /** * Add comment lines to the init block with this stream. */ std::ostream & initComments() { return initStream; } /** * Add comment lines to the next event to be written out with this stream. */ std::ostream & eventComments() { return eventStream; } #endif /** * Add header lines consisting of XML code with this stream. */ void headerBlock(const std::string& a) { headerStream<= int(heprup.eventfiles.size()) ) return false; if ( curreventfile >= 0 ) { EventFile & ef = heprup.eventfiles[curreventfile]; if ( ef.neve > 0 && ef.neve != currfileevent ) std::cerr << "LHEF::Writer number of events in event file " << ef.filename << " does not match the given number." << std::endl; ef.neve = currfileevent; } efile.close(); std::string fname = heprup.eventfiles[ifile].filename; if ( fname[0] != '/' ) fname = dirpath + fname; efile.open(fname.c_str()); if ( !efile ) throw std::runtime_error("Could not open event file " + fname); std::cerr << "Opened event file " << fname << std::endl; file = &efile; curreventfile = ifile; currfileevent = 0; return true; } /** * Write out an optional header block followed by the standard init * block information together with any comment lines. */ void writeinit() { // Write out the standard XML tag for the event file. if ( heprup.version == 3 ) *file << "\n"; else if ( heprup.version == 2 ) *file << "\n"; else *file << "\n"; *file << std::setprecision(10); using std::setw; std::string headBlock = headerStream.str(); if ( headBlock.length() ) { if ( headBlock.find("
") == std::string::npos ) *file << "
\n"; if ( headBlock[headBlock.length() - 1] != '\n' ) headBlock += '\n'; *file << headBlock; if ( headBlock.find("
") == std::string::npos ) *file << "
\n"; } heprup.print(*file); } /** * Write the current HEPEUP object to the stream; */ void writeEvent() { if ( !heprup.eventfiles.empty() ) { if ( currfileevent == heprup.eventfiles[curreventfile].neve && curreventfile + 1 < int(heprup.eventfiles.size()) ) openeventfile(curreventfile + 1); } hepeup.print(*file); ++lastevent; ++currfileevent; } protected: /** * A local stream which is unused if a stream is supplied from the * outside. */ std::ofstream intstream; /** * The stream we are writing to. This may be a reference to an * external stream or the internal intstream. */ std::ostream * file; /** * The original stream from where we read the init block. */ std::ostream * initfile; /** * A separate stream for reading multi-file runs. */ std::ofstream efile; /** * The number of the last event written (starting from 1). */ int lastevent; /** * The current event file being written to (-1 means there are no * separate event files). */ int curreventfile; /** * The number of the current event in the current event file. */ int currfileevent; /** * The directory from where we are reading files. */ std::string dirpath; public: /** * The standard init information. */ HEPRUP heprup; /** * The standard information about the event we will write next. */ HEPEUP hepeup; private: /** * Stream to add all lines in the header block. */ std::ostringstream headerStream; /** * Stream to add additional comments to be put in the init block. */ std::ostringstream initStream; /** * Stream to add additional comments to be written together the next event. */ std::ostringstream eventStream; /** * The default constructor should never be used. */ Writer(); /** * The copy constructor should never be used. */ Writer(const Writer &); /** * The Writer cannot be assigned to. */ Writer & operator=(const Writer &); }; } /* \example LHEFCat.cc This is a main function which simply reads a Les Houches Event File from the standard input and writes it again to the standard output. This file can be downloaded from here. There are also two sample event files, ttV_unweighted_events.lhe and testlhef3.lhe to try it on. */ /* \mainpage Les Houches Event File Here are some example classes for reading and writing Les Houches Event Files according to the proposal by Torbjörn Sjöstrand discussed at the MC4LHC workshop at CERN 2006. The classes has now been updated to handle the suggested version 3 of this file standard as discussed at the Les Houches workshop 2013 (The previous suggested version 2.0 was discussed at the Les Houches workshop 2009). There is a whole set of classes available in a single header file called LHEF.h. The idea is that matrix element or parton shower generators will implement their own wrapper using these classes as containers. The two classes LHEF::HEPRUP and LHEF::HEPEUP are simple container classes which contain the same information as the Les Houches standard Fortran common blocks with the same names. They also contain the extra information defined in version 3 in the standard. The other two main classes are called LHEF::Reader and LHEF::Writer and are used to read and write Les Houches Event Files Here are a few examples of how to use the classes: \namespace LHEF The LHEF namespace contains some example classes for reading and writing Les Houches Event Files according to the proposal by Torbjörn Sjöstrand discussed at the MC4LHC workshop at CERN 2006. */ #endif /* HEPMC3_LHEF_H */ diff --git a/src/BufferedEventReader.cc b/src/BufferedEventReader.cc index 211e83e..10452a8 100644 --- a/src/BufferedEventReader.cc +++ b/src/BufferedEventReader.cc @@ -1,20 +1,20 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/BufferedEventReader.hh" -namespace HEJ{ +namespace HEJ { bool BufferedEventReader::read_event() { if(buffer_.empty()) { const bool read_success = reader_->read_event(); if(read_success) cur_event_ = reader_->hepeup(); return read_success; } cur_event_ = buffer_.top(); buffer_.pop(); return true; } -} +} // namespace HEJ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6e760b0..3577801 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,56 +1,57 @@ file(GLOB src_files *.cc) ## hej source add_library(HEJ SHARED ${src_files}) add_dependencies(HEJ currents) set_target_properties(HEJ PROPERTIES OUTPUT_NAME "HEJ" VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} ) target_include_directories(HEJ PUBLIC ${HEJ_INCLUDE_DIR}) set(libraries ${CMAKE_DL_LIBS}) target_link_libraries(HEJ PRIVATE ${libraries}) ## Dependencies target_link_libraries(HEJ PUBLIC - fastjet::fastjet - LHAPDF::LHAPDF - CLHEP::CLHEP - yaml-cpp Boost::boost Boost::iostreams + CLHEP::CLHEP + fastjet::fastjet + yaml-cpp + PRIVATE + LHAPDF::LHAPDF ) if(QCDloop_FOUND) target_link_libraries(HEJ PRIVATE QCDloop::qcdloop quadmath) endif() if(HepMC_FOUND) target_link_libraries(HEJ PRIVATE HepMC::HepMC) endif() if(HepMC3_FOUND) # HepMC 3 doesn't export a target target_link_libraries(HEJ PRIVATE ${HEPMC3_LIBRARIES}) target_include_directories(HEJ PRIVATE ${HEPMC3_INCLUDE_DIR}) endif() if(rivet_FOUND) target_link_libraries(HEJ PRIVATE rivet::rivet) endif() if(HighFive_FOUND) target_link_libraries(HEJ PRIVATE HighFive) endif() ## install & export target install(TARGETS HEJ EXPORT HEJ-export DESTINATION ${INSTALL_LIB_DIR} ) install(EXPORT HEJ-export FILE hejTargets.cmake NAMESPACE HEJ:: DESTINATION ${INSTALL_CONFIG_DIR} ) diff --git a/src/CombinedEventWriter.cc b/src/CombinedEventWriter.cc index 77c5432..3139d8b 100644 --- a/src/CombinedEventWriter.cc +++ b/src/CombinedEventWriter.cc @@ -1,29 +1,41 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/CombinedEventWriter.hh" +#include + +#include "HEJ/exceptions.hh" #include "HEJ/make_writer.hh" #include "HEJ/output_formats.hh" -namespace HEJ{ +namespace HEJ { CombinedEventWriter::CombinedEventWriter( std::vector const & outfiles, LHEF::HEPRUP const & heprup ){ writers_.reserve(outfiles.size()); for(OutputFile const & outfile: outfiles){ writers_.emplace_back( make_format_writer(outfile.format, outfile.name, heprup) ); } } void CombinedEventWriter::write(Event const & ev){ for(auto & writer: writers_) writer->write(ev); } -} + void CombinedEventWriter::finish(){ + EventWriter::finish(); + for(auto & writer: writers_) writer->finish(); + } + + CombinedEventWriter::~CombinedEventWriter(){ + finish_or_abort(this, "CombinedEventWriter"); + } + +} // namespace HEJ diff --git a/src/CrossSectionAccumulator.cc b/src/CrossSectionAccumulator.cc index cb3811d..af70c4b 100644 --- a/src/CrossSectionAccumulator.cc +++ b/src/CrossSectionAccumulator.cc @@ -1,67 +1,69 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/CrossSectionAccumulator.hh" -#include #include +#include #include #include #include "HEJ/Event.hh" #include "HEJ/Parameters.hh" namespace HEJ { void CrossSectionAccumulator::fill( double const wt, double const err, event_type::EventType const type ){ total_.value += wt; total_.error += err; auto & entry = xs_[type]; entry.value += wt; entry.error += err; } void CrossSectionAccumulator::fill( double const wt, event_type::EventType const type ){ fill(wt, wt*wt, type); } void CrossSectionAccumulator::fill(Event const & ev){ fill(ev.central().weight, ev.type()); } void CrossSectionAccumulator::fill_correlated( double const sum, double const sum2, event_type::EventType const type ){ fill(sum, sum*sum+sum2, type); } void CrossSectionAccumulator::fill_correlated(std::vector const & evts){ if(evts.empty()) return; fill_correlated(evts.cbegin(), evts.cend()); } std::ostream& operator<<(std::ostream& os, const CrossSectionAccumulator& xs){ + constexpr size_t name_width = 25; + constexpr size_t precision = 3; const std::streamsize orig_prec = os.precision(); - os << std::scientific << std::setprecision(3) - << " " << std::left << std::setw(25) + os << std::scientific << std::setprecision(precision) + << " " << std::left << std::setw(name_width) << "Cross section: " << xs.total().value << " +- " << std::sqrt(xs.total().error) << " (pb)\n"; for(auto const & xs_type: xs) { - os << " " << std::left << std::scientific < #include #include "yaml-cpp/yaml.h" #include "HEJ/exceptions.hh" -namespace HEJ{ - namespace{ +namespace HEJ { + namespace { std::vector param_as_strings(YAML::Node const & parameters){ using YAML::NodeType; switch(parameters.Type()){ case NodeType::Null: case NodeType::Undefined: return {}; case NodeType::Scalar: return {parameters.as()}; case NodeType::Sequence: { std::vector param_strings; for(auto && param: parameters){ param_strings.emplace_back(param.as()); } return param_strings; } case NodeType::Map: { std::vector param_strings; for(auto && param: parameters){ param_strings.emplace_back(param.first.as()); } return param_strings; } default:; } throw std::logic_error{"unreachable"}; } - } + } // namespace std::unique_ptr EmptyAnalysis::create( - YAML::Node const & parameters, LHEF::HEPRUP const & + YAML::Node const & parameters, LHEF::HEPRUP const & /*unused*/ ){ const auto param_strings = param_as_strings(parameters); if(! param_strings.empty()){ std::string error{"Unknown analysis parameter(s):"}; for(auto && p: param_strings) error += " " + p; throw unknown_option{error}; } return std::make_unique(); } - void EmptyAnalysis::fill(Event const &, Event const &){ + void EmptyAnalysis::fill( + Event const & /*res_event*/, Event const & /*FO_event*/ + ){ // do nothing } - bool EmptyAnalysis::pass_cuts(Event const &, Event const &){ + bool EmptyAnalysis::pass_cuts( + Event const & /*res_event*/, Event const & /*FO_event*/ + ){ return true; } void EmptyAnalysis::finalise(){ // do nothing } -} +} // namespace HEJ diff --git a/src/Event.cc b/src/Event.cc index 45e17e0..1c8999c 100644 --- a/src/Event.cc +++ b/src/Event.cc @@ -1,1294 +1,1303 @@ /** * \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/exceptions.hh" -#include "HEJ/optional.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/RNG.hh" +#include "HEJ/exceptions.hh" +#include "HEJ/optional.hh" -namespace HEJ{ +namespace HEJ { namespace { using std::size_t; - constexpr int status_in = -1; - constexpr int status_decayed = 2; - constexpr int status_out = 1; + + //! 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]); } //! true if supported decay bool valid_decay(std::vector const & decays){ return valid_W_decay(+1, decays) || // Wp valid_W_decay(-1, decays) || // Wm valid_Z_decay( decays) // Z/gamma ; } /// @name helper functions to determine event type //@{ /** * \brief check if final state valid for HEJ * * check final state has the expected number of valid decays for bosons * and all the rest are quarks or gluons */ bool final_state_ok(Event const & ev){ size_t invalid_decays = ev.decays().size(); std::vector const & outgoing = ev.outgoing(); for( size_t i=0; i0?+1:-1, decay->second) ){ --invalid_decays; } else return false; } // Higgs decays (optional) else if(out.type == ParticleID::h){ if(decay != ev.decays().cend()) --invalid_decays; } // Z decays (required) else if(out.type == ParticleID::Z_photon_mix){ if( decay != ev.decays().cend() && valid_Z_decay(decay->second) ){ --invalid_decays; } else return false; } } else if(! is_parton(out.type)) return false; } // any invalid decays? if(invalid_decays != 0) 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; else if(bosons.size() == 1) { switch (bosons[0].type) { case ParticleID::Wp: case ParticleID::Wm: return FKL | unob | unof | qqxexb | qqxexf | qqxmid; case ParticleID::Z_photon_mix: return FKL | unob | unof; case ParticleID::h: return FKL | unob | unof; default: return non_resummable; } } else if(bosons.size() == 2) { if(bosons[0].type == ParticleID::Wp && bosons[1].type == ParticleID::Wp) { return FKL; } return non_resummable; } else { 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? * * \see is_Wm_Change */ bool is_Wp_Change(ParticleID in, ParticleID out, bool qqx){ if(!qqx && (in==-1 || in== 2 || in==-3 || in== 4)) return out== (in-1); if( qqx && (in== 1 || in==-2 || in== 3 || in==-4)) 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? * * Ensures that change type of quark line is possible by a flavour changing * Wm emission. Allows checking of qqx currents also. */ bool is_Wm_Change(ParticleID in, ParticleID out, bool qqx){ if(!qqx && (in== 1 || in==-2 || in== 3 || in==-4)) return out== (in+1); if( qqx && (in==-1 || in== 2 || in==-3 || in== 4)) 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? */ bool no_flavour_change(ParticleID in, ParticleID out, bool qqx){ const int qqxCurrent = qqx?-1:1; - if(std::abs(in)<=6 || in==pid::gluon) return (in==out*qqxCurrent); - else return false; + if(std::abs(in)<=pid::top || in==pid::gluon) + return (in==out*qqxCurrent); + 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 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 ){ if( no_flavour_change(in, out, qqx) ){ return true; } if( is_Wp_Change(in, out, qqx) ) { W_change+=1; return true; } if( is_Wm_Change(in, out, qqx) ) { 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; // keep track of all states that we don't test size_t not_tested = qqxmid; if(backward) not_tested |= unof | qqxexf; else not_tested |= unob | qqxexb; // 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 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 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; // 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; // 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 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 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 bosons{ filter_AWZH_bosons(ev.outgoing()) }; // keep track of potential W couplings, at the end the sum should be 0 int remaining_Wp = 0; int remaining_Wm = 0; for(auto const & boson : bosons){ if(boson.type == ParticleID::Wp) ++remaining_Wp; else if(boson.type == ParticleID::Wm) ++remaining_Wm; } size_t final_type = ~(no_2_jets | bad_final_state); // check forward impact factor int W_change = 0; final_type &= possible_impact_factors( in.front().type, begin_out, end_out.base(), W_change, bosons, 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; // check backward impact factor W_change = 0; final_type &= possible_impact_factors( in.back().type, end_out, std::make_reverse_iterator(begin_out), W_change, bosons, 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; // check central emissions W_change = 0; final_type &= possible_central( begin_out, end_out.base(), W_change, bosons ); 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(bosons)) != 0 ) return non_resummable; return static_cast(final_type); } //@} Particle extract_particle(LHEF::HEPEUP const & hepeup, size_t i){ - const ParticleID id = static_cast(hepeup.IDUP[i]); - const fastjet::PseudoJet momentum{ - hepeup.PUP[i][0], hepeup.PUP[i][1], - hepeup.PUP[i][2], hepeup.PUP[i][3] - }; - if(is_parton(id)) - return Particle{ id, std::move(momentum), hepeup.ICOLUP[i] }; - return Particle{ id, std::move(momentum), {} }; + 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 anonymous + } // 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] == status_in){ + 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 o1, Particle 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 { // use valid_X_decay to determine boson type ParticleID reconstruct_type(std::vector const & progeny) { if(valid_W_decay(+1, progeny)) { return ParticleID::Wp; } if(valid_W_decay(-1, progeny)) { return ParticleID::Wm; } if(valid_Z_decay(progeny)) { return ParticleID::Z_photon_mix; } throw not_implemented{ "final state with decay X -> " + name(progeny[0].type) + " + " + name(progeny[1].type) }; } // reconstruct particle with explicit ParticleID Particle reconstruct_boson( std::vector const & progeny, ParticleID const & type ) { Particle progenitor; progenitor.p = progeny[0].p + progeny[1].p; progenitor.type = type; return progenitor; } // reconstruct via call to reconstruct_type Particle reconstruct_boson(std::vector const & progeny) { Particle progenitor {reconstruct_boson(progeny, reconstruct_type(progeny))}; assert(is_AWZH_boson(progenitor)); return progenitor; } typedef std::vector< std::vector > GroupedParticles; typedef std::pair > Decay; typedef std::vector< Decay > Decays; // return groups of reconstructable progeny std::vector group_progeny ( std::vector & leptons ) { /** Warning: The partition in to charged/neutral leptons is valid ONLY for WW. **/ assert(leptons.size() == 4); const auto begin_neutrino = std::partition( begin(leptons), end(leptons), [](Particle const & p) {return !is_anyneutrino(p);} ); std::vector neutrinos (begin_neutrino, end(leptons)); leptons.erase(begin_neutrino, end(leptons)); std::sort(begin(leptons), end(leptons), type_less{}); std::sort(begin(neutrinos), end(neutrinos), type_less{}); if(leptons.size() != 2 && neutrinos.size() != 2) { return {}; } assert(leptons.size() == 2 && neutrinos.size() == 2); std::vector< GroupedParticles > candidate_grouping { {{leptons[0], neutrinos[0]}, {leptons[1], neutrinos[1]}}, {{leptons[1], neutrinos[0]}, {leptons[0], neutrinos[1]}} }; // erase groupings containing invalid decays candidate_grouping.erase( std::remove_if(begin(candidate_grouping), end(candidate_grouping), [] (GroupedParticles & candidate) -> bool { return ! std::accumulate( cbegin(candidate), cend(candidate), true, [] (bool acc_valid, std::vector const & decay) -> bool { return acc_valid && valid_decay(decay); } ); } ), candidate_grouping.end() ); return candidate_grouping; } // 'best' decay ordering measure double decay_measure(Particle reconstructed, EWConstants const & params) { ParticleProperties ref = params.prop(reconstructed.type); return std::abs(reconstructed.p.m() - ref.mass); } // decay_measure accumulated over decays double decay_measure(Decays const & decays, EWConstants const & params) { return std::accumulate( cbegin(decays), cend(decays), 0., [¶ms] (double dm, Decay const & decay) -> double { return dm + decay_measure(decay.first, params); } ); } // select best combination of decays for the event Decays select_decays ( std::vector & leptons, EWConstants const & ew_parameters ) { std::vector groupings = group_progeny(leptons); std::vector valid_decays; valid_decays.reserve(groupings.size()); // Reconstruct all groupings for(GroupedParticles const & group : groupings) { Decays decays; for(auto const & progeny : group) { decays.emplace_back(make_pair(reconstruct_boson(progeny), progeny)); } valid_decays.emplace_back(decays); } if (valid_decays.empty()) { throw not_implemented{"No supported intermediate reconstruction available"}; } else if (valid_decays.size() == 1) { return valid_decays[0]; } else { // select decay with smallest decay_measure auto selected = std::min_element(cbegin(valid_decays), cend(valid_decays), [&ew_parameters] (auto const & d1, auto const & d2) -> bool { return decay_measure(d1, ew_parameters) < decay_measure(d2, ew_parameters); } ); return *selected; } } - - } + } // namespace void Event::EventData::reconstruct_intermediate(EWConstants const & ew_parameters) { const auto begin_leptons = std::partition( begin(outgoing), end(outgoing), [](Particle const & p) {return !is_anylepton(p);} ); if(begin_leptons == end(outgoing)) return; assert(is_anylepton(*begin_leptons)); std::vector leptons(begin_leptons, end(outgoing)); outgoing.erase(begin_leptons, end(outgoing)); if(leptons.empty()) { return; } // nothing to do else if(leptons.size() == 2) { outgoing.emplace_back(reconstruct_boson(leptons)); std::sort(begin(leptons), end(leptons), type_less{}); decays.emplace(outgoing.size()-1, std::move(leptons)); } else if(leptons.size() == 4) { // select_decays only supports WpWp Decays valid_decays = select_decays(leptons, ew_parameters); for(auto &decay : valid_decays) { outgoing.emplace_back(decay.first); std::sort(begin(decay.second), end(decay.second), type_less{}); decays.emplace(outgoing.size()-1, std::move(decay.second)); } } else { throw not_implemented { std::to_string(leptons.size()) + " leptons in the final state" }; } } 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 anonymous + } // 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: { 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: { 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:{ auto it_next = std::next(it_part); if( !try_connect_t(it_part, line_colour) // u-channel only allowed at qqx/qxq 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; - return; - } + } //! 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 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 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 anonymous + } // 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: { 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: { 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:{ 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 max_ext_soft_pt_fraction, 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] }; - if( (parton.p - jet).pt()/jet.pt() > max_ext_soft_pt_fraction) - return false; - return true; + return (parton.p - jet).pt()/jet.pt() <= max_ext_soft_pt_fraction; } - } + } // namespace // this should work with multiple types bool Event::valid_hej_state(double const max_frac, 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, max_frac, min_pt) ) return false; ++part_begin; ++idx_begin; if( !valid_parton(jets(), *part_end, *idx_end, max_frac, min_pt) ) return false; ++part_end; ++idx_end; // unob -> second parton in own jet if( type() & (unob | qqxexb) ){ if( !valid_parton(jets(), *part_begin, *idx_begin, max_frac, min_pt) ) return false; ++part_begin; ++idx_begin; } if( type() & (unof | qqxexf) ){ if( !valid_parton(jets(), *part_end, *idx_end, max_frac, 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(), [](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, max_frac,min_pt) && valid_parton(jets(),*(++begin_qqx),*(++idx_begin),max_frac,min_pt) )) return false; } return true; } Event::ConstPartonIterator Event::begin_partons() const { return cbegin_partons(); } Event::ConstPartonIterator Event::cbegin_partons() const { return boost::make_filter_iterator( static_cast(is_parton), cbegin(outgoing()), cend(outgoing()) ); } Event::ConstPartonIterator Event::end_partons() const { return cend_partons(); } Event::ConstPartonIterator Event::cend_partons() const { return boost::make_filter_iterator( static_cast(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 boost::make_filter_iterator( static_cast(is_parton), begin(outgoing_), end(outgoing_) ); } Event::PartonIterator Event::end_partons() { return boost::make_filter_iterator( static_cast(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){ - 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 << ")"; + 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=" - < 0 ){ + if(!ev.decays().empty() ){ os << "\nDecays: " << ev.decays().size() << "\n"; for(auto const & decay: ev.decays()){ - os < 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(status_in); + 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)?status_decayed:status_out; + 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(status_out); + 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/EventReader.cc b/src/EventReader.cc index d360ab4..18080dc 100644 --- a/src/EventReader.cc +++ b/src/EventReader.cc @@ -1,77 +1,77 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/EventReader.hh" #include #include #include "LHEF/LHEF.h" #include "HEJ/ConfigFlags.hh" #include "HEJ/HDF5Reader.hh" #include "HEJ/LesHouchesReader.hh" #include "HEJ/Version.hh" namespace { enum class generator{ HEJ, HEJFOG, Sherpa, MG, unknown }; generator get_generator( LHEF::HEPRUP const & heprup, std::string const & header ){ // try getting generator name from specific tag - if(heprup.generators.size()>0){ + if(!heprup.generators.empty()){ std::string const & gen_name = heprup.generators.back().name; if(gen_name == "HEJ" || gen_name == HEJ::Version::String()) return generator::HEJ; if(gen_name == "HEJ Fixed Order Generation") return generator::HEJFOG; if(gen_name == "MadGraph5_aMC@NLO") return generator::MG; std::cerr << "Unknown LHE Generator " << gen_name << " using default LHE interface.\n"; return generator::unknown; } // The generator tag is not always used -> check by hand if(header.find("generated with HEJ")!=std::string::npos) return generator::HEJ; if(header.find("# created by SHERPA")!=std::string::npos) return generator::Sherpa; if(header.find("")!=std::string::npos) return generator::MG; std::cerr<<"Could not determine LHE Generator using default LHE interface.\n"; return generator::unknown; } -} +} // namespace namespace HEJ { std::unique_ptr make_reader(std::string const & filename) { try { auto reader{ std::make_unique(filename) }; switch( get_generator(reader->heprup(), reader->header()) ){ case generator::Sherpa: return std::make_unique(filename); case generator::HEJ: case generator::HEJFOG: case generator::MG: //! @TODO we could directly fix the MG weights here similar to Sherpa default: return reader; } } catch(std::runtime_error&) { #ifdef HEJ_BUILD_WITH_HDF5 return std::make_unique(filename); #else throw; #endif } } -} +} // namespace HEJ diff --git a/src/EventReweighter.cc b/src/EventReweighter.cc index aa4c057..b09dabf 100644 --- a/src/EventReweighter.cc +++ b/src/EventReweighter.cc @@ -1,257 +1,257 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/EventReweighter.hh" #include #include #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "fastjet/PseudoJet.hh" #include "LHEF/LHEF.h" -#include "HEJ/Fraction.hh" #include "HEJ/Event.hh" -#include "HEJ/exceptions.hh" -#include "HEJ/Particle.hh" +#include "HEJ/Fraction.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/Particle.hh" #include "HEJ/PhaseSpacePoint.hh" +#include "HEJ/exceptions.hh" -namespace HEJ{ +namespace HEJ { EventReweighter::EventReweighter( LHEF::HEPRUP const & heprup, ScaleGenerator scale_gen, EventReweighterConfig conf, std::shared_ptr ran ): EventReweighter{ - HEJ::Beam{ + Beam{ heprup.EBMUP.first, {{ - static_cast(heprup.IDBMUP.first), - static_cast(heprup.IDBMUP.second) + static_cast(heprup.IDBMUP.first), + static_cast(heprup.IDBMUP.second) }} }, heprup.PDFSUP.first, std::move(scale_gen), std::move(conf), std::move(ran) } { if(heprup.EBMUP.second != E_beam_){ throw std::invalid_argument( "asymmetric beam: " + std::to_string(E_beam_) + " ---> <--- " + std::to_string(heprup.EBMUP.second) ); } if(heprup.PDFSUP.second != pdf_.id()){ throw std::invalid_argument( "conflicting PDF ids: " + std::to_string(pdf_.id()) + " vs. " + std::to_string(heprup.PDFSUP.second) ); } } EventReweighter::EventReweighter( Beam const & beam, int pdf_id, ScaleGenerator scale_gen, EventReweighterConfig conf, std::shared_ptr ran ): param_{std::move(conf)}, E_beam_{beam.E}, pdf_{pdf_id, beam.type.front(), beam.type.back()}, MEt2_{ [this](double mu){ return pdf_.Halphas(mu); }, param_.ME_config }, scale_gen_{std::move(scale_gen)}, ran_{std::move(ran)} { assert(ran_); } PDF const & EventReweighter::pdf() const{ return pdf_; } std::vector EventReweighter::reweight( Event const & input_ev, std::size_t num_events ){ auto res_events{ gen_res_events(input_ev, num_events) }; if(res_events.empty()) return {}; for(auto & event: res_events) event = scale_gen_(std::move(event)); return rescale(input_ev, std::move(res_events)); } EventTreatment EventReweighter::treatment(EventType type) const { return param_.treat.at(type); } std::vector EventReweighter::gen_res_events( Event const & ev, std::size_t phase_space_points ){ assert(ev.variations().empty()); status_.clear(); switch(treatment(ev.type())){ case EventTreatment::discard: { status_.emplace_back(StatusCode::discard); return {}; } case EventTreatment::keep: if(! jets_pass_resummation_cuts(ev)) { status_.emplace_back(StatusCode::failed_resummation_cuts); return {}; } else { status_.emplace_back(StatusCode::good); return {ev}; } default:; } const double Born_shat = shat(ev); std::vector resummation_events; status_.reserve(phase_space_points); for(std::size_t psp_number = 0; psp_number < phase_space_points; ++psp_number){ PhaseSpacePoint psp{ev, param_.psp_config, *ran_}; status_.emplace_back(psp.status()); assert(psp.status() != StatusCode::unspecified); if(psp.status() != StatusCode::good) continue; assert(psp.weight() != 0.); if(psp.incoming()[0].E() > E_beam_ || psp.incoming()[1].E() > E_beam_) { status_.back() = StatusCode::too_much_energy; continue; } resummation_events.emplace_back( to_EventData( std::move(psp) ).cluster( param_.jet_param().def, param_.jet_param().min_pt ) ); auto & new_event = resummation_events.back(); assert( new_event.valid_hej_state( param_.psp_config.max_ext_soft_pt_fraction, param_.psp_config.min_extparton_pt ) ); if( new_event.type() != ev.type() ) throw std::logic_error{"Resummation Event does not match Born event"}; new_event.generate_colours(*ran_); assert(new_event.variations().empty()); new_event.central().mur = ev.central().mur; new_event.central().muf = ev.central().muf; const double resum_shat = shat(new_event); new_event.central().weight *= ev.central().weight*Born_shat*Born_shat/ (phase_space_points*resum_shat*resum_shat); } return resummation_events; } std::vector EventReweighter::rescale( Event const & Born_ev, std::vector events ) const{ const double Born_pdf = pdf_factors(Born_ev).central; const double Born_ME = tree_matrix_element(Born_ev); for(auto & cur_event: events){ const auto pdf = pdf_factors(cur_event); assert(pdf.variations.size() == cur_event.variations().size()); const auto ME = matrix_elements(cur_event); assert(ME.variations.size() == cur_event.variations().size()); cur_event.parameters() *= pdf*ME/(Born_pdf*Born_ME); } return events; } bool EventReweighter::jets_pass_resummation_cuts( Event const & ev ) const{ const auto out_as_PseudoJet = to_PseudoJet(filter_partons(ev.outgoing())); fastjet::ClusterSequence cs{out_as_PseudoJet, param_.jet_param().def}; return cs.inclusive_jets(param_.jet_param().min_pt).size() == ev.jets().size(); } Weights EventReweighter::pdf_factors(Event const & ev) const{ auto const & a = ev.incoming().front(); auto const & b = ev.incoming().back(); const double xa = a.p.e()/E_beam_; const double xb = b.p.e()/E_beam_; Weights result; std::unordered_map known_pdf; result.central = pdf_.pdfpt(0,xa,ev.central().muf,a.type)* pdf_.pdfpt(1,xb,ev.central().muf,b.type); known_pdf.emplace(ev.central().muf, result.central); result.variations.reserve(ev.variations().size()); for(auto const & ev_param: ev.variations()){ const double muf = ev_param.muf; auto cur_pdf = known_pdf.find(muf); if(cur_pdf == known_pdf.end()){ cur_pdf = known_pdf.emplace( muf, pdf_.pdfpt(0,xa,muf,a.type)*pdf_.pdfpt(1,xb,muf,b.type) ).first; } result.variations.emplace_back(cur_pdf->second); } assert(result.variations.size() == ev.variations().size()); return result; } Weights EventReweighter::matrix_elements(Event const & ev) const{ assert(param_.treat.count(ev.type()) > 0); if(param_.treat.find(ev.type())->second == EventTreatment::keep){ return fixed_order_scale_ME(ev); } return MEt2_(ev); } double EventReweighter::tree_matrix_element(Event const & ev) const{ assert(ev.variations().empty()); assert(param_.treat.count(ev.type()) > 0); if(param_.treat.find(ev.type())->second == EventTreatment::keep){ return fixed_order_scale_ME(ev).central; } return MEt2_.tree(ev).central; } Weights EventReweighter::fixed_order_scale_ME(Event const & ev) const{ int alpha_s_power = 0; for(auto const & part: ev.outgoing()){ if(is_parton(part)) ++alpha_s_power; else if(part.type == pid::Higgs) { alpha_s_power += 2; } // nothing to do for other uncoloured particles } Weights result; result.central = std::pow(pdf_.Halphas(ev.central().mur), alpha_s_power); for(auto const & var: ev.variations()){ result.variations.emplace_back( std::pow(pdf_.Halphas(var.mur), alpha_s_power) ); } return result; } } // namespace HEJ diff --git a/src/EventWriter.cc b/src/EventWriter.cc new file mode 100644 index 0000000..45282ce --- /dev/null +++ b/src/EventWriter.cc @@ -0,0 +1,27 @@ +/** + * \authors The HEJ collaboration (see AUTHORS for details) + * \date 2020 + * \copyright GPLv2 or later + */ + +#include "HEJ/EventWriter.hh" + +#include + +#include "HEJ/exceptions.hh" + +namespace HEJ { + void EventWriter::finish_or_abort( + EventWriter* writer, std::string const & name + ) const noexcept { + if(!finished_){ + try { + writer->finish(); + } catch (std::exception const & e){ + std::cerr << "Failed to finish "<< name <<":\n" + << e.what() << std::endl; + std::terminate(); + } + } + } +} // namespace HEJ diff --git a/src/HDF5Reader.cc b/src/HDF5Reader.cc index 0c3459f..79c9060 100644 --- a/src/HDF5Reader.cc +++ b/src/HDF5Reader.cc @@ -1,322 +1,317 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HDF5Reader.hh" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HDF5 #include #include #include #include #include #include #include #include #include "highfive/H5File.hpp" #include "LHEF/LHEF.h" #else #include "HEJ/exceptions.hh" #endif #ifdef HEJ_BUILD_WITH_HDF5 namespace HEJ { namespace { // buffer size for reader // each "reading from disk" reads "chunk_size" many event at once - constexpr std::size_t chunk_size = 10000; + constexpr std::size_t CHUNK_SIZE = 10000; struct ParticleData { std::vector id; std::vector status; std::vector mother1; std::vector mother2; std::vector color1; std::vector color2; std::vector px; std::vector py; std::vector pz; std::vector e; std::vector m; std::vector lifetime; std::vector spin; }; struct EventRecords { std::vector particle_start; std::vector nparticles; std::vector pid; std::vector weight; std::vector scale; std::vector fscale; std::vector rscale; std::vector aqed; std::vector aqcd; double trials; ParticleData particles; }; class ConstEventIterator { public: - // iterator traits + // iterator traits for LegacyBidirectionalIterator using iterator_category = std::bidirectional_iterator_tag; using value_type = LHEF::HEPEUP; using difference_type = std::ptrdiff_t; using pointer = const LHEF::HEPEUP*; using reference = LHEF::HEPEUP const &; using iterator = ConstEventIterator; friend iterator cbegin(EventRecords const & records) noexcept; friend iterator cend(EventRecords const & records) noexcept; iterator& operator++() { particle_offset_ += records_.get().nparticles[idx_]; ++idx_; return *this; } iterator& operator--() { --idx_; particle_offset_ -= records_.get().nparticles[idx_]; return *this; } iterator operator--(int) { auto res = *this; --(*this); return res; } bool operator==(iterator const & other) const { return idx_ == other.idx_; } bool operator!=(iterator other) const { return !(*this == other); } value_type operator*() const { value_type hepeup{}; auto const & r = records_.get(); hepeup.NUP = r.nparticles[idx_]; hepeup.IDPRUP = r.pid[idx_]; hepeup.XWGTUP = r.weight[idx_]/r.trials; hepeup.weights.emplace_back(hepeup.XWGTUP, nullptr); hepeup.SCALUP = r.scale[idx_]; hepeup.scales.muf = r.fscale[idx_]; hepeup.scales.mur = r.rscale[idx_]; hepeup.AQEDUP = r.aqed[idx_]; hepeup.AQCDUP = r.aqcd[idx_]; const size_t start = particle_offset_; const size_t end = start + hepeup.NUP; auto const & p = r.particles; hepeup.IDUP = std::vector( begin(p.id)+start, begin(p.id)+end ); hepeup.ISTUP = std::vector( begin(p.status)+start, begin(p.status)+end ); hepeup.VTIMUP = std::vector( begin(p.lifetime)+start, begin(p.lifetime)+end ); hepeup.SPINUP = std::vector( begin(p.spin)+start, begin(p.spin)+end ); hepeup.MOTHUP.resize(hepeup.NUP); hepeup.ICOLUP.resize(hepeup.NUP); hepeup.PUP.resize(hepeup.NUP); for(size_t i = 0; i < hepeup.MOTHUP.size(); ++i) { const size_t idx = start + i; assert(idx < end); hepeup.MOTHUP[i] = std::make_pair(p.mother1[idx], p.mother2[idx]); hepeup.ICOLUP[i] = std::make_pair(p.color1[idx], p.color2[idx]); hepeup.PUP[i] = std::vector{ p.px[idx], p.py[idx], p.pz[idx], p.e[idx], p.m[idx] }; } return hepeup; } private: explicit ConstEventIterator(EventRecords const & records): records_{records} {} std::reference_wrapper records_; size_t idx_ = 0; size_t particle_offset_ = 0; }; // end ConstEventIterator ConstEventIterator cbegin(EventRecords const & records) noexcept { return ConstEventIterator{records}; } ConstEventIterator cend(EventRecords const & records) noexcept { auto it =ConstEventIterator{records}; it.idx_ = records.aqcd.size(); // or size of any other records member return it; } } // end anonymous namespace struct HDF5Reader::HDF5ReaderImpl{ HighFive::File file; std::size_t event_idx; std::size_t particle_idx; std::size_t nevents; EventRecords records; ConstEventIterator cur_event; LHEF::HEPRUP heprup; LHEF::HEPEUP hepeup; explicit HDF5ReaderImpl(std::string const & filename): file{filename}, event_idx{0}, particle_idx{0}, nevents{ file.getGroup("event") .getDataSet("nparticles") // or any other dataset .getSpace().getDimensions().front() }, records{}, - cur_event{cbegin(records)}, - heprup{}, - hepeup{} + cur_event{cbegin(records)} { read_heprup(); - read_event_records(chunk_size); + read_event_records(CHUNK_SIZE); } void read_heprup() { const auto init = file.getGroup("init"); init.getDataSet( "PDFgroupA" ).read(heprup.PDFGUP.first); init.getDataSet( "PDFgroupB" ).read(heprup.PDFGUP.second); init.getDataSet( "PDFsetA" ).read(heprup.PDFSUP.first); init.getDataSet( "PDFsetB" ).read(heprup.PDFSUP.second); init.getDataSet( "beamA" ).read(heprup.IDBMUP.first); init.getDataSet( "beamB" ).read(heprup.IDBMUP.second); init.getDataSet( "energyA" ).read(heprup.EBMUP.first); init.getDataSet( "energyB" ).read(heprup.EBMUP.second); init.getDataSet( "numProcesses" ).read(heprup.NPRUP); init.getDataSet( "weightingStrategy" ).read(heprup.IDWTUP); const auto proc_info = file.getGroup("procInfo"); proc_info.getDataSet( "procId" ).read(heprup.LPRUP); proc_info.getDataSet( "xSection" ).read(heprup.XSECUP); proc_info.getDataSet( "error" ).read(heprup.XERRUP); // TODO: is this identification correct? proc_info.getDataSet( "unitWeight" ).read(heprup.XMAXUP); std::vector trials; file.getGroup("event").getDataSet("trials").read(trials); records.trials = std::accumulate(begin(trials), end(trials), 0.); } std::size_t read_event_records(std::size_t count) { count = std::min(count, nevents-event_idx); auto events = file.getGroup("event"); events.getDataSet("nparticles").select({event_idx}, {count}).read(records.nparticles); assert(records.nparticles.size() == count); events.getDataSet("pid").select( {event_idx}, {count} ).read( records.pid ); events.getDataSet("weight").select( {event_idx}, {count} ).read( records.weight ); events.getDataSet("scale").select( {event_idx}, {count} ).read( records.scale ); events.getDataSet("fscale").select( {event_idx}, {count} ).read( records.fscale ); events.getDataSet("rscale").select( {event_idx}, {count} ).read( records.rscale ); events.getDataSet("aqed").select( {event_idx}, {count} ).read( records.aqed ); events.getDataSet("aqcd").select( {event_idx}, {count} ).read( records.aqcd ); const std::size_t particle_count = std::accumulate( begin(records.nparticles), end(records.nparticles), 0 ); auto pdata = file.getGroup("particle"); auto & particles = records.particles; pdata.getDataSet("id").select( {particle_idx}, {particle_count} ).read( particles.id ); pdata.getDataSet("status").select( {particle_idx}, {particle_count} ).read( particles.status ); pdata.getDataSet("mother1").select( {particle_idx}, {particle_count} ).read( particles.mother1 ); pdata.getDataSet("mother2").select( {particle_idx}, {particle_count} ).read( particles.mother2 ); pdata.getDataSet("color1").select( {particle_idx}, {particle_count} ).read( particles.color1 ); pdata.getDataSet("color2").select( {particle_idx}, {particle_count} ).read( particles.color2 ); pdata.getDataSet("px").select( {particle_idx}, {particle_count} ).read( particles.px ); pdata.getDataSet("py").select( {particle_idx}, {particle_count} ).read( particles.py ); pdata.getDataSet("pz").select( {particle_idx}, {particle_count} ).read( particles.pz ); pdata.getDataSet("e").select( {particle_idx}, {particle_count} ).read( particles.e ); pdata.getDataSet("m").select( {particle_idx}, {particle_count} ).read( particles.m ); pdata.getDataSet("lifetime").select( {particle_idx}, {particle_count} ).read( particles.lifetime ); pdata.getDataSet("spin").select( {particle_idx}, {particle_count} ).read( particles.spin ); event_idx += count; particle_idx += particle_count; return count; } }; HDF5Reader::HDF5Reader(std::string const & filename): impl_{std::make_unique(filename)} {} bool HDF5Reader::read_event() { if(impl_->cur_event == cend(impl_->records)) { // end of active chunk, read new events from file - const auto events_read = impl_->read_event_records(chunk_size); + const auto events_read = impl_->read_event_records(CHUNK_SIZE); impl_->cur_event = cbegin(impl_->records); if(events_read == 0) return false; } impl_->hepeup = *impl_->cur_event; ++impl_->cur_event; return true; } - namespace { - static const std::string nothing = ""; - } - std::string const & HDF5Reader::header() const { + static const std::string nothing{}; return nothing; } LHEF::HEPRUP const & HDF5Reader::heprup() const { return impl_->heprup; } LHEF::HEPEUP const & HDF5Reader::hepeup() const { return impl_->hepeup; } - HEJ::optional HDF5Reader::number_events() const { + optional HDF5Reader::number_events() const { return impl_->nevents; } -} +} // namespace HEJ #else // no HDF5 support namespace HEJ { - class HDF5Reader::HDF5ReaderImpl{}; + struct HDF5Reader::HDF5ReaderImpl{}; - HDF5Reader::HDF5Reader(std::string const &){ + HDF5Reader::HDF5Reader(std::string const & /*filename*/){ throw std::invalid_argument{ "Failed to create HDF5 reader: " "HEJ 2 was built without HDF5 support" }; } bool HDF5Reader::read_event() { throw std::logic_error{"unreachable"}; } std::string const & HDF5Reader::header() const { throw std::logic_error{"unreachable"}; } LHEF::HEPRUP const & HDF5Reader::heprup() const { throw std::logic_error{"unreachable"}; } LHEF::HEPEUP const & HDF5Reader::hepeup() const { throw std::logic_error{"unreachable"}; } - HEJ::optional HDF5Reader::number_events() const { + optional HDF5Reader::number_events() const { throw std::logic_error{"unreachable"}; } } #endif namespace HEJ { HDF5Reader::~HDF5Reader() = default; } diff --git a/src/HDF5Writer.cc b/src/HDF5Writer.cc index 5bd43b2..7ebeec4 100644 --- a/src/HDF5Writer.cc +++ b/src/HDF5Writer.cc @@ -1,417 +1,431 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HDF5Writer.hh" #include #include "LHEF/LHEF.h" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HDF5 #include #include #include #include #include #include #include "highfive/H5File.hpp" #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #else #include "HEJ/exceptions.hh" #endif #ifdef HEJ_BUILD_WITH_HDF5 -namespace HEJ{ +namespace HEJ { using HighFive::File; using HighFive::DataSpace; namespace { using std::size_t; - constexpr size_t chunk_size = 1000; - constexpr unsigned compression_level = 3; + constexpr size_t CHUNK_SIZE = 1000; + constexpr unsigned COMPRESSION_LEVEL = 3; size_t to_index(event_type::EventType const type){ return type==0?0:std::floor(std::log2(static_cast(type)))+1; } template void write_dataset(HighFive::Group & group, std::string const & name, T val) { using data_t = std::decay_t; group.createDataSet(name, DataSpace::From(val)).write(val); } template void write_dataset( HighFive::Group & group, std::string const & name, std::vector const & val ) { using data_t = std::decay_t; group.createDataSet(name, DataSpace::From(val)).write(val); } struct Cache { explicit Cache(size_t capacity): capacity{capacity} { nparticles.reserve(capacity); start.reserve(capacity); pid.reserve(capacity); weight.reserve(capacity); scale.reserve(capacity); fscale.reserve(capacity); rscale.reserve(capacity); aqed.reserve(capacity); aqcd.reserve(capacity); trials.reserve(capacity); npLO.reserve(capacity); npNLO.reserve(capacity); } - void fill(HEJ::Event const & ev) { + void fill(Event const & ev) { const auto hepeup = to_HEPEUP(ev, nullptr); // HEJ event to get nice wrapper const auto num_partons = std::distance(ev.cbegin_partons(), ev.cend_partons()); assert(num_partons>0); // Number of partons for LO matching, HEJ requires at least 2 partons npLO.emplace_back(num_partons>1?num_partons-2:num_partons); // Number of real emissions in NLO, HEJ is LO -> -1 npNLO.emplace_back(-1); fill_event_params(hepeup); fill_event_particles(hepeup); } void fill_event_params(LHEF::HEPEUP const & ev) { nparticles.emplace_back(ev.NUP); start.emplace_back(particle_pos); pid.emplace_back(ev.IDPRUP); weight.emplace_back(ev.XWGTUP); scale.emplace_back(ev.SCALUP); fscale.emplace_back(ev.scales.muf); rscale.emplace_back(ev.scales.mur); aqed.emplace_back(ev.AQEDUP); aqcd.emplace_back(ev.AQCDUP); // set first trial=1 for first event // -> sum(trials) = 1 -> xs=sum(weights)/sum(trials) as in Sherpa if(particle_pos == 0){ trials.emplace_back(1.); } else { trials.emplace_back(0.); } particle_pos += ev.NUP; } void fill_event_particles(LHEF::HEPEUP const & ev) { id.insert(end(id), begin(ev.IDUP), end(ev.IDUP)); status.insert(end(status), begin(ev.ISTUP), end(ev.ISTUP)); lifetime.insert(end(lifetime), begin(ev.VTIMUP), end(ev.VTIMUP)); spin.insert(end(spin), begin(ev.SPINUP), end(ev.SPINUP)); for(int i = 0; i < ev.NUP; ++i) { mother1.emplace_back(ev.MOTHUP[i].first); mother2.emplace_back(ev.MOTHUP[i].second); color1.emplace_back(ev.ICOLUP[i].first); color2.emplace_back(ev.ICOLUP[i].second); px.emplace_back(ev.PUP[i][0]); py.emplace_back(ev.PUP[i][1]); pz.emplace_back(ev.PUP[i][2]); e.emplace_back(ev.PUP[i][3]); m.emplace_back(ev.PUP[i][4]); } } bool is_full() const { return nparticles.size() >= capacity; } void clear() { nparticles.clear(); start.clear(); pid.clear(); id.clear(); status.clear(); mother1.clear(); mother2.clear(); color1.clear(); color2.clear(); weight.clear(); scale.clear(); fscale.clear(); rscale.clear(); aqed.clear(); aqcd.clear(); trials.clear(); npLO.clear(); npNLO.clear(); px.clear(); py.clear(); pz.clear(); e.clear(); m.clear(); lifetime.clear(); spin.clear(); } size_t capacity; std::vector nparticles, start, pid, id, status, mother1, mother2, color1, color2, npLO, npNLO; std::vector weight, scale, fscale, rscale, aqed, aqcd, trials, px, py, pz, e, m, lifetime, spin; - - private: size_t particle_pos = 0; }; - } + } // namespace - struct HDF5Writer::HDF5WriterImpl{ + struct HDF5Writer::HDF5WriterImpl: EventWriter{ File file; LHEF::HEPRUP heprup; - Cache cache{chunk_size}; + Cache cache{CHUNK_SIZE}; size_t event_idx = 0; size_t particle_idx = 0; HDF5WriterImpl(std::string const & filename, LHEF::HEPRUP && hepr): file{filename, File::ReadWrite | File::Create | File::Truncate}, - heprup{std::move(hepr)} + heprup{std::forward(hepr)} { // TODO: code duplication with Les Houches Writer - const int max_number_types = to_index(event_type::last_type)+1; + const size_t max_number_types = to_index(event_type::last_type)+1; heprup.NPRUP = max_number_types; // ids of event types heprup.LPRUP.clear(); heprup.LPRUP.reserve(max_number_types); heprup.LPRUP.emplace_back(0); for(size_t i=event_type::first_type+1; i<=event_type::last_type; i*=2) { heprup.LPRUP.emplace_back(i); } heprup.XSECUP = std::vector(max_number_types, 0.); heprup.XERRUP = std::vector(max_number_types, 0.); heprup.XMAXUP = std::vector(max_number_types, 0.); write_init(); create_event_group(); create_particle_group(); } void write_init() { auto init = file.createGroup("init"); write_dataset(init, "PDFgroupA" , heprup.PDFGUP.first); write_dataset(init, "PDFgroupB" , heprup.PDFGUP.second); write_dataset(init, "PDFsetA" , heprup.PDFSUP.first); write_dataset(init, "PDFsetB" , heprup.PDFSUP.second); write_dataset(init, "beamA" , heprup.IDBMUP.first); write_dataset(init, "beamB" , heprup.IDBMUP.second); write_dataset(init, "energyA" , heprup.EBMUP.first); write_dataset(init, "energyB" , heprup.EBMUP.second); write_dataset(init, "numProcesses" , heprup.NPRUP); write_dataset(init, "weightingStrategy", heprup.IDWTUP); auto proc_info = file.createGroup("procInfo"); write_dataset(proc_info, "procId", heprup.LPRUP); } static HighFive::DataSetCreateProps const & hdf5_chunk() { static const auto props = [](){ HighFive::DataSetCreateProps props; - props.add(HighFive::Chunking({chunk_size})); - props.add(HighFive::Deflate(compression_level)); + props.add(HighFive::Chunking({CHUNK_SIZE})); + props.add(HighFive::Deflate(COMPRESSION_LEVEL)); return props; }(); return props; } void create_event_group() { static const auto dim = DataSpace({0}, {DataSpace::UNLIMITED}); auto events = file.createGroup("event"); events.createDataSet("nparticles", dim, hdf5_chunk()); events.createDataSet("start", dim, hdf5_chunk()); events.createDataSet("pid", dim, hdf5_chunk()); events.createDataSet("weight", dim, hdf5_chunk()); events.createDataSet("scale", dim, hdf5_chunk()); events.createDataSet("fscale", dim, hdf5_chunk()); events.createDataSet("rscale", dim, hdf5_chunk()); events.createDataSet("aqed", dim, hdf5_chunk()); events.createDataSet("aqcd", dim, hdf5_chunk()); events.createDataSet("trials", dim, hdf5_chunk()); events.createDataSet("npLO", dim, hdf5_chunk()); events.createDataSet("npNLO", dim, hdf5_chunk()); } void resize_event_group(size_t new_size) { auto events = file.getGroup("event"); events.getDataSet("nparticles").resize({new_size}); events.getDataSet("start").resize({new_size}); events.getDataSet("pid").resize({new_size}); events.getDataSet("weight").resize({new_size}); events.getDataSet("scale").resize({new_size}); events.getDataSet("fscale").resize({new_size}); events.getDataSet("rscale").resize({new_size}); events.getDataSet("aqed").resize({new_size}); events.getDataSet("aqcd").resize({new_size}); events.getDataSet("trials").resize({new_size}); events.getDataSet("npLO").resize({new_size}); events.getDataSet("npNLO").resize({new_size}); } void create_particle_group() { static const auto dim = DataSpace({0}, {DataSpace::UNLIMITED}); auto particles = file.createGroup("particle"); particles.createDataSet("id", dim, hdf5_chunk()); particles.createDataSet("status", dim, hdf5_chunk()); particles.createDataSet("mother1", dim, hdf5_chunk()); particles.createDataSet("mother2", dim, hdf5_chunk()); particles.createDataSet("color1", dim, hdf5_chunk()); particles.createDataSet("color2", dim, hdf5_chunk()); particles.createDataSet("px", dim, hdf5_chunk()); particles.createDataSet("py", dim, hdf5_chunk()); particles.createDataSet("pz", dim, hdf5_chunk()); particles.createDataSet("e", dim, hdf5_chunk()); particles.createDataSet("m", dim, hdf5_chunk()); particles.createDataSet("lifetime", dim, hdf5_chunk()); particles.createDataSet("spin", dim, hdf5_chunk()); } void resize_particle_group(size_t new_size) { auto particles = file.getGroup("particle"); particles.getDataSet("id").resize({new_size}); particles.getDataSet("status").resize({new_size}); particles.getDataSet("mother1").resize({new_size}); particles.getDataSet("mother2").resize({new_size}); particles.getDataSet("color1").resize({new_size}); particles.getDataSet("color2").resize({new_size}); particles.getDataSet("px").resize({new_size}); particles.getDataSet("py").resize({new_size}); particles.getDataSet("pz").resize({new_size}); particles.getDataSet("e").resize({new_size}); particles.getDataSet("m").resize({new_size}); particles.getDataSet("lifetime").resize({new_size}); particles.getDataSet("spin").resize({new_size}); } - void write(Event const & ev){ + void write(Event const & ev) override { cache.fill(ev); if(cache.is_full()) { dump_cache(); } const double wt = ev.central().weight; const size_t idx = to_index(ev.type()); heprup.XSECUP[idx] += wt; heprup.XERRUP[idx] += wt*wt; if(wt > heprup.XMAXUP[idx]){ heprup.XMAXUP[idx] = wt; } } void dump_cache() { write_event_params(); write_event_particles(); cache.clear(); } void write_event_params() { auto events = file.getGroup("event"); // choose arbitrary dataset to find size const auto dataset = events.getDataSet("nparticles"); const size_t size = dataset.getSpace().getDimensions().front(); resize_event_group(size + cache.nparticles.size()); #define WRITE_FROM_CACHE(GROUP, PROPERTY) \ GROUP.getDataSet(#PROPERTY).select({size}, {cache.PROPERTY.size()}).write(cache.PROPERTY) WRITE_FROM_CACHE(events, nparticles); WRITE_FROM_CACHE(events, start); WRITE_FROM_CACHE(events, pid); WRITE_FROM_CACHE(events, weight); WRITE_FROM_CACHE(events, scale); WRITE_FROM_CACHE(events, fscale); WRITE_FROM_CACHE(events, rscale); WRITE_FROM_CACHE(events, aqed); WRITE_FROM_CACHE(events, aqcd); WRITE_FROM_CACHE(events, trials); WRITE_FROM_CACHE(events, npLO); WRITE_FROM_CACHE(events, npNLO); } void write_event_particles() { auto particles = file.getGroup("particle"); // choose arbitrary dataset to find size const auto dataset = particles.getDataSet("id"); const size_t size = dataset.getSpace().getDimensions().front(); resize_particle_group(size + cache.id.size()); WRITE_FROM_CACHE(particles, id); WRITE_FROM_CACHE(particles, status); WRITE_FROM_CACHE(particles, lifetime); WRITE_FROM_CACHE(particles, spin); WRITE_FROM_CACHE(particles, mother1); WRITE_FROM_CACHE(particles, mother2); WRITE_FROM_CACHE(particles, color1); WRITE_FROM_CACHE(particles, color2); WRITE_FROM_CACHE(particles, px); WRITE_FROM_CACHE(particles, py); WRITE_FROM_CACHE(particles, pz); WRITE_FROM_CACHE(particles, e); WRITE_FROM_CACHE(particles, m); } #undef WRITE_FROM_CACHE - ~HDF5WriterImpl(){ + void finish() override { + if(finished()) + throw std::ios_base::failure("HDF5Writer writer already finished."); + EventWriter::finish(); dump_cache(); auto proc_info = file.getGroup("procInfo"); write_dataset(proc_info, "xSection", heprup.XSECUP); write_dataset(proc_info, "error", heprup.XERRUP); write_dataset(proc_info, "unitWeight", heprup.XMAXUP); + file.flush(); + } + + ~HDF5WriterImpl() override { + finish_or_abort(this, "HDF5Writer"); } }; HDF5Writer::HDF5Writer(std::string const & filename, LHEF::HEPRUP heprup): impl_{std::make_unique( filename, std::move(heprup))} {} void HDF5Writer::write(Event const & ev){ impl_->write(ev); } -} + + void HDF5Writer::finish(){ + impl_->finish(); + } +} // namespace HEJ #else // no HDF5 support -namespace HEJ{ +namespace HEJ { - class HDF5Writer::HDF5WriterImpl{}; + struct HDF5Writer::HDF5WriterImpl{}; - HDF5Writer::HDF5Writer(std::string const &, LHEF::HEPRUP){ + HDF5Writer::HDF5Writer(std::string const & /*file*/, LHEF::HEPRUP /*heprup*/){ throw std::invalid_argument{ "Failed to create HDF5 writer: " "HEJ 2 was built without HDF5 support" }; } - void HDF5Writer::write(Event const &){ + void HDF5Writer::write(Event const & /*ev*/){ + assert(false); + } + + void HDF5Writer::finish(){ assert(false); } } #endif namespace HEJ { HDF5Writer::~HDF5Writer() = default; } diff --git a/src/HepMC2Interface.cc b/src/HepMC2Interface.cc index 222b8c7..edba099 100644 --- a/src/HepMC2Interface.cc +++ b/src/HepMC2Interface.cc @@ -1,159 +1,165 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC2Interface.hh" -#include "HEJ/exceptions.hh" #include "HEJ/ConfigFlags.hh" +#include "HEJ/exceptions.hh" #ifdef HEJ_BUILD_WITH_HepMC2 #include #include #include #include "LHEF/LHEF.h" #include "HepMC/GenCrossSection.h" #include "HepMC/GenEvent.h" #include "HepMC/GenParticle.h" #include "HepMC/GenVertex.h" #include "HepMC/SimpleVector.h" #include "HepMC/Units.h" -#include "HEJ/detail/HepMCInterface_common.hh" #include "HEJ/Event.hh" #include "HEJ/Particle.hh" +#include "HEJ/detail/HepMCInterface_common.hh" + +#else // no HepMC2 + +#include "HEJ/utility.hh" + +#endif + +#ifdef HEJ_BUILD_WITH_HepMC2 -namespace HEJ{ +namespace HEJ { namespace detail_HepMC { template<> struct HepMCVersion<2> { using GenEvent = HepMC::GenEvent; using Beam = std::array; }; template<> auto make_particle_ptr<2> ( Particle const & sp, int status ) { return new HepMC::GenParticle( to_FourVector(sp), static_cast (sp.type), status ); } template<> auto make_vx_ptr<2>() { return new HepMC::GenVertex(); } - } + } // namespace detail_HepMC HepMC2Interface::HepMC2Interface(LHEF::HEPRUP const & heprup): + beam_particle_{static_cast(heprup.IDBMUP.first), + static_cast(heprup.IDBMUP.second)}, + beam_energy_{heprup.EBMUP.first, heprup.EBMUP.second}, event_count_(0.), tot_weight_(0.), tot_weight2_(0.) - { - beam_particle_[0] = static_cast(heprup.IDBMUP.first); - beam_particle_[1] = static_cast(heprup.IDBMUP.second); - beam_energy_[0] = heprup.EBMUP.first; - beam_energy_[1] = heprup.EBMUP.second; - } + {} HepMC::GenCrossSection HepMC2Interface::cross_section() const { HepMC::GenCrossSection xs; xs.set_cross_section(tot_weight_, std::sqrt(tot_weight2_)); return xs; } HepMC::GenEvent HepMC2Interface::init_event(Event const & event) const { const std::array beam { new HepMC::GenParticle( HepMC::FourVector(0,0,-beam_energy_[0],beam_energy_[0]), - beam_particle_[0], detail_HepMC::status_beam ), + beam_particle_[0], detail_HepMC::Status::beam ), new HepMC::GenParticle( HepMC::FourVector(0,0, beam_energy_[1],beam_energy_[1]), - beam_particle_[1], detail_HepMC::status_beam ) + beam_particle_[1], detail_HepMC::Status::beam ) }; auto hepmc_ev{ detail_HepMC::HepMC_init_kinematics<2>( event, beam, HepMC::GenEvent{ HepMC::Units::GEV, HepMC::Units::MM } ) }; hepmc_ev.weights().push_back( event.central().weight ); for(auto const & var: event.variations()){ hepmc_ev.weights().push_back( var.weight ); // no weight name for HepMC2 since rivet3 seem to mix them up // (could be added via hepmc_ev.weights()[name]=weight) } return hepmc_ev; } void HepMC2Interface::set_central( HepMC::GenEvent & out_ev, Event const & event, int const weight_index ){ EventParameters event_param; if(weight_index < 0) event_param = event.central(); else if ( static_cast(weight_index) < event.variations().size()) event_param = event.variations(weight_index); else throw std::invalid_argument{ "HepMC2Interface tried to access a weight outside of the variation range." }; const double wt = event_param.weight; tot_weight_ += wt; tot_weight2_ += wt * wt; ++event_count_; // central always on first assert(out_ev.weights().size() == event.variations().size()+1); out_ev.weights()[0] = wt; out_ev.set_cross_section( cross_section() ); out_ev.set_signal_process_id(event.type()); out_ev.set_event_scale(event_param.mur); out_ev.set_event_number(event_count_); /// @TODO add alphaQCD (need function) and alphaQED /// @TODO output pdf (currently not avaiable from event alone) } HepMC::GenEvent HepMC2Interface::operator()(Event const & event, int const weight_index ){ HepMC::GenEvent out_ev(init_event(event)); set_central(out_ev, event, weight_index); return out_ev; } -} +} // namespace HEJ #else // no HepMC2 => empty class namespace HepMC { class GenEvent {}; class GenCrossSection {}; } -namespace HEJ{ - HepMC2Interface::HepMC2Interface(LHEF::HEPRUP const &){ - throw std::invalid_argument( - "Failed to create HepMC2Interface: " - "HEJ 2 was built without HepMC2 support" - ); +namespace HEJ { + HepMC2Interface::HepMC2Interface(LHEF::HEPRUP const & /*heprup*/){ + ignore(beam_particle_,beam_energy_,event_count_,tot_weight_,tot_weight2_); + throw std::invalid_argument( + "Failed to create HepMC2Interface: " + "HEJ 2 was built without HepMC2 support" + ); } - HepMC::GenEvent HepMC2Interface::operator()(Event const &, int) + HepMC::GenEvent HepMC2Interface::operator()( + Event const & /*event*/, int /*weight_index*/ + ){return HepMC::GenEvent();} + HepMC::GenEvent HepMC2Interface::init_event(Event const & /*event*/) const {return HepMC::GenEvent();} - HepMC::GenEvent HepMC2Interface::init_event(Event const &) const - {return HepMC::GenEvent();} - void HepMC2Interface::set_central(HepMC::GenEvent &, Event const &, int){} + void HepMC2Interface::set_central( + HepMC::GenEvent & /*out_ev*/, Event const & /*event*/, int /*weight_index*/ + ){} HepMC::GenCrossSection HepMC2Interface::cross_section() const {return HepMC::GenCrossSection();} } #endif - -namespace HEJ{ - HepMC2Interface::~HepMC2Interface() = default; -} diff --git a/src/HepMC2Writer.cc b/src/HepMC2Writer.cc index f83ff01..04f77e0 100644 --- a/src/HepMC2Writer.cc +++ b/src/HepMC2Writer.cc @@ -1,85 +1,88 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC2Writer.hh" #include "LHEF/LHEF.h" #include "HEJ/ConfigFlags.hh" #ifdef HEJ_BUILD_WITH_HepMC2 #include #include "HepMC/IO_GenEvent.h" #include "HEJ/HepMC2Interface.hh" #else #include #include "HEJ/exceptions.hh" #endif #ifdef HEJ_BUILD_WITH_HepMC2 -namespace HEJ{ +namespace HEJ { struct HepMC2Writer::HepMC2WriterImpl{ HepMC2Interface hepmc_; HepMC2WriterImpl & operator=(HepMC2WriterImpl const & other) = delete; HepMC2WriterImpl(HepMC2WriterImpl const & other) = delete; HepMC2WriterImpl & operator=(HepMC2WriterImpl && other) = delete; HepMC2WriterImpl(HepMC2WriterImpl && other) = delete; + ~HepMC2WriterImpl() = default; HepMC::IO_GenEvent writer_; HepMC2WriterImpl( std::string const & file, LHEF::HEPRUP && heprup ): hepmc_(heprup), writer_{file} {} void write(Event const & ev){ auto out_ev = hepmc_(ev); writer_.write_event(&out_ev); } }; HepMC2Writer::HepMC2Writer(std::string const & file, LHEF::HEPRUP heprup): impl_{std::make_unique(file, std::move(heprup))} {} void HepMC2Writer::write(Event const & ev){ impl_->write(ev); } } // namespace HEJ #else // no HepMC2 -namespace HEJ{ +namespace HEJ { - class HepMC2Writer::HepMC2WriterImpl{}; + struct HepMC2Writer::HepMC2WriterImpl{}; - HepMC2Writer::HepMC2Writer(std::string const &, LHEF::HEPRUP){ + HepMC2Writer::HepMC2Writer( + std::string const & /*file*/, LHEF::HEPRUP /*heprup*/ + ){ throw std::invalid_argument( "Failed to create HepMC writer: " "HEJ 2 was built without HepMC2 support" ); } - void HepMC2Writer::write(Event const &){ + void HepMC2Writer::write(Event const & /*ev*/){ assert(false); } } #endif -namespace HEJ{ +namespace HEJ { HepMC2Writer::~HepMC2Writer() = default; } diff --git a/src/HepMC3Interface.cc b/src/HepMC3Interface.cc index 06cc966..d6c2714 100644 --- a/src/HepMC3Interface.cc +++ b/src/HepMC3Interface.cc @@ -1,214 +1,226 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC3Interface.hh" -#include "HEJ/exceptions.hh" #include "HEJ/ConfigFlags.hh" +#include "HEJ/exceptions.hh" + +// include before HepMC3 to override include of "HepMC3/LHEF.h" +// (theoretically both files should be the same) +#include "LHEF/LHEF.h" #ifdef HEJ_BUILD_WITH_HepMC3 #include #include #include #include #include -// include before HepMC3 to override include of "HepMC3/LHEF.h" -// (theoretically both files should be the same) -#include "LHEF/LHEF.h" - #include "HepMC3/Attribute.h" #include "HepMC3/FourVector.h" #include "HepMC3/GenCrossSection.h" #include "HepMC3/GenCrossSection_fwd.h" #include "HepMC3/GenEvent.h" #include "HepMC3/GenParticle.h" #include "HepMC3/GenParticle_fwd.h" #include "HepMC3/GenRunInfo.h" #include "HepMC3/GenVertex.h" #include "HepMC3/LHEFAttributes.h" #include "HepMC3/Units.h" -#include "HEJ/detail/HepMCInterface_common.hh" #include "HEJ/Event.hh" #include "HEJ/Parameters.hh" #include "HEJ/Particle.hh" +#include "HEJ/detail/HepMCInterface_common.hh" + +#else // no HepMC3 -namespace HEJ{ +#include "HEJ/utility.hh" + +#endif + +#ifdef HEJ_BUILD_WITH_HepMC3 + +namespace HEJ { namespace detail_HepMC { template<> struct HepMCVersion<3> { using GenEvent = HepMC3::GenEvent; using Beam = std::array; }; template<> auto make_particle_ptr<3> ( Particle const & sp, int status ) { return HepMC3::make_shared( to_FourVector(sp), static_cast (sp.type), status ); } template<> auto make_vx_ptr<3>() { return HepMC3::make_shared(); } - } + } // namespace detail_HepMC namespace { void reset_weight_info(LHEF::HEPRUP & heprup){ heprup.IDWTUP = 2; // use placeholders for unknown init block values // we can overwrite them after processing all events heprup.XSECUP = {0.}; heprup.XERRUP = {0.}; heprup.XMAXUP = {0.}; } HepMC3::shared_ptr init_runinfo(LHEF::HEPRUP heprup){ reset_weight_info(heprup); auto runinfo{ HepMC3::make_shared() }; auto hepr{ HepMC3::make_shared() }; - hepr->heprup = heprup; + hepr->heprup = std::move(heprup); runinfo->add_attribute(std::string("HEPRUP"), hepr); - for(auto const & gen: heprup.generators){ + for(auto const & gen: hepr->heprup.generators){ runinfo->tools().emplace_back( HepMC3::GenRunInfo::ToolInfo{gen.name, gen.version, gen.contents} ); } return runinfo; } std::vector get_weight_names(Event const & ev){ std::vector names; names.reserve(ev.variations().size()+1); // +1 from central names.emplace_back(""); // rivet assumes central band to have no name - for( size_t i=0; i(heprup.IDBMUP.first), + static_cast(heprup.IDBMUP.second)}, + beam_energy_{heprup.EBMUP.first, heprup.EBMUP.second}, + run_info_{ init_runinfo(std::move(heprup)) }, event_count_(0.), tot_weight_(0.), tot_weight2_(0.), xs_{std::make_shared()} { - beam_particle_[0] = static_cast(heprup.IDBMUP.first); - beam_particle_[1] = static_cast(heprup.IDBMUP.second); - beam_energy_[0] = heprup.EBMUP.first; - beam_energy_[1] = heprup.EBMUP.second; } HepMC3::GenEvent HepMC3Interface::init_event(Event const & event) const { const std::array beam { HepMC3::make_shared( HepMC3::FourVector(0,0,-beam_energy_[0],beam_energy_[0]), - beam_particle_[0], detail_HepMC::status_beam ), + beam_particle_[0], detail_HepMC::Status::beam ), HepMC3::make_shared( HepMC3::FourVector(0,0, beam_energy_[1],beam_energy_[1]), - beam_particle_[1], detail_HepMC::status_beam ) + beam_particle_[1], detail_HepMC::Status::beam ) }; auto hepmc_ev{ detail_HepMC::HepMC_init_kinematics<3>( event, beam, HepMC3::GenEvent{ HepMC3::Units::GEV, HepMC3::Units::MM } ) }; // set up run specific informations - if( run_info->weight_names().size() != event.variations().size()+1 ){ - run_info->set_weight_names( get_weight_names(event) ); + if( run_info_->weight_names().size() != event.variations().size()+1 ){ + run_info_->set_weight_names( get_weight_names(event) ); } // order matters: weights in hepmc_ev initialised when registering run_info - hepmc_ev.set_run_info(run_info); + hepmc_ev.set_run_info(run_info_); assert(hepmc_ev.weights().size() == event.variations().size()+1); for(size_t i=0; i(weight_index) < event.variations().size()) event_param = event.variations(weight_index); else throw std::invalid_argument{ "HepMC3Interface tried to access a weight outside of the variation range." }; const double wt = event_param.weight; tot_weight_ += wt; tot_weight2_ += wt * wt; ++event_count_; // central always on first assert(out_ev.weights().size() == event.variations().size()+1); out_ev.weights()[0] = wt; // out_ev can be setup with a different central scale -> save xs manually out_ev.set_cross_section(xs_); assert(out_ev.cross_section() && out_ev.cross_section() == xs_); // overwrites all previously set xs ... xs_->set_cross_section(tot_weight_,std::sqrt(tot_weight2_)); out_ev.set_event_number(event_count_); /// @TODO add number of attempted events xs_->set_accepted_events(event_count_); /// @TODO add alphaQCD (need function) and alphaQED /// @TODO output pdf (currently not avaiable from event alone) } HepMC3::GenEvent HepMC3Interface::operator()( Event const & event, int const weight_index ){ HepMC3::GenEvent out_ev(init_event(event)); set_central(out_ev, event, weight_index); return out_ev; } -} +} // namespace HEJ + #else // no HepMC3 => empty class + namespace HepMC3 { class GenEvent {}; class GenCrossSection {}; class GenRunInfo {}; } -namespace HEJ{ - HepMC3Interface::HepMC3Interface(LHEF::HEPRUP const &){ - throw std::invalid_argument( - "Failed to create HepMC3Interface: " - "HEJ 2 was built without HepMC3 support" - ); +namespace HEJ { + HepMC3Interface::HepMC3Interface(LHEF::HEPRUP /*heprup*/){ + ignore(beam_particle_,beam_energy_,event_count_,tot_weight_,tot_weight2_); + throw std::invalid_argument( + "Failed to create HepMC3Interface: " + "HEJ 2 was built without HepMC3 support" + ); } - HepMC3::GenEvent HepMC3Interface::operator()(Event const &, int) - {return HepMC3::GenEvent();} - HepMC3::GenEvent HepMC3Interface::init_event(Event const &) const + HepMC3::GenEvent HepMC3Interface::operator()( + Event const & /*event*/, int /*weight_index*/ + ){return HepMC3::GenEvent();} + HepMC3::GenEvent HepMC3Interface::init_event(Event const & /*event*/) const {return HepMC3::GenEvent();} - void HepMC3Interface::set_central(HepMC3::GenEvent &, Event const &, int){} + void HepMC3Interface::set_central( + HepMC3::GenEvent & /*out_ev*/, Event const & /*event*/, int /*weight_index*/ + ){} } #endif -namespace HEJ{ +namespace HEJ { HepMC3Interface::~HepMC3Interface() = default; } diff --git a/src/HepMC3Writer.cc b/src/HepMC3Writer.cc index f2d4025..5059cbf 100644 --- a/src/HepMC3Writer.cc +++ b/src/HepMC3Writer.cc @@ -1,101 +1,111 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/HepMC3Writer.hh" #include "LHEF/LHEF.h" #include "HEJ/ConfigFlags.hh" +#include "HEJ/exceptions.hh" #ifdef HEJ_BUILD_WITH_HepMC3 #include #include "HepMC3/Attribute.h" #include "HepMC3/WriterAscii.h" #include "HEJ/HepMC3Interface.hh" #else #include -#include "HEJ/exceptions.hh" - #endif #ifdef HEJ_BUILD_WITH_HepMC3 -namespace HEJ{ +namespace HEJ { - struct HepMC3Writer::HepMC3WriterImpl{ + struct HepMC3Writer::HepMC3WriterImpl: EventWriter{ HepMC3Interface HepMC3_; - HepMC3WriterImpl & operator=(HepMC3WriterImpl const & other) = delete; - HepMC3WriterImpl(HepMC3WriterImpl const & other) = delete; - HepMC3WriterImpl & operator=(HepMC3WriterImpl && other) = delete; - HepMC3WriterImpl(HepMC3WriterImpl && other) = delete; - std::unique_ptr writer_; std::string const file_; HepMC3WriterImpl( - std::string const & file, LHEF::HEPRUP && heprup + std::string file, LHEF::HEPRUP && heprup ): - HepMC3_{std::move(heprup)}, - file_{file} + HepMC3_{std::forward(heprup)}, + file_{std::move(file)} {} void init_writer(){ - writer_ = std::make_unique(file_, HepMC3_.run_info); + writer_ = std::make_unique(file_, HepMC3_.run_info()); } - ~HepMC3WriterImpl(){ + void finish() override { + if(finished()) + throw std::ios_base::failure("HepMC3 writer already finished."); + EventWriter::finish(); if(!writer_) // make sure that we always write something init_writer(); writer_->close(); } - void write(Event const & ev){ + void write(Event const & ev) override { auto out_ev = HepMC3_(ev); //! weight names are only available after first event if(!writer_) init_writer(); writer_->write_event(out_ev); } + + ~HepMC3WriterImpl() override { + finish_or_abort(this, "HepMC3Writer"); + } }; HepMC3Writer::HepMC3Writer(std::string const & file, LHEF::HEPRUP heprup): impl_{ std::make_unique(file, std::move(heprup)) } {} void HepMC3Writer::write(Event const & ev){ impl_->write(ev); } + + void HepMC3Writer::finish(){ + impl_->finish(); + } } // namespace HEJ #else // no HepMC3 -namespace HEJ{ +namespace HEJ { - class HepMC3Writer::HepMC3WriterImpl{}; + struct HepMC3Writer::HepMC3WriterImpl{}; - HepMC3Writer::HepMC3Writer(std::string const &, LHEF::HEPRUP){ + HepMC3Writer::HepMC3Writer( + std::string const & /*file*/, LHEF::HEPRUP /*heprup*/ + ){ throw std::invalid_argument( "Failed to create HepMC3 writer: " "HEJ 2 was built without HepMC3 support" ); } - void HepMC3Writer::write(Event const &){ + void HepMC3Writer::write(Event const & /*ev*/){ + assert(false); + } + void HepMC3Writer::finish(){ assert(false); } } #endif -namespace HEJ{ +namespace HEJ { HepMC3Writer::~HepMC3Writer() = default; } diff --git a/src/Hjets.cc b/src/Hjets.cc index 18b2a53..701dfe8 100644 --- a/src/Hjets.cc +++ b/src/Hjets.cc @@ -1,1133 +1,1154 @@ /** * \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 "CLHEP/Vector/LorentzVector.h" -#include "HEJ/Constants.hh" #include "HEJ/ConfigFlags.hh" +#include "HEJ/Constants.hh" #ifdef HEJ_BUILD_WITH_QCDLOOP #include #include "qcdloop/qcdloop.h" #else #include "HEJ/exceptions.hh" #endif namespace HEJ { namespace currents { namespace { // short hand for math functions using std::abs; using std::conj; using std::pow; using std::sqrt; - const COM looprwfactor = (COM(0.,1.)*M_PI*M_PI)/pow((2.*M_PI),4); - constexpr double infinity = std::numeric_limits::infinity(); + constexpr double infinity = std::numeric_limits::infinity(); // NOLINT // Loop integrals - #ifdef HEJ_BUILD_WITH_QCDLOOP + #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) { 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]; } COM A1(HLV const & q1, HLV const & q2, double mt) // As given in Eq. (B.2) of VDD { double q12,q22,Q2; HLV Q; double Delta3,mt2; COM ans(COM(0.,0.)); q12=q1.m2(); q22=q2.m2(); Q=-q1-q2; // Define all momenta ingoing as in appendix of VDD Q2=Q.m2(); Delta3=q12*q12+q22*q22+Q2*Q2-2*q12*q22-2*q12*Q2-2*q22*Q2; assert(mt > 0.); mt2=mt*mt; - ans=looprwfactor*COM(0,-1)*C0DD(q1,q2,mt)*( 4.*mt2/Delta3*(Q2-q12-q22) + ans=LOOPRWFACTOR*COM(0,-1)*C0DD(q1,q2,mt)*( 4.*mt2/Delta3*(Q2-q12-q22) -1.-4.*q12*q22/Delta3-12.*q12*q22*Q2/Delta3/Delta3*(q12+q22-Q2) ) - - looprwfactor*COM(0,-1)*( B0DD(q2,mt)-B0DD(Q,mt) ) + - LOOPRWFACTOR*COM(0,-1)*( B0DD(q2,mt)-B0DD(Q,mt) ) * ( 2.*q22/Delta3+12.*q12*q22/Delta3/Delta3*(q22-q12+Q2) ) - - looprwfactor*COM(0,-1)*( B0DD(q1,mt)-B0DD(Q,mt) ) + - LOOPRWFACTOR*COM(0,-1)*( B0DD(q1,mt)-B0DD(Q,mt) ) * ( 2.*q12/Delta3+12.*q12*q22/Delta3/Delta3*(q12-q22+Q2) ) - 2./Delta3/16/M_PI/M_PI*(q12+q22-Q2); return ans; } COM A2(HLV const & q1, HLV const & q2, double mt) // As given in Eq. (B.2) of VDD, but with high energy limit // of invariants taken. { double q12,q22,Q2; HLV Q; double Delta3,mt2; COM ans(COM(0.,0.)); assert(mt > 0.); mt2=mt*mt; q12=q1.m2(); q22=q2.m2(); Q=-q1-q2; // Define all momenta ingoing as in appendix of VDD Q2=Q.m2(); Delta3=q12*q12+q22*q22+Q2*Q2-2*q12*q22-2*q12*Q2-2*q22*Q2; - ans=looprwfactor*COM(0,-1)*C0DD(q1,q2,mt)*( 2.*mt2+1./2.*(q12+q22-Q2) + ans=LOOPRWFACTOR*COM(0,-1)*C0DD(q1,q2,mt)*( 2.*mt2+1./2.*(q12+q22-Q2) +2.*q12*q22*Q2/Delta3 ) - +looprwfactor*COM(0,-1)*(B0DD(q2,mt)-B0DD(Q,mt)) + +LOOPRWFACTOR*COM(0,-1)*(B0DD(q2,mt)-B0DD(Q,mt)) *q22*(q22-q12-Q2)/Delta3 - +looprwfactor*COM(0,-1)*(B0DD(q1,mt)-B0DD(Q,mt)) + +LOOPRWFACTOR*COM(0,-1)*(B0DD(q1,mt)-B0DD(Q,mt)) *q12*(q12-q22-Q2)/Delta3+1./16/M_PI/M_PI; return ans; } #else // no QCDloop - COM A1(HLV const &, HLV const &, double) { + COM A1(HLV const & /*q1*/, HLV const & /*q2*/, double /*mt*/){ throw std::logic_error{"A1 called without QCDloop support"}; } - COM A2(HLV const & , HLV const & , double) { + COM A2(HLV const & /*q1*/, HLV const & /*q2*/, double /*mt*/){ throw std::logic_error{"A2 called without QCDloop support"}; } #endif void to_current(const HLV & q, current & ret){ ret[0]=q.e(); ret[1]=q.x(); ret[2]=q.y(); ret[3]=q.z(); } /** * @brief Higgs vertex contracted with current @param C1 and @param C2 */ - COM cHdot(const current & C1, const current & C2, const current & q1, - const current & q2, double mt, bool incBot, double mb, double vev) + COM cHdot(const current & C1, const current & C2, const current & qH1, + const current & qH2, double mt, bool incBot, double mb, double vev) { if (mt == infinity) { - return (cdot(C1,C2)*cdot(q1,q2)-cdot(C1,q2)*cdot(C2,q1))/(3*M_PI*vev); + return (cdot(C1,C2)*cdot(qH1,qH2)-cdot(C1,qH2)*cdot(C2,qH1))/(3*M_PI*vev); } - else { - HLV vq1,vq2; - vq1.set(q1[1].real(),q1[2].real(),q1[3].real(),q1[0].real()); - vq2.set(q2[1].real(),q2[2].real(),q2[3].real(),q2[0].real()); - // first minus sign obtained because of q1-difference to VDD - // Factor is because 4 mt^2 g^2/vev A1 -> 16 pi mt^2/vev alphas, - if(!(incBot)) - return 16.*M_PI*mt*mt/vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mt) - -cdot(C1,C2)*A2(-vq1,vq2,mt)); - else - return 16.*M_PI*mt*mt/vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mt) - -cdot(C1,C2)*A2(-vq1,vq2,mt)) - + 16.*M_PI*mb*mb/vev*(-cdot(C1,q2)*cdot(C2,q1)*A1(-vq1,vq2,mb) - -cdot(C1,C2)*A2(-vq1,vq2,mb)); + HLV vq1(qH1[1].real(),qH1[2].real(),qH1[3].real(),qH1[0].real()); + HLV vq2(qH2[1].real(),qH2[2].real(),qH2[3].real(),qH2[0].real()); + // first minus sign obtained because of qH1-difference to VDD + // Factor is because 4 mt^2 g^2/vev A1 -> 16 pi mt^2/vev alphas, + if(incBot){ + return 16.*M_PI*mt*mt/vev*(-cdot(C1,qH2)*cdot(C2,qH1)*A1(-vq1,vq2,mt) + -cdot(C1,C2)*A2(-vq1,vq2,mt)) + + 16.*M_PI*mb*mb/vev*(-cdot(C1,qH2)*cdot(C2,qH1)*A1(-vq1,vq2,mb) + -cdot(C1,C2)*A2(-vq1,vq2,mb)); } + + return 16.*M_PI*mt*mt/vev*(-cdot(C1,qH2)*cdot(C2,qH1)*A1(-vq1,vq2,mt) + -cdot(C1,C2)*A2(-vq1,vq2,mt)); + } //@{ /** * @brief Higgs+Jets FKL Contributions, function to handle all incoming types. * @param p1out Outgoing Particle 1. (W emission) * @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 q1 t-channel momenta into higgs vertex - * @param q2 t-channel momenta out of higgs vertex + * @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 incBot Bool, to include bottom mass (true) or not (false)? * @param mb bottom mass (value) * @param pg Unordered Gluon momenta * * 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 & q1, HLV const & q2, + HLV const & p2in, HLV const & qH1, HLV const & qH2, double mt, bool incBot, double mb, double vev ){ - current j1p,j1m,j2p,j2m, q1v, q2v; + current j1p; + current j1m; + current j2p; + current j2m; + current q1v; + current q2v; // Note need to flip helicities in anti-quark case. joi(p1out, false, p1in, false, j1p); joi(p1out, true, p1in, true, j1m); joi(p2out, false, p2in, false, j2p); joi(p2out, true, p2in, true, j2m); - to_current(q1, q1v); - to_current(q2, q2v); + to_current(qH1, q1v); + to_current(qH2, q2v); COM Mmp=cHdot(j1m,j2p,q1v,q2v,mt, incBot, mb, vev); COM Mmm=cHdot(j1m,j2m,q1v,q2v,mt, incBot, mb, vev); COM Mpp=cHdot(j1p,j2p,q1v,q2v,mt, incBot, mb, vev); COM Mpm=cHdot(j1p,j2m,q1v,q2v,mt, incBot, mb, vev); // average over helicities const double sst=(abs2(Mmp)+abs2(Mmm)+abs2(Mpp)+abs2(Mpm))/4.; - return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*q1.m2()*q2.m2()); + return sst/((p1in-p1out).m2()*(p2in-p2out).m2()*qH1.m2()*qH2.m2()); } - } // namespace anonymous + } // namespace double ME_H_qQ(HLV const & p1out, HLV const & p1in, HLV const & p2out, - HLV const & p2in, HLV const & q1, HLV const & q2, double mt, - bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 & q1, HLV const & q2, - double mt, bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 & q1, HLV const & q2, - double mt, bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 & q1, HLV const & q2, - double mt, bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 & q1, HLV const & q2, - double mt, bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 & q1, HLV const & q2, - double mt, bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 & q1, HLV const & q2, - double mt, bool incBot, double mb, double vev + 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, q1, q2, mt, incBot, mb, 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 { //@{ /// @brief Higgs vertex contracted with one current CCurrent jH(HLV const & pout, bool helout, HLV const & pin, - bool helin, HLV const & q1, HLV const & q2, + bool helin, HLV const & qH1, HLV const & qH2, double mt, bool incBot, double mb, double vev ){ CCurrent j2 = joi(pout,helout,pin,helin); - CCurrent jq2(q2.e(),q2.px(),q2.py(),q2.pz()); + CCurrent jq2(qH2.e(),qH2.px(),qH2.py(),qH2.pz()); if(mt == infinity) - return ((q1.dot(q2))*j2 - j2.dot(q1)*jq2)/(3*M_PI*vev); - else - { - if(incBot) - return (-16.*M_PI*mb*mb/vev*j2.dot(q1)*jq2*A1(-q1,q2,mb) - -16.*M_PI*mb*mb/vev*j2*A2(-q1,q2,mb)) - + (-16.*M_PI*mt*mt/vev*j2.dot(q1)*jq2*A1(-q1,q2,mt) - -16.*M_PI*mt*mt/vev*j2*A2(-q1,q2,mt)); - else - return (-16.*M_PI*mt*mt/vev*j2.dot(q1)*jq2*A1(-q1,q2,mt) - -16.*M_PI*mt*mt/vev*j2*A2(-q1,q2,mt)); + return ((qH1.dot(qH2))*j2 - j2.dot(qH1)*jq2)/(3*M_PI*vev); + + if(incBot){ + return (-16.*M_PI*mb*mb/vev*j2.dot(qH1)*jq2*A1(-qH1,qH2,mb) + -16.*M_PI*mb*mb/vev*j2*A2(-qH1,qH2,mb)) + + (-16.*M_PI*mt*mt/vev*j2.dot(qH1)*jq2*A1(-qH1,qH2,mt) + -16.*M_PI*mt*mt/vev*j2*A2(-qH1,qH2,mt)); } + + return (-16.*M_PI*mt*mt/vev*j2.dot(qH1)*jq2*A1(-qH1,qH2,mt) + -16.*M_PI*mt*mt/vev*j2*A2(-qH1,qH2,mt)); + } //@} //@{ /** * @brief Higgs+Jets Unordered Contributions, function to handle all incoming types. * @param pg Unordered Gluon momenta * @param p1out Outgoing Particle 1. (W emission) * @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 q1 t-channel momenta into higgs vertex - * @param q2 t-channel momenta out of higgs vertex + * @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 incBot Bool, to include bottom mass (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, double mt, bool incBot, double mb, double vev ){ // This construction is taking rapidity order: pg > p1out >> p2out HLV q1=p1in-p1out; // Top End HLV q2=-(p2in-p2out); // Bottom End HLV qg=p1in-p1out-pg; // Extra bit post-gluon // Note current split into two by gauge choice. // See James C's Thesis (p72). -> CCurrent mj1p=joi(p1out,false, p1in,false); CCurrent mj1m=joi(p1out, true, p1in, true); CCurrent jgap=joi(pg, false, p1in,false); CCurrent jgam=joi(pg, true, p1in, true); // Note for function joo(): = . CCurrent j2gp=joo(p1out, false, pg, false); CCurrent j2gm=joo(p1out, true, pg, true); CCurrent mjH2p=jH(p2out, true,p2in, true,qH1,qH2,mt,incBot,mb, vev); CCurrent mjH2m=jH(p2out,false,p2in,false,qH1,qH2,mt,incBot,mb, vev); // Dot products of these which occur again and again COM MHmp=mj1m.dot(mjH2p); COM MHmm=mj1m.dot(mjH2m); COM MHpp=mj1p.dot(mjH2p); COM MHpm=mj1p.dot(mjH2m); - CCurrent p2o(p2out), p2i(p2in), p1o(p1out), p1i(p1in), qsum(q1+qg); + CCurrent p2o(p2out); + CCurrent p2i(p2in); + CCurrent p1o(p1out); + CCurrent p1i(p1in); + CCurrent qsum(q1+qg); CCurrent Lmm=(qsum*(MHmm) + (-2.*mjH2m.dot(pg))*mj1m + 2.*mj1m.dot(pg)*mjH2m + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHmm/2.) )/q1.m2(); CCurrent Lmp=(qsum*(MHmp) + (-2.*mjH2p.dot(pg))*mj1m + 2.*mj1m.dot(pg)*mjH2p + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHmp/2.) )/q1.m2(); CCurrent Lpm=(qsum*(MHpm) + (-2.*mjH2m.dot(pg))*mj1p + 2.*mj1p.dot(pg)*mjH2m + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHpm/2.) )/q1.m2(); CCurrent Lpp=(qsum*(MHpp) + (-2.*mjH2p.dot(pg))*mj1p + 2.*mj1p.dot(pg)*mjH2p + ( p2o/pg.dot(p2out) + p2i/pg.dot(p2in) )*( qg.m2()*MHpp/2.) )/q1.m2(); CCurrent U1mm=(jgam.dot(mjH2m)*j2gm+2.*p1o*MHmm)/(p1out+pg).m2(); CCurrent U1mp=(jgam.dot(mjH2p)*j2gm+2.*p1o*MHmp)/(p1out+pg).m2(); CCurrent U1pm=(jgap.dot(mjH2m)*j2gp+2.*p1o*MHpm)/(p1out+pg).m2(); CCurrent U1pp=(jgap.dot(mjH2p)*j2gp+2.*p1o*MHpp)/(p1out+pg).m2(); CCurrent U2mm=((-1.)*j2gm.dot(mjH2m)*jgam+2.*p1i*MHmm)/(p1in-pg).m2(); CCurrent U2mp=((-1.)*j2gm.dot(mjH2p)*jgam+2.*p1i*MHmp)/(p1in-pg).m2(); CCurrent U2pm=((-1.)*j2gp.dot(mjH2m)*jgap+2.*p1i*MHpm)/(p1in-pg).m2(); CCurrent U2pp=((-1.)*j2gp.dot(mjH2p)*jgap+2.*p1i*MHpp)/(p1in-pg).m2(); constexpr double cf=C_F; double amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm); double amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp); double apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm); double app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp); double ampsq=-(amm+amp+apm+app)/(q2.m2()*qH2.m2()); // Now add the t-channels for the Higgs ampsq/=qH1.m2()*qg.m2(); ampsq/=16.; // Factor of (Cf/Ca) for each quark to match ME_H_qQ. ampsq*=C_F*C_F/C_A/C_A; return ampsq; } -} // namespace anonymous +} // 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 incBot, double mb, + HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ - return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, 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 incBot, double mb, + HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ - return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, 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 incBot, double mb, + HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ - return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, 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 incBot, double mb, + HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ - return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, 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 incBot, double mb, + HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ - return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, 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 incBot, double mb, + HLV const & qH2, double mt, bool include_bottom, double mb, double vev ){ - return juno_h_j(pg, p1out, p1in, p2out, p2in, qH1, qH2, mt, incBot, mb, 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); - return looprwfactor*(-s12*D0DD(k2, k1, q2, mq)*(1 - 8.*mq*mq/s12 + S2/(2.*s12) + + 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); - return looprwfactor*(-S2*D0DD(k1, k2, q2, + 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; - return looprwfactor*(S2*D0DD(k1, q2, k2, + 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); - return looprwfactor* (-s12*D0DD(k2, k1, q2, + 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); - return looprwfactor* (-s12*D0DD(k1, k2, q2, + 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; - return looprwfactor* (-D0DD(k1, q2, k2, + 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); - return looprwfactor*(-s12*D0DD(k2, k1, q2, mq)*((s34 + S1)/Delta + + 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); - return looprwfactor* (s12*D0DD(k1, k2, q2, + 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; - return looprwfactor* (-D0DD(k1, q2, k2, mq)*(1. + + 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 H1(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 H4(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 H10(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 H2(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return -1.*H1(k2,k1,kh,mq); } COM H5(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return -1.*H4(k2,k1,kh,mq); } COM H12(HLV const & k1, HLV const & k2, HLV const & kh, double mq){ return -1.*H10(k2,k1,kh,mq); } // FL and FT functions COM FL(HLV const & q1, HLV const & q2, double mq){ HLV Q = q1 + q2; double detQ2 = q1.m2()*q2.m2() - q1.dot(q2)*q1.dot(q2); return -1./(2.*detQ2)*((2.- 3.*q1.m2()*q2.dot(Q)/detQ2)*(B0DD(q1, mq) - B0DD(Q, mq)) + (2. - 3.*q2.m2()*q1.dot(Q)/detQ2)*(B0DD(q2, mq) - B0DD(Q, mq)) - (4.*mq*mq + q1.m2() + q2.m2() + Q.m2() - 3.*q1.m2()*q2.m2()*Q.m2()/detQ2)*C0DD( q1, q2, mq) - 2.); } COM FT(HLV const & q1, HLV const & q2, double mq){ HLV Q = q1 + q2; double detQ2 = q1.m2()*q2.m2() - q1.dot(q2)*q1.dot(q2); return -1./(2.*detQ2)*(Q.m2()*(B0DD(q1, mq) + B0DD(q2, mq) - 2.*B0DD(Q, mq) - 2.*q1.dot(q2)*C0DD(q1, q2, mq)) + (q1.m2() - q2.m2()) *(B0DD(q1, mq) - B0DD(q2, mq))) - q1.dot(q2)*FL(q1, q2, mq); } HLV ParityFlip(HLV const & p){ HLV flippedVector; flippedVector.setE(p.e()); flippedVector.setX(-p.x()); flippedVector.setY(-p.y()); flippedVector.setZ(-p.z()); return flippedVector; } /// @brief HC amp for qg->qgH with finite top (i.e. j^{++}_H) void g_gH_HC(HLV const & pa, HLV const & p1, HLV pH, double mq, double vev, current &retAns) { current cura1,pacur,p1cur,pHcur,conjeps1,conjepsH1,epsa,epsHa,epsHapart1, epsHapart2,conjepsH1part1,conjepsH1part2; COM ang1a,sqa1; const double F = 4.*mq*mq/vev; // Easier to have the whole thing as current object so I can use cdot functionality. // Means I need to write pa,p1 as current objects to_current(pa, pacur); to_current(p1,p1cur); to_current(pH,pHcur); bool gluonforward = true; if(pa.z() < 0) gluonforward = false; //HEJ gauge jio(pa,false,p1,false,cura1); if(gluonforward){ // sqrt(2pa_-/p1_-)*p1_perp/abs(p1_perp) ang1a = sqrt(pa.plus()*p1.minus())*(p1.x()+COM(0.,1.)*p1.y())/p1.perp(); // sqrt(2pa_-/p1_-)*p1_perp*/abs(p1_perp) sqa1 = sqrt(pa.plus()*p1.minus())*(p1.x()-COM(0.,1.)*p1.y())/p1.perp(); } else { ang1a = sqrt(pa.minus()*p1.plus()); sqa1 = sqrt(pa.minus()*p1.plus()); } const double prop = (pa-p1-pH).m2(); cmult(-1./sqrt(2.)/ang1a,cura1,conjeps1); cmult(1./sqrt(2.)/sqa1,cura1,epsa); const COM Fta = FT(-pa,pa-pH,mq)/(pa-pH).m2(); const COM Ft1 = FT(-p1-pH,p1,mq)/(p1+pH).m2(); const COM h4 = H4(p1,-pa,pH,mq); const COM h5 = H5(p1,-pa,pH,mq); const COM h10 = H10(p1,-pa,pH,mq); const COM h12 = H12(p1,-pa,pH,mq); cmult(Fta*pa.dot(pH), epsa, epsHapart1); cmult(-1.*Fta*cdot(pHcur,epsa), pacur, epsHapart2); cmult(Ft1*cdot(pHcur,conjeps1), p1cur, conjepsH1part1); cmult(-Ft1*p1.dot(pH), conjeps1, conjepsH1part2); cadd(epsHapart1, epsHapart2, epsHa); cadd(conjepsH1part1, conjepsH1part2, conjepsH1); const COM aH1 = cdot(pHcur, cura1); current T1,T2,T3,T4,T5,T6,T7,T8,T9,T10; if(gluonforward){ cmult(sqrt(2.)*sqrt(p1.plus()/pa.plus())*prop/sqa1, conjepsH1, T1); cmult(-sqrt(2.)*sqrt(pa.plus()/p1.plus())*prop/ang1a, epsHa, T2); } else{ cmult(-sqrt(2.)*sqrt(p1.minus()/pa.minus()) *((p1.x()-COM(0.,1.)*p1.y())/p1.perp())*prop/sqa1, conjepsH1, T1); cmult(sqrt(2.)*sqrt(pa.minus()/p1.minus()) *((p1.x()-COM(0.,1.)*p1.y())/p1.perp())*prop/ang1a, epsHa, T2); } cmult(sqrt(2.)/ang1a*aH1, epsHa, T3); cmult(sqrt(2.)/sqa1*aH1, conjepsH1, T4); cmult(-sqrt(2.)*Fta*pa.dot(p1)*aH1/sqa1, conjeps1, T5); cmult(-sqrt(2.)*Ft1*pa.dot(p1)*aH1/ang1a, epsa, T6); cmult(-aH1/sqrt(2.)/sqa1*h4*8.*COM(0.,1.)*M_PI*M_PI, conjeps1, T7); cmult(aH1/sqrt(2.)/ang1a*h5*8.*COM(0.,1.)*M_PI*M_PI, epsa, T8); cmult(aH1*aH1/2./ang1a/sqa1*h10*8.*COM(0.,1.)*M_PI*M_PI, pacur, T9); cmult(-aH1*aH1/2./ang1a/sqa1*h12*8.*COM(0.,1.)*M_PI*M_PI, p1cur, T10); current ans; for(int i=0;i<4;++i) { ans[i] = T1[i]+T2[i]+T3[i]+T4[i]+T5[i]+T6[i]+T7[i]+T8[i]+T9[i]+T10[i]; } retAns[0] = F/prop*ans[0]; retAns[1] = F/prop*ans[1]; retAns[2] = F/prop*ans[2]; retAns[3] = F/prop*ans[3]; } /// @brief HNC amp for qg->qgH with finite top (i.e. j^{+-}_H) void g_gH_HNC(HLV const & pa, HLV const & p1, HLV const & pH, double mq, double vev, current &retAns ){ const double F = 4.*mq*mq/vev; current conjepsH1,epsHa,p1cur,pacur,pHcur,conjeps1,epsa,paplusp1cur, p1minuspacur,cur1a,cura1,epsHapart1,epsHapart2,conjepsH1part1, conjepsH1part2; // Find here if pa, meaning the gluon, is forward or backward bool gluonforward = true; if(pa.z() < 0) gluonforward = false; jio(pa,true,p1,true,cura1); joi(p1,true,pa,true,cur1a); to_current(pa,pacur); to_current(p1,p1cur); to_current(pH,pHcur); to_current(pa+p1,paplusp1cur); to_current(p1-pa,p1minuspacur); const COM aH1 = cdot(pHcur,cura1); const COM oneHa = conj(aH1); // = cdot(pHcur,cur1a) COM const sqa1 = gluonforward // sqrt(2pa_-/p1_-)*p1_perp*/abs(p1_perp) ?sqrt(pa.plus()*p1.minus())*(p1.x()-COM(0.,1.)*p1.y())/p1.perp() :sqrt(pa.minus()*p1.plus()); const double prop = (pa-p1-pH).m2(); cmult(1./sqrt(2.)/sqa1, cur1a, epsa); cmult(-1./sqrt(2.)/sqa1, cura1, conjeps1); const COM phase = cdot(conjeps1, epsa); const COM Fta = FT(-pa,pa-pH,mq)/(pa-pH).m2(); const COM Ft1 = FT(-p1-pH,p1,mq)/(p1+pH).m2(); const COM Falpha = FT(p1-pa,pa-p1-pH,mq); const COM Fbeta = FL(p1-pa,pa-p1-pH,mq); const COM h1 = H1(p1,-pa, pH, mq); const COM h2 = H2(p1,-pa, pH, mq); const COM h4 = H4(p1,-pa, pH, mq); const COM h5 = H5(p1,-pa, pH, mq); const COM h10 = H10(p1,-pa, pH, mq); const COM h12 = H12(p1,-pa, pH, mq); cmult(Fta*pa.dot(pH), epsa, epsHapart1); cmult(-1.*Fta*cdot(pHcur,epsa), pacur, epsHapart2); cmult(Ft1*cdot(pHcur,conjeps1), p1cur, conjepsH1part1); cmult(-Ft1*p1.dot(pH), conjeps1, conjepsH1part2); cadd(epsHapart1, epsHapart2, epsHa); cadd(conjepsH1part1, conjepsH1part2, conjepsH1); current T1,T2,T3,T4,T5a,T5b,T6,T7,T8a,T8b,T9,T10,T11a, T11b,T12a,T12b,T13; if(gluonforward){ cmult(sqrt(2.)*sqrt(p1.plus()/pa.plus())*prop/sqa1, conjepsH1, T1); cmult(-sqrt(2.)*sqrt(pa.plus()/p1.plus())*prop/sqa1, epsHa, T2); } else{ cmult(-sqrt(2.)*sqrt(p1.minus()/pa.minus())*((p1.x()-COM(0.,1.)*p1.y())/p1.perp()) *prop/sqa1, conjepsH1, T1); cmult(sqrt(2.)*sqrt(pa.minus()/p1.minus())*((p1.x()+COM(0.,1.)*p1.y())/p1.perp()) *prop/sqa1, epsHa, T2); } const COM boxdiagFact = 8.*COM(0.,1.)*M_PI*M_PI; cmult(aH1*sqrt(2.)/sqa1, epsHa, T3); cmult(oneHa*sqrt(2.)/sqa1, conjepsH1, T4); cmult(-2.*phase*Fta*pa.dot(pH), p1cur, T5a); cmult(2.*phase*Ft1*p1.dot(pH), pacur, T5b); cmult(-sqrt(2.)*Fta*p1.dot(pa)*oneHa/sqa1, conjeps1, T6); cmult(-sqrt(2.)*Ft1*pa.dot(p1)*aH1/sqa1, epsa, T7); cmult(-boxdiagFact*phase*h2, pacur, T8a); cmult(boxdiagFact*phase*h1, p1cur, T8b); cmult(boxdiagFact*aH1/sqrt(2.)/sqa1*h5, epsa, T9); cmult(-boxdiagFact*oneHa/sqrt(2.)/sqa1*h4, conjeps1, T10); cmult(boxdiagFact*aH1*oneHa/2./sqa1/sqa1*h10, pacur, T11a); cmult(-boxdiagFact*aH1*oneHa/2./sqa1/sqa1*h12, p1cur, T11b); cmult(-phase/(pa-p1).m2()*Falpha*(p1-pa).dot(pa-p1-pH), paplusp1cur, T12a); cmult(phase/(pa-p1).m2()*Falpha*(pa+p1).dot(pa-p1-pH), p1minuspacur, T12b); cmult(-phase*Fbeta*(pa-p1-pH).m2(), paplusp1cur, T13); current ans; for(int i=0;i<4;++i) { ans[i] = T1[i]+T2[i]+T3[i]+T4[i]+T5a[i]+T5b[i]+T6[i]+T7[i]+T8a[i]+T8b[i] +T9[i]+T10[i]+T11a[i]+T11b[i]+T12a[i]+T12b[i]+T13[i]; } retAns[0] = F/prop*ans[0]; retAns[1] = F/prop*ans[1]; retAns[2] = F/prop*ans[2]; retAns[3] = F/prop*ans[3]; } -} // namespace anonymous +} // namespace // JDC - new amplitude with Higgs emitted close to gluon with full mt effects. // Keep usual HEJ-style function call double ME_Houtside_gq(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in, HLV const & pH, - double mq, bool includeBottom, double mq2, double vev + double mq, bool include_bottom, double mq2, double vev ){ current cur2bplus,cur2bminus, cur2bplusFlip, cur2bminusFlip; current retAns,retAnsb; joi(p2out,true,p2in,true,cur2bplus); joi(p2out,false,p2in,false,cur2bminus); joi(ParityFlip(p2out),true,ParityFlip(p2in),true,cur2bplusFlip); joi(ParityFlip(p2out),false,ParityFlip(p2in),false,cur2bminusFlip); COM app1,app2,apm1,apm2; COM app3, app4, apm3, apm4; - if(!includeBottom) + if(!include_bottom) { g_gH_HC(p1in,p1out,pH,mq,vev,retAns); app1=cdot(retAns,cur2bplus); app2=cdot(retAns,cur2bminus); g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); app3=cdot(retAns,cur2bplusFlip); app4=cdot(retAns,cur2bminusFlip); // And non-conserving bits g_gH_HNC(p1in,p1out,pH,mq,vev,retAns); apm1=cdot(retAns,cur2bplus); apm2=cdot(retAns,cur2bminus); g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); apm3=cdot(retAns,cur2bplusFlip); apm4=cdot(retAns,cur2bminusFlip); } else { g_gH_HC(p1in,p1out,pH,mq,vev,retAns); g_gH_HC(p1in,p1out,pH,mq2,vev,retAnsb); app1=cdot(retAns,cur2bplus) + cdot(retAnsb,cur2bplus); app2=cdot(retAns,cur2bminus) + cdot(retAnsb,cur2bminus); g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); g_gH_HC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq2,vev,retAnsb); app3=cdot(retAns,cur2bplusFlip) + cdot(retAnsb,cur2bplusFlip); app4=cdot(retAns,cur2bminusFlip) + cdot(retAnsb,cur2bminusFlip); // And non-conserving bits g_gH_HNC(p1in,p1out,pH,mq,vev,retAns); g_gH_HNC(p1in,p1out,pH,mq2,vev,retAnsb); apm1=cdot(retAns,cur2bplus) + cdot(retAnsb,cur2bplus); apm2=cdot(retAns,cur2bminus) + cdot(retAnsb,cur2bminus); g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq,vev,retAns); g_gH_HNC(ParityFlip(p1in),ParityFlip(p1out),ParityFlip(pH),mq2,vev,retAnsb); apm3=cdot(retAns,cur2bplusFlip) + cdot(retAnsb,cur2bplusFlip); apm4=cdot(retAns,cur2bminusFlip) + cdot(retAnsb,cur2bminusFlip); } return abs2(app1) + abs2(app2) + abs2(app3) + abs2(app4) + abs2(apm1) + abs2(apm2) + abs2(apm3) + abs2(apm4); } #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,p1p,p2p; - COM p1perp,p3perp,phperp; + 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,php,p1p,phm; - COM p1perp,p3perp,phperp; + 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,p2p,p1p; - COM p1perp,p3perp,phperp; + 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/JetSplitter.cc b/src/JetSplitter.cc index ade6731..05c4157 100644 --- a/src/JetSplitter.cc +++ b/src/JetSplitter.cc @@ -1,192 +1,200 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/JetSplitter.hh" #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "fastjet/JetDefinition.hh" #include "fastjet/PseudoJet.hh" #include "HEJ/Constants.hh" -#include "HEJ/exceptions.hh" #include "HEJ/RNG.hh" +#include "HEJ/exceptions.hh" namespace HEJ { - namespace{ - constexpr double ccut=HEJ::CMINPT; // min parton pt + namespace { template bool same_pt_and_rapidity( Iterator begin, Iterator end, fastjet::PseudoJet const & jet ){ constexpr double ep = 1e-2; const fastjet::PseudoJet reconstructed_jet = std::accumulate( begin, end, fastjet::PseudoJet{} ); return (std::abs(reconstructed_jet.pt() - jet.pt()) < ep) && (std::abs(reconstructed_jet.rapidity() - jet.rapidity()) < ep) ; } bool all_in_one_jet( std::vector const & partons, - fastjet::JetDefinition jet_def, double min_jet_pt + fastjet::JetDefinition const & jet_def, double min_jet_pt ){ fastjet::ClusterSequence ev(partons, jet_def); const std::vector testjet = ev.inclusive_jets(min_jet_pt); return testjet.size() == 1u && testjet[0].constituents().size() == partons.size(); } - } - JetSplitter::JetSplitter( - fastjet::JetDefinition jet_def, double min_pt - ): - min_jet_pt_{min_pt}, - jet_def_{std::move(jet_def)} - {} - using SplitResult = JetSplitter::SplitResult; + /** @brief sample y-phi distance to jet pt axis for a jet splitting into two + * partons + * + * @param wt Multiplied by the weight of the sampling point + * @param ran Random number generator + * @returns The distance in units of the jet radius + */ + double sample_distance_2p(double & wt, RNG & ran) { + static constexpr double x_small = 0.1; + static constexpr double p_small = 0.4; + + const double pR = ran.flat(); + if(pR < p_small){ + wt *= x_small/p_small; + return x_small/p_small*pR; + } + wt *= (1-x_small)/(1-p_small); + return (1-x_small)/(1-p_small)*(pR-p_small) + x_small; + } + } // namespace + + JetSplitter::JetSplitter( + fastjet::JetDefinition jet_def, double min_pt + ): + min_jet_pt_{min_pt}, + jet_def_{std::move(jet_def)} + {} - SplitResult JetSplitter::split( + JetSplitter::SplitResult JetSplitter::split( fastjet::PseudoJet const & j2split, int ncons, RNG & ran ) const{ if(ncons <= 0) { throw std::invalid_argument{ "number of requested jet constituents less than 1" }; } double swt = 1.; std::vector jcons; if(ncons == 1){ jcons.emplace_back(j2split); jcons.back().set_user_index(0); return {jcons, swt}; } if(ncons == 2){ return Split2(j2split, ran); } - const double R_max = R_factor*jet_def_.R(); + const double R_max = R_FACTOR*jet_def_.R(); assert(R_max < M_PI); double pt_remaining = j2split.pt(); const double phi_jet = j2split.phi(); const double y_jet = j2split.rapidity(); for(int i = 0; i < ncons - 1; ++i){ /** * Generate rapidity and azimuthal angle with a distance * R = sqrt(delta_y^2 + delta_phi^2) < R_max * from the jet centre */ const double R = R_max*ran.flat(); const double theta = 2*M_PI*ran.flat(); const double delta_phi = R*std::cos(theta); const double delta_y = R*std::sin(theta); /** * Generate pt such that the total contribution of all partons * along the jet pt axis does not exceed the jet pt */ const double pt_max = pt_remaining/std::cos(delta_phi); assert(pt_max > 0); - if(pt_max < ccut) return {}; // no pt remaining for this parton - const double pt = (pt_max - ccut)*ran.flat() + ccut; + if(pt_max < CMINPT) return {}; // no pt remaining for this parton + const double pt = (pt_max - CMINPT)*ran.flat() + CMINPT; pt_remaining -= pt*std::cos(delta_phi); jcons.emplace_back( pt*std::cos(phi_jet + delta_phi), pt*std::sin(phi_jet + delta_phi), pt*std::sinh(y_jet + delta_y), pt*std::cosh(y_jet + delta_y) ); jcons.back().set_user_index(i); - swt *= 2*M_PI*R*R_max*pt*(pt_max - ccut); + swt *= 2*M_PI*R*R_max*pt*(pt_max - CMINPT); } const fastjet::PseudoJet p_total = std::accumulate( jcons.cbegin(), jcons.cend(), fastjet::PseudoJet{} ); // Calculate the pt of the last parton const double last_px = j2split.px() - p_total.px(); const double last_py = j2split.py() - p_total.py(); const double last_pt = std::sqrt(last_px*last_px + last_py*last_py); - if(last_pt < ccut) return {}; + if(last_pt < CMINPT) return {}; // Calculate the rapidity of the last parton using the requirement that the // new jet must have the same rapidity as the LO jet. const double exp_2y_jet = (j2split.e() + j2split.pz())/(j2split.e() - j2split.pz()); const double bb = (p_total.e()+p_total.pz()) - exp_2y_jet*(p_total.e()-p_total.pz()); const double lasty = std::log( ( -bb+std::sqrt(bb*bb+4.*exp_2y_jet*last_pt*last_pt) ) / ( 2.*last_pt )); jcons.emplace_back( last_px, last_py, last_pt*std::sinh(lasty), last_pt*std::cosh(lasty) ); jcons.back().set_user_index(ncons-1); assert(same_pt_and_rapidity(jcons.cbegin(), jcons.cend(), j2split)); // Test that the last parton is not too far away from the jet centre. if (jcons.back().delta_R(j2split) > R_max) return {}; if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {}; return {jcons, swt}; } - double JetSplitter::sample_distance_2p(double & wt, RNG & ran) const{ - static constexpr double x_small = 0.1; - static constexpr double p_small = 0.4; - - const double pR = ran.flat(); - if(pR < p_small){ - wt *= x_small/p_small; - return x_small/p_small*pR; - } - wt *= (1-x_small)/(1-p_small); - return (1-x_small)/(1-p_small)*(pR-p_small) + x_small; - } - - SplitResult JetSplitter::Split2( + JetSplitter::SplitResult JetSplitter::Split2( fastjet::PseudoJet const & j2split, RNG & ran ) const { static constexpr std::size_t ncons = 2; std::vector jcons(ncons); - std::array R, phi, y, pt; + std::array R{}; + std::array phi{}; + std::array y{}; + std::array pt{}; double wt = 1; const double theta = 2*M_PI*ran.flat(); // angle in y-phi plane // empiric observation: we are always within the jet radius R[0] = sample_distance_2p(wt, ran)*jet_def_.R(); R[1] = -sample_distance_2p(wt, ran)*jet_def_.R(); for(std::size_t i = 0; i <= 1; ++i){ phi[i] = j2split.phi() + R[i]*std::cos(theta); y[i] = j2split.rapidity() + R[i]*std::sin(theta); } for(std::size_t i = 0; i <= 1; ++i){ pt[i] = (j2split.py() - std::tan(phi[1-i])*j2split.px())/ (std::sin(phi[i]) - std::tan(phi[1-i])*std::cos(phi[i])); - if(pt[i] < ccut) return {}; + if(pt[i] < CMINPT) return {}; jcons[i].reset_PtYPhiM(pt[i], y[i], phi[i]); jcons[i].set_user_index(i); } assert(same_pt_and_rapidity(jcons.cbegin(), jcons.cend(), j2split)); if(! all_in_one_jet(jcons, jet_def_, min_jet_pt_)) return {}; wt *= 2*M_PI*pt[0]*R[0]*jet_def_.R()*jet_def_.R(); // from transformation of delta(R[1] - ...) to delta(pt[0] - ...) const double dphi0 = phi[0] - j2split.phi(); const double ptJ = j2split.pt(); const double jacobian = std::cos(theta)*pt[1]*pt[1]/(ptJ*std::sin(dphi0)); return {jcons, jacobian*wt}; } -} +} // namespace HEJ diff --git a/src/LesHouchesReader.cc b/src/LesHouchesReader.cc index d1e849c..4c41db4 100644 --- a/src/LesHouchesReader.cc +++ b/src/LesHouchesReader.cc @@ -1,36 +1,36 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ #include "HEJ/LesHouchesReader.hh" #include -namespace HEJ{ +namespace HEJ { SherpaLHEReader::SherpaLHEReader(std::string const & filename): LesHouchesReader{filename}, - num_trials{0.}, num_events{0} + num_trials_{0.}, num_events_{0} { LesHouchesReader tmp_reader{filename}; - reader_.heprup.XSECUP = std::vector{0}; + reader().heprup.XSECUP = std::vector{0}; while(tmp_reader.read_event()){ - ++num_events; - num_trials += std::stod(tmp_reader.hepeup().attributes.at("trials")); - reader_.heprup.XSECUP.front() += tmp_reader.hepeup().XWGTUP; + ++num_events_; + num_trials_ += std::stod(tmp_reader.hepeup().attributes.at("trials")); + reader().heprup.XSECUP.front() += tmp_reader.hepeup().XWGTUP; } - reader_.heprup.XSECUP.front() /= num_trials; + reader().heprup.XSECUP.front() /= num_trials_; // For IDWTUP == 1 or 4 we assume avg(weight)=xs // With the modified weights we have in Sherpa sum(weight)=xs // -> overwrite IDWTUP to "something neutral" - reader_.heprup.IDWTUP = reader_.heprup.IDWTUP>0?3:-3; + reader().heprup.IDWTUP = reader().heprup.IDWTUP>0?3:-3; } bool SherpaLHEReader::read_event() { if(!LesHouchesReader::read_event()) return false; - reader_.hepeup.XWGTUP/=num_trials; - for(auto & wt: reader_.hepeup.weights) - wt.first/=num_trials; + reader().hepeup.XWGTUP/=num_trials_; + for(auto & wt: reader().hepeup.weights) + wt.first/=num_trials_; return true; } -} +} // namespace HEJ diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc index a8b8726..6f6b864 100644 --- a/src/LesHouchesWriter.cc +++ b/src/LesHouchesWriter.cc @@ -1,125 +1,135 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/LesHouchesWriter.hh" #include #include #include #include #include #include "HEJ/Event.hh" -#include "HEJ/event_types.hh" #include "HEJ/Parameters.hh" +#include "HEJ/event_types.hh" +#include "HEJ/exceptions.hh" +#include "HEJ/utility.hh" -namespace HEJ{ +namespace HEJ { namespace { std::size_t to_index(event_type::EventType const type){ return type==0?0:1+std::floor(std::log2(static_cast(type))); } } LesHouchesWriter::LesHouchesWriter( std::string const & file, LHEF::HEPRUP heprup ): out_{file, std::fstream::in | std::fstream::out | std::fstream::trunc}, writer_{std::make_unique(out_)} { if(! out_.is_open()){ throw std::ios_base::failure("Failed to open " + file); }; // scientific style is needed to allow rewriting the init block out_ << std::scientific; writer_->heprup = std::move(heprup); // lhe Standard: IDWTUP (negative => weights = +/-) // IDWTUP: HEJ -> SHG/Pythia/next program // 1: weighted->unweighted, xs = mean(weight), XMAXUP given // 2: weighted->unweighted, xs = XSECUP, XMAXUP given // 3: unweighted (weight=+1)->unweighted, no additional information // 4: weighted->weighted, xs = mean(weight) // // None of these codes actually match what we want: // 1 and 4 require xs = mean(weight), which is impossible until after generation // 2 tells the SHG to unweight our events, which is wasteful // 3 claims we produce unweighted events, which is both wasteful _and_ // impossible until after generation (we don't know the maximum weight before) // // For the time being, we choose -3. If the consumer (like Pythia) assumes // weight=+1, the final weights have to be corrected by multiplying with // the original weight we provided. We are also often use NLO-PDFs which can // give negative weights, hence the native IDWTUP. // writer_->heprup.IDWTUP = -3; // always use the newest LHE version // Pythia only saves weights (hepeup.XWGTUP) for version >=2 writer_->heprup.version = LHEF::HEPRUP().version; - const int max_number_types = to_index(event_type::last_type)+1; + const std::size_t max_number_types = to_index(event_type::last_type)+1; writer_->heprup.NPRUP = max_number_types; // ids of event types writer_->heprup.LPRUP.clear(); writer_->heprup.LPRUP.reserve(max_number_types); writer_->heprup.LPRUP.emplace_back(0); for(auto i=event_type::first_type+1; i<=event_type::last_type; i*=2) writer_->heprup.LPRUP.emplace_back(i); // use placeholders for unknown init block values // we can overwrite them after processing all events writer_->heprup.XSECUP = std::vector(max_number_types, 0.); writer_->heprup.XERRUP = std::vector(max_number_types, 0.); writer_->heprup.XMAXUP = std::vector(max_number_types, 0.); write_init(); } void LesHouchesWriter::write(Event const & ev){ assert(writer_ && out_.is_open()); const double wt = ev.central().weight; - writer_->hepeup = HEJ::to_HEPEUP(std::move(ev), &heprup()); + writer_->hepeup = to_HEPEUP(ev, &heprup()); writer_->writeEvent(); assert(heprup().XSECUP.size() > to_index(ev.type())); heprup().XSECUP[to_index(ev.type())] += wt; heprup().XERRUP[to_index(ev.type())] += wt*wt; if(wt > heprup().XMAXUP[to_index(ev.type())]){ heprup().XMAXUP[to_index(ev.type())] = wt; } } // this function is called after overwritting the Les Houches init block // assert that we have overwritten *exactly* the init block, // i.e. we are at the end of the file or an intact event block is next void assert_next_event_intact(std::istream & out){ - (void) out; // suppress compiler warnings if not in debug mode + ignore(out); // suppress compiler warnings if not in debug mode #ifndef NDEBUG std::string line; getline(out, line); assert(out.eof() || line.rfind(" #include namespace HEJ { namespace { using COM = std::complex; COM perp(CLHEP::HepLorentzVector const & p) { return COM{p.x(), p.y()}; } void angle_aux(CLHEP::HepLorentzVector const & p, COM & perp_hat, double & p_plus, double & p_minus) { const COM p_perp = perp(p); if(p_perp == COM{0., 0.}){ perp_hat = -1.; if(std::abs(std::abs(p.z())/p.t() - 1.) > 1e-9){ throw std::invalid_argument("zero pT particle with |pz| != E"); } if(p.z() > 0){ p_plus = p.plus(); p_minus = 0.; }else{ p_plus = 0.; p_minus = p.minus(); } }else{ perp_hat = p_perp/std::abs(p_perp); p_plus = p.plus(); p_minus = p.minus(); } } } // anonymous namespace // "angle" product angle(pi, pj) = // see eq. (53) (\eqref{eq:angle_product}) in developer manual COM angle( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ) { - COM pi_perp_hat, pj_perp_hat; - double pi_plus, pi_minus, pj_plus, pj_minus; + COM pi_perp_hat; + COM pj_perp_hat; + double pi_plus = NAN; + double pi_minus = NAN; + double pj_plus = NAN; + double pj_minus = NAN; angle_aux(pi, pi_perp_hat, pi_plus, pi_minus); angle_aux(pj, pj_perp_hat, pj_plus, pj_minus); return + std::sqrt(pi_minus*pj_plus)*pi_perp_hat - std::sqrt(pi_plus*pj_minus)*pj_perp_hat; } // "square" product square(pi, pj) = [i j] // see eq. (54) (\eqref{eq:square_product}) in developer manual COM square( CLHEP::HepLorentzVector const & pi, CLHEP::HepLorentzVector const & pj ) { return -std::conj(angle(pi, pj)); } // see eq:P_massive, eq:P_massive_p, eq:P_massive_q, , eq:P_massive_plus, // eq:P_massive_minus in the developer manual std::pair split_into_lightlike(CLHEP::HepLorentzVector const & P) { const double pt = P.perp(); if(P.plus() > 0){ const double y = std::log(P.plus()/pt); const double E = P.m2()/(2.*P.plus()); return std::make_pair( CLHEP::HepLorentzVector{P.x(), P.y(), pt*std::sinh(y), pt*std::cosh(y)}, CLHEP::HepLorentzVector{0., 0., -E, E} ); - } else { - const double y = std::log(pt/P.minus()); - const double E = P.m2()/(2.*P.minus()); - return std::make_pair( - CLHEP::HepLorentzVector{P.x(), P.y(), pt*std::sinh(y), pt*std::cosh(y)}, - CLHEP::HepLorentzVector{0., 0., +E, E} - ); } + const double y = std::log(pt/P.minus()); + const double E = P.m2()/(2.*P.minus()); + return std::make_pair( + CLHEP::HepLorentzVector{P.x(), P.y(), pt*std::sinh(y), pt*std::cosh(y)}, + CLHEP::HepLorentzVector{0., 0., +E, E} + ); } -} +} // namespace HEJ diff --git a/src/MatrixElement.cc b/src/MatrixElement.cc index 41b0d2b..1317234 100644 --- a/src/MatrixElement.cc +++ b/src/MatrixElement.cc @@ -1,2247 +1,2186 @@ /** * \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 "CLHEP/Vector/LorentzVector.h" #include "fastjet/PseudoJet.hh" #include "HEJ/ConfigFlags.hh" #include "HEJ/Constants.hh" -#include "HEJ/Event.hh" -#include "HEJ/event_types.hh" #include "HEJ/EWConstants.hh" -#include "HEJ/exceptions.hh" +#include "HEJ/Event.hh" #include "HEJ/HiggsCouplingSettings.hh" #include "HEJ/Hjets.hh" -#include "HEJ/jets.hh" -#include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" -#include "HEJ/utility.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{ +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)) + 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(size_t i=0; i(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 // 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) { 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){ --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; //if qqxMid event, virtual correction do not occur between //qqx pair. if(event.type() == event_type::qqxmid){ 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; } 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 (wqq) q -= WBoson.p; for(std::size_t j = first_idx_qqx; j < last_idx_qqx; ++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 ); #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., sum_bot=0., sum_mix=0.; + 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)}; - }else if(is_gluon(in.front().type)){ + } + if(is_gluon(in.front().type)){ // This is a gq event return {virtual_corrections_Z_qg(event, mur, *AWZH_boson, true)}; - }else{ - // This is a qq event - return virtual_corrections_Z_qq(event, mur, *AWZH_boson); } + // 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 // 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){ q -= out[1].p; ++first_idx; } if((out.back().type == pid::Higgs) || event.type() == event_type::unof || event.type() == event_type::qqxexf){ --last_idx; } std::size_t first_idx_qqx = last_idx; std::size_t last_idx_qqx = last_idx; //if qqxMid event, virtual correction do not occur between //qqx pair. if(event.type() == event_type::qqxmid){ 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; } 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; for(std::size_t j = first_idx_qqx; j < last_idx_qqx; ++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 ); 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) { + if (kperp > lambda){ return Cls; - } else { - return Cls - 4.0 / (kperp * kperp); } + 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; - } else { - return Cls - 4.0 / (kperp * kperp); } + 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); - else - return ME_unob_qbarg(pg,p1,pa,pn,pb); + return ME_unob_qbarg(pg,p1,pa,pn,pb); } - else if (is_antiquark(bptype)) { + if (is_antiquark(bptype)) { if (is_quark(aptype)) return ME_unob_qQbar(pg,p1,pa,pn,pb); - else - return ME_unob_qbarQbar(pg,p1,pa,pn,pb); + return ME_unob_qbarQbar(pg,p1,pa,pn,pb); } - else { //bptype == quark - if (is_quark(aptype)) + //bptype == quark + if (is_quark(aptype)) return ME_unob_qQ(pg,p1,pa,pn,pb); - else - return ME_unob_qbarQ(pg,p1,pa,pn,pb); - } - throw std::logic_error("unreachable"); + 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. * @returns ME Squared for Tree-Level Current-Current Scattering * * @note The qqxf contribution can be calculated by reversing the argument ordering. */ double ME_qqx_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 ){ using namespace currents; if (bptype==pid::gluon) { if (swap_q_qx) // pq extremal return ME_Exqqx_qqbarg(pgin,pq,pqbar,pn,pb); - else // pqbar extremal - return ME_Exqqx_qbarqg(pgin,pq,pqbar,pn,pb); - } - else { // b leg quark line - if (swap_q_qx) //extremal pq - return ME_Exqqx_qqbarQ(pgin,pq,pqbar,pn,pb); - else - return ME_Exqqx_qbarqQ(pgin,pq,pqbar,pn,pb); + // pqbar extremal + return ME_Exqqx_qbarqg(pgin,pq,pqbar,pn,pb); } - throw std::logic_error("unreachable"); + // 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); } /* \brief Matrix element squared for central qqx 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 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 */ double ME_qqxmid_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(); 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); } /** 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); - } else if (aptype==pid::gluon && bptype!=pid::gluon) { + } + if (aptype==pid::gluon && bptype!=pid::gluon) { if (is_quark(bptype)) return ME_qg(pn,pb,p1,pa); - else - return ME_qbarg(pn,pb,p1,pa); + return ME_qbarg(pn,pb,p1,pa); } - else if (bptype==pid::gluon && aptype!=pid::gluon) { + if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_qg(p1,pa,pn,pb); - else - return ME_qbarg(p1,pa,pn,pb); + return ME_qbarg(p1,pa,pn,pb); } - else { // they are both quark - if (is_quark(bptype)) { - if (is_quark(aptype)) - return ME_qQ(pn,pb,p1,pa); - else - return ME_qQbar(pn,pb,p1,pa); - } - else { - if (is_quark(aptype)) - return ME_qQbar(p1,pa,pn,pb); - else - return ME_qbarQbar(pn,pb,p1,pa); - } + // 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); } - throw std::logic_error("unreachable"); + 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); - else - return ME_W_qbarg(pn,plbar,pl,pb,p1,pa,Wprop); + return ME_W_qbarg(pn,plbar,pl,pb,p1,pa,Wprop); } - else if (bptype==pid::gluon && aptype!=pid::gluon) { + if (bptype==pid::gluon && aptype!=pid::gluon) { if (is_quark(aptype)) return ME_W_qg(p1,plbar,pl,pa,pn,pb,Wprop); - else - return ME_W_qbarg(p1,plbar,pl,pa,pn,pb,Wprop); - } - else { // they are both quark - if (wc==true){ // 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); - else - return ME_W_qQbar(pn,plbar,pl,pb,p1,pa,Wprop); - } - else { - if (is_quark(aptype)) - return ME_W_qbarQ(pn,plbar,pl,pb,p1,pa,Wprop); - else - return ME_W_qbarQbar(pn,plbar,pl,pb,p1,pa,Wprop); - } - } - else{ // 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); - else - return ME_W_qQbar(p1,plbar,pl,pa,pn,pb,Wprop); - } - else { // a is anti-quark - if (is_quark(bptype)) - return ME_W_qbarQ(p1,plbar,pl,pa,pn,pb,Wprop); - else - return ME_W_qbarQbar(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); } - throw std::logic_error("unreachable"); + // 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); - else - return ME_Wuno_qbarg(p1,pa,pn,pb,pg,plbar,pl,Wprop); - } - else { // 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); - else - return ME_W_unob_qQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); - } - else{ - if (is_quark(aptype)) - return ME_W_unob_qbarQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); - else - return ME_W_unob_qbarQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); - } - } - else {// 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); - else //qqbar - return ME_Wuno_qQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); - } - else { // a is anti-quark - if (is_quark(bptype)) //qbarq - return ME_Wuno_qbarQ(p1,pa,pn,pb,pg,plbar,pl,Wprop); - else //qbarqbar - return ME_Wuno_qbarQbar(p1,pa,pn,pb,pg,plbar,pl,Wprop); - } - } + return ME_Wuno_qbarg(p1,pa,pn,pb,pg,plbar,pl,Wprop); } - throw std::logic_error("unreachable"); + // 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 * 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 wc Boolean. True->W Emitted from b. Else; emitted from leg a * @returns ME Squared for qqxb Tree-Level Current-Current Scattering * * @note calculate forwards qqx contribution by reversing argument ordering. */ double ME_W_qqx_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, ParticleProperties const & Wprop ){ using namespace currents; // CAM factors for the qqx amps, and qqbar ordering (default, qbar extremal) - const double CFbackward = K_g( (swap_q_qx)?pq:pqbar ,pa)/HEJ::C_F; + const double CFbackward = K_g( (swap_q_qx)?pq:pqbar ,pa)/C_F; // 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 // Site. if (swap_q_qx) return ME_WExqqx_qqbarg(pa, pqbar, plbar, pl, pq, pn, pb, Wprop) - * CFbackward; - else - return ME_WExqqx_qbarqg(pa, pq, plbar, pl, pqbar, pn, pb, Wprop) - * CFbackward; + * CFbackward; + return ME_WExqqx_qbarqg(pa, pq, plbar, pl, pqbar, pn, pb, Wprop) + * CFbackward; } - else { - 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) - * CFbackward; - else - return ME_WExqqx_qbarqQ(pa, pq, plbar, pl, pqbar, pn, pb, Wprop) - * CFbackward; - } - else { // W Must be emitted from forwards leg. - if (swap_q_qx) - return ME_W_Exqqx_QQq(pb, pa, pn, pqbar, pq, plbar, pl, is_antiquark(bptype), Wprop) - * CFbackward; - else - return ME_W_Exqqx_QQq(pb, pa, pn, pq, pqbar, plbar, pl, is_antiquark(bptype), Wprop) - * CFbackward; - } + 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) + * CFbackward; + return ME_WExqqx_qbarqQ(pa, pq, plbar, pl, pqbar, pn, pb, Wprop) + * CFbackward; } + // W Must be emitted from forwards leg. + if (swap_q_qx) + return ME_W_Exqqx_QQq(pb, pa, pn, pqbar, pq, plbar, pl, is_antiquark(bptype), Wprop) + * CFbackward; + return ME_W_Exqqx_QQq(pb, pa, pn, pq, pqbar, plbar, pl, is_antiquark(bptype), Wprop) + * CFbackward; + + throw std::logic_error("unreachable"); } /* \brief Matrix element squared for central qqx 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 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 wc Boolean. wc=true signifies w boson emitted from leg b; if wqq=false. * @returns ME Squared for qqxmid Tree-Level Current-Current Scattering */ double ME_W_qqxmid_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(); double wt=1.; - if (aptype==pid::gluon) wt*=K_g(partons.front(),pa)/HEJ::C_F; - if (bptype==pid::gluon) wt*=K_g(partons.back(),pb)/HEJ::C_F; + 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, is_antiquark(bptype),is_antiquark(aptype), swap_q_qx, nabove, Wprop); return wt*ME_W_Cenqqx_qq(pa, pb, pl, plbar, partons, is_antiquark(bptype), is_antiquark(aptype), swap_q_qx, 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 - const double current_factor=ME_Z_qg(pa,pb,p1,pn,plbar,pl,aptype,bptype,Zprop,stw2,ctw); - return { current_factor }; - }else if(is_gluon(aptype) && is_anyquark(bptype)){ - // This is a gq event - const double current_factor=ME_Z_qg(pb,pa,pn,p1,plbar,pl,bptype,aptype,Zprop,stw2,ctw); - return { current_factor }; - }else if(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); - }else{ - // It cannot be gg incoming - throw std::logic_error("Bad particle types"); + // 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 - const double current_factor=ME_Zuno_qg(pa,pb,pg,p1,pn,plbar,pl,aptype,bptype,Zprop,stw2,ctw); - return { current_factor }; - } else if (is_gluon(aptype) && is_anyquark(bptype)) { + 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 - const double current_factor=ME_Zuno_qg(pb,pa,pg,pn,p1,plbar,pl,bptype,aptype,Zprop,stw2,ctw); - return { current_factor }; - } else if (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); + return { ME_Zuno_qg(pb,pa,pg,pn,p1,plbar,pl,bptype,aptype,Zprop,stw2,ctw) }; } - throw std::logic_error("unreachable"); + 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); - else if (aptype==pid::gluon&&bptype!=pid::gluon) { + 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.; - else - return ME_H_qbarg(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.; } - else if (bptype==pid::gluon && aptype!=pid::gluon) { + 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.; - else - return ME_H_qbarg(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.; } - else { // 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.); - else - return ME_H_qQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); - } - else { - if (is_quark(aptype)) - return ME_H_qbarQ(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*9.); - else - return ME_H_qbarQbar(pn,pb,p1,pa,-qHp1,-qH,mt,include_bottom,mb,vev)*4.*4./(9.*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.); } - throw std::logic_error("unreachable"); + 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); - else - return ME_H_unob_gQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); - } - else { // 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); - else - return ME_H_unob_qbarQ(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); - } - else { - if (is_quark(bptype)) - return ME_H_unob_qQbar(pg,p1,pa,pn,pb,-qH,-qHp1,mt,include_bottom,mb,vev); - else - return ME_H_unob_qbarQbar(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); } - throw std::logic_error("unreachable"); + // 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); } - CLHEP::HepLorentzVector to_HepLorentzVector(HEJ::Particle const & particle){ + CLHEP::HepLorentzVector to_HepLorentzVector(Particle const & particle){ return {particle.p.px(), particle.p.py(), particle.p.pz(), particle.p.E()}; } - void validate(HEJ::MatrixElementConfig const & config) { + 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 anonymous +} // 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.}; std::vector bosons = filter_AWZH_bosons(ev.outgoing()); if(bosons.empty()) { return {tree_kin_jets(ev)}; } else if(bosons.size() == 1) { switch(bosons[0].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"); } } else if(bosons.size() == 2) { if(bosons[0].type == pid::Wp && bosons[1].type == pid::Wp){ return {tree_kin_WW(ev)}; } throw not_implemented("Emission of bosons of unsupported type"); } else { throw not_implemented("Emission of >2 bosons is unsupported"); } } - namespace{ - constexpr int extremal_jet_idx = 1; - constexpr int no_extremal_jet_idx = 0; + 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; + 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 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 - ){ + 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}; } - } // namespace anonymous - std::vector MatrixElement::tag_extremal_jet_partons( - Event const & ev - ) const{ - 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); + 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){ + assert(jets.size() >= 3); + ++most_backward; + } + else if(ev.type() == event_type::unof || ev.type() == event_type::qqxexf){ + 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; } - const auto & 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){ - assert(jets.size() >= 3); - ++most_backward; - } - else if(ev.type() == event_type::unof || ev.type() == event_type::qqxexf){ - 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(HEJ::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; - } - namespace { double tree_kin_jets_qqxmid( ParticleID aptype, ParticleID bptype, - CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pb, + CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, std::vector const & partons, double lambda ){ - CLHEP::HepLorentzVector pq,pqbar; + 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; 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); } 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( 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, 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); 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 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 )/(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 + template double tree_kin_jets_uno(InIter BeginIn, InIter EndIn, partIter BeginPart, - partIter EndPart, double lambda){ + 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 )/(4.*(N_C*N_C - 1.)); 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()==HEJ::event_type::FKL){ + 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 ); } - else if (ev.type()==HEJ::event_type::unordered_backward){ + if (ev.type()==event_type::unordered_backward){ return tree_kin_jets_uno(incoming.begin(), incoming.end(), - partons.begin(), partons.end(), - param_.regulator_lambda); + partons.begin(), partons.end(), + param_.regulator_lambda); } - else if (ev.type()==HEJ::event_type::unordered_forward){ + if (ev.type()==event_type::unordered_forward){ return tree_kin_jets_uno(incoming.rbegin(), incoming.rend(), partons.rbegin(), partons.rend(), param_.regulator_lambda); } - else if (ev.type()==HEJ::event_type::extremal_qqxb){ + if (ev.type()==event_type::extremal_qqxb){ return tree_kin_jets_qqx(incoming.begin(), incoming.end(), - partons.begin(), partons.end(), - param_.regulator_lambda); + partons.begin(), partons.end(), + param_.regulator_lambda); } - else if (ev.type()==HEJ::event_type::extremal_qqxf){ + if (ev.type()==event_type::extremal_qqxf){ return tree_kin_jets_qqx(incoming.rbegin(), incoming.rend(), partons.rbegin(), partons.rend(), param_.regulator_lambda); } - else if (ev.type()==HEJ::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); - } - else { - throw std::logic_error("Cannot reweight non-resummable processes in Pure Jets"); - } + 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); + } + throw std::logic_error("Cannot reweight non-resummable processes in Pure Jets"); } - namespace{ + namespace { double tree_kin_W_FKL( ParticleID aptype, ParticleID bptype, - CLHEP::HepLorentzVector pa, CLHEP::HepLorentzVector pb, + CLHEP::HepLorentzVector const & pa, CLHEP::HepLorentzVector const & pb, std::vector const & partons, - CLHEP::HepLorentzVector plbar, CLHEP::HepLorentzVector pl, + 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*C_A*C_A/(N_C*N_C-1.)*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 ){ const bool swap_q_qx=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 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( (BeginIn)->type, (BeginIn+1)->type, pa, pb, pq, pqbar, pn, plbar, pl, swap_q_qx, wc, Wprop ); const double ladder_factor = FKL_ladder_weight( BeginPart+2, EndPart-1, q0, pa, pb, p1, pn, lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } double tree_kin_W_qqxmid( ParticleID aptype, ParticleID bptype, - CLHEP::HepLorentzVector pa, - CLHEP::HepLorentzVector pb, + CLHEP::HepLorentzVector const & pa, + CLHEP::HepLorentzVector const & pb, std::vector const & partons, - CLHEP::HepLorentzVector plbar, CLHEP::HepLorentzVector pl, + CLHEP::HepLorentzVector const & plbar, CLHEP::HepLorentzVector const & pl, double lambda, ParticleProperties const & Wprop ){ - CLHEP::HepLorentzVector pq,pqbar; + 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; bool wqq = backmidquark->type != -(backmidquark+1)->type; // qqx emit W bool wc = !wqq && (aptype==partons.front().type); //leg b emits w assert(!wqq || !wc); if(wqq){ // emission from qqx qqxt -= pl + plbar; } else if(!wc) { // emission from leg a q0 -= pl + plbar; qqxt -= 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); } 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( 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, lambda ); return current_factor*C_A*C_A/(N_C*N_C-1.)*ladder_factor; } - } // namespace anonymous + } // 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, pl; + 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()); } double MatrixElement::tree_kin_WW(Event const & ev) const { using namespace event_type; if(ev.type() == FKL) { return 1.0; } return 0.; } 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)){ + } 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{ + } 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, pl; + 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; + 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 = HEJ::C_A*HEJ::C_A/2*tree_kin_Higgs_uno( + 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 = HEJ::C_A*HEJ::C_A/2*tree_kin_Higgs_uno( + 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) { std::vector bosons = filter_AWZH_bosons(ev.outgoing()); if(bosons.empty()) { return 1.; } else if(bosons.size() == 1) { switch(bosons[0].type){ case pid::Higgs: return alpha_s*alpha_s; case pid::Wp: case pid::Wm: return alpha_w*alpha_w; 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"); } } else if(bosons.size() == 2) { if(bosons[0].type == pid::Wp && bosons[1].type == pid::Wp){ return alpha_w*alpha_w*alpha_w*alpha_w; } throw not_implemented("Emission of bosons of unsupported type"); } else { throw not_implemented("Emission of >2 bosons is unsupported"); } } - } + } // 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()); + 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/PDF.cc b/src/PDF.cc index 29942ec..f30a540 100644 --- a/src/PDF.cc +++ b/src/PDF.cc @@ -1,114 +1,74 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/PDF.hh" #include #include #include #include #include -namespace HEJ{ +#include "LHAPDF/LHAPDF.h" - namespace{ +namespace HEJ { + + namespace { int to_beam(ParticleID id){ if(std::abs(id) == pid::proton){ return (id > 0)?1:-1; } throw std::invalid_argument( "unknown beam type: " + std::to_string(id) ); } - } - -#if defined LHAPDF_MAJOR_VERSION && LHAPDF_MAJOR_VERSION == 6 + } // namespace PDF::PDF(int id, ParticleID beam1, ParticleID beam2): - pdf{LHAPDF::mkPDF(id)}, - beamtype{{to_beam(beam1), to_beam(beam2)}} + pdf_{LHAPDF::mkPDF(id)}, + beamtype_{{to_beam(beam1), to_beam(beam2)}} {} double PDF::pdfpt(size_t beam_idx, double x, double q, ParticleID id) const{ if(!(inRangeQ(q) && inRangeX(x))) return 0.; if(id == pid::gluon){ - return pdf->xfxQ(21,x,q); + return pdf_->xfxQ(pid::gluon,x,q); } - else if(std::abs(id) < 7){ - return pdf->xfxQ(id*beamtype[beam_idx],x,q); - } - else { - std::cerr << "particle type unknown: "<< id << std::endl; - return 0.0; + if(std::abs(id) <= pid::top){ + return pdf_->xfxQ(id*beamtype_[beam_idx],x,q); } + std::cerr << "particle type unknown: "<< id << std::endl; + return 0.0; + } double PDF::Halphas(double q) const{ - double as = pdf->alphasQ(q); + double as = pdf_->alphasQ(q); if (std::isnan(as) || as > 0.5) { as = 0.5; } return as; } int PDF::id() const{ - return pdf->lhapdfID(); + return pdf_->lhapdfID(); } bool PDF::inRangeQ(double q) const{ - return pdf->inRangeQ(q); + return pdf_->inRangeQ(q); } bool PDF::inRangeX(double x) const{ - return pdf->inRangeX(x); - } - -#else /* LHAPDF version unknown or older than 6 */ - - PDF::PDF(std::string LHAPDFName, int LHAPDFsubset, ParticleID beam1, ParticleID beam2): - beamtype{{to_beam(beam1_type), to_beam(beam2_type)}} -{ - LHAPDF::initPDFSet(LHAPDFName, LHAPDF::LHGRID, LHAPDFsubset); -} - - double PDF::pdfpt(size_t beam_idx, double x, double q, ParticleID id) const{ - - if(!(inRangeQ(q) && inRangeX(x))) return 0.; - - if (id == pid::gluon){ - return LHAPDF::xfx(x,q,0); - } - else if (std::abs(id) < 7){ - return LHAPDF::xfx(x,q,id*beamtype[beam_idx]); - } - else { - std::cerr << "particle type unknown: "<< id < 0.5) as = 0.5; - return as; + return pdf_->inRangeX(x); } - bool PDF::inRangeQ(double q) const{ - // here we assume that all members actually have the same range! - static constexpr int member = 0; - return (LHAPDF::getQ2min(member) < q*q) && (q*q < LHAPDF::getQ2max(member)); - } + // can't be in header since we forward declare LHAPDF::PDF + PDF::~PDF() = default; - bool PDF::inRangeX(double x) const{ - // here we assume that all members actually have the same range! - static constexpr int member = 0; - return (LHAPDF::getXmin(member) < x) && (x < LHAPDF::getXmax(member)); - } -#endif -} +} // namespace HEJ diff --git a/src/PDG_codes.cc b/src/PDG_codes.cc index ef9e5e4..59a58d9 100644 --- a/src/PDG_codes.cc +++ b/src/PDG_codes.cc @@ -1,112 +1,113 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/PDG_codes.hh" #include #include "HEJ/exceptions.hh" namespace HEJ { ParticleID to_ParticleID(std::string const & name){ using namespace HEJ::pid; static const std::map known = { {"d", d}, {"down", down}, {"1",static_cast(1)}, {"u", u}, {"up", up}, {"2",static_cast(2)}, {"s", s}, {"strange", strange}, {"3",static_cast(3)}, {"c", c}, {"charm", charm}, {"4",static_cast(4)}, {"b", b}, {"bottom", bottom}, {"5",static_cast(5)}, {"t", t}, {"top", top}, {"6",static_cast(6)}, {"e", e}, {"electron", electron}, {"e-", e}, {"11",static_cast(11)}, {"nu_e", nu_e}, {"electron_neutrino", electron_neutrino}, {"electron-neutrino", electron_neutrino}, {"12",static_cast(12)}, {"mu", mu}, {"muon", muon}, {"mu-", mu}, {"13",static_cast(13)}, {"nu_mu", nu_mu}, {"muon_neutrino", muon_neutrino}, {"muon-neutrino", muon_neutrino}, {"14",static_cast(14)}, {"tau", tau}, {"tau-", tau}, {"15",static_cast(15)}, {"nu_tau", nu_tau}, {"tau_neutrino", tau_neutrino}, {"tau-neutrino", tau_neutrino}, {"16",static_cast(16)}, {"d_bar", d_bar}, {"antidown", antidown}, {"-1",static_cast(-1)}, {"u_bar", u_bar}, {"antiup", antiup}, {"-2",static_cast(-2)}, {"s_bar", s_bar}, {"antistrange", antistrange}, {"-3",static_cast(-3)}, {"c_bar", c_bar}, {"anticharm", anticharm}, {"-4",static_cast(-4)}, {"b_bar", b_bar}, {"antibottom", antibottom}, {"-5",static_cast(-5)}, {"t_bar", t_bar}, {"antitop", antitop}, {"-6",static_cast(-6)}, {"e_bar", e_bar}, {"antielectron", antielectron}, {"positron", positron}, {"e+", e_bar}, {"-11",static_cast(-11)}, {"nu_e_bar", nu_e_bar}, {"electron_antineutrino", electron_antineutrino}, {"electron-antineutrino", electron_antineutrino}, {"-12",static_cast(-12)}, {"mu_bar", mu_bar}, {"mu+", mu_bar}, {"antimuon", antimuon}, {"-13",static_cast(-13)}, {"nu_mu_bar", nu_mu_bar}, {"muon_antineutrino", muon_antineutrino}, {"muon-antineutrino", muon_antineutrino}, {"-14",static_cast(-14)}, {"tau_bar", tau_bar}, {"tau+", tau_bar}, {"antitau", antitau}, {"-15",static_cast(-15)}, {"nu_tau_bar", nu_tau_bar}, {"tau_antineutrino", tau_antineutrino}, {"tau-antineutrino", tau_antineutrino}, {"-16",static_cast(-16)}, {"gluon", gluon}, {"g", g}, {"21",static_cast(21)}, {"photon", photon}, {"gamma", gamma}, {"22",static_cast(22)}, {"Z", Z}, {"23",static_cast(23)}, {"Z_photon_mix", Z_photon_mix}, {"Z_gamma_mix", Z_gamma_mix}, {"Z/photon superposition",Z_photon_mix}, {"81",static_cast(81)}, {"Wp", Wp}, {"W+", Wp}, {"24",static_cast(24)}, {"Wm", Wm}, {"W-", Wm}, {"-24",static_cast(-24)}, {"h", h}, {"H", h}, {"Higgs", Higgs}, {"higgs", higgs}, {"25",static_cast(25)}, {"p", p}, {"proton", proton}, {"2212",static_cast(2212)}, {"p_bar", p_bar}, {"antiproton", antiproton}, {"-2212",static_cast(-2212)} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown particle " + name); } return res->second; } namespace pid { std::string name(ParticleID id) { using namespace HEJ::pid; switch (id) { + case unspecified: return "unspecified"; case down: return "down"; case up: return "up"; case strange: return "strange"; case charm: return "charm"; case bottom: return "bottom"; case top: return "top"; case electron: return "electron"; case muon: return "muon"; case tau: return "tau"; case electron_neutrino: return "electron-neutrino"; case muon_neutrino: return "muon-neutrino"; case tau_neutrino: return "tau-neutrino"; case antidown: return "antidown"; case antiup: return "antiup"; case antistrange: return "antistrange"; case anticharm: return "anticharm"; case antibottom: return "antibottom"; case antitop: return "antitop"; case positron: return "positron"; case antimuon: return "antimuon"; case antitau: return "antitau"; case electron_antineutrino: return "electron-antineutrino"; case muon_antineutrino: return "muon-antineutrino"; case tau_antineutrino: return "tau-antineutrino"; case gluon: return "gluon"; case photon: return "photon"; case Z: return "Z"; case Z_photon_mix: return "Z/photon superposition"; case Wp: return "W+"; case Wm: return "W-"; case Higgs: return "Higgs"; case proton: return "proton"; case antiproton: return "antiproton"; } throw std::logic_error{"unreachable"}; } } // namespace pid } // namespace HEJ diff --git a/src/Parameters.cc b/src/Parameters.cc index 0239707..46d14ba 100644 --- a/src/Parameters.cc +++ b/src/Parameters.cc @@ -1,56 +1,56 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/Parameters.hh" #include #include namespace HEJ { namespace { void replace( std::string & str, char to_replace, std::string const & replacement ) { - for( auto pos = str.find(to_replace); pos != str.npos; + for( auto pos = str.find(to_replace); pos != std::string::npos; pos = str.find(to_replace, pos) ){ str.replace(pos, 1, replacement); pos += replacement.size(); } } // remove "special" characters from scale name // so that we can more easily use it as part of a file name std::string sanitise_scalename(std::string scalename) { replace(scalename, '/', "_over_"); replace(scalename, '*', "_times_"); replace(scalename, ' ', "_"); return scalename; } - } + } // namespace std::string to_string(ParameterDescription const & p) { // use ostringstream over std::to_string to remove trailing 0s std::ostringstream stream; stream << "\\mu_r = "; if(p.mur_factor != 1.) stream << p.mur_factor << '*'; stream << p.scale_name << ", " "\\mu_f = "; if(p.muf_factor != 1.) stream << p.muf_factor << '*'; stream << p.scale_name; return stream.str(); } std::string to_simple_string(ParameterDescription const & p) { // use ostringstream over std::to_string to remove trailing 0s std::ostringstream stream; stream << "Scale_" << sanitise_scalename(p.scale_name) << "_MuR" << p.mur_factor << "_MuF" << p.muf_factor; return stream.str(); } -} +} // namespace HEJ diff --git a/src/PhaseSpacePoint.cc b/src/PhaseSpacePoint.cc index 004bba3..81bd814 100644 --- a/src/PhaseSpacePoint.cc +++ b/src/PhaseSpacePoint.cc @@ -1,849 +1,856 @@ /** * \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 #include "fastjet/ClusterSequence.hh" #include "fastjet/JetDefinition.hh" #include "HEJ/Constants.hh" #include "HEJ/Event.hh" -#include "HEJ/event_types.hh" #include "HEJ/JetSplitter.hh" -#include "HEJ/kinematics.hh" #include "HEJ/PDG_codes.hh" -#include "HEJ/resummation_jet.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 HEJ { namespace { - constexpr int max_jet_user_idx = PhaseSpacePoint::ng_max; + 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; + 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; - } - - // user indices for partons with extremal rapidity - constexpr int qqxmid1_uid = -9; - constexpr int qqxmid2_uid = -8; - constexpr int qqxb_uid = -7; - constexpr int qqxf_uid = -6; - constexpr int unob_uid = -5; - constexpr int unof_uid = -4; - constexpr int backward_FKL_uid = -3; - constexpr int forward_FKL_uid = -2; + 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, + unob = -5, + unof = -4, + backward_fkl = -3, + forward_fkl = -2, + }; + } // namespace user_idx + using UID = user_idx::ID; double estimate_ng_mean(std::vector const & Born_jets){ const double delta_y = Born_jets.back().rapidity() - Born_jets.front().rapidity(); assert(delta_y > 0); // Formula derived from fit in arXiv:1805.04446 (see Fig. 2) return 0.975052*delta_y; } - static_assert( - std::numeric_limits::has_quiet_NaN, - "no quiet NaN for double" - ); - constexpr double NaN = std::numeric_limits::quiet_NaN(); - } // namespace anonymous + 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_; - assert(result.incoming.size() == 2); - result.outgoing = std::move(psp).outgoing_; + 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{} + rapidity_less{} ) ); assert(result.outgoing.size() >= 2); - result.decays = std::move(psp).decays_; - result.parameters.central = {NaN, NaN, psp.weight()}; + 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(); } int PhaseSpacePoint::sample_ng( std::vector const & Born_jets, RNG & ran ){ const double ng_mean = estimate_ng_mean(Born_jets); std::poisson_distribution dist(ng_mean); const int ng = dist(ran); assert(ng >= 0); - assert(ng < ng_max); + assert(ng < NG_MAX); weight_ *= std::tgamma(ng + 1)*std::exp(ng_mean)*std::pow(ng_mean, -ng); return ng; } void PhaseSpacePoint::copy_AWZH_boson_from(Event const & event){ auto const & from = event.outgoing(); const auto AWZH_boson = std::find_if( begin(from), end(from), [](Particle const & p){ return is_AWZH_boson(p); } ); if(AWZH_boson == end(from)) return; auto insertion_point = std::lower_bound( begin(outgoing_), end(outgoing_), *AWZH_boson, rapidity_less{} ); outgoing_.insert(insertion_point, *AWZH_boson); // copy decay products const int idx = std::distance(begin(from), AWZH_boson); assert(idx >= 0); const auto decay_it = event.decays().find(idx); if(decay_it != end(event.decays())){ const int new_idx = std::distance(begin(outgoing_), insertion_point); assert(new_idx >= 0); assert(outgoing_[new_idx].type == AWZH_boson->type); decays_.emplace(new_idx, decay_it->second); } assert(std::is_sorted(begin(outgoing_), end(outgoing_), rapidity_less{})); } namespace { auto get_first_anyquark_emission(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 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_first_anyquark_emission(ev); return get_back_quark_jet(ev, firstquark); } template void label_extremal_qqx( 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){ 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() ); return; } if(qqxf_){ // same as qqxb with reversed order label_extremal_qqx( event.outgoing().crbegin(), event.outgoing().crend(), outgoing_.rbegin() ); return; } // central qqx const auto firstquark = get_first_anyquark_emission(event); // 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_.max_ext_soft_pt_fraction; 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 // 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; 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_) { 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_) { 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; + 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); } } 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}, param_{std::move(conf)}, status_{unspecified} { weight_ = 1; - const auto & Born_jets = ev.jets(); + auto const & Born_jets = ev.jets(); const int ng = sample_ng(Born_jets, ran); weight_ /= std::tgamma(ng + 1); const int ng_jets = sample_ng_jets(ng, Born_jets, 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); } const auto qperp = std::accumulate( begin(out_partons), end(out_partons), fastjet::PseudoJet{} ); const auto jets = reshuffle(Born_jets, qperp); if(weight_ == 0.) { status_ = failed_reshuffle; return; } if(! pass_resummation_cuts(jets)){ status_ = failed_resummation_cuts; weight_ = 0.; return; } std::vector jet_partons = split( jets, ng_jets, qqxbackjet, ran ); if(weight_ == 0.) { status_ = StatusCode::failed_split; return; } if(qqxmid_){ rescale_qqx_rapidities( out_partons, jets, most_backward_FKL(jet_partons).rapidity(), most_forward_FKL(jet_partons).rapidity(), qqxbackjet ); } 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 & p: out_partons){ - outgoing_.emplace_back(Particle{pid::gluon, std::move(p), {}}); + 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; } copy_AWZH_boson_from(ev); reconstruct_incoming(ev.incoming()); status_ = StatusCode::good; } std::vector PhaseSpacePoint::gen_non_jet( - int count, double ptmin, double ptmax, RNG & ran + int const ng_non_jet, double const ptmin, double const ptmax, RNG & ran ){ // heuristic parameters for pt sampling - const double ptpar = 1.3 + count/5.; + const double ptpar = 1.3 + ng_non_jet/5.; const double temp1 = std::atan((ptmax - ptmin)/ptpar); - std::vector partons(count); - for(size_t i = 0; i < static_cast(count); ++i){ + 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); + 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( std::vector & out_partons, std::vector const & jets, const double ymin1, const double ymax2, const int qqxbackjet ){ const double ymax1 = jets[qqxbackjet].rapidity(); const double ymin2 = jets[qqxbackjet+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 p){ + [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( std::vector const & Born_jets ) const{ assert(std::is_sorted(begin(Born_jets), end(Born_jets), rapidity_less{})); assert(Born_jets.size() >= 2); const double dy = Born_jets.back().rapidity() - Born_jets.front().rapidity(); const double R = param_.jet_param.def.R(); const int njets = Born_jets.size(); 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( int ng, std::vector const & Born_jets, RNG & ran ){ const double p_J = probability_in_jet(Born_jets); 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::vector PhaseSpacePoint::reshuffle( std::vector const & Born_jets, fastjet::PseudoJet const & q ){ if(q == fastjet::PseudoJet{0, 0, 0, 0}) return Born_jets; - const auto jets = resummation_jet_momenta(Born_jets, q); + auto jets = resummation_jet_momenta(Born_jets, q); if(jets.empty()){ weight_ = 0; return {}; } // additional Jacobian to ensure Born integration over delta gives 1 weight_ *= resummation_jet_weight(Born_jets, q); return jets; } 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){ ++first_valid_jet; --num_valid_jets; } else if((unof_||qqxf_) && 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{ + 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() == backward_FKL_uid; + 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() == forward_FKL_uid; + 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 anonymous + } // namespace #endif std::vector PhaseSpacePoint::split( std::vector const & jets, int ng_jets, size_t qqxbackjet, RNG & ran ){ return split( jets, distribute_jet_partons(ng_jets, jets, ran), qqxbackjet, 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_.max_ext_soft_pt_fraction; } std::vector PhaseSpacePoint::split( std::vector const & jets, std::vector const & np, size_t qqxbackjet, 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_; - const size_t most_forward_FKL_idx = jets.size() - 1 - unof_ - qqxf_; - const auto & jet = param_.jet_param; + const size_t most_backward_FKL_idx = 0 + unob_ + qqxb_; // NOLINT + const size_t most_forward_FKL_idx = jets.size() - 1 - unof_ - qqxf_; // 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. 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(backward_FKL_uid); + extremal->set_user_index(UID::backward_fkl); } else if(((unob_ || qqxb_) && i == 0)){ // unordered/qqxb extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index((unob_)?unob_uid:qqxb_uid); + extremal->set_user_index((unob_)?UID::unob:UID::qqxb); } else if (i == most_forward_FKL_idx){ extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index(forward_FKL_uid); + extremal->set_user_index(UID::forward_fkl); } else if(((unof_ || qqxf_) && i == jets.size() - 1)){ // unordered/qqxf extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index((unof_)?unof_uid:qqxf_uid); + extremal->set_user_index((unof_)?UID::unof:UID::qqxf); } else if((qqxmid_ && i == qqxbackjet)){ extremal = std::max_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index(qqxmid1_uid); + extremal->set_user_index(UID::qqxmid1); } else if((qqxmid_ && i == qqxbackjet+1)){ extremal = std::min_element( first_new_parton, end(jet_partons), rapidity_less{} ); - extremal->set_user_index(qqxmid2_uid); + extremal->set_user_index(UID::qqxmid2); } 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() != unob_uid) return false; - if(unof_ && partons.back().user_index() != unof_uid) return false; - if(qqxb_ && partons.front().user_index() != qqxb_uid) return false; - if(qqxf_ && partons.back().user_index() != qqxf_uid) return false; + 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; return - most_backward_FKL(partons).user_index() == backward_FKL_uid - && most_forward_FKL(partons).user_index() == forward_FKL_uid; + 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_]; } template Particle const & PhaseSpacePoint::most_forward_FKL( std::vector const & partons ) const{ const size_t idx = partons.size() - 1 - unof_ - qqxf_; assert(idx < partons.size()); return partons[idx]; } template Particle & PhaseSpacePoint::most_backward_FKL( std::vector & partons ) const{ return partons[0 + unob_ + qqxb_]; } template Particle & PhaseSpacePoint::most_forward_FKL( std::vector & partons ) const{ const size_t idx = partons.size() - 1 - unof_ - qqxf_; 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_.max_ext_soft_pt_fraction; 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(size_t i = 0; i < jets.size(); ++i){ - assert(jets[i].has_constituents()); - for(auto && parton: jets[i].constituents()){ + for(auto const & jet : jets){ + assert(jet.has_constituents()); + for(auto && parton: jet.constituents()){ if(is_nonjet_parton(parton)) return false; } - in_jet += jets[i].constituents().size(); + 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(unof_ && !contains_idx(jets.back(), partons.back())) return false; if(qqxf_ && !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()); } - double PhaseSpacePoint::phase_space_normalisation( - int num_Born_jets, int num_out_partons - ) const{ - return std::pow(16*std::pow(M_PI,3), num_Born_jets - num_out_partons); - } - 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/Ranlux64.cc b/src/Ranlux64.cc index c208e93..59b12fb 100644 --- a/src/Ranlux64.cc +++ b/src/Ranlux64.cc @@ -1,58 +1,58 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/Ranlux64.hh" #include namespace HEJ { namespace { //! create Ranlux64Engine with state read from the given file CLHEP::Ranlux64Engine make_Ranlux64Engine(std::string const & seed_file) { CLHEP::Ranlux64Engine result; result.restoreStatus(seed_file.c_str()); return result; } CLHEP::Ranlux64Engine make_Ranlux64Engine() { /* * some (all?) of the Ranlux64Engine constructors leave fields * uninitialised, invoking undefined behaviour. This can be * circumvented by restoring the state from a file */ static const std::string state = "9876\n" "0.91280703978419097666\n" "0.41606065829518357191\n" "0.99156342622341142601\n" "0.030922955274050423213\n" "0.16206278421638486975\n" "0.76151768001958330956\n" "0.43765760066092695979\n" "0.42904698253748563275\n" "0.11476317525663759511\n" "0.026620053590963976831\n" "0.65953715764414511114\n" "0.30136722624439826745\n" "3.5527136788005009294e-15 4\n" "1 202\n"; std::FILE* tmpf = std::tmpfile(); std::fputs(state.c_str(),tmpf); auto result = make_Ranlux64Engine("/proc/self/fd/"+std::to_string(fileno(tmpf))); return result; } - } + } // namespace Ranlux64::Ranlux64(): ran_{make_Ranlux64Engine()} {} Ranlux64::Ranlux64(std::string const & seed_file): ran_{make_Ranlux64Engine(seed_file)} {} double Ranlux64::flat() { return ran_.flat(); } -} +} // namespace HEJ diff --git a/src/RivetAnalysis.cc b/src/RivetAnalysis.cc index a9e9d5e..68cf6c6 100644 --- a/src/RivetAnalysis.cc +++ b/src/RivetAnalysis.cc @@ -1,186 +1,191 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/RivetAnalysis.hh" #include "HEJ/ConfigFlags.hh" +#include "HEJ/utility.hh" #ifdef HEJ_BUILD_WITH_RIVET -#include #include +#include +#include #include "yaml-cpp/yaml.h" #include "Rivet/AnalysisHandler.hh" #include "Rivet/Config/RivetConfig.hh" #ifdef RIVET_ENABLE_HEPMC_3 #include "HepMC3/GenEvent.h" #include "HEJ/HepMC3Interface.hh" #else // rivet with HepMC 2 -#include "HepMC/GenEvent.h" #include "HEJ/HepMC2Interface.hh" +#include "HepMC/GenEvent.h" -#endif +#endif // RIVET_ENABLE_HEPMC_3 #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" #endif -namespace HEJ{ +namespace HEJ { std::unique_ptr RivetAnalysis::create( YAML::Node const & config, LHEF::HEPRUP const & heprup ){ return std::make_unique(config, heprup); } } #ifdef HEJ_BUILD_WITH_RIVET namespace HEJ { #ifdef RIVET_ENABLE_HEPMC_3 using HepMCInterface=HepMC3Interface; #else using HepMCInterface=HepMC2Interface; #endif struct RivetAnalysis::rivet_info { rivet_info(std::unique_ptr && h, - std::string && s, HEJ::HepMCInterface && m): + std::string && s, HepMCInterface && m): handler{std::move(h)}, name{std::move(s)}, hepmc{std::move(m)} {} // AnalysisHandler doesn't allow a copy constructor -> use ptr std::unique_ptr handler; std::string name; - HEJ::HepMCInterface hepmc; + HepMCInterface hepmc; }; RivetAnalysis::RivetAnalysis(YAML::Node const & config, - LHEF::HEPRUP const & heprup + LHEF::HEPRUP heprup ): - heprup_{heprup}, + heprup_{std::move(heprup)}, first_event_(true) { // assert that only supported options are provided if(!config.IsMap()) throw invalid_type{"Rivet config has to be a map!"}; for(auto const & entry: config){ const auto name = entry.first.as(); if(name != "output" && name != "rivet") throw unknown_option{"Unknown option \'"+name+"\' provided to rivet."}; } // get output name if(!config["output"]) throw std::invalid_argument{ "No output was provided to rivet. " "Either give an output or deactivate rivet." }; if(!config["output"].IsScalar()) throw invalid_type{ "Output for rivet must be a scalar." }; output_name_ = config["output"].as(); // read in analyses - const auto & name_node = config["rivet"]; + auto const & name_node = config["rivet"]; switch(name_node.Type()){ case YAML::NodeType::Scalar: analyses_names_.push_back(name_node.as()); break; case YAML::NodeType::Sequence: for(YAML::const_iterator it = name_node.begin(); it != name_node.end(); ++it){ analyses_names_.push_back(it->as()); } break; default: throw std::invalid_argument{ "No analysis was provided to rivet. " "Either give an analysis or deactivate rivet." }; } } // it is a bit ugly that we can't do this directly in `initialise` void RivetAnalysis::init(Event const & event){ #ifdef HEJ_USE_RIVET2 rivet_runs_.reserve(event.variations().size()+1); #else - (void) event; // shut up compiler + ignore(event); // shut up compiler #endif rivet_runs_.emplace_back( std::make_unique(), std::string(""), HepMCInterface(heprup_) ); rivet_runs_.back().handler->addAnalyses(analyses_names_); #ifdef HEJ_USE_RIVET2 //! scale variation in rivet 2 through multiple analyses if( !event.variations().empty() ){ for(auto const & vari : event.variations()){ rivet_runs_.emplace_back( std::make_unique(), "."+to_simple_string(*vari.description), HepMCInterface(heprup_) ); rivet_runs_.back().handler->addAnalyses(analyses_names_); } } #endif } - void RivetAnalysis::fill(Event const & event, Event const &){ + void RivetAnalysis::fill(Event const & event, Event const & /*unused*/){ if(first_event_){ first_event_=false; init(event); } auto hepmc_event(rivet_runs_.front().hepmc(event)); rivet_runs_.front().handler->analyze(hepmc_event); #ifdef HEJ_USE_RIVET2 for(std::size_t i = 1; i < rivet_runs_.size(); ++i){ auto & run = rivet_runs_[i]; run.hepmc.set_central(hepmc_event, event, i-1); run.handler->analyze(hepmc_event); } #endif } void RivetAnalysis::finalise(){ for(auto & run: rivet_runs_){ run.handler->finalize(); run.handler->writeData(output_name_+run.name+std::string(".yoda")); } } } // namespace HEJ #else // no rivet => create empty analysis namespace Rivet { class AnalysisHandler {}; } namespace HEJ { struct RivetAnalysis::rivet_info{}; - RivetAnalysis::RivetAnalysis(YAML::Node const &, LHEF::HEPRUP const &){ + RivetAnalysis::RivetAnalysis( + YAML::Node const & /*config*/, LHEF::HEPRUP /*heprup*/ + ){ + ignore(first_event_); throw std::invalid_argument( "Failed to create RivetAnalysis: " "HEJ 2 was built without rivet support" ); } - void RivetAnalysis::init(Event const &){} - void RivetAnalysis::fill(Event const &, Event const &){} + void RivetAnalysis::init(Event const & /*event*/){} + void RivetAnalysis::fill(Event const & /*event*/, Event const & /*unused*/){} void RivetAnalysis::finalise(){} } // namespace HEJ #endif namespace HEJ { RivetAnalysis::~RivetAnalysis() = default; } diff --git a/src/ScaleFunction.cc b/src/ScaleFunction.cc index 04eb410..b70697a 100644 --- a/src/ScaleFunction.cc +++ b/src/ScaleFunction.cc @@ -1,171 +1,171 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/ScaleFunction.hh" #include #include "HEJ/Event.hh" #include "HEJ/exceptions.hh" -namespace HEJ{ +namespace HEJ { double H_T(Event const & ev){ double result = 0.; for(size_t i = 0; i < ev.outgoing().size(); ++i){ auto const decay_products = ev.decays().find(i); if(decay_products == end(ev.decays())){ result += ev.outgoing()[i].perp(); } else{ for(auto const & particle: decay_products->second){ result += particle.perp(); } } } return result; } double max_jet_pt(Event const & ev) { return sorted_by_pt(ev.jets()).front().pt(); } double jet_invariant_mass(Event const & ev) { fastjet::PseudoJet sum; - for(const auto & jet: ev.jets()) sum+=jet; + for(auto const & jet: ev.jets()) sum+=jet; return sum.m(); } double m_j1j2(Event const & ev) { const auto jets = sorted_by_pt(ev.jets()); assert(jets.size() >= 2); return (jets[0] + jets[1]).m(); } ScaleFunction operator*(double factor, ScaleFunction base_scale) { base_scale.name_.insert(0, std::to_string(factor) + '*'); auto new_fun = - [factor,fun{std::move(base_scale.fun_)}](HEJ::Event const & ev) { + [factor,fun{std::move(base_scale.fun_)}](Event const & ev) { return factor*fun(ev); }; base_scale.fun_ = std::move(new_fun); return base_scale; } ScaleFunction operator*(ScaleFunction factor, ScaleFunction base_scale) { base_scale.name_.insert(0, factor.name_ + '*'); auto new_fun = [fun1{std::move(factor.fun_)}, fun2{std::move(base_scale.fun_)}, name{base_scale.name_}] - (HEJ::Event const & ev) { + (Event const & ev) { return fun1(ev)*fun2(ev); }; base_scale.fun_ = std::move(new_fun); return base_scale; } ScaleFunction operator/(ScaleFunction base_scale, double denom) { base_scale.name_.append('/' + std::to_string(denom)); auto new_fun = - [denom,fun{std::move(base_scale.fun_)}](HEJ::Event const & ev) { + [denom,fun{std::move(base_scale.fun_)}](Event const & ev) { return fun(ev)/denom; }; base_scale.fun_ = std::move(new_fun); return base_scale; } ScaleFunction operator/(ScaleFunction base_scale, ScaleFunction denom) { base_scale.name_.append('/' + denom.name_); auto new_fun = [fun2{std::move(denom.fun_)}, fun1{std::move(base_scale.fun_)}] - (HEJ::Event const & ev) { + (Event const & ev) { return fun1(ev)/fun2(ev); }; base_scale.fun_ = std::move(new_fun); return base_scale; } // TODO: significant logic duplication with operator() void ScaleGenerator::gen_descriptions() { if(scales_.empty()) { throw std::logic_error{"Need at least one scale"}; } descriptions_.emplace_back( std::make_shared(scales_.front().name(), 1., 1.) ); for(auto & base_scale: scales_){ - const auto base_name = base_scale.name(); + auto const & base_name = base_scale.name(); descriptions_.emplace_back( std::make_shared(base_name, 1., 1.) ); //multiplicative scale variation for(double mur_factor: scale_factors_){ for(double muf_factor: scale_factors_){ if(muf_factor == 1. && mur_factor == 1.) continue; if( mur_factor/muf_factor < 1/max_scale_ratio_ || mur_factor/muf_factor > max_scale_ratio_ ) continue; descriptions_.emplace_back( std::make_shared( base_name, mur_factor, muf_factor ) ); } } } } Event ScaleGenerator::operator()(Event ev) const { if(! ev.variations().empty()) { throw std::invalid_argument{"Input event already has scale variation"}; } assert(!scales_.empty()); assert(!descriptions_.empty()); size_t descr_idx = 0; const double mu_central = (scales_.front())(ev); ev.central().mur = mu_central; ev.central().muf = mu_central; assert(descr_idx < descriptions_.size()); assert(descriptions_[descr_idx]); ev.central().description = descriptions_[descr_idx++]; // check if we are doing scale variation at all if(scales_.size() == 1 && scale_factors_.empty()) return ev; - for(auto & base_scale: scales_){ + for(auto const & base_scale: scales_){ const double mu_base = base_scale(ev); assert(descr_idx < descriptions_.size()); assert(descriptions_[descr_idx]); ev.variations().emplace_back( EventParameters{ mu_base, mu_base, ev.central().weight, descriptions_[descr_idx++] } ); //multiplicative scale variation for(double mur_factor: scale_factors_){ const double mur = mu_base*mur_factor; for(double muf_factor: scale_factors_){ if(muf_factor == 1. && mur_factor == 1.) continue; const double muf = mu_base*muf_factor; if( mur/muf < 1/max_scale_ratio_ || mur/muf > max_scale_ratio_ ) continue; assert(descr_idx < descriptions_.size()); assert(descriptions_[descr_idx]); ev.variations().emplace_back( EventParameters{ mur, muf, ev.central().weight, descriptions_[descr_idx++] } ); } } }; return ev; } -} +} // namespace HEJ diff --git a/src/Unweighter.cc b/src/Unweighter.cc index d435794..2646586 100644 --- a/src/Unweighter.cc +++ b/src/Unweighter.cc @@ -1,32 +1,32 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/Unweighter.hh" namespace HEJ { bool Unweighter::discard(RNG & ran, Event & ev) const { const double awt = std::abs(ev.central().weight); if(awt >= get_cut() ) return false; if(ran.flat() > awt/get_cut()) return true; ev.parameters() *= get_cut()/awt; return false; } optional Unweighter::unweight(Event ev, RNG & ran) const { if(discard(ran, ev)) return {}; return ev; } std::vector Unweighter::unweight( std::vector events, RNG & ran ) const { events.erase( unweight(events.begin(), events.end(), ran), events.end() ); return events; } -} +} // namespace HEJ diff --git a/src/Wjets.cc b/src/Wjets.cc index 56a8fe1..cacc376 100644 --- a/src/Wjets.cc +++ b/src/Wjets.cc @@ -1,793 +1,799 @@ /** * \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/jets.hh" #include "HEJ/LorentzVector.hh" #include "HEJ/exceptions.hh" +#include "HEJ/jets.hh" // generated headers -#include "HEJ/currents/jW_j.hh" -#include "HEJ/currents/jWuno_j.hh" -#include "HEJ/currents/jWqqbar_j.hh" +#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" -#include "HEJ/currents/jW_jqqbar.hh" -#include "HEJ/currents/jW_juno.hh" namespace HEJ { namespace currents { namespace { // short hands using std::abs; using std::conj; using std::pow; // --- 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 ){ - 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); + 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); + 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 = HEJ::jW_j(p1in,p1out,p2in,p2out,pl,plbar); - const COM ampp = HEJ::jW_j(p1in,p1out,p2in,p2out,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); + 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 ){ - const COM u1 = U1_jW(pa,p1,pb,p2,pg,pl,plbar); - const COM u2 = U2_jW(pa,p1,pb,p2,pg,pl,plbar); - const COM l = L_jW(pa,p1,pb,p2,pg,pl,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); - return WProp(plbar, pl, wprop)*ampsq/((16)*(q2.m2()*q1.m2())); + //! @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(...) template double jWqqx_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 ME2h1pp = jM2_W_extremal_qqbar( + const double amp_h1pp = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); - const double ME2h1pm = jM2_W_extremal_qqbar( + const double amp_h1pm = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); - const double ME2h1mp = jM2_W_extremal_qqbar( + const double amp_h1mp = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); - const double ME2h1mm = jM2_W_extremal_qqbar( + const double amp_h1mm = jM2_W_extremal_qqbar( pg, pq, plbar, pl, pqbar, p3, pb, wprop ); - return ME2h1pp + ME2h1pm + ME2h1mp + ME2h1mm; + return amp_h1pp + amp_h1pm + amp_h1mp + amp_h1mm; } /** * @brief W+Jets Extremal qqx 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 plbar Outgoing anti-lepton momenta * @param pl Outgoing lepton momenta * @param pqbarout Anti-quark of extremal qqx 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. * Handles all possible incoming states. Calculated via crossing symmetry from * jWuno_j. */ double jWqqx_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; //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( 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); } 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 ){ return jWqqx_j(pgin, pqbarout, plbar, pl, pqout, p2out, p2in, true, wprop); } 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 ){ return jWqqx_j(pgin, pqout, plbar, pl, pqbarout, p2out, p2in, false, wprop) *K_g(p2out,p2in)/C_F; } 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 ){ return jWqqx_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 ){ static constexpr COM cm1m1 = 8./3.; static constexpr COM cm2m2 = 8./3.; static constexpr COM cm3m3 = 6.; static constexpr COM 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 real( cm1m1*pow(abs(m1),2) + cm2m2*pow(abs(m2),2) + cm3m3*pow(abs(m3),2) + 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( 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./( + 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( 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 ){ 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 COM cmsms = 3.; static constexpr COM cmumu = 4./3.; static constexpr COM cmcmc = 4./3.; static constexpr COM cmsmu = COM{0., 3./2.}; static constexpr COM cmsmc = COM{0.,-3./2.}; static constexpr COM cmumc = -1./6.; return real( cmsms*pow(abs(sym),2) +cmumu*pow(abs(uncross),2) +cmcmc*pow(abs(cross),2) ) +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( 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 ){ 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, pqbar; + HLV pq; + HLV pqbar; if (qqxmarker){ 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( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double amp_mp = amp_WCenqqx_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double amp_pm = amp_WCenqqx_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double amp_pp = amp_WCenqqx_qq( pa, p1, pb, p4, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double t1 = q1.m2(); const double t3 = (q1-pl-plbar-pq-pqbar).m2(); const double amp = WProp(plbar, pl, wprop)*( amp_mm+amp_mp+amp_pm+amp_pp )/(9.*4.*t1*t1*t3*t3); 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 template double amp_W_Cenqqx_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 ){ 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 COM cmsms = 3.; static constexpr COM cmumu = 4./3.; static constexpr COM cmcmc = 4./3.; static constexpr COM cmsmu = COM{0.,3./2.}; static constexpr COM cmsmc = COM{0.,-3./2.}; static constexpr COM cmumc = -1./6.; return real( cmsms*pow(abs(sym),2) +cmumu*pow(abs(uncrossed),2) +cmcmc*pow(abs(crossed),2) ) +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( 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 ){ 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; std::swap(nabove,nbelow); } HLV q1=pa; for(int i=0;i( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double amp_mp = amp_W_Cenqqx_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double amp_pm = amp_W_Cenqqx_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double amp_pp = amp_W_Cenqqx_qq( pa, p1, pb, pn, pq, pqbar, pl, plbar, qq.first, -qq.second ); const double t1 = q1.m2(); const double t3 = (q1 - pq - pqbar).m2(); const double amp= WProp(plbar, pl, wprop)*( amp_mm+amp_mp+amp_pm+amp_pp )/(9.*4.*t1*t1*t3*t3); return amp; } } // namespace currents } // namespace HEJ diff --git a/src/YAMLreader.cc b/src/YAMLreader.cc index f01914a..0f7614c 100644 --- a/src/YAMLreader.cc +++ b/src/YAMLreader.cc @@ -1,565 +1,564 @@ /** * \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/ConfigFlags.hh" #include "HEJ/output_formats.hh" -#include "HEJ/ScaleFunction.hh" -namespace HEJ{ +namespace HEJ { class Event; - namespace{ + 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", "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"}){ 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 anonymous + } // 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; + 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; + 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; + 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]){ + 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 == filename.npos) return {}; + 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 HEJ { namespace detail{ void set_from_yaml(OutputFile & setting, YAML::Node const & yaml){ setting = yaml.as(); } } - namespace{ + 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; + 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 ) { - auto handle = dlopen(file.c_str(), RTLD_NOW); + 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)); + 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"]) { + 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); } + 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 == scale_fun.npos){ + 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); } - else{ - assert(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}, {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(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 use \"max ext soft pt fraction\" instead.\n"; set_from_yaml( config.max_ext_soft_pt_fraction, yaml, "max ext soft pt fraction" ); // 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"]) { + else if(yaml["unweight"].IsDefined()) { for(auto && opt: {"trials", "max deviation"}) { - if(yaml["unweight"][opt]) { + 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"]){ + 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 anonymous + } // 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/Zjets.cc b/src/Zjets.cc index 37cb5b8..1599815 100644 --- a/src/Zjets.cc +++ b/src/Zjets.cc @@ -1,510 +1,502 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2020 * \copyright GPLv2 or later */ +#include #include #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/jets.hh" // generated headers -#include "HEJ/currents/jZ_j.hh" -#include "HEJ/currents/jZuno_j.hh" -#include "HEJ/currents/jZ_juno.hh" +#include "HEJ/currents/jV_j.hh" +#include "HEJ/currents/jV_juno.hh" +#include "HEJ/currents/jVuno_j.hh" namespace HEJ { namespace currents { - namespace { + // helper for multidimensional std::array, for example + // Array = std::array, N2> + template + struct ArrayTag{ + using type = typename ArrayTag, Ns...>::type; + }; + template + struct ArrayTag { + using type = std::array; + }; + template + using Array = typename ArrayTag::type; + // Z propagator COM ZProp(const double q, ParticleProperties const & zprop){ return 1. / (q - zprop.mass*zprop.mass + COM(0.,1.)*zprop.width*zprop.mass); } // Photon propagator COM GProp(const double q) { return 1. / q; } // Weak charge - double Zq (const ParticleID PID, const Helicity h, const double stw2, const double ctw) { + template + double Zq(ParticleID PID, double stw2, double ctw); + + // Weak charge - Positive Spin + template<> + double Zq( + const ParticleID PID, + const double stw2, + const double ctw + ) { using namespace pid; - // Positive Spin - if (h == helicity::plus) { - // quarks - if (PID == d || PID == s || PID == b) return (+ 1.0 * stw2 / 3.0) / ctw; - if (PID == u || PID == c) return (- 2.0 * stw2 / 3.0) / ctw; - // antiquarks - if (PID == d_bar || PID == s_bar || PID == b_bar) return (+ 0.5 - 1.0 * stw2 / 3.0) / ctw; - if (PID == u_bar || PID == c_bar) return (- 0.5 + 2.0 * stw2 / 3.0) / ctw; - // electron - if (PID == electron) return stw2 / ctw; - } - // Negative Spin - else { - // quarks - if (PID == d || PID == s || PID == b) return (- 0.5 + 1.0 * stw2 / 3.0) / ctw; - if (PID == u || PID == c) return (+ 0.5 - 2.0 * stw2 / 3.0) / ctw; - // antiquarks - if (PID == d_bar || PID == s_bar || PID == b_bar) return (- 1.0 * stw2 / 3.0) / ctw; - if (PID == u_bar || PID == c_bar) return (+ 2.0 * stw2 / 3.0) / ctw; - // electron - if (PID == electron) return (-1.0 / 2.0 + stw2) / ctw; - } + // quarks + if (PID == d || PID == s || PID == b) return (+ 1.0 * stw2 / 3.0) / ctw; + if (PID == u || PID == c) return (- 2.0 * stw2 / 3.0) / ctw; + // antiquarks + if (PID == d_bar || PID == s_bar || PID == b_bar) return (+ 0.5 - 1.0 * stw2 / 3.0) / ctw; + if (PID == u_bar || PID == c_bar) return (- 0.5 + 2.0 * stw2 / 3.0) / ctw; + // electron + if (PID == electron) return stw2 / ctw; + throw std::logic_error("ERROR! No weak charge found"); + } + + // Weak charge - Negative Spin + template<> + double Zq( + const ParticleID PID, + const double stw2, + const double ctw + ) { + using namespace pid; + // quarks + if (PID == d || PID == s || PID == b) return (- 0.5 + 1.0 * stw2 / 3.0) / ctw; + if (PID == u || PID == c) return (+ 0.5 - 2.0 * stw2 / 3.0) / ctw; + // antiquarks + if (PID == d_bar || PID == s_bar || PID == b_bar) return (- 1.0 * stw2 / 3.0) / ctw; + if (PID == u_bar || PID == c_bar) return (+ 2.0 * stw2 / 3.0) / ctw; + // electron + if (PID == electron) return (-1.0 / 2.0 + stw2) / ctw; throw std::logic_error("ERROR! No weak charge found"); } // Electric charge double Gq (const ParticleID PID) { using namespace pid; if (PID == d || PID == s || PID == b) return -1./3.; if (PID == u || PID == c) return +2./3.; if (PID == d_bar || PID == s_bar || PID == b_bar) return +1./3.; if (PID == u_bar || PID == c_bar) return -2./3.; throw std::logic_error("ERROR! No electric charge found"); } //! Prefactor for Z+Jets Contributions /** * @brief Z+Jets Contributions Prefactor * @param aptype Incoming Particle 1 type (Z emission) - * @param PZ Z Propagator - * @param PG Photon Propagator + * @param propZ Z Propagator + * @param propG Photon Propagator * @param stw2 Value of sin(theta_w)^2 * @param ctw Value of cos(theta_w) - * @param res Ouput: pref[h1][hl] - * - * Calculates prefactor for Z+Jets (includes couplings and propagators) + * @returns Prefactors for Z+Jets for all helicity combinations + * (includes couplings and propagators) */ - void Z_amp_pref(const ParticleID aptype, const COM PZ, const COM PG, - const double stw2, const double ctw, COM (&res)[2][2] + Array Z_amp_pref( + const ParticleID aptype, + COM const & propZ, COM const & propG, + const double stw2, const double ctw ){ using helicity::plus; using helicity::minus; - const double Zq_a_p = Zq(aptype, plus, stw2, ctw); - const double Zq_a_m = Zq(aptype, minus, stw2, ctw); - const double Ze_p = Zq(pid::electron, plus, stw2, ctw); - const double Ze_m = Zq(pid::electron, minus, stw2, ctw); - const double Gq_a = Gq(aptype); - - res[1][1] = -2.*(Zq_a_p * Ze_p * PZ - Gq_a * PG * stw2); - res[1][0] = -2.*(Zq_a_p * Ze_m * PZ - Gq_a * PG * stw2); - res[0][0] = -2.*(Zq_a_m * Ze_m * PZ - Gq_a * PG * stw2); - res[0][1] = -2.*(Zq_a_m * Ze_p * PZ - Gq_a * PG * stw2); + const double zq_a_p = Zq(aptype, stw2, ctw); + const double zq_a_m = Zq(aptype, stw2, ctw); + const double ze_p = Zq(pid::electron, stw2, ctw); + const double ze_m = Zq(pid::electron, stw2, ctw); + const double gq_a = Gq(aptype); + + Array res; + res[ plus][ plus] = -2.*(zq_a_p * ze_p * propZ - gq_a * propG * stw2); + res[ plus][minus] = -2.*(zq_a_p * ze_m * propZ - gq_a * propG * stw2); + res[minus][minus] = -2.*(zq_a_m * ze_m * propZ - gq_a * propG * stw2); + res[minus][ plus] = -2.*(zq_a_m * ze_p * propZ - gq_a * propG * stw2); + return res; } //! Z+Jets FKL Contribution /** * @brief Z+Jets FKL Contribution * @param pa Incoming Particle 1 (Z emission) * @param pb Incoming Particle 2 * @param p1 Outgoing Particle 1 (Z emission) * @param p2 Outgoing Particle 2 * @param pep Outgoing positron * @param pem Outgoing electron - * @param res Ouput: jZ_j[h1][hl][h2] - * - * Calculates j_Z^\mu j_\mu. + * @returns j_Z^\mu j_\mu for all helicities h1, hl, h2 */ - void jZ_j(const HLV & pa, const HLV & pb, const HLV & p1, const HLV & p2, - const HLV & pep, const HLV & pem, COM (&res)[2][2][2] + Array jZ_j( + const HLV & pa, const HLV & pb, + const HLV & p1, const HLV & p2, + const HLV & pep, const HLV & pem ){ using helicity::plus; using helicity::minus; - res[1][1][1] = HEJ::jZ_j (pa, p1, pb, p2, pem, pep); - res[1][1][0] = HEJ::jZ_j(pa, p1, pb, p2, pem, pep); - res[1][0][1] = HEJ::jZ_j (pa, p1, pb, p2, pem, pep); - res[1][0][0] = HEJ::jZ_j(pa, p1, pb, p2, pem, pep); + Array res; +#define ASSIGN_HEL(RES, J, H1, HL, H2) \ + RES[H1][HL][H2] = J(pa, p1, pb, p2, pem, pep) // NOLINT + + ASSIGN_HEL(res, jV_j, plus, minus, minus); + ASSIGN_HEL(res, jV_j, plus, minus, plus); + ASSIGN_HEL(res, jV_j, plus, plus, minus); + ASSIGN_HEL(res, jV_j, plus, plus, plus); + +#undef ASSIGN_HEL - res[0][0][0] = conj(res[1][1][1]); - res[0][0][1] = conj(res[1][1][0]); - res[0][1][0] = conj(res[1][0][1]); - res[0][1][1] = conj(res[1][0][0]); + for(auto hl: {minus, plus}) { + for(auto h2: {minus, plus}) { + res[minus][hl][h2] = std::conj(res[plus][flip(hl)][flip(h2)]); + } + } + return res; } + // X and Y as used in contractions with unordered currents + struct XY { + COM X; + COM Y; + }; + /** * @brief Z+Jets Unordered Contribution, unordered on Z side + * @tparam h1 Helicity of line 1 (Z emission line) + * @tparam hl Lepton Helicity + * @tparam h2 Helicity of line 2 + * @tparam hg Helicity of unordered gluon * @param pa Incoming Particle 1 (Z and Uno emission) * @param pb Incoming Particle 2 * @param pg Unordered Gluon * @param p1 Outgoing Particle 1 (Z and Uno emission) * @param p2 Outgoing Particle 2 * @param pep Outgoing positron * @param pem Outgoing electron - * @param X Ouput: (U1-L)[h1][hl][h2][hg] - * @param Y Ouput: (U2+L)[h1][hl][h2][hg] + * @returns X: (U1-L), Y: (U2+l) * * Calculates j_Z_{uno}^\mu j_\mu. Ie, unordered with Z emission same side. */ - void jZuno_j(const HLV & pa, const HLV & pb, const HLV & pg, const HLV & p1, const HLV & p2, - const HLV & pep, const HLV & pem, COM (&X)[2][2][2][2], COM (&Y)[2][2][2][2] + template + XY amp_jZuno_j( + const HLV & pa, const HLV & pb, const HLV & pg, + const HLV & p1, const HLV & p2, + const HLV & pep, const HLV & pem + ){ + const COM u1 = U1(p1, p2, pa, pb, pg, pem, pep); + const COM u2 = U2(p1, p2, pa, pb, pg, pem, pep); + const COM l = L (p1, p2, pa, pb, pg, pem, pep); + + return {u1 - l, u2 + l}; + } + + Array jZuno_j( + const HLV & pa, const HLV & pb, const HLV & pg, + const HLV & p1, const HLV & p2, + const HLV & pep, const HLV & pem ){ using helicity::plus; using helicity::minus; - COM U1[2][2][2][2]; - U1[1][1][1][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[1][1][1][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - U1[1][1][0][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[1][1][0][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - - U1[1][0][1][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[1][0][1][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - U1[1][0][0][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[1][0][0][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - - U1[0][1][1][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[0][1][1][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - U1[0][1][0][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[0][1][0][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - - U1[0][0][1][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[0][0][1][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - U1[0][0][0][1] = HEJ::U1 (p1, p2, pa, pb, pg, pem, pep); - U1[0][0][0][0] = HEJ::U1(p1, p2, pa, pb, pg, pem, pep); - - COM U2[2][2][2][2]; - U2[1][1][1][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[1][1][1][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - U2[1][1][0][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[1][1][0][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - - U2[1][0][1][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[1][0][1][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - U2[1][0][0][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[1][0][0][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - - U2[0][1][1][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[0][1][1][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - U2[0][1][0][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[0][1][0][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - - U2[0][0][1][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[0][0][1][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - U2[0][0][0][1] = HEJ::U2 (p1, p2, pa, pb, pg, pem, pep); - U2[0][0][0][0] = HEJ::U2(p1, p2, pa, pb, pg, pem, pep); - - COM L[2][2][2][2]; - L[1][1][1][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[1][1][1][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - L[1][1][0][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[1][1][0][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - - L[1][0][1][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[1][0][1][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - L[1][0][0][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[1][0][0][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - - L[0][1][1][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[0][1][1][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - L[0][1][0][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[0][1][0][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - - L[0][0][1][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[0][0][1][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - L[0][0][0][1] = HEJ::L (p1, p2, pa, pb, pg, pem, pep); - L[0][0][0][0] = HEJ::L(p1, p2, pa, pb, pg, pem, pep); - - for(int h1=0; h1<2; h1++){ - for(int hl=0; hl<2; hl++){ - for(int h2=0; h2<2; h2++){ - for(int hg=0; hg<2; hg++){ - X[h1][hl][h2][hg] = U1[h1][hl][h2][hg] - L[h1][hl][h2][hg]; - Y[h1][hl][h2][hg] = U2[h1][hl][h2][hg] + L[h1][hl][h2][hg]; - } - } - } - } + Array xy; +#define ASSIGN_HEL(XY, J, H1, H2, H3, H4) \ + XY[H1][H2][H3][H4] = J(pa, pb, pg, p1, p2, pep, pem) // NOLINT + + ASSIGN_HEL(xy, amp_jZuno_j, minus, minus, minus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, minus, minus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, minus, plus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, minus, plus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, plus, minus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, plus, minus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, plus, plus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, minus, plus, plus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, minus, minus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, minus, minus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, minus, plus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, minus, plus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, plus, minus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, plus, minus, plus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, plus, plus, minus); + ASSIGN_HEL(xy, amp_jZuno_j, plus, plus, plus, plus); + +#undef ASSIGN_HEL + + return xy; } /** * @brief Z+Jets Unordered Contribution, unordered opposite to Z side + * @tparam h1 Helicity of line 1 (Z emission) + * @tparam hl Lepton Helicity + * @tparam h2 Helicity of line 2 (unordered emission) + * @tparam hg Helicity of unordered gluon * @param pa Incoming Particle 1 (Z emission) * @param pb Incoming Particle 2 (unordered emission) * @param p1 Outgoing Particle 1 (Z emission) * @param p2 Outgoing Particle 2 (unordered emission) * @param pg Unordered Gluon * @param pep Outgoing positron * @param pem Outgoing electron - * @param X Ouput: (U1-L)[h1][hl][h2][hg] - * @param Y Ouput: (U2+L)[h1][hl][h2][hg] + * @returns X: (U1-L), Y: (U2+l) * * Calculates j_Z^\mu j_{uno}_\mu. Ie, unordered with Z emission opposite side. */ - void jZ_juno(const HLV & pa, const HLV & pb, const HLV & p1, const HLV & p2, const HLV & pg, - const HLV & pep, const HLV & pem, COM (&X)[2][2][2][2], COM (&Y)[2][2][2][2] + template + XY amp_jZ_juno( + const HLV & pa, const HLV & pb, + const HLV & p1, const HLV & p2, const HLV & pg, + const HLV & pep, const HLV & pem + ){ + const COM u1 = U1_jV(pa, p1, pb, p2, pg, pem, pep); + const COM u2 = U2_jV(pa, p1, pb, p2, pg, pem, pep); + const COM l = L_jV (pa, p1, pb, p2, pg, pem, pep); + + return {u1 - l, u2 + l}; + } + + Array jZ_juno( + const HLV & pa, const HLV & pb, + const HLV & p1, const HLV & p2, const HLV & pg, + const HLV & pep, const HLV & pem ){ using helicity::plus; using helicity::minus; - COM U1[2][2][2][2]; - U1[1][1][1][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[1][1][1][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - U1[1][1][0][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[1][1][0][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - - U1[1][0][1][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[1][0][1][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - U1[1][0][0][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[1][0][0][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - - U1[0][1][1][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[0][1][1][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - U1[0][1][0][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[0][1][0][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - - U1[0][0][1][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[0][0][1][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - U1[0][0][0][1] = HEJ::U1_jZ (pa, p1, pb, p2, pg, pem, pep); - U1[0][0][0][0] = HEJ::U1_jZ(pa, p1, pb, p2, pg, pem, pep); - - COM U2[2][2][2][2]; - U2[1][1][1][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[1][1][1][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - U2[1][1][0][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[1][1][0][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - - U2[1][0][1][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[1][0][1][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - U2[1][0][0][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[1][0][0][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - - U2[0][1][1][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[0][1][1][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - U2[0][1][0][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[0][1][0][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - - U2[0][0][1][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[0][0][1][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - U2[0][0][0][1] = HEJ::U2_jZ (pa, p1, pb, p2, pg, pem, pep); - U2[0][0][0][0] = HEJ::U2_jZ(pa, p1, pb, p2, pg, pem, pep); - - COM L[2][2][2][2]; - L[1][1][1][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[1][1][1][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - L[1][1][0][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[1][1][0][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - - L[1][0][1][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[1][0][1][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - L[1][0][0][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[1][0][0][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - - L[0][1][1][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[0][1][1][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - L[0][1][0][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[0][1][0][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - - L[0][0][1][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[0][0][1][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - L[0][0][0][1] = HEJ::L_jZ (pa, p1, pb, p2, pg, pem, pep); - L[0][0][0][0] = HEJ::L_jZ(pa, p1, pb, p2, pg, pem, pep); - - for(int h1=0; h1<2; h1++){ - for(int hl=0; hl<2; hl++){ - for(int h2=0; h2<2; h2++){ - for(int hg=0; hg<2; hg++){ - X[h1][hl][h2][hg] = U1[h1][hl][h2][hg] - L[h1][hl][h2][hg]; - Y[h1][hl][h2][hg] = U2[h1][hl][h2][hg] + L[h1][hl][h2][hg]; - } - } - } - } + Array xy; +#define ASSIGN_HEL(XY, J, H1, H2, H3, H4) \ + XY[H1][H2][H3][H4] = J(pa, pb, p1, p2, pg, pep, pem) // NOLINT + + ASSIGN_HEL(xy, amp_jZ_juno, minus, minus, minus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, minus, minus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, minus, plus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, minus, plus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, plus, minus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, plus, minus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, plus, plus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, minus, plus, plus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, minus, minus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, minus, minus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, minus, plus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, minus, plus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, plus, minus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, plus, minus, plus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, plus, plus, minus); + ASSIGN_HEL(xy, amp_jZ_juno, plus, plus, plus, plus); + +#undef ASSIGN_HEL + + return xy; } + } // Anonymous Namespace std::vector ME_Z_qQ(const HLV & pa, const HLV & pb, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, const ParticleID aptype, const ParticleID bptype, ParticleProperties const & zprop, const double stw2, const double ctw ){ + using helicity::minus; + using helicity::plus; + const HLV pZ = pep + pem; - const COM PZ = ZProp(pZ.m2(), zprop); - const COM PG = GProp(pZ.m2()); - - COM pref_top[2][2], pref_bot[2][2]; - Z_amp_pref(aptype, PZ, PG, stw2, ctw, pref_top); - Z_amp_pref(bptype, PZ, PG, stw2, ctw, pref_bot); - - COM Coeff_top[2][2][2], Coeff_bot[2][2][2]; - jZ_j(pa, pb, p1, p2, pep, pem, Coeff_top); - jZ_j(pb, pa, p2, p1, pep, pem, Coeff_bot); - - double sum_top=0., sum_bot=0., sum_mix=0.; - for(int h1=0; h1<2; h1++){ - for(int hl=0; hl<2; hl++){ - for(int h2=0; h2<2; h2++){ - const COM res_top = pref_top[h1][hl] * Coeff_top[h1][hl][h2]; - const COM res_bot = pref_bot[h2][hl] * Coeff_bot[h2][hl][h1]; + const COM propZ = ZProp(pZ.m2(), zprop); + const COM propG = GProp(pZ.m2()); + + Array pref_top = Z_amp_pref(aptype, propZ, propG, stw2, ctw); + Array pref_bot = Z_amp_pref(bptype, propZ, propG, stw2, ctw); + + Array coeff_top = jZ_j(pa, pb, p1, p2, pep, pem); + Array coeff_bot = jZ_j(pb, pa, p2, p1, pep, pem); + + double sum_top=0.; + double sum_bot=0.; + double sum_mix=0.; + for(auto h1: {minus, plus}){ + for(auto hl: {minus, plus}){ + for(auto h2: {minus, plus}){ + const COM res_top = pref_top[h1][hl] * coeff_top[h1][hl][h2]; + const COM res_bot = pref_bot[h2][hl] * coeff_bot[h2][hl][h1]; sum_top += norm(res_top); sum_bot += norm(res_bot); sum_mix += 2.0 * real(res_top * conj(res_bot)); } } } const double t1_top = (pa-p1-pZ).m2(); const double t2_top = (pb-p2 ).m2(); const double t1_bot = (pa-p1 ).m2(); const double t2_bot = (pb-p2-pZ).m2(); sum_top /= t1_top * t2_top; sum_bot /= t1_bot * t2_bot; sum_mix /= sqrt(t1_top * t2_top * t1_bot * t2_bot); // Colour factor: (CF*CA)/2 // Colour and helicity average: 1/(4*Nc^2) const double pref = (C_F*C_A) / (8*N_C*N_C); return {sum_top*pref, sum_bot*pref, sum_mix*pref}; } double ME_Z_qg(const HLV & pa, const HLV & pb, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, const ParticleID aptype, const ParticleID /*bptype*/, ParticleProperties const & zprop, const double stw2, const double ctw ){ + using helicity::minus; + using helicity::plus; + const HLV pZ = pep + pem; - const COM PZ = ZProp(pZ.m2(), zprop); - const COM PG = GProp(pZ.m2()); + const COM propZ = ZProp(pZ.m2(), zprop); + const COM propG = GProp(pZ.m2()); - COM pref[2][2], Coeff[2][2][2]; - Z_amp_pref(aptype, PZ, PG, stw2, ctw, pref); - jZ_j(pa, pb, p1, p2, pep, pem, Coeff); + Array pref = Z_amp_pref(aptype, propZ, propG, stw2, ctw); + Array coeff = jZ_j(pa, pb, p1, p2, pep, pem); double sum = 0.; - for(int h1=0; h1<2; h1++){ - for(int hl=0; hl<2; hl++){ - for(int h2=0; h2<2; h2++){ - sum += norm(pref[h1][hl] * Coeff[h1][hl][h2]); + for(auto h1: {minus, plus}){ + for(auto hl: {minus, plus}){ + for(auto h2: {minus, plus}){ + sum += norm(pref[h1][hl] * coeff[h1][hl][h2]); } } } sum /= (pa-p1-pZ).m2()*(pb-p2).m2(); // Colour factor: (CF*CA)/2 // Colour and helicity average: 1/(4*Nc^2) // Divide by CF because of gluon (K_g -> CA) - sum *= C_A / (8*N_C*N_C); + //! TODO explain magic 8 + sum *= C_A / (8.*N_C*N_C); // Multiply by CAM return sum * K_g(p2, pb); } std::vector ME_Zuno_qQ(const HLV & pa, const HLV & pb, const HLV & pg, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, const ParticleID aptype, const ParticleID bptype, ParticleProperties const & zprop, const double stw2, const double ctw ){ - const HLV pZ = pep + pem; - const COM PZ = ZProp(pZ.m2(), zprop); - const COM PG = GProp(pZ.m2()); - - COM prefact_top[2][2], prefact_bot[2][2]; - Z_amp_pref(aptype, PZ, PG, stw2, ctw, prefact_top); - Z_amp_pref(bptype, PZ, PG, stw2, ctw, prefact_bot); + using helicity::minus; + using helicity::plus; - COM CoeffX_top[2][2][2][2], CoeffY_top[2][2][2][2]; - jZuno_j(pa, pb, pg, p1, p2, pep, pem, CoeffX_top, CoeffY_top); - - COM CoeffX_bot[2][2][2][2], CoeffY_bot[2][2][2][2]; - jZ_juno(pb, pa, p2, p1, pg, pep, pem, CoeffX_bot, CoeffY_bot); - - double sum_top=0., sum_bot=0., sum_mix=0.; - for(int h1=0; h1<2; h1++){ - for(int hl=0; hl<2; hl++){ - for(int h2=0; h2<2; h2++){ - for(int hg=0; hg<2; hg++){ + const HLV pZ = pep + pem; + const COM propZ = ZProp(pZ.m2(), zprop); + const COM propG = GProp(pZ.m2()); + + Array prefact_top = Z_amp_pref(aptype, propZ, propG, stw2, ctw); + Array prefact_bot = Z_amp_pref(bptype, propZ, propG, stw2, ctw); + + const Array coeff_top = jZuno_j(pa, pb, pg, p1, p2, pep, pem); + const Array coeff_bot = jZ_juno(pb, pa, p2, p1, pg, pep, pem); + + double sum_top=0.; + double sum_bot=0.; + double sum_mix=0.; + for(auto h1: {minus, plus}){ + for(auto hl: {minus, plus}){ + for(auto h2: {minus, plus}){ + for(auto hg: {minus, plus}){ const COM pref_top = prefact_top[h1][hl]; - const COM X_top = CoeffX_top[h1][hl][h2][hg]; - const COM Y_top = CoeffY_top[h1][hl][h2][hg]; + const COM x_top = coeff_top[h1][hl][h2][hg].X; + const COM y_top = coeff_top[h1][hl][h2][hg].Y; const COM pref_bot = prefact_bot[h2][hl]; - const COM X_bot = CoeffX_bot[h2][hl][h1][hg]; - const COM Y_bot = CoeffY_bot[h2][hl][h1][hg]; - - sum_top += norm(pref_top) * (C_A*C_F*C_F/2.*(norm(X_top)+norm(Y_top)) - - C_F/2.*(X_top*conj(Y_top)).real()); - sum_bot += norm(pref_bot) * (C_A*C_F*C_F/2.*(norm(X_bot)+norm(Y_bot)) - - C_F/2.*(X_bot*conj(Y_bot)).real()); - - const COM XX = C_A*C_F*C_F/2. * pref_top * X_top * conj(pref_bot * X_bot); - const COM YY = C_A*C_F*C_F/2. * pref_top * Y_top * conj(pref_bot * Y_bot); - const COM XY = -C_F/2. * (pref_top * X_top * conj(pref_bot * Y_bot) - + pref_top * Y_top * conj(pref_bot * X_bot)); - sum_mix += 2.0 * real(XX + YY + XY); + const COM x_bot = coeff_bot[h2][hl][h1][hg].X; + const COM y_bot = coeff_bot[h2][hl][h1][hg].Y; + + sum_top += norm(pref_top) * (C_A*C_F*C_F/2.*(norm(x_top)+norm(y_top)) + - C_F/2.*(x_top*conj(y_top)).real()); + sum_bot += norm(pref_bot) * (C_A*C_F*C_F/2.*(norm(x_bot)+norm(y_bot)) + - C_F/2.*(x_bot*conj(y_bot)).real()); + + const COM xx = C_A*C_F*C_F/2. * pref_top * x_top * conj(pref_bot * x_bot); + const COM yy = C_A*C_F*C_F/2. * pref_top * y_top * conj(pref_bot * y_bot); + const COM xy = -C_F/2. * (pref_top * x_top * conj(pref_bot * y_bot) + + pref_top * y_top * conj(pref_bot * x_bot)); + sum_mix += 2.0 * real(xx + yy + xy); } } } } const double t1_top = (pa-pg-p1-pZ).m2(); const double t2_top = (pb-p2 ).m2(); const double t1_bot = (pa-pg-p1).m2(); const double t2_bot = (pb-p2-pZ).m2(); sum_top /= t1_top * t2_top; sum_bot /= t1_bot * t2_bot; sum_mix /= sqrt(t1_top * t2_top * t1_bot * t2_bot); //Helicity sum and average over initial states const double pref = 1./(4.*C_A*C_A); return {sum_top*pref, sum_bot*pref, sum_mix*pref}; } double ME_Zuno_qg(const HLV & pa, const HLV & pb, const HLV & pg, const HLV & p1, const HLV & p2, const HLV & pep, const HLV & pem, const ParticleID aptype, const ParticleID /*bptype*/, ParticleProperties const & zprop, const double stw2, const double ctw ){ + using helicity::minus; + using helicity::plus; + const HLV pZ = pep + pem; - const COM PZ = ZProp(pZ.m2(), zprop); - const COM PG = GProp(pZ.m2()); + const COM propZ = ZProp(pZ.m2(), zprop); + const COM propG = GProp(pZ.m2()); - COM pref[2][2], CoeffX[2][2][2][2], CoeffY[2][2][2][2]; - Z_amp_pref(aptype, PZ, PG, stw2, ctw, pref); - jZuno_j(pa, pb, pg, p1, p2, pep, pem, CoeffX, CoeffY); + Array pref = Z_amp_pref(aptype, propZ, propG, stw2, ctw); + const auto coeff = jZuno_j(pa, pb, pg, p1, p2, pep, pem); double sum = 0.; - for(int h1=0; h1<2; h1++){ - for(int hl=0; hl<2; hl++){ - for(int h2=0; h2<2; h2++){ - for(int hg=0; hg<2; hg++){ - const COM X = CoeffX[h1][hl][h2][hg]; - const COM Y = CoeffY[h1][hl][h2][hg]; + for(auto h1: {minus, plus}){ + for(auto hl: {minus, plus}){ + for(auto h2: {minus, plus}){ + for(auto hg: {minus, plus}){ + const COM X = coeff[h1][hl][h2][hg].X; + const COM Y = coeff[h1][hl][h2][hg].Y; sum += norm(pref[h1][hl]) * (C_A*C_F*C_F/2.*(norm(X)+norm(Y)) - C_F/2.*(X*conj(Y)).real()); } } } } sum /= (pa-pg-p1-pZ).m2()*(p2-pb).m2(); //Helicity sum and average over initial states sum /= (4.*C_A*C_A); // Multiply by CAM return sum * (K_g(p2, pb) / C_F); } } // namespace currents } // namespace HEJ diff --git a/src/bin/HEJ.cc b/src/bin/HEJ.cc index 67c5c7e..8d2553e 100644 --- a/src/bin/HEJ.cc +++ b/src/bin/HEJ.cc @@ -1,387 +1,388 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include #include #include #include #include #include #include "yaml-cpp/yaml.h" #include "fastjet/ClusterSequence.hh" #include "HEJ/CombinedEventWriter.hh" #include "HEJ/Config.hh" #include "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/EventReader.hh" #include "HEJ/BufferedEventReader.hh" #include "HEJ/EventReweighter.hh" #include "HEJ/get_analysis.hh" #include "HEJ/make_RNG.hh" #include "HEJ/optional.hh" #include "HEJ/ProgressBar.hh" #include "HEJ/stream.hh" #include "HEJ/Unweighter.hh" #include "HEJ/Version.hh" #include "HEJ/YAMLreader.hh" HEJ::Config load_config(char const * filename){ try{ return HEJ::load_config(filename); } catch(std::exception const & exc){ std::cerr << "Error: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } std::vector> get_analyses( std::vector const & parameters, LHEF::HEPRUP const & heprup ){ try{ return HEJ::get_analyses(parameters, heprup); } catch(std::exception const & exc){ std::cerr << "Failed to load analysis: " << exc.what() << '\n'; std::exit(EXIT_FAILURE); } } // unique_ptr is a workaround: // HEJ::optional is a better fit, but gives spurious errors with g++ 7.3.0 std::unique_ptr> make_progress_bar( std::vector const & xs ) { if(xs.empty()) return {}; const double Born_xs = std::accumulate(begin(xs), end(xs), 0.); return std::make_unique>(std::cout, Born_xs); } std::string time_to_string(const time_t time){ char s[30]; struct tm * p = localtime(&time); strftime(s, 30, "%a %b %d %Y %H:%M:%S", p); return s; } HEJ::Event to_event( LHEF::HEPEUP const & hepeup, HEJ::JetParameters const & fixed_order_jets, HEJ::EWConstants const & ew_parameters ) { HEJ::Event::EventData event_data{hepeup}; event_data.reconstruct_intermediate(ew_parameters); return HEJ::Event{ std::move(event_data).cluster( fixed_order_jets.def, fixed_order_jets.min_pt ) }; } void unweight( HEJ::Unweighter & unweighter, HEJ::WeightType weight_type, std::vector & events, HEJ::RNG & ran ) { if(weight_type == HEJ::WeightType::unweighted_resum){ unweighter.set_cut_to_maxwt(events); } events.erase( unweighter.unweight(begin(events), end(events), ran), end(events) ); } // peek up to nevents events from reader std::vector peek_events( HEJ::BufferedEventReader & reader, const int nevents ) { std::vector events; while( static_cast(events.size()) < nevents && reader.read_event() ) { events.emplace_back(reader.hepeup()); } // put everything back into the reader for(auto it = rbegin(events); it != rend(events); ++it) { reader.emplace(*it); } return events; } void append_resummed_events( std::vector & resummation_events, HEJ::EventReweighter & reweighter, LHEF::HEPEUP const & hepeup, const size_t trials, HEJ::JetParameters const & fixed_order_jets, HEJ::EWConstants const & ew_parameters ) { const HEJ::Event FO_event = to_event(hepeup, fixed_order_jets, ew_parameters); if(reweighter.treatment(FO_event.type()) != HEJ::EventTreatment::reweight) { return; } const auto resummed = reweighter.reweight(FO_event, trials); resummation_events.insert( end(resummation_events), begin(resummed), end(resummed) ); } void train( HEJ::Unweighter & unweighter, HEJ::BufferedEventReader & reader, HEJ::EventReweighter & reweighter, const size_t total_trials, const double max_dev, double reweight_factor, HEJ::JetParameters const & fixed_order_jets, HEJ::EWConstants const & ew_parameters ) { std::cout << "Reading up to " << total_trials << " training events...\n"; auto FO_events = peek_events(reader, total_trials); if(FO_events.empty()) { throw std::runtime_error{ "No events generated to calibrate the unweighting weight!" "Please increase the number \"trials\" or deactivate the unweighting." }; } const size_t trials = total_trials/FO_events.size(); // adjust reweight factor so that the overall normalisation // is the same as in the full run reweight_factor *= trials; for(auto & hepeup: FO_events) { hepeup.XWGTUP *= reweight_factor; } std::cout << "Training unweighter with " << trials << '*' << FO_events.size() << " events\n"; auto progress = HEJ::ProgressBar{ std::cout, static_cast(FO_events.size()) }; std::vector resummation_events; for(auto const & hepeup: FO_events) { append_resummed_events( resummation_events, reweighter, hepeup, trials, fixed_order_jets, ew_parameters ); ++progress; } unweighter.set_cut_to_peakwt(resummation_events, max_dev); std::cout << "\nUnweighting events with weight up to " << unweighter.get_cut() << '\n'; } int main(int argn, char** argv) { using clock = std::chrono::system_clock; if (argn != 3) { std::cerr << "\n# Usage:\n."<< argv[0] <<" config_file input_file\n\n"; return EXIT_FAILURE; } const auto start_time = clock::now(); { std::cout << "Starting " << HEJ::Version::package_name_full() << ", revision " << HEJ::Version::revision() << " (" << time_to_string(clock::to_time_t(start_time)) << ")" << std::endl; } fastjet::ClusterSequence::print_banner(); // read configuration const HEJ::Config config = load_config(argv[1]); auto reader = HEJ::make_reader(argv[2]); assert(reader); auto heprup{ reader->heprup() }; heprup.generators.emplace_back(LHEF::XMLTag{}); heprup.generators.back().name = HEJ::Version::package_name(); heprup.generators.back().version = HEJ::Version::String(); auto analyses = get_analyses( config.analyses_parameters, heprup ); assert(analyses.empty() || analyses.front() != nullptr); HEJ::CombinedEventWriter writer{config.output, std::move(heprup)}; double global_reweight = 1.; - const auto & max_events = config.max_events; + auto const & max_events = config.max_events; // if we need the event number: if(std::abs(heprup.IDWTUP) == 4 || std::abs(heprup.IDWTUP) == 1 || max_events){ // try to read from LHE head auto input_events{reader->number_events()}; if(!input_events) { // else count manually auto t_reader = HEJ::make_reader(argv[2]); input_events = 0; while(t_reader->read_event()) ++(*input_events); } if(std::abs(heprup.IDWTUP) == 4 || std::abs(heprup.IDWTUP) == 1){ // IDWTUP 4 or 1 assume average(weight)=xs, but we need sum(weights)=xs std::cout << "Found IDWTUP " << heprup.IDWTUP << ": " << "assuming \"cross section = average weight\".\n" << "converting to \"cross section = sum of weights\" "; global_reweight /= *input_events; } if(max_events && (*input_events > *max_events)){ // maximal number of events given global_reweight *= *input_events/static_cast(*max_events); std::cout << "Processing " << *max_events << " out of " << *input_events << " events\n"; } } HEJ::ScaleGenerator scale_gen{ config.scales.base, config.scales.factors, config.scales.max_ratio }; std::shared_ptr ran{ HEJ::make_RNG(config.rng.name, config.rng.seed)}; assert(ran != nullptr); HEJ::EventReweighter hej{ reader->heprup(), std::move(scale_gen), to_EventReweighterConfig(config), ran }; HEJ::optional unweighter{}; if(config.weight_type != HEJ::WeightType::weighted) { unweighter = HEJ::Unweighter{}; } if(config.weight_type == HEJ::WeightType::partially_unweighted) { HEJ::BufferedEventReader buffered_reader{std::move(reader)}; assert(config.unweight_config); train( *unweighter, buffered_reader, hej, config.unweight_config->trials, config.unweight_config->max_dev, global_reweight/config.trials, config.fixed_order_jets, config.ew_parameters ); reader = std::make_unique( std::move(buffered_reader) ); } // status infos & eye candy size_t nevent = 0; std::array nevent_type{0}, nfailed_type{0}; auto progress = make_progress_bar(reader->heprup().XSECUP); HEJ::CrossSectionAccumulator xs; std::map status_counter; size_t total_trials = 0; size_t total_resum = 0; // Loop over the events in the input file while(reader->read_event() && (!max_events || nevent < *max_events) ){ ++nevent; // reweight events so that the total cross section is conserved auto hepeup = reader->hepeup(); hepeup.XWGTUP *= global_reweight; const auto FO_event = to_event(hepeup, config.fixed_order_jets, config.ew_parameters); if(FO_event.central().weight == 0) { static const bool warned_once = [argv,nevent](){ std::cerr << "WARNING: event number " << nevent << " in " << argv[2] << " has zero weight. " "Ignoring this and all further events with vanishing weight.\n"; return true; }(); (void) warned_once; // shut up compiler warnings continue; } auto resummed_events{ hej.reweight(FO_event, config.trials) }; // some bookkeeping for(auto const & s: hej.status()) ++status_counter[s]; total_trials+=hej.status().size(); ++nevent_type[FO_event.type()]; if(resummed_events.empty()) ++nfailed_type[FO_event.type()]; if(unweighter) { unweight(*unweighter, config.weight_type, resummed_events, *ran); } // analysis for(auto & ev: resummed_events){ //TODO: move pass_cuts to after phase space point generation bool passed = analyses.empty(); for(auto const & analysis: analyses){ if(analysis->pass_cuts(ev, FO_event)){ passed = true; analysis->fill(ev, FO_event); }; } if(passed){ writer.write(ev); } else { ev.parameters()*=0; // do not use discarded events afterwards } } xs.fill_correlated(resummed_events); total_resum += resummed_events.size(); if(progress) progress->increment(FO_event.central().weight); } // main event loop std::cout << '\n'; for(auto const & analysis: analyses){ analysis->finalise(); } + writer.finish(); using namespace HEJ::event_type; std::cout<< "Events processed: " << nevent << " (" << total_resum << " resummed)"<< '\n'; std::cout << '\t' << name(EventType::first_type) << ": " << nevent_type[EventType::first_type] << ", failed to reconstruct " << nfailed_type[EventType::first_type] << '\n'; for(auto i=EventType::first_type+1; i<=EventType::last_type; i*=2){ std::cout << '\t' << name(static_cast(i)) << ": " << nevent_type[i] << ", failed to reconstruct " << nfailed_type[i] << '\n'; } std::cout << '\n' << xs << '\n'; std::cout << "Generation statistic: " << status_counter[HEJ::StatusCode::good] << "/" << total_trials << " trials successful.\n"; for(auto && entry: status_counter){ const double fraction = static_cast(entry.second)/total_trials; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(17) << (to_string(entry.first) + ":") << " ["; for(int i = 0; i < percent/2; ++i) std::cout << '#'; for(int i = percent/2; i < 50; ++i) std::cout << ' '; std::cout << "] " < run_time = (clock::now() - start_time); std::cout << "\nFinished " << HEJ::Version::package_name() << " at " << time_to_string(clock::to_time_t(clock::now())) << "\n=> Runtime: " << run_time.count() << " sec (" << nevent/run_time.count() << " Events/sec).\n"; return EXIT_SUCCESS; } diff --git a/src/get_analysis.cc b/src/get_analysis.cc index 1262a28..1436e44 100644 --- a/src/get_analysis.cc +++ b/src/get_analysis.cc @@ -1,51 +1,52 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/get_analysis.hh" #include #include #include "yaml-cpp/yaml.h" #include "HEJ/EmptyAnalysis.hh" #include "HEJ/RivetAnalysis.hh" -namespace HEJ{ +namespace HEJ { std::unique_ptr get_analysis( YAML::Node const & parameters, LHEF::HEPRUP const & heprup ){ if(!parameters["plugin"]){ - if(parameters["rivet"]) + if(parameters["rivet"].IsDefined()) return RivetAnalysis::create(parameters, heprup); return EmptyAnalysis::create(parameters, heprup); } using AnalysisMaker = std::unique_ptr (*)( YAML::Node const &, LHEF::HEPRUP const &); const auto plugin_name = parameters["plugin"].as(); - auto handle = dlopen(plugin_name.c_str(), RTLD_NOW); + void * handle = dlopen(plugin_name.c_str(), RTLD_NOW); char * error = dlerror(); if(error != nullptr) throw std::runtime_error(error); void * sym = dlsym(handle, "make_analysis"); error = dlerror(); if(error != nullptr) throw std::runtime_error(error); - auto make_analysis = reinterpret_cast(sym); + auto make_analysis = reinterpret_cast(sym); // NOLINT return make_analysis(parameters, heprup); } std::vector> get_analyses( std::vector const & parameters, LHEF::HEPRUP const & heprup ){ std::vector> anas; + anas.reserve(parameters.size()); for(auto const & param: parameters){ anas.emplace_back(get_analysis(param, heprup)); } return anas; } -} +} // namespace HEJ diff --git a/src/jets.cc b/src/jets.cc index bc40e40..9f132af 100644 --- a/src/jets.cc +++ b/src/jets.cc @@ -1,855 +1,887 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/jets.hh" #include #include #include "HEJ/Constants.hh" namespace { // short hand for math functions using std::abs; using std::conj; using std::pow; using std::sqrt; double metric(const size_t mu, const size_t nu) { if(mu != nu) return 0.; return (mu == 0)?1.:-1.; } } // 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()); } CCurrent CCurrent::operator+(const CCurrent& other) const { COM result_c0=c0 + other.c0; COM result_c1=c1 + other.c1; COM result_c2=c2 + other.c2; COM result_c3=c3 + other.c3; - return CCurrent(result_c0,result_c1,result_c2,result_c3); + return {result_c0,result_c1,result_c2,result_c3}; } CCurrent CCurrent::operator-(const CCurrent& other) const { COM result_c0=c0 - other.c0; COM result_c1=c1 - other.c1; COM result_c2=c2 - other.c2; COM result_c3=c3 - other.c3; - return CCurrent(result_c0,result_c1,result_c2,result_c3); + return {result_c0,result_c1,result_c2,result_c3}; } CCurrent CCurrent::operator*(const double x) const { COM result_c0=x*CCurrent::c0; COM result_c1=x*CCurrent::c1; COM result_c2=x*CCurrent::c2; COM result_c3=x*CCurrent::c3; - return CCurrent(result_c0,result_c1,result_c2,result_c3); + return {result_c0,result_c1,result_c2,result_c3}; } CCurrent CCurrent::operator/(const double x) const { COM result_c0=CCurrent::c0/x; COM result_c1=CCurrent::c1/x; COM result_c2=CCurrent::c2/x; COM result_c3=CCurrent::c3/x; - return CCurrent(result_c0,result_c1,result_c2,result_c3); + return {result_c0,result_c1,result_c2,result_c3}; } CCurrent CCurrent::operator*(const COM x) const { COM result_c0=x*CCurrent::c0; COM result_c1=x*CCurrent::c1; COM result_c2=x*CCurrent::c2; COM result_c3=x*CCurrent::c3; - return CCurrent(result_c0,result_c1,result_c2,result_c3); + return {result_c0,result_c1,result_c2,result_c3}; } CCurrent CCurrent::operator/(const COM x) const { COM result_c0=(CCurrent::c0)/x; COM result_c1=(CCurrent::c1)/x; COM result_c2=(CCurrent::c2)/x; COM result_c3=(CCurrent::c3)/x; - return CCurrent(result_c0,result_c1,result_c2,result_c3); + return {result_c0,result_c1,result_c2,result_c3}; } std::ostream& operator <<(std::ostream& os, const CCurrent& cur) { os << "("< pin.minus()) { // if forward const double sqpip = sqrt(abs(pin.plus())); cur[0] = sqpop * sqpip; cur[1] = sqpom * sqpip * poperp / abs(poperp); cur[2] = -COM(0,1) * cur[1]; cur[3] = cur[0]; } else { // if backward const double sqpim = sqrt(abs(pin.minus())); cur[0] = -sqpom * sqpim * poperp / abs(poperp); cur[1] = -sqpim * sqpop; cur[2] = COM(0,1) * cur[1]; cur[3] = -cur[0]; } } else { // positive helicity if (pin.plus() > pin.minus()) { // if forward const double sqpip = sqrt(abs(pin.plus())); cur[0] = sqpop * sqpip; cur[1] = sqpom * sqpip * conj(poperp) / abs(poperp); cur[2] = COM(0,1) * cur[1]; cur[3] = cur[0]; } else { // if backward double sqpim = sqrt(abs(pin.minus())); cur[0] = -sqpom * sqpim * conj(poperp) / abs(poperp); cur[1] = -sqpim * sqpop; cur[2] = -COM(0,1) * cur[1]; cur[3] = -cur[0]; } } } CCurrent joi(HLV const & pout, bool helout, HLV const & pin, bool helin) { current cur; joi(pout, helout, pin, helin, cur); - return CCurrent(cur[0],cur[1],cur[2],cur[3]); + return {cur[0],cur[1],cur[2],cur[3]}; } void jio(HLV const & pin, bool helin, HLV const & pout, bool helout, current &cur ) { joi(pout, !helout, pin, !helin, cur); } CCurrent jio(HLV const & pin, bool helin, HLV const & pout, bool helout){ current cur; jio(pin, helin, pout, helout, cur); - return CCurrent(cur[0],cur[1],cur[2],cur[3]); + return {cur[0],cur[1],cur[2],cur[3]}; } void joo(HLV pi, bool heli, HLV pj, bool helj, current &cur) { // Zero our current cur[0] = 0.0; cur[1] = 0.0; cur[2] = 0.0; cur[3] = 0.0; if (heli!=helj) { throw std::invalid_argument{"Non-matching helicities"}; - } else if ( heli == true ) { // If positive helicity swap momenta + } + if ( heli ) { // If positive helicity swap momenta std::swap(pi,pj); } const double sqpjp = sqrt(abs(pj.plus() )); const double sqpjm = sqrt(abs(pj.minus())); const double sqpip = sqrt(abs(pi.plus() )); const double sqpim = sqrt(abs(pi.minus())); // Allow for "jii" format const COM piperp = (pi.x()==0 && pi.y() ==0) ? -1 : pi.x()+COM(0,1)*pi.y(); const COM pjperp = (pj.x()==0 && pj.y() ==0) ? -1 : pj.x()+COM(0,1)*pj.y(); const COM phasei = piperp / abs(piperp); const COM phasej = pjperp / abs(pjperp); cur[0] = sqpim * sqpjm * phasei * conj(phasej) + sqpip * sqpjp; cur[1] = sqpim * sqpjp * phasei + sqpip * sqpjm * conj(phasej); cur[2] = -COM(0, 1) * (sqpim * sqpjp * phasei - sqpip * sqpjm * conj(phasej)); cur[3] = -sqpim * sqpjm * phasei * conj(phasej) + sqpip * sqpjp; } CCurrent joo(HLV const & pi, bool heli, HLV const & pj, bool helj) { current cur; joo(pi, heli, pj, helj, cur); - return CCurrent(cur[0],cur[1],cur[2],cur[3]); + return {cur[0],cur[1],cur[2],cur[3]}; } 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. */ double j_j(HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ HLV const q1=p1in-p1out; HLV const q2=-(p2in-p2out); - current mj1m,mj1p,mj2m,mj2p; + current mj1m; + current mj1p; + current mj2m; + current mj2p; // Note need to flip helicities in anti-quark case. joi(p1out, false, p1in, false, mj1p); joi(p1out, true, p1in, true, mj1m); joi(p2out, false, p2in, false, mj2p); joi(p2out, true, p2in, true, mj2m); COM const Mmp=cdot(mj1m,mj2p); COM const Mmm=cdot(mj1m,mj2m); COM const Mpp=cdot(mj1p,mj2p); COM const Mpm=cdot(mj1p,mj2m); double const sst=abs2(Mmm)+abs2(Mmp)+abs2(Mpp)+abs2(Mpm); // Multiply by Cf^2 return 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 { double juno_j(HLV const & pg, HLV const & p1out, HLV const & p1in, HLV const & p2out, HLV const & p2in ){ // This construction is taking rapidity order: pg > p1out >> p2out HLV q1=p1in-p1out; // Top End HLV q2=-(p2in-p2out); // Bottom End HLV qg=p1in-p1out-pg; // Extra bit post-gluon // Note current split into two by gauge choice. // See James C's Thesis (p72). -> CCurrent mj1p=joi(p1out, false, p1in, false); CCurrent mj1m=joi(p1out, true, p1in, true); CCurrent jgap=joi(pg, false, p1in, false); CCurrent jgam=joi(pg, true, p1in, true); // Note for function joo(): = . CCurrent j2gp=joo(p1out, false, pg, false); CCurrent j2gm=joo(p1out, true, pg, true); CCurrent mj2p=joi(p2out, false, p2in, false); CCurrent mj2m=joi(p2out, true, p2in, true); // Dot products of these which occur again and again COM Mmp=mj1m.dot(mj2p); COM Mmm=mj1m.dot(mj2m); COM Mpp=mj1p.dot(mj2p); COM Mpm=mj1p.dot(mj2m); - CCurrent p1o(p1out),p2o(p2out),p2i(p2in),qsum(q1+qg),p1i(p1in); + CCurrent p1o(p1out); + CCurrent p2o(p2out); + CCurrent p2i(p2in); + CCurrent qsum(q1+qg); + CCurrent p1i(p1in); CCurrent Lmm=(qsum*(Mmm)+(-2.*mj2m.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2m +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mmm/2.))/q1.m2(); CCurrent Lmp=(qsum*(Mmp) + (-2.*mj2p.dot(pg))*mj1m+2.*mj1m.dot(pg)*mj2p +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mmp/2.))/q1.m2(); CCurrent Lpm=(qsum*(Mpm) + (-2.*mj2m.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2m +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mpm/2.))/q1.m2(); CCurrent Lpp=(qsum*(Mpp) + (-2.*mj2p.dot(pg))*mj1p+2.*mj1p.dot(pg)*mj2p +(p2o/pg.dot(p2out) + p2i/pg.dot(p2in))*(qg.m2()*Mpp/2.))/q1.m2(); CCurrent U1mm=(jgam.dot(mj2m)*j2gm+2.*p1o*Mmm)/(p1out+pg).m2(); CCurrent U1mp=(jgam.dot(mj2p)*j2gm+2.*p1o*Mmp)/(p1out+pg).m2(); CCurrent U1pm=(jgap.dot(mj2m)*j2gp+2.*p1o*Mpm)/(p1out+pg).m2(); CCurrent U1pp=(jgap.dot(mj2p)*j2gp+2.*p1o*Mpp)/(p1out+pg).m2(); CCurrent U2mm=((-1.)*j2gm.dot(mj2m)*jgam+2.*p1i*Mmm)/(p1in-pg).m2(); CCurrent U2mp=((-1.)*j2gm.dot(mj2p)*jgam+2.*p1i*Mmp)/(p1in-pg).m2(); CCurrent U2pm=((-1.)*j2gp.dot(mj2m)*jgap+2.*p1i*Mpm)/(p1in-pg).m2(); CCurrent U2pp=((-1.)*j2gp.dot(mj2p)*jgap+2.*p1i*Mpp)/(p1in-pg).m2(); constexpr double cf=C_F; double amm=cf*(2.*vre(Lmm-U1mm,Lmm+U2mm))+2.*cf*cf/3.*vabs2(U1mm+U2mm); double amp=cf*(2.*vre(Lmp-U1mp,Lmp+U2mp))+2.*cf*cf/3.*vabs2(U1mp+U2mp); double apm=cf*(2.*vre(Lpm-U1pm,Lpm+U2pm))+2.*cf*cf/3.*vabs2(U1pm+U2pm); double app=cf*(2.*vre(Lpp-U1pp,Lpp+U2pp))+2.*cf*cf/3.*vabs2(U1pp+U2pp); double ampsq=-(amm+amp+apm+app); //Divide by t-channels ampsq/=q2.m2()*qg.m2(); ampsq/=16.; // Factor of (Cf/Ca) for each quark to match j_j. ampsq*=(C_F*C_F)/(C_A*C_A); return ampsq; } } // 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 { void eps(HLV const & refmom, HLV const & kb, bool hel, current &ep){ //Positive helicity eps has negative helicity choices for spinors and vice versa joi(refmom,hel,kb,hel,ep); - double norm; + double norm = NAN; if(kb.z()<0.) norm = sqrt(2.*refmom.plus()*kb.minus()); else norm = sqrt(2.*refmom.minus()*kb.plus()); // Normalise std::for_each(begin(ep), end(ep), [&,norm](auto & val){val/=norm;}); } COM qggm1(HLV const & pa, HLV const & pb, HLV const & p1, HLV const & p2, HLV const & p3, bool helchain, bool heltop, bool helb, HLV const & refmom ){ // Since everything is defined with currents, need to use compeleness relation // to expand p slash. i.e. pslash = |p> const & partons, unsigned int nabove ){ const double s23 = 2.*(k2*k3); const double sa2 = 2.*(ka*k2); const double sa3 = 2.*(ka*k3); const double s12 = 2.*(partons.front()*k2); const double s13 = 2.*(partons.front()*k3); const double sb2 = 2.*(kb*k2); const double sb3 = 2.*(kb*k3); const double s42 = 2.*(partons.back()*k2); const double s43 = 2.*(partons.back()*k3); HLV q1=ka-partons.front(); for(unsigned int i=1;i const & partons, unsigned int nabove ){ //In order to get correct momentum dependence in the vertex, forst //have to work with CCurrent objects and then convert to 'current' - current cur22,cur23,cur2q,curq3; + current cur22; + current cur23; + current cur2q; + current curq3; COM qarray[4][4]={}; COM temp[4][4]={}; joo(k2,hel2,k2,hel2,cur22); joo(k2,hel2,k3,hel2,cur23); joi(k2,hel2,ka,hel2,cur2q); jio(ka,hel2,k3,hel2,curq3); CurrentMatrix(cur2q, curq3, qarray); for(unsigned int i =0; i const & partons, unsigned int nabove ){ COM M3=0.; - current cur23,cur33,cur2q,curq3; + current cur23; + current cur33; + current cur2q; + current curq3; COM qarray[4][4]={}; COM temp[4][4]={}; joo(k3,hel2,k3,hel2,cur33); joo(k2,hel2,k3,hel2,cur23); joi(k2,hel2,ka,hel2,cur2q); jio(ka,hel2,k3,hel2,curq3); CurrentMatrix(cur2q, curq3, qarray); for(unsigned int i =0; i const & partons, bool aqlinepa, bool aqlinepb, bool qqxmarker, int nabove ){ //Get all the possible outer currents - current j1p,j1m,j4p,j4m; + current j1p; + current j1m; + current j4p; + current j4m; if(!(aqlinepa)){ joi(partons.front(),true,ka,true,j1p); joi(partons.front(),false,ka,false,j1m); } if(aqlinepa){ jio(ka,true,partons.front(),true,j1p); jio(ka,false,partons.front(),false,j1m); } if(!(aqlinepb)){ joi(partons.back(),true,kb,true,j4p); joi(partons.back(),false,kb,false,j4m); } if(aqlinepb){ jio(kb,true,partons.back(),true,j4p); jio(kb,false,partons.back(),false,j4m); } - HLV k2,k3; + HLV k2; + HLV k3; if(!(qqxmarker)){ k2=partons.at(nabove+1); k3=partons.at(nabove+2); } else{ k2=partons.at(nabove+2); k3=partons.at(nabove+1); } //8 helicity choices we can make, but only 4 indepedent ones //(complex conjugation related). const COM Mmmm1 = m1(j1m,j4m,false,ka,kb,k2,k3,partons,nabove); const COM Mmmm2 = m2(j1m,j4m,false,ka, k2,k3,partons,nabove); const COM Mmmm3 = m3(j1m,j4m,false,ka, k2,k3,partons,nabove); const COM Mmmp1 = m1(j1m,j4m,true, ka,kb,k2,k3,partons,nabove); const COM Mmmp2 = m2(j1m,j4m,true, ka, k2,k3,partons,nabove); const COM Mmmp3 = m3(j1m,j4m,true, ka, k2,k3,partons,nabove); const COM Mpmm1 = m1(j1p,j4m,false,ka,kb,k2,k3,partons,nabove); const COM Mpmm2 = m2(j1p,j4m,false,ka, k2,k3,partons,nabove); const COM Mpmm3 = m3(j1p,j4m,false,ka, k2,k3,partons,nabove); const COM Mpmp1 = m1(j1p,j4m,true, ka,kb,k2,k3,partons,nabove); const COM Mpmp2 = m2(j1p,j4m,true, ka, k2,k3,partons,nabove); const COM Mpmp3 = m3(j1p,j4m,true, ka, k2,k3,partons,nabove); //Colour factors: const COM cm1m1=3.; const COM cm2m2=4./3.; const COM cm3m3=4./3.; const COM cm1m2 =3./2.*COM(0.,1.); const COM cm1m3 = -3./2.*COM(0.,1.); const COM cm2m3 = -1./6.; //Square and sum for each helicity config: const double Mmmm = real(cm1m1*pow(abs(Mmmm1),2)+cm2m2*pow(abs(Mmmm2),2)+ cm3m3*pow(abs(Mmmm3),2)+2.*real(cm1m2*Mmmm1*conj(Mmmm2))+ 2.*real(cm1m3*Mmmm1*conj(Mmmm3))+2.*real(cm2m3*Mmmm2*conj(Mmmm3))); const double Mmmp = real(cm1m1*pow(abs(Mmmp1),2)+cm2m2*pow(abs(Mmmp2),2)+ cm3m3*pow(abs(Mmmp3),2)+2.*real(cm1m2*Mmmp1*conj(Mmmp2))+ 2.*real(cm1m3*Mmmp1*conj(Mmmp3))+2.*real(cm2m3*Mmmp2*conj(Mmmp3))); const double Mpmm = real(cm1m1*pow(abs(Mpmm1),2)+cm2m2*pow(abs(Mpmm2),2)+ cm3m3*pow(abs(Mpmm3),2)+2.*real(cm1m2*Mpmm1*conj(Mpmm2))+ 2.*real(cm1m3*Mpmm1*conj(Mpmm3))+2.*real(cm2m3*Mpmm2*conj(Mpmm3))); const double Mpmp = real(cm1m1*pow(abs(Mpmp1),2)+cm2m2*pow(abs(Mpmp2),2)+ cm3m3*pow(abs(Mpmp3),2)+2.*real(cm1m2*Mpmp1*conj(Mpmp2))+ 2.*real(cm1m3*Mpmp1*conj(Mpmp3))+2.*real(cm2m3*Mpmp2*conj(Mpmp3))); //Result (averaged, without coupling or t-channel props). Factor of //2 for the 4 helicity configurations I didn't work out explicitly HLV prop1 = ka; for(int i=0; i<=nabove; ++i){ prop1 -= partons[i]; } const HLV prop2 = prop1 - k2 - k3; return (2.*(Mmmm+Mmmp+Mpmm+Mpmp)/9./4.) / ((ka-partons.front()).m2()*(kb-partons.back()).m2()*prop1.m2()*prop2.m2()); } } // namespace currents } // namespace HEJ diff --git a/src/kinematics.cc b/src/kinematics.cc index ce88056..8a4c365 100644 --- a/src/kinematics.cc +++ b/src/kinematics.cc @@ -1,27 +1,28 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/kinematics.hh" #include "fastjet/PseudoJet.hh" #include "HEJ/Particle.hh" -namespace HEJ{ +namespace HEJ { //reconstruct incoming momenta from momentum conservation std::tuple incoming_momenta( std::vector const & outgoing ){ - double xa(0.), xb(0.); + double xa(0.); + double xb(0.); for(auto const & out: outgoing){ xa += out.p.e() - out.p.pz(); xb += out.p.e() + out.p.pz(); } return std::tuple{ {0,0,-xa/2.,xa/2.}, {0,0,xb/2.,xb/2.} }; } -} +} // namespace HEJ diff --git a/src/make_RNG.cc b/src/make_RNG.cc index d96c67f..9fbc2c8 100644 --- a/src/make_RNG.cc +++ b/src/make_RNG.cc @@ -1,35 +1,35 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) - * \date 2019 + * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/make_RNG.hh" #include #include -#include "HEJ/exceptions.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Ranlux64.hh" +#include "HEJ/exceptions.hh" namespace HEJ { - std::unique_ptr make_RNG( + std::unique_ptr make_RNG( std::string const & name, optional const & seed ) { std::string lname; std::transform( begin(name), end(name), std::back_inserter(lname), [](char c) { return std::tolower(c, std::locale()); } ); if(lname == "mixmax") { if(seed) return std::make_unique(std::stol(*seed)); return std::make_unique(); } if(lname == "ranlux64") { if(seed) return std::make_unique(*seed); return std::make_unique(); } throw std::invalid_argument{"Unknown random number generator: " + name}; } -} +} // namespace HEJ diff --git a/src/make_writer.cc b/src/make_writer.cc index 707bf74..49a4609 100644 --- a/src/make_writer.cc +++ b/src/make_writer.cc @@ -1,33 +1,33 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/make_writer.hh" -#include "HEJ/exceptions.hh" +#include "HEJ/HDF5Writer.hh" #include "HEJ/HepMC2Writer.hh" #include "HEJ/HepMC3Writer.hh" -#include "HEJ/HDF5Writer.hh" #include "HEJ/LesHouchesWriter.hh" +#include "HEJ/exceptions.hh" -namespace HEJ{ +namespace HEJ { std::unique_ptr make_format_writer( FileFormat format, std::string const & outfile, LHEF::HEPRUP const & heprup ){ switch(format){ case FileFormat::Les_Houches: return std::make_unique(outfile, heprup); case FileFormat::HepMC2: return std::make_unique(outfile, heprup); case FileFormat::HepMC3: return std::make_unique(outfile, heprup); case FileFormat::HDF5: return std::make_unique(outfile, heprup); default: throw std::logic_error("unhandled file format"); } } -} +} // namespace HEJ diff --git a/src/resummation_jet.cc b/src/resummation_jet.cc index 59c772c..1584463 100644 --- a/src/resummation_jet.cc +++ b/src/resummation_jet.cc @@ -1,114 +1,114 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/resummation_jet.hh" #include #include #include #include #include #include "fastjet/PseudoJet.hh" #include "HEJ/utility.hh" -namespace HEJ{ +namespace HEJ { std::vector resummation_jet_momenta( std::vector const & p_born, fastjet::PseudoJet const & qperp ) { // for "new" reshuffling p^B = p + qperp*|p^B|/P^B double Pperp_born = 0.; for(auto const & p: p_born) Pperp_born += p.perp(); std::vector p_res; p_res.reserve(p_born.size()); - for(auto & pB: p_born) { + for(auto const & pB: p_born) { const double px = pB.px() - qperp.px()*pB.perp()/Pperp_born; const double py = pB.py() - qperp.py()*pB.perp()/Pperp_born; const double pperp = std::sqrt(px*px + py*py); // keep the rapidities fixed const double pz = pperp*std::sinh(pB.rapidity()); const double E = pperp*std::cosh(pB.rapidity()); p_res.emplace_back(px, py, pz, E); assert( - HEJ::nearby_ep( + nearby_ep( p_res.back().rapidity(), pB.rapidity(), 1e-5 ) ); } return p_res; } namespace { enum coordinates : std::size_t { x1, x2 }; namespace ublas = boost::numeric::ublas; template double det(ublas::matrix_expression const& m) { ublas::permutation_matrix pivots{m().size1()}; Matrix mLu{m()}; const auto is_singular = lu_factorize(mLu, pivots); if(is_singular) return 0.; double det = 1.0; for (std::size_t i = 0; i < pivots.size(); ++i){ if (pivots(i) != i) det = -det; det *= mLu(i,i); } return det; } using ublas::matrix; - } + } // namespace double resummation_jet_weight( std::vector const & p_born, fastjet::PseudoJet const & qperp ) { using std::size_t; static constexpr int num_coordinates = 2; auto Jacobian = matrix{ num_coordinates*p_born.size(), num_coordinates*p_born.size() }; double P_perp = 0.; for(auto const & J: p_born) P_perp += J.perp(); for(size_t l = 0; l < p_born.size(); ++l){ const double Jl_perp = p_born[l].perp(); for(size_t lp = 0; lp < p_born.size(); ++lp){ - const int delta_l = (l == lp); - const auto & Jlp = p_born[lp]; + const int delta_l = static_cast(l == lp); + auto const & Jlp = p_born[lp]; const double Jlp_perp = Jlp.perp(); for(size_t x = x1; x <= x2; ++x){ const double qxy = (x==x1)?qperp.px():qperp.py(); for(size_t xp = x1; xp <= x2; ++xp){ const double Jxy = (xp==x1)?Jlp.px():Jlp.py(); - const int delta_x = x == xp; + const int delta_x = static_cast(x == xp); Jacobian(2*l + x, 2*lp + xp) = + delta_l*delta_x - qxy*Jxy/(P_perp*Jlp_perp) * (delta_l - Jl_perp/P_perp); } } } } return det(Jacobian); } -} +} // namespace HEJ diff --git a/src/stream.cc b/src/stream.cc index e1b9ad6..9b3777a 100644 --- a/src/stream.cc +++ b/src/stream.cc @@ -1,32 +1,34 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "HEJ/stream.hh" +#include + #include -namespace HEJ{ +namespace HEJ { - namespace{ + namespace { bool is_gzip(std::ifstream & file){ - static constexpr char magic_bytes[] = {'\x1f', '\x8b'}; + static constexpr std::array magic_bytes = {'\x1f', '\x8b'}; if(file.peek() != magic_bytes[0]) return false; file.get(); const char second = file.peek(); file.unget(); return second == magic_bytes[1]; } } istream::istream(std::string const & filename): file_{filename, std::ios_base::in | std::ios_base::binary}, stream_{std::make_unique()} { if(is_gzip(file_)){ stream_->push(boost::iostreams::gzip_decompressor{}); } stream_->push(file_); } -} +} // namespace HEJ diff --git a/t/test_hdf5_write.cc b/t/test_hdf5_write.cc index 25dc039..33f351f 100644 --- a/t/test_hdf5_write.cc +++ b/t/test_hdf5_write.cc @@ -1,87 +1,86 @@ /** * \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 "HEJ/Event.hh" #include "HEJ/EventReader.hh" #include "HEJ/EventWriter.hh" #include "HEJ/HDF5Reader.hh" #include "HEJ/make_writer.hh" #include "HEJ/output_formats.hh" #include "HEJ/Parameters.hh" #include "HEJ/Particle.hh" #include "HEJ/utility.hh" #include "fastjet/JetDefinition.hh" namespace { const fastjet::JetDefinition jet_def{fastjet::JetAlgorithm::antikt_algorithm, 0.4}; const double min_jet_pt{20.}; } int main(int argc, char** argv) { if(argc != 3) { std::cerr << "Usage: " << argv[0] << " in_file.hdf5 out_file.hdf5\n"; return EXIT_FAILURE; } std::vector events; std::size_t max_nevent = 15299; - // this scope is needed to trigger the HEJ::HDF5Writer destructor - // don't remove it - { - auto reader = HEJ::make_reader(argv[1]); + auto reader = HEJ::make_reader(argv[1]); - auto writer = HEJ::make_format_writer( - HEJ::FileFormat::HDF5, argv[2], reader->heprup() - ); + auto writer = HEJ::make_format_writer( + HEJ::FileFormat::HDF5, argv[2], reader->heprup() + ); - std::size_t nevent = 0; - while(reader->read_event()) { - ++nevent; - const auto event = HEJ::Event::EventData{reader->hepeup()}.cluster( - jet_def, min_jet_pt - ); - events.emplace_back(event); - writer->write(event); - if(nevent>=max_nevent) - break; - } - max_nevent = std::min(nevent,max_nevent); + std::size_t nevent = 0; + while(reader->read_event()) { + ++nevent; + const auto event = HEJ::Event::EventData{reader->hepeup()}.cluster( + jet_def, min_jet_pt + ); + events.emplace_back(event); + writer->write(event); + if(nevent>=max_nevent) + break; } + writer->finish(); + // throw on second call to finish + ASSERT_THROW(writer->finish(), std::ios_base::failure); + max_nevent = std::min(nevent,max_nevent); - HEJ::HDF5Reader reader{argv[2]}; - std::size_t nevent = 0; + HEJ::HDF5Reader new_reader{argv[2]}; + nevent = 0; for(auto const & event: events) { ++nevent; - reader.read_event(); - const auto ev = HEJ::Event::EventData{reader.hepeup()}.cluster( + new_reader.read_event(); + const auto ev = HEJ::Event::EventData{new_reader.hepeup()}.cluster( jet_def, min_jet_pt ); ASSERT_PROPERTY(ev, event, incoming().size()); for(std::size_t i = 0; i < ev.incoming().size(); ++i) { ASSERT(HEJ::nearby(ev.incoming()[i].p, event.incoming()[i].p)); } ASSERT_PROPERTY(ev, event, outgoing().size()); for(std::size_t i = 0; i < ev.outgoing().size(); ++i) { ASSERT(HEJ::nearby(ev.outgoing()[i].p, event.outgoing()[i].p)); } ASSERT_PROPERTY(ev, event, decays().size()); ASSERT_PROPERTY(ev, event, type()); ASSERT_PROPERTY(ev, event, central().weight); } ASSERT(nevent == max_nevent); return EXIT_SUCCESS; } diff --git a/t/test_pid.cc b/t/test_pid.cc index 990dd2b..bbd7ee8 100644 --- a/t/test_pid.cc +++ b/t/test_pid.cc @@ -1,61 +1,67 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "hej_test.hh" #include "HEJ/PDG_codes.hh" namespace { void consistent_name(int const id){ ASSERT(HEJ::to_ParticleID(name(HEJ::ParticleID(id))) == id); ASSERT(HEJ::to_ParticleID(std::to_string(id)) == id); } } int main() { using namespace HEJ; for(int i=1; i<7; ++i){ // quarks consistent_name(i); ASSERT(is_quark(ParticleID(i))); consistent_name(-i); ASSERT(is_antiquark(ParticleID(-i))); // leptons consistent_name(i+10); ASSERT(is_lepton(ParticleID(i+10))); consistent_name(-i-10); ASSERT(is_antilepton(ParticleID(-i-10))); // negative tests ASSERT(!is_anylepton(ParticleID(i))); ASSERT(!is_AWZH_boson(ParticleID(-i))); ASSERT(!is_anyneutrino(ParticleID(i))); ASSERT(!is_anyquark(ParticleID(i+10))); ASSERT(!is_parton(ParticleID(-i-10))); } ASSERT(!is_parton(ParticleID(-6))); // bosons consistent_name(21); ASSERT(is_parton(ParticleID(21))); ASSERT(is_gluon(ParticleID(21))); for(int i=22; i<25; ++i){ consistent_name(i); ASSERT(is_AWZ_boson(ParticleID(i))); } consistent_name(-24); ASSERT(is_AWZH_boson(ParticleID(-24))); consistent_name(25); ASSERT(is_AWZH_boson(ParticleID(25))); consistent_name(81); ASSERT(is_AWZ_boson(ParticleID(81))); // proton consistent_name(2212); consistent_name(-2212); + // unspecified + ASSERT(!is_parton(pid::unspecified)); + ASSERT(!is_anyquark(pid::unspecified)); + ASSERT(!is_AWZH_boson(pid::unspecified)); + ASSERT(!is_anylepton(pid::unspecified)); + ASSERT(!is_anyneutrino(pid::unspecified)); + ASSERT_THROW(HEJ::to_ParticleID("unspecified"), std::invalid_argument); + ASSERT_THROW(HEJ::to_ParticleID("0"), std::invalid_argument); // not existing - ASSERT(!is_parton(ParticleID(0))); - ASSERT(!is_anyquark(ParticleID(0))); - ASSERT_THROW(name(HEJ::ParticleID(0)), std::logic_error); + ASSERT_THROW(name(HEJ::ParticleID(39)), std::logic_error); ASSERT_THROW(HEJ::to_ParticleID("gluino"), std::invalid_argument); return EXIT_SUCCESS; } diff --git a/t/test_psp.cc b/t/test_psp.cc index f721d5d..7c25fc8 100644 --- a/t/test_psp.cc +++ b/t/test_psp.cc @@ -1,74 +1,74 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "hej_test.hh" #include #include #include "HEJ/stream.hh" #include "HEJ/Config.hh" #include "HEJ/event_types.hh" #include "HEJ/Event.hh" #include "HEJ/PhaseSpacePoint.hh" #include "HEJ/Ranlux64.hh" #include "LHEF/LHEF.h" #include "fastjet/JetDefinition.hh" -namespace{ +namespace { constexpr int max_trials = 100; constexpr double max_ext_soft_pt_fraction = 0.1; const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4}; constexpr double min_jet_pt = 50; } int main(int argn, char** argv) { if(argn != 2){ std::cerr << "Usage: " << argv[0] << " eventfile"; return EXIT_FAILURE; } HEJ::istream in{argv[1]}; LHEF::Reader reader{in}; LHEF::Writer writer{std::cerr}; writer.heprup = reader.heprup; HEJ::PhaseSpacePointConfig conf; conf.jet_param = HEJ::JetParameters{jet_def, min_jet_pt}; conf.max_ext_soft_pt_fraction = max_ext_soft_pt_fraction; HEJ::Ranlux64 ran{}; while(reader.readEvent()){ HEJ::Event::EventData ev_data{ reader.hepeup }; shuffle_particles(ev_data); const HEJ::Event ev{ ev_data( jet_def, min_jet_pt ) }; for(int trial = 0; trial < max_trials; ++trial){ HEJ::PhaseSpacePoint psp{ev, conf, ran}; if(psp.weight() != 0){ HEJ::Event::EventData tmp_ev; tmp_ev.incoming = psp.incoming(); tmp_ev.outgoing = psp.outgoing(); tmp_ev.parameters.central = {0,0,0}; shuffle_particles(tmp_ev); HEJ::Event out_ev{ tmp_ev(jet_def, min_jet_pt) }; if(out_ev.type() != ev.type()){ using HEJ::event_type::name; std::cerr << "Wrong class of phase space point:\n" "original event of class " << name(ev.type()) << ":\n"; writer.hepeup = reader.hepeup; writer.writeEvent(); std::cerr << "Phase space point of class " << name(out_ev.type()) << ":\n"; writer.hepeup = to_HEPEUP(out_ev, &writer.heprup); writer.writeEvent(); return EXIT_FAILURE; } } } } return EXIT_SUCCESS; } diff --git a/t/test_scale_arithmetics.cc b/t/test_scale_arithmetics.cc index b1975f0..c3833b3 100644 --- a/t/test_scale_arithmetics.cc +++ b/t/test_scale_arithmetics.cc @@ -1,102 +1,102 @@ /** * \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 "HEJ/Config.hh" #include "HEJ/Event.hh" #include "HEJ/EventReweighter.hh" #include "HEJ/make_RNG.hh" #include "HEJ/Parameters.hh" #include "HEJ/RNG.hh" #include "HEJ/stream.hh" #include "HEJ/YAMLreader.hh" #include "LHEF/LHEF.h" constexpr double ep = 1e-13; void dump(HEJ::Event const & ev){ { LHEF::Writer writer{std::cout}; std::cout << std::setprecision(6); writer.hepeup = to_HEPEUP(std::move(ev), nullptr); writer.writeEvent(); } std::cout << "Rapidity ordering:\n"; - for(const auto & part: ev.outgoing()){ + for(auto const & part: ev.outgoing()){ std::cout << std::setw(2) << part.type << ": "<< std::setw(7) << part.rapidity() << std::endl; } } int main(int argn, char** argv){ if(argn != 3){ std::cerr << "\n# Usage:\n."<< argv[0] <<" config.yml input_file.lhe\n\n"; return EXIT_FAILURE; } HEJ::Config config = HEJ::load_config(argv[1]); config.scales = HEJ::to_ScaleConfig( YAML::Load("scales: [H_T, 1 * H_T, 2/2 * H_T, 2*H_T/2, H_T/2*2, H_T/2/2*4, H_T*H_T/H_T]") ); HEJ::istream in{argv[2]}; LHEF::Reader reader{in}; std::shared_ptr ran{ HEJ::make_RNG(config.rng.name, config.rng.seed)}; HEJ::ScaleGenerator scale_gen{ config.scales.base, config.scales.factors, config.scales.max_ratio }; HEJ::EventReweighter resum{ reader.heprup, std::move(scale_gen), to_EventReweighterConfig(config), ran }; std::size_t i = 0; while(reader.readEvent()){ ++i; HEJ::Event::EventData data{reader.hepeup}; shuffle_particles(data); HEJ::Event event{ data.cluster( config.resummation_jets.def, config.resummation_jets.min_pt ) }; auto resummed = resum.reweight(event, config.trials); for(auto && ev: resummed) { for(auto &&var: ev.variations()) { if(std::abs(var.muf - ev.central().muf) > ep) { std::cerr << std::setprecision(15) << "unequal scales: " << var.muf << " != " << ev.central().muf << '\n' << "in resummed event:\n"; dump(ev); std::cerr << "\noriginal event:\n"; dump(event); return EXIT_FAILURE; } } } } return EXIT_SUCCESS; } diff --git a/t/test_unweighter.cc b/t/test_unweighter.cc index ec06381..9803fe7 100644 --- a/t/test_unweighter.cc +++ b/t/test_unweighter.cc @@ -1,160 +1,160 @@ /** * \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 "HEJ/CrossSectionAccumulator.hh" #include "HEJ/Event.hh" #include "HEJ/EventReader.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Unweighter.hh" #include "fastjet/JetDefinition.hh" -namespace{ +namespace { const fastjet::JetDefinition jet_def{fastjet::kt_algorithm, 0.4}; const double min_pt{30.}; const double sample_ratio{10.}; const double max_dev{2.}; bool compare_xs( HEJ::XSWithError const & xs1, HEJ::XSWithError const & xs2 ){ std::cout << xs1.value << "+/-" << xs1.error << " vs. " << xs2.value << "+/-" << xs2.error << std::endl; return std::abs(xs1.value/xs2.value-1.)number_events()}; if(!nevents) { auto t_reader = HEJ::make_reader(file); nevents = 0; while(t_reader->read_event()) ++(*nevents); } ASSERT(*nevents>sample_ratio); const size_t size_sample = std::floor(*nevents/sample_ratio); HEJ::Mixmax ran{}; // no unweighting HEJ::CrossSectionAccumulator xs_base; std::vector all_evts; // full unweighting HEJ::CrossSectionAccumulator xs_max; HEJ::Unweighter unw_max; size_t n_max{0}; // midpoint on full sample HEJ::CrossSectionAccumulator xs_mid; HEJ::Unweighter unw_mid; size_t n_mid{0}; // calc max from partial sample HEJ::CrossSectionAccumulator xs_pmax; HEJ::Unweighter unw_pmax; size_t n_pmax{0}; // midpoint on partial sample HEJ::CrossSectionAccumulator xs_pmid; HEJ::Unweighter unw_pmid; size_t n_pmid{0}; // initialise sample for(size_t n = 0; n < size_sample; ++n){ if(!reader->read_event()){ std::cerr << "Sample size bigger than event sample\n"; return EXIT_FAILURE; } const HEJ::Event event{ HEJ::Event::EventData{reader->hepeup()}.cluster(jet_def, min_pt) }; xs_base.fill(event); all_evts.push_back(event); } // calculate partial settings unw_pmax.set_cut_to_maxwt(all_evts); unw_pmid.set_cut_to_peakwt(all_evts, max_dev); for(auto const & ev: unw_pmax.unweight(all_evts, ran)){ xs_pmax.fill(ev); ++n_pmax; } for(auto const & ev: unw_pmid.unweight(all_evts, ran)){ xs_pmid.fill(ev); ++n_pmid; } while(reader->read_event()){ const HEJ::Event event{ HEJ::Event::EventData{reader->hepeup()}.cluster(jet_def, min_pt) }; xs_base.fill(event); auto ev{ unw_pmid.unweight(event, ran) }; if(ev){ xs_pmid.fill(*ev); ++n_pmid; } ev = unw_pmax.unweight(event, ran); if(ev){ xs_pmax.fill(*ev); ++n_pmax; } all_evts.push_back(event); } unw_max.set_cut_to_maxwt(all_evts); unw_mid.set_cut_to_peakwt(all_evts, max_dev); for(auto const & ev: unw_max.unweight(all_evts, ran)){ // make sure all the events have the same weight ASSERT( std::abs( std::abs(unw_max.get_cut()/ev.central().weight)-1. ) < 10e-16); xs_max.fill(ev); ++n_max; } for(auto const & ev: unw_mid.unweight(all_evts, ran)){ xs_mid.fill(ev); ++n_mid; } // sanity check number of events ASSERT( all_evts.size() > 0); ASSERT( n_pmax > 0); ASSERT( n_max > 0); ASSERT( n_pmid > 0); ASSERT( n_mid > 0); ASSERT( n_pmax < all_evts.size() ); ASSERT( n_max < n_pmax ); ASSERT( n_pmid < all_evts.size() ); ASSERT( n_mid < all_evts.size() ); ASSERT( n_max < n_mid ); std::cout << "all_evts.size() " << all_evts.size() << " n_pmax " << n_pmax << " n_max " << n_max << " n_pmid " << n_pmid << " n_mid " << n_mid << std::endl; // control xs (in circle) ASSERT(compare_xs( xs_base.total(), xs_pmax.total() )); ASSERT(compare_xs( xs_pmax.total(), xs_max.total() )); ASSERT(compare_xs( xs_max.total() , xs_pmid.total() )); ASSERT(compare_xs( xs_pmid.total(), xs_mid.total() )); ASSERT(compare_xs( xs_mid.total() , xs_base.total() )); return EXIT_SUCCESS; }