diff --git a/.clang-tidy b/.clang-tidy index 0dc056c..f96b147 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,38 +1,39 @@ 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, + -bugprone-macro-parentheses, ' 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/.gitlab-ci.yml b/.gitlab-ci.yml index 029cab8..aff6876 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,449 +1,478 @@ # --------------------------------- # - General Setup - # --------------------------------- stages: - build - test - FOG:build - FOG:test - clean_code - FOG:clear_code - long_test - publish +workflow: + rules: + - when: always + variables: GIT_SUBMODULE_STRATEGY: recursive # build directories HEJ_BUILD_DIR: tmp_HEJ/HEJ_build HEJ_INSTALL_DIR: tmp_HEJ/HEJ_installed FOG_BUILD_DIR: tmp_HEJ/FOG_build FOG_INSTALL_DIR: ${HEJ_INSTALL_DIR} # docker images DOCKER_BASIC: hejdock/hepenv # slc6 DOCKER_CLANG: 'hejdock/hepenv:clang9_ubuntu' # ubuntu with clang DOCKER_HEPMC3: hejdock/hepmc3env # centos7 DOCKER_HIGHFIVE: hejdock/highfiveenv # centos7 DOCKER_QCDLOOP: hejdock/qcdloopenv # slc6, includes rivet DOCKER_RIVET3: 'hejdock/rivetenv:rivet3_gcc9_ubuntu' # ubuntu DOCKER_RIVET3H2: 'hejdock/rivetenv:rivet3_hepmc2_gcc9_ubuntu' # ubuntu DOCKER_RIVET: hejdock/rivetenv # slc6 # default name of cmake CMAKE: cmake CTEST: ctest OVERWRITE_BOOST: "" # ----------- Macros ----------- after_script: - date .tags_template: tags: &tags_def - docker # default pipeline triggers .rules_template: rules: &rules_def - if: $CI_MERGE_REQUEST_ID when: on_success - if: $CI_COMMIT_TAG when: on_success - if: '$CI_COMMIT_BRANCH != null && $CI_COMMIT_BRANCH == "master"' when: on_success - if: '$CI_COMMIT_BRANCH != null && $CI_COMMIT_BRANCH =~ /^v\d+\.\d+$/' when: on_success - when: manual # non-blocking manual allow_failure: true # save complete history of failed tests .save_failure: artifacts: &artifacts_failed when: on_failure untracked: true expire_in: 3d # --------------------------------- # - Script Templates - # --------------------------------- # ----------- Build ----------- .HEJ_build: tags: *tags_def rules: *rules_def stage: build before_script: - date - source /cvmfs/pheno.egi.eu/HEJ/HEJ_env.sh || exit 1 # prepare build - t_HEJ_DIR=${PWD} - t_HEJ_INSTALL_DIR=${PWD}/${HEJ_INSTALL_DIR} - t_HEJ_BUILD_DIR=${PWD}/${HEJ_BUILD_DIR} # hack to allow setting "variable in variable" - t_CMAKE_FLAGS="" - if [[ -n ${OVERWRITE_BOOST} ]]; then declare -n t_boost_root=${OVERWRITE_BOOST}; t_CMAKE_FLAGS="-DBOOST_ROOT=${t_boost_root}"; fi - echo ${t_CMAKE_FLAGS} - mkdir -p ${t_HEJ_BUILD_DIR} - cd ${t_HEJ_BUILD_DIR} - ${CMAKE} ${t_HEJ_DIR} -DCMAKE_BUILD_TYPE=DEBUG -DCMAKE_INSTALL_PREFIX=${t_HEJ_INSTALL_DIR} ${t_CMAKE_FLAGS} script: - make -j $(nproc --ignore=1) - make install needs: [] # can run immediately artifacts: # save build and installed folder name: build expire_in: 1d paths: - ${HEJ_INSTALL_DIR} - ${HEJ_BUILD_DIR} # ----------- Test ----------- .HEJ_test: tags: *tags_def rules: *rules_def stage: test before_script: - date - source /cvmfs/pheno.egi.eu/HEJ/HEJ_env.sh || exit 1 # load HEJ - t_HEJ_DIR=${PWD} - t_HEJ_INSTALL_DIR=${PWD}/${HEJ_INSTALL_DIR} - export LD_LIBRARY_PATH=${t_HEJ_INSTALL_DIR}/lib:${LD_LIBRARY_PATH} - export PATH=${t_HEJ_INSTALL_DIR}/bin:${PATH} - cd ${HEJ_BUILD_DIR} - ${CMAKE} ${t_HEJ_DIR} # rerun cmake to create all test configure files script: - ${CTEST} --output-on-failure -j $(nproc --ignore=1) artifacts: *artifacts_failed .HEJrivet_test: extends: .HEJ_test script: - ${CTEST} --output-on-failure -j $(nproc --ignore=1) - bash -c '[ -f t/tst.yoda ]' && echo "found rivet output" - rivet-cmphistos t/tst.yoda - bash -c '[ -f MC_XS_XS.dat ]' && echo "yoda not empty" ## ----------- FOG build ----------- .FOG_build: tags: *tags_def rules: *rules_def stage: FOG:build before_script: - date - source /cvmfs/pheno.egi.eu/HEJ/HEJ_env.sh || exit 1 # load HEJ - t_HEJ_INSTALL_DIR=${PWD}/${HEJ_INSTALL_DIR} - export LD_LIBRARY_PATH=${t_HEJ_INSTALL_DIR}/lib:${LD_LIBRARY_PATH} - export PATH=${t_HEJ_INSTALL_DIR}/bin:${PATH} # prepare build - t_FOG_DIR=${PWD}/FixedOrderGen - t_FOG_INSTALL_DIR=${PWD}/${FOG_INSTALL_DIR} - t_FOG_BUILD_DIR=${PWD}/${FOG_BUILD_DIR} # hack to allow setting "variable in variable" - t_CMAKE_FLAGS="" - if [[ -n ${OVERWRITE_BOOST} ]]; then declare -n t_boost_root=${OVERWRITE_BOOST}; t_CMAKE_FLAGS="-DBOOST_ROOT=${t_boost_root}"; fi - mkdir -p ${t_FOG_BUILD_DIR} - cd ${t_FOG_BUILD_DIR} - ${CMAKE} ${t_FOG_DIR} -DCMAKE_BUILD_TYPE=DEBUG -DCMAKE_INSTALL_PREFIX=${t_FOG_INSTALL_DIR} ${t_CMAKE_FLAGS} script: - make -j $(nproc --ignore=1) - make install artifacts: # save build and installed folder name: FOG_build expire_in: 1d paths: - ${HEJ_INSTALL_DIR} - ${FOG_INSTALL_DIR} - ${FOG_BUILD_DIR} ## ----------- FOG test ----------- .FOG_test: tags: *tags_def rules: *rules_def stage: FOG:test before_script: - date - source /cvmfs/pheno.egi.eu/HEJ/HEJ_env.sh || exit 1 # load HEJ - t_FOG_DIR=${PWD}/FixedOrderGen - t_HEJ_INSTALL_DIR=${PWD}/${HEJ_INSTALL_DIR} - t_FOG_INSTALL_DIR=${PWD}/${FOG_INSTALL_DIR} - export LD_LIBRARY_PATH=${t_HEJ_INSTALL_DIR}/lib:${LD_LIBRARY_PATH} - export PATH=${t_HEJ_INSTALL_DIR}/bin:${t_FOG_INSTALL_DIR}/bin:${PATH} - t_FOG_BUILD_DIR=${PWD}/${FOG_BUILD_DIR} - cd ${t_FOG_BUILD_DIR} - ${CMAKE} ${t_FOG_DIR} # rerun cmake to create all test configure files script: - ${CTEST} --output-on-failure -j $(nproc --ignore=1) artifacts: *artifacts_failed # --------------------------------- # - Build & Test - # --------------------------------- # ----------- basic (always run) ----------- build:basic: image: ${DOCKER_BASIC} extends: .HEJ_build rules: - when: on_success test:basic: image: ${DOCKER_BASIC} extends: .HEJ_test needs: - job: build:basic artifacts: true rules: - when: on_success FOG:build:basic: image: ${DOCKER_BASIC} extends: .FOG_build needs: - job: build:basic artifacts: true rules: - when: on_success FOG:test:basic: image: ${DOCKER_BASIC} extends: .FOG_test needs: - job: FOG:build:basic artifacts: true rules: - when: on_success # ----------- HighFive/hdf5 ----------- build:HighFive: image: ${DOCKER_HIGHFIVE} extends: .HEJ_build variables: CMAKE: cmake3 test:HighFive: image: ${DOCKER_HIGHFIVE} extends: .HEJ_test variables: CMAKE: cmake3 CTEST: ctest3 needs: - job: build:HighFive artifacts: true # ----------- QCDloop ----------- build:qcdloop: image: ${DOCKER_QCDLOOP} extends: .HEJ_build test:qcdloop: image: ${DOCKER_QCDLOOP} extends: .HEJ_test needs: - job: build:qcdloop artifacts: true # ----------- rivet3 & HepMC3 ----------- build:rivet3: image: ${DOCKER_RIVET3} extends: .HEJ_build variables: OVERWRITE_BOOST: boost_ROOT_DIR test:rivet3: image: ${DOCKER_RIVET3} extends: .HEJrivet_test needs: - job: build:rivet3 artifacts: true # ----------- HepMC 3 ----------- # HepMC 3 already in rivet3 build:hepmc3: image: ${DOCKER_HEPMC3} extends: .HEJ_build variables: CMAKE: cmake3 test:hepmc3: image: ${DOCKER_HEPMC3} extends: .HEJ_test variables: CMAKE: cmake3 CTEST: ctest3 needs: - job: build:hepmc3 artifacts: true # ----------- rivet ----------- # rivet2 already in qcdloop build:rivet: image: ${DOCKER_RIVET} extends: .HEJ_build test:rivet: image: ${DOCKER_RIVET} extends: .HEJrivet_test needs: - job: build:rivet artifacts: true # ----------- rivet3 & HepMC2 ----------- # This shouldn't change too often build:rivet3h2: image: ${DOCKER_RIVET3H2} extends: .HEJ_build variables: OVERWRITE_BOOST: boost_ROOT_DIR test:rivet3h2: image: ${DOCKER_RIVET3H2} extends: .HEJrivet_test needs: - job: build:rivet3h2 artifacts: true # --------------------------------- # - Clean Code - # --------------------------------- No_tabs: stage: clean_code tags: *tags_def rules: *rules_def image: hejdock/git needs: [] # can run immediately script: - date - check_tabs artifacts: # don't save anything # ----------- Template no compiler warnings ----------- .Warning_build: extends: .HEJ_build stage: clean_code script: - ${CMAKE} ${t_HEJ_DIR} -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_BUILD_TYPE=RelWithDebInfo - make -j $(nproc --ignore=1) - make -j $(nproc --ignore=1) install artifacts: # don't save anything needs: [] # can run immediately .Warning_FOG: extends: .FOG_build stage: FOG:clear_code script: - ${CMAKE} ${t_FOG_DIR} -DCMAKE_CXX_FLAGS="-Werror" ${t_CMAKE_FLAGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo - make -j $(nproc --ignore=1) - make -j $(nproc --ignore=1) install artifacts: # don't save anything # ----------- No gcc warnings ----------- No_Warning:basic: image: ${DOCKER_BASIC} extends: .Warning_build + artifacts: + # save build and installed folder (needed for Long_test:FOG) + name: build + expire_in: 1d + paths: + - ${HEJ_INSTALL_DIR} + - ${HEJ_BUILD_DIR} No_Warning:basic:FOG: image: ${DOCKER_BASIC} extends: .Warning_FOG needs: - job: build:basic artifacts: true # ----------- No clang warnings ----------- No_Warning:clang: image: ${DOCKER_CLANG} extends: .Warning_build variables: OVERWRITE_BOOST: boost_ROOT_DIR artifacts: # save installed folder name: build expire_in: 1d paths: - ${HEJ_INSTALL_DIR} No_Warning:clang:FOG: image: ${DOCKER_CLANG} extends: .Warning_FOG needs: - job: No_Warning:clang artifacts: true variables: OVERWRITE_BOOST: boost_ROOT_DIR # --------------------------------- # - Long tests - # --------------------------------- .HEJ_test_long: extends: .HEJ_build stage: long_test script: - ${CMAKE} ${t_HEJ_DIR} -DCMAKE_BUILD_TYPE=RelWithDebInfo ${t_CMAKE_FLAGS} -DTEST_ALL=TRUE - make -j $(nproc --ignore=1) install - ${CTEST} --output-on-failure -j $(nproc --ignore=1) needs: [] # can run immediately +.FOG_test_long: + extends: .FOG_build + stage: long_test + script: + - ${CMAKE} ${t_FOG_DIR} -DCMAKE_BUILD_TYPE=RelWithDebInfo ${t_CMAKE_FLAGS} + -DTEST_ALL=TRUE + - make -j $(nproc --ignore=1) install + - ${CTEST} --output-on-failure -j $(nproc --ignore=1) + # ----------- QCDloop ----------- Long_test:qcdloop: image: ${DOCKER_QCDLOOP} extends: .HEJ_test_long +# ----------- Basic ----------- + +Long_test:FOG: + image: ${DOCKER_BASIC} + extends: .FOG_test_long + needs: + - job: No_Warning:basic + artifacts: true + # --------------------------------- # - Publish - # --------------------------------- Publish_version: stage: publish image: hejdock/git dependencies: [] artifacts: # don't save anything rules: - if: '$CI_COMMIT_BRANCH != null && $CI_COMMIT_BRANCH =~ /^v\d+\.\d+$/' when: on_success before_script: - mkdir -p .ssh/ && echo "${SSH_KEY}" > .ssh/id_rsa && chmod 0600 .ssh/id_rsa - rm -rf ~/.ssh/id_rsa; mkdir -p ~/.ssh/ - ln -s $PWD/.ssh/id_rsa ~/.ssh/id_rsa && chmod 0600 ~/.ssh/id_rsa - ssh -T ${PUBLIC_GIT_PREFIX} -o "StrictHostKeyChecking no" || echo "added ssh" script: - git remote add public ${PUBLIC_GIT_PREFIX}${PUBLIC_GIT_POSTFIX} - git checkout $CI_COMMIT_REF_NAME - git branch - git pull - git push public - git push public --tags after_script: - rm -f /root/.ssh/id_rsa && rm -fr .ssh - git remote rm public diff --git a/CMakeLists.txt b/CMakeLists.txt index b40d944..f91fc7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,274 +1,305 @@ 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) +## Check if this is a git folder +find_package(Git QUIET) +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + set(_is_git TRUE) +endif() + +## download FORM submodule +# from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html +if(_is_git) + option(GIT_SUBMODULE "Check submodules during build" ON) + if(GIT_SUBMODULE + AND NOT EXISTS "${PROJECT_SOURCE_DIR}/current_generator/form/Makefile.am") + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() +endif() + +if(NOT EXISTS "${PROJECT_SOURCE_DIR}/current_generator/form/Makefile.am") + message(FATAL_ERROR "FORM was not downloaded! Please update git-submodules and try again.") +endif() + ## 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 +if(_is_git) + execute_process( + COMMAND ${GIT_EXECUTABLE} 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_EXECUTABLE} rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE PROJECT_GIT_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + set(PROJECT_GIT_REVISION "UNKNOWN") + set(PROJECT_GIT_BRANCH "UNKNOWN") +endif() ## 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(CLHEP 2.3 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 $ $ $ ) - +# Add test coverage 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 9fa53fb..2c8667e 100644 --- a/Changes-API.md +++ b/Changes-API.md @@ -1,113 +1,116 @@ # 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 + - `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` - Only supports 1-boson or `WpWp` events. - In the case of WpWp same-flavour the reconstruction will minimise the total off-shell momentum. * 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 + produced by `HEJ` according to the `soft pt regulator` 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 `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`) +* Renamed `Config::max_ext_soft_pt_fraction` to `Config::soft_pt_regulator`. + The new `Config::soft_pt_regulator` parameter is optional and the old + `Config::max_ext_soft_pt_fraction` is **deprecated**. * 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 8fee3a0..790bbc3 100644 --- a/Changes.md +++ b/Changes.md @@ -1,142 +1,145 @@ # Changelog This is the log for changes to the HEJ program. Further changes to the HEJ API are documented in [`Changes-API.md`](Changes-API.md). If you are using HEJ as a library, please also read the changes there. ## Version 2.1 This release adds support for three new processes: * W boson with jets. * Wp+Wp 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 WpWp with jets, including interference between configurations. * 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. +* Use relative fraction for soft transverse momentum in tagging jets (`soft pt + regulator`) as new (optional) parameter. + - This supersedes `min extparton pt`, which is marked **deprecated** and will + be removed in future versions. + - This is a direct replacement for the old `max ext soft pt fraction`, which + is also **deprecated**. #### 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 + - `HEJFOG`: decays are now specified in `decays` setting (previously under `particle properties`) * Allow loading multiple analyses with `analyses`. The old `analysis` (with "i") - is marked deprecated. + is marked **deprecated**. * Optional setting to specify maximal number of Fixed Order events (`max events`, default is all) * Allow changing the regulator lambda in input (`regulator parameter`, only for advanced users) #### Changes to Input/Output * Added support to read & write `hdf5` event files suggested in [arXiv:1905.05120](https://arxiv.org/abs/1905.05120) (needs [HighFive](https://github.com/BlueBrain/HighFive)) * Support input with average weight equal to the cross section (`IDWTUP=1 or 4`) * Support unmodified Les Houches Event Files written by Sherpa with `cross section = sum(weights)/sum(trials)` * Analyses now get general run information (`LHEF::HEPRUP`) in the constructor. **This might break previously written, external analyses!** - external analyses should now be created with `make_analysis(YAML::Node const & config, LHEF::HEPRUP const & heprup)` * Support `rivet` version 3 with both `HepMC` version 2 and 3 - Multiple weights with `rivet 3` will only create one `.yoda` file (instead of one per weight/scale) * Added option to unweight only resummation events (`unweight: {type: resummation}`) * Added option for partially unweighting resummation events, similar to the fixed-order generator. * Improved unweighting algorithm. * Follow HepMC convention for particle Status codes: incoming = 11, decaying = 2, outgoing = 1 (unchanged) #### Miscellaneous * Print cross sections at end of run * Added example analysis & scale to `examples/`. Everything in `examples/` will be build when the flag `-DBUILD_EXAMPLES=TRUE` is set in `cmake`. * Dropped support for HepMC 3.0.0, either HepMC version 2 or >3.1 is required - It is now possible to write out both HepMC 2 and HepMC 3 events at the same time * Require LHADPF version 6. Dropped support for all other versions. * Use `git-lfs` for raw data in test (`make test` now requires `git-lfs`) * Currents are now generated with [`FORM`](https://github.com/vermaseren/form) - `FORM` is included as a `git submodule`, use `git submodule update --init` to download `FORM` * Create [Sphinx](http://sphinx-doc.org/) and [Doxygen](http://doxygen.org/) documentation by `make sphinx` or `make doxygen` in your `build/` folder ## Version 2.0 First release of HEJ 2. Complete code rewrite compared to HEJ 1. Improved matching to Fixed Order ([arXiv:1805.04446](https://arxiv.org/abs/1805.04446)). Implemented processes: Higgs boson with jets (FKL and unordered gluon emission, with finite quark mass loop, [arXiv:1812.08072](https://arxiv.org/abs/1812.08072)), and pure jets (only FKL). See [arXiv:1902.08430](https://arxiv.org/abs/1902.08430) ### 2.0.6 * Fixed compiling rivet when YODA headers are _outside_ of rivet directory ### 2.0.5 * Fixed event classification for input not ordered in rapidity ### 2.0.4 * Fixed wrong path of `HEJ_INCLUDE_DIR` in `hej-config.cmake` ### 2.0.3 * Fixed parsing of (numerical factor) * (base scale) in configuration * Don't change scale names, but sanitise Rivet output file names instead ### 2.0.2 * Changed scale names to `"_over_"` and `"_times_"` for proper file names (was `"/"` and `"*"` before) ### 2.0.1 * Fixed name of fixed-order generator in error message. ### 2.0.0 * First release diff --git a/FixedOrderGen/CMakeLists.txt b/FixedOrderGen/CMakeLists.txt index 8736ae3..992f3ba 100644 --- a/FixedOrderGen/CMakeLists.txt +++ b/FixedOrderGen/CMakeLists.txt @@ -1,106 +1,106 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) set(CMAKE_LEGACY_CYGWIN_WIN32 0) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project("HEJ Fixed Order Generation" VERSION 2.0.6 LANGUAGES C CXX) +## User settings +include(CMakeDependentOption) + +option(TEST_ALL "Run additional (longer) tests" FALSE) +option(RUN_CLANG_TIDY "Run clang tidy" FALSE) +option(TEST_COVERAGE "Generate test coverage with \"gcovr\"" 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 ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Templates/Version.hh.in include/Version.hh @ONLY ) ## Use cmake modules from HEJ src set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/../cmake/Modules/") ## Find HEJ (only dependency) ## HEJ includes all sub dependencies (fastjet, lhapdf, ...) find_package(HEJ 2 REQUIRED) include(RepairTargets) # more reliable cmake targets -## define executable -file(GLOB HEJFOG_source ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc) -list(REMOVE_ITEM HEJFOG_source ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc) -add_library(hejfog_lib STATIC ${HEJFOG_source}) -target_include_directories(hejfog_lib - PUBLIC - ${PROJECT_SOURCE_DIR}/include - ${PROJECT_BINARY_DIR}/include -) -add_executable(HEJFOG ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc) - -## link libraries -set(libraries ${CMAKE_DL_LIBS}) +# Add test coverage +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}/t/*" + "${PROJECT_BINARY_DIR}/*") + SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML( + NAME ctest_coverage + EXECUTABLE ctest + ) +endif() -target_link_libraries(hejfog_lib ${libraries} HEJ::HEJ) +## define executable +add_subdirectory(src) +add_executable(HEJFOG src/bin/main.cc) target_link_libraries(HEJFOG hejfog_lib) install(TARGETS HEJFOG DESTINATION bin) ## tests include(CTest) ## advanced version of enable_testing() ## adds "BUILD_TESTING" option to deactivate testing if(BUILD_TESTING) - set(tst_dir "${CMAKE_CURRENT_SOURCE_DIR}/t") - foreach(tst h_2j h_3j h_5j h_3j_uno1 h_3j_uno2 h_2j_decay 2j 4j W_reconstruct_enu - W_2j_classify W_nj_classify) - add_executable(test_${tst} ${tst_dir}/${tst}.cc) - target_link_libraries(test_${tst} hejfog_lib) - add_test(NAME ${tst} COMMAND test_${tst} WORKING_DIRECTORY ${tst_dir}) - endforeach() - # this only tests if the runcard actually works, not if the result is correct - add_test( - NAME main_2j - COMMAND HEJFOG ${tst_dir}/config_2j.yml - ) - add_test( - NAME main_h2j - COMMAND HEJFOG ${tst_dir}/config_h_2j.yml - ) - add_test( - NAME main_h2j_decay - COMMAND HEJFOG ${tst_dir}/config_h_2j_decay.yml - ) - add_test( - NAME peakpt - COMMAND HEJFOG ${tst_dir}/config_2j_peak.yml - ) + add_subdirectory(t) +endif() + +## 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( + hejfog_lib + PROPERTIES CXX_CLANG_TIDY + "${CLANG_TIDY_EXE};-header-filter=${CMAKE_SOURCE_DIR}/include;-fix;--fix-errors" ) + endif() endif() diff --git a/FixedOrderGen/configFO.yml b/FixedOrderGen/configFO.yml index 6fca80b..7630ec5 100644 --- a/FixedOrderGen/configFO.yml +++ b/FixedOrderGen/configFO.yml @@ -1,94 +1,95 @@ ## Number of generated events events: 200 jets: min pt: 20 # minimal jet pt, should be slightly below analysis cut peak pt: 30 # peak pt of jets, should be at analysis cut algorithm: antikt # jet clustering algorithm R: 0.4 # jet R parameter max rapidity: 5 # maximum jet rapidity ## Particle beam beam: energy: 6500 particles: [p, p] ## PDF ID according to LHAPDF pdf: 230000 ## Scattering process -process: p p => h 4j +process: p p => Wp 4j ## Particle decays (multiple decays are allowed) decays: Higgs: {into: [photon, photon], branching ratio: 0.0023568762400521404} Wp: {into: [e+, nu_e], branching ratio: 1} Wm: {into: [e-, nu_e_bar]} # equivalent to "branching ratio: 1" ## Fraction of events with two extremal emissions in one direction ## that contain an subleading emission e.g. unordered emission subleading fraction: 0.05 ## Allow different subleading configurations. ## By default all processes are allowed. ## This does not check if the processes are implemented in HEJ! # # subleading channels: # - unordered -# - qqx +# - central qqx +# - extremal qqx ## Unweighting parameters ## remove to obtain weighted events # unweight: # sample size: 200 # should be similar to "events:", but not more than ~10000 # max deviation: 0 ## --- The following settings are equivalent to HEJ, --- ## ## --- see HEJ for reference. --- ## ## Central scale choice scales: max jet pperp ## Event output files event output: - HEJFO.lhe # - HEJFO_events.hepmc ## Analyses # # analyses: ## Rivet analysis # - rivet: MC_XS # rivet analysis name # output: HEJ # name of the yoda files, ".yoda" and scale suffix will be added ## Custom analysis # - plugin: /path/to/libmyanalysis.so # my analysis parameter: some value ## Selection of random number generator and seed random generator: name: mixmax # seed: 1 ## Vacuum expectation value vev: 246.2196508 ## Properties of the weak gauge bosons particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 ## Parameters for Higgs-gluon couplings ## this requires compilation with QCDloop # # Higgs coupling: # use impact factors: false # mt: 174 # include bottom: true # mb: 4.7 diff --git a/FixedOrderGen/include/Beam.hh b/FixedOrderGen/include/Beam.hh index 4054083..57b55fe 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 { struct Beam{ - double energy; + double energy{}; std::array particles{{ HEJ::pid::proton, HEJ::pid::proton }}; }; } diff --git a/FixedOrderGen/include/Decay.hh b/FixedOrderGen/include/Decay.hh index 2c92262..9d97c9e 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 { struct Decay{ std::vector products; - double branching_ratio; + 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 -} +} // namespace HEJFOG diff --git a/FixedOrderGen/include/EventGenerator.hh b/FixedOrderGen/include/EventGenerator.hh index 680deec..be6cb31 100644 --- a/FixedOrderGen/include/EventGenerator.hh +++ b/FixedOrderGen/include/EventGenerator.hh @@ -1,67 +1,68 @@ /** * \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 "HEJ/optional.hh" #include "Beam.hh" #include "Decay.hh" #include "JetParameters.hh" #include "Process.hh" #include "Status.hh" +#include "Subleading.hh" namespace HEJ { class Event; class HiggsCouplingSettings; class RNG; } //! Namespace for HEJ Fixed Order Generator 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, + double subl_chance, + Subleading 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_; + double subl_chance_; + Subleading subl_channels_; ParticlesDecayMap particle_decays_; HEJ::EWConstants ew_parameters_; std::shared_ptr ran_; }; -} +} // namespace HEJFOG diff --git a/FixedOrderGen/include/JetParameters.hh b/FixedOrderGen/include/JetParameters.hh index 6ea2711..f648d2c 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 { struct JetParameters{ fastjet::JetDefinition def; - double min_pt; - double max_y; + double min_pt{}; + double max_y{}; HEJ::optional peak_pt; }; } diff --git a/FixedOrderGen/include/PhaseSpacePoint.hh b/FixedOrderGen/include/PhaseSpacePoint.hh index e88e82a..9c01895 100644 --- a/FixedOrderGen/include/PhaseSpacePoint.hh +++ b/FixedOrderGen/include/PhaseSpacePoint.hh @@ -1,238 +1,296 @@ /** \file PhaseSpacePoint.hh * \brief Contains the PhaseSpacePoint Class * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #pragma once #include #include #include #include #include +#include "boost/iterator/filter_iterator.hpp" + #include "HEJ/Event.hh" -#include "HEJ/Particle.hh" #include "HEJ/PDG_codes.hh" +#include "HEJ/Particle.hh" #include "fastjet/PseudoJet.hh" #include "Decay.hh" #include "Status.hh" +#include "Subleading.hh" namespace HEJ { class EWConstants; class PDF; class RNG; } namespace HEJFOG { - class JetParameters; - class Process; + struct JetParameters; + struct Process; //! A point in resummation phase space class PhaseSpacePoint{ public: + + //! Iterator over partons + using ConstPartonIterator = boost::filter_iterator< + bool (*)(HEJ::Particle const &), + std::vector::const_iterator + >; + //! Reverse Iterator over partons + using ConstReversePartonIterator = std::reverse_iterator< + ConstPartonIterator>; + //! Default PhaseSpacePoint Constructor PhaseSpacePoint() = delete; //! PhaseSpacePoint Constructor /** * @param proc The process to generate - * @param jet_properties Jet defintion & cuts + * @param jet_param Jet defintion & cuts * @param pdf The pdf set (used for sampling) * @param E_beam Energie of the beam * @param subl_chance Chance to turn a potentially unordered * emission into an actual one * @param subl_channels Possible subleading channels. * see HEJFOG::Subleading * @param particle_properties Properties of producted boson * * Initially, only FKL phase space points are generated. subl_chance gives - * the change of turning one emissions into a subleading configuration, + * the chance of turning one emissions into a subleading configuration, * i.e. either unordered or central quark/anti-quark pair. Unordered * emissions require that the most extremal emission in any direction is * a quark or anti-quark and the next emission is a gluon. Quark/anti-quark * pairs are only generated for W processes. At most one subleading * emission will be generated in this way. */ PhaseSpacePoint( Process const & proc, - JetParameters const & jet_properties, + JetParameters const & jet_param, HEJ::PDF & pdf, double E_beam, double subl_chance, - unsigned int subl_channels, + Subleading subl_channels, ParticlesDecayMap const & particle_decays, HEJ::EWConstants const & ew_parameters, HEJ::RNG & ran ); //! Get Weight Function /** * @returns Weight of Event */ double weight() const{ return weight_; } Status status() const{ return status_; } //! Get Incoming Function /** * @returns Incoming Particles */ std::array const & incoming() const{ return incoming_; } //! Get Outgoing Function /** * @returns Outgoing Particles */ std::vector const & outgoing() const{ return outgoing_; } std::unordered_map> const & decays() const{ return decays_; } + //! Iterator to the first outgoing parton + ConstPartonIterator begin_partons() const; + //! Iterator to the first outgoing parton + ConstPartonIterator cbegin_partons() const; + + //! Iterator to the end of the outgoing partons + ConstPartonIterator end_partons() const; + //! Iterator to the end of the outgoing partons + ConstPartonIterator cend_partons() const; + + //! Reverse Iterator to the first outgoing parton + ConstReversePartonIterator rbegin_partons() const; + //! Reverse Iterator to the first outgoing parton + ConstReversePartonIterator crbegin_partons() const; + //! Reverse Iterator to the first outgoing parton + ConstReversePartonIterator rend_partons() const; + //! Reverse Iterator to the first outgoing parton + ConstReversePartonIterator crend_partons() const; + + private: + static constexpr std::size_t NUM_PARTONS = 11; //!< Number of partons + public: + using part_mask = std::bitset; //!< Selection mask for partons private: friend HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); /** * @internal * @brief Generate LO parton momentum * - * @param count Number of partons to generate + * @param np Number of partons to generate * @param is_pure_jets If true ensures momentum conservation in x and y * @param jet_param Jet properties to fulfil * @param max_pt max allowed pt for a parton (typically E_CMS) * @param ran Random Number Generator * * @returns Momentum of partons * * Ensures that each parton is in its own jet. * Generation is independent of parton flavour. Output is sorted in rapidity. */ std::vector gen_LO_partons( - int count, bool is_pure_jets, + int np, bool is_pure_jets, JetParameters const & jet_param, double max_pt, HEJ::RNG & ran ); HEJ::Particle gen_boson( HEJ::ParticleID bosonid, double mass, double width, HEJ::RNG & ran ); template fastjet::PseudoJet gen_last_momentum( ParticleMomenta const & other_momenta, double mass_square, double y ) const; bool jets_ok( std::vector const & Born_jets, std::vector const & partons ) const; /** * @internal * @brief Generate incoming partons according to the PDF * * @param uf Scale used in the PDF */ void reconstruct_incoming( - Process const & proc, unsigned int subl_channels, + Process const & proc, + double subl_chance, Subleading 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 + part_mask allowed_partons, HEJ::RNG & ran + ); + //! @brief Returns list of all allowed initial states partons + std::array allowed_incoming( + Process const & proc, + double subl_chance, Subleading subl_channels, + HEJ::RNG & ran ); + //! Ensure that incoming state compatible with A/W/Z production + std::array incoming_AWZ( + Process const & proc, Subleading subl_channels, + std::array allowed_partons, + HEJ::RNG & ran + ); + //! Ensure that incoming state compatible with extremal qqx + std::array incoming_eqqx( + std::array allowed_partons, HEJ::RNG & ran); + //! Ensure that incoming state compatible with unordered + std::array incoming_uno( + std::array allowed_partons, HEJ::RNG & ran); + bool momentum_conserved(double ep) const; - 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 chance Chance to switch to subleading configuration * @param channels Allowed channels for subleading process * @param proc Process to decide which subleading * configurations are allowed * * With a chance of "chance" the FKL configuration is either turned into * a unordered configuration or, for A/W/Z bosons, a configuration with * a central quark/anti-quark pair. */ - void maybe_turn_to_subl(double chance, unsigned int channels, + void maybe_turn_to_subl(double chance, Subleading channels, Process const & proc, HEJ::RNG & ran); + void turn_to_subl( + Subleading channels, + bool can_be_uno_backward, bool can_be_uno_forward, bool allow_strange, + HEJ::RNG & ran); void turn_to_uno(bool can_be_uno_backward, bool can_be_uno_forward, HEJ::RNG & ran); - void turn_to_qqx(bool allow_strange, HEJ::RNG & ran); + HEJ::ParticleID select_qqx_flavour(bool allow_strange, HEJ::RNG & ran); + void turn_to_cqqx(bool allow_strange, HEJ::RNG & ran); + void turn_to_eqqx(bool allow_strange, HEJ::RNG & ran); //! decay where we select the decay channel - std::vector decay_boson( + std::vector decay_channel( 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_soft_pt(int np, double max_pt, HEJ::RNG & ran); double gen_parton_pt( - int count, JetParameters const & jet_param, double ptmax, double y, + int count, JetParameters const & jet_param, double max_pt, double y, HEJ::RNG & ran ); + //! Iterator over partons (non-const) + using PartonIterator = boost::filter_iterator< + bool (*)(HEJ::Particle const &), + std::vector::iterator + >; + //! Reverse Iterator over partons (non-const) + using ReversePartonIterator = std::reverse_iterator; + + //! Iterator to the first outgoing parton (non-const) + PartonIterator begin_partons(); + //! Iterator to the end of the outgoing partons (non-const) + PartonIterator end_partons(); + + //! Reverse Iterator to the first outgoing parton (non-const) + ReversePartonIterator rbegin_partons(); + //! Reverse Iterator to the first outgoing parton (non-const) + ReversePartonIterator rend_partons(); + double weight_; Status status_; std::array incoming_; std::vector outgoing_; //! Particle decays in the format {outgoing index, decay products} std::unordered_map> decays_; }; //! Extract HEJ::Event::EventData from PhaseSpacePoint HEJ::Event::EventData to_EventData(PhaseSpacePoint psp); -} +} // namespace HEJFOG diff --git a/FixedOrderGen/include/Process.hh b/FixedOrderGen/include/Process.hh index aebea2f..e6c950c 100644 --- a/FixedOrderGen/include/Process.hh +++ b/FixedOrderGen/include/Process.hh @@ -1,22 +1,23 @@ /** * \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 { struct Process{ - std::array incoming; - int njets; + //! @internal pair to match Event::incoming + std::array incoming{}; + std::size_t njets{}; HEJ::optional boson; std::vector boson_decay; }; } diff --git a/FixedOrderGen/include/Status.hh b/FixedOrderGen/include/Status.hh index 0b5e926..515a702 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 +#include namespace HEJFOG { - enum Status{ + enum class 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"; + case Status::good: return "good"; + case Status::not_enough_jets: return "not enough jets"; + case Status::too_much_energy: return "too much energy"; default:; } throw std::logic_error{"unreachable"}; } -} +} // namespace HEJFOG diff --git a/FixedOrderGen/include/Subleading.hh b/FixedOrderGen/include/Subleading.hh index ef76017..f86935a 100644 --- a/FixedOrderGen/include/Subleading.hh +++ b/FixedOrderGen/include/Subleading.hh @@ -1,24 +1,34 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019 * \copyright GPLv2 or later */ #pragma once +#include + namespace HEJFOG { - namespace channels{ + namespace subleading { //!< @TODO confusing name with capital Subleading /** * Bit position of different subleading channels * e.g. (unsigned int) 1 => only unordered */ - enum Subleading: unsigned { - none = 0u, - all = ~0u, - uno = 1u, + enum Channels: unsigned { + uno, unordered = uno, - qqx = 2u + cqqx, + central_qqx = cqqx, + eqqx, + extremal_qqx = eqqx, + first = uno, + last = eqqx, }; - } + } // namespace subleading - using Subleading = channels::Subleading; -} + using Subleading = std::bitset; + + namespace subleading { + static constexpr Subleading ALL{~0u}; + static constexpr Subleading NONE{0u}; + } +} // namespace HEJFOG diff --git a/FixedOrderGen/include/config.hh b/FixedOrderGen/include/config.hh index 212f059..c40fb6b 100644 --- a/FixedOrderGen/include/config.hh +++ b/FixedOrderGen/include/config.hh @@ -1,49 +1,50 @@ /** * \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 "Subleading.hh" #include "UnweightSettings.hh" namespace HEJFOG { struct Config{ Process process; - std::size_t events; + std::size_t events{}; JetParameters jets; Beam beam; - int pdf_id; - HEJ::Fraction subleading_fraction; - unsigned int subleading_channels; //! < see HEJFOG::Subleading + int pdf_id{}; + HEJ::Fraction subleading_fraction{}; + Subleading 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); -} +} // namespace HEJFOG diff --git a/FixedOrderGen/src/CMakeLists.txt b/FixedOrderGen/src/CMakeLists.txt new file mode 100644 index 0000000..b03336d --- /dev/null +++ b/FixedOrderGen/src/CMakeLists.txt @@ -0,0 +1,13 @@ +file(GLOB src_files *.cc) + +add_library(hejfog_lib STATIC ${src_files}) +target_include_directories(hejfog_lib + PUBLIC + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/include +) + +## link libraries +set(libraries ${CMAKE_DL_LIBS}) + +target_link_libraries(hejfog_lib ${libraries} HEJ::HEJ) diff --git a/FixedOrderGen/src/EventGenerator.cc b/FixedOrderGen/src/EventGenerator.cc index 84b03c7..0720491 100644 --- a/FixedOrderGen/src/EventGenerator.cc +++ b/FixedOrderGen/src/EventGenerator.cc @@ -1,99 +1,101 @@ /** * \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/EWConstants.hh" #include "HEJ/Event.hh" +#include "HEJ/HiggsCouplingSettings.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" +#include "Process.hh" namespace HEJFOG { EventGenerator::EventGenerator( Process process, Beam beam, HEJ::ScaleGenerator scale_gen, JetParameters jets, int pdf_id, - double subl_change, - unsigned int subl_channels, + double subl_chance, + Subleading 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}, + status_{Status::good}, + subl_chance_{subl_chance}, 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_, + subl_chance_, subl_channels_, particle_decays_, ew_parameters_, *ran_ }; status_ = psp.status(); - if(status_ != good) return {}; + if(status_ != 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())) + 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; } -} +} // namespace HEJFOG diff --git a/FixedOrderGen/src/PhaseSpacePoint.cc b/FixedOrderGen/src/PhaseSpacePoint.cc index 6169c52..f7071c3 100644 --- a/FixedOrderGen/src/PhaseSpacePoint.cc +++ b/FixedOrderGen/src/PhaseSpacePoint.cc @@ -1,701 +1,976 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "PhaseSpacePoint.hh" #include #include #include #include #include #include #include #include #include #include #include "fastjet/ClusterSequence.hh" #include "HEJ/Constants.hh" #include "HEJ/EWConstants.hh" -#include "HEJ/exceptions.hh" -#include "HEJ/kinematics.hh" -#include "HEJ/Particle.hh" #include "HEJ/PDF.hh" +#include "HEJ/Particle.hh" #include "HEJ/RNG.hh" +#include "HEJ/exceptions.hh" +#include "HEJ/kinematics.hh" #include "HEJ/utility.hh" #include "JetParameters.hh" #include "Process.hh" -#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 { - Event::EventData to_EventData(PhaseSpacePoint psp){ + HEJ::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_; + HEJ::Event::EventData result; + result.incoming = std::move(psp).incoming_; // NOLINT(bugprone-use-after-move) + result.outgoing = std::move(psp).outgoing_; // NOLINT(bugprone-use-after-move) // technically Event::EventData doesn't have to be sorted, // but PhaseSpacePoint should be anyway assert( std::is_sorted( begin(result.outgoing), end(result.outgoing), - rapidity_less{} + HEJ::rapidity_less{} ) ); assert(result.outgoing.size() >= 2); - result.decays = std::move(psp).decays_; - result.parameters.central = {NaN, NaN, psp.weight()}; + result.decays = std::move(psp).decays_; // NOLINT(bugprone-use-after-move) + static_assert( + std::numeric_limits::has_quiet_NaN, + "no quiet NaN for double" + ); + constexpr double nan = std::numeric_limits::quiet_NaN(); + result.parameters.central = {nan, nan, psp.weight()}; // NOLINT(bugprone-use-after-move) return result; } + PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::begin_partons() const { + return cbegin_partons(); + } + PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::cbegin_partons() const { + return {HEJ::is_parton, cbegin(outgoing()), cend(outgoing())}; + } + + PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::end_partons() const { + return cend_partons(); + } + PhaseSpacePoint::ConstPartonIterator PhaseSpacePoint::cend_partons() const { + return {HEJ::is_parton, cend(outgoing()), cend(outgoing())}; + } + + PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::rbegin_partons() const { + return crbegin_partons(); + } + PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::crbegin_partons() const { + return std::reverse_iterator( cend_partons() ); + } + + PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::rend_partons() const { + return crend_partons(); + } + PhaseSpacePoint::ConstReversePartonIterator PhaseSpacePoint::crend_partons() const { + return std::reverse_iterator( cbegin_partons() ); + } + + PhaseSpacePoint::PartonIterator PhaseSpacePoint::begin_partons() { + return {HEJ::is_parton, begin(outgoing_), end(outgoing_)}; + } + + PhaseSpacePoint::PartonIterator PhaseSpacePoint::end_partons() { + return {HEJ::is_parton, end(outgoing_), end(outgoing_)}; + } + + PhaseSpacePoint::ReversePartonIterator PhaseSpacePoint::rbegin_partons() { + return std::reverse_iterator( end_partons() ); + } + + PhaseSpacePoint::ReversePartonIterator PhaseSpacePoint::rend_partons() { + return std::reverse_iterator( begin_partons() ); + } + namespace { bool can_swap_to_uno( - Particle const & p1, Particle const & p2 + HEJ::Particle const & p1, HEJ::Particle const & p2 ){ - return is_parton(p1) - && p1.type != pid::gluon - && p2.type == pid::gluon; + assert(is_parton(p1) && is_parton(p2)); + return p1.type != HEJ::pid::gluon + && p2.type == HEJ::pid::gluon; } - size_t count_gluons(std::vector::const_iterator first, - std::vector::const_iterator last + size_t count_gluons(PhaseSpacePoint::ConstPartonIterator first, + PhaseSpacePoint::ConstPartonIterator last ){ - return std::count_if(first, last, [](Particle const & p) - {return p.type == pid::gluon;}); + return std::count_if(first, last, [](HEJ::Particle const & p) + {return p.type == HEJ::pid::gluon;}); } /** assumes FKL configurations between first and last, * else there can be a quark in a non-extreme position * e.g. uno configuration gqg would pass */ - bool can_change_to_qqx( - std::vector::const_iterator first, - std::vector::const_iterator last + Subleading possible_qqx( + PhaseSpacePoint::ConstPartonIterator first, + const PhaseSpacePoint::ConstReversePartonIterator& last + ){ + using namespace subleading; + assert( std::distance( first,last.base() )>2 ); + Subleading channels = ALL; + channels.reset(eqqx); + channels.reset(cqqx); + auto const ngluon = count_gluons(first,last.base()); + if(ngluon < 2) return channels; + if(first->type==HEJ::pid::gluon || last->type==HEJ::pid::gluon){ + channels.set(eqqx); + } + if(std::distance(first,last.base())>=4){ + channels.set(cqqx); + } + return channels; + } + + template + bool uno_possible( + PartonIt first_parton, OutIt first_out ){ - return 1 < count_gluons(first,last); + using namespace HEJ; + // Special case: Higgs can not be outside of uno + if(first_out->type == pid::Higgs + || std::next(first_out)->type==pid::Higgs){ + return false; + } + // decide what kind of subleading process is allowed + return can_swap_to_uno( *first_parton, *std::next(first_parton) ); } + bool is_AWZ_proccess(Process const & proc){ - return proc.boson && is_AWZ_boson(*proc.boson); + return proc.boson && HEJ::is_AWZ_boson(*proc.boson); } - bool is_up_type(Particle const & part){ - return is_anyquark(part) && !(std::abs(part.type)%2); + bool is_up_type(HEJ::Particle const & part){ + return is_anyquark(part) && ((std::abs(part.type)%2) == 0); } - bool is_down_type(Particle const & part){ - return is_anyquark(part) && std::abs(part.type)%2; + bool is_down_type(HEJ::Particle const & part){ + return is_anyquark(part) && ((std::abs(part.type)%2) != 0); } - bool can_couple_to_W(Particle const & part, pid::ParticleID const W_id){ + bool can_couple_to_W( + HEJ::Particle const & part, HEJ::pid::ParticleID const W_id + ){ const int W_charge = W_id>0?1:-1; - return std::abs(part.type)<5 + return std::abs(part.type) 0 && is_up_type(part)) || (W_charge*part.type < 0 && is_down_type(part)) ); } + + Subleading ensure_AWZ( + double & subl_chance, bool & allow_strange, + HEJ::ParticleID const boson, + PhaseSpacePoint::ConstPartonIterator first_parton, + PhaseSpacePoint::ConstPartonIterator last_parton + ){ + auto channels = subleading::ALL; + if(std::none_of(first_parton, last_parton, + [&boson](HEJ::Particle const & p){ + return can_couple_to_W(p, boson);})) { + // enforce qqx if A/W/Z can't couple somewhere else + // this is ensured to work through filter_partons in reconstruct_incoming + channels.reset(subleading::uno); + assert(channels.any()); + subl_chance = 1.; + // strange not allowed for W + if(std::abs(boson)== HEJ::pid::Wp) allow_strange = false; + } + return channels; + } + } // namespace + + void PhaseSpacePoint::turn_to_subl( + Subleading const channels, + bool const can_be_uno_backward, bool const can_be_uno_forward, + bool const allow_strange, + HEJ::RNG & ran + ){ + double const nchannels = channels.count(); + double const step = 1./nchannels; + weight_*=nchannels; + unsigned selected = subleading::first; + double rnd = nchannels>1?ran.flat():0.; + // @TODO optimise this sampling + for(; selected<=subleading::last; ++selected){ + assert(rnd>=0); + if(channels[selected]){ + if(rnd= 2); // decide what kind of subleading process is allowed - bool allow_uno = false; + bool const can_be_uno_backward = uno_possible(cbegin_partons(), + outgoing_.cbegin()); + bool const can_be_uno_forward = uno_possible(crbegin_partons(), + outgoing_.crbegin()); + if(channels[subleading::uno]){ + channels.set(subleading::uno, can_be_uno_backward || can_be_uno_forward); + } + channels &= possible_qqx(cbegin_partons(), crbegin_partons()); + 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; - } + channels &= ensure_AWZ(chance, allow_strange, *proc.boson, + cbegin_partons(), cend_partons()); } - 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; + std::size_t const nchannels = channels.count(); + // no subleading + if(nchannels==0) return; + if(ran.flat() >= chance){ + weight_ /= 1 - chance; + return; + } + weight_ /= chance; + // select channel + return turn_to_subl( channels, can_be_uno_backward, can_be_uno_forward, + allow_strange, ran); } void PhaseSpacePoint::turn_to_uno( const bool can_be_uno_backward, const bool can_be_uno_forward, - RNG & ran + HEJ::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){ + weight_ *= 2.; if(ran.flat() < 0.5){ - std::swap(outgoing_[0].type, outgoing_[1].type); - } else { - std::swap(outgoing_[nout-1].type, outgoing_[nout-2].type); + return std::swap(begin_partons()->type, std::next(begin_partons())->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); + return std::swap(rbegin_partons()->type, std::next(rbegin_partons())->type); + } + if(can_be_uno_backward){ + return std::swap(begin_partons()->type, std::next(begin_partons())->type); } + assert(can_be_uno_forward); + std::swap(rbegin_partons()->type, std::next(rbegin_partons())->type); } - 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 + //! select flavour of quark + HEJ::ParticleID PhaseSpacePoint::select_qqx_flavour( + const bool allow_strange, HEJ::RNG & ran + ){ const double r1 = 2.*ran.flat()-1.; - const double max_flavour = allow_strange?N_F:N_F-1; + const double max_flavour = allow_strange?HEJ::N_F:HEJ::N_F-1; weight_ *= max_flavour*2; - int flavour = pid::down + std::floor(std::abs(r1)*max_flavour); - flavour*=r1<0.?-1:1; + double const flavour = HEJ::pid::down + std::floor(std::abs(r1)*max_flavour); + return static_cast(flavour*(r1<0.?-1:1)); + } + + void PhaseSpacePoint::turn_to_cqqx(const bool allow_strange, HEJ::RNG & ran){ + // we assume all FKL partons to be gluons + auto first = std::next(begin_partons()); + auto last = std::next(rbegin_partons()); + auto const ng = std::distance(first, last.base()); + if(ng < 2) + throw std::logic_error("not enough gluons to create qqx"); + auto flavour = select_qqx_flavour(allow_strange, ran); // 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); + if(ng!=2){ + const double steps = 1./(ng-1.); + weight_ /= steps; + for(auto rnd = ran.flat(); rnd>steps; ++first){ + rnd-=steps; + } + } + first->type = flavour; + std::next(first)->type = anti(flavour); + } + + void PhaseSpacePoint::turn_to_eqqx(const bool allow_strange, HEJ::RNG & ran){ + /// find first and last gluon in FKL chain + auto first = begin_partons(); + const bool can_forward = !is_anyquark(*first); + auto last = rbegin_partons(); + const bool can_backward = !is_anyquark(*last); + if(std::distance(first, last.base()) < 2) + throw std::logic_error("not enough gluons to create qqx"); + auto flavour = select_qqx_flavour(allow_strange, ran); + // select gluon for switch + if(can_forward && !can_backward){ + first->type = flavour; + std::next(first)->type = anti(flavour); + return; + } + if(!can_forward && can_backward){ + last->type = flavour; + std::next(last)->type = anti(flavour); + return; + } + assert(can_forward && can_backward); + weight_*=2.; + if(ran.flat()>0.5){ + first->type = flavour; + std::next(first)->type = anti(flavour); + return; + } + last->type = flavour; + std::next(last)->type = anti(flavour); } template fastjet::PseudoJet PhaseSpacePoint::gen_last_momentum( ParticleMomenta const & other_momenta, const double mass_square, const double y ) const { std::array pt{0.,0.}; for (auto const & p: other_momenta) { pt[0]-= p.px(); pt[1]-= p.py(); } const double mperp = std::sqrt(pt[0]*pt[0]+pt[1]*pt[1]+mass_square); const double pz=mperp*std::sinh(y); const double E=mperp*std::cosh(y); return {pt[0], pt[1], pz, E}; } + Decay PhaseSpacePoint::select_decay_channel( + std::vector const & decays, + HEJ::RNG & ran + ){ + double br_total = 0.; + for(auto const & decay: decays) br_total += decay.branching_ratio; + // adjust weight + // this is given by (channel branching ratio)/(chance to pick channel) + // where (chance to pick channel) = + // (channel branching ratio)/(total branching ratio) + weight_ *= br_total; + if(decays.size()==1) return decays.front(); + const double r1 = br_total*ran.flat(); + double br_sum = 0.; + for(auto const & decay: decays){ + br_sum += decay.branching_ratio; + if(r1 < br_sum) return decay; + } + throw std::logic_error{"unreachable"}; + } + namespace { + //! generate decay products of a boson + std::vector decay_boson( + HEJ::Particle const & parent, + std::vector const & decays, + HEJ::RNG & ran + ){ + if(decays.size() != 2){ + throw HEJ::not_implemented{ + "only decays into two particles are implemented" + }; + } + std::vector decay_products(decays.size()); + for(size_t i = 0; i < decays.size(); ++i){ + decay_products[i].type = decays[i]; + } + // choose polar and azimuth angle in parent rest frame + const double E = parent.m()/2; + const double theta = 2.*M_PI*ran.flat(); + const double cos_phi = 2.*ran.flat()-1.; // Jacobian Factors for W in line 418 + const double sin_phi = std::sqrt(1. - cos_phi*cos_phi); // Know 0 < phi < pi + const double px = E*std::cos(theta)*sin_phi; + const double py = E*std::sin(theta)*sin_phi; + const double pz = E*cos_phi; + decay_products[0].p.reset(px, py, pz, E); + decay_products[1].p.reset(-px, -py, -pz, E); + for(auto & particle: decay_products) particle.p.boost(parent.p); + return decay_products; + } + } // namespace + + std::vector PhaseSpacePoint::decay_channel( + HEJ::Particle const & parent, + std::vector const & decays, + HEJ::RNG & ran + ){ + const auto channel = select_decay_channel(decays, ran); + return decay_boson(parent, channel.products, ran); + } + namespace { //! adds a particle to target (in correct rapidity ordering) //! @returns positon of insertion - auto insert_particle(std::vector & target, - Particle && particle + auto insert_particle(std::vector & target, + HEJ::Particle && particle ){ const auto pos = std::upper_bound( - begin(target),end(target),particle,rapidity_less{} + begin(target),end(target),particle,HEJ::rapidity_less{} ); target.insert(pos, std::move(particle)); return pos; } - } + } // namespace PhaseSpacePoint::PhaseSpacePoint( Process const & proc, JetParameters const & jet_param, - PDF & pdf, double E_beam, + HEJ::PDF & pdf, double E_beam, double const subl_chance, - unsigned int const subl_channels, + Subleading subl_channels, ParticlesDecayMap const & particle_decays, - EWConstants const & ew_parameters, - RNG & ran + HEJ::EWConstants const & ew_parameters, + HEJ::RNG & ran ){ assert(proc.njets >= 2); - status_ = good; + status_ = Status::good; weight_ = 1; - const int nout = proc.njets + (proc.boson?1:0) + proc.boson_decay.size(); + // ensure that all setting are consistent + if(subl_chance == 0.) + subl_channels.reset(); + + const std::size_t nout = proc.njets + (proc.boson?1:0) + + proc.boson_decay.size(); outgoing_.reserve(nout); // generate parton momenta const bool is_pure_jets = (nout == proc.njets); auto partons = gen_LO_partons( proc.njets, is_pure_jets, jet_param, E_beam, ran ); // pre fill flavour with gluons - for(auto&& p_out: partons) { - outgoing_.emplace_back(Particle{pid::gluon, std::move(p_out), {}}); + for( auto it = std::make_move_iterator(partons.begin()); + it != std::make_move_iterator(partons.end()); + ++it + ){ + outgoing_.emplace_back(HEJ::Particle{HEJ::pid::gluon, *it, {}}); } - if(status_ != good) return; + if(status_ != Status::good) return; if(proc.boson){ // decay boson auto const & boson_prop = ew_parameters.prop(*proc.boson) ; auto boson{ gen_boson(*proc.boson, boson_prop.mass, boson_prop.width, ran) }; const auto pos{insert_particle(outgoing_, std::move(boson))}; const size_t boson_idx = std::distance(begin(outgoing_), pos); auto const & boson_decay = particle_decays.find(*proc.boson); if( !proc.boson_decay.empty() ){ // decay given in proc decays_.emplace( boson_idx, decay_boson(outgoing_[boson_idx], proc.boson_decay, ran) ); } else if( boson_decay != particle_decays.end() && !boson_decay->second.empty() ){ // decay given explicitly decays_.emplace( boson_idx, - decay_boson(outgoing_[boson_idx], boson_decay->second, ran) + decay_channel(outgoing_[boson_idx], boson_decay->second, ran) ); } } // normalisation of momentum-conserving delta function weight_ *= std::pow(2*M_PI, 4); /** @TODO * uf (jet_param.min_pt) doesn't correspond to our final scale choice. * The HEJ scale generators currently expect a full event as input, * so fixing this is not completely trivial */ - reconstruct_incoming(proc, subl_channels, pdf, E_beam, jet_param.min_pt, ran); - if(status_ != good) return; + reconstruct_incoming(proc, subl_chance, subl_channels, pdf, E_beam, jet_param.min_pt, ran); + if(status_ != Status::good) return; // set outgoing states - most_backward_FKL(outgoing_).type = incoming_[0].type; - most_forward_FKL(outgoing_).type = incoming_[1].type; + begin_partons()->type = incoming_[0].type; + rbegin_partons()->type = incoming_[1].type; maybe_turn_to_subl(subl_chance, subl_channels, proc, ran); if(proc.boson) couple_boson(*proc.boson, ran); } // pt generation, see eq:pt_sampling in developer manual double PhaseSpacePoint::gen_hard_pt( const int np , const double ptmin, const double ptmax, const double /* y */, - RNG & ran - ) { + HEJ::RNG & ran + ){ // heuristic parameter for pt sampling, see eq:pt_par in developer manual const double ptpar = ptmin + np/5.; const double arctan = std::atan((ptmax - ptmin)/ptpar); const double xpt = ran.flat(); const double pt = ptmin + ptpar*std::tan(xpt*arctan); const double cosine = std::cos(xpt*arctan); weight_ *= pt*ptpar*arctan/(cosine*cosine); return pt; } - double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, RNG & ran) { + double PhaseSpacePoint::gen_soft_pt(int np, double max_pt, HEJ::RNG & ran) { constexpr double ptpar = 4.; const double r = ran.flat(); const double pt = max_pt + ptpar/np*std::log(r); weight_ *= pt*ptpar/(np*r); return pt; } double PhaseSpacePoint::gen_parton_pt( int count, JetParameters const & jet_param, double max_pt, double y, - RNG & ran + HEJ::RNG & ran ) { constexpr double p_small_pt = 0.02; if(! jet_param.peak_pt) { return gen_hard_pt(count, jet_param.min_pt, max_pt, y, ran); } const double r = ran.flat(); if(r > p_small_pt) { weight_ /= 1. - p_small_pt; return gen_hard_pt(count, *jet_param.peak_pt, max_pt, y, ran); } weight_ /= p_small_pt; const double pt = gen_soft_pt(count, *jet_param.peak_pt, ran); if(pt < jet_param.min_pt) { weight_=0.0; - status_ = not_enough_jets; + status_ = Status::not_enough_jets; return jet_param.min_pt; } return pt; } std::vector PhaseSpacePoint::gen_LO_partons( int np, bool is_pure_jets, JetParameters const & jet_param, double max_pt, - RNG & ran + HEJ::RNG & ran ){ if (np<2) throw std::invalid_argument{"Not enough partons in gen_LO_partons"}; weight_ /= std::pow(16.*std::pow(M_PI,3),np); weight_ /= std::tgamma(np+1); //remove rapidity ordering std::vector partons; partons.reserve(np); for(int i = 0; i < np; ++i){ const double y = -jet_param.max_y + 2*jet_param.max_y*ran.flat(); weight_ *= 2*jet_param.max_y; const bool is_last_parton = i+1 == np; if(is_pure_jets && is_last_parton) { constexpr double parton_mass_sq = 0.; partons.emplace_back(gen_last_momentum(partons, parton_mass_sq, y)); break; } const double phi = 2*M_PI*ran.flat(); weight_ *= 2.0*M_PI; const double pt = gen_parton_pt(np, jet_param, max_pt, y, ran); if(weight_ == 0.0) return {}; partons.emplace_back(fastjet::PtYPhiM(pt, y, phi)); assert(jet_param.min_pt <= partons[i].pt()); assert(partons[i].pt() <= max_pt+1e-5); } // Need to check that at LO, the number of jets = number of partons; fastjet::ClusterSequence cs(partons, jet_param.def); auto cluster_jets=cs.inclusive_jets(jet_param.min_pt); if (cluster_jets.size()!=unsigned(np)){ weight_=0.0; - status_ = not_enough_jets; + status_ = Status::not_enough_jets; return {}; } - std::sort(begin(partons), end(partons), rapidity_less{}); + std::sort(begin(partons), end(partons), HEJ::rapidity_less{}); return partons; } - Particle PhaseSpacePoint::gen_boson( - ParticleID bosonid, double mass, double width, - RNG & ran + HEJ::Particle PhaseSpacePoint::gen_boson( + HEJ::ParticleID bosonid, double mass, double width, + HEJ::RNG & ran ){ // Usual phase space measure weight_ /= 16.*std::pow(M_PI, 3); // Generate a y Gaussian distributed around 0 /// @TODO check magic numbers for different boson Higgs /// @TODO better sampling for W const double stddev_y = 1.6; const double y = random_normal(stddev_y, ran); const double r1 = ran.flat(); const double s_boson = mass*( mass + width*std::tan(M_PI/2.*r1 + (r1-1.)*std::atan(mass/width)) ); // off-shell s_boson sampling, compensates for Breit-Wigner /// @TODO use a flag instead - if(std::abs(bosonid) == pid::Wp){ - weight_/=M_PI*M_PI*8.; + if(std::abs(bosonid) == HEJ::pid::Wp){ + weight_/=M_PI*M_PI*16.; //Corrects B-W factors, see git issue 132 weight_*= mass*width*( M_PI+2.*std::atan(mass/width) ) / ( 1. + std::cos( M_PI*r1 + 2.*(r1-1.)*std::atan(mass/width) ) ); } - 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]; + return { bosonid, + gen_last_momentum(outgoing_, s_boson, y), + {} + }; } 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 ); + HEJ::ParticleID index_to_pid(size_t i){ + if(!i) return HEJ::pid::gluon; + return static_cast( i%2 ? (i+1)/2 : -i/2 ); } /// partons are ordered: even = anti, 0 = gluon - size_t pid_to_index(ParticleID id){ - if(id==pid::gluon) return 0; + size_t pid_to_index(HEJ::ParticleID id){ + if(id==HEJ::pid::gluon) return 0; return id>0 ? id*2-1 : std::abs(id)*2; } - std::bitset<11> init_allowed(ParticleID const id){ - if(std::abs(id) == pid::proton) + PhaseSpacePoint::part_mask init_allowed(HEJ::ParticleID const id){ + if(std::abs(id) == HEJ::pid::proton) return ~0; - std::bitset<11> out{0}; - if(is_parton(id)) - out[pid_to_index(id)] = 1; + PhaseSpacePoint::part_mask out{0}; + if(HEJ::is_parton(id)) + out[pid_to_index(id)] = true; return out; } /// decides which "index" (see index_to_pid) are allowed for process - 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; + PhaseSpacePoint::part_mask allowed_quarks(HEJ::ParticleID const boson){ + if(std::abs(boson) != HEJ::pid::Wp){ + return ~1; // not a gluon } - return allowed; + // special case W: + // Wp: anti-down or up-type quark, no b/t + // Wm: down or anti-up-type quark, no b/t + return boson>0?0b00011001100 // NOLINT(readability-magic-numbers) + :0b00100110010; // NOLINT(readability-magic-numbers) + } + } // namespace + + std::array PhaseSpacePoint::incoming_AWZ( + Process const & proc, Subleading const subl_channels, + std::array allowed_partons, + HEJ::RNG & ran + ){ + assert(proc.boson); + auto couple_parton = allowed_quarks(*proc.boson); + // eqqx possible if one incoming is a gluon + if(proc.njets >= 3 && subl_channels[subleading::eqqx]){ + couple_parton.set(pid_to_index(HEJ::ParticleID::gluon)); + } + if( // coupling possible through input + allowed_partons[0] == (couple_parton&allowed_partons[0]) + || allowed_partons[1] == (couple_parton&allowed_partons[1]) + // cqqx possible + || (proc.njets >= 4 && subl_channels[subleading::cqqx]) + ){ + return allowed_partons; } + // only first can couple + if( (allowed_partons[0]&couple_parton).any() + &&(allowed_partons[1]&couple_parton).none() + ){ + return {allowed_partons[0]&couple_parton, allowed_partons[1]}; + } + // only second can couple + if( (allowed_partons[0]&couple_parton).none() + && (allowed_partons[1]&couple_parton).any() + ){ + return {allowed_partons[0], allowed_partons[1]&couple_parton}; + } + // both can couple + if( (allowed_partons[0]&couple_parton).any() + && (allowed_partons[1]&couple_parton).any() + ){ + double rnd = ran.flat(); + weight_*=3.; + if(rnd<1./3.){ + return { + allowed_partons[0] & couple_parton, + allowed_partons[1] & ~couple_parton + }; + } + if(rnd<2./3.){ + return { + allowed_partons[0] & ~couple_parton, + allowed_partons[1] & couple_parton + }; + } + return { + allowed_partons[0] & couple_parton, + allowed_partons[1] & couple_parton + }; + } + throw std::invalid_argument{"Incoming state not allowed."}; + } + + std::array PhaseSpacePoint::incoming_eqqx( + std::array allowed_partons, HEJ::RNG & ran + ){ + auto const gluon_idx = pid_to_index(HEJ::pid::gluon); + auto & first_beam = allowed_partons[0]; + auto & second_beam = allowed_partons[1]; + if(first_beam[gluon_idx] && !second_beam[gluon_idx]){ + first_beam.reset(); + first_beam.set(gluon_idx); + return allowed_partons; + } + if(!first_beam[gluon_idx] && second_beam[gluon_idx]) { + second_beam.reset(); + second_beam.set(gluon_idx); + return allowed_partons; + } + if(first_beam[gluon_idx] && second_beam[gluon_idx]) { + // both beams can be gluons + // if one can't be a quark everything is good + auto first_quarks = first_beam; + first_quarks.reset(gluon_idx); + auto second_quarks = second_beam; + second_quarks.reset(gluon_idx); + if(first_quarks.none() || second_quarks.none()){ + return allowed_partons; + } + // else choose one to be a gluon + double rnd = ran.flat(); + weight_*=3.; + if(rnd<1./3.){ + allowed_partons[0].reset(); + allowed_partons[0].set(gluon_idx); + allowed_partons[1].reset(gluon_idx); + } else if(rnd<2./3.){ + allowed_partons[1].reset(); + allowed_partons[1].set(gluon_idx); + allowed_partons[0].reset(gluon_idx); + } else { + allowed_partons[0].reset(); + allowed_partons[0].set(gluon_idx); + allowed_partons[1].reset(); + allowed_partons[1].set(gluon_idx); + } + return allowed_partons; + } + throw std::invalid_argument{ + "Incoming state not allowed for pure extremal qqx."}; + } + + std::array PhaseSpacePoint::incoming_uno( + std::array allowed_partons, HEJ::RNG & ran + ){ + auto const gluon_idx = pid_to_index(HEJ::pid::gluon); + auto & first_beam = allowed_partons[0]; + auto first_quarks = first_beam; + first_quarks.reset(gluon_idx); + auto & second_beam = allowed_partons[1]; + auto second_quarks = second_beam; + second_quarks.reset(gluon_idx); + if(first_quarks.any() && second_quarks.none()){ + first_beam.reset(gluon_idx); + return allowed_partons; + } + if(first_quarks.none() && second_quarks.any()) { + second_beam.reset(gluon_idx); + return allowed_partons; + } + if(first_quarks.any() && second_quarks.any()) { + // both beams can be quarks + // if one can't be gluon everything is good + if(!first_beam[gluon_idx] || !second_beam[gluon_idx]){ + return allowed_partons; + } + // else choose one to be a quark + double rnd = ran.flat(); + weight_*=3.; + if(rnd<1./3.){ + allowed_partons[0].reset(gluon_idx); + allowed_partons[1].reset(); + allowed_partons[1].set(gluon_idx); + } else if(rnd<2./3.){ + allowed_partons[1].reset(gluon_idx); + allowed_partons[0].reset(); + allowed_partons[0].set(gluon_idx); + } else { + allowed_partons[0].reset(gluon_idx); + allowed_partons[1].reset(gluon_idx); + } + return allowed_partons; + } + throw std::invalid_argument{ + "Incoming state not allowed for pure unordered."}; } /** + * @brief Returns list of all allowed initial states partons + * * checks which partons are allowed as initial state: * 1. only allow what is given in the Runcard (p -> all) * 2. A/W/Z require something to couple to * a) no qqx => no incoming gluon * b) 2j => no incoming gluon - * c) 3j => can couple OR is gluon => 2 gluons become qqx later + * c) >3j => can couple OR is gluon => 2 gluons become qqx later + * 3. pure eqqx requires at least one gluon + * 4. pure uno requires at least one quark */ - std::array,2> PhaseSpacePoint::filter_partons( - Process const & proc, unsigned int const subl_channels, RNG & ran + std::array PhaseSpacePoint::allowed_incoming( + Process const & proc, + double const subl_chance, Subleading const subl_channels, + HEJ::RNG & ran ){ - std::array,2> allowed_partons{ + // all possible incoming states + std::array 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."}; - } + if(proc.boson && is_AWZ_proccess(proc)){ + allowed_partons = incoming_AWZ(proc, subl_channels, allowed_partons, ran); + } + // special case: pure subleading + if(subl_chance!=1.){ + return allowed_partons; + } + auto other_channels = subl_channels; + // pure eqqx + other_channels.reset(subleading::eqqx); + if(other_channels.none()){ + return incoming_eqqx(allowed_partons, ran); + } + other_channels = subl_channels; + // pure uno + other_channels.reset(subleading::uno); + if(other_channels.none()){ + return incoming_uno(allowed_partons, ran); } return allowed_partons; } + void PhaseSpacePoint::reconstruct_incoming( - Process const & proc, unsigned int const subl_channels, - PDF & pdf, double E_beam, + Process const & proc, + double const subl_chance, Subleading const subl_channels, + HEJ::PDF & pdf, double E_beam, double uf, - RNG & ran + HEJ::RNG & ran ){ std::tie(incoming_[0].p, incoming_[1].p) = incoming_momenta(outgoing_); // calculate xa, xb const double sqrts=2*E_beam; const double xa=(incoming_[0].E()-incoming_[0].pz())/sqrts; const double xb=(incoming_[1].E()+incoming_[1].pz())/sqrts; // abort if phase space point is outside of collider energy reach if (xa>1. || xb>1.){ weight_=0; - status_ = too_much_energy; + status_ = Status::too_much_energy; return; } auto const & ids = proc.incoming; - std::array,2> allowed_partons( - filter_partons(proc, subl_channels, ran)); + std::array allowed_partons + = allowed_incoming(proc, subl_chance, subl_channels, ran); for(size_t i = 0; i < 2; ++i){ - if(ids[i] == pid::proton || ids[i] == pid::p_bar){ + if(ids[i] == HEJ::pid::proton || ids[i] == HEJ::pid::p_bar){ // pick ids according to pdfs incoming_[i].type = generate_incoming_id(i, i?xb:xa, uf, pdf, allowed_partons[i], ran); } else { assert(allowed_partons[i][pid_to_index(ids[i])]); incoming_[i].type = ids[i]; } } assert(momentum_conserved(1e-7)); } - ParticleID PhaseSpacePoint::generate_incoming_id( + HEJ::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 + HEJ::PDF & pdf, part_mask allowed_partons, HEJ::RNG & ran ){ - std::array pdf_wt; + std::array pdf_wt{}; pdf_wt[0] = allowed_partons[0]? - std::fabs(pdf.pdfpt(beam_idx,x,uf,pid::gluon)):0.; + std::fabs(pdf.pdfpt(beam_idx,x,uf,HEJ::pid::gluon)):0.; double pdftot = pdf_wt[0]; for(size_t i = 1; i < pdf_wt.size(); ++i){ pdf_wt[i] = allowed_partons[i]? 4./9.*std::fabs(pdf.pdfpt(beam_idx,x,uf,index_to_pid(i))):0; pdftot += pdf_wt[i]; } const double r1 = pdftot * ran.flat(); double sum = 0; for(size_t i=0; i < pdf_wt.size(); ++i){ if (r1 < (sum+=pdf_wt[i])){ weight_*= pdftot/pdf_wt[i]; return index_to_pid(i); } } std::cerr << "Error in choosing incoming parton: "< allowed_parts; - for(auto & part: outgoing_){ + std::vector allowed_parts; + for(auto part_it=begin_partons(); part_it!=end_partons(); ++part_it){ // Wp -> up OR anti-down, Wm -> anti-up OR down, no bottom - if ( can_couple_to_W(part, boson) ) - allowed_parts.push_back(&part); + if ( can_couple_to_W(*part_it, boson) ) + allowed_parts.push_back(part_it); } - if(allowed_parts.size() == 0){ + if(allowed_parts.empty()){ throw std::logic_error{"Found no parton for coupling with boson"}; } // select one and flip it size_t idx = 0; if(allowed_parts.size() > 1){ /// @TODO more efficient sampling /// old code: probability[i] = exp(parton[i].y - W.y) idx = std::floor(ran.flat()*allowed_parts.size()); weight_ *= allowed_parts.size(); } const int W_charge = boson>0?1:-1; allowed_parts[idx]->type = - static_cast( allowed_parts[idx]->type - W_charge ); + static_cast( allowed_parts[idx]->type - W_charge ); } - double PhaseSpacePoint::random_normal( - double stddev, - RNG & ran - ){ + double PhaseSpacePoint::random_normal( double stddev, HEJ::RNG & ran ){ const double r1 = ran.flat(); const double r2 = ran.flat(); const double lninvr1 = -std::log(r1); const double result = stddev*std::sqrt(2.*lninvr1)*std::cos(2.*M_PI*r2); weight_ *= exp(result*result/(2*stddev*stddev))*std::sqrt(2.*M_PI)*stddev; return result; } bool PhaseSpacePoint::momentum_conserved(double ep) const{ fastjet::PseudoJet diff; for(auto const & in: incoming()) diff += in.p; for(auto const & out: outgoing()) diff -= out.p; - return 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"}; + return HEJ::nearby_ep(diff, fastjet::PseudoJet{}, ep); } - 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; - } -} +} // namespace HEJFOG diff --git a/FixedOrderGen/src/main.cc b/FixedOrderGen/src/bin/main.cc similarity index 97% rename from FixedOrderGen/src/main.cc rename to FixedOrderGen/src/bin/main.cc index c9b7fec..579428f 100644 --- a/FixedOrderGen/src/main.cc +++ b/FixedOrderGen/src/bin/main.cc @@ -1,293 +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; + assert( (generator.status() == HEJFOG::Status::good) == bool(ev) ); + if(generator.status() != HEJFOG::Status::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; + assert( (generator.status() == HEJFOG::Status::good) == bool(ev) ); + if(generator.status() != HEJFOG::Status::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/src/config.cc b/FixedOrderGen/src/config.cc index 4d869b4..3cdacfe 100644 --- a/FixedOrderGen/src/config.cc +++ b/FixedOrderGen/src/config.cc @@ -1,439 +1,419 @@ /** * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ #include "config.hh" #include #include #include +#include #include #include #include #include -#include "HEJ/exceptions.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/YAMLreader.hh" - -#include "Subleading.hh" +#include "HEJ/exceptions.hh" namespace HEJFOG { using HEJ::set_from_yaml; using HEJ::set_from_yaml_if_defined; using HEJ::pid::ParticleID; namespace { //! Get YAML tree of supported options /** * The configuration file is checked against this tree of options * in assert_all_options_known. */ YAML::Node const & get_supported_options(){ const static YAML::Node supported = [](){ YAML::Node supported; static const auto opts = { "process", "events", "subleading fraction","subleading channels", "scales", "scale factors", "max scale ratio", "pdf", "vev", "event output", "analyses", "analysis", "import scales" }; // add subnodes to "supported" - the assigned value is irrelevant for(auto && opt: opts) supported[opt] = ""; for(auto && jet_opt: {"min pt", "peak pt", "algorithm", "R", "max rapidity"}){ supported["jets"][jet_opt] = ""; } for(auto && particle_type: {"Higgs", "W", "Z"}){ for(auto && particle_opt: {"mass", "width"}){ supported["particle properties"][particle_type][particle_opt] = ""; } } for(auto && particle_type: {"Higgs", "Wp", "W+", "Wm", "W-", "Z"}){ for(auto && particle_opt: {"into", "branching ratio"}){ supported["decays"][particle_type][particle_opt] = ""; } } for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){ supported["Higgs coupling"][opt] = ""; } for(auto && beam_opt: {"energy", "particles"}){ supported["beam"][beam_opt] = ""; } for(auto && unweight_opt: {"sample size", "max deviation"}){ supported["unweight"][unweight_opt] = ""; } for(auto && opt: {"name", "seed"}){ supported["random generator"][opt] = ""; } return supported; }(); return supported; } JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ){ const auto p = HEJ::get_jet_parameters(node, entry); JetParameters result; result.def = p.def; result.min_pt = p.min_pt; set_from_yaml(result.max_y, node, entry, "max rapidity"); set_from_yaml_if_defined(result.peak_pt, node, entry, "peak pt"); if(result.peak_pt && *result.peak_pt <= result.min_pt) throw std::invalid_argument{ "Value of option 'peak pt' has to be larger than 'min pt'." }; return result; } Beam get_Beam( YAML::Node const & node, std::string const & entry ){ Beam beam; std::vector particles; set_from_yaml(beam.energy, node, entry, "energy"); set_from_yaml_if_defined(particles, node, entry, "particles"); if(! particles.empty()){ for(HEJ::ParticleID particle: particles){ if(particle != HEJ::pid::p && particle != HEJ::pid::p_bar){ throw std::invalid_argument{ "Unsupported value in option " + entry + ": particles:" " only proton ('p') and antiproton ('p_bar') beams are supported" }; } } if(particles.size() != 2){ throw std::invalid_argument{"Not exactly two beam particles"}; } beam.particles.front() = particles.front(); beam.particles.back() = particles.back(); } return beam; } std::vector split( std::string const & str, std::string const & delims ){ std::vector result; - for(size_t begin, end = 0; end != str.npos;){ + for(size_t begin = 0, end = 0; end != std::string::npos;){ begin = str.find_first_not_of(delims, end); - if(begin == str.npos) break; + if(begin == std::string::npos) break; end = str.find_first_of(delims, begin + 1); result.emplace_back(str.substr(begin, end - begin)); } return result; } std::invalid_argument invalid_incoming(std::string const & what){ return std::invalid_argument{ "Incoming particle type " + what + " not supported," " incoming particles have to be 'p', 'p_bar' or partons" }; } std::invalid_argument invalid_outgoing(std::string const & what){ return std::invalid_argument{ "Outgoing particle type " + what + " not supported," " outgoing particles have to be 'j', 'photon', 'H', 'Wm', 'Wp', 'e-', 'e+', 'nu_e', 'nu_e_bar'" }; } HEJ::ParticleID reconstruct_boson_id( std::vector const & ids ){ assert(ids.size()==2); const int pidsum = ids[0] + ids[1]; if(pidsum == +1) { assert(HEJ::is_antilepton(ids[0])); if(HEJ::is_antineutrino(ids[0])) { throw HEJ::not_implemented{"lepton-flavour violating final state"}; } assert(HEJ::is_neutrino(ids[1])); // charged antilepton + neutrino means we had a W+ return HEJ::pid::Wp; } if(pidsum == -1) { assert(HEJ::is_antilepton(ids[0])); if(HEJ::is_neutrino(ids[1])) { throw HEJ::not_implemented{"lepton-flavour violating final state"}; } assert(HEJ::is_antineutrino(ids[0])); // charged lepton + antineutrino means we had a W- return HEJ::pid::Wm; } throw HEJ::not_implemented{ "final state with leptons "+name(ids[0])+" and "+name(ids[1]) +" not supported" }; } Process get_process( YAML::Node const & node, std::string const & entry ){ Process result; std::string process_string; set_from_yaml(process_string, node, entry); assert(! process_string.empty()); const auto particles = split(process_string, " \n\t\v=>"); if(particles.size() < 3){ throw std::invalid_argument{ "Bad format in option process: '" + process_string + "', expected format is 'in1 in2 => out1 ...'" }; } result.incoming.front() = HEJ::to_ParticleID(particles[0]); result.incoming.back() = HEJ::to_ParticleID(particles[1]); for(size_t i = 0; i < result.incoming.size(); ++i){ const HEJ::ParticleID in = result.incoming[i]; if( in != HEJ::pid::proton && in != HEJ::pid::p_bar && !HEJ::is_parton(in) ){ throw invalid_incoming(particles[i]); } } result.njets = 0; for(size_t i = result.incoming.size(); i < particles.size(); ++i){ assert(! particles[i].empty()); if(particles[i] == "j") ++result.njets; else if(std::isdigit(particles[i].front()) && particles[i].back() == 'j') result.njets += std::stoi(particles[i]); else{ const auto pid = HEJ::to_ParticleID(particles[i]); if(pid==HEJ::pid::Higgs || pid==HEJ::pid::Wp || pid==HEJ::pid::Wm){ if(result.boson) throw std::invalid_argument{ "More than one outgoing boson is not supported" }; if(!result.boson_decay.empty()) throw std::invalid_argument{ "Production of a boson together with a lepton is not supported" }; result.boson = pid; } else if (HEJ::is_anylepton(pid)){ // Do not accept more leptons, if two leptons are already mentioned if( result.boson_decay.size()>=2 ) throw std::invalid_argument{"Too many leptons provided"}; if(result.boson) throw std::invalid_argument{ "Production of a lepton together with a boson is not supported" }; result.boson_decay.emplace_back(pid); } else { throw invalid_outgoing(particles[i]); } } } if(result.njets < 2){ throw std::invalid_argument{ "Process has to include at least two jets ('j')" }; } if(!result.boson_decay.empty()){ std::sort(begin(result.boson_decay),end(result.boson_decay)); assert(!result.boson); result.boson = reconstruct_boson_id(result.boson_decay); } return result; } HEJFOG::Subleading to_subleading_channel(YAML::Node const & yaml){ std::string name; - using namespace HEJFOG::channels; + using namespace HEJFOG::subleading; set_from_yaml(name, yaml); if(name == "none") - return none; + return NONE; if(name == "all") - return all; + return ALL; + Subleading channel = NONE; if(name == "unordered" || name == "uno") - return uno; - if(name == "qqx") - return qqx; + return channel.set(uno); + if(name == "central qqx" || name == "cqqx") + return channel.set(cqqx); + if(name == "extremal qqx" || name == "eqqx") + return channel.set(eqqx); throw HEJ::unknown_option("Unknown subleading channel '"+name+"'"); } - unsigned int get_subleading_channels(YAML::Node const & node){ + Subleading get_subleading_channels(YAML::Node const & node){ using YAML::NodeType; - using namespace HEJFOG::channels; + using namespace HEJFOG::subleading; // all channels allowed by default - if(!node) return all; + if(!node) return ALL; switch(node.Type()){ case NodeType::Undefined: - return all; + return ALL; case NodeType::Null: - return none; + 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; + Subleading channels; for(auto && channel_node: node){ channels |= get_subleading_channels(channel_node); } return channels; } throw std::logic_error{"unreachable"}; } Decay get_decay(YAML::Node const & node, std::string const & entry, std::string const & boson ){ Decay decay; set_from_yaml(decay.products, node, entry, boson, "into"); decay.branching_ratio=1; set_from_yaml_if_defined(decay.branching_ratio, node, entry, boson, "branching ratio"); return decay; } std::vector get_decays(YAML::Node const & node, std::string const & entry, std::string const & boson ){ using YAML::NodeType; if(!node[entry][boson]) return {}; switch(node[entry][boson].Type()){ case NodeType::Null: case NodeType::Undefined: return {}; case NodeType::Scalar: throw HEJ::invalid_type{"value is not a list of decays"}; case NodeType::Map: return {get_decay(node, entry, boson)}; case NodeType::Sequence: std::vector result; for(auto && decay_str: node[entry][boson]){ result.emplace_back(get_decay(decay_str, entry, boson)); } return result; } throw std::logic_error{"unreachable"}; } ParticlesDecayMap get_all_decays(YAML::Node const & node, std::string const & entry ){ if(!node[entry]) return {}; if(!node[entry].IsMap()) throw HEJ::invalid_type{entry + " have to be a map"}; ParticlesDecayMap result; for(auto const & sub_node: node[entry]) { const auto boson = sub_node.first.as(); const auto id = HEJ::to_ParticleID(boson); result.emplace(id, get_decays(node, entry, boson)); } return result; } - 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; + UnweightSettings result{}; set_from_yaml(result.sample_size, node, entry, "sample size"); if(result.sample_size <= 0){ throw std::invalid_argument{ "negative sample size " + std::to_string(result.sample_size) }; } set_from_yaml(result.max_dev, node, entry, "max deviation"); return result; } Config to_Config(YAML::Node const & yaml){ try{ HEJ::assert_all_options_known(yaml, get_supported_options()); } catch(HEJ::unknown_option const & ex){ throw HEJ::unknown_option{std::string{"Unknown option '"} + ex.what() + "'"}; } Config config; config.process = get_process(yaml, "process"); set_from_yaml(config.events, yaml, "events"); config.jets = get_jet_parameters(yaml, "jets"); config.beam = get_Beam(yaml, "beam"); for(size_t i = 0; i < config.process.incoming.size(); ++i){ auto const & in = config.process.incoming[i]; using namespace HEJ::pid; if( (in == p || in == p_bar) && in != config.beam.particles[i]){ throw std::invalid_argument{ "Particle type of beam " + std::to_string(i+1) + " incompatible" + " with type of incoming particle " + std::to_string(i+1) }; } } set_from_yaml(config.pdf_id, yaml, "pdf"); set_from_yaml(config.subleading_fraction, yaml, "subleading fraction"); if(config.subleading_fraction == 0) - config.subleading_channels = Subleading::none; + config.subleading_channels.reset(); else config.subleading_channels = get_subleading_channels(yaml["subleading channels"]); - config.ew_parameters = get_ew_parameters(yaml); + config.ew_parameters = HEJ::get_ew_parameters(yaml); config.particle_decays = get_all_decays(yaml, "decays"); if(config.process.boson // check that Ws always decay && std::abs(*config.process.boson) == HEJ::ParticleID::Wp && config.process.boson_decay.empty() ){ auto const & decay = config.particle_decays.find(*config.process.boson); if(decay == config.particle_decays.end() || decay->second.empty()) throw std::invalid_argument{ "Decay for "+name(*config.process.boson)+" is required"}; } set_from_yaml_if_defined(config.analyses_parameters, yaml, "analyses"); - if(yaml["analysis"]){ + if(yaml["analysis"].IsDefined()){ std::cerr << "WARNING: Configuration entry 'analysis' is deprecated. " " Use 'analyses' instead.\n"; YAML::Node ana; set_from_yaml(ana, yaml, "analysis"); if(!ana.IsNull()){ config.analyses_parameters.push_back(ana); } } config.scales = HEJ::to_ScaleConfig(yaml); set_from_yaml_if_defined(config.output, yaml, "event output"); config.rng = HEJ::to_RNGConfig(yaml, "random generator"); config.Higgs_coupling = HEJ::get_Higgs_coupling(yaml, "Higgs coupling"); - if(yaml["unweight"]) config.unweight = get_unweight(yaml, "unweight"); + if(yaml["unweight"].IsDefined()) config.unweight = get_unweight(yaml, "unweight"); return config; } - } // namespace anonymous + } // namespace Config load_config(std::string const & config_file){ try{ return to_Config(YAML::LoadFile(config_file)); } catch(...){ std::cerr << "Error reading " << config_file << ":\n "; throw; } } -} +} // namespace HEJFOG diff --git a/FixedOrderGen/t/2j.cc b/FixedOrderGen/t/2j.cc deleted file mode 100644 index e63be93..0000000 --- a/FixedOrderGen/t/2j.cc +++ /dev/null @@ -1,69 +0,0 @@ -/** - * \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 "HEJ/Event.hh" -#include "HEJ/Mixmax.hh" -#include "HEJ/ScaleFunction.hh" - -#include "config.hh" -#include "EventGenerator.hh" -#include "Status.hh" - -int main(){ - using namespace HEJFOG; - - constexpr double invGeV2_to_pb = 389379292.; - constexpr double xs_ref = 86.42031848*1e6; //calculated with "combined" HEJ svn r3480 - - auto config = load_config("config_2j.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); - 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.01*xs); - return EXIT_SUCCESS; -} diff --git a/FixedOrderGen/t/4j.cc b/FixedOrderGen/t/4j.cc deleted file mode 100644 index 4ca2c01..0000000 --- a/FixedOrderGen/t/4j.cc +++ /dev/null @@ -1,72 +0,0 @@ -/** - * \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 "HEJ/Event.hh" -#include "HEJ/Mixmax.hh" -#include "HEJ/ScaleFunction.hh" - -#include "config.hh" -#include "EventGenerator.hh" -#include "Status.hh" - -int main(){ - using namespace HEJFOG; - - constexpr double invGeV2_to_pb = 389379292.; - // calculated with 13207b5f67a5f40a2141aa7ee515b022bd4efb65 - constexpr double xs_ref = 915072; //+- 7227.72 - - auto config = load_config("config_2j.yml"); - config.process.njets = 4; - config.events *= 1.5; - - 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); - 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.04*xs); - return EXIT_SUCCESS; -} diff --git a/FixedOrderGen/t/CMakeLists.txt b/FixedOrderGen/t/CMakeLists.txt new file mode 100644 index 0000000..61559ff --- /dev/null +++ b/FixedOrderGen/t/CMakeLists.txt @@ -0,0 +1,213 @@ +set(tst_dir "${CMAKE_CURRENT_SOURCE_DIR}") +set(runcard_dir ${tst_dir}/runcards) + +function(add_long_test) + set(options NONE) + set(oneValueArgs NAME) + set(multiValueArgs COMMAND) + cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + add_test( + NAME ${TEST_NAME}_short + COMMAND ${TEST_COMMAND} short + ) + if(TEST_ALL) + add_test( + NAME ${TEST_NAME} + COMMAND ${TEST_COMMAND} + ) + endif() +endfunction() + +foreach(tst W_reconstruct_enu W_2j_classify W_nj_classify) + add_executable(test_${tst} ${tst_dir}/${tst}.cc) + target_link_libraries(test_${tst} hejfog_lib) + add_test(NAME ${tst} + COMMAND test_${tst} + WORKING_DIRECTORY ${runcard_dir} + ) +endforeach() +# this only tests if the runcard actually works, not if the result is correct +add_test( + NAME main_example + COMMAND HEJFOG ${PROJECT_SOURCE_DIR}/configFO.yml +) +add_test( + NAME main_2j + COMMAND HEJFOG ${runcard_dir}/2j.yml +) +add_test( + NAME main_h2j + COMMAND HEJFOG ${runcard_dir}/h2j.yml +) +add_test( + NAME main_h2j_decay + COMMAND HEJFOG ${runcard_dir}/h2j_decay.yml +) +add_test( + NAME peakpt + COMMAND HEJFOG ${runcard_dir}/2j_peak.yml +) +# special case where coupling not trivial +add_test( + NAME main_W4j_uno+eqqx + COMMAND HEJFOG ${runcard_dir}/Wp4j_uno+eqqx.yml +) + +# check that uno emission doesn't change FKL xs +add_executable(FKL_uno FKL_uno.cc) +target_link_libraries(FKL_uno hejfog_lib) +add_test( + NAME FKL_uno + # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 + COMMAND FKL_uno ${runcard_dir}/h3j_uno.yml 0.0243548 0.000119862 +) +# xs tests +add_executable(xs_gen xs_gen.cc) +target_link_libraries(xs_gen hejfog_lib) +## Higgs +add_test( + NAME xs_hqQ + # calculated with Sherpa see #132 + COMMAND xs_gen ${runcard_dir}/hqQ.yml 1.612e-02 1.26303e-04 +) +add_test( + NAME xs_h2j + # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 + COMMAND xs_gen ${runcard_dir}/h2j.yml 2.04928 0.00956022 +) +add_test( + NAME xs_h3j + # calculated with HEJ revision bd4388fe55cbc3f5a7b6139096456c551294aa31 + COMMAND xs_gen ${runcard_dir}/h3j.yml 1.07807 0.0132409 +) +add_long_test( + NAME xs_h3j_uno + # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 + COMMAND xs_gen ${runcard_dir}/h3j_uno.yml 0.00347538 3.85875e-05 +) +add_test( + NAME xs_h5j + # calculated with HEJ revision dbde2ffbb3b383ae6709b2424d8f0f9d5658270b + COMMAND xs_gen ${runcard_dir}/h5j.yml 0.0112504 0.000199633 +) +add_test( + NAME xs_h2j_decay + # calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 + COMMAND xs_gen ${runcard_dir}/h2j_decay.yml 0.00466994 2.20995e-05 +) +## pure jets +add_test( + NAME xs_qQ + # calculated with Sherpa see #132 + COMMAND xs_gen ${runcard_dir}/qQ.yml 7.354e+05 1.905e+03 +) +add_test( + NAME xs_2j + # calculated with "combined" HEJ svn r3480 + COMMAND xs_gen ${runcard_dir}/2j.yml 86.42031848e06 590570 +) +add_test( + NAME xs_3j_uno + # calculated with HEJ revision 59b8452accfe1462796406fa8e008c8b16b9657b + COMMAND xs_gen ${runcard_dir}/3j_uno.yml 1.449280e+05 2.318716e+03 +) +add_test( + NAME xs_3j_qqx + # calculated with HEJ revision 9401196fba75b5d16bc33f2a309175fecaca00f1 + COMMAND xs_gen ${runcard_dir}/3j_qqx.yml 62040 1005 +) +add_long_test( + NAME xs_4j_qqx + # calculated with HEJ revision 9401196fba75b5d16bc33f2a309175fecaca00f1 + COMMAND xs_gen ${runcard_dir}/4j_qqx.yml 28936 550 +) +add_test( + NAME xs_4j + # calculated with HEJ revision 59b8452accfe1462796406fa8e008c8b16b9657b + COMMAND xs_gen ${runcard_dir}/4j.yml 1.042808e+06 2.137257e+04 +) +## W +add_test( + NAME xs_WqQ + # calculated with Sherpa see #132 + COMMAND xs_gen ${runcard_dir}/WpqQ.yml 3.086e+00 4.511e-02 +) +add_test( + NAME xs_W2j + # calculated with HEJ revision 449f2f6b597020e9c9e35699568edc05c827fc11+1 + COMMAND xs_gen ${runcard_dir}/Wm2j.yml 4.177443e+02 7.446928e+00 +) +add_long_test( + NAME xs_W3j_eqqx + # calculated with HEJ revision 667eb768fbefa99148bf6fe9ffb6fcd16c0f976e + COMMAND xs_gen ${runcard_dir}/Wp3j_qqx.yml 2.267785e+01 3.707774e-01 +) +add_long_test( + NAME xs_W3j_uno + # calculated with HEJ revision 449f2f6b597020e9c9e35699568edc05c827fc11+1 + COMMAND xs_gen ${runcard_dir}/Wp3j_uno.yml 3.000955e-01 5.831799e-03 +) +add_long_test( + NAME xs_W4j_qqx + # calculated with HEJ revision 667eb768fbefa99148bf6fe9ffb6fcd16c0f976e + COMMAND xs_gen ${runcard_dir}/Wp4j_qqx.yml 1.159881e-01 3.633528e-03 +) + +# Test that sum of partons == proton +add_executable(PSP_channel PSP_channel.cc) +target_link_libraries(PSP_channel hejfog_lib) +# pure jets +add_long_test( + NAME channel_2j + COMMAND PSP_channel ${runcard_dir}/2j.yml +) +add_test( + NAME channel_3j_qqx + COMMAND PSP_channel ${runcard_dir}/3j_qqx.yml +) +add_long_test( + NAME channel_3j_uno + COMMAND PSP_channel ${runcard_dir}/3j_uno.yml +) +add_long_test( + NAME channel_4j_qqx + COMMAND PSP_channel ${runcard_dir}/4j_qqx.yml +) +# W+jets +# pure jets +add_long_test( + NAME channel_W2j + COMMAND PSP_channel ${runcard_dir}/Wm2j.yml +) +add_long_test( + NAME channel_W3j_uno + COMMAND PSP_channel ${runcard_dir}/Wp3j_uno.yml +) +add_long_test( + NAME channel_W3j_eqqx + COMMAND PSP_channel ${runcard_dir}/Wp3j_qqx.yml +) +add_long_test( + NAME channel_W4j_qqx + COMMAND PSP_channel ${runcard_dir}/Wp4j_qqx.yml +) + +# Test that each subleading channel is generated consistently +add_executable(PSP_subl PSP_subl.cc) +target_link_libraries(PSP_subl hejfog_lib) +add_long_test( + NAME subl_5j + COMMAND PSP_subl ${runcard_dir}/5j.yml +) +add_long_test( + NAME subl_h5j + COMMAND PSP_subl ${runcard_dir}/h5j.yml +) +add_long_test( + NAME subl_W3j + COMMAND PSP_subl ${runcard_dir}/Wp3j_qqx.yml +) +add_long_test( + NAME subl_W5j + COMMAND PSP_subl ${runcard_dir}/Wm5j.yml +) diff --git a/FixedOrderGen/t/h_3j_uno1.cc b/FixedOrderGen/t/FKL_uno.cc similarity index 68% rename from FixedOrderGen/t/h_3j_uno1.cc rename to FixedOrderGen/t/FKL_uno.cc index 264e048..f8449f3 100644 --- a/FixedOrderGen/t/h_3j_uno1.cc +++ b/FixedOrderGen/t/FKL_uno.cc @@ -1,85 +1,90 @@ /** * check that adding uno emissions doesn't change the FKL cross section * * \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 "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/PDG_codes.hh" #include "HEJ/Ranlux64.hh" #include "HEJ/ScaleFunction.hh" #include "config.hh" #include "EventGenerator.hh" #include "Subleading.hh" #include "Status.hh" -int main(){ - using namespace HEJFOG; +//! throw error if condition not fulfilled +#define ASSERT(x) if(!(x)) { \ + throw std::logic_error("Assertion '" #x "' failed."); \ + } +namespace { constexpr double invGeV2_to_pb = 389379292.; - constexpr double xs_ref = 0.0243548; // +- 0.000119862 - //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 +} + +int main(int argn, char const *argv[]){ + using namespace HEJFOG; + if(argn != 4){ + std::cerr << "Usage: " << argv[0] << " config.yml xs xs_err"; + return EXIT_FAILURE; + } - auto config = load_config("config_h_2j.yml"); - config.process.njets = 3; - config.process.incoming = {HEJ::pid::u, HEJ::pid::u}; - config.subleading_channels = HEJFOG::Subleading::uno; - config.events *= 1.5; + const double xs_ref = std::stod(argv[2]); + const double err_ref = std::stod(argv[3]); + (void) err_ref; + + auto config = load_config(argv[1]); + config.subleading_fraction = 0.37; 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.; std::size_t uno_found = 0; for (std::size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); - if(generator.status() != good) continue; - assert(ev); + if(generator.status() != Status::good) continue; + ASSERT(ev); if(ev->type() != HEJ::event_type::FKL){ ++uno_found; 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 *= invGeV2_to_pb/config.events; xs_err = std::sqrt(xs_err); + xs_err *= invGeV2_to_pb/config.events; std::cout << xs_ref << " ~ " << xs << " +- " << xs_err << '\n'; std::cout << uno_found << " events with unordered emission" << std::endl; - assert(uno_found > 0); - assert(std::abs(xs - xs_ref) < 3*xs_err); - assert(xs_err < 0.05*xs); + ASSERT(uno_found > 0); + ASSERT(std::abs(xs - xs_ref) < 3*xs_err); + ASSERT(xs_err < 0.05*xs); return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/PSP_channel.cc b/FixedOrderGen/t/PSP_channel.cc new file mode 100644 index 0000000..adb7491 --- /dev/null +++ b/FixedOrderGen/t/PSP_channel.cc @@ -0,0 +1,226 @@ +/** + * check that the sum of all possible quarks is the same as a proton + * + * \authors The HEJ collaboration (see AUTHORS for details) + * \date 2020 + * \copyright GPLv2 or later + */ +#include +#include +#include +#include + +#include "HEJ/Mixmax.hh" +#include "HEJ/Event.hh" + +#include "config.hh" +#include "EventGenerator.hh" + +//! throw error if condition not fulfilled +#define ASSERT(x) if(!(x)) { \ + throw std::logic_error("Assertion '" #x "' failed."); \ + } + +//! throw error if condition not fulfilled +#define ASSERT_THROW(x, exception) try { \ + x; \ + std::cerr << "'" #x "' did not throw an exception.\n"; \ + throw; \ + } catch(exception const & ex){ \ + std::cout << "throw triggered: " << ex.what() << std::endl; \ + } \ + catch (...) { \ + std::cerr << "Unexpected exception thrown for '" #x "'.\n"; \ + throw; \ + } + +namespace { + + HEJFOG::EventGenerator make_generator( + HEJFOG::Config const & config, + std::shared_ptr ran + ){ + return HEJFOG::EventGenerator{ + config.process, + config.beam, + HEJ::ScaleGenerator{ + config.scales.base, + config.scales.factors, + config.scales.max_ratio + }, + config.jets, + config.pdf_id, + config.subleading_fraction, + config.subleading_channels, + config.particle_decays, + config.Higgs_coupling, + config.ew_parameters, + ran + }; + } + + const static std::array PARTONS{ + HEJ::ParticleID::g, + HEJ::ParticleID::d, HEJ::ParticleID::u, + HEJ::ParticleID::s, HEJ::ParticleID::c, + HEJ::ParticleID::b, + HEJ::ParticleID::d_bar, HEJ::ParticleID::u_bar, + HEJ::ParticleID::s_bar, HEJ::ParticleID::c_bar, + HEJ::ParticleID::b_bar + }; + + bool only_channel( + HEJFOG::Subleading channels, + HEJFOG::subleading::Channels selected + ){ + return channels[selected] && channels.reset(selected).none(); + } + + void generate_till_throw(HEJFOG::EventGenerator & gen ){ + for(std::size_t i=0; i<100; ++i){ + auto ev = gen.gen_event(); + if(gen.status()==HEJFOG::Status::good){ + std::cerr << "Wrongfully generated valid event!" << *ev <<"\n"; + throw; + } + } + std::cerr << "Unable to generate a proper throw!\n"; + throw; + } + + bool can_couple_to_W(HEJ::ParticleID const id, HEJ::ParticleID const boson){ + using namespace HEJ; + if(!is_anyquark(id)){ + return false; + } + if(std::abs(id)==HEJ::ParticleID::b || std::abs(id)==HEJ::ParticleID::top) + return false; + // Wp: + if(boson>0){ + // anti-down + if(id%2==0){ + return is_quark(id); + } + // or up-type quark + return is_antiquark(id); + } + // Wm: + // down + if(id%2==0){ + return is_antiquark(id); + } + // or anti-up-type quark + return is_quark(id); + } +} + +int main(int argc, char const *argv[]) +{ + if(argc != 2 && argc != 3){ + std::cerr << "Usage: " << argv[0] << " config.yaml\n"; + return EXIT_FAILURE; + } + const bool short_only = argc==3; + std::cout <(); + + if(short_only) + config.events/=20.; + + double tot_weight = 0.; + double tot_err = 0.; + config.process.incoming[0] = config.process.incoming[1] = HEJ::ParticleID::proton; + { + HEJFOG::EventGenerator gen = make_generator(config, ran); + for(std::size_t i=0; icentral().weight/config.events; + tot_weight += wgt; + tot_err += wgt*wgt; + } + } + ASSERT(tot_weight!=0.); + } + tot_err = std::sqrt(tot_err); + + + config.events /= PARTONS.size(); + double tot_channels = 0.; + double err_channels = 0.; + for(auto b1: PARTONS){ + for(auto b2: PARTONS){ + double wgt_channel = 0.; + double err_channel = 0.; + config.process.incoming[0] = b1; + config.process.incoming[1] = b2; + HEJFOG::EventGenerator gen = make_generator(config, ran); + std::cout << name(b1) << "+" << name(b2) << " " <3 + && config.subleading_fraction>0. + && config.subleading_channels[HEJFOG::subleading::cqqx]){ + // this will force central qqx + } else if(config.process.njets>2 + && config.subleading_fraction>0. + && config.subleading_channels[HEJFOG::subleading::eqqx]){ + // this will force extremal qqx + } else { + auto const boson = *config.process.boson; + if(!can_couple_to_W(b1, boson) + && !can_couple_to_W(b2, boson) + ){ + std::cout << "bad " << name(boson) << std::endl; + ASSERT_THROW(generate_till_throw(gen), std::invalid_argument); + continue; + } + } + } + for(std::size_t i=0; icentral().weight/config.events; + wgt_channel += wgt; + err_channel += wgt*wgt; + } + } + ASSERT(wgt_channel!=0.); + tot_channels += wgt_channel; + err_channels += err_channel; + err_channel = std::sqrt(err_channel); + std::cout << "=> " << wgt_channel << " +/- " << err_channel << std::endl; + } + } + err_channels = std::sqrt(err_channels); + std::cout << tot_weight << " +/- " << tot_err + << " vs. " << tot_channels << " +/- " << err_channels << std::endl; + + ASSERT(std::abs(tot_weight - tot_channels) + < 2.*sqrt(err_channels*err_channels+tot_err*tot_err)); + + return EXIT_SUCCESS; +} diff --git a/FixedOrderGen/t/PSP_subl.cc b/FixedOrderGen/t/PSP_subl.cc new file mode 100644 index 0000000..067dc39 --- /dev/null +++ b/FixedOrderGen/t/PSP_subl.cc @@ -0,0 +1,221 @@ +/** + * check that each subprocess xs is correctly generated when only selecting one + * + * \authors The HEJ collaboration (see AUTHORS for details) + * \date 2020 + * \copyright GPLv2 or later + */ +#include +#include +#include +#include + +#include "HEJ/Mixmax.hh" +#include "HEJ/Event.hh" +#include "HEJ/CrossSectionAccumulator.hh" + +#include "config.hh" +#include "EventGenerator.hh" + +//! throw error if condition not fulfilled +#define ASSERT(x) if(!(x)) { \ + throw std::logic_error("Assertion '" #x "' failed."); \ + } + +//! throw error if condition not fulfilled +#define ASSERT_THROW(x, exception) try { \ + x; \ + std::cerr << "'" #x "' did not throw an exception.\n"; \ + throw; \ + } catch(exception const & ex){ \ + std::cout << "throw triggered: " << ex.what() << std::endl; \ + } \ + catch (...) { \ + std::cerr << "Unexpected exception thrown for '" #x "'.\n"; \ + throw; \ + } + +namespace { + const static std::array ALL_TYPES{ + HEJ::event_type::FKL, + HEJ::event_type::unordered_backward, + HEJ::event_type::unordered_forward, + HEJ::event_type::extremal_qqxb, + HEJ::event_type::extremal_qqxf, + HEJ::event_type::central_qqx + }; + + HEJFOG::EventGenerator make_generator( + HEJFOG::Config const & config, + std::shared_ptr ran + ){ + return HEJFOG::EventGenerator{ + config.process, + config.beam, + HEJ::ScaleGenerator{ + config.scales.base, + config.scales.factors, + config.scales.max_ratio + }, + config.jets, + config.pdf_id, + config.subleading_fraction, + config.subleading_channels, + config.particle_decays, + config.Higgs_coupling, + config.ew_parameters, + ran + }; + } + + bool valid_type( + HEJFOG::Subleading const channels, HEJ::event_type::EventType type + ){ + using namespace HEJ::event_type; + using c = HEJFOG::subleading::Channels; + if(channels.none()) + return type == FKL; + if(channels.count()!=1) + return false; + if(channels[c::uno]) + return type == unob || type == unof; + if(channels[c::eqqx]) + return type == qqxexb || type == qqxexf; + if(channels[c::cqqx]) + return type == qqxmid; + throw HEJ::unknown_option{"wrong channel"}; + } + + void generate_till_throw(HEJFOG::EventGenerator & gen ){ + for(std::size_t i=0; i<100; ++i){ + auto ev = gen.gen_event(); + if(gen.status()==HEJFOG::Status::good){ + std::cerr << "Wrongfully generated valid event!" << *ev <<"\n"; + throw; + } + } + std::cerr << "Unable to generate a proper throw!\n"; + throw; + } +} + +int main(int argc, char const *argv[]) +{ + if(argc != 2 && argc != 3){ + std::cerr << "Usage: " << argv[0] << " config.yaml\n"; + return EXIT_FAILURE; + } + const bool short_only = argc==3; + std::cout <(); + + if(short_only) + config.events/=40.; + + // mixes sample + HEJ::CrossSectionAccumulator xs_tot; + config.process.incoming[0] = config.process.incoming[1] = HEJ::ParticleID::proton; + config.subleading_fraction=0.8; + if(config.process.boson && *config.process.boson == HEJ::ParticleID::Higgs){ + config.subleading_channels.reset(); + config.subleading_channels.set(HEJFOG::subleading::uno); + } else { + config.subleading_channels.set(); + } + { + HEJFOG::EventGenerator gen = make_generator(config, ran); + for(std::size_t i=0; icentral().weight /= config.events; + xs_tot.fill(*ev); + } + } + ASSERT(xs_tot.total().value!=0.); + } + + // config.events /= HEJFOG::subleading::last+1; + + // pure FKL + HEJ::CrossSectionAccumulator xs_subl; + { + config.subleading_fraction = 0.; + HEJFOG::EventGenerator gen = make_generator(config, ran); + for(std::size_t i=0; icentral().weight /= config.events; + xs_subl.fill(*ev); + ASSERT(valid_type(0,ev->type())); + } + } + ASSERT(xs_subl.total().value!=0.); + std::cout << "=>\n" << xs_subl << std::endl; + } + + // pure subleading + config.subleading_fraction = 1.; + for(unsigned channel = HEJFOG::subleading::first; + channel <= HEJFOG::subleading::last; ++channel + ){ + if(config.process.njets < 4 && channel == HEJFOG::subleading::cqqx) + continue; + config.subleading_channels.reset(); + config.subleading_channels.set(channel); + HEJFOG::EventGenerator gen = make_generator(config, ran); + HEJ::CrossSectionAccumulator xs_channel; + std::cout << config.subleading_channels << " " <type()==HEJ::event_type::FKL) + continue; + ev->central().weight /= config.events; + xs_subl.fill(*ev); + xs_channel.fill(*ev); + ASSERT(valid_type(config.subleading_channels,ev->type())); + } + } + ASSERT(xs_subl.total().value!=0.); + std::cout << "=>\n" << xs_channel << std::endl; + } + std::cout << "Total:\n" << xs_tot << " vs.\n" << xs_subl << std::endl; + + const double max_sigma = short_only?3.:2.; + for(auto type: ALL_TYPES){ + double diff = 0.; + double err = 1.; + try { + auto const & tot = xs_tot[type]; + auto const & subl = xs_subl[type]; + diff = tot.value - subl.value; + err = sqrt(tot.error+subl.error); + } catch (std::out_of_range const &){ + std::cout << name(type) << " not set" << std::endl; + ASSERT_THROW(xs_tot[type], std::out_of_range); + ASSERT_THROW(xs_subl[type], std::out_of_range); + continue; + } + if(std::abs(diff) > max_sigma*err){ + std::cerr << "Large difference in " << name(type) + << " (" << (diff/err) << " sigma)\n"; + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/FixedOrderGen/t/W_2j_classify.cc b/FixedOrderGen/t/W_2j_classify.cc index 622e288..7b7ee17 100644 --- a/FixedOrderGen/t/W_2j_classify.cc +++ b/FixedOrderGen/t/W_2j_classify.cc @@ -1,168 +1,163 @@ /** * \brief check that the PSP generates only "valid" W + 2 jets events * * \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 "fastjet/JetDefinition.hh" #include "HEJ/EWConstants.hh" #include "HEJ/exceptions.hh" #include "HEJ/Mixmax.hh" #include "HEJ/Particle.hh" #include "HEJ/PDF.hh" #include "HEJ/PDG_codes.hh" #include "Decay.hh" #include "JetParameters.hh" #include "PhaseSpacePoint.hh" #include "Process.hh" #include "Status.hh" #include "Subleading.hh" namespace { using namespace HEJFOG; using namespace HEJ; void print_psp(PhaseSpacePoint const & psp){ std::cerr << "Process:\n" << psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> "; for(auto const & out: psp.outgoing()){ std::cerr << out.type << " "; } std::cerr << "\n"; } void bail_out(PhaseSpacePoint const & psp, std::string msg){ print_psp(psp); throw std::logic_error{msg}; } bool is_up_type(Particle const & part){ return HEJ::is_anyquark(part) && !(std::abs(part.type)%2); } bool is_down_type(Particle const & part){ return HEJ::is_anyquark(part) && std::abs(part.type)%2; } bool check_W2j(PhaseSpacePoint const & psp, ParticleID const W_type){ bool found_quark = false; bool found_anti = false; std::vector out_partons; std::vector Wp; for(auto const & p: psp.outgoing()){ if(p.type == W_type) Wp.push_back(p); else if(is_parton(p)) out_partons.push_back(p); else bail_out(psp, "Found particle with is not " +std::to_string(int(W_type))+" or parton"); } if(Wp.size() != 1 || out_partons.size() != 2){ bail_out(psp, "Found wrong number of outgoing partons"); } for(std::size_t j=0; j<2; ++j){ auto const & in = psp.incoming()[j]; auto const & out = out_partons[j]; if(is_quark(in) || is_antiquark(in)) { found_quark = true; if(in.type != out.type) { // switch in quark type -> Wp couples to it if(found_anti){ // already found qq for coupling to W bail_out(psp, "Found second up/down pair"); } else if(std::abs(in.type)>4 || std::abs(out.type)>4){ bail_out(psp, "Found bottom/top pair"); } found_anti = true; if( is_up_type(in)) { // "up" in if(W_type > 0){ // -> only allowed u -> Wp + d if(in.type < 0 || is_up_type(out) || out.type < 0) bail_out(psp, "u -/> Wp + d"); } else { // -> only allowed ux -> Wm + dx if(in.type > 0 || is_up_type(out) || out.type > 0) bail_out(psp, "ux -/> Wm + dx"); } } else { // "down" in if(W_type > 0){ // -> only allowed dx -> Wp + ux if(in.type > 0 || is_down_type(out) || out.type > 0) bail_out(psp, "dx -/> Wp + ux"); } else { // -> only allowed d -> Wm + u if(in.type < 0 || is_down_type(out) || out.type < 0) bail_out(psp, "d -/> Wm + u"); } } } } } if(!found_quark) { bail_out(psp, "Found no initial quarks"); } else if(!found_anti){ bail_out(psp, "Found no up/down pair"); } return true; } } int main(){ constexpr std::size_t n_psp_base = 1337; const JetParameters jet_para{ fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30}; PDF pdf(11000, pid::proton, pid::proton); constexpr double E_cms = 13000.; - constexpr double subl_change = 0.5; - constexpr auto subl_channels = Subleading::all; + constexpr double subl_chance = 0.5; + const Subleading subl_channels = subleading::ALL; const ParticlesDecayMap boson_decays{ {pid::Wp, {Decay{ {pid::e_bar, pid::nu_e}, 1.} }}, {pid::Wm, {Decay{ {pid::e, pid::nu_e_bar}, 1.} }} }; - const EWConstants ew_constants{246.2196508, - ParticleProperties{80.385, 2.085}, - ParticleProperties{91.187, 2.495}, - ParticleProperties{125, 0.004165} - }; + constexpr EWConstants ew_constants{246.2196508, {80.385, 2.085}, + {91.187, 2.495}, + {125, 0.004165} }; HEJ::Mixmax ran{}; // Wp2j Process proc {{pid::proton,pid::proton}, 2, pid::Wp, {}}; std::size_t n_psp = n_psp_base; for( std::size_t i = 0; i try again ++n_psp; } } std::cout << "Wp+2j: Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; // Wm2j proc = Process{{pid::proton,pid::proton}, 2, pid::Wm, {}}; n_psp = n_psp_base; for( std::size_t i = 0; i try again ++n_psp; } } std::cout << "Wm+2j: Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "All processes passed." << std::endl; return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/W_nj_classify.cc b/FixedOrderGen/t/W_nj_classify.cc index db1609e..185bc91 100644 --- a/FixedOrderGen/t/W_nj_classify.cc +++ b/FixedOrderGen/t/W_nj_classify.cc @@ -1,207 +1,226 @@ /** * \brief check that the PSP generates the all W+jet subleading processes * * \authors The HEJ collaboration (see AUTHORS for details) * \date 2019-2020 * \copyright GPLv2 or later */ -#ifdef NDEBUG -#undef NDEBUG -#endif #include #include #include #include #include #include #include #include #include "HEJ/Event.hh" #include "HEJ/event_types.hh" #include "HEJ/EWConstants.hh" #include "HEJ/exceptions.hh" #include "HEJ/Mixmax.hh" #include "HEJ/PDF.hh" #include "HEJ/PDG_codes.hh" #include "fastjet/JetDefinition.hh" #include "Decay.hh" #include "JetParameters.hh" #include "PhaseSpacePoint.hh" #include "Process.hh" #include "Status.hh" #include "Subleading.hh" namespace { using namespace HEJFOG; using namespace HEJ; void print_psp(PhaseSpacePoint const & psp){ std::cerr << "Process:\n" << psp.incoming()[0].type << " + "<< psp.incoming()[1].type << " -> "; for(auto const & out: psp.outgoing()){ std::cerr << out.type << " "; } std::cerr << "\n"; } void bail_out(PhaseSpacePoint const & psp, std::string msg){ print_psp(psp); throw std::logic_error{msg}; } + + #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 6) + // gcc version < 6 explicitly needs hash function for enum + // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 + using type_map = std::unordered_map>; + #else + using type_map = std::unordered_map; + #endif } int main(){ constexpr std::size_t n_psp_base = 10375; const JetParameters jet_para{ fastjet::JetDefinition(fastjet::JetAlgorithm::antikt_algorithm, 0.4), 30, 5, 30}; PDF pdf(11000, pid::proton, pid::proton); constexpr double E_cms = 13000.; - constexpr double subl_change = 0.8; + constexpr double subl_chance = 0.8; const ParticlesDecayMap boson_decays{ {pid::Wp, {Decay{ {pid::e_bar, pid::nu_e}, 1.} }}, {pid::Wm, {Decay{ {pid::e, pid::nu_e_bar}, 1.} }} }; - const EWConstants ew_constants{246.2196508, - ParticleProperties{80.385, 2.085}, - ParticleProperties{91.187, 2.495}, - ParticleProperties{125, 0.004165} + constexpr EWConstants ew_constants{246.2196508, {80.385, 2.085}, + {91.187, 2.495}, + {125, 0.004165} }; HEJ::Mixmax ran{}; - auto subl_channels = Subleading::all; + Subleading subl_channels = subleading::ALL; std::vector allowed_types{event_type::FKL, event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf}; std::cout << "Wp3j" << std::endl; // Wp3j Process proc {{pid::proton,pid::proton}, 3, pid::Wp, {}}; std::size_t n_psp = n_psp_base; - #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 - std::unordered_map> type_counter; - #else - std::unordered_map type_counter; - #endif + type_map type_counter; for( std::size_t i = 0; i try again ++n_psp; } } std::cout << "Wp+3j: Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "States by classification:\n"; for(auto const & entry: type_counter){ const double fraction = static_cast(entry.second)/n_psp_base; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(25) << (name(entry.first) + std::string(":")) << entry.second << " (" << percent << "%)\n"; } for(auto const & t: allowed_types){ if(type_counter[t] < 0.05 * n_psp_base){ std::cerr << "Less than 5% of the events are of type " << name(t) << std::endl; return EXIT_FAILURE; } } // Wm3j - only uno proc = Process{{pid::proton,pid::proton}, 3, pid::Wm, {}}; n_psp = n_psp_base; - subl_channels = Subleading::uno; + subl_channels.reset(); + subl_channels.set(subleading::uno); allowed_types = {event_type::FKL, event_type::unob, event_type::unof}; type_counter.clear(); for( std::size_t i = 0; i try again ++n_psp; } } std::cout << "Wm+3j (only uno): Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "States by classification:\n"; for(auto const & entry: type_counter){ const double fraction = static_cast(entry.second)/n_psp_base; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(25) << (name(entry.first) + std::string(":")) << entry.second << " (" << percent << "%)\n"; } for(auto const & t: allowed_types){ if(type_counter[t] < 0.05 * n_psp_base){ std::cerr << "Less than 5% of the events are of type " << name(t) << std::endl; return EXIT_FAILURE; } } // Wm4j proc = Process{{pid::proton,pid::proton}, 4, pid::Wm, {}}; n_psp = n_psp_base; - subl_channels = Subleading::all; + subl_channels.set(); allowed_types = {event_type::FKL, event_type::unob, event_type::unof, event_type::qqxexb, event_type::qqxexf, event_type::qqxmid}; type_counter.clear(); + std::array wpos_counter; // position of the W boson (back, central, forward) for( std::size_t i = 0; itype==pid::Wm){ + ++wpos_counter[0][ev.type()]; + } else if(ev.outgoing().rbegin()->type==pid::Wm){ + ++wpos_counter[2][ev.type()]; + } else { + ++wpos_counter[1][ev.type()]; + } } else { // bad process -> try again ++n_psp; } } std::cout << "Wm+4j: Took " << n_psp << " to generate " << n_psp_base << " successfully PSP (" << 1.*n_psp/n_psp_base << " trials/PSP)" << std::endl; std::cout << "States by classification:\n"; for(auto const & entry: type_counter){ const double fraction = static_cast(entry.second)/n_psp_base; const int percent = std::round(100*fraction); std::cout << std::left << std::setw(25) << (name(entry.first) + std::string(":")) << entry.second << " (" << percent << "%)\n"; } for(auto const & t: allowed_types){ if(type_counter[t] < 0.03 * n_psp_base){ std::cerr << "Less than 3% of the events are of type " << name(t) << std::endl; return EXIT_FAILURE; } } + std::cout << "Stats by Wpos:\n"; + for(std::size_t i=0; i #include #include #include #include #include "HEJ/Event.hh" #include "HEJ/Mixmax.hh" #include "HEJ/ScaleFunction.hh" #include "config.hh" #include "EventGenerator.hh" #include "Status.hh" +//! throw error if condition not fulfilled +#define ASSERT(x) if(!(x)) { \ + throw std::logic_error("Assertion '" #x "' failed."); \ + } + namespace { using namespace HEJFOG; using namespace HEJ; constexpr std::size_t num_events = 1000; constexpr double invGeV2_to_pb = 389379292.; } double get_xs(std::string config_name){ auto config { load_config(config_name) }; config.events = num_events; std::shared_ptr ran{std::make_shared(11)}; 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.; for (std::size_t trials = 0; trials < config.events; ++trials){ auto ev = generator.gen_event(); - if(generator.status() != good) continue; - assert(ev); + if(generator.status() != Status::good) continue; + ASSERT(ev); ev->central().weight *= invGeV2_to_pb; ev->central().weight /= config.events; xs += ev->central().weight; } return xs; } int main(){ - double xs_W{ get_xs("config_Wp_2j.yml")}; - double xs_enu{ get_xs("config_Wp_2j_decay.yml")}; + double xs_W{ get_xs("Wp_2j.yml")}; + double xs_enu{ get_xs("Wp_2j_decay.yml")}; if(std::abs(xs_W/xs_enu-1.)>1e-6){ std::cerr << "Reconstructing the W in the runcard gave a different results (" << xs_W << " vs. "<< xs_enu << " -> " << std::abs(xs_W/xs_enu-1.)*100 << "%)\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } diff --git a/FixedOrderGen/t/h_2j.cc b/FixedOrderGen/t/h_2j.cc deleted file mode 100644 index 05ed97f..0000000 --- a/FixedOrderGen/t/h_2j.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * \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 "HEJ/Event.hh" -#include "HEJ/Mixmax.hh" -#include "HEJ/PDG_codes.hh" -#include "HEJ/ScaleFunction.hh" - -#include "config.hh" -#include "EventGenerator.hh" -#include "Status.hh" - -int main(){ - using namespace HEJFOG; - - constexpr double invGeV2_to_pb = 389379292.; - constexpr double xs_ref = 2.04928; // +- 0.00377252 - //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 - - auto config = load_config("config_h_2j.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); - ev->central().weight *= invGeV2_to_pb; - ev->central().weight /= config.events; - - const auto the_Higgs = std::find_if( - begin(ev->outgoing()), end(ev->outgoing()), - [](HEJ::Particle const & p){ return p.type == HEJ::ParticleID::h; } - ); - assert(the_Higgs != end(ev->outgoing())); - if(std::abs(the_Higgs->rapidity()) > 5.) continue; - - 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.01*xs); - return EXIT_SUCCESS; -} diff --git a/FixedOrderGen/t/h_2j_decay.cc b/FixedOrderGen/t/h_2j_decay.cc deleted file mode 100644 index 69e609d..0000000 --- a/FixedOrderGen/t/h_2j_decay.cc +++ /dev/null @@ -1,101 +0,0 @@ -/** - * \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); - 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/FixedOrderGen/t/h_3j.cc b/FixedOrderGen/t/h_3j.cc deleted file mode 100644 index bdb19f5..0000000 --- a/FixedOrderGen/t/h_3j.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * \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 "HEJ/Event.hh" -#include "HEJ/PDG_codes.hh" -#include "HEJ/Ranlux64.hh" -#include "HEJ/ScaleFunction.hh" - -#include "config.hh" -#include "EventGenerator.hh" -#include "Status.hh" - -int main(){ - using namespace HEJFOG; - - constexpr double invGeV2_to_pb = 389379292.; - constexpr double xs_ref = 1.07807; // +- 0.0071 - //calculated with HEJ revision 93efdc851b02a907a6fcc63956387f9f4c1111c2 +1 - - auto config = load_config("config_h_2j.yml"); - config.process.njets = 3; - - 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); - ev->central().weight *= invGeV2_to_pb; - ev->central().weight /= config.events; - - const auto the_Higgs = std::find_if( - begin(ev->outgoing()), end(ev->outgoing()), - [](HEJ::Particle const & p){ return p.type == HEJ::ParticleID::h; } - ); - assert(the_Higgs != end(ev->outgoing())); - if(std::abs(the_Higgs->rapidity()) > 5.) continue; - - 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.02*xs); - return EXIT_SUCCESS; -} diff --git a/FixedOrderGen/t/h_3j_uno2.cc b/FixedOrderGen/t/h_3j_uno2.cc deleted file mode 100644 index 81fb910..0000000 --- a/FixedOrderGen/t/h_3j_uno2.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * check uno cross section - * - * \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 "HEJ/Event.hh" -#include "HEJ/event_types.hh" -#include "HEJ/PDG_codes.hh" -#include "HEJ/Ranlux64.hh" -#include "HEJ/ScaleFunction.hh" - -#include "config.hh" -#include "EventGenerator.hh" -#include "Subleading.hh" -#include "Status.hh" - -int main(){ - using namespace HEJFOG; - - constexpr double invGeV2_to_pb = 389379292.; - constexpr double xs_ref = 0.00347538; // +- 3.85875e-05 - //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 - - auto config = load_config("config_h_2j.yml"); - config.process.njets = 3; - config.process.incoming = {HEJ::pid::u, HEJ::pid::u}; - config.subleading_fraction = 1.; - config.subleading_channels = HEJFOG::Subleading::uno; - - 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); - if(ev->type() == HEJ::event_type::FKL) 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.05*xs); - return EXIT_SUCCESS; -} diff --git a/FixedOrderGen/t/h_5j.cc b/FixedOrderGen/t/h_5j.cc deleted file mode 100644 index 75c880c..0000000 --- a/FixedOrderGen/t/h_5j.cc +++ /dev/null @@ -1,74 +0,0 @@ -/** - * This is a regression test - * the reference cross section has not been checked against any other program - * - * \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 "HEJ/Event.hh" -#include "HEJ/Ranlux64.hh" -#include "HEJ/ScaleFunction.hh" - -#include "config.hh" -#include "EventGenerator.hh" -#include "Status.hh" - -int main(){ - using namespace HEJFOG; - - constexpr double invGeV2_to_pb = 389379292.; - constexpr double xs_ref = 0.252273; // +- 0.00657742 - //calculated with HEJ revision 9570e3809613272ac4b8bf3236279ba23cf64d20 - - auto config = load_config("config_h_2j.yml"); - config.process.njets = 5; - - 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); - 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.06*xs); - return EXIT_SUCCESS; -} diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/2j.yml similarity index 90% copy from FixedOrderGen/t/config_2j.yml copy to FixedOrderGen/t/runcards/2j.yml index 31ebe8b..137650c 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/2j.yml @@ -1,42 +1,43 @@ events: 100000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 6500 particles: [p, p] pdf: 11000 process: p p => 2j subleading fraction: 0.1 subleading channels: - - qqx - - uno + - extremal qqx + - central qqx + - unordered scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_2j_peak.yml b/FixedOrderGen/t/runcards/2j_peak.yml similarity index 94% rename from FixedOrderGen/t/config_2j_peak.yml rename to FixedOrderGen/t/runcards/2j_peak.yml index 346e13f..deeec6e 100644 --- a/FixedOrderGen/t/config_2j_peak.yml +++ b/FixedOrderGen/t/runcards/2j_peak.yml @@ -1,41 +1,41 @@ events: 1000 jets: min pt: 20 R: 0.4 algorithm: antikt max rapidity: 5 peak pt: 30 unweight: sample size: 100 max deviation: 1 beam: energy: 6500 particles: [p, p] pdf: 11000 process: p p => 2j subleading fraction: 0.1 -subleading channels: none +subleading channels: all scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/3j_qqx.yml similarity index 74% copy from FixedOrderGen/t/config_2j.yml copy to FixedOrderGen/t/runcards/3j_qqx.yml index 31ebe8b..2873ade 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/3j_qqx.yml @@ -1,42 +1,41 @@ -events: 100000 +events: 300000 jets: - min pt: 30 + min pt: 50 R: 0.4 algorithm: antikt - max rapidity: 5 + max rapidity: 4 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => 2j +process: p p => 3j -subleading fraction: 0.1 +subleading fraction: 1 subleading channels: - - qqx - - uno + - eqqx -scales: 125 +scales: 91 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/3j_uno.yml similarity index 74% copy from FixedOrderGen/t/config_2j.yml copy to FixedOrderGen/t/runcards/3j_uno.yml index 31ebe8b..5f4e0db 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/3j_uno.yml @@ -1,42 +1,41 @@ -events: 100000 +events: 200000 jets: - min pt: 30 - R: 0.4 + min pt: 40 + R: 0.5 algorithm: antikt - max rapidity: 5 + max rapidity: 6 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => 2j +process: p p => 3j -subleading fraction: 0.1 +subleading fraction: 1 subleading channels: - - qqx - uno -scales: 125 +scales: 123 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/4j.yml similarity index 89% copy from FixedOrderGen/t/config_2j.yml copy to FixedOrderGen/t/runcards/4j.yml index 31ebe8b..8bd1134 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/4j.yml @@ -1,42 +1,43 @@ -events: 100000 +events: 250000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => 2j +process: p p => 4j subleading fraction: 0.1 subleading channels: - - qqx + - cqqx + - eqqx - uno scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/4j_qqx.yml similarity index 80% copy from FixedOrderGen/t/config_2j.yml copy to FixedOrderGen/t/runcards/4j_qqx.yml index 31ebe8b..f16787e 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/4j_qqx.yml @@ -1,42 +1,42 @@ -events: 100000 +events: 1100000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: - energy: 6500 + energy: 3500 particles: [p, p] pdf: 11000 -process: p p => 2j +process: p p => 4j -subleading fraction: 0.1 +subleading fraction: 1 subleading channels: - - qqx - - uno + - cqqx + - eqqx scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/5j.yml similarity index 77% copy from FixedOrderGen/t/config_2j.yml copy to FixedOrderGen/t/runcards/5j.yml index 31ebe8b..666455e 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/5j.yml @@ -1,42 +1,39 @@ -events: 100000 +events: 500000 jets: min pt: 30 R: 0.4 algorithm: antikt - max rapidity: 5 + max rapidity: 6 beam: - energy: 6500 + energy: 7000 particles: [p, p] pdf: 11000 -process: p p => 2j +process: p p => 5j subleading fraction: 0.1 -subleading channels: - - qqx - - uno scales: 125 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_2j.yml b/FixedOrderGen/t/runcards/Wm2j.yml similarity index 72% rename from FixedOrderGen/t/config_2j.yml rename to FixedOrderGen/t/runcards/Wm2j.yml index 31ebe8b..b31fe82 100644 --- a/FixedOrderGen/t/config_2j.yml +++ b/FixedOrderGen/t/runcards/Wm2j.yml @@ -1,42 +1,40 @@ -events: 100000 +events: 200000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: - energy: 6500 + energy: 10000 particles: [p, p] pdf: 11000 -process: p p => 2j +process: p p => 2j e- electron_antineutrino -subleading fraction: 0.1 -subleading channels: - - qqx - - uno +subleading fraction: 0 +subleading channels: none -scales: 125 +scales: 91 random generator: name: mixmax particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 vev: 246.2196508 unweight: sample size: 100 max deviation: -10 diff --git a/FixedOrderGen/t/config_h_2j.yml b/FixedOrderGen/t/runcards/Wm5j.yml similarity index 62% copy from FixedOrderGen/t/config_h_2j.yml copy to FixedOrderGen/t/runcards/Wm5j.yml index 7e8695e..77b3cd5 100644 --- a/FixedOrderGen/t/config_h_2j.yml +++ b/FixedOrderGen/t/runcards/Wm5j.yml @@ -1,36 +1,38 @@ -events: 200000 +events: 4000000 jets: - min pt: 30 + min pt: 50 R: 0.4 algorithm: antikt max rapidity: 5 beam: - energy: 6500 + energy: 15000 particles: [p, p] pdf: 11000 -process: p p => h 2j +process: p p => W- 5j -subleading fraction: 0.37 -subleading channels: none +subleading fraction: 1. -scales: 125 +scales: 91 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 +decays: + Wm: {into: [e-, electron_antineutrino], branching ratio: 1} + random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j_decay.yml b/FixedOrderGen/t/runcards/Wp3j_qqx.yml similarity index 71% copy from FixedOrderGen/t/config_Wp_2j_decay.yml copy to FixedOrderGen/t/runcards/Wp3j_qqx.yml index 37ce351..6a738b9 100644 --- a/FixedOrderGen/t/config_Wp_2j_decay.yml +++ b/FixedOrderGen/t/runcards/Wp3j_qqx.yml @@ -1,38 +1,40 @@ -events: 200000 +events: 1000000 jets: - min pt: 30 + min pt: 20 R: 0.4 algorithm: antikt max rapidity: 5 beam: - energy: 6500 + energy: 5000 particles: [p, p] pdf: 11000 -process: p p => Wp 2j +process: p p => W+ 3j -subleading fraction: 0.37 +subleading fraction: 1. +subleading channels: + - eqqx scales: 125 particle properties: Higgs: mass: 125 - width: 0.004165 + width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j_decay.yml b/FixedOrderGen/t/runcards/Wp3j_uno.yml similarity index 68% copy from FixedOrderGen/t/config_Wp_2j_decay.yml copy to FixedOrderGen/t/runcards/Wp3j_uno.yml index 37ce351..9b39135 100644 --- a/FixedOrderGen/t/config_Wp_2j_decay.yml +++ b/FixedOrderGen/t/runcards/Wp3j_uno.yml @@ -1,38 +1,40 @@ -events: 200000 +events: 2000000 jets: - min pt: 30 - R: 0.4 + min pt: 100 + R: 0.5 algorithm: antikt max rapidity: 5 beam: - energy: 6500 + energy: 8000 particles: [p, p] pdf: 11000 -process: p p => Wp 2j +process: u u => W+ 3j -subleading fraction: 0.37 +subleading fraction: 1. +subleading channels: + - unordered scales: 125 particle properties: Higgs: mass: 125 - width: 0.004165 + width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j_decay.yml b/FixedOrderGen/t/runcards/Wp4j_qqx.yml similarity index 66% copy from FixedOrderGen/t/config_Wp_2j_decay.yml copy to FixedOrderGen/t/runcards/Wp4j_qqx.yml index 37ce351..3228cf1 100644 --- a/FixedOrderGen/t/config_Wp_2j_decay.yml +++ b/FixedOrderGen/t/runcards/Wp4j_qqx.yml @@ -1,38 +1,41 @@ -events: 200000 +events: 1000000 jets: - min pt: 30 + min pt: 40 R: 0.4 algorithm: antikt - max rapidity: 5 + max rapidity: 6 beam: - energy: 6500 + energy: 6000 particles: [p, p] pdf: 11000 -process: p p => Wp 2j +process: p d => W+ 4j -subleading fraction: 0.37 +subleading fraction: 1. +subleading channels: + - cqqx + - eqqx scales: 125 particle properties: Higgs: mass: 125 - width: 0.004165 + width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j_decay.yml b/FixedOrderGen/t/runcards/Wp4j_uno+eqqx.yml similarity index 61% copy from FixedOrderGen/t/config_Wp_2j_decay.yml copy to FixedOrderGen/t/runcards/Wp4j_uno+eqqx.yml index 37ce351..fc223db 100644 --- a/FixedOrderGen/t/config_Wp_2j_decay.yml +++ b/FixedOrderGen/t/runcards/Wp4j_uno+eqqx.yml @@ -1,38 +1,42 @@ -events: 200000 +events: 10000 jets: - min pt: 30 + min pt: 40 R: 0.4 algorithm: antikt - max rapidity: 5 + max rapidity: 6 beam: - energy: 6500 + energy: 6000 particles: [p, p] pdf: 11000 -process: p p => Wp 2j +# code should enforce coupling at proton leg +process: d p => W+ 4j -subleading fraction: 0.37 +subleading fraction: 1. +subleading channels: + - eqqx + - uno scales: 125 particle properties: Higgs: mass: 125 - width: 0.004165 + width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 decays: Wp: {into: [e+, nu_e], branching ratio: 1} random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j.yml b/FixedOrderGen/t/runcards/Wp_2j.yml similarity index 100% copy from FixedOrderGen/t/config_Wp_2j.yml copy to FixedOrderGen/t/runcards/Wp_2j.yml diff --git a/FixedOrderGen/t/config_Wp_2j_decay.yml b/FixedOrderGen/t/runcards/Wp_2j_decay.yml similarity index 100% copy from FixedOrderGen/t/config_Wp_2j_decay.yml copy to FixedOrderGen/t/runcards/Wp_2j_decay.yml diff --git a/FixedOrderGen/t/config_Wp_2j.yml b/FixedOrderGen/t/runcards/WpqQ.yml similarity index 67% copy from FixedOrderGen/t/config_Wp_2j.yml copy to FixedOrderGen/t/runcards/WpqQ.yml index 14b9216..f57da2d 100644 --- a/FixedOrderGen/t/config_Wp_2j.yml +++ b/FixedOrderGen/t/runcards/WpqQ.yml @@ -1,35 +1,35 @@ -events: 200000 +events: 400000 jets: - min pt: 30 - R: 0.4 + min pt: 20 algorithm: antikt - max rapidity: 5 + R: 0.4 + max rapidity: 6 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => e+ nu_e 2j +process: u s => e+ nu_e 2j + +subleading fraction: .4 -subleading fraction: 0.37 +scales: 125. + +random generator: + name: mixmax -scales: 125 +vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: - mass: 91.187 + mass: 91.1876 width: 2.495 - -random generator: - name: mixmax - -vev: 246.2196508 diff --git a/FixedOrderGen/t/config_h_2j.yml b/FixedOrderGen/t/runcards/h2j.yml similarity index 100% copy from FixedOrderGen/t/config_h_2j.yml copy to FixedOrderGen/t/runcards/h2j.yml diff --git a/FixedOrderGen/t/config_h_2j_decay.yml b/FixedOrderGen/t/runcards/h2j_decay.yml similarity index 100% rename from FixedOrderGen/t/config_h_2j_decay.yml rename to FixedOrderGen/t/runcards/h2j_decay.yml diff --git a/FixedOrderGen/t/config_h_2j.yml b/FixedOrderGen/t/runcards/h3j.yml similarity index 88% copy from FixedOrderGen/t/config_h_2j.yml copy to FixedOrderGen/t/runcards/h3j.yml index 7e8695e..bf7c437 100644 --- a/FixedOrderGen/t/config_h_2j.yml +++ b/FixedOrderGen/t/runcards/h3j.yml @@ -1,36 +1,36 @@ events: 200000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => h 2j +process: p p => h 3j subleading fraction: 0.37 -subleading channels: none +subleading channels: scales: 125 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_h_2j.yml b/FixedOrderGen/t/runcards/h3j_uno.yml similarity index 77% copy from FixedOrderGen/t/config_h_2j.yml copy to FixedOrderGen/t/runcards/h3j_uno.yml index 7e8695e..11ed2d4 100644 --- a/FixedOrderGen/t/config_h_2j.yml +++ b/FixedOrderGen/t/runcards/h3j_uno.yml @@ -1,36 +1,37 @@ -events: 200000 +events: 700000 jets: min pt: 30 R: 0.4 algorithm: antikt max rapidity: 5 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => h 2j +process: u u => h 3j -subleading fraction: 0.37 -subleading channels: none +subleading fraction: 1. +subleading channels: + - unordered scales: 125 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_h_2j.yml b/FixedOrderGen/t/runcards/h5j.yml similarity index 83% rename from FixedOrderGen/t/config_h_2j.yml rename to FixedOrderGen/t/runcards/h5j.yml index 7e8695e..a6b9bac 100644 --- a/FixedOrderGen/t/config_h_2j.yml +++ b/FixedOrderGen/t/runcards/h5j.yml @@ -1,36 +1,36 @@ -events: 200000 +events: 1000000 jets: - min pt: 30 + min pt: 100 R: 0.4 algorithm: antikt max rapidity: 5 beam: - energy: 6500 + energy: 10000 particles: [p, p] pdf: 11000 -process: p p => h 2j +process: p p => h 5j subleading fraction: 0.37 subleading channels: none scales: 125 particle properties: Higgs: mass: 125 width: 0 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 random generator: name: mixmax vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j_decay.yml b/FixedOrderGen/t/runcards/hqQ.yml similarity index 60% rename from FixedOrderGen/t/config_Wp_2j_decay.yml rename to FixedOrderGen/t/runcards/hqQ.yml index 37ce351..a3849ea 100644 --- a/FixedOrderGen/t/config_Wp_2j_decay.yml +++ b/FixedOrderGen/t/runcards/hqQ.yml @@ -1,38 +1,40 @@ -events: 200000 +events: 400000 jets: - min pt: 30 - R: 0.4 + min pt: 20 algorithm: antikt - max rapidity: 5 + R: 0.4 + max rapidity: 6 beam: energy: 6500 particles: [p, p] pdf: 11000 -process: p p => Wp 2j +process: u d => h 2j + +subleading fraction: .4 -subleading fraction: 0.37 +scales: 125. -scales: 125 +random generator: + name: mixmax + +decays: + Higgs: + branching ratio: 1 + into: [photon, photon] + +vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: - mass: 91.187 + mass: 91.1876 width: 2.495 - -decays: - Wp: {into: [e+, nu_e], branching ratio: 1} - -random generator: - name: mixmax - -vev: 246.2196508 diff --git a/FixedOrderGen/t/config_Wp_2j.yml b/FixedOrderGen/t/runcards/qQ.yml similarity index 64% rename from FixedOrderGen/t/config_Wp_2j.yml rename to FixedOrderGen/t/runcards/qQ.yml index 14b9216..483f8f1 100644 --- a/FixedOrderGen/t/config_Wp_2j.yml +++ b/FixedOrderGen/t/runcards/qQ.yml @@ -1,35 +1,35 @@ -events: 200000 +events: 400000 jets: - min pt: 30 - R: 0.4 + min pt: 20 algorithm: antikt - max rapidity: 5 + R: 0.4 + max rapidity: 6 beam: - energy: 6500 + energy: 7000 particles: [p, p] pdf: 11000 -process: p p => e+ nu_e 2j +process: u d => 2j + +subleading fraction: .4 -subleading fraction: 0.37 +scales: 125. + +random generator: + name: mixmax -scales: 125 +vev: 246.2196508 particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: - mass: 91.187 + mass: 91.1876 width: 2.495 - -random generator: - name: mixmax - -vev: 246.2196508 diff --git a/FixedOrderGen/t/xs_gen.cc b/FixedOrderGen/t/xs_gen.cc new file mode 100644 index 0000000..1b02581 --- /dev/null +++ b/FixedOrderGen/t/xs_gen.cc @@ -0,0 +1,114 @@ +/** + * \authors The HEJ collaboration (see AUTHORS for details) + * \date 2020 + * \copyright GPLv2 or later + */ +#include +#include +#include +#include +#include + +#include "HEJ/Event.hh" +#include "HEJ/Mixmax.hh" +#include "HEJ/PDG_codes.hh" +#include "HEJ/ScaleFunction.hh" +#include "HEJ/utility.hh" + +#include "config.hh" +#include "EventGenerator.hh" +#include "Status.hh" + +//! throw error if condition not fulfilled +#define ASSERT(x) if(!(x)) { \ + throw std::logic_error("Assertion '" #x "' failed."); \ + } + +namespace { + constexpr double invGeV2_to_pb = 389379292.; +} + +int main(int argn, char const *argv[]){ + using namespace HEJFOG; + if(argn != 4 && argn != 5){ + std::cerr << "Usage: " << argv[0] << " config.yml xs xs_err"; + return EXIT_FAILURE; + } + + const bool short_only = argn==5; + + const double xs_ref = std::stod(argv[2]); + const double err_ref = std::stod(argv[3]); + + auto config = load_config(argv[1]); + + 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 + }; + if(short_only) + config.events/=10.; + + double xs = 0., xs_err = 0.; + for (std::size_t trials = 0; trials < config.events; ++trials){ + auto ev = generator.gen_event(); + if(generator.status() != Status::good) continue; + ASSERT(ev); + + if(config.process.boson){ + const auto boson = std::find_if( + begin(ev->outgoing()), end(ev->outgoing()), + [&](HEJ::Particle const & p){ return p.type == *config.process.boson; } + ); + ASSERT(boson != end(ev->outgoing())); + + if(!config.process.boson_decay.empty()){ + ASSERT(ev->decays().size() == 1); + const auto decay = ev->decays().begin(); + ASSERT(ev->outgoing().size() > decay->first); + ASSERT(decay->first == static_cast( + std::distance(ev->outgoing().begin(), boson))); + ASSERT(decay->second.size() == 2); + auto const & decay_part = decay->second; + ASSERT(decay_part[0].type == config.process.boson_decay[0]); + ASSERT(decay_part[1].type == config.process.boson_decay[1]); + ASSERT(HEJ::nearby_ep(decay_part[0].p + decay_part[1].p, boson->p, 1e-6)); + } + } + + xs += ev->central().weight; + xs_err += ev->central().weight*ev->central().weight; + } + xs_err = std::sqrt(xs_err); + xs *= invGeV2_to_pb/config.events; + xs_err *= invGeV2_to_pb/config.events; + std::cout < " + << std::abs(xs - xs_ref)/sqrt(xs_err*xs_err+err_ref*err_ref) + << " sigma" << std::endl; + + ASSERT(std::abs(xs - xs_ref) < 2.*sqrt(xs_err*xs_err+err_ref*err_ref)); + if(!short_only){ + ASSERT(std::abs(err_ref - xs_err) < 0.1*xs_err); + } + + return EXIT_SUCCESS; +} diff --git a/config.yml b/config.yml index 59e9a43..8d52b20 100644 --- a/config.yml +++ b/config.yml @@ -1,125 +1,129 @@ ## Number of attempted resummation phase space points for each input event trials: 10 -## Maximum soft transverse momentum fraction in extremal jets -max ext soft pt fraction: 0.1 - resummation jets: # resummation jet properties min pt: 30 # minimum jet transverse momentum algorithm: antikt # jet clustering algorithm R: 0.4 # jet R parameter fixed order jets: # properties of input jets min pt: 20 # by default, algorithm and R are like for resummation jets ## Treatment of he various event classes ## the supported settings are: reweight, keep, discard ## non-resummable events cannot be reweighted event treatment: FKL: reweight unordered: keep extremal qqx: keep central qqx: keep non-resummable: keep ## Central scale choice or choices # ## multiple scales are allowed, e.g. # scales: [125, max jet pperp, H_T/2, 2*jet invariant mass, m_j1j2] scales: 91.188 ## Factors by which the central scales should be multiplied ## renormalisation and factorisation scales are varied independently # # scale factors: [0.5, 0.7071, 1, 1.41421, 2] ## Maximum ratio between renormalisation and factorisation scale # # max scale ratio: 2.0001 ## Import scale setting functions # # import scales: # lib_my_scales.so: [scale0,scale1] ## Unweighting setting ## remove to obtain weighted events # unweight: # # type of unweighting (one of 'weighted', 'resummation', 'partial') # type: partial # trials: 10000 # max deviation: 0 ## Event output files # # the supported formats are # - Les Houches (suffix .lhe) # - HepMC2 (suffix .hepmc2) # - HepMC3 (suffix .hepmc3 or .hepmc) # - HDF5 (suffix .hdf5) # ## An output file's format is deduced either automatically from the suffix ## or from an explicit specification, e.g. ## - Les Houches: outfile # event output: - HEJ.lhe # - HEJ_events.hepmc ## Analyses # # analyses: ## Rivet analysis # - rivet: MC_XS # rivet analysis name # output: HEJ # name of the yoda files, ".yoda" and scale suffix will be added ## Custom analysis # - plugin: /path/to/libmyanalysis.so # my analysis parameter: some value ## Selection of random number generator and seed ## The choices are ## - mixmax (seed is an integer) ## - ranlux64 (seed is a filename containing parameters) random generator: name: mixmax # seed: 1 ## Whether or not to include higher order logs log correction: false ## Vacuum expectation value vev: 246.2196508 ## Properties of the weak gauge bosons particle properties: Higgs: mass: 125 width: 0.004165 W: mass: 80.385 width: 2.085 Z: mass: 91.187 width: 2.495 ## Parameters for Higgs-gluon couplings ## This requires compilation with QCDloop # # Higgs coupling: # use impact factors: false # mt: 174 # include bottom: true # mb: 4.7 ## ---------------------------------------------------------------------- ## ## The following settings are only intended for advanced users. ## ## Please DO NOT SET them unless you know exactly what you are doing! ## ## ---------------------------------------------------------------------- ## # +## Maximum soft transverse momentum fraction in any tagging jets, e.g. +## extremal or qqx jet +# soft pt regulator: 0.1 +# ## Minimum transverse momentum of extremal partons -## deprecated: use "max ext soft pt fraction" instead +## deprecated: use "soft pt regulator" instead # min extparton pt: 30 # +## deprecated: this cot directly replaced by "soft pt regulator" +# max ext soft pt fraction: 0.1 +# # max events: -1 # Maximal number of fixed order Events to process # regulator parameter: 0.2 # The regulator lambda for the subtraction terms diff --git a/current_generator/CMakeLists.txt b/current_generator/CMakeLists.txt index 654b53e..ee03585 100644 --- a/current_generator/CMakeLists.txt +++ b/current_generator/CMakeLists.txt @@ -1,35 +1,34 @@ set(form_folder ${CMAKE_CURRENT_SOURCE_DIR}/form) include(ExternalProject) ExternalProject_Add( FORM - GIT_SUBMODULES ${form_folder} SOURCE_DIR ${form_folder} INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR} CONFIGURE_COMMAND autoreconf -i ${form_folder} COMMAND CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} ${form_folder}/configure --without-gmp --disable-native --without-zlib --disable-threaded --prefix= -q # we only run FORM once, don't spend time on optimising it COMPILEFLAGS="-O0" ) set(form_binary ${CMAKE_CURRENT_BINARY_DIR}/bin/form) file(GLOB form_headers include/*.frm) file(GLOB current_files *.frm) foreach(current ${current_files}) get_filename_component(header_name ${current} NAME_WE) set(header ${PROJECT_BINARY_DIR}/include/HEJ/currents/${header_name}.hh) list(APPEND current_headers ${header}) add_custom_command( OUTPUT ${header} COMMAND ./gen_current.sh ${form_binary} ${current} ${header} ${CMAKE_CURRENT_BINARY_DIR}/${header_name}.log WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${current} ${form_headers} gen_current.sh FORM ) endforeach() add_custom_target(currents DEPENDS ${current_headers}) diff --git a/current_generator/include/currents.frm b/current_generator/include/currents.frm index a8d213e..9f9a68d 100644 --- a/current_generator/include/currents.frm +++ b/current_generator/include/currents.frm @@ -1,51 +1,134 @@ */** * \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; +* Extremal Higgs boson emission vertex +cfunction JH; * FKL current for vector boson cfunction JV; * Polarisation vectors cfunction Eps; -cfunction m2,sqrt; +* Polarisation vectors contracted with Higgs emission vertex +cfunction EPSH; +* Higgs emission vertex +cfunction VH; +cfunction m2,plus,minus,perphat,sqrt,conj,dot; +cf T1,T2; +cf ,...,; * internal symbols are all uppercase -symbols H, H1, HL; +symbols H, H1, HL, M; indices MU1,...,MU50; -vectors PA,P1,PLBAR,PL,PG,PR; +vectors PA,P1,PH,PLBAR,PL,PG,PR,Q1,Q2; #procedure InsSpecialCurrents #call InsJV + #call InsJH #call InsEps + #call InsVH #endprocedure * Replace vector boson FKL current #procedure InsJV() * we use a loop here to introduce a new dummy index for each occurence repeat; 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 Conjugate(Eps(H1?, MU1?, PG?, PR?)) = - Eps(-H1, MU1, PG, PR); 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 +* Higgs emission vertex +* see eq:VH in developer manual, but without overall prefactor +#procedure InsVH() + + id VH(MU1?, MU2?, Q1?, Q2?) = d_(MU1, MU2)*T1 - Q2(MU1)*Q1(MU2)*T2; + +#endprocedure + +* Higgs currents +* see eq:jH_same_helicity and jH_helicity_flip in developer manual +* the overall prefactor is omitted +#procedure InsJH() + + id JH(+1, +1, MU1?, P1?, PA?, PH?, M?) = + conj(perphat(P1))*m2(PA - P1 - PH)*( + - sqrt(2*minus(P1)/minus(PA))/square(PA, P1)*Conjugate(EPSH(+1, MU1, P1, PH, PA, M)) + + sqrt(2*minus(PA)/minus(P1))/angle(P1, PA)*EPSH(+1, MU1, PA, PH, P1, M) + ) + + sqrt(2)*Current(+1, P1, PH, PA)*( + + EPSH(+1, MU1, PA, PH, P1, M)/angle(P1, PA) + + Conjugate(EPSH(+1, MU1, P1, PH, PA, M))/square(PA, P1) + - angle(P1, PA)/(2*m2(PA - PH))*T2(PA, PA-PH, M)*Conjugate(Eps(+1, MU1, P1, PA)) + - square(PA, P1)/(2*m2(P1 + PH))*T2(P1+PH, P1, M)*Eps(+1, MU1, PA, P1) + - 4*pi_^2*i_*H4DD(P1, -PA, PH, M)/square(PA, P1)*Conjugate(Eps(+1, MU1, P1, PA)) + + 4*pi_^2*i_*H5DD(P1, -PA, PH, M)/angle(P1, PA)*Eps(+1, MU1, PA, P1) + ) + - 4*pi_^2*i_*Current(+1, P1, PH, PA)^2/m2(PA-P1)*( + PA(MU1)*H10DD(P1, -PA, PH, M) - P1(MU1)*H12DD(P1, -PA, PH, M) + ) + ; + +* TODO: +* signs of negative-helicity polarisation vectors +* and of the terms proportional to H10, H12 +* differ from developer manual and arXiv:1812.08072 + id JH(+1, -1, MU1?, P1?, PA?, PH?, M?) = + m2(PA - P1 - PH)/square(PA, P1)*( + + sqrt(2*minus(P1)/minus(PA))*conj(perphat(P1))*Conjugate(EPSH(-1, MU1, P1, PH, PA, M)) + + sqrt(2*minus(PA)/minus(P1))*perphat(P1)*EPSH(+1, MU1, PA, PH, P1, M) + ) + + sqrt(2)*Current(+1, P1, PH, PA)*( + - Conjugate(EPSH(-1, MU1, P1, PH, PA, M))/square(PA, P1) + + angle(P1, PA)/(2*m2(PA - PH))*T2(PA, PA-PH, M)*Conjugate(Eps(-1, MU1, P1, PA)) + + 4*pi_^2*i_*H4DD(P1, -PA, PH, M)/square(PA, P1)*Conjugate(Eps(-1, MU1, P1, PA)) + ) + + sqrt(2)*Current(+1, PA, PH, P1)*( + + EPSH(+1, MU1, PA, PH, P1, M)/square(PA, P1) + - angle(P1, PA)/(2*m2(P1 + PH))*T2(P1+PH, P1, M)*Eps(+1, MU1, PA, P1) + + 4*pi_^2*i_*H5DD(P1, -PA, PH, M)/square(PA, P1)*Eps(+1, MU1, PA, P1) + ) + + 4*pi_^2*i_*Current(+1, P1, PH, PA)*Current(+1, PA, PH, P1)/square(PA, P1)^2*( + PA(MU1)*H10DD(P1, -PA, PH, M) - P1(MU1)*H12DD(P1, -PA, PH, M) + ) + + angle(P1, PA)/square(PA, P1)*( + + 8*pi_^2*i_*H1DD(P1, -PA, PH, M)*P1(MU1) + - 8*pi_^2*i_*H2DD(P1, -PA, PH, M)*PA(MU1) + + 2*dot(P1, PH)*T2(P1+PH, P1, M)/m2(P1+PH)*PA(MU1) + - 2*dot(PA, PH)*T2(PA, PA-PH, M)/m2(PA-PH)*P1(MU1) + + T1(PA-P1, PA-P1-PH, M)/m2(PA-P1)*(P1(MU1) + PA(MU1)) + - dot(P1+PA, PH)/m2(PA-P1)*T2(PA-P1, PA-P1-PH, M)*(P1(MU1) - PA(MU1)) + ); + +* eq:eps_H in developer manual + id EPSH(H1?, MU1?, PA?, PH?, P1?, M?) = T2(PA, PA-PH, M)/m2(PA - PH)*( + dot(PA,PH)*Eps(H1, MU1, PA, P1) - Eps(H1, PH, PA, P1)*PA(MU1) + ); + id Conjugate(EPSH(H1?, MU1?, P1?, PH?, PA?, M?)) = -T2(P1+PH, P1, M)/m2(P1 + PH)*( + dot(P1,PH)*Conjugate(Eps(H1, MU1, P1, PA)) - Conjugate(Eps(H1, PH, P1, PA))*P1(MU1) + ); + +#endprocedure + #endif diff --git a/current_generator/include/write.frm b/current_generator/include/write.frm index 78cb2cd..9903829 100644 --- a/current_generator/include/write.frm +++ b/current_generator/include/write.frm @@ -1,166 +1,162 @@ */** * \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) +#procedure WriteOptimised(OUTPUT,EXPRESSION,NUMHELICITIES,?ARGS) #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') + #call WriteArgs(`OUTPUT',`?ARGS') #write <`OUTPUT'> "\n );\n" - #call WriteOptimisedHelper(`OUTPUT',`EXPRESSION',`NUMHELICITIES',`?MOMENTA') + #call WriteOptimisedHelper(`OUTPUT',`EXPRESSION',`NUMHELICITIES',`?ARGS') #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 "" + #define FARGS "" #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'," +* C++ function arguments + #if "`FARGS'" != "" + #redefine FARGS "`FARGS'," #endif - #redefine MOMENTA "`MOMENTA'`ARG'" + #redefine FARGS "`FARGS'`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 + #call WriteArgs(`OUTPUT',`FARGS') #write <`OUTPUT'> "\n ) {" + #write <`OUTPUT'> " using namespace currents;" #write <`OUTPUT'> " static constexpr std::complex i_{0., 1.};" - #write <`OUTPUT'> " ignore(i_); //potentially unused" + #write <`OUTPUT'> " static constexpr double pi_{M_PI};" + #write <`OUTPUT'> " ignore(i_,pi_%" + #do ARG={`FARGS'} + #if ("`ARG'" != "COMPLEX:") && ("`ARG'" != "DOUBLE:") && ("`ARG'" != "VECTORS:") + #write <`OUTPUT'> ",`ARG'%" + #endif + #enddo + #write <`OUTPUT'> ");" #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) +* Write C++ function arguments to `OUTPUT' +#procedure WriteArgs(OUTPUT,?ARGS) + #define TYPE "CLHEP::HepLorentzVector const &" #define FIRST "1" - #do P={`?MOMENTA'} - #ifdef `FIRST' - #undefine FIRST - #write <`OUTPUT'> " CLHEP::HepLorentzVector const & `P'%" + #do P={`?ARGS'} + #if "`P'" == "COMPLEX:" + #redefine TYPE "std::complex const &" + #elseif "`P'" == "DOUBLE:" + #redefine TYPE "double const" + #elseif "`P'" == "VECTORS:" + #redefine TYPE "CLHEP::HepLorentzVector const &" #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' */%" + #ifdef `FIRST' + #undefine FIRST + #else + #write <`OUTPUT'> "," + #endif + #write <`OUTPUT'> " `TYPE' `P'%" #endif #enddo #endprocedure * Write end of C++ header to `OUTPUT' #procedure WriteFooter(OUTPUT) #write <`OUTPUT'> "}" #endprocedure #endif diff --git a/current_generator/j_h_j.frm b/current_generator/j_h_j.frm new file mode 100644 index 0000000..0eb7f21 --- /dev/null +++ b/current_generator/j_h_j.frm @@ -0,0 +1,43 @@ +*/** +* \brief Contraction of two FKL currents with central Higgs boson emission +* +* \authors The HEJ collaboration (see AUTHORS for details) +* \date 2020 +* \copyright GPLv2 or later +* +* The t-channel momenta before and after the Higgs boson emission +* are decomposed into +* q1 = q11 - q12 +* q2 = q21 - q22 +* such that the momenta on the right-hand side are lightlike and +* have positive energy. +*/ +#include- include/helspin.frm +#include- include/write.frm + +v pb,p2,pa,p1,q1,q2,q11,q12,q21,q22; +i mu,nu; + +#do h1={+,-} + #do h2={+,-} + l [j_h_j `h1'`h2'] = ( + Current(`h1'1, pa, mu, p1) + *VH(mu, nu, q1, q2) + *Current(`h2'1, p2, nu, pb) + ); + #enddo +#enddo + +multiply replace_( + q1, q11 - q12, + q2, q21 - q22 +); + +#call ContractCurrents +.sort +format O4; +format c; +#call WriteHeader(`OUTPUT') +#call WriteOptimised(`OUTPUT',j_h_j,2,pa,p1,pb,p2,q11,q12,q21,q22,COMPLEX:,T1,T2) +#call WriteFooter(`OUTPUT') +.end diff --git a/current_generator/j_j.frm b/current_generator/j_j.frm new file mode 100644 index 0000000..e99dc03 --- /dev/null +++ b/current_generator/j_j.frm @@ -0,0 +1,27 @@ +*/** +* \brief Contraction of two FKL currents +* +* \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; +i mu; + +#do h1={+,-} + #do h2={+,-} + l [j_j `h1'`h2'] = Current(`h1'1, pa, mu, p1)*Current(`h2'1, p2, mu, pb); + #enddo +#enddo + +#call ContractCurrents +.sort +format O4; +format c; +#call WriteHeader(`OUTPUT') +#call WriteOptimised(`OUTPUT',j_j,2,pa,p1,pb,p2) +#call WriteFooter(`OUTPUT') +.end diff --git a/current_generator/j_jqqbar.frm b/current_generator/j_jqqbar.frm new file mode 100644 index 0000000..dde640a --- /dev/null +++ b/current_generator/j_jqqbar.frm @@ -0,0 +1,48 @@ +*/** +* \brief Contraction of FKL 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,p; +i mu,nu,sigma; +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 [j_qggm`i' `h1'`hg'] = qggm`i'*Eps(`hg'1, nu, pa, pb)*Current( + `h1'1, pa, mu, p1 + ); + #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',j_qggm1,2,pb,p2,p3,pa,p1) +#call WriteOptimised(`OUTPUT',j_qggm2,2,pb,p2,p3,pa,p1) +#call WriteOptimised(`OUTPUT',j_qggm3,2,pb,p2,p3,pa,p1) +#call WriteFooter(`OUTPUT') +.end diff --git a/current_generator/j_qqbar_j.frm b/current_generator/j_qqbar_j.frm new file mode 100644 index 0000000..1a82fb3 --- /dev/null +++ b/current_generator/j_qqbar_j.frm @@ -0,0 +1,84 @@ +*/** +* \brief Contraction of FKL current with central qqbar emission and another 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; + +* central vertex, cf. eq:Xcen in developer manual +#do h={+,-} +* eq:Xcrossed in developer manual, normalisation added later on + l [X_cross `h'] = -Current( + `h'1, pq, nu, q1 - pqbar, mu, pqbar + ); +* eq:Xqprop in developer manual, normalisation added later on + l [X_qbar `h'] = Current( + `h'1, pq, mu, q1 - pq, nu, pqbar + ); + l [X_sym `h'] = -i_*( + d_(mu,nu)*Xsym(sigma) + V3g(mu, nu, sigma) + )/sqqbar*Current(`h'1, pq, sigma, pqbar); +#enddo +.sort +drop; + +* multiply with currents +#do h1={+,-} + #do h={+,-} + #do PART={cross,qbar,sym} + l [M_`PART' `h1'`h'] = [X_`PART' `h'] + *Current(`h1'1, p1, mu, pa) + *Current(-1, pn, nu, pb) + ; + #enddo + #enddo +#enddo + +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,q11,q12) +#call WriteOptimised(`OUTPUT',M_cross,2,pa,p1,pb,pn,pq,pqbar,q11,q12) +#call WriteOptimised(`OUTPUT',M_sym,2,pa,p1,pb,pn,pq,pqbar,q11,q12) +#call WriteFooter(`OUTPUT') +.end diff --git a/current_generator/jh_j.frm b/current_generator/jh_j.frm new file mode 100644 index 0000000..caf2252 --- /dev/null +++ b/current_generator/jh_j.frm @@ -0,0 +1,77 @@ +*/** +* \brief Contraction of extremal Higgs boson emission 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,ph,ph1,ph2; +i mu; +s mq; + +* backward (p1.z < 0) +* the first helicity corresponds to the outgoing gluon, +* where - corresponds to a helicity flip +* h2 is the helicity of the current without Higgs emission +#do h2={+,-} + l [jh_j_backward +`h2'] = ( + JH(+1, +1, mu, p1, pa, ph, mq) + *Current(`h2'1, p2, mu, pb) + ); + l [jh_j_backward -`h2'] = ( + JH(+1, -1, mu, p1, pa, ph, mq) + *Current(`h2'1, p2, mu, pb) + ); +#enddo + +multiply replace_( + ph, ph1 + ph2 +); + +#call ContractCurrents +.sort +skip; + +* forward (p1.z > 0) +#do hout={+,-} + #do h2={+,-} + l [jh_j_forward `hout'`h2'] = [jh_j_backward `hout'`h2']; + #enddo +#enddo + +multiply replace_(minus,plus); +id perphat(p1) = -1; +id conj(perphat(p1)) = -1; +.sort + +format O4; +format c; +#call WriteHeader(`OUTPUT') +#write <`OUTPUT'> " namespace currents {" +#write <`OUTPUT'> " namespace {" +#do i=1,2 + #write <`OUTPUT'> " std::complex T`i'(" + #write <`OUTPUT'> " CLHEP::HepLorentzVector const & q1," + #write <`OUTPUT'> " CLHEP::HepLorentzVector const & q2," + #write <`OUTPUT'> " double m" + #write <`OUTPUT'> " );" +#enddo +#do i={1,2,4,5,10,12} + #write <`OUTPUT'> " std::complex H`i'DD(" + #write <`OUTPUT'> " CLHEP::HepLorentzVector const & k1," + #write <`OUTPUT'> " CLHEP::HepLorentzVector const & k2," + #write <`OUTPUT'> " CLHEP::HepLorentzVector const & kh," + #write <`OUTPUT'> " double mq" + #write <`OUTPUT'> " );" +#enddo +#write <`OUTPUT'> " }" +#write <`OUTPUT'> " }" +#do DIR={backward,forward} + #call WriteOptimised(`OUTPUT',jh_j_`DIR',2,pa,p1,pb,p2,ph1,ph2,DOUBLE:,mq) +#enddo +#call WriteFooter(`OUTPUT') +.end diff --git a/current_generator/juno_h_j.frm b/current_generator/juno_h_j.frm new file mode 100644 index 0000000..bc3157c --- /dev/null +++ b/current_generator/juno_h_j.frm @@ -0,0 +1,73 @@ +*/** +* \brief Contraction of unordered current with Higgs emission vertex 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 + +v pb,p2,pa,p1,pg,q2,q3,p,pr,qH1,qH2,q11,q12,q21,q22; +i mu,nu,sigma; +s h,foo,bar; + +#do h1={+,-} +* eq:juno in developer manual with p1 -> pg, p2 -> p1 + l [U1_h_j `h1'] = ( + + Current(`h1'1, p1, nu, pg)*Current(`h1'1, pg, mu, pa) + + 2*p1(nu)*Current(`h1'1, p1, mu, pa) + )/m2(p1 + pg); + l [U2_h_j `h1'] = ( + + 2*Current(`h1'1, p1, mu, pa)*pa(nu) + - Current(`h1'1, p1, mu, pg)*Current(`h1'1, pg, nu, pa) + )/m2(pa - pg); + l [L_h_j `h1'] = ( + - (q2(nu) + q3(nu))*Current(`h1'1, p1, mu, pa) + - 2*pg(mu)*Current(`h1'1, p1, nu, pa) + + 2*Current(`h1'1, p1, pg, pa)*d_(mu, nu) + + m2(q2)*Current(`h1'1, p1, mu, pa)*(p2(nu)/m2(p2+pg) + pb(nu)/m2(pb+pg)) + )/m2(q3); +#enddo +.sort +drop; + +* multiply with polarisation vector and other current +#do h1={+,-} + #do h2={+,-} + #do hg={+,-} + #do TENSOR={U1_h_j,U2_h_j,L_h_j} + l [`TENSOR' `h1'`h2'`hg'] = ( + [`TENSOR' `h1'] + *Current(`h2'1, pb, sigma, p2) + *Eps(`hg'1, nu, pr) + ); + #enddo + #enddo + #enddo +#enddo + +multiply VH(mu, sigma, qH1, qH2); + +* TODO: choice of auxiliary vector +id Eps(h?, mu?, pr) = Eps(h, mu, pg, p1); +*id Eps(h?, mu?, pr)*Current(h?, ?a) = Eps(h, mu, pg, p1)*Current(h, ?a); + +multiply replace_( + q2,p1+pg-pa, + q3,p1-pa, + qH1, q11 - q12, + qH2, q21 - q22 +); +.sort +#call ContractCurrents +id p1?.p2? = dot(p1, p2); +.sort +format O4; +format c; +#call WriteHeader(`OUTPUT') +#call WriteOptimised(`OUTPUT',U1_h_j,3,pa,p1,pb,p2,pg,q11,q12,q21,q22,COMPLEX:,T1,T2) +#call WriteOptimised(`OUTPUT',U2_h_j,3,pa,p1,pb,p2,pg,q11,q12,q21,q22,COMPLEX:,T1,T2) +#call WriteOptimised(`OUTPUT',L_h_j,3,pa,p1,pb,p2,pg,q11,q12,q21,q22,COMPLEX:,T1,T2) +#call WriteFooter(`OUTPUT') +.end diff --git a/current_generator/juno_j.frm b/current_generator/juno_j.frm new file mode 100644 index 0000000..78ce64d --- /dev/null +++ b/current_generator/juno_j.frm @@ -0,0 +1,66 @@ +*/** +* \brief Contraction of FKL 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,q2,q3,p,pr; +i mu,nu,sigma; +s h,foo,bar; + +#do h1={+,-} +* eq:juno in developer manual with p1 -> pg, p2 -> p1 + l [U1_j `h1'] = ( + + Current(`h1'1, p1, nu, pg)*Current(`h1'1, pg, mu, pa) + + 2*p1(nu)*Current(`h1'1, p1, mu, pa) + )/m2(p1 + pg); + l [U2_j `h1'] = ( + + 2*Current(`h1'1, p1, mu, pa)*pa(nu) + - Current(`h1'1, p1, mu, pg)*Current(`h1'1, pg, nu, pa) + )/m2(pa - pg); + l [L_j `h1'] = ( + - (q2(nu) + q3(nu))*Current(`h1'1, p1, mu, pa) + - 2*pg(mu)*Current(`h1'1, p1, nu, pa) + + 2*Current(`h1'1, p1, pg, pa)*d_(mu, nu) + + m2(q2)*Current(`h1'1, p1, mu, pa)*(p2(nu)/m2(p2+pg) + pb(nu)/m2(pb+pg)) + )/m2(q3); +#enddo +.sort +drop; + +* multiply with polarisation vector and other currents +#do h1={+,-} + #do hg={+,-} + #do TENSOR={U1_j,U2_j,L_j} + l [`TENSOR' `h1'`hg'] = ( + [`TENSOR' `h1'] + *Current(-1, pb, mu, p2) + *Eps(`hg'1, nu, pr) + ); + #enddo + #enddo +#enddo + +* choice of auxiliary vector +id Eps(h?, mu?, pr) = Eps(h, mu, pg, p1); +*id Eps(h?, mu?, pr)*Current(h?, ?a) = Eps(h, mu, pg, p1)*Current(h, ?a); + +multiply replace_( + q2,p1+pg-pa, + q3,p1-pa +); +.sort +#call ContractCurrents +.sort +format O4; +format c; +#call WriteHeader(`OUTPUT') +#call WriteOptimised(`OUTPUT',U1_j,2,pa,p1,pb,p2,pg) +#call WriteOptimised(`OUTPUT',U2_j,2,pa,p1,pb,p2,pg) +#call WriteOptimised(`OUTPUT',L_j,2,pa,p1,pb,p2,pg) +#call WriteFooter(`OUTPUT') +.end diff --git a/doc/developer_manual/currents.tex b/doc/developer_manual/currents.tex index b9d5fdc..b73bd42 100644 --- a/doc/developer_manual/currents.tex +++ b/doc/developer_manual/currents.tex @@ -1,704 +1,734 @@ \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. Each implemented current has its own separate source file (e.g. \texttt{src/.cc}), and associated header file (e.g. \texttt{include/HEJ/.hh}). The processes (and their filename) that are implemented are: Pure jets (\texttt{jets}), $W$+jets (\texttt{Wjets}), $Z/\gamma$+jets (\texttt{Zjets}), $h$+jets (\texttt{Hjets}), $W^+W^+$+jets (\texttt{WWjets}). 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!. +The exact form depends on the helicity and direction (forward/backwards) for the quarks. \subsubsection{Gluon} In \HEJ the currents for gluons and quarks are the same, up to a colour factor $K_g/C_F$, where \begin{align} K_g(p_1^-, p_a^-) = \frac{1}{2}\left(\frac{p_1^-}{p_a^-} + \frac{p_a^-}{p_1^-}\right)\left(C_A - \frac{1}{C_A}\right)+\frac{1}{C_A}. \end{align} Thus we can just reuse the results from sec.~\ref{sec:current_quark}. \subsubsection{Single unordered gluon} Configuration $q(p_a) \to g(p_1) q(p_2) g^*(\tilde{q}_2)$~\cite{Andersen:2017kfc} \begin{align} \label{eq:juno} \begin{split} &j^{{\rm uno}\; \mu\ cd}(p_2,p_1,p_a) = i \varepsilon_{1\nu} \left( T_{2i}^{c}T_{ia}^d\ \left(U_1^{\mu\nu}-L^{\mu\nu} \right) + T_{2i}^{d}T_{ia}^c\ \left(U_2^{\mu\nu} + L^{\mu\nu} \right) \right). \\ U_1^{\mu\nu} &= \frac1{s_{21}} \left( j_{21}^\nu j_{1a}^\mu + 2 p_2^\nu j_{2a}^\mu \right) \qquad \qquad U_2^{\mu\nu} = \frac1{t_{a1}} \left( 2 j_{2a}^\mu p_a^\nu - j_{21}^\mu j_{1a}^\nu \right) \\ L^{\mu\nu} &= \frac1{t_{a2}} \left(-2p_1^\mu j_{2a}^\nu+2p_1.j_{2a} g^{\mu\nu} + (\tilde{q}_1+\tilde{q}_2)^\nu j_{2a}^\mu + \frac{t_{b2}}{2} j_{2a}^\mu \left( \frac{p_2^\nu}{p_1.p_2} + \frac{p_b^\nu}{p_1.p_b} \right) \right) , \end{split} \end{align} $j^{{\rm uno}\; \mu}$ is currently not calculated as a separate current, but always as needed for the ME (i.e. in \lstinline!ME_unob_XX!). \subsubsection{Extremal \texorpdfstring{$q\bar{q}$}{qqx}} 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: +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} + \epsilon_\mu^\pm(p_a) = \epsilon_\mu^\pm(p_a, p_1),\qquad \epsilon_\mu^\pm(p_1) = \epsilon_\mu^\pm(p_1, p_a)\,. +\end{equation} The polarisation vectors with two momentum arguments +are defined in equation~(\ref{eq:pol_vector}). + Following~\cite{DelDuca:2001fn}, we introduce effective polarisation vectors to describe the contraction with the Higgs-boson production vertex eq.~\eqref{eq:VH}: \begin{align} \label{eq:eps_H} \epsilon_{H,\mu}(p_a) = \frac{T_2(p_a, p_a-p_H)}{(p_a-p_H)^2}\big[p_a\cdot p_H\epsilon_\mu(p_a) - p_H\cdot\epsilon(p_a) p_{a,\mu}\big]\,,\\ \epsilon_{H,\mu}^*(p_1) = -\frac{T_2(p_1+p_H, p_1)}{(p_1+p_H)^2}\big[p_1\cdot p_H\epsilon_\mu^*(p_1) - p_H\cdot\epsilon^*(p_1) p_{1,\mu}\big]\,, \end{align} -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} +with $T_1$ from equation~\eqref{eq:T_1} and $T_2$ from equation~\eqref{eq:T_2}. + Without loss of generality, we consider only the case where the incoming gluon has positive helicity. The remaining helicity configurations can be obtained through parity transformation. Labelling the effective current by the helicities of the gluons we obtain for the same-helicity case \begin{equation} \label{eq:jH_same_helicity} \begin{split} j_{H,\mu}^{++}{}&(p_1,p_a,p_H) = \frac{m^2}{\pi v}\bigg[\\ &-\sqrt{\frac{2p_1^-}{p_a^-}}\frac{p_{1\perp}^*}{|p_{1\perp}|}\frac{t_2}{\spb a.1}\epsilon^{+,*}_{H,\mu}(p_1) +\sqrt{\frac{2p_a^-}{p_1^-}}\frac{p_{1\perp}^*}{|p_{1\perp}|}\frac{t_2}{\spa 1.a}\epsilon^{+}_{H,\mu}(p_a)\\ &+ [1|H|a\rangle \bigg( \frac{\sqrt{2}}{\spa 1.a}\epsilon^{+}_{H,\mu}(p_a) + \frac{\sqrt{2}}{\spb a.1}\epsilon^{+,*}_{H,\mu}(p_1)-\frac{\spa 1.a T_2(p_a, p_a-p_H)}{\sqrt{2}(p_a-p_H)^2}\epsilon^{+,*}_{\mu}(p_1)\\ & \qquad -\frac{\spb a.1 T_2(p_1+p_H, p_1)}{\sqrt{2}(p_1+p_H)^2}\epsilon^{+}_{\mu}(p_a)-\frac{RH_4}{\sqrt{2}\spb a.1}\epsilon^{+,*}_{\mu}(p_1)+\frac{RH_5}{\sqrt{2}\spa 1.a}\epsilon^{+}_{\mu}(p_a) \bigg)\\ & - \frac{[1|H|a\rangle^2}{2 t_1}(p_{a,\mu} RH_{10} - p_{1,\mu} RH_{12})\bigg] \end{split} \end{equation} -with $t_1 = (p_a-p_1)^2$, $t_2 = (p_a-p_1-p_H)^2$ and $R = 8 \pi^2$. 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.}. +with $t_1 = (p_a-p_1)^2$, $t_2 = (p_a-p_1-p_H)^2$ and $R = 8 \pi^2$\todo{Code has $R=8\pi^2 i$}. Like other special currents, eq.~\eqref{eq:jH_same_helicity} +is implemented in \texttt{current\_generator/include/currents.frm}. The currents with a helicity flip is given through \begin{equation} \label{eq:jH_helicity_flip} \begin{split} j_{H,\mu}^{+-}{}&(p_1,p_a,p_H) = \frac{m^2}{\pi v}\bigg[\\ &-\sqrt{\frac{2p_1^-}{p_a^-}}\frac{p_{1\perp}^*}{|p_{1\perp}|}\frac{t_2}{\spb a.1}\epsilon^{-,*}_{H,\mu}(p_1) +\sqrt{\frac{2p_a^-}{p_1^-}}\frac{p_{1\perp}}{|p_{1\perp}|}\frac{t_2}{\spb a.1}\epsilon^{+}_{H,\mu}(p_a)\\ &+ [1|H|a\rangle \left( \frac{\sqrt{2}}{\spb a.1} \epsilon^{-,*}_{H,\mu}(p_1) -\frac{\spa 1.a T_2(p_a, p_a-p_H)}{\sqrt{2}(p_a-p_H)^2}\epsilon^{-,*}_{\mu}(p_1) - \frac{RH_4}{\sqrt{2}\spb a.1}\epsilon^{-,*}_{\mu}(p_1)\right) \\ &+ [a|H|1\rangle \left( \frac{\sqrt{2}}{\spb a.1}\epsilon^{+}_{H,\mu}(p_a) -\frac{\spa 1.a T_2(p_1+p_H,p_1)}{\sqrt{2}(p_1+p_H)^2}\epsilon^{+}_{\mu}(p_a) +\frac{RH_5}{\sqrt{2}\spb a.1}\epsilon^{+}_{\mu}(p_a) \right)\\ & - \frac{[1|H|a\rangle [a|H|1\rangle}{2 \spb a.1 ^2}(p_{a,\mu} RH_{10} - p_{1,\mu} RH_{12})\\ &+ \frac{\spa 1.a}{\spb a.1}\bigg(RH_1p_{1,\mu}-RH_2p_{a,\mu}+2 p_1\cdot p_H \frac{T_2(p_1+p_H, p_1)}{(p_1+p_H)^2} p_{a,\mu} \\ & \qquad- 2p_a \cdot p_H \frac{T_2(p_a, p_a-p_H)}{(p_a-p_H)^2} p_{1,\mu}+ T_1(p_a-p_1, p_a-p_1-p_H)\frac{(p_1 + p_a)_\mu}{t_1}\\ &\qquad-\frac{(p_1+p_a)\cdot p_H}{t_1} T_2(p_a-p_1, p_a-p_1-p_H)(p_1 - p_a)_\mu \bigg) \bigg]\,, \end{split} \end{equation} -and implemented by \lstinline!g_gH_HNC! again in \texttt{src/Hjets.cc}. +and implemented again in \texttt{current\_generator/include/currents.frm}.\todo{sign mismatch in line 5 and negative-helicity polarisation vectors} If we instead choose the gluon momentum in the $+$ direction, so that $p_a^- = p_{a\perp} = 0$, the corresponding currents are obtained by replacing $p_1^- \to p_1^+, p_a^- \to p_a^+, \frac{p_{1\perp}}{|p_{1\perp}|} \to -1$ in the second line of -eq.~\eqref{eq:jH_same_helicity} and eq.~\eqref{eq:jH_helicity_flip} (see variables \lstinline!ang1a! and \lstinline!sqa1! in the implementation). +eq.~\eqref{eq:jH_same_helicity} and eq.~\eqref{eq:jH_helicity_flip}.. - The form factors $H_1,H_2,H_4,H_5, H_{10}, H_{12}$ are given in~\cite{DelDuca:2003ba}, and are implemented under their name in \texttt{src/Hjets.cc}. They reduce down to fundamental QCD integrals, which are again provided by \QCDloop. + The form factors $H_1,H_2,H_4,H_5, H_{10}, H_{12}$ are given in~\cite{DelDuca:2003ba}, and are implemented as \lstinline!H1DD,H2DD! etc. in \texttt{src/Hjets.cc}. They reduce down to fundamental QCD integrals, which are again provided by \QCDloop. \subsubsection{Peripheral Higgs emission - Infinite top mass} \label{sec:jH_eff} To get the result with infinite top mass we could either take the limit $m_t\to \infty$ in~\eqref{eq:jH_helicity_flip} and~\eqref{eq:jH_same_helicity}, or use the \textit{impact factors} as given in~\cite{DelDuca:2003ba}. Both methods are equivalent, and lead to the same result. For the first one would find \begin{align} \lim_{m_t\to\infty} m_t^2 H_1 &= i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_2 &=-i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_4 &= i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_5 &=-i \frac{1}{24 \pi^2}\\ \lim_{m_t\to\infty} m_t^2 H_{10} &= 0 \\ \lim_{m_t\to\infty} m_t^2 H_{12} &= 0. \end{align} \todo{double check this, see James thesis eq. 4.33} However only the second method is implemented in the code through \lstinline!C2gHgp! and \lstinline!C2gHgm! inside \texttt{src/Hjets.cc}, each function calculates the square of eq. (4.23) and (4.22) from~\cite{DelDuca:2003ba} respectively. \subsection{Vector Boson + Jets} \label{sec:currents_W} \subsubsection{Quark+ Vector Boson} \begin{figure} \centering \begin{minipage}[b]{0.2\textwidth} \includegraphics[width=\textwidth]{Wbits.pdf} \end{minipage} \begin{minipage}[b]{0.1\textwidth} \centering{=} \vspace{0.7cm} \end{minipage} \begin{minipage}[b]{0.2\textwidth} \includegraphics[width=\textwidth]{Wbits2.pdf} \end{minipage} \begin{minipage}[b]{0.1\textwidth} \centering{+} \vspace{0.7cm} \end{minipage} \begin{minipage}[b]{0.2\textwidth} \includegraphics[width=\textwidth]{Wbits3.pdf} \end{minipage} \caption{The $j_V$ current is constructed from the two diagrams which contribute to the emission of a vector boson from a given quark line.} \label{fig:jV} \end{figure} For a $W, Z$, or photon emission we require a fermion. The current is actually a sum of two separate contributions, see figure~\ref{fig:jV}, one with a vector boson emission from the initial state, and one with the vector boson emission from the final state. This can be seen as the following two terms, given for the example of a $W$ emission~\cite{Andersen:2012gk}\todo{cite W subleading paper}: \begin{align} \label{eq:Weffcur1} j_W^\mu(p_a,p_{\ell},p_{\bar{\ell}}, p_1) =&\ \frac{g_W^2}{2}\ \frac1{p_W^2-M_W^2+i\ \Gamma_W M_W}\ \bar{u}^-(p_\ell) \gamma_\alpha v^-(p_{\bar\ell})\nonumber \\ & \cdot \left( \frac{ \bar{u}^-(p_1) \gamma^\alpha (\slashed{p}_W + \slashed{p}_1)\gamma^\mu u^-(p_a)}{(p_W+p_1)^2} + \frac{ \bar{u}^-(p_1)\gamma^\mu (\slashed{p}_a - \slashed{p}_W)\gamma^\alpha u^-(p_a)}{(p_a-p_W)^2} \right). \end{align} There are a couple of subtleties here. There is a minus sign distinction between the quark-anti-quark cases due to the fermion flow of the propagator in the current. Note that the type of $W$ emission (+ or -) will depend on the quark flavour, and that the handedness of the quark-line is given by whether its a quark or anti-quark. The coupling and propagator factor in eq.~(\ref{eq:Weffcur1}) have to be adapted depending on the emitted boson. The remaining product of currents \begin{equation} \label{eq:J_V} J_{\text{V}}^\mu(p_2,p_l,p_{\bar{l}},p_3)=\left( \frac{ \bar{u}_2 \gamma^\nu (\slashed{p}_2 + \slashed{p}_l + \slashed{p}_{\bar{l}}) \gamma^\mu u_3}{s_{2l\bar{l}}} - \frac{\bar u_2 \gamma^\mu(\slashed{p}_3 + \slashed{p}_l + \slashed{p}_{\bar{l}}) \gamma^\nu u_3}{s_{3l\bar{l}}} \right) [\bar{u}_l \gamma_\nu u_{\bar{l}}] \end{equation} with $s_{il\bar{l}} = (p_i + p_l +p_{\bar{l}})^2$ is universal. The implementation is in \texttt{include/currents.frm} inside the \texttt{current\_generator} (see section~\ref{sec:cur_gen}). To use it inside \FORM use the place-holder \lstinline!JV(h1, hl, mu, pa, p1, plbar, pl)!, where \lstinline!h1! is the helicity of the quark line and \lstinline!hl! the helicity of the lepton line. \subsubsection{Vector boson with unordered emission} \begin{figure} \centering \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno1} \caption{} \label{fig:U1diags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno2} \caption{} \label{fig:U2diags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno3} \caption{} \label{fig:Cdiags} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{vuno4} \caption{} \label{fig:Ddiags} \end{subfigure} \vspace{0.4cm} \caption{Examples of each of the four categories of Feynman diagram which contribute to at leading-order; there are twelve in total. (a) is an example where the gluon and vector boson are emitted from the same quark line and the gluon comes after the $t$-channel propagator. In (b), the gluon and vector boson are emitted from the same quark line and the gluon comes before the $t$-channel proagator. In (c) the gluon is emitted from the $t$-channel gluon and in (d) the gluon is emitted from the $b$--$3$ quark line.} \label{fig:Vunodiags} \end{figure} It is necessary to include subleading processes in vector boson + jets also. Similarly to the pure jet case, the unordered currents are not calculated separately, and only in the ME functions when required in -the \texttt{src/Wjets.cc} or \texttt{src/Zjets.cc} file. 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:Vunodiags}). Using $T^m_{ij}$ represent fundamental colour -matrices between quark state $i$ and $j$ with adjoint index $m$ we find +the \texttt{src/Wjets.cc} or \texttt{src/Zjets.cc} file. The following shows the derivation of the calculation of +this ME within HEJ. We start with a contraction of two currents: +\begin{equation} + \label{eq:SabsVuno} + S_{qQ\to Vgq^\prime Q} = + j_{V{\rm uno}\,\mu}^d(p_a,p_1,p_2,p_\ell,p_{\bar\ell})\ g^{\mu + \nu}\ T^d_{3b}\ j^{h_b,h_3}_{\nu}(p_b,p_{3}), +\end{equation} +where $j_{V,{\rm uno}}$ is our new unordered current which is is +only non-zero for $h_a=h_1=-$ and hence we have suppressed its +helicity indices. It is derived from the 12 leading-order Feynman +diagrams in the QMRK limit (see figure~\ref{fig:Vunodiags}). Using +$T^m_{ij}$ represent fundamental colour matrices between quark state +$i$ and $j$ with adjoint index $m$ we find \begin{align}\label{eq:wunocurrent} \begin{split} j^{d\,\mu}_{\rm V,uno}(p_a,p_1,p_2,p_\ell,p_{\bar{\ell}}) =& \ i \varepsilon_{\nu}(p_1)\ \bar{u}^-(p_\ell) \gamma_\rho v^-(p_{\bar \ell}) \\ & \quad \times\ \left(T^1_{2i} T^d_{ia} (\tilde U_1^{\nu\mu\rho}-\tilde L^{\nu\mu\rho}) + T^d_{2i} T^1_{ia} (\tilde U_2^{\nu\mu\rho}+\tilde L^{\nu\mu\rho}) \right), \end{split} \end{align} where expressions for $\tilde U_{1,2}^{\nu\mu\rho}$ and $\tilde L^{\nu\mu\rho}$ are given as: \begin{align} \label{eq:U1tensor} \begin{split} \tilde U_1^{\nu\mu\rho} ={}&\frac{\langle 2|\nu (\slashed{p}_2+ \slashed{p}_1)\mu (\slashed{p}_a - \slashed{p}_V)\rho P_L |a\rangle }{s_{12}t_{aV}} + \frac{\langle 2|\nu (\slashed{p}_2+ \slashed{p}_1)\rho P_L (\slashed{p}_2+\slashed{p}_1 + \slashed{p}_V)\mu |a\rangle }{s_{12}s_{12V}} \\ &+ \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_V) \nu (\slashed{p}_1 + \slashed{p}_2+\slashed{p}_V)\mu |a\rangle}{s_{2V}s_{12V}}\,, \end{split}\\ \label{eq:U2tensor} \begin{split} \tilde U_2^{\nu\mu\rho} ={}&\frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_V-\slashed{p}_1)\nu (\slashed{p}_a - \slashed{p}_V)\rho P_L |a\rangle }{t_{aV1}t_{aV}} + \frac{\langle 2|\mu (\slashed{p}_a-\slashed{p}_V- \slashed{p}_1)\rho P_L (\slashed{p}_a-\slashed{p}_1) \nu |a\rangle }{t_{a1V}t_{a1}} \\ &+ \frac{\langle 2|\rho P_L (\slashed{p}_2+ \slashed{p}_V) \mu (\slashed{p}_a-\slashed{p}_1)\nu |a\rangle}{s_{2V}t_{a1}}\,, \end{split}\\ \label{eq:Ltensor} \begin{split} \tilde L^{\nu\mu\rho} ={}& \frac{1}{t_{aV2}}\left[ \frac{\langle 2 | \sigma (\slashed{p}_a-\slashed{p}_V)\rho|a\rangle}{t_{aV}} +\frac{\langle 2 | \rho (\slashed{p}_2+\slashed{p}_V)\sigma|a\rangle}{s_{2V}} \right]\\ &\times \left\{\left(\frac{p_b^\nu}{s_{1b}} + \frac{p_3^\nu}{s_{13}}\right)(q_1-p_1)^2g^{\mu\sigma}+(2q_1-p_1)^\nu g^{\mu\sigma} - 2p_1^\mu g^{\nu\sigma} + (2p_1-q_1)^\sigma g^{\mu\nu} \right\}\,, \end{split} \end{align} -where $s_{ij\dots} = (p_i + p_j + \dots)^2, t_{ij\dots} = (p_i - p_j - \dots)^2$ and $q_1 = p_a-p_2-p_V$. +where $s_{ij\dots} = (p_i + p_j + \dots)^2, t_{ij\dots} = (p_i - p_j - +\dots)^2$ and $q_1 = p_a-p_2-p_V$. This is actually calculated and +used in the code in a much cleaner way as follows: +\begin{align}\label{eq:spinorstringVuno} + S_{qQ\to Vgq^\prime Q} = i\varepsilon_\nu (p_g) \bar{u}^-(p_2)&\gamma_\rho\nu(p_{\bar{q}})\times T^d_{3b} \bar{u}^{h_3}(p_3)\gamma_\mu u^{h_b}(p_b) \times \nonumber \\ + &\left( T^1_{2i}T^d_{ia} \left( \tilde{U}_1^{\nu\mu\rho}-\tilde{L}^{\nu\mu\rho}\right)+T^d_{2i}T^1_{ia}\left(\tilde{U}_2^{\nu\mu\rho}+\tilde{L}^{\nu\mu\rho}\right) \right) +\end{align} +If we define the objects: +\begin{align}\label{eq:VunoX} + X &= \varepsilon_\nu(p_g) \left[ \bar{u}^-(p_2)\gamma_\rho\nu(p_{\bar{q}})\right] \left[ \bar{u}^{h_3}(p_3)\gamma_\mu u^{h_b}(p_b)\right] \left( \tilde{U}_1^{\nu\mu\rho}-\tilde{L}^{\nu\mu\rho}\right)\\ + Y &= \varepsilon_\nu(p_g) \left[ \bar{u}^-(p_2)\gamma_\rho\nu(p_{\bar{q}})\right] \left[ \bar{u}^{h_3}(p_3)\gamma_\mu u^{h_b}(p_b)\right] \left( \tilde{U}_2^{\nu\mu\rho}+\tilde{L}^{\nu\mu\rho}\right) + \label{eq:WunoY} +\end{align} +then we can rewrite Equation \eqref{eq:spinorstringVuno} in the much simpler form: +\begin{equation} + S_{qQ\to Vgq^\prime Q} = iT^d_{3b} \left( T^{1}_{2i}T^d_{ia} X + T^d_{2i}T^1_{ia} Y \right) +\end{equation} +then, by using: +\begin{align} + \sum_{\text{all indices}}& T^d_{3b}T^e_{b3}T^1_{2i}T^d_{ia}T^e_{ai}T^1_{i2} = \frac{1}{2}C_F^2C_A \\ + \sum_{\text{all indices}}& T^d_{3b}T^e_{b3}T^1_{2i}T^d_{ia}T^1_{ai}T^e_{i2} = \frac{1}{2}C_F^2C_A - \frac{1}{4}C_A^2C_F = -\frac{1}{4}C_F +\end{align} +giving then, the spin summed and helicity averaged spinor string as: +\begin{equation}\label{eq:VunoSumAveS} + ||\;\bar{S}_{qQ\to Vgq^\prime Q}\;|| = \frac{1}{4N_C^2} \left( \frac{1}{2}C_F^2C_A\left(|X|^2+|Y|^2\right)-\frac{1}{4}C_F\times2\mathrm{Re}\left(XY^*\right)\right) +\end{equation} + + \subsubsection{\texorpdfstring{$W$}{W}+Extremal \texorpdfstring{$\mathbf{q\bar{q}}$}{qqx}} \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}}=& - i \frac{C_1}{s_{23AB}}\left(X^{\mu\nu\sigma}_{1a}\hat{t_1} + X^{\mu\nu\sigma}_{4b}\hat{t_3} +V^{\mu\nu\sigma}_{3g}\right)J_{\text{V} \sigma}(p_2,p_A,p_B,p_3) \\ &\quad +iC_2X^{\mu\nu}_{Unc}+iC_3X^{\mu\nu}_{Cro}, \end{split} \end{align} where: \begin{align} \begin{split} C_1=&T^e_{1q}T^g_{qa}T^e_{23}T^g_{4b} - T^g_{1q}T^e_{qa}T^e_{23}T^g_{4b} = f^{egc}T^c_{1a}T^e_{23}T^g_{4b}, \\ C_2=&T^g_{1a}T^g_{2q}T^{g'}_{q3}T^{g'}_{4b} \\ C_3=&T^g_{1a}T^{g'}_{2q}T^g_{q3}T^{g'}_{4b} \end{split} \end{align} are the colour factors of different contributions and $J_\text{V}$ is given in equation~(\ref{eq:J_V}). The following tensor structures correspond to groupings of diagrams in figure~\ref{fig:qq_qQQq_graphs}. \begin{eqnarray} \label{eq:X_1a} X_{1a}^{\mu\nu\sigma} &= \frac{-g^{\mu\nu}}{s_{23AB}\hat{t_3}}\left(\frac{p^\sigma_a}{s_{a2} + s_{a3} + s_{aA} + s_{aB}} + \frac{p^\sigma_1}{s_{12} + s_{13} + s_{1A} + s_{1B}}\right) \\ \label{eq:X_4b} X_{4b}^{\mu\nu\sigma} &=\frac{g^{\mu\nu}}{s_{23AB}\hat{t_1}}\left(\frac{p^\sigma_b}{s_{b2} + s_{b3} + s_{bA} + s_{bB}}+ \frac{p^\sigma_4}{s_{42} + s_{43} + s_{4A} + s_{4B}}\right) \end{eqnarray} correspond to the first and second row of diagrams in figure~\ref{fig:qq_qQQq_graphs}. \begin{align} \label{eq:3GluonWEmit} \begin{split} V^{\mu\nu\sigma}_{3g}=\frac{1}{ \hat{t}_1s_{23AB}\,\hat{t}_3} \bigg[&\left(q_1+p_2+p_3+p_A+p_B\right)^\nu g^{\mu\sigma}+ \\ &\quad\left(q_3-p_2-p_3-p_A-p_B\right)^\mu g^{\sigma\nu}- \\ & \qquad\left(q_1+q_3\right)^\sigma g^{\mu\nu}\bigg]J_{\text{V} \sigma}(p_2,p_A,p_B,p_3) \end{split} \end{align} corresponds to the left diagram on the third row in figure~\ref{fig:qq_qQQq_graphs}. One notes that all of these contributions have the same colour factor, and as such we can group them together nicely before summing over helicities etc. As such, the function \lstinline!M_sym_W! returns a contraction of the above tensor containing the information from these 5 groupings of contributions (30 diagrams in total). It is available through the generated header \texttt{j\_Wqqbar\_j.hh} (see section~\ref{sec:cur_gen}). \begin{align} \label{eq:X_Unc} \begin{split} X^{\mu\nu}_{Unc}=\frac{\langle A|\sigma P_L|B\rangle}{\hat{t_1}\hat{t_3}} \bar{u}_2&\left[-\frac{ \gamma^\sigma P_L(\slashed{p}_2+\slashed{p}_A+\slashed{p}_B)\gamma^\mu (\slashed{q}_3+ \slashed{p}_3)\gamma^\nu}{(s_{2AB})(t_{unc_{2}})}\right.+ \\ &\qquad\left. \frac{\gamma^\mu(\slashed{q}_1-\slashed{p}_2)\gamma^\sigma P_L(\slashed{q}_3+\slashed{p}_3)\gamma^\nu}{(t_{unc_{1}})(t_{unc_{2}})}\right. + \\ &\qquad\qquad\left. \frac{\gamma^\mu(\slashed{q}_1-\slashed{p}_2)\gamma^\nu(\slashed{p}_3+\slashed{p}_A+\slashed{p}_B)\gamma^\sigma P_L }{(t_{unc_1})(s_{3AB})}\right]v_3 \end{split} \end{align} corresponds to the diagram on the right of row three in figure~\ref{fig:qq_qQQq_graphs}. This contribution to the current contraction can be obtained in the code with the function \lstinline!M_uncross_W!. \begin{align} \begin{split} \label{eq:X_Cro} X^{\mu\nu}_{Cro}=\frac{\langle A|\sigma P_L|B\rangle}{\hat{t_1}\hat{t_3}} \bar{u}_2&\left[-\frac{ \gamma^\nu(\slashed{q}_3+\slashed{p}_2)\gamma^\mu (\slashed{p}_3+\slashed{p}_A+\slashed{p}_B)\gamma^\sigma P_L}{(t_{cro_1})(s_{3AB})}\right.+ \\ &\qquad\left. \frac{\gamma^\nu(\slashed{q}_3+\slashed{p}_2)\gamma^\sigma P_L(\slashed{q}_1-\slashed{p}_3)\gamma^\mu}{(t_{cro_{1}})(t_{cro_{2}})}\right.+ \\ &\qquad\qquad\left . \frac{\gamma^\sigma P_L(\slashed{p}_2+\slashed{p}_A+\slashed{p}_B)\gamma^\nu(\slashed{q}_1-\slashed{p}_3)\gamma^\mu }{(s_{2AB})(t_{cro_2})}\right]v_3 \end{split} \end{align} corresponds to the last diagram in figure~\ref{fig:qq_qQQq_graphs}. This contribution to the current contraction can be obtained in the code with the function \lstinline!M_cross_W!. \subsubsection{$W^+ W^+$ + jets} The production of same-sign $WW$ + jets is implemented through the contraction of two vector boson currents; see eqn~\ref{eq:J_V} and more generally subsection~\ref{sec:currents_W}. This contraction is available in FORM under the header \texttt{jV\_jV.hh}, while the matrix elements can be found in \texttt{src/WWjets.cc}. There are two distinct contributions to this process which correspond to the possible pairings of the $W$-bosons and incoming legs. Labelling the bosons as \texttt{W1} and \texttt{W2}, the two configurations correspond to \texttt{W1}-forward with \texttt{W2}-backward, and \texttt{W2}-forward with \texttt{W1}-backward. The interference between these contributions is included. For same-flavour decays, \texttt{reconstruct\_intermediate()} will compute the difference between reconstruced mass and reference mass in possible pairings and select the pairing which minimises this quantity. %%% 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 968593c..efbc6c1 100644 --- a/doc/developer_manual/developer_manual.tex +++ b/doc/developer_manual/developer_manual.tex @@ -1,2004 +1,2034 @@ \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} +For configurations beyond leading-log, gluon emission is not allowed +everywhere and the rapidity span has to be adjusted. When determining +the most forward and backward jets, we exclude unordered emissions and +the extremal jet in extremal quark-antiquark emissions. In addition, +we subtract the rapidity region between central quark and antiquark +emission. Technically, we could have two or more additional gluons +inside one of the latter jets, in which case there could be gluons +that end up in between the jet centres but still outside the region +spanned by quark and antiquark. However, the phase space for this is +very small and should not affect the average number of emitted gluons +very much. \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} +Again, for configurations beyond leading-log the allowed rapidity +range has to be modified as explained in section~\ref{sec:psp_ng}. We +also subtract the areas of unordered jets and the extremal jet +in extremal quark-antiquark emission when we estimate the phase space +area covered by jets. The reason is that in this case any additional +emission inside such a jet would have to beyond the hard parton +dominating the next jet in rapidity, which is possible but rare. \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 +Before adding a new current contraction, first find a representation +where all momenta that appear inside the currents are \textcolor{red}{lightlike} +and have \textcolor{red}{positive energy}. Section~\ref{sec:p_massive} describes +how massive momenta can be decomposed into massless ones. For momenta +$p'$ with negative energies replace $p' \to - p$ and exploit that +negative- and positive-energy spinors are related by a phase, which is +usually irrelevant: $u^\lambda(-p) = \pm i u^\lambda(p)$. + +To implement a 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: +\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} +The decomposition is implemented in the \lstinline!split_into_lightlike! function. \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} +We also define polarisation vectors +\begin{equation} + \label{eq:pol_vector} + \epsilon_\mu^-(p_g, p_r) = \frac{\langle p_r|\mu|p_g]}{\sqrt{2}\spa p_g.{p_r}}\,,\qquad\epsilon_\mu^+(p_g, p_r) = \frac{\langle p_g|\mu|p_r]}{\sqrt{2}\spb p_g.{p_r}}\,. +\end{equation} +fulfilling +\begin{equation} + \label{eq:pol_vector_norm} + \epsilon_\mu^\lambda(p_g, p_r)\big[\epsilon^{\mu\,\lambda'}(p_g, p_r)\big]^* = -\delta_{\lambda\lambda'}\,. +\end{equation} + \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. 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}} + 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)\,, \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 $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/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in index cfa9f96..d5bddf5 100644 --- a/doc/doxygen/Doxyfile.in +++ b/doc/doxygen/Doxyfile.in @@ -1,2430 +1,2526 @@ -# Doxyfile 1.8.11 +# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = @PROJECT_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @PROJECT_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "High energy resummation for hadron colliders" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) -ALIASES += TODO="\todo" +ALIASES = "TODO=\todo" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = TRUE # "TRUE" for documenting ConfigFlags.hh.in correctly, see \cond @CMAKE_OPTION@ # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = @CMAKE_CURRENT_SOURCE_DIR@/biblio.bib #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/mainpage.dox \ @PROJECT_SOURCE_DIR@/include/HEJ \ @PROJECT_BINARY_DIR@/include/HEJ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, -# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse-libclang=ON option for CMake. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -USE_MATHJAX = NO +USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /