Page MenuHomeHEPForge

No OneTemporary

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..b5a356b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+t/Wm9-g4-repack.hdf5 filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0a65191..7cac922 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,296 +1,316 @@
# ---------------------------------
# - General Setup -
# ---------------------------------
stages:
- build
- test
- FOG:build
- FOG:test
- clean_code
- publish
variables:
# 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
DOCKER_HEPMC3: hejdock/hepmc3env
DOCKER_QCDLOOP: hejdock/qcdloopenv
DOCKER_RIVET: hejdock/rivetenv
+ DOCKER_HIGHFIVE: "hejdock/highfiveenv:centos7"
+ # default name of cmake
+ CMAKE: cmake
+ CTEST: ctest
# ----------- Macros -----------
after_script:
- date
.tags_template:
tags: &tags_def
- docker
# 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
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}
- mkdir -p ${t_HEJ_BUILD_DIR}
- cd ${t_HEJ_BUILD_DIR}
- - cmake ${t_HEJ_DIR} -DCMAKE_BUILD_TYPE=DEBUG
+ - ${CMAKE} ${t_HEJ_DIR} -DCMAKE_BUILD_TYPE=DEBUG
-DCMAKE_INSTALL_PREFIX=${t_HEJ_INSTALL_DIR}
script:
- make -j $(nproc --ignore=1)
- make install
artifacts:
# save build and installed folder
name: build
expire_in: 1d
paths:
- ${HEJ_INSTALL_DIR}
- ${HEJ_BUILD_DIR}
# ----------- Test -----------
.HEJ_test:
tags: *tags_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
+ - ${CMAKE} ${t_HEJ_DIR} # rerun cmake to create all test configure files
script:
- - ctest --output-on-failure
+ - ${CTEST} --output-on-failure
artifacts: *artifacts_failed
## ----------- FOG build -----------
.FOG_build:
tags: *tags_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}
- mkdir -p ${t_FOG_BUILD_DIR}
- cd ${t_FOG_BUILD_DIR}
- - cmake ${t_FOG_DIR} -DCMAKE_BUILD_TYPE=DEBUG
+ - ${CMAKE} ${t_FOG_DIR} -DCMAKE_BUILD_TYPE=DEBUG
-DCMAKE_INSTALL_PREFIX=${t_FOG_INSTALL_DIR}
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
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
+ - ${CMAKE} ${t_FOG_DIR} # rerun cmake to create all test configure files
script:
- - ctest --output-on-failure
+ - ${CTEST} --output-on-failure
artifacts: *artifacts_failed
# ---------------------------------
# - Build & Test -
# ---------------------------------
# ----------- basic -----------
build:basic:
image: ${DOCKER_BASIC}
extends: .HEJ_build
test:basic:
image: ${DOCKER_BASIC}
extends: .HEJ_test
dependencies:
- build:basic
FOG:build:basic:
image: ${DOCKER_BASIC}
extends: .FOG_build
dependencies:
- build:basic
FOG:test:basic:
image: ${DOCKER_BASIC}
extends: .FOG_test
dependencies:
- FOG:build:basic
# ----------- HepMC 3 -----------
build:hepmc3:
image: ${DOCKER_HEPMC3}
extends: .HEJ_build
test:hepmc3:
image: ${DOCKER_HEPMC3}
extends: .HEJ_test
dependencies:
- build:hepmc3
+# ----------- 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
+ dependencies:
+ - build:HighFive
# ----------- QCDloop -----------
build:qcdloop:
image: ${DOCKER_QCDLOOP}
extends: .HEJ_build
test:qcdloop:
image: ${DOCKER_QCDLOOP}
extends: .HEJ_test
dependencies:
- build:qcdloop
# ----------- rivet -----------
build:rivet:
image: ${DOCKER_RIVET}
extends: .HEJ_build
test:rivet:
image: ${DOCKER_RIVET}
extends: .HEJ_test
dependencies:
- build:rivet
script:
- - ctest --output-on-failure
+ - ${CTEST} --output-on-failure
- bash -c '[ -f tst.yoda ]' && echo "found rivet output"
- rivet-cmphistos tst.yoda
- bash -c '[ -f MC_XS_XS.dat ]' && echo "yoda not empty"
# ---------------------------------
# - Clean Code -
# ---------------------------------
No_tabs:
stage: clean_code
tags: *tags_def
image: hejdock/git
dependencies: []
script:
- date
- check_tabs
# ----------- No gcc warnings -----------
.Warning_build:
extends: .HEJ_build
stage: clean_code
dependencies: []
script:
- cd ${t_HEJ_DIR}
# suppress warnings from side packages
- sed -i 's/include_directories(${LHAPDF/include_directories(SYSTEM ${LHAPDF/g' CMakeLists.txt
- sed -i 's/include_directories(${fastjet/include_directories(SYSTEM ${fastjet/g' CMakeLists.txt
- sed -i 's/include_directories(${Boost/include_directories(SYSTEM ${Boost/g' CMakeLists.txt
- cd ${t_HEJ_BUILD_DIR}
- - cmake ${t_HEJ_DIR} -DCMAKE_CXX_FLAGS="-Werror"
+ - ${CMAKE} ${t_HEJ_DIR} -DCMAKE_CXX_FLAGS="-Werror"
- make -j $(nproc --ignore=1)
artifacts: # don't save anything
.Warning_FOG:
extends: .FOG_build
stage: clean_code
script:
- cd ${t_FOG_DIR}
# suppress warnings from side packages
- sed -i 's/include_directories(${LHAPDF/include_directories(SYSTEM ${LHAPDF/g' CMakeLists.txt
- sed -i 's/include_directories(${fastjet/include_directories(SYSTEM ${fastjet/g' CMakeLists.txt
- sed -i 's/include_directories(${Boost/include_directories(SYSTEM ${Boost/g' CMakeLists.txt
- cd ${t_FOG_BUILD_DIR}
- - cmake ${t_FOG_DIR} -DCMAKE_CXX_FLAGS="-Werror"
+ - ${CMAKE} ${t_FOG_DIR} -DCMAKE_CXX_FLAGS="-Werror"
- make -j $(nproc --ignore=1)
artifacts: # don't save anything
No_Warning:basic:
image: ${DOCKER_BASIC}
extends: .Warning_build
No_Warning:basic:FOG:
image: ${DOCKER_BASIC}
extends: .Warning_FOG
dependencies:
- build:basic
# ---------------------------------
# - Publish -
# ---------------------------------
Publish_version:
stage: publish
tags: *tags_def
image: hejdock/git
dependencies: []
only:
- /^v\d+\.\d+$/
except:
- tags
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 903f689..ffab14c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,292 +1,310 @@
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.5 LANGUAGES C CXX)
# 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()
## Flags for the compiler. No warning allowed.
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
elseif (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX /EHsc")
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)
## Create Version
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Get the current working branch
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
## target directories for install
set(INSTALL_INCLUDE_DIR_BASE include)
set(INSTALL_INCLUDE_DIR ${INSTALL_INCLUDE_DIR_BASE}/HEJ)
set(INSTALL_BIN_DIR bin)
set(INSTALL_LIB_DIR lib)
set(INSTALL_CONFIG_DIR lib/cmake/HEJ)
## Template files
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Templates/Version.hh.in
${PROJECT_BINARY_DIR}/include/HEJ/Version.hh @ONLY )
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Templates/HEJ-config.cc.in
${PROJECT_BINARY_DIR}/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})
## Add directories and find dependences
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include)
find_package(fastjet REQUIRED)
include_directories(${fastjet_INCLUDE_DIRS})
find_package(CLHEP 2.3 REQUIRED)
include_directories(${CLHEP_INCLUDE_DIRS})
find_package(LHAPDF REQUIRED)
include_directories(${LHAPDF_INCLUDE_DIRS})
## 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)
include_directories(${Boost_INCLUDE_DIRS})
find_package(yaml-cpp) # requiring yaml does not work with fedora
include_directories(${YAML_CPP_INCLUDE_DIR})
if(${EXCLUDE_HepMC})
message(STATUS "Skipping HepMC")
# avoid "unused variable" warning if EXCLUDE_rivet is set by user
set(EXCLUDE_rivet TRUE)
else()
find_package(HepMC 2)
endif()
if(${HepMC_FOUND})
set(
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -DHEJ_BUILD_WITH_HepMC_VERSION=${HepMC_VERSION_MAJOR}"
)
include_directories(${HepMC_INCLUDE_DIRS})
if(${EXCLUDE_rivet})
message(STATUS "Skipping rivet")
else()
find_package(rivet)
endif()
if(${rivet_FOUND})
include_directories(${rivet_INCLUDE_DIRS})
set(
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -DHEJ_BUILD_WITH_RIVET"
)
endif()
endif()
if(${EXCLUDE_QCDloop})
message(STATUS "Skipping QCDloop")
else()
find_package(QCDloop 2)
endif()
if(${QCDloop_FOUND})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHEJ_BUILD_WITH_QCDLOOP")
include_directories(SYSTEM ${QCDloop_INCLUDE_DIRS})
endif()
+if(${EXCLUDE_HighFive})
+ message(STATUS "Skipping HighFive")
+else()
+ find_package(HighFive QUIET)
+ find_package_handle_standard_args( HighFive CONFIG_MODE )
+endif()
+if(${HighFive_FOUND})
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHEJ_BUILD_WITH_HDF5")
+ include_directories($<TARGET_PROPERTY:HighFive,INTERFACE_INCLUDE_DIRECTORIES>)
+endif()
add_subdirectory(src)
## define executable
add_executable(HEJ src/bin/HEJ.cc)
## link libraries
target_link_libraries(HEJ hejlib)
add_executable(HEJ-config src/bin/HEJ-config.cc)
file(GLOB hej_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/HEJ/*.hh ${PROJECT_BINARY_DIR}/include/HEJ/*.hh)
file(GLOB lhef_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/LHEF/*.h)
install(FILES ${hej_headers} DESTINATION ${INSTALL_INCLUDE_DIR})
install(FILES ${lhef_headers} DESTINATION include/LHEF/)
install(TARGETS HEJ HEJ-config DESTINATION ${INSTALL_BIN_DIR})
## tests
enable_testing()
set(tst_dir "${CMAKE_CURRENT_SOURCE_DIR}/t")
add_executable(test_classify ${tst_dir}/test_classify.cc)
add_executable(test_psp ${tst_dir}/test_psp.cc)
add_executable(test_ME_generic ${tst_dir}/test_ME_generic.cc)
add_executable(check_res ${tst_dir}/check_res.cc)
add_executable(check_lhe ${tst_dir}/check_lhe.cc)
add_library(scales SHARED ${tst_dir}/scales.cc)
add_executable(test_scale_import ${tst_dir}/test_scale_import)
add_executable(test_descriptions ${tst_dir}/test_descriptions)
add_executable(test_scale_arithmetics ${tst_dir}/test_scale_arithmetics)
add_executable(test_parameters ${tst_dir}/test_parameters)
add_executable(test_colours ${tst_dir}/test_colours)
target_link_libraries(test_classify hejlib)
target_link_libraries(test_psp hejlib)
target_link_libraries(test_ME_generic hejlib)
target_link_libraries(check_res hejlib)
target_link_libraries(check_lhe hejlib)
target_link_libraries(test_scale_import hejlib)
target_link_libraries(test_descriptions hejlib)
target_link_libraries(test_scale_arithmetics hejlib)
target_link_libraries(test_parameters hejlib)
target_link_libraries(test_colours hejlib)
## add tests
add_test(
NAME t_classify
COMMAND test_classify ${tst_dir}/classify.lhe.gz
)
add_test(
NAME t_psp
COMMAND test_psp ${tst_dir}/psp_gen.lhe.gz
)
set(tst_ME_data_dir "${tst_dir}/ME_data")
add_test(
NAME t_ME_j
COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtinf.yml ${tst_ME_data_dir}/ME_jets_tree.dat ${tst_ME_data_dir}/PSP_jets.lhe.gz
)
add_test(
NAME t_ME_h
COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtinf.yml ${tst_ME_data_dir}/ME_h_mtinf_tree.dat ${tst_ME_data_dir}/PSP_h.lhe.gz
)
# add_test(
# NAME t_ME_w
# COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_w_tree.dat ${tst_ME_data_dir}/PSP_w.lhe.gz
# )
add_test(
NAME t_ME_w_FKL
COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_w_FKL_tree.dat ${tst_ME_data_dir}/PSP_w_FKL.lhe.gz
)
add_test(
NAME t_ME_w_FKL_virt
COMMAND test_ME_generic ${tst_ME_data_dir}/config_w_ME.yml ${tst_ME_data_dir}/ME_w_FKL_virt.dat ${tst_ME_data_dir}/PSP_w_FKL.lhe.gz
)
if(${QCDloop_FOUND})
add_test(
NAME t_ME_h_mt
COMMAND test_ME_generic ${tst_ME_data_dir}/config_mt.yml ${tst_ME_data_dir}/ME_h_mt_tree.dat ${tst_ME_data_dir}/PSP_h.lhe.gz
)
add_test(
NAME t_ME_h_mtmb
COMMAND test_ME_generic ${tst_ME_data_dir}/config_mtmb.yml ${tst_ME_data_dir}/ME_h_mtmb_tree.dat ${tst_ME_data_dir}/PSP_h.lhe.gz
)
endif()
add_test(
NAME t_2j
COMMAND check_res ${tst_dir}/2j.lhe.gz 3.49391e+07 419684
)
add_test(
NAME t_3j
COMMAND check_res ${tst_dir}/3j.lhe.gz 2.37902e+06 25746.6
)
add_test(
NAME t_4j
COMMAND check_res ${tst_dir}/4j.lhe.gz 603713 72822.6
)
add_test(
NAME t_h_3j
COMMAND check_res ${tst_dir}/h_3j.lhe.gz 0.821622 0.0220334
)
add_test(
NAME t_h_3j_uno
COMMAND check_res ${tst_dir}/h_3j_uno.lhe.gz 0.0261968 0.000341549 uno
)
if(${HepMC_FOUND})
file(READ "${tst_dir}/jet_config.yml" config)
file(WRITE "${tst_dir}/jet_config_withHepMC.yml" "${config} - tst.hepmc")
if(${rivet_FOUND})
file(READ "${tst_dir}/jet_config_withHepMC.yml" config)
file(WRITE "${tst_dir}/jet_config_withRivet.yml" "${config}\n\nanalysis:\n rivet: MC_XS\n output: tst")
add_test(
NAME t_main
COMMAND HEJ ${tst_dir}/jet_config_withRivet.yml ${tst_dir}/2j.lhe.gz
)
else()
add_test(
NAME t_main
COMMAND HEJ ${tst_dir}/jet_config_withHepMC.yml ${tst_dir}/2j.lhe.gz
)
endif()
if(${HepMC_VERSION_MAJOR} GREATER 2)
add_executable(check_hepmc ${tst_dir}/check_hepmc.cc)
target_link_libraries(check_hepmc hejlib)
add_test(
NAME t_hepmc
COMMAND check_hepmc tst.hepmc
)
endif()
else()
add_test(
NAME t_main
COMMAND HEJ ${tst_dir}/jet_config.yml ${tst_dir}/2j.lhe.gz
)
endif()
add_test(
NAME t_lhe
COMMAND check_lhe tst.lhe
)
add_test(
NAME t_scale_import
COMMAND test_scale_import ${tst_dir}/jet_config_with_import.yml
)
add_test(
NAME t_descriptions
COMMAND test_descriptions
)
add_test(
NAME t_scale_arithmetics
COMMAND test_scale_arithmetics ${tst_dir}/jet_config.yml ${tst_dir}/2j.lhe.gz
)
add_test(
NAME test_parameters
COMMAND test_parameters
)
add_test(
NAME t_colour_flow
COMMAND test_colours
)
+if(${HighFive_FOUND})
+ add_executable(test_hdf5 ${tst_dir}/test_hdf5.cc)
+ target_link_libraries(test_hdf5 hejlib)
+ add_test(
+ NAME t_hdf5
+ COMMAND test_hdf5 ${tst_dir}/Wm9-g4-repack.hdf5
+ )
+endif()
diff --git a/Changes-API.md b/Changes-API.md
index 8df426b..ab0bf51 100644
--- a/Changes-API.md
+++ b/Changes-API.md
@@ -1,56 +1,59 @@
# 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`, where the main features are documented.
## Version 2.X
### 2.X.0
* Made `MatrixElement.tree_kin(...)` and `MatrixElement.tree_param(...)` public
* New class `CrossSectionAccumulator` to keep track of Cross Section of the
different subproccess
* New template struct `Parameters` similar to old `Weights`
- `Weights` are now an alias for `Parameters<double>`. Calling `Weights` did
not change
- `Weights.hh` was replaced by `Parameters.hh`. The old `Weights.hh` header
will be removed in HEJ Version 2.3.0
* Function to multiplication and division of `EventParameters.weight` by double
- This can be combined with `Parameters`, e.g.
`Parameters<EventParameters>*Weights`, see also `Events.parameters()`
- Moved `EventParameters` to `Parameters.hh` header
* Restructured `Event` class
- `Event` can now only be build from a (new) `Event::EventData` class
- Removed default constructor for `Event`
- `Event::EventData` replaces the old `UnclusteredEvent` struct.
- `UnclusteredEvent` is now deprecated, and will be removed in HEJ Version
2.3.0
- Removed `Event.unclustered()` function
- Added new member function `Events.parameters()`, to directly access
(underlying) `Parameters<EventParameters>`
* 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)
+* New abstact `EventReader` class, as base for reading events from files
+ - Moved LHE file reader to `HEJ::LesHouchesReader`
+ - New `HEJ::HDF5Reader` to read `hdf5` files
## Version 2.0
### 2.0.5
* no further changes to API
### 2.0.4
* Fixed wrong path of `HEJ_INCLUDE_DIR` in `hej-config.cmake`
### 2.0.3
* no further changes to API
### 2.0.2
* no further changes to API
### 2.0.1
* no further changes to API
diff --git a/Changes.md b/Changes.md
index f1d36fb..22ecbcd 100644
--- a/Changes.md
+++ b/Changes.md
@@ -1,43 +1,46 @@
# Changelog
This is the log for changes to the HEJ program. Further changes to the HEJ API
are documented in `Changes-API.md`. If you are using HEJ as a library, please
also read the changes there.
## Version 2.X
### 2.X.0
* Allow multiplication and division of multiple scale functions e.g.
`H_T/2*m_j1j2`
* Print cross sections at end of run
* Follow HepMC convention for particle Status codes: incoming = 11,
decaying = 2, outgoing = 1 (unchanged)
* 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
* Allow changing the regulator lambda in input (`regulator parameter`, only for
advanced users)
+* Added support to read `hdf5` event files suggested in
+ [arXiv:1905.05120](https://arxiv.org/abs/1905.05120) (needs
+ [HighFive](https://github.com/BlueBrain/HighFive))
## 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.
diff --git a/doc/developer_manual/biblio.bib b/doc/developer_manual/biblio.bib
index a2bfb84..82da7ee 100644
--- a/doc/developer_manual/biblio.bib
+++ b/doc/developer_manual/biblio.bib
@@ -1,173 +1,184 @@
@Article{Andersen:2008gc,
author = "Andersen, Jeppe R. and Del Duca, Vittorio and White, Chris
D.",
title = "{Higgs Boson Production in Association with Multiple Hard
Jets}",
journal = "JHEP",
volume = "02",
year = "2009",
pages = "015",
eprint = "0808.3696",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
doi = "10.1088/1126-6708/2009/02/015",
SLACcitation = "%%CITATION = 0808.3696;%%"
}
@Article{Andersen:2008ue,
author = "Andersen, Jeppe R. and White, Chris D.",
title = "{A New Framework for Multijet Predictions and its
application to Higgs Boson production at the LHC}",
journal = "Phys. Rev.",
volume = "D78",
year = "2008",
pages = "051501",
eprint = "0802.2858",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
doi = "10.1103/PhysRevD.78.051501",
SLACcitation = "%%CITATION = 0802.2858;%%"
}
@article{Alwall:2006yp,
author = "Alwall, Johan and others",
title = "{A Standard format for Les Houches event files}",
booktitle = "{Monte Carlos for the LHC: A Workshop on the Tools for
LHC Event Simulation (MC4LHC) Geneva, Switzerland, July
17-16, 2006}",
journal = "Comput. Phys. Commun.",
volume = "176",
year = "2007",
pages = "300-304",
doi = "10.1016/j.cpc.2006.11.010",
eprint = "hep-ph/0609017",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "FERMILAB-PUB-06-337-T, CERN-LCGAPP-2006-03",
SLACcitation = "%%CITATION = HEP-PH/0609017;%%"
}
@article{Andersen:2017kfc,
author = "Andersen, Jeppe R. and Hapola, Tuomas and Maier, Andreas
and Smillie, Jennifer M.",
title = "{Higgs Boson Plus Dijets: Higher Order Corrections}",
journal = "JHEP",
volume = "09",
year = "2017",
pages = "065",
doi = "10.1007/JHEP09(2017)065",
eprint = "1706.01002",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "EDINBURGH-2017-11, IPPP-17-33, DCPT-17-66, MCNET-17-9",
SLACcitation = "%%CITATION = ARXIV:1706.01002;%%"
}
@article{Andersen:2018tnm,
author = "Andersen, Jeppe R. and Hapola, Tuomas and Heil, Marian
and Maier, Andreas and Smillie, Jennifer M.",
title = "{Higgs-boson plus Dijets: Higher-Order Matching for
High-Energy Predictions}",
year = "2018",
eprint = "1805.04446",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "DCPT/18/66, IPPP/18/33, MCnet-18-10, DCPT-18-66,
IPPP-18-33, MCNET-18-10",
SLACcitation = "%%CITATION = ARXIV:1805.04446;%%"
}
@article{Andersen:2012gk,
author = "Andersen, Jeppe R. and Hapola, Tuomas and Smillie,
Jennifer M.",
title = "{W Plus Multiple Jets at the LHC with High Energy Jets}",
journal = "JHEP",
volume = "09",
year = "2012",
pages = "047",
doi = "10.1007/JHEP09(2012)047",
eprint = "1206.6763",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "EDINBURGH-2012-13, IPPP-12-45, DCPT-12-90,
CP3-ORIGINS-2012-017, DIAS-2012-18, --DIAS-2012-18",
SLACcitation = "%%CITATION = ARXIV:1206.6763;%%"
}
@article{Andersen:2016vkp,
author = "Andersen, Jeppe R. and Medley, Jack J. and Smillie,
Jennifer M.",
title = "{$Z/\gamma^{∗}$ plus multiple hard jets in high energy
collisions}",
journal = "JHEP",
volume = "05",
year = "2016",
pages = "136",
doi = "10.1007/JHEP05(2016)136",
eprint = "1603.05460",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "EDINBURGH-2016-03, IPPP-16-19, DCPT-16-38, MCNET-16-08",
SLACcitation = "%%CITATION = ARXIV:1603.05460;%%"
}
@article{DelDuca:2003ba,
author = "Del Duca, V. and Kilgore, W. and Oleari, C. and Schmidt,
C.R. and Zeppenfeld, D.",
title = "{Kinematical limits on Higgs boson production via gluon
fusion in association with jets}",
journal = "Phys.Rev.",
volume = "D67",
pages = "073003",
doi = "10.1103/PhysRevD.67.073003",
year = "2003",
eprint = "hep-ph/0301013",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "DCPT-02-148, DFTT-20-2002, IPPP-02-74, MADPH-02-1276,
MSUHEP-20620",
SLACcitation = "%%CITATION = HEP-PH/0301013;%%",
}
@article{DelDuca:2001fn,
author = "Del Duca, V. and Kilgore, W. and Oleari, C. and Schmidt,
C. and Zeppenfeld, D.",
title = "{Gluon fusion contributions to H + 2 jet production}",
journal = "Nucl.Phys.",
volume = "B616",
pages = "367-399",
doi = "10.1016/S0550-3213(01)00446-1",
year = "2001",
eprint = "hep-ph/0108030",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "MADPH-01-1235, BNL-HET-01-28, MSUHEP-10709, DFTT-19-2001",
SLACcitation = "%%CITATION = HEP-PH/0108030;%%",
}
@article{Andersen:2011zd,
author = "Andersen, Jeppe R. and Lonnblad, Leif and Smillie,
Jennifer M.",
title = "{A Parton Shower for High Energy Jets}",
journal = "JHEP",
volume = "07",
year = "2011",
pages = "110",
doi = "10.1007/JHEP07(2011)110",
eprint = "1104.1316",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "CERN-PH-TH-2011-072, CP3-ORIGINS-2011-14,
EDINBURGH-2011-16, LU-TP-11-15, MCNET-11-12, LU-TP
--11-15",
SLACcitation = "%%CITATION = ARXIV:1104.1316;%%"
}
@article{Andersen:2017sht,
author = "Andersen, Jeppe R. and Brooks, Helen M. and Lönnblad,
Leif",
title = "{Merging High Energy with Soft and Collinear Logarithms
using HEJ and PYTHIA}",
journal = "JHEP",
volume = "09",
year = "2018",
pages = "074",
doi = "10.1007/JHEP09(2018)074",
eprint = "1712.00178",
archivePrefix = "arXiv",
primaryClass = "hep-ph",
reportNumber = "CPT/17/184, IPPP/17/92, LU-TP 17-38, MCnet-17-22,
CoEPP-MN-17-22, CPT-17-184, IPPP-17-92, LU-TP-17-38,
MCNET-17-22, COEPP-MN-17-22",
SLACcitation = "%%CITATION = ARXIV:1712.00178;%%"
}
+@article{Hoeche:2019rti,
+ author = "Höche, Stefan and Prestel, Stefan and Schulz, Holger",
+ title = "{Simulation of vector boson plus many jet final states at
+ the high luminosity LHC}",
+ year = "2019",
+ eprint = "1905.05120",
+ archivePrefix = "arXiv",
+ primaryClass = "hep-ph",
+ reportNumber = "FERMILAB-PUB-19-192-T, LU-TP 19-14, MCNET-19-09",
+ SLACcitation = "%%CITATION = ARXIV:1905.05120;%%"
+}
diff --git a/doc/developer_manual/developer_manual.tex b/doc/developer_manual/developer_manual.tex
index 6c83b9a..2c95ca3 100644
--- a/doc/developer_manual/developer_manual.tex
+++ b/doc/developer_manual/developer_manual.tex
@@ -1,1537 +1,1544 @@
\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{subcaption}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{shapes}
\usetikzlibrary{calc}
\usepackage[colorlinks,linkcolor={blue!50!black}]{hyperref}
\graphicspath{{build/figures/}{figures/}}
\emergencystretch \hsize
\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{\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}}
\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[doc:] Contains additional documentation, see section~\ref{sec:doc}.
\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. 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[FixedOrderGen:] Contains the code for the fixed-order generator,
see section~\ref{sec:HEJFOG}.
\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 html
\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={}]
doxygen Doxyfile
\end{lstlisting}
in the \texttt{doc/doxygen} 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:gitlabCI}).
\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.
\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_analysis! function creates an object 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_analysis! creates either a user-defined
analysis loaded from an external library (see the user documentation
\url{https://hej.web.cern.ch/HEJ/doc/current/user/}) or the default \lstinline!EmptyAnalysis!, which does
nothing.
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!LHEF::Reader! object (see
-\href{http://home.thep.lu.se/~leif/LHEF/}{\texttt{include/LHEF/LHEF.h}}) for
-reading events from a file in the Les
-Houches event file format~\cite{Alwall:2006yp}. A small wrapper around
-the
+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}.
\subsection{Event processing}
\label{sec:processing}
In the second stage events are continously read from the event
file. After jet clustering, a number of corresponding resummation events
are generated for each input event and fed into the analysis 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!LHEF::Reader::readEvent!\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
(fill) [mynode,below left=of cut]
{\lstinline!Analysis::fill!\nodepart{second}{analyse event}};
\node
(write) [mynode,below right=of cut]
{\lstinline!CombinedEventWriter::write!\nodepart{second}{write out event}};
\node
(control) [below=of cut] {};
\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)+(7mm, 0cm)$) -- ($(cut.north)+(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(resum.south)-(7mm, 0cm)$) -- node[left] {\lstinline!Event!} ($(cut.north)-(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(cut.south)-(3mm,0mm)$) .. controls ($(control)-(3mm,0mm)$) ..node[left] {\lstinline!Event!} (fill.east);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(cut.south)-(3mm,0mm)$) .. controls ($(control)-(3mm,0mm)$) .. (write.west);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(cut.south)+(3mm,0mm)$) .. controls ($(control)+(3mm,0mm)$) .. (fill.east);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(cut.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 and the \lstinline!HepMCWriter! 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-HEJ, \dots)
and decide according to the user settings whether to discard, keep, or
resum the event. If we perform resummation for the given event, we
generate a number of trial \lstinline!PhaseSpacePoint! objects. Phase
space generation is discussed in more detail in
section~\ref{sec:pspgen}. We then perform jet clustering according to
the settings for the resummation jets on each
\lstinline!PhaseSpacePoint!, update the factorisation and
renormalisation scale in the resulting \lstinline!Event! and reweight it
according to the ratio of pdf factors and \HEJ matrix elements between
resummation and original fixed-order event:
\begin{center}
\begin{tikzpicture}[node distance=1.5cm and 5mm]
\node (in) {};
\node (treat) [diamond,draw,below=of in,minimum size=3.5cm,
label={[anchor=west, inner sep=8pt]west:discard},
label={[anchor=east, inner sep=14pt]east:keep},
label={[anchor=south, inner sep=20pt]south:reweight}
] {};
\draw (treat.north west) -- (treat.south east);
\draw (treat.north east) -- (treat.south west);
\node
(psp) [mynode,below=of treat]
{\lstinline!PhaseSpacePoint! constructor};
\node
(cluster) [mynode,below=of psp]
{\lstinline!Event::EventData::cluster!\nodepart{second}{cluster jets}};
\node
(colour) [mynode,below=of cluster]
{\lstinline!Event::generate_colours()!\nodepart{second}{generate particle colour}};
\node
(gen_scales) [mynode,below=of colour]
{\lstinline!ScaleGenerator::operator()!\nodepart{second}{update scales}};
\node
(rescale) [mynode,below=of gen_scales]
{\lstinline!PDF::pdfpt!,
\lstinline!MatrixElement!\nodepart{second}{reweight}};
\node (out) [below of=rescale] {};
\draw[-{Latex[length=3mm, width=1.5mm]}]
(in.south) -- node[left] {\lstinline!Event!} (treat.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
(treat.south) -- node[left] {\lstinline!Event!} (psp.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
(psp.south) -- (cluster.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(psp.south)+(7mm, 0cm)$) -- ($(cluster.north)+(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(psp.south)-(7mm, 0cm)$) -- node[left]
{\lstinline!PhaseSpacePoint!} ($(cluster.north)-(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
(cluster.south) -- (colour.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(cluster.south)+(7mm, 0cm)$) -- ($(colour.north)+(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(cluster.south)-(7mm, 0cm)$) -- node[left]
{\lstinline!Event!} ($(colour.north)-(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
(colour.south) -- (gen_scales.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(colour.south)+(7mm, 0cm)$) -- ($(gen_scales.north)+(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(colour.south)-(7mm, 0cm)$) -- node[left]
{\lstinline!Event!} ($(gen_scales.north)-(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
(gen_scales.south) -- (rescale.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(gen_scales.south)+(7mm, 0cm)$) -- ($(rescale.north)+(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(gen_scales.south)-(7mm, 0cm)$) -- node[left]
{\lstinline!Event!} ($(rescale.north)-(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
(rescale.south) -- (out.north);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(rescale.south)+(7mm, 0cm)$) -- ($(out.north)+(7mm, 0cm)$);
\draw[-{Latex[length=3mm, width=1.5mm]}]
($(rescale.south)-(7mm, 0cm)$) -- node[left]
{\lstinline!Event!} ($(out.north)-(7mm, 0cm)$);
\node (helper) at ($(treat.east) + (15mm,0cm)$) {};
\draw[-{Latex[length=3mm, width=1.5mm]}]
(treat.east) -- ($(treat.east) + (15mm,0cm)$)
-- node[left] {\lstinline!Event!} (helper |- gen_scales.east) -- (gen_scales.east)
;
\end{tikzpicture}
\end{center}
\subsection{Phase space point generation}
\label{sec:pspgen}
The resummed and matched \HEJ cross section for pure jet production of
FKL configurations is given by (cf. eq. (3) of~\cite{Andersen:2018tnm})
\begin{align}
\label{eq:resumdijetFKLmatched2}
% \begin{split}
\sigma&_{2j}^\mathrm{resum, match}=\sum_{f_1, f_2}\ \sum_m
\prod_{j=1}^m\left(
\int_{p_{j\perp}^B=0}^{p_{j\perp}^B=\infty}
\frac{\mathrm{d}^2\mathbf{p}_{j\perp}^B}{(2\pi)^3}\ \int
\frac{\mathrm{d} y_j^B}{2} \right) \
(2\pi)^4\ \delta^{(2)}\!\!\left(\sum_{k=1}^{m}
\mathbf{p}_{k\perp}^B\right)\nonumber\\
&\times\ x_a^B\ f_{a, f_1}(x_a^B, Q_a^B)\ x_b^B\ f_{b, f_2}(x_b^B, Q_b^B)\
\frac{\overline{\left|\mathcal{M}_\text{LO}^{f_1f_2\to f_1g\cdots
gf_2}\big(\big\{p^B_j\big\}\big)\right|}^2}{(\hat {s}^B)^2}\nonumber\\
& \times (2\pi)^{-4+3m}\ 2^m \nonumber\\
&\times\ \sum_{n=2}^\infty\
\int_{p_{1\perp}=p_{\perp,\mathrm{min}} }^{p_{1\perp}=\infty}
\frac{\mathrm{d}^2\mathbf{p}_{1\perp}}{(2\pi)^3}\
\int_{p_{n\perp}=p_{\perp,\mathrm{min}}}^{p_{n\perp}=\infty}
\frac{\mathrm{d}^2\mathbf{p}_{n\perp}}{(2\pi)^3}\
\prod_{i=2}^{n-1}\int_{p_{i\perp}=\lambda}^{p_{i\perp}=\infty}
\frac{\mathrm{d}^2\mathbf{p}_{i\perp}}{(2\pi)^3}\ (2\pi)^4\ \delta^{(2)}\!\!\left(\sum_{k=1}^n
\mathbf{p}_{k\perp}\right )\\
&\times \ \mathbf{T}_y \prod_{i=1}^n
\left(\int \frac{\mathrm{d} y_i}{2}\right)\
\mathcal{O}_{mj}^e\
\left(\prod_{l=1}^{m-1}\delta^{(2)}(\mathbf{p}_{\mathcal{J}_{l}\perp}^B -
\mathbf{j}_{l\perp})\right)\
\left(\prod_{l=1}^m\delta(y^B_{\mathcal{J}_l}-y_{\mathcal{J}_l})\right)
\ \mathcal{O}_{2j}(\{p_i\})\nonumber\\
&\times \frac{(\hat{s}^B)^2}{\hat{s}^2}\ \frac{x_a f_{a,f_1}(x_a, Q_a)\ x_b f_{b,f_2}(x_b, Q_b)}{x_a^B\ f_{a,f_1}(x_a^B, Q_a^B)\ x_b^B\ f_{b,f_2}(x_b^B, Q_b^B)}\ \frac{\overline{\left|\mathcal{M}_{\mathrm{HEJ}}^{f_1 f_2\to f_1 g\cdots
gf_2}(\{ p_i\})\right|}^2}{\overline{\left|\mathcal{M}_\text{LO, HEJ}^{f_1f_2\to f_1g\cdots
gf_2}\big(\big\{p^B_j\big\}\big)\right|}^{2}} \,.\nonumber
% \end{split}
\end{align}
The first two lines correspond to the generation of the fixed-order
input events with incoming partons $f_1, f_2$ and outgoing momenta
$p_j^B$, where $\mathbf{p}_{j\perp}^B$ and $y_j^B$ denote the respective
transverse momentum and rapidity. Note that, at leading order, these
coincide with the fixed-order jet momenta $p_{\mathcal{J}_j}^B$.
$f_{a,f_1}(x_a, Q_a),f_{b,f_2}(x_b, Q_b)$ are the pdf factors for the incoming partons with
momentum fractions $x_a$ and $x_b$. The square of the partonic
centre-of-mass energy is denoted by $\hat{s}^B$ and
$\mathcal{M}_\text{LO}^{f_1f_2\to f_1g\cdots gf_2}$ is the
leading-order matrix element.
The third line is a factor accounting for the different multiplicities
between fixed-order and resummation events. Lines four and five are
the integration over the resummation phase space described in this
section. $p_i$ are the momenta of the outgoing partons in resummation
phase space. $\mathbf{T}_y$ denotes rapidity
ordering and $\mathcal{O}_{mj}^e$ projects out the exclusive $m$-jet
component. The relation between resummation and fixed-order momenta is
fixed by the $\delta$ functions. The first sets each transverse fixed-order jet
momentum to some function $\mathbf{j_{l\perp}}$ of the resummation
momenta. The exact form is described in section~\ref{sec:ptj_res}. The second
$\delta$ forces the rapidities of resummation and fixed-order jets to be
the same. Finally, the last line is the reweighting of pdf and matrix
element factors already shown in section~\ref{sec:resum}.
There are two kinds of cut-off in the integration over the resummation
partons. $\lambda$ is a technical cut-off connected to the cancellation
of infrared divergencies between real and virtual corrections. Its
numerical value is set in
\texttt{include/HEJ/Constants.h}. $p_{\perp,\mathrm{min}}$ regulates
and \emph{uncancelled} divergence in the extremal parton momenta. Its
size is set by the user configuration \url{https://hej.web.cern.ch/HEJ/doc/current/user/HEJ.html#settings}.
It is straightforward to generalise eq.~(\ref{eq:resumdijetFKLmatched2})
to unordered configurations and processes with additional colourless
emissions, for example a Higgs or electroweak boson. In the latter case only
the fixed-order integration and the matrix elements change.
\subsubsection{Gluon Multiplicity}
\label{sec:psp_ng}
The first step in evaluating the resummation phase space in
eq.~(\ref{eq:resumdijetFKLmatched2}) is to randomly pick terms in the
sum over the number of emissions. This sampling of the gluon
multiplicity is done in the \lstinline!PhaseSpacePoint::sample_ng!
function in \texttt{src/PhaseSpacePoint.cc}.
The typical number of extra emissions depends strongly on the rapidity
span of the underlying fixed-order event. Let us, for example, consider
a fixed-order FKL-type multi-jet configuration with rapidities
$y_{j_f},\,y_{j_b}$ of the most forward and backward jets,
respectively. By eq.~(\ref{eq:resumdijetFKLmatched2}), the jet
multiplicity and the rapidity of each jet are conserved when adding
resummation. This implies that additional hard radiation is restricted
to rapidities $y$ within a region $y_{j_b} \lesssim y \lesssim
y_{j_f}$. Within \HEJ, we require the most forward and most backward
emissions to be hard \todo{specify how hard} in order to avoid divergences, so this constraint
in fact applies to \emph{all} additional radiation.
To simplify the remaining discussion, let us remove the FKL rapidity
ordering
\begin{equation}
\label{eq:remove_y_order}
\mathbf{T}_y \prod_{i=1}^n\int \frac{\mathrm{d}y_i}{2} =
\frac{1}{n!}\prod_{i=1}^n\int
\frac{\mathrm{d}y_i}{2}\,,
\end{equation}
where all rapidity integrals now cover a region which is approximately
bounded by $y_{j_b}$ and $y_{j_f}$. Each of the $m$ jets has to contain at least
one parton; selecting random emissions we can rewrite the phase space
integrals as
\begin{equation}
\label{eq:select_jets}
\frac{1}{n!}\prod_{i=1}^n\int [\mathrm{d}p_i] =
\left(\prod_{i=1}^{m}\int [\mathrm{d}p_i]\ {\cal J}_i(p_i)\right)
\frac{1}{n_g!}\prod_{i=m+1}^{m+n_g}\int [\mathrm{d}p_i]
\end{equation}
with jet selection functions
\begin{equation}
\label{eq:def_jet_selection}
{\cal J}_i(p) =
\begin{cases}
1 &p\text{ clustered into jet }i\\
0 & \text{otherwise}
\end{cases}
\end{equation}
and $n_g \equiv n - m$. Here and in the following we use the short-hand
notation $[\mathrm{d}p_i]$ to denote the phase-space measure for parton
$i$. As is evident from eq.~\eqref{eq:select_jets}, adding an extra emission
$n_g+1$ introduces a suppression factor $\tfrac{1}{n_g+1}$. However, the
additional phase space integral also results in an enhancement proportional
to $\Delta y_{j_f j_b} = y_{j_f} - y_{j_b}$. This is a result of the
rapidity-independence of the MRK limit of the integrand, consisting of the
matrix elements divided by the flux factor. Indeed, we observe that the
typical number of gluon emissions is to a good approximation proportional to
the rapidity separation and the phase space integral is dominated by events
with $n_g \approx \Delta y_{j_f j_b}$.
For the actual phase space sampling, we assume a Poisson distribution
and extract the mean number of gluon emissions in different rapidity
bins and fit the results to a linear function in $\Delta y_{j_f j_b}$,
finding a coefficient of $0.975$ for the inclusive production of a Higgs
boson with two jets. Here are the observed and fitted average gluon
multiplicities as a function of $\Delta y_{j_f j_b}$:
\begin{center}
\includegraphics[width=.75\textwidth]{ng_mean}
\end{center}
As shown for two rapidity slices the assumption of a Poisson
distribution is also a good approximation:
\begin{center}
\includegraphics[width=.49\textwidth]{{ng_1.5}.pdf}\hfill
\includegraphics[width=.49\textwidth]{{ng_5.5}.pdf}
\end{center}
\subsubsection{Number of Gluons inside Jets}
\label{sec:psp_ng_jet}
For each of the $n_g$ gluon emissions we can split the phase-space
integral into a (disconnected) region inside the jets and a remainder:
\begin{equation}
\label{eq:psp_split}
\int [\mathrm{d}p_i] = \int [\mathrm{d}p_i]\,
\theta\bigg(\sum_{j=1}^{m}{\cal J}_j(p_i)\bigg) + \int [\mathrm{d}p_i]\,
\bigg[1-\theta\bigg(\sum_{j=1}^{m}{\cal J}_j(p_i)\bigg)\bigg]\,.
\end{equation}
The next step is to decide how many of the gluons will form part of a
jet. This is done in the \lstinline!PhaseSpacePoint::sample_ng_jets!
function.
We choose an importance sampling which is flat in the plane
spanned by the azimuthal angle $\phi$ and the rapidity $y$. This is
observed in BFKL and valid in the limit of Multi-Regge-Kinematics
(MRK). Furthermore, we assume anti-$k_t$ jets, which cover an area of
$\pi R^2$.
In principle, the total accessible area in the $y$-$\phi$ plane is given
by $2\pi \Delta y_{fb}$, where $\Delta y_{fb}\geq \Delta y_{j_f j_b}$ is
the a priori unknown rapidity separation between the most forward and
backward partons. In most cases the extremal jets consist of single
partons, so that $\Delta y_{fb} = \Delta y_{j_f j_b}$. For the less common
case of two partons forming a jet we observe a maximum distance of $R$
between the constituents and the jet centre. In rare cases jets have
more than two constituents. Empirically, they are always within a
distance of $\tfrac{5}{3}R$ to the centre of the jet, so
$\Delta y_{fb} \leq \Delta y_{j_f j_b} + \tfrac{10}{3} R$. In practice, the
extremal partons are required to carry a large fraction of the jet
transverse momentum and will therefore be much closer to the jet axis.
In summary, for sufficiently large rapidity separations we can use the
approximation $\Delta y_{fb} \approx \Delta y_{j_f j_b}$. This scenario
is depicted here:
\begin{center}
\includegraphics[width=0.5\linewidth]{ps_large_y}
\end{center}
If there is no overlap between jets, the probability $p_{\cal J, >}$ for
an extra gluon to end up inside a jet is then given by
\begin{equation}
\label{eq:p_J_large}
p_{\cal J, >} = \frac{(m - 1)\*R^2}{2\Delta y_{j_f j_b}}\,.
\end{equation}
For a very small rapidity separation, eq.~\eqref{eq:p_J_large}
obviously overestimates the true probability. The maximum phase space
covered by jets in the limit of a vanishing rapidity distance between
all partons is $2mR \Delta y_{fb}$:
\begin{center}
\includegraphics[width=0.5\linewidth]{ps_small_y}
\end{center}
We therefore estimate the probability for a parton to end up inside a jet as
\begin{equation}
\label{eq:p_J}
p_{\cal J} = \min\bigg(\frac{(m - 1)\*R^2}{2\Delta y_{j_f j_b}}, \frac{mR}{\pi}\bigg)\,.
\end{equation}
Here we compare this estimate with the actually observed
fraction of additional emissions into jets as a function of the rapidity
separation:
\begin{center}
\includegraphics[width=0.75\linewidth]{pJ}
\end{center}
\subsubsection{Gluons outside Jets}
\label{sec:gluons_nonjet}
Using our estimate for the probability of a gluon to be a jet
constituent, we choose a number $n_{g,{\cal J}}$ of gluons inside
jets, which also fixes the number $n_g - n_{g,{\cal J}}$ of gluons
outside jets. As explained later on, we need to generate the momenta of
the gluons outside jets first. This is done in
\lstinline!PhaseSpacePoint::gen_non_jet!.
The azimuthal angle $\phi$ is generated flat within $0\leq \phi \leq 2
\pi$. The allowed rapidity interval is set by the most forward and
backward partons, which are necessarily inside jets. Since these parton
rapidities are not known at this point, we also have to postpone the
rapidity generation for the gluons outside jets. For the scalar
transverse momentum $p_\perp = |\mathbf{p}_\perp|$ of a gluon outside
jets we use the parametrisation
\begin{equation}
\label{eq:p_nonjet}
p_\perp = \lambda + \tilde{p}_\perp\*\tan(\tau\*r)\,, \qquad
\tau = \arctan\bigg(\frac{p_{\perp{\cal J}_\text{min}} - \lambda}{\tilde{p}_\perp}\bigg)\,.
\end{equation}
For $r \in [0,1)$, $p_\perp$ is always less than the minimum momentum
$p_{\perp{\cal J}_\text{min}}$ required for a jet. $\tilde{p}_\perp$ is
a free parameter, a good empirical value is $\tilde{p}_\perp = [1.3 +
0.2\*(n_g - n_{g,\cal J})]\,$GeV
\subsubsection{Resummation jet momenta}
\label{sec:ptj_res}
On the one hand, each jet momentum is given by the sum of its
constituent momenta. On the other hand, the resummation jet momenta are
fixed by the constraints in line five of the master
equation~\eqref{eq:resumdijetFKLmatched2}. We therefore have to
calculate the resummation jet momenta from these constraints before
generating the momenta of the gluons inside jets. This is done in
\lstinline!PhaseSpacePoint::reshuffle! and in the free
\lstinline!resummation_jet_momenta! function (declared in \texttt{resummation\_jet.hh}).
The resummation jet momenta are determined by the $\delta$ functions in
line five of eq.~(\ref{eq:resumdijetFKLmatched2}). The rapidities are
fixed to the rapidities of the jets in the input fixed-order events, so
that the FKL ordering is guaranteed to be preserved.
In traditional \HEJ reshuffling the transverse momentum are given through
\begin{equation}
\label{eq:ptreassign_old}
\mathbf{p}^B_{\mathcal{J}_{l\perp}} = \mathbf{j}_{l\perp} \equiv \mathbf{p}_{\mathcal{J}_{l}\perp}
+ \mathbf{q}_\perp \,\frac{|\mathbf{p}_{\mathcal{J}_{l}\perp}|}{P_\perp},
\end{equation}
where $\mathbf{q}_\perp = \sum_{j=1}^n \mathbf{p}_{i\perp}
\bigg[1-\theta\bigg(\sum_{j=1}^{m}{\cal J}_j(p_i)\bigg)\bigg] $ is the
total transverse momentum of all partons \emph{outside} jets and
$P_\perp = \sum_{j=1}^m |\mathbf{p}_{\mathcal{J}_{j}\perp}|$. Since the
total transverse momentum of an event vanishes, we can also use
$\mathbf{q}_\perp = - \sum_{j=1}^m
\mathbf{p}_{\mathcal{J}_{j}\perp}$. Eq.~(\ref{eq:ptreassign}) is a
non-linear system of equations in the resummation jet momenta
$\mathbf{p}_{\mathcal{J}_{l}\perp}$. Hence we would have to solve
\begin{equation}
\label{eq:ptreassign_eq}
\mathbf{p}_{\mathcal{J}_{l}\perp}=\mathbf{j}^B_{l\perp} \equiv\mathbf{j}_{l\perp}^{-1}
\left(\mathbf{p}^B_{\mathcal{J}_{l\perp}}\right)
\end{equation}
numerically.
Since solving such a system is computationally expensive, we instead
change the reshuffling around to be linear in the resummation jet
momenta. Hence~\eqref{eq:ptreassign_eq} gets replaces by
\begin{equation}
\label{eq:ptreassign}
\mathbf{p}_{\mathcal{J}_{l\perp}} = \mathbf{j}^B_{l\perp} \equiv \mathbf{p}^B_{\mathcal{J}_{l}\perp}
- \mathbf{q}_\perp \,\frac{|\mathbf{p}^B_{\mathcal{J}_{l}\perp}|}{P^B_\perp},
\end{equation}
which is linear in the resummation momentum. Consequently the equivalent
of~\eqref{eq:ptreassign_old} is non-linear in the Born momentum. However
the exact form of~\eqref{eq:ptreassign_old} is not relevant for the resummation.
Both methods have been tested for two and three jets with the \textsc{rivet}
standard analysis \texttt{MC\_JETS}. They didn't show any differences even
after $10^9$ events.
The reshuffling relation~\eqref{eq:ptreassign} allows the transverse
momenta $p^B_{\mathcal{J}_{l\perp}}$ of the fixed-order jets to be
somewhat below the minimum transverse momentum of resummation jets. It
is crucial that this difference does not become too large, as the
fixed-order cross section diverges for vanishing transverse momenta. In
the production of a Higgs boson with resummation jets above $30\,$GeV we observe
that the contribution from fixed-order events with jets softer than
about $20\,$GeV can be safely neglected. This is shown in the following
plot of the differential cross section over the transverse momentum of
the softest fixed-order jet:
\begin{center}
\includegraphics[width=.75\textwidth]{ptBMin}
\end{center}
Finally, we have to account for the fact that the reshuffling
relation~\eqref{eq:ptreassign} is non-linear in the Born momenta. To
arrive at the master formula~\eqref{eq:resumdijetFKLmatched2} for the
cross section, we have introduced unity in the form of an integral over
the Born momenta with $\delta$ functions in the integrand, that is
\begin{equation}
\label{eq:delta_intro}
1 = \int_{p_{j\perp}^B=0}^{p_{j\perp}^B=\infty}
\mathrm{d}^2\mathbf{p}_{j\perp}^B\delta^{(2)}(\mathbf{p}_{\mathcal{J}_{j\perp}}^B -
\mathbf{j}_{j\perp})\,.
\end{equation}
If the arguments of the $\delta$ functions are not linear in the Born
momenta, we have to compensate with additional Jacobians as
factors. Explicitly, for the reshuffling relation~\eqref{eq:ptreassign}
we have
\begin{equation}
\label{eq:delta_rewrite}
\prod_{l=1}^m \delta^{(2)}(\mathbf{p}_{\mathcal{J}_{l\perp}}^B -
\mathbf{j}_{l\perp}) = \Delta \prod_{l=1}^m \delta^{(2)}(\mathbf{p}_{\mathcal{J}_{l\perp}} -
\mathbf{j}_{l\perp}^B)\,,
\end{equation}
where $\mathbf{j}_{l\perp}^B$ is given by~\eqref{eq:ptreassign_eq} and only
depends on the Born momenta. We have extended the product to run to $m$
instead of $m-1$ by eliminating the last $\delta$ function
$\delta^{(2)}\!\!\left(\sum_{k=1}^n \mathbf{p}_{k\perp}\right )$.
The Jacobian $\Delta$ is the determinant of a $2m \times 2m$ matrix with $l, l' = 1,\dots,m$
and $X, X' = x,y$.
\begin{equation}
\label{eq:jacobian}
\Delta = \left|\frac{\partial\,\mathbf{j}^B_{l'\perp}}{\partial\, \mathbf{p}^B_{{\cal J}_l \perp}} \right|
= \left| \delta_{l l'} \delta_{X X'} - \frac{q_X\, p^B_{{\cal
J}_{l'}X'}}{\left|\mathbf{p}^B_{{\cal J}_{l'} \perp}\right| P^B_\perp}\left(\delta_{l l'}
- \frac{\left|\mathbf{p}^B_{{\cal J}_l \perp}\right|}{P^B_\perp}\right)\right|\,.
\end{equation}
The determinant is calculated in \lstinline!resummation_jet_weight!,
again coming from the \texttt{resummation\_jet.hh} header.
Having to introduce this Jacobian is not a disadvantage specific to the new
reshuffling. If we instead use the old reshuffling
relation~\eqref{eq:ptreassign_old} we \emph{also} have to introduce a
similar Jacobian since we actually want to integrate over the
resummation phase space and need to transform the argument of the
$\delta$ function to be linear in the resummation momenta for this.
\subsubsection{Gluons inside Jets}
\label{sec:gluons_jet}
After the steps outlined in section~\ref{sec:psp_ng_jet}, we have a
total number of $m + n_{g,{\cal J}}$ constituents. In
\lstinline!PhaseSpacePoint::distribute_jet_partons! we distribute them
randomly among the jets such that each jet has at least one
constituent. We then generate their momenta in
\lstinline!PhaseSpacePoint::split! using the \lstinline!Splitter! class.
The phase space integral for a jet ${\cal J}$ is given by
\begin{equation}
\label{eq:ps_jetparton} \prod_{i\text{ in }{\cal J}} \bigg(\int
\mathrm{d}\mathbf{p}_{i\perp}\ \int \mathrm{d} y_i
\bigg)\delta^{(2)}\Big(\sum_{i\text{ in }{\cal J}} \mathbf{p}_{i\perp} -
\mathbf{j}_{\perp}^B\Big)\delta(y_{\mathcal{J}}-y^B_{\mathcal{J}})\,.
\end{equation}
For jets with a single constituent, the parton momentum is obiously equal to the
jet momentum. In the case of two constituents, we observe that the
partons are always inside the jet cone with radius $R$ and often very
close to the jet centre. The following plots show the typical relative
distance $\Delta R/R$ for this scenario:
\begin{center}
\includegraphics[width=0.45\linewidth]{dR_2}
\includegraphics[width=0.45\linewidth]{dR_2_small}
\end{center}
According to this preference for small values of $\Delta R$, we
parametrise the $\Delta R$ integrals as
\begin{equation}
\label{eq:dR_sampling}
\frac{\Delta R}{R} =
\begin{cases}
0.25\,x_R & x_R < 0.4 \\
1.5\,x_R - 0.5 & x_R \geq 0.4
\end{cases}\,.
\end{equation}
Next, we generate $\Theta_1 \equiv \Theta$ and use the constraint $\Theta_2 = \Theta
\pm \pi$. The transverse momentum of the first parton is then given by
\begin{equation}
\label{eq:delta_constraints}
p_{1\perp} =
\frac{p_{\mathcal{J} y} - \tan(\phi_2) p_{\mathcal{J} x}}{\sin(\phi_1)
- \tan(\phi_2)\cos(\phi_1)}\,.
\end{equation}
We get $p_{2\perp}$ by exchanging $1 \leftrightarrow 2$ in the
indices. To obtain the Jacobian of the transformation, we start from the
single jet phase space eq.~(\ref{eq:ps_jetparton}) with the rapidity
delta function already rewritten to be linear in the rapidity of the
last parton, i.e.
\begin{equation}
\label{eq:jet_2p}
\prod_{i=1,2} \bigg(\int
\mathrm{d}\mathbf{p}_{i\perp}\ \int \mathrm{d} y_i
\bigg)\delta^{(2)}\Big(\mathbf{p}_{1\perp} + \mathbf{p}_{2\perp} -
\mathbf{j}_{\perp}^B\Big)\delta(y_2- \dots)\,.
\end{equation}
The integral over the second parton momentum is now trivial; we can just replace
the integral over $y_2$ with the equivalent constraint
\begin{equation}
\label{eq:R2}
\int \mathrm{d}R_2 \ \delta\bigg(R_2 - \bigg[\phi_{\cal J} - \arctan
\bigg(\frac{p_{{\cal J}y} - p_{1y}}{p_{{\cal J}x} -
p_{1x}}\bigg)\bigg]/\cos \Theta\bigg) \,.
\end{equation}
In order to fix the integral over $p_{1\perp}$ instead, we rewrite this
$\delta$ function. This introduces the Jacobian
\begin{equation}
\label{eq:jac_pt1}
\bigg|\frac{\partial p_{1\perp}}{\partial R_2} \bigg| =
\frac{\cos(\Theta)\mathbf{p}_{2\perp}^2}{p_{{\cal J}\perp}\sin(\phi_{\cal J}-\phi_1)}\,.
\end{equation}
The final form of the integral over the two parton momenta is then
\begin{equation}
\label{eq:ps_jet_2p}
\int \mathrm{d}R_1\ R_1 \int \mathrm{d}R_2 \int \mathrm{d}x_\Theta\ 2\pi \int
\mathrm{d}p_{1\perp}\ p_{1\perp} \int \mathrm{d}p_{2\perp}
\ \bigg|\frac{\partial p_{1\perp}}{\partial R_2} \bigg|\delta(p_{1\perp}
-\dots) \delta(p_{2\perp} - \dots)\,.
\end{equation}
As is evident from section~\ref{sec:psp_ng_jet}, jets with three or more
constituents are rare and an efficient phase-space sampling is less
important. For such jets, we exploit the observation that partons with a
distance larger than $R_{\text{max}} = \tfrac{5}{3} R$ to
the jet centre are never clustered into the jet. Assuming $N$
constituents, we generate all components
for the first $N-1$ partons and fix the remaining parton with the
$\delta$-functional. In order to end up inside the jet, we use the
parametrisation
\begin{align}
\label{eq:ps_jet_param}
\phi_i ={}& \phi_{\cal J} + \Delta \phi_i\,, & \Delta \phi_i ={}& \Delta
R_i
\cos(\Theta_i)\,, \\
y_i ={}& y_{\cal J} + \Delta y_i\,, & \Delta y_i ={}& \Delta
R_i
\sin(\Theta_i)\,,
\end{align}
and generate $\Theta_i$ and $\Delta R_i$ randomly with $\Delta R_i \leq
R_{\text{max}}$ and the empiric value $R_{\text{max}} = 5\*R/3$. We can
then write the phase space integral for a single parton as $(p_\perp = |\mathbf{p}_\perp|)$
\begin{equation}
\label{eq:ps_jetparton_x}
\int \mathrm{d}\mathbf{p}_{\perp}\ \int
\mathrm{d} y \approx \int_{\Box} \mathrm{d}x_{\perp}
\mathrm{d}x_{ R}
\mathrm{d}x_{\theta}\
2\*\pi\,\*R_{\text{max}}^2\,\*x_{R}\,\*p_{\perp}\,\*(p_{\perp,\text{max}}
- p_{\perp,\text{min}})
\end{equation}
with
\begin{align}
\label{eq:ps_jetparton_parameters}
\Delta \phi ={}& R_{\text{max}}\*x_{R}\*\cos(2\*\pi\*x_\theta)\,,&
\Delta y ={}& R_{\text{max}}\*x_{R}\*\sin(2\*\pi\*x_\theta)\,, \\
p_{\perp} ={}& (p_{\perp,\text{max}} - p_{\perp,\text{min}})\*x_\perp +
p_{\perp,\text{min}}\,.
\end{align}
$p_{\perp,\text{max}}$ is determined from the requirement that the total
contribution from the first $n-1$ partons --- i.e. the projection onto the
jet $p_{\perp}$ axis --- must never exceed the jet $p_\perp$. This gives
\todo{This bound is too high}
\begin{equation}
\label{eq:pt_max}
p_{i\perp,\text{max}} = \frac{p_{{\cal J}\perp} - \sum_{j<i} p_{j\perp}
\cos \Delta
\phi_j}{\cos \Delta
\phi_i}\,.
\end{equation}
The $x$ and $y$ components of the last parton follow immediately from
the first $\delta$ function. The last rapidity is fixed by the condition that
the jet rapidity is kept fixed by the reshuffling, i.e.
\begin{equation}
\label{eq:yJ_delta}
y^B_{\cal J} = y_{\cal J} = \frac 1 2 \ln \frac{\sum_{i=1}^n E_i+ p_{iz}}{\sum_{i=1}^n E_i - p_{iz}}\,.
\end{equation}
With $E_n \pm p_{nz} = p_{n\perp}\exp(\pm y_n)$ this can be rewritten to
\begin{equation}
\label{eq:yn_quad_eq}
\exp(2y_{\cal J}) = \frac{\sum_{i=1}^{n-1} E_i+ p_{iz}+p_{n\perp} \exp(y_n)}{\sum_{i=1}^{n-1} E_i - p_{iz}+p_{n\perp} \exp(-y_n)}\,,
\end{equation}
which is a quadratic equation in $\exp(y_n)$. The physical solution is
\begin{align}
\label{eq:yn}
y_n ={}& \log\Big(-b + \sqrt{b^2 + \exp(2y_{\cal J})}\,\Big)\,,\\
b ={}& \bigg(\sum_{i=1}^{n-1} E_i + p_{iz} - \exp(2y_{\cal J})
\sum_{i=1}^{n-1} E_i - p_{iz}\bigg)/(2 p_{n\perp})\,.
\end{align}
\todo{what's wrong with the following?} To eliminate the remaining rapidity
integral, we transform the $\delta$ function to be linear in the
rapidity $y$ of the last parton. The corresponding Jacobian is
\begin{equation}
\label{eq:jacobian_y}
\bigg|\frac{\partial y_{\cal J}}{\partial y_n}\bigg|^{-1} = 2 \bigg( \frac{E_n +
p_{nz}}{E_{\cal J} + p_{{\cal J}z}} + \frac{E_n - p_{nz}}{E_{\cal J} -
p_{{\cal J}z}}\bigg)^{-1}\,.
\end{equation}
Finally, we check that all designated constituents are actually
clustered into the considered jet.
\subsubsection{Final steps}
\label{sec:final}
Knowing the rapidity span covered by the extremal partons, we can now
generate the rapdities for the partons outside jets. We perform jet
clustering on all partons and check in
\lstinline!PhaseSpacePoint::jets_ok! that all the following criteria are
fulfilled:
\begin{itemize}
\item The number of resummation jets must match the number of
fixed-order jets.
\item No partons designated to be outside jets may end up inside jets.
\item All other outgoing partons \emph{must} end up inside jets.
\item The extremal (in rapidity) partons must be inside the extremal
jets. If there is, for example, an unordered forward emission, the
most forward parton must end up inside the most forward jet and the
next parton must end up inside second jet.
\item The rapidities of fixed-order and resummation jets must match.
\end{itemize}
After this, we adjust the phase-space normalisation according to the
third line of eq.~(\ref{eq:resumdijetFKLmatched2}), determine the
flavours of the outgoing partons, and adopt any additional colourless
bosons from the fixed-order input event. Finally, we use momentum
conservation to reconstruct the momenta of the incoming partons.
\subsection{Colour connection}
\label{sec:Colour}
\begin{figure}
\input{src/ColourConnect.tex}
\caption{Left: Non-crossing colour flow dominating in the MRK limit. The
crossing of the colour line connecting to particle 2 can be resolved by
writing particle 2 on the left. Right: A colour flow with a (manifest)
colour-crossing. The crossing can only be resolved if one breaks the
rapidities order, e.g. switching particles 2 and 3. From~\cite{Andersen:2017sht}.}
\label{fig:Colour_crossing}
\end{figure}
After the phase space for the resummation event is generated, we can construct
the colour for each particle. To generate the colour flow one has to call
\lstinline!Event::generate_colours! on any \HEJ configuration. For non-\HEJ
event we do not change the colour, and assume it is provided by the user (e.g.
through the LHE file input). The colour connection is done in the large $N_c$
(infinite number of colour) limit with leading colour in
MRK~\cite{Andersen:2008ue, Andersen:2017sht}. The idea is to allow only
$t$-channel colour exchange, without any crossing colour lines. For example the
colour crossing in the colour connection on the left of
figure~\ref{fig:Colour_crossing} can be resolved by switching \textit{particle
2} to the left.
We can write down the colour connections by following the colour flow from
\textit{gluon a} to \textit{gluon b} and back to \textit{gluon a}, e.g.
figure~\ref{fig:Colour_gleft} corresponds to $a123ba$. In such an expression any
valid, non-crossing colour flow will connect all external legs while respecting
the rapidity ordering. Thus configurations like the left of
figure~\ref{fig:Colour_crossing} are allowed ($a134b2a$), but the right of the
same figures breaks the rapidity ordering between 2 and 3 ($a1324ba$). Note that
connections between $b$ and $a$ are in inverse order, e.g. $ab321a$ corresponds to~\ref{fig:Colour_gright} ($a123ba$) just with colour and anti-colour swapped.
\begin{figure}
\centering
\subcaptionbox{$a123ba$\label{fig:Colour_gright}}{
\includegraphics[height=0.25\textwidth]{figures/colour_gright.jpg}}
\subcaptionbox{$a13b2a$\label{fig:Colour_gleft}}{
\includegraphics[height=0.25\textwidth]{figures/colour_gleft.jpg}}
\subcaptionbox{$a\_123ba$\label{fig:Colour_qx}}{
\includegraphics[height=0.25\textwidth]{figures/colour_qx.jpg}}
\subcaptionbox{$a\_23b1a$\label{fig:Colour_uno}}{
\includegraphics[height=0.25\textwidth]{figures/colour_uno.jpg}}
\subcaptionbox{$a14b3\_2a$\label{fig:Colour_qqx}}{
\includegraphics[height=0.25\textwidth]{figures/colour_centralqqx.jpg}}
\caption{Different colour non-crossing colour connections. Both incoming
particles are drawn at the top or bottom and the outgoing left or right.
The Feynman diagram is shown in black and the colour flow in blue.}
%TODO Maybe make these plots nicer (in Latex/asy)
\end{figure}
If we replace two gluons with a quark, (anti-)quark pair we break one of the
colour connections. Still the basic concept from before holds, we just have to
treat the connection between two (anti-)quarks like an unmovable (anti-)colour.
We denote such a connection by a underscore (e.g. $1\_a$). For example the
equivalent of~\ref{fig:Colour_gright} ($a123ba$) with an incoming antiquark
is~\ref{fig:Colour_qx} ($a\_123ba$). As said this also holds for other
subleading configurations like unordered emission~\ref{fig:Colour_uno} or
central quark-antiquark pairs~\ref{fig:Colour_qqx} \footnote{Obviously this can
not be guaranteed for non-\HEJ configurations, e.g. $qQ\to Qq$ requires a
$u$-channel exchange.}.
Some rapidity ordering can have multiple possible colour connections,
e.g.~\ref{fig:Colour_gright} and~\ref{fig:Colour_gleft}. This is always the case
if a gluon radiates off a gluon line. In that case we randomly connect the gluon
to either the colour or anti-colour. Thus in the generation we keep track
whether we are on a quark or gluon line, and act accordingly.
\subsection{The matrix element }
\label{sec:ME}
The derivation of the \HEJ matrix element is explained in some detail
in~\cite{Andersen:2017kfc}, where also results for leading and
subleading matrix elements for pure multijet production and production
of a Higgs boson with at least two associated jets are listed. Matrix
elements for $W$ and $Z/\gamma^*$ production together with jets are
given in~\cite{Andersen:2012gk,Andersen:2016vkp}, but not yet included.
The matrix elements are implemented in the \lstinline!MatrixElement!
class. To discuss the structure, let us consider the squared matrix
element for FKL multijet production with $n$ final-state partons:
\begin{align}
\label{eq:ME}
\begin{split}
\overline{\left|\mathcal{M}_\text{HEJ}^{f_1 f_2 \to f_1
g\cdots g f_2}\right|}^2 = \ &\frac {(4\pi\alpha_s)^n} {4\ (N_c^2-1)}
\cdot\ \textcolor{blue}{\frac {K_{f_1}(p_1^-, p_a^-)} {t_1}\ \cdot\ \frac{K_{f_2}(p_n^+, p_b^+)}{t_{n-1}}\ \cdot\ \left\|S_{f_1 f_2\to f_1 f_2}\right\|^2}\\
& \cdot \prod_{i=1}^{n-2} \textcolor{gray}{\left( \frac{-C_A}{t_it_{i+1}}\
V^\mu(q_i,q_{i+1})V_\mu(q_i,q_{i+1}) \right)}\\
& \cdot \prod_{j=1}^{n-1} \textcolor{red}{\exp\left[\omega^0(q_{j\perp})(y_{j+1}-y_j)\right]}.
\end{split}
\end{align}
The structure and momentum assignment of the unsquared matrix element is
as illustrated here:
\begin{center}
\includegraphics{HEJ_amplitude}
\end{center}
The square
of the complete matrix element as given in eq.~\eqref{eq:ME} is
calculated by \lstinline!MatrixElement::operator()!. The \textcolor{red}{last line} of
eq.~\eqref{eq:ME} constitutes the all-order virtual correction,
implemented in
\lstinline!MatrixElement::virtual_corrections!.
$\omega^0$ is the
\textit{regularised Regge trajectory}
\begin{equation}
\label{eq:omega_0}
\omega^0(q_\perp) = - C_A \frac{\alpha_s}{\pi} \log \left(\frac{q_\perp^2}{\lambda^2}\right)\,,
\end{equation}
where $\lambda$ is the slicing parameter limiting the softness of real
gluon emissions, cf. eq.~\eqref{eq:resumdijetFKLmatched2}. $\lambda$ can be
changed at runtime by setting \lstinline!regulator parameter! in
\lstinline!conifg.yml!.
The remaining parts, which correspond to the square of the leading-order
HEJ matrix element $\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}$, are computed in
\lstinline!MatrixElement::tree!. We can further factor off the
scale-dependent ``parametric'' part
\lstinline!MatrixElement::tree_param! containing all factors of the
strong coupling $4\pi\alpha_s$. Using this function saves some CPU time
when adjusting the renormalisation scale, see
section~\ref{sec:resum}. The remaining ``kinematic'' factors are
calculated in \lstinline!MatrixElement::kin!.
\subsubsection{Matrix elements for Higgs plus dijet}
\label{sec:ME_h_jets}
In the production of a Higgs boson together with jets the parametric
parts and the virtual corrections only require minor changes in the
respective functions. However, in the ``kinematic'' parts we have to
distinguish between several cases, which is done in
\lstinline!MatrixElement::tree_kin_Higgs!. The Higgs boson can be
\emph{central}, i.e. inside the rapidity range spanned by the extremal
partons (\lstinline!MatrixElement::tree_kin_Higgs_central!) or
\emph{peripheral} and outside this range
(\lstinline!MatrixElement::tree_kin_Higgs_first! or
\lstinline!MatrixElement::tree_kin_Higgs_last!). In case there is also
an unordered gluon emission the matrix element is already suppressed by one
logarithm $\log(s/t)$. Therefore at NLL the Higgs boson can only be emitted
centrally\footnote{In principle emitting a Higgs boson \textit{on the other
side} of the unordered gluon is possible by contracting an unordered and
external Higgs current. Obviously this would not cover all possible
configurations, e.g. $qQ\to HgqQ$ requires contraction of the standard $Q\to Q$
current with an (unknown) $q\to Hgq$ one.}.
If a Higgs boson with momentum $p_H$ is emitted centrally, after parton
$j$ in rapidity, the matrix element reads
\begin{equation}
\label{eq:ME_h_jets_central}
\begin{split}
\overline{\left|\mathcal{M}_\text{HEJ}^{f_1 f_2 \to f_1 g\cdot H
\cdot g f_2}\right|}^2 = \ &\frac {\alpha_s^2 (4\pi\alpha_s)^n} {4\ (N_c^2-1)}
\cdot\ \textcolor{blue}{\frac {K_{f_1}(p_1^-, p_a^-)} {t_1}\
\cdot\ \frac{1}{t_j t_{j+1}} \cdot\ \frac{K_{f_2}(p_n^+, p_b^+)}{t_{n}}\ \cdot\ \left\|S_{f_1
f_2\to f_1 H f_2}\right\|^2}\\
& \cdot \prod_{\substack{i=1\\i \neq j}}^{n-1} \textcolor{gray}{\left( \frac{-C_A}{t_it_{i+1}}\
V^\mu(q_i,q_{i+1})V_\mu(q_i,q_{i+1}) \right)}\\
& \cdot \textcolor{red}{\prod_{i=1}^{n-1}
\exp\left[\omega^0(q_{i\perp})\Delta y_i\right]}
\end{split}
\end{equation}
with the momentum definitions
\begin{center}
\includegraphics{HEJ_central_Higgs_amplitude}
\end{center}
$q_i$ is the $i$th $t$-channel momentum and $\Delta y_i$ the rapidity
gap between outgoing \emph{particles} (not partons) $i$ and $i+1$ in
rapidity ordering.
For \emph{peripheral} emission in the backward direction
(\lstinline!MatrixElement::tree_kin_Higgs_first!) we first check whether
the most backward parton is a gluon or an (anti-)quark. In the latter
case the leading contribution to the matrix element arises through
emission off the $t$-channel gluons and we can use the same formula
eq.~(\ref{eq:ME_h_jets_central}) as for central emission. If the most
backward parton is a gluon, the square of the matrix element can be
written as
\begin{equation}
\label{eq:ME_h_jets_peripheral}
\begin{split}
\overline{\left|\mathcal{M}_\text{HEJ}^{g f_2 \to H g\cdot g f_2}\right|}^2 = \ &\frac {\alpha_s^2 (4\pi\alpha_s)^n} {\textcolor{blue}{4\ (N_c^2-1)}}
\textcolor{blue}{\cdot\ K_{H}\
\frac{K_{f_2}(p_n^+, p_b^+)}{t_{n-1}}\ \cdot\ \left\|S_{g
f_2\to H g f_2}\right\|^2}\\
& \cdot \prod_{\substack{i=1}}^{n-2} \textcolor{gray}{\left( \frac{-C_A}{t_it_{i+1}}\
V^\mu(q_i,q_{i+1})V_\mu(q_i,q_{i+1}) \right)}\\
& \cdot \textcolor{red}{\prod_{i=1}^{n-1}
\exp\left[\omega^0(q_{i\perp}) (y_{i+1} - y_i)\right]}
\end{split}
\end{equation}
with the momenta as follows:
\begin{center}
\includegraphics{HEJ_peripheral_Higgs_amplitude}
\end{center}
The \textcolor{blue}{blue part} is implemented in
\lstinline!MatrixElement::MH2_forwardH!. All other building blocks are
already available.\todo{Impact factors} The actual current contraction
is calculated in \lstinline!MH2gq_outsideH! inside
\lstinline!currents.cc!, which corresponds to $\tfrac{16 \pi^2}{t_1} \left\|S_{g
f_2\to H g f_2}\right\|^2$.\todo{Fix this insane normalisation}
The forward emission of a Higgs boson is completely analogous. We can
use the same function \lstinline!MatrixElement::MH2_forwardH!, swapping
$p_1 \leftrightarrow p_n,\,p_a \leftrightarrow p_b$.
\subsubsection{FKL ladder and Lipatov vertices}
\label{sec:FKL_ladder}
The ``FKL ladder'' is the product
\begin{equation}
\label{eq:FKL_ladder}
\prod_{i=1}^{n-2} \left( \frac{-C_A}{t_it_{i+1}}\
V^\mu(q_i,q_{i+1})V_\mu(q_i,q_{i+1}) \right)
\end{equation}
appearing in the square of the matrix element for $n$ parton production,
cf. eq.~(\ref{eq:ME}), and implemented in
\lstinline!MatrixElement::FKL_ladder_weight!. The Lipatov vertex contraction
$V^\mu(q_i,q_{i+1})V_\mu(q_i,q_{i+1})$ is implemented \lstinline!C2Lipatovots!.
It is given by \todo{equation} \todo{mention difference between the two versions
of \lstinline!C2Lipatovots!, maybe even get rid of one}.
\subsubsection{Currents}
\label{sec:currents}
The current factors $\frac{K_{f_1}K_{f_2}}{t_1 t_{n-1}}\left\|S_{f_1
f_2\to f_1 f_2}\right\|^2$ and their extensions for unordered and Higgs
boson emissions are implemented in the \lstinline!jM2!$\dots$ functions
of \texttt{src/currents.cc}. \todo{Only $\|S\|^2$ should be in currents}
\footnote{The current implementation for
Higgs production in \texttt{src/currents.cc} includes the $1/4$ factor
inside $S$, opposing to~\eqref{eq:ME}. Thus the overall normalisation is
unaffected.} The ``colour acceleration multiplier'' (CAM) $K_{f}$
for a parton $f\in\{g,q,\bar{q}\}$ is defined as
\begin{align}
\label{eq:K_g}
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}\\
\label{eq:K_q}
K_q(p_1^-, p_a^-) ={}&K_{\bar{q}}(p_1^-, p_a^-) = C_F\,.
\end{align}
The Higgs current CAM used in eq.~(\ref{eq:ME_h_jets_peripheral}) is
\begin{equation}
\label{eq:K_H}
K_H = C_A\,.
\end{equation}
The current contractions are given by\todo{check all this
carefully!}
\begin{align}
\label{eq:S}
\left\|S_{f_1 f_2\to f_1 f_2}\right\|^2 ={}& \sum_{\substack{\lambda_a =
+,-\\\lambda_b = +,-}} \left|j^{\lambda_a}_\mu(p_1, p_a)\
j^{\lambda_b\,\mu}(p_n, p_b)\right|^2 = 2\sum_{\lambda =
+,-} \left|j^{-}_\mu(p_1, p_a)\ j^{\lambda\,\mu}(p_n, p_b)\right|^2\,,\\
\left\|S_{f_1 f_2\to f_1 H f_2}\right\|^2 ={}& \sum_{\substack{\lambda_a =
+,-\\\lambda_b = +,-}} \left|j^{\lambda_a}_\mu(p_1, p_a)V_H^{\mu\nu}(q_j, q_{j+1})\
j^{\lambda_b}_\nu(p_n, p_b)\right|^2\,,\\
\left\|S_{g f_2 \to H g f_2}\right\|^2 ={}& \sum_{
\substack{
\lambda_{a} = +,-\\
\lambda_{1} =+,-\\
\lambda_{b} = +,-
}}
\left|j^{\lambda_a\lambda_1}_{H\,\mu}(p_1, p_a, p_H)\ j^{\lambda_b\,\mu}(p_n, p_b)\right|^2\,.
\end{align}
The ``basic'' currents $j$ are independent of the parton flavour and read
\begin{equation}
\label{eq:j}
j^\pm_\mu(p, q) = u^{\pm,\dagger}(p)\ \sigma^\pm_\mu\ u^{\pm}(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}
The two-component chiral spinors are given by
\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$.
Explicitly, the 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)^*
\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}
\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 \lstinline!HEJ::Analysis! and a
\lstinline!HEJ::CombinedEventWriter!.
\subsection{Event generation}
\label{sec:evgen}
Event generation is performed by the
\lstinline!EventGenerator::gen_event! member function. We begin by generating a
\lstinline!PhaseSpacePoint!. This is not to be confused with
the resummation phase space points represented by
\lstinline!HEJ::PhaseSpacePoint!! After jet clustering, we compute the
leading-order matrix element (see section~\ref{sec:ME}) and pdf factors.
The phase space point generation is performed in the
\lstinline!PhaseSpacePoint! constructor. We first construct the
user-defined number of $n_p$ partons (by default gluons) in
\lstinline!PhaseSpacePoint::gen_LO_partons!. We use flat sampling in
rapidity and azimuthal angle. For the scalar transverse momenta, we
distinguish between two cases. By default, they are generated based on a
random variable $x_{p_\perp}$ according to
\begin{equation}
\label{eq:pt_sampling}
p_\perp = p_{\perp,\text{min}} +
\begin{cases}
p_{\perp,\text{par}}
\tan\left(
x_{p_\perp}
\arctan\left(
\frac{p_{\perp,\text{max}} - p_{\perp,\text{min}}}{p_{\perp,\text{par}}}
\right)
\right)
& y < y_\text{cut}
\\
- \tilde{p}_{\perp,\text{par}}\log\left(1 - x_{p_\perp}\left[1 -
\exp\left(\frac{p_{\perp,\text{min}} -
p_{\perp,\text{max}}}{\tilde{p}_{\perp,\text{par}}}\right)\right]\right)
& y \geq y_\text{cut}
\end{cases}\,,
\end{equation}
where $p_{\perp,\text{min}}$ is the minimum jet transverse momentum,
$p_{\perp,\text{max}}$ is the maximum transverse parton momentum,
tentatively set to the beam energy, and $y_\text{cut}$, $p_{\perp,\text{par}}$
and $\tilde{p}_{\perp,\text{par}}$ are generation parameters set to
heuristically determined values of
\begin{align}
y_\text{cut}&=3,\\
p_{\perp,\text{par}}&=p_{\perp,\min}+\frac{n_p}{5}, \\
\tilde{p}_{\perp,\text{par}}&=\frac{p_{\perp,\text{par}}}{1 +
5(y-y_\text{cut})}.
\end{align}
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.
\subsection{Unweighting}
\label{sec:unweight}
Straightforward event generation tends to produce many events with small
weights. Those events have a negligible contribution to the final
observables, but can take up considerable storage space and CPU time in
later processing stages. This problem can be addressed by unweighting.
For naive unweighting, one would determine the maximum weight
$w_\text{max}$ of all events, discard each event with weight $w$ with a
probability $p=w/w_\text{max}$, and set the weights of all remaining
events to $w_\text{max}$. The downside to this procedure is that it also
eliminates a sizeable fraction of events with moderate weight, so that
the statistical convergence deteriorates.
To ameliorate this problem, we perform unweighting only for events with
sufficiently small weights. This is done by the
\lstinline!Unweighter! class. In the constructor we estimate the
mean and width of the weight-weight distribution from a sample of
events. We use these estimates to determine the maximum weight below
which unweighting is performed. The actual unweighting is the done in
the \lstinline!Unweighter::unweight! function.
\input{currents}
\appendix
\section{Continuous Integration}
\label{sec:gitlabCI}
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/}.
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 dependences, 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
\textit{build} contains the jobs \lstinline!build:basic!,
\lstinline!build:qcdloop!, \lstinline!build:rivet!, etc., which compile \HEJ for
different environments and dependences, by using different in the Docker images.
Jobs staring 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 one stage and the next 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.
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.
\bibliographystyle{JHEP}
\bibliography{biblio}
\end{document}
diff --git a/doc/sphinx/HEJ.rst b/doc/sphinx/HEJ.rst
index d130a0a..1365039 100644
--- a/doc/sphinx/HEJ.rst
+++ b/doc/sphinx/HEJ.rst
@@ -1,302 +1,304 @@
.. _`Running HEJ 2`:
Running HEJ 2
=============
Quick start
-----------
In order to run HEJ 2, you need a configuration file and a file
containing fixed-order events. A sample configuration is given by the
-:file:`config.yml` file distributed together with HEJ 2. Events
-in the Les Houches Event File format can be generated with standard
-Monte Carlo generators like `MadGraph5_aMC@NLO
-<https://launchpad.net/mg5amcnlo>`_ or `Sherpa
-<https://sherpa.hepforge.org/trac/wiki>`_. HEJ 2 assumes that the
-cross section is given by the sum of the event weights. Depending on the
-fixed-order generator it may be necessary to adjust the weights in the
-Les Houches Event File accordingly.
+:file:`config.yml` file distributed together with HEJ 2. Events in the
+Les Houches Event File format can be generated with standard Monte Carlo
+generators like `MadGraph5_aMC@NLO <https://launchpad.net/mg5amcnlo>`_
+or `Sherpa <https://sherpa.hepforge.org/trac/wiki>`_. If HEJ 2 was
+compiled with `HDF5 <https://www.hdfgroup.org/>`_ support, it can also
+read event files in the format suggested in
+`arXiv:1905.05120 <https://arxiv.org/abs/1905.05120>`_.
+HEJ 2 assumes that the cross section is given by the sum of the event
+weights. Depending on the fixed-order generator it may be necessary to
+adjust the weights in the Les Houches Event File accordingly.
The processes supported by HEJ 2 are
- Pure multijet production
- Production of a Higgs boson with jets
..
- *TODO* Production of a W boson with jets
- *TODO* Production of a Z boson or photon with jets
where at least two jets are required in each case. For the time being,
only leading-order events are supported.
After generating an event file :file:`events.lhe` adjust the parameters
under the `fixed order jets`_ setting in :file:`config.yml` to the
settings in the fixed-order generation. Resummation can then be added by
running::
HEJ config.yml events.lhe
Using the default settings, this will produce an output event file
:file:`HEJ.lhe` with events including high-energy resummation.
When using the `Docker image <https://hub.docker.com/r/hejdock/hej>`_,
HEJ can be run with
.. code-block:: bash
docker run -v $PWD:$PWD -w $PWD hejdock/hej HEJ config.yml events.lhe
.. _`HEJ 2 settings`:
Settings
--------
HEJ 2 configuration files follow the `YAML <http://yaml.org/>`_
format. The following configuration parameters are supported:
.. _`trials`:
**trials**
High-energy resummation is performed by generating a number of
resummation phase space configurations corresponding to an input
fixed-order event. This parameter specifies how many such
configurations HEJ 2 should try to generate for each input
event. Typical values vary between 10 and 100.
.. _`min extparton pt`:
**min extparton pt**
Specifies the minimum transverse momentum in GeV of the most forward
and the most backward parton. This setting is needed to regulate an
otherwise uncancelled divergence. Its value should be slightly below
the minimum transverse momentum of jets specified by `resummation
jets: min pt`_. See also the `max ext soft pt fraction`_ setting.
.. _`max ext soft pt fraction`:
**max ext soft pt fraction**
Specifies the maximum fraction that soft radiation can contribute to
the transverse momentum of each the most forward and the most backward
jet. Values between around 0.05 and 0.1 are recommended. See also the
`min extparton pt`_ setting.
.. _`fixed order jets`:
**fixed order jets**
This tag collects a number of settings specifying the jet definition
in the event input. The settings should correspond to the ones used in
the fixed-order Monte Carlo that generated the input events.
.. _`fixed order jets: min pt`:
**min pt**
Minimum transverse momentum in GeV of fixed-order jets.
.. _`fixed order jets: algorithm`:
**algorithm**
The algorithm used to define jets. Allowed settings are
:code:`kt`, :code:`cambridge`, :code:`antikt`,
:code:`cambridge for passive`. See the `FastJet
<http://fastjet.fr/>`_ documentation for a description of these
algorithms.
.. _`fixed order jets: R`:
**R**
The R parameter used in the jet algorithm, roughly corresponding
to the jet radius in the plane spanned by the rapidity and the
azimuthal angle.
.. _`resummation jets`:
**resummation jets**
This tag collects a number of settings specifying the jet definition
in the observed, i.e. resummed events. These settings are optional, by
default the same values as for the `fixed order jets`_ are assumed.
.. _`resummation jets: min pt`:
**min pt**
Minimum transverse momentum in GeV of resummation jets. This
should be between 25% and 50% larger than the minimum transverse
momentum of fixed order jets set by `fixed order jets: min pt`_.
.. _`resummation jets: algorithm`:
**algorithm**
The algorithm used to define jets. The HEJ 2 approach to
resummation relies on properties of :code:`antikt` jets, so this
value is strongly recommended. For a list of possible other
values, see the `fixed order jets: algorithm`_ setting.
.. _`resummation jets: R`:
**R**
The R parameter used in the jet algorithm.
.. _`FKL`:
**FKL**
Specifies how to treat events respecting FKL rapidity ordering. These
configurations are dominant in the high-energy limit. The possible
values are :code:`reweight` to enable resummation, :code:`keep` to
keep the events as they are up to a possible change of
renormalisation and factorisation scale, and :code:`discard` to
discard these events.
.. _`unordered`:
**unordered**
Specifies how to treat events with one emission that does not respect
FKL ordering. In the high-energy limit, such configurations are
logarithmically suppressed compared to FKL configurations. The
possible values are the same as for the `FKL`_ setting, but
:code:`reweight` is currently only supported for Higgs boson plus
jets production.
.. _`non-HEJ`:
**non-HEJ**
Specifies how to treat events where no resummation is possible. The
allowed values are :code:`keep` to keep the events as they are up to
a possible change of renormalisation and factorisation scale and
:code:`discard` to discard these events.
.. _`scales`:
**scales**
Specifies the renormalisation and factorisation scales for the output
events. This can either be a single entry or a list :code:`[scale1,
scale2, ...]`. For the case of a list the first entry defines the
central scale. Possible values are fixed numbers to set the scale in
GeV or the following:
- :code:`H_T`: The sum of the scalar transverse momenta of all
final-state particles
- :code:`max jet pperp`: The maximum transverse momentum of all jets
- :code:`jet invariant mass`: Sum of the invariant masses of all jets
- :code:`m_j1j2`: Invariant mass between the two hardest jets.
Scales can be multiplied or divided by an overall factor,
e.g. :code:`H_T/2`.
It is also possible to import scales from an external library, see
:ref:`Custom scales`
.. _`scale factors`:
**scale factors**
A list of numeric factors by which each of the `scales`_ should be
multiplied. Renormalisation and factorisation scales are varied
independently. For example, a list with entries :code:`[0.5, 2]`
would give the four scale choices (0.5μ\ :sub:`r`, 0.5μ\ :sub:`f`);
(0.5μ\ :sub:`r`, 2μ\ :sub:`f`); (2μ\ :sub:`r`, 0.5μ\ :sub:`f`); (2μ\
:sub:`r`, 2μ\ :sub:`f`) in this order. The ordering corresponds to
the order of the final event weights.
.. _`max scale ratio`:
**max scale ratio**
Specifies the maximum factor by which renormalisation and
factorisation scales may difer. For a value of :code:`2` and the
example given for the `scale factors`_ the scale choices
(0.5μ\ :sub:`r`, 2μ\ :sub:`f`) and (2μ\ :sub:`r`, 0.5μ\ :sub:`f`)
will be discarded.
.. _`log correction`:
**log correction**
Whether to include corrections due to the evolution of the strong
coupling constant in the virtual corrections. Allowed values are
:code:`true` and :code:`false`.
.. _`event output`:
**event output**
Specifies the name of a single event output file or a list of such
files. The file format is either specified explicitly or derived from
the suffix. For example, :code:`events.lhe` or, equivalently
:code:`Les Houches: events.lhe` generates an output event file
:code:`events.lhe` in the Les Houches format. The supported formats
are
- :code:`file.lhe` or :code:`Les Houches: file`: The Les Houches
event file format.
- :code:`file.hepmc` or :code:`HepMC: file`: The HepMC format.
.. _`random generator`:
**random generator**
Sets parameters for random number generation.
.. _`random generator: name`:
**name**
Which random number generator to use. Currently, :code:`mixmax`
and :code:`ranlux64` are supported. Mixmax is recommended. See
the `CLHEP documentation
<http://proj-clhep.web.cern.ch/proj-clhep/index.html#docu>`_ for
details on the generators.
.. _`random generator: seed`:
**seed**
The seed for random generation. This should be a single number for
:code:`mixmax` and the name of a state file for :code:`ranlux64`.
.. _`analysis`:
**analysis**
Name and Setting for the event analyses; either a custom
analysis plugin or Rivet. For the first the :code:`plugin` sub-entry
should be set to the analysis file path. All further entries are passed on
to the analysis. To use Rivet a list of Rivet analyses have to be
given in :code:`rivet` and prefix for the yoda file has to be set
through :code:`output`. See :ref:`Writing custom analyses` for details.
.. _`Higgs coupling`:
**Higgs coupling**
This collects a number of settings concerning the effective coupling
of the Higgs boson to gluons. This is only relevant for the
production process of a Higgs boson with jets and only supported if
HEJ 2 was compiled with `QCDLoop
<https://github.com/scarrazza/qcdloop>`_ support.
.. _`Higgs coupling: use impact factors`:
**use impact factors**
Whether to use impact factors for the coupling to the most forward
and most backward partons. Impact factors imply the infinite
top-quark mass limit.
.. _`Higgs coupling: mt`:
**mt**
The value of the top-quark mass in GeV. If this is not specified,
the limit of an infinite mass is taken.
.. _`Higgs coupling: include bottom`:
**include bottom**
Whether to include the Higgs coupling to bottom quarks.
.. _`Higgs coupling: mb`:
**mb**
The value of the bottom-quark mass in GeV. Only used for the Higgs
coupling, external bottom-quarks are always assumed to be massless.
Advanced Settings
~~~~~~~~~~~~~~~~~
All of the following settings are optional. Please **do not set** any of the
following options, unless you know exactly what you are doing. The default
behaviour gives the most reliable results for a wide range of observables.
.. _`regulator parameter`:
**regulator parameter**
Slicing parameter to regularise the subtraction term, see :math:`\lambda`
in `arxiv:1706.01002 <https://arxiv.org/abs/1706.01002>`_. Default is 0.2
diff --git a/doc/sphinx/installation.rst b/doc/sphinx/installation.rst
index 79398c5..92a71b3 100644
--- a/doc/sphinx/installation.rst
+++ b/doc/sphinx/installation.rst
@@ -1,84 +1,91 @@
.. _Installation:
Installation
============
Download
--------
A tar archive of the HEJ 2 source code can be downloaded and
decompressed with the command::
curl https://hej.web.cern.ch/HEJ/downloads/HEJ_2.0.tar.gz | tar -xz
To obtain the latest stable HEJ version, `HEJ_2.0.tar.gz` should be
replaced by `HEJ.tar.gz`.
Alternatively, the HEJ source code can be obtained by installing the
`git version control system <https://git-scm.com/>`_. and running::
git clone https://phab.hepforge.org/source/hej.git
We also provide a `Docker image <https://hub.docker.com/r/hejdock/hej>`_
containing a HEJ 2 installation on. This image can be pulled with::
docker pull hejdock/hej
When using the Docker image the remaining installation steps can be
skipped.
Prerequisites
-------------
Before installing HEJ 2, you need the following programs and
libraries:
- `CMake <https://cmake.org/>`_ version 3.1
- A compiler supporting the C++14 standard, for example `gcc <https://gcc.gnu.org/>`_ 5 or later
- `FastJet <http://fastjet.fr/>`_
- `CLHEP <https://gitlab.cern.ch/CLHEP/CLHEP>`_
- `LHAPDF <https://lhapdf.hepforge.org/>`_
- The `IOStreams` and `uBLAS` `boost <https://www.boost.org>`_ libraries
- `yaml-cpp <https://github.com/jbeder/yaml-cpp>`_
-If you want to include finite top mass corrections in Higgs boson + jets
-production, you additionally need version 2 of the `QCDLoop
-<https://github.com/scarrazza/qcdloop>`_ library. HEJ 2 supports
-versions 2 and 3 of `HepMC <https://hepmc.web.cern.ch/hepmc/>`_ if it is
-installed, but does not require it. Additional HEJ 2 can be interfaced to
-`rivet <https://rivet.hepforge.org/>`_, which requires HepMC version 2.
+In addition, some optional features have additional dependencies:
+
+- `Version 2 of QCDLoop <https://github.com/scarrazza/qcdloop>`_ is
+ required to include finite top mass corrections in Higgs boson + jets
+ production.
+- `HepMC versions 2 and 3 <https://hepmc.web.cern.ch/hepmc/>`_ enable
+ event output in the respective format.
+- `Rivet <https://rivet.hepforge.org/>`_ together with HepMC 2 allow
+ using Rivet analyses.
+- `HighFive <https://github.com/BlueBrain/HighFive>`_ has to be
+ installed in order to read event files in the
+ `HDF5 <https://www.hdfgroup.org/>`_-based format suggested in
+ `arXiv:1905.05120 <https://arxiv.org/abs/1905.05120>`_.
Compilation
-----------
To compile and install HEJ 2 run::
cmake source/directory -DCMAKE_INSTALL_PREFIX=target/directory
make install
:file:`source/directory` is the directory containing the file
:file:`CMakeLists.txt`. If you omit
:code:`-DCMAKE_INSTALL_PREFIX=target/directory` HEJ 2 will be
installed to some default location.
In case some of the aforementioned prerequisites are not found by
:code:`cmake` you can give a hint by adding an additional argument
:code:`-Dlibname_ROOT_DIR=/directory/with/library`, where
:code:`libname` should be replaced by the name of the library in
question.
To not include specific packages one can add
:code:`-DEXCLUDE_packagename=TRUE` to :code:`cmake`, e.g. by
setting :code:`-DEXCLUDE_rivet=TRUE` HEJ 2 will not be interfaced
to `rivet` even if rivet is available on the system.
Testing
-------
To test your installation, download the NNPDF 2.3 PDF set with::
lhapdf install NNPDF23_nlo_as_0119
and run::
make test
diff --git a/include/HEJ/EventReader.hh b/include/HEJ/EventReader.hh
new file mode 100644
index 0000000..ff920b6
--- /dev/null
+++ b/include/HEJ/EventReader.hh
@@ -0,0 +1,44 @@
+/** \file
+ * \brief Header file for event reader interface
+ *
+ * This header defines an abstract base class for reading events from files.
+ *
+ * \authors Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ * \date 2019
+ * \copyright GPLv2 or later
+ */
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "LHEF/LHEF.h"
+
+namespace HEJ{
+ class EventData;
+
+ // Abstract base class for reading events from files
+ struct EventReader {
+ //! Read an event
+ virtual bool read_event() = 0;
+
+ //! Access header text
+ virtual std::string const & header() const = 0;
+
+ //! Access run information
+ virtual LHEF::HEPRUP const & heprup() const = 0;
+
+ //! Access last read event
+ virtual LHEF::HEPEUP const & hepeup() const = 0;
+
+ virtual ~EventReader() = default;
+ };
+
+ //! Factory function for event readers
+ /**
+ * @param infile The name of the input file
+ * @returns A pointer to an instance of an EventReader
+ * for the input file
+ */
+ std::unique_ptr<EventReader> make_reader(std::string const & filename);
+}
diff --git a/include/HEJ/HDF5Reader.hh b/include/HEJ/HDF5Reader.hh
new file mode 100644
index 0000000..c3eba20
--- /dev/null
+++ b/include/HEJ/HDF5Reader.hh
@@ -0,0 +1,43 @@
+/** \file
+ * \brief Header file for reading events in the HDF5 event format.
+ *
+ * This format is specified in arXiv:1905.05120.
+ *
+ * \authors Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ * \date 2019
+ * \copyright GPLv2 or later
+ */
+#pragma once
+
+#include "HEJ/EventReader.hh"
+
+namespace HEJ{
+
+ //! Class for writing events to a file in the Les Houches Event File format
+ class HDF5Reader : public EventReader{
+ public:
+ //! Contruct object reading from the given file
+ explicit HDF5Reader(std::string const & filename);
+
+ //! Read an event
+ bool read_event() override;
+
+ //! Access header text
+ std::string const & header() const override;
+
+ //! Access run information
+ LHEF::HEPRUP const & heprup() const override;
+
+ //! Access last read event
+ LHEF::HEPEUP const & hepeup() const override;
+
+ private:
+ struct HDF5ReaderImpl;
+ struct HDF5ReaderImplDeleter {
+ void operator()(HDF5ReaderImpl* p);
+ };
+
+ std::unique_ptr<HDF5ReaderImpl, HDF5ReaderImplDeleter> impl_;
+ };
+
+}
diff --git a/include/HEJ/LesHouchesReader.hh b/include/HEJ/LesHouchesReader.hh
new file mode 100644
index 0000000..56656f6
--- /dev/null
+++ b/include/HEJ/LesHouchesReader.hh
@@ -0,0 +1,53 @@
+/** \file
+ * \brief Header file for reading events in the Les Houches Event File format.
+ *
+ * \authors Jeppe Andersen, Marian Heil, Andreas Maier, Jennifer Smillie
+ * \date 2019
+ * \copyright GPLv2 or later
+ */
+#pragma once
+
+#include "LHEF/LHEF.h"
+
+#include "HEJ/Event.hh"
+#include "HEJ/EventReader.hh"
+#include "HEJ/stream.hh"
+
+
+namespace HEJ{
+
+ //! Class for writing events to a file in the Les Houches Event File format
+ class LesHouchesReader : public EventReader{
+ public:
+ //! Contruct object reading from the given file
+ explicit LesHouchesReader(std::string const & filename):
+ stream_{filename},
+ reader_{stream_}
+ {}
+
+ //! Read an event
+ bool read_event() override {
+ return reader_.readEvent();
+ }
+
+ //! Access header text
+ std::string const & header() const override {
+ return reader_.headerBlock;
+ }
+
+ //! Access run information
+ LHEF::HEPRUP const & heprup() const override {
+ return reader_.heprup;
+ }
+
+ //! Access last read event
+ LHEF::HEPEUP const & hepeup() const override {
+ return reader_.hepeup;
+ }
+
+ private:
+ HEJ::istream stream_;
+ LHEF::Reader reader_;
+ };
+
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0b3005c..2bad3ab 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,19 +1,22 @@
file(GLOB src_files *.cc)
add_library(hejlib SHARED ${src_files})
set_target_properties(hejlib PROPERTIES OUTPUT_NAME "HEJ")
set(libraries ${CMAKE_DL_LIBS} ${LHAPDF_LIBRARIES} ${CLHEP_LIBRARIES}
${fastjet_LIBRARIES} ${Boost_LIBRARIES} ${YAML_CPP_LIBRARIES} yaml-cpp)
if(${QCDloop_FOUND})
list(APPEND libraries ${QCDloop_LIBRARIES} quadmath)
endif()
if(${HepMC_FOUND})
list(APPEND libraries ${HepMC_LIBRARIES})
if(${rivet_FOUND})
list(APPEND libraries ${rivet_LIBRARIES})
endif()
endif()
+if(${HighFive_FOUND})
+ list(APPEND libraries $<TARGET_PROPERTY:HighFive,INTERFACE_LINK_LIBRARIES>)
+endif()
target_link_libraries(hejlib ${libraries})
install(TARGETS hejlib DESTINATION ${INSTALL_LIB_DIR})
diff --git a/src/EventReader.cc b/src/EventReader.cc
new file mode 100644
index 0000000..0067f31
--- /dev/null
+++ b/src/EventReader.cc
@@ -0,0 +1,19 @@
+#include "HEJ/EventReader.hh"
+#include "HEJ/HDF5Reader.hh"
+#include "HEJ/LesHouchesReader.hh"
+#include "HEJ/utility.hh"
+
+namespace HEJ {
+ std::unique_ptr<EventReader> make_reader(std::string const & filename) {
+ try {
+ return std::make_unique<LesHouchesReader>(filename);
+ }
+ catch(std::runtime_error&) {
+#if HEJ_BUILD_WITH_HDF5
+ return std::make_unique<HDF5Reader>(filename);
+#else
+ throw;
+#endif
+ }
+ }
+}
diff --git a/src/HDF5Reader.cc b/src/HDF5Reader.cc
new file mode 100644
index 0000000..073739b
--- /dev/null
+++ b/src/HDF5Reader.cc
@@ -0,0 +1,293 @@
+#include "HEJ/HDF5Reader.hh"
+
+#ifdef HEJ_BUILD_WITH_HDF5
+
+#include <numeric>
+#include <iterator>
+
+#include "highfive/H5File.hpp"
+
+namespace HEJ {
+ namespace {
+ // buffer size for reader
+ // each "reading from disk" reads "chunk_size" many event at once
+ constexpr std::size_t chunk_size = 10000;
+
+ struct ParticleData {
+ std::vector<int> id;
+ std::vector<int> status;
+ std::vector<int> mother1;
+ std::vector<int> mother2;
+ std::vector<int> color1;
+ std::vector<int> color2;
+ std::vector<double> px;
+ std::vector<double> py;
+ std::vector<double> pz;
+ std::vector<double> e;
+ std::vector<double> m;
+ std::vector<double> lifetime;
+ std::vector<double> spin;
+ };
+
+ struct EventRecords {
+ std::vector<int> particle_start;
+ std::vector<int> nparticles;
+ std::vector<int> pid;
+ std::vector<int> weight;
+ std::vector<double> scale;
+ std::vector<double> fscale;
+ std::vector<double> rscale;
+ std::vector<double> aqed;
+ std::vector<double> aqcd;
+ ParticleData particles;
+ };
+
+class ConstEventIterator {
+ public:
+ // iterator traits
+ using iterator_category = std::bidirectional_iterator_tag;
+ using value_type = LHEF::HEPEUP;
+ using difference_type = std::ptrdiff_t;
+ using pointer = const LHEF::HEPEUP*;
+ using reference = LHEF::HEPEUP const &;
+
+ using iterator = ConstEventIterator;
+ friend iterator cbegin(EventRecords const & records) noexcept;
+ friend iterator cend(EventRecords const & records) noexcept;
+
+ iterator& operator++() {
+ particle_offset_ += records_.get().nparticles[idx_];
+ ++idx_;
+ return *this;
+ }
+ iterator& operator--() {
+ --idx_;
+ particle_offset_ -= records_.get().nparticles[idx_];
+ return *this;
+ }
+ iterator operator--(int) {
+ auto res = *this;
+ --(*this);
+ return res;
+ }
+ bool operator==(iterator const & other) const {
+ return idx_ == other.idx_;
+ }
+ bool operator!=(iterator other) const {
+ return !(*this == other);
+ }
+ value_type operator*() const {
+ value_type hepeup{};
+ auto const & r = records_.get();
+ hepeup.NUP = r.nparticles[idx_];
+ hepeup.IDPRUP = r.pid[idx_];
+ hepeup.XWGTUP = r.weight[idx_];
+ hepeup.weights.emplace_back(hepeup.XWGTUP, nullptr);
+ hepeup.SCALUP = r.scale[idx_];
+ hepeup.scales.muf = r.fscale[idx_];
+ hepeup.scales.mur = r.rscale[idx_];
+ hepeup.AQEDUP = r.aqed[idx_];
+ hepeup.AQCDUP = r.aqcd[idx_];
+ const size_t start = particle_offset_;
+ const size_t end = start + hepeup.NUP;
+ auto const & p = r.particles;
+ hepeup.IDUP = std::vector<long>( begin(p.id)+start, begin(p.id)+end );
+ hepeup.ISTUP = std::vector<int>( begin(p.status)+start, begin(p.status)+end );
+ hepeup.VTIMUP = std::vector<double>( begin(p.lifetime)+start, begin(p.lifetime)+end );
+ hepeup.SPINUP = std::vector<double>( begin(p.spin)+start, begin(p.spin)+end );
+ hepeup.MOTHUP.resize(hepeup.NUP);
+ hepeup.ICOLUP.resize(hepeup.NUP);
+ hepeup.PUP.resize(hepeup.NUP);
+ for(size_t i = 0; i < hepeup.MOTHUP.size(); ++i) {
+ const size_t idx = start + i;
+ assert(idx < end);
+ hepeup.MOTHUP[i] = std::make_pair(p.mother1[idx], p.mother2[idx]);
+ hepeup.ICOLUP[i] = std::make_pair(p.color1[idx], p.color2[idx]);
+ hepeup.PUP[i] = std::vector<double>{
+ p.px[idx], p.py[idx], p.pz[idx], p.e[idx], p.m[idx]
+ };
+ }
+ return hepeup;
+ }
+
+ private:
+ explicit ConstEventIterator(EventRecords const & records):
+ records_{records} {}
+
+ std::reference_wrapper<const EventRecords> records_;
+ size_t idx_ = 0;
+ size_t particle_offset_ = 0;
+ }; // end ConstEventIterator
+
+ ConstEventIterator cbegin(EventRecords const & records) noexcept {
+ return ConstEventIterator{records};
+ }
+
+ ConstEventIterator cend(EventRecords const & records) noexcept {
+ auto it =ConstEventIterator{records};
+ it.idx_ = records.aqcd.size(); // or size of any other records member
+ return it;
+ }
+
+ } // end anonymous namespace
+
+ struct HDF5Reader::HDF5ReaderImpl{
+ HighFive::File file;
+ std::size_t event_idx;
+ std::size_t particle_idx;
+ std::size_t nevents;
+
+ EventRecords records;
+ ConstEventIterator cur_event;
+
+ LHEF::HEPRUP heprup;
+ LHEF::HEPEUP hepeup;
+
+ explicit HDF5ReaderImpl(std::string const & filename):
+ file{filename},
+ event_idx{0},
+ particle_idx{0},
+ nevents{
+ file.getGroup("event")
+ .getDataSet("nparticles") // or any other dataset
+ .getSpace().getDimensions().front()
+ },
+ records{},
+ cur_event{cbegin(records)},
+ heprup{},
+ hepeup{}
+ {
+ read_heprup();
+ read_event_records(chunk_size);
+ }
+
+ void read_heprup() {
+ const auto init = file.getGroup("init");
+ init.getDataSet( "PDFgroupA" ).read(heprup.PDFGUP.first);
+ init.getDataSet( "PDFgroupB" ).read(heprup.PDFGUP.second);
+ init.getDataSet( "PDFsetA" ).read(heprup.PDFSUP.first);
+ init.getDataSet( "PDFsetB" ).read(heprup.PDFSUP.second);
+ init.getDataSet( "beamA" ).read(heprup.IDBMUP.first);
+ init.getDataSet( "beamB" ).read(heprup.IDBMUP.second);
+ init.getDataSet( "energyA" ).read(heprup.EBMUP.first);
+ init.getDataSet( "energyB" ).read(heprup.EBMUP.second);
+ init.getDataSet( "numProcesses" ).read(heprup.NPRUP);
+ init.getDataSet( "weightingStrategy" ).read(heprup.IDWTUP);
+ const auto proc_info = file.getGroup("procInfo");
+ proc_info.getDataSet( "procId" ).read(heprup.LPRUP);
+ proc_info.getDataSet( "xSection" ).read(heprup.XSECUP);
+ proc_info.getDataSet( "error" ).read(heprup.XERRUP);
+ // TODO: is this identification correct?
+ proc_info.getDataSet( "unitWeight" ).read(heprup.XMAXUP);
+ }
+
+ std::size_t read_event_records(std::size_t count) {
+ count = std::min(count, nevents-event_idx);
+
+ auto events = file.getGroup("event");
+ events.getDataSet("nparticles").select({event_idx}, {count}).read(records.nparticles);
+ assert(records.nparticles.size() == count);
+ events.getDataSet("pid").select( {event_idx}, {count} ).read( records.pid );
+ events.getDataSet("weight").select( {event_idx}, {count} ).read( records.weight );
+ events.getDataSet("scale").select( {event_idx}, {count} ).read( records.scale );
+ events.getDataSet("fscale").select( {event_idx}, {count} ).read( records.fscale );
+ events.getDataSet("rscale").select( {event_idx}, {count} ).read( records.rscale );
+ events.getDataSet("aqed").select( {event_idx}, {count} ).read( records.aqed );
+ events.getDataSet("aqcd").select( {event_idx}, {count} ).read( records.aqcd );
+ const std::size_t particle_count = std::accumulate(
+ begin(records.nparticles), end(records.nparticles), 0
+ );
+ auto pdata = file.getGroup("particle");
+ auto & particles = records.particles;
+ pdata.getDataSet("id").select( {particle_idx}, {particle_count} ).read( particles.id );
+ pdata.getDataSet("status").select( {particle_idx}, {particle_count} ).read( particles.status );
+ pdata.getDataSet("mother1").select( {particle_idx}, {particle_count} ).read( particles.mother1 );
+ pdata.getDataSet("mother2").select( {particle_idx}, {particle_count} ).read( particles.mother2 );
+ pdata.getDataSet("color1").select( {particle_idx}, {particle_count} ).read( particles.color1 );
+ pdata.getDataSet("color2").select( {particle_idx}, {particle_count} ).read( particles.color2 );
+ pdata.getDataSet("px").select( {particle_idx}, {particle_count} ).read( particles.px );
+ pdata.getDataSet("px").select( {particle_idx}, {particle_count} ).read( particles.py );
+ pdata.getDataSet("pz").select( {particle_idx}, {particle_count} ).read( particles.pz );
+ pdata.getDataSet("e").select( {particle_idx}, {particle_count} ).read( particles.e );
+ pdata.getDataSet("m").select( {particle_idx}, {particle_count} ).read( particles.m );
+ pdata.getDataSet("lifetime").select( {particle_idx}, {particle_count} ).read( particles.lifetime );
+ pdata.getDataSet("spin").select( {particle_idx}, {particle_count} ).read( particles.spin );
+
+ event_idx += count;
+ particle_idx += particle_count;
+ return count;
+ }
+ };
+
+ HDF5Reader::HDF5Reader(std::string const & filename):
+ impl_{
+ new HDF5Reader::HDF5ReaderImpl{filename},
+ HDF5Reader::HDF5ReaderImplDeleter{}
+ }
+ {}
+
+ bool HDF5Reader::read_event() {
+ if(impl_->cur_event == cend(impl_->records)) {
+ // end of active chunk, read new events from file
+ const auto events_read = impl_->read_event_records(chunk_size);
+ impl_->cur_event = cbegin(impl_->records);
+ if(events_read == 0) return false;
+ }
+ impl_->hepeup = *impl_->cur_event;
+ ++impl_->cur_event;
+ return true;
+ }
+
+ namespace {
+ static const std::string nothing = "";
+ }
+
+ std::string const & HDF5Reader::header() const {
+ return nothing;
+ }
+
+ LHEF::HEPRUP const & HDF5Reader::heprup() const {
+ return impl_->heprup;
+ }
+
+ LHEF::HEPEUP const & HDF5Reader::hepeup() const {
+ return impl_->hepeup;
+ }
+}
+
+#else // no HDF5 support
+
+namespace HEJ {
+ class HDF5Reader::HDF5ReaderImpl{};
+
+ HDF5Reader::HDF5Reader(std::string const &){
+ throw std::invalid_argument{
+ "Failed to create HDF5 reader: "
+ "HEJ 2 was built without HDF5 support"
+ };
+ }
+
+ bool HDF5Reader::read_event() {
+ throw std::logic_error{"unreachable"};
+ }
+
+ std::string const & HDF5Reader::header() const {
+ throw std::logic_error{"unreachable"};
+ }
+
+ LHEF::HEPRUP const & HDF5Reader::heprup() const {
+ throw std::logic_error{"unreachable"};
+ }
+
+ LHEF::HEPEUP const & HDF5Reader::hepeup() const {
+ throw std::logic_error{"unreachable"};
+ }
+}
+
+#endif
+
+namespace HEJ {
+ void HDF5Reader::HDF5ReaderImplDeleter::operator()(HDF5ReaderImpl* p) {
+ delete p;
+ }
+}
diff --git a/src/LesHouchesWriter.cc b/src/LesHouchesWriter.cc
index 0ae63b4..e9c40cc 100644
--- a/src/LesHouchesWriter.cc
+++ b/src/LesHouchesWriter.cc
@@ -1,95 +1,95 @@
/**
* \authors Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
* \date 2019
* \copyright GPLv2 or later
*/
-#include "HEJ/LesHouchesWriter.hh"
#include <cassert>
#include <utility>
#include <vector>
#include "HEJ/Event.hh"
#include "HEJ/event_types.hh"
+#include "HEJ/LesHouchesWriter.hh"
#include "HEJ/utility.hh"
namespace HEJ{
namespace{
template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... a){
return std::unique_ptr<T>{new T{std::forward<Args>(a)...}};
}
}
LesHouchesWriter::LesHouchesWriter(
std::string const & file, LHEF::HEPRUP heprup
):
out_{file, std::fstream::in | std::fstream::out | std::fstream::trunc},
writer_{HEJ::make_unique<LHEF::Writer>(out_)}
{
if(! out_.is_open()){
throw std::ios_base::failure("Failed to open " + file);
};
writer_->heprup = std::move(heprup);
// lhe Stardard: IDWTUP (negative => weights = +/-)
// 1: weighted events, xs = mean(weight), XMAXUP given
// 2: weighted events, xs = XSECUP, XMAXUP given
// 3: unweighted events, no additional information given
// 4: unweighted events, xs = mean(weight), no additional information given
writer_->heprup.IDWTUP = 2;
// use placeholders for unknown init block values
// we can overwrite them after processing all events
writer_->heprup.XSECUP = std::vector<double>(event_type::last_type+1, 0.);
writer_->heprup.XERRUP = std::vector<double>(event_type::last_type+1, 0.);
writer_->heprup.XMAXUP = std::vector<double>(event_type::last_type+1, 0.);
write_init();
}
void LesHouchesWriter::write(Event const & ev){
assert(writer_ && out_.is_open());
const double wt = ev.central().weight;
writer_->hepeup = HEJ::to_HEPEUP(std::move(ev), &heprup());
writer_->writeEvent();
heprup().XSECUP[ev.type()] += wt;
heprup().XERRUP[ev.type()] += wt*wt;
if(wt > heprup().XMAXUP[ev.type()]){
heprup().XMAXUP[ev.type()] = wt;
}
}
// this function is called after overwritting the Les Houches init block
// assert that we have overwritten *exactly* the init block,
// i.e. we are at the end of the file or an intact event block is next
void assert_next_event_intact(std::istream & out){
(void) out; // suppress compiler warnings if not in debug mode
#ifndef NDEBUG
std::string line;
getline(out, line);
assert(out.eof() || line == "<event>");
#endif
}
void LesHouchesWriter::rewrite_init(){
assert(writer_ && out_.is_open());
// replace placeholder entries
const auto pos = out_.tellp();
out_.seekp(0);
writer_->init();
assert_next_event_intact(out_);
out_.seekp(pos);
}
LesHouchesWriter::~LesHouchesWriter(){
assert(writer_ && out_.is_open());
for(auto & xs_err: heprup().XERRUP)
{
xs_err = sqrt(xs_err);
}
rewrite_init();
}
}
diff --git a/src/bin/HEJ.cc b/src/bin/HEJ.cc
index 77d6f1e..d799cc4 100644
--- a/src/bin/HEJ.cc
+++ b/src/bin/HEJ.cc
@@ -1,189 +1,188 @@
/**
* \authors Jeppe Andersen, Tuomas Hapola, Marian Heil, Andreas Maier, Jennifer Smillie
* \date 2019
* \copyright GPLv2 or later
*/
#include <array>
#include <chrono>
#include <iostream>
#include <limits>
#include <memory>
#include <numeric>
#include "yaml-cpp/yaml.h"
-#include "LHEF/LHEF.h"
-
#include "fastjet/ClusterSequence.hh"
#include "HEJ/CombinedEventWriter.hh"
#include "HEJ/config.hh"
#include "HEJ/CrossSectionAccumulator.hh"
#include "HEJ/Event.hh"
+#include "HEJ/EventReader.hh"
#include "HEJ/EventReweighter.hh"
#include "HEJ/get_analysis.hh"
#include "HEJ/make_RNG.hh"
#include "HEJ/ProgressBar.hh"
#include "HEJ/stream.hh"
#include "HEJ/Version.hh"
#include "HEJ/YAMLreader.hh"
int event_number(std::string const & record){
size_t start = record.rfind("Number of Events");
start = record.find_first_of("123456789", start);
if(start == std::string::npos) {
throw std::invalid_argument("no event number record found");
}
const size_t end = record.find_first_not_of("0123456789", start);
return std::stoi(record.substr(start, end - start));
}
HEJ::Config load_config(char const * filename){
try{
return HEJ::load_config(filename);
}
catch(std::exception const & exc){
std::cerr << "Error: " << exc.what() << '\n';
std::exit(EXIT_FAILURE);
}
}
std::unique_ptr<HEJ::Analysis> get_analysis(
YAML::Node const & parameters
){
try{
return HEJ::get_analysis(parameters);
}
catch(std::exception const & exc){
std::cerr << "Failed to load analysis: " << exc.what() << '\n';
std::exit(EXIT_FAILURE);
}
}
// unique_ptr is a workaround:
// HEJ::optional is a better fit, but gives spurious errors with g++ 7.3.0
std::unique_ptr<HEJ::ProgressBar<double>> make_progress_bar(
std::vector<double> const & xs
) {
if(xs.empty()) return {};
const double Born_xs = std::accumulate(begin(xs), end(xs), 0.);
return std::make_unique<HEJ::ProgressBar<double>>(std::cout, Born_xs);
}
std::string time_to_string(const time_t time){
char s[30];
struct tm * p = localtime(&time);
strftime(s, 30, "%a %b %d %Y %H:%M:%S", p);
return s;
}
int main(int argn, char** argv) {
using clock = std::chrono::system_clock;
if (argn < 3) {
std::cerr << "\n# Usage:\n."<< argv[0] <<" config_file input_file\n\n";
return EXIT_FAILURE;
}
const auto start_time = clock::now();
{
std::cout << "Starting " << HEJ::Version::package_name_full()
<< ", revision " << HEJ::Version::revision() << " ("
<< time_to_string(clock::to_time_t(start_time)) << ")" << std::endl;
}
fastjet::ClusterSequence::print_banner();
// read configuration
const HEJ::Config config = load_config(argv[1]);
-
- HEJ::istream in{argv[2]};
+ auto reader = HEJ::make_reader(argv[2]);
+ assert(reader);
std::unique_ptr<HEJ::Analysis> analysis = get_analysis(
config.analysis_parameters
);
assert(analysis != nullptr);
- LHEF::Reader reader{in};
- auto heprup = reader.heprup;
+ auto heprup = reader->heprup();
heprup.generators.emplace_back(LHEF::XMLTag{});
heprup.generators.back().name = HEJ::Version::package_name();
heprup.generators.back().version = HEJ::Version::String();
HEJ::CombinedEventWriter writer{config.output, std::move(heprup)};
double global_reweight = 1.;
int max_events = std::numeric_limits<int>::max();
if(argn > 3){
max_events = std::stoi(argv[3]);
- const int input_events = event_number(reader.headerBlock);
+ const int input_events = event_number(reader->header());
global_reweight = input_events/static_cast<double>(max_events);
std::cout << "Processing " << max_events
<< " out of " << input_events << " events\n";
}
HEJ::ScaleGenerator scale_gen{
config.scales.base,
config.scales.factors,
config.scales.max_ratio
};
auto ran = HEJ::make_RNG(config.rng.name, config.rng.seed);
assert(ran != nullptr);
HEJ::EventReweighter hej{
- reader.heprup,
+ reader->heprup(),
std::move(scale_gen),
to_EventReweighterConfig(config),
*ran
};
int nevent = 0;
std::array<int, HEJ::event_type::last_type + 1>
nevent_type{0}, nfailed_type{0};
- auto progress = make_progress_bar(reader.heprup.XSECUP);
+ auto progress = make_progress_bar(reader->heprup().XSECUP);
HEJ::CrossSectionAccumulator xs;
// Loop over the events in the inputfile
- while(reader.readEvent()){
+ while(reader->read_event()){
// reweight events so that the total cross section is conserved
- reader.hepeup.setWeight(0, global_reweight * reader.hepeup.weight());
+ auto hepeup = reader->hepeup();
+ hepeup.setWeight(0, global_reweight * hepeup.weight());
if(nevent == max_events) break;
++nevent;
// calculate HEJ weight
HEJ::Event FO_event{
- HEJ::Event::EventData{reader.hepeup}(
+ HEJ::Event::EventData{hepeup}(
config.fixed_order_jets.def, config.fixed_order_jets.min_pt
)
};
auto resummed_events = hej.reweight(FO_event, config.trials);
++nevent_type[FO_event.type()];
if(resummed_events.empty()) ++nfailed_type[FO_event.type()];
for(auto const & ev: resummed_events){
//TODO: move pass_cuts to after phase space point generation
if(analysis->pass_cuts(ev, FO_event)){
analysis->fill(ev, FO_event);
writer.write(ev);
xs.fill(ev);
}
}
if(progress) progress->increment(FO_event.central().weight);
} // main event loop
std::cout << '\n';
analysis->finalise();
using namespace HEJ::event_type;
std::cout<< "Events processed: " << nevent << '\n';
for(size_t ev_type = first_type; ev_type <= last_type; ++ev_type){
std::cout << '\t' << names[ev_type] << ": " << nevent_type[ev_type]
<< ", failed to reconstruct " << nfailed_type[ev_type]
<< '\n';
}
std::cout << '\n' << xs << '\n';
std::chrono::duration<double> run_time = (clock::now() - start_time);
std::cout << "Finished " << HEJ::Version::package_name() << " at "
<< time_to_string(clock::to_time_t(clock::now()))
<< "\n=> Runtime: " << run_time.count() << " sec ("
<< nevent/run_time.count() << " Events/sec).\n";
}
diff --git a/t/Wm9-g4-repack.hdf5 b/t/Wm9-g4-repack.hdf5
new file mode 100644
index 0000000..9376eff
--- /dev/null
+++ b/t/Wm9-g4-repack.hdf5
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11d6b8c9248390545dcaf2dc98fbec56dbedb73e705b7ad6e5b92f3a0b14ee36
+size 24701851
diff --git a/t/test_hdf5.cc b/t/test_hdf5.cc
new file mode 100644
index 0000000..8b14d31
--- /dev/null
+++ b/t/test_hdf5.cc
@@ -0,0 +1,40 @@
+#include <iostream>
+
+#include "HEJ/EventReader.hh"
+
+int main(int argc, char** argv) {
+ if(argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " file.hdf5\n";
+ return EXIT_FAILURE;
+ }
+ auto reader = HEJ::make_reader(argv[1]);
+ if(
+ reader->heprup().EBMUP != std::make_pair(7000., 7000.)
+ || reader->heprup().PDFSUP != std::make_pair(13000, 13000)
+ ) {
+ std::cerr << "Read incorrect init parameters\n";
+ return EXIT_FAILURE;
+ }
+ int nevent = 0;
+ while(reader->read_event()) {
+ ++nevent;
+ if(reader->hepeup().NUP != 13) {
+ std::cerr << "Read wrong number of particles: "
+ << reader->hepeup().NUP << " != 13 in event " << nevent;
+ return EXIT_FAILURE;
+ }
+ for(size_t i = 0; i < 2; ++i) {
+ for(size_t j = 0; j < 2; ++j) {
+ if(reader->hepeup().PUP[i][j] != 0) {
+ std::cerr << "Non-vanishing transverse momentum in incoming particle"
+ " in event " << nevent;
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ }
+ if(nevent != 51200) {
+ std::cerr << "Wrong number of events " << nevent << " != 51200\n";
+ return EXIT_FAILURE;
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 19, 8:59 PM (1 d, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3806187
Default Alt Text
(147 KB)

Event Timeline