diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/__init__.py b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/__init__.py index 3123240fb..3c383aca1 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/__init__.py +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) 2020-2024 CERN and UCLouvain. # Licensed under the GNU Lesser General Public License (version 3 or later). # Created by: O. Mattelaer (Sep 2021) for the MG5aMC CUDACPP plugin. -# Further modified by: O. Mattelaer, A. Valassi (2021-2024) for the MG5aMC CUDACPP plugin. +# Further modified by: O. Mattelaer, A. Valassi, Z. Wettersten (2021-2024) for the MG5aMC CUDACPP plugin. # AV - Rename the plugin as CUDACPP_OUTPUT (even if the madgraph4gpu directory is still called CUDACPP_SA_OUTPUT) # This can be used in mg5amcnlo in one of two ways: @@ -36,15 +36,19 @@ ###import PLUGIN.CUDACPP_OUTPUT.output as output # AV modify this to also allow MG5aMC_PLUGIN __import__('%s.output'%PLUGIN_NAME) output = sys.modules['%s.output'%PLUGIN_NAME] + __import__('%s.trex'%PLUGIN_NAME) + trex = sys.modules['%s.trex'%PLUGIN_NAME] new_output = { 'madevent_simd' : output.SIMD_ProcessExporter, 'madevent_gpu' : output.GPU_ProcessExporter, 'standalone_cudacpp' : output.PLUGIN_ProcessExporter, + 'standalone_trex' : trex.TREX_ProcessExporter, # the following one are used for the second exporter class # (not really needed so far but interesting if need # specialization in the futur) 'standalone_simd' : output.SIMD_ProcessExporter, 'standalone_cuda' : output.GPU_ProcessExporter, } + new_reweight = {'trex': trex.TREX_ReweightInterface} # 2. Define new way to handle the cluster. # Example: new_cluster = {'mycluster': MYCLUSTERCLASS} diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/REX b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/REX new file mode 120000 index 000000000..1a916a1ca --- /dev/null +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/REX @@ -0,0 +1 @@ +../../../../../../../../tools/REX/ \ No newline at end of file diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/Bridge.h b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/Bridge.h index 87aa648dd..6f18ec1d6 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/Bridge.h +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/Bridge.h @@ -1,7 +1,7 @@ // Copyright (C) 2020-2024 CERN and UCLouvain. // Licensed under the GNU Lesser General Public License (version 3 or later). // Created by: S. Roiser (Nov 2021) for the MG5aMC CUDACPP plugin. -// Further modified by: S. Roiser, J. Teig, A. Valassi (2021-2024) for the MG5aMC CUDACPP plugin. +// Further modified by: S. Roiser, J. Teig, A. Valassi, Z. Wettersten (2021-2024) for the MG5aMC CUDACPP plugin. #ifndef BRIDGE_H #define BRIDGE_H 1 @@ -255,18 +255,22 @@ namespace mg5amcCpu throw std::logic_error( "Bridge constructor: FIXME! cannot choose gputhreads" ); // this should never happen! m_gpublocks = m_nevt / m_gputhreads; } +#ifdef MGONGPU_VERBOSE_BRIDGE std::cout << "WARNING! Instantiate device Bridge (nevt=" << m_nevt << ", gpublocks=" << m_gpublocks << ", gputhreads=" << m_gputhreads << ", gpublocks*gputhreads=" << m_gpublocks * m_gputhreads << ")" << std::endl; +#endif m_pmek.reset( new MatrixElementKernelDevice( m_devMomentaC, m_devGs, m_devRndHel, m_devRndCol, m_devChannelIds, m_devMEs, m_devSelHel, m_devSelCol, m_gpublocks, m_gputhreads ) ); #else +#ifdef MGONGPU_VERBOSE_BRIDGE std::cout << "WARNING! Instantiate host Bridge (nevt=" << m_nevt << ")" << std::endl; +#endif m_pmek.reset( new MatrixElementKernelHost( m_hstMomentaC, m_hstGs, m_hstRndHel, m_hstRndCol, m_hstChannelIds, m_hstMEs, m_hstSelHel, m_hstSelCol, m_nevt ) ); #endif // MGONGPUCPP_GPUIMPL // Create a process object, read param card and set parameters // FIXME: the process instance can happily go out of scope because it is only needed to read parameters? // FIXME: the CPPProcess should really be a singleton? what if fbridgecreate is called from several Fortran threads? CPPProcess process( /*verbose=*/false ); - std::string paramCard = "../../Cards/param_card.dat"; + std::string paramCard = "../Cards/param_card.dat"; // ZW: change default param_card.dat location to one dir down /* #ifdef __HIPCC__ if( !std::experimental::filesystem::exists( paramCard ) ) paramCard = "../" + paramCard; @@ -278,7 +282,12 @@ namespace mg5amcCpu //if( !( stat( paramCard.c_str(), &dummyBuffer ) == 0 ) ) paramCard = "../" + paramCard; // auto fileExists = []( std::string& fileName ) { struct stat buffer; return stat( fileName.c_str(), &buffer ) == 0; }; - if( !fileExists( paramCard ) ) paramCard = "../" + paramCard; // bypass std::filesystem #803 + size_t paramCardCheck = 2; // ZW: check for paramCard up to 2 directories up + for( size_t k = 0; k < paramCardCheck; ++k ) + { + if( fileExists( paramCard ) ) break; // bypass std::filesystem #803 + paramCard = "../" + paramCard; + } process.initProc( paramCard ); } @@ -347,7 +356,9 @@ namespace mg5amcCpu if( goodHelOnly ) return; m_pmek->computeMatrixElements( useChannelIds ); copyHostFromDevice( m_hstMEs, m_devMEs ); +#ifdef MGONGPU_VERBOSE_BRIDGE flagAbnormalMEs( m_hstMEs.data(), m_nevt ); +#endif copyHostFromDevice( m_hstSelHel, m_devSelHel ); copyHostFromDevice( m_hstSelCol, m_devSelCol ); if constexpr( std::is_same_v ) @@ -400,7 +411,9 @@ namespace mg5amcCpu } if( goodHelOnly ) return; m_pmek->computeMatrixElements( useChannelIds ); +#ifdef MGONGPU_VERBOSE_BRIDGE flagAbnormalMEs( m_hstMEs.data(), m_nevt ); +#endif if constexpr( std::is_same_v ) { memcpy( mes, m_hstMEs.data(), m_hstMEs.bytes() ); diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/GpuRuntime.h b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/GpuRuntime.h index 860c7fde1..1088c9956 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/GpuRuntime.h +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/GpuRuntime.h @@ -1,7 +1,7 @@ // Copyright (C) 2020-2024 CERN and UCLouvain. // Licensed under the GNU Lesser General Public License (version 3 or later). // Created by: J. Teig (Jun 2023, based on earlier work by S. Roiser) for the MG5aMC CUDACPP plugin. -// Further modified by: O. Mattelaer, S. Roiser, J. Teig, A. Valassi (2020-2024) for the MG5aMC CUDACPP plugin. +// Further modified by: O. Mattelaer, S. Roiser, J. Teig, A. Valassi, Z. Wettersten (2020-2024) for the MG5aMC CUDACPP plugin. #ifndef MG5AMC_GPURUNTIME_H #define MG5AMC_GPURUNTIME_H 1 @@ -38,8 +38,11 @@ namespace mg5amcGpu // *** FIXME! This will all need to be designed differently when going to multi-GPU nodes! *** struct GpuRuntime final { - GpuRuntime( const bool debug = true ) - : m_debug( debug ) { setUp( m_debug ); } + GpuRuntime( const bool debug = false ) // ZW: default debug to false + : m_debug( debug ) + { + setUp( m_debug ); + } ~GpuRuntime() { tearDown( m_debug ); } GpuRuntime( const GpuRuntime& ) = delete; GpuRuntime( GpuRuntime&& ) = delete; @@ -50,7 +53,7 @@ namespace mg5amcGpu // Set up CUDA application // ** NB: strictly speaking this is not needed when using the CUDA runtime API ** // Calling cudaSetDevice on startup is useful to properly book-keep the time spent in CUDA initialization - static void setUp( const bool debug = true ) + static void setUp( const bool debug = false ) // ZW: default debug to false { // ** NB: it is useful to call cudaSetDevice, or cudaFree, to properly book-keep the time spent in CUDA initialization // ** NB: otherwise, the first CUDA operation (eg a cudaMemcpyToSymbol in CPPProcess ctor) appears to take much longer! @@ -71,7 +74,7 @@ namespace mg5amcGpu // ** NB: strictly speaking this is not needed when using the CUDA runtime API ** // Calling cudaDeviceReset on shutdown is only needed for checking memory leaks in cuda-memcheck // See https://docs.nvidia.com/cuda/cuda-memcheck/index.html#leak-checking - static void tearDown( const bool debug = true ) + static void tearDown( const bool debug = false ) // ZW: default debug to false { if( debug ) std::cout << "__GpuRuntime: calling GpuDeviceReset()" << std::endl; checkGpu( gpuDeviceReset() ); diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.cc b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.cc index f463977c1..36833a4c7 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.cc +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.cc @@ -1,7 +1,7 @@ // Copyright (C) 2020-2024 CERN and UCLouvain. // Licensed under the GNU Lesser General Public License (version 3 or later). // Created by: A. Valassi (Jan 2022) for the MG5aMC CUDACPP plugin. -// Further modified by: J. Teig, A. Valassi (2022-2024) for the MG5aMC CUDACPP plugin. +// Further modified by: J. Teig, A. Valassi, Z. Wettersten (2022-2024) for the MG5aMC CUDACPP plugin. #include "MatrixElementKernels.h" @@ -60,7 +60,9 @@ namespace mg5amcCpu #ifdef MGONGPU_CHANNELID_DEBUG MatrixElementKernelBase::dumpNevtProcessedByChannel(); #endif +#ifdef MGONGPU_VERBOSE_FPES MatrixElementKernelBase::dumpSignallingFPEs(); +#endif } //-------------------------------------------------------------------------- diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.h b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.h index 7acff4b30..1a98d5ff8 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.h +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/MatrixElementKernels.h @@ -134,7 +134,7 @@ namespace mg5amcCpu // Does this host system support the SIMD used in the matrix element calculation? // [NB: this is private, SIMD vectorization in mg5amc C++ code is currently only used in the ME calculations below MatrixElementKernelHost!] - static bool hostSupportsSIMD( const bool verbose = true ); + static bool hostSupportsSIMD( const bool verbose = false ); // ZW: set verbose to false by default private: diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_config.mk b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_config.mk index b57e56d18..19cfb7fed 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_config.mk +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_config.mk @@ -1,7 +1,7 @@ # Copyright (C) 2020-2024 CERN and UCLouvain. # Licensed under the GNU Lesser General Public License (version 3 or later). # Created by: A. Valassi (Mar 2024) for the MG5aMC CUDACPP plugin. -# Further modified by: A. Valassi (2024) for the MG5aMC CUDACPP plugin. +# Further modified by: A. Valassi, Z. Wettersten (2024) for the MG5aMC CUDACPP plugin. #------------------------------------------------------------------------------- @@ -10,7 +10,21 @@ # Set the default BACKEND (CUDA, HIP or C++/SIMD) choice ifeq ($(BACKEND),) - override BACKEND = cppauto + override BACKEND = gpucpp +endif + +# ZW: gpucpp backend checks if there is a GPU backend available before going to SIMD +# prioritises CUDA over HIP +ifeq ($(BACKEND),gpucpp) + ifeq ($(shell which nvcc 2>/dev/null),) + ifeq ($(shell which hipcc 2>/dev/null),) + override BACKEND = cppauto + else + override BACKEND = hip + endif + else + override BACKEND = cuda + endif endif # Set the default FPTYPE (floating point type) choice diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_driver.mk b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_driver.mk new file mode 100644 index 000000000..1b4de8226 --- /dev/null +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_driver.mk @@ -0,0 +1,862 @@ +# Copyright (C) 2020-2024 CERN and UCLouvain. +# Licensed under the GNU Lesser General Public License (version 3 or later). +# Created by: S. Roiser (Feb 2020) for the MG5aMC CUDACPP plugin. +# Further modified by: S. Hageboeck, O. Mattelaer, S. Roiser, J. Teig, A. Valassi, Z. Wettersten (2020-2024) for the MG5aMC CUDACPP plugin. + +#=== Determine the name of this makefile (https://ftp.gnu.org/old-gnu/Manuals/make-3.80/html_node/make_17.html) +#=== NB: use ':=' to ensure that the value of CUDACPP_MAKEFILE is not modified further down after including make_opts +#=== NB: use 'override' to ensure that the value can not be modified from the outside +override CUDACPP_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +###$(info CUDACPP_MAKEFILE='$(CUDACPP_MAKEFILE)') + +#=== NB: different names (e.g. cudacpp.mk and cudacpp_src.mk) are used in the Subprocess and src directories +override CUDACPP_SRC_MAKEFILE = cudacpp_src.mk + +#------------------------------------------------------------------------------- + +#=== Include cudacpp_config.mk + +# Check that the user-defined choices of BACKEND, FPTYPE, HELINL, HRDCOD are supported (and configure defaults if no user-defined choices exist) +# Stop with an error if BACKEND=cuda and nvcc is missing or if BACKEND=hip and hipcc is missing. +# Determine CUDACPP_BUILDDIR from a DIRTAG based on BACKEND, FPTYPE, HELINL, HRDCOD and from the user-defined choice of USEBUILDDIR +include ../src/cudacpp_config.mk + +# Export CUDACPP_BUILDDIR (so that there is no need to check/define it again in cudacpp_src.mk) +export CUDACPP_BUILDDIR + +#------------------------------------------------------------------------------- + +#=== Use bash in the Makefile (https://www.gnu.org/software/make/manual/html_node/Choosing-the-Shell.html) + +SHELL := /bin/bash + +#------------------------------------------------------------------------------- + +#=== Detect O/S and architecture (assuming uname is available, https://en.wikipedia.org/wiki/Uname) + +# Detect O/S kernel (Linux, Darwin...) +UNAME_S := $(shell uname -s) +###$(info UNAME_S='$(UNAME_S)') + +# Detect architecture (x86_64, ppc64le...) +UNAME_P := $(shell uname -p) +###$(info UNAME_P='$(UNAME_P)') + +#------------------------------------------------------------------------------- + +#=== Include the common MG5aMC Makefile options + +# OM: including make_opts is crucial for MG5aMC flag consistency/documentation +# AV: disable the inclusion of make_opts if the file has not been generated (standalone cudacpp) +ifneq ($(wildcard ../Source/make_opts),) + include ../Source/make_opts +endif + +#------------------------------------------------------------------------------- + +#=== Redefine BACKEND if the current value is 'cppauto' + +# Set the default BACKEND choice corresponding to 'cppauto' (the 'best' C++ vectorization available: eventually use native instead?) +ifeq ($(BACKEND),cppauto) + ifeq ($(UNAME_P),ppc64le) + override BACKEND = cppsse4 + else ifeq ($(UNAME_P),arm) + override BACKEND = cppsse4 + else ifeq ($(wildcard /proc/cpuinfo),) + override BACKEND = cppnone + ###$(warning Using BACKEND='$(BACKEND)' because host SIMD features cannot be read from /proc/cpuinfo) + else ifeq ($(shell grep -m1 -c avx512vl /proc/cpuinfo)$(shell $(CXX) --version | grep ^clang),1) + override BACKEND = cpp512y + else + override BACKEND = cppavx2 + ###ifneq ($(shell grep -m1 -c avx512vl /proc/cpuinfo),1) + ### $(warning Using BACKEND='$(BACKEND)' because host does not support avx512vl) + ###else + ### $(warning Using BACKEND='$(BACKEND)' because this is faster than avx512vl for clang) + ###endif + endif + $(info BACKEND=$(BACKEND) (was cppauto)) +else + $(info BACKEND='$(BACKEND)') +endif + +#------------------------------------------------------------------------------- + +#=== Configure the C++ compiler + +CXXFLAGS = $(OPTFLAGS) -std=c++17 -Wall -Wshadow -Wextra +ifeq ($(shell $(CXX) --version | grep ^nvc++),) + CXXFLAGS += -ffast-math # see issue #117 +endif +###CXXFLAGS+= -Ofast # performance is not different from --fast-math +###CXXFLAGS+= -g # FOR DEBUGGING ONLY + +# Optionally add debug flags to display the full list of flags (eg on Darwin) +###CXXFLAGS+= -v + +# Note: AR, CXX and FC are implicitly defined if not set externally +# See https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html + +# Add -mmacosx-version-min=11.3 to avoid "ld: warning: object file was built for newer macOS version than being linked" +ifneq ($(shell $(CXX) --version | egrep '^Apple clang'),) + CXXFLAGS += -mmacosx-version-min=11.3 +endif + +# Export CXXFLAGS (so that there is no need to check/define it again in cudacpp_src.mk) +export CXXFLAGS + +#------------------------------------------------------------------------------- + +#=== Configure the GPU compiler (CUDA or HIP) +#=== (note, this is done also for C++, as NVTX and CURAND/ROCRAND are also needed by the C++ backends) + +# Set CUDA_HOME from the path to nvcc, if it exists +#override CUDA_HOME = $(patsubst %%/bin/nvcc,%%,$(shell which nvcc 2>/dev/null)) +CUDA_HOME := $(patsubst %/bin/nvcc,%,$(shell which nvcc 2>/dev/null)) + +# Set HIP_HOME from the path to hipcc, if it exists +override HIP_HOME = $(patsubst %%/bin/hipcc,%%,$(shell which hipcc 2>/dev/null)) + +# Configure CUDA_INC (for CURAND and NVTX) and NVTX if a CUDA installation exists +# (FIXME? Is there any equivalent of NVTX FOR HIP? What should be configured if both CUDA and HIP are installed?) +ifneq ($(CUDA_HOME),) + USE_NVTX ?=-DUSE_NVTX + CUDA_INC = -I$(CUDA_HOME)/include/ +else + override USE_NVTX= + override CUDA_INC= +endif + +# NB: NEW LOGIC FOR ENABLING AND DISABLING CUDA OR HIP BUILDS (AV Feb-Mar 2024) +# - In the old implementation, by default the C++ targets for one specific AVX were always built together with either CUDA or HIP. +# If both CUDA and HIP were installed, then CUDA took precedence over HIP, and the only way to force HIP builds was to disable +# CUDA builds by setting CUDA_HOME to an invalid value (as CUDA_HOME took precdence over PATH to find the installation of nvcc). +# Similarly, C++-only builds could be forced by setting CUDA_HOME and/or HIP_HOME to invalid values. A check for an invalid nvcc +# in CUDA_HOME or an invalid hipcc HIP_HOME was necessary to ensure this logic, and had to be performed at the very beginning. +# - In the new implementation (PR #798), separate individual builds are performed for one specific C++/AVX mode, for CUDA or +# for HIP. The choice of the type of build is taken depending on the value of the BACKEND variable (replacing the AVX variable). +# Unlike what happened in the past, nvcc and hipcc must have already been added to PATH. Using 'which nvcc' and 'which hipcc', +# their existence and their location is checked, and the variables CUDA_HOME and HIP_HOME are internally set by this makefile. +# This must be still done before backend-specific customizations, e.g. because CURAND and NVTX are also used in C++ builds. +# Note also that a preliminary check for nvcc and hipcc if BACKEND is cuda or hip is performed in cudacpp_config.mk. +# - Note also that the REQUIRE_CUDA variable (which was used in the past, e.g. for CI tests on GPU #443) is now (PR #798) no +# longer necessary, as it is now equivalent to BACKEND=cuda. Similarly, there is no need to introduce a REQUIRE_HIP variable. + +#=== Configure the CUDA or HIP compiler (only for the CUDA and HIP backends) +#=== (NB: throughout all makefiles, an empty GPUCC is used to indicate that this is a C++ build, i.e. that BACKEND is neither cuda nor hip!) + +ifeq ($(BACKEND),cuda) + + # If CXX is not a single word (example "clang++ --gcc-toolchain...") then disable CUDA builds (issue #505) + # This is because it is impossible to pass this to "GPUFLAGS += -ccbin " below + ifneq ($(words $(subst ccache ,,$(CXX))),1) # allow at most "CXX=ccache " from outside + $(error BACKEND=$(BACKEND) but CUDA builds are not supported for multi-word CXX "$(CXX)") + endif + + # Set GPUCC as $(CUDA_HOME)/bin/nvcc (it was already checked above that this exists) + GPUCC = $(CUDA_HOME)/bin/nvcc + XCOMPILERFLAG = -Xcompiler + GPULANGUAGE = cu + GPUSUFFIX = cuda + + # Basic compiler flags (optimization and includes) + GPUFLAGS = $(foreach opt, $(OPTFLAGS), $(XCOMPILERFLAG) $(opt)) + + # NVidia CUDA architecture flags + # See https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html + # See https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/ + # Default: use compute capability 70 for V100 (CERN lxbatch, CERN itscrd, Juwels Cluster). + # This will embed device code for 70, and PTX for 70+. + # One may pass MADGRAPH_CUDA_ARCHITECTURE (comma-separated list) to the make command to use another value or list of values (see #533). + # Examples: use 60 for P100 (Piz Daint), 80 for A100 (Juwels Booster, NVidia raplab/Curiosity). + MADGRAPH_CUDA_ARCHITECTURE ?= 70 + ###GPUARCHFLAGS = -gencode arch=compute_$(MADGRAPH_CUDA_ARCHITECTURE),code=compute_$(MADGRAPH_CUDA_ARCHITECTURE) -gencode arch=compute_$(MADGRAPH_CUDA_ARCHITECTURE),code=sm_$(MADGRAPH_CUDA_ARCHITECTURE) # Older implementation (AV): go back to this one for multi-GPU support #533 + ###GPUARCHFLAGS = --gpu-architecture=compute_$(MADGRAPH_CUDA_ARCHITECTURE) --gpu-code=sm_$(MADGRAPH_CUDA_ARCHITECTURE),compute_$(MADGRAPH_CUDA_ARCHITECTURE) # Newer implementation (SH): cannot use this as-is for multi-GPU support #533 + comma:=, + GPUARCHFLAGS = $(foreach arch,$(subst $(comma), ,$(MADGRAPH_CUDA_ARCHITECTURE)),-gencode arch=compute_$(arch),code=compute_$(arch) -gencode arch=compute_$(arch),code=sm_$(arch)) + GPUFLAGS += $(GPUARCHFLAGS) + + # Other NVidia-specific flags + CUDA_OPTFLAGS = -lineinfo + GPUFLAGS += $(CUDA_OPTFLAGS) + + # NVCC version + ###GPUCC_VERSION = $(shell $(GPUCC) --version | grep 'Cuda compilation tools' | cut -d' ' -f5 | cut -d, -f1) + + # Fast math + GPUFLAGS += -use_fast_math + + # Extra build warnings + ###GPUFLAGS += $(XCOMPILERFLAG) -Wall $(XCOMPILERFLAG) -Wextra $(XCOMPILERFLAG) -Wshadow + + # CUDA includes and NVTX + GPUFLAGS += $(CUDA_INC) $(USE_NVTX) + + # C++ standard + GPUFLAGS += -std=c++17 # need CUDA >= 11.2 (see #333): this is enforced in mgOnGpuConfig.h + + # For nvcc, use -maxrregcount to control the maximum number of registries (this does not exist in hipcc) + # Without -maxrregcount: baseline throughput: 6.5E8 (16384 32 12) up to 7.3E8 (65536 128 12) + ###GPUFLAGS+= --maxrregcount 160 # improves throughput: 6.9E8 (16384 32 12) up to 7.7E8 (65536 128 12) + ###GPUFLAGS+= --maxrregcount 128 # improves throughput: 7.3E8 (16384 32 12) up to 7.6E8 (65536 128 12) + ###GPUFLAGS+= --maxrregcount 96 # degrades throughput: 4.1E8 (16384 32 12) up to 4.5E8 (65536 128 12) + ###GPUFLAGS+= --maxrregcount 64 # degrades throughput: 1.7E8 (16384 32 12) flat at 1.7E8 (65536 128 12) + + # Set the host C++ compiler for nvcc via "-ccbin " + # (NB issue #505: this must be a single word, "clang++ --gcc-toolchain..." is not supported) + GPUFLAGS += -ccbin $(shell which $(subst ccache ,,$(CXX))) + + # Allow newer (unsupported) C++ compilers with older versions of CUDA if ALLOW_UNSUPPORTED_COMPILER_IN_CUDA is set (#504) + ifneq ($(origin ALLOW_UNSUPPORTED_COMPILER_IN_CUDA),undefined) + GPUFLAGS += -allow-unsupported-compiler + endif + +else ifeq ($(BACKEND),hip) + + # Set GPUCC as $(HIP_HOME)/bin/hipcc (it was already checked above that this exists) + GPUCC = $(HIP_HOME)/bin/hipcc + XCOMPILERFLAG = + GPULANGUAGE = hip + GPUSUFFIX = hip + + # Basic compiler flags (optimization and includes) + GPUFLAGS = $(foreach opt, $(OPTFLAGS), $(XCOMPILERFLAG) $(opt)) + + # AMD HIP architecture flags + GPUARCHFLAGS = --offload-arch=gfx90a + GPUFLAGS += $(GPUARCHFLAGS) + + # Other AMD-specific flags + GPUFLAGS += -target x86_64-linux-gnu -DHIP_PLATFORM=amd + + # Fast math (is -DHIP_FAST_MATH equivalent to -ffast-math?) + GPUFLAGS += -DHIP_FAST_MATH + + # Extra build warnings + ###GPUFLAGS += $(XCOMPILERFLAG) -Wall $(XCOMPILERFLAG) -Wextra $(XCOMPILERFLAG) -Wshadow + + # HIP includes + HIP_INC = -I$(HIP_HOME)/include/ + GPUFLAGS += $(HIP_INC) + + # C++ standard + GPUFLAGS += -std=c++17 + +else + + # Backend is neither cuda nor hip + override GPUCC= + override GPUFLAGS= + + # Sanity check, this should never happen: if GPUCC is empty, then this is a C++ build, i.e. BACKEND is neither cuda nor hip. + # In practice, in the following, "ifeq ($(GPUCC),)" is equivalent to "ifneq ($(findstring cpp,$(BACKEND)),)". + # Conversely, note that GPUFLAGS is non-empty also for C++ builds, but it is never used in that case. + ifeq ($(findstring cpp,$(BACKEND)),) + $(error INTERNAL ERROR! Unknown backend BACKEND='$(BACKEND)': supported backends are $(foreach backend,$(SUPPORTED_BACKENDS),'$(backend)')) + endif + +endif + +# Export GPUCC, GPUFLAGS, GPULANGUAGE, GPUSUFFIX (these are needed by both src and rwgt_runners, but should not be overwritten there) +export CUDA_HOME +export GPUCC +export GPUFLAGS +export GPULANGUAGE +export GPUSUFFIX +export XCOMPILERFLAG + +#------------------------------------------------------------------------------- + +#=== Configure ccache for C++ and CUDA/HIP builds + +# Enable ccache if USECCACHE=1 +ifeq ($(USECCACHE)$(shell echo $(CXX) | grep ccache),1) + override CXX:=ccache $(CXX) +endif +#ifeq ($(USECCACHE)$(shell echo $(AR) | grep ccache),1) +# override AR:=ccache $(AR) +#endif +ifneq ($(GPUCC),) + ifeq ($(USECCACHE)$(shell echo $(GPUCC) | grep ccache),1) + override GPUCC:=ccache $(GPUCC) + endif +endif + +#------------------------------------------------------------------------------- + +#=== Configure common compiler flags for C++ and CUDA/HIP + +INCFLAGS = -I. +OPTFLAGS = -O3 # this ends up in GPUFLAGS too (should it?), cannot add -Ofast or -ffast-math here + +# Dependency on src directory +ifeq ($(GPUCC),) +MG5AMC_COMMONLIB = mg5amc_common_cpp +else +MG5AMC_COMMONLIB = mg5amc_common_$(GPUSUFFIX) +endif +LIBFLAGS = -L$(LIBDIR) -l$(MG5AMC_COMMONLIB) +INCFLAGS += -I../src + +# Compiler-specific googletest build directory (#125 and #738) +ifneq ($(shell $(CXX) --version | grep '^Intel(R) oneAPI DPC++/C++ Compiler'),) + override CXXNAME = icpx$(shell $(CXX) --version | head -1 | cut -d' ' -f5) +else ifneq ($(shell $(CXX) --version | egrep '^clang'),) + override CXXNAME = clang$(shell $(CXX) --version | head -1 | cut -d' ' -f3) +else ifneq ($(shell $(CXX) --version | grep '^g++ (GCC)'),) + override CXXNAME = gcc$(shell $(CXX) --version | head -1 | cut -d' ' -f3) +else + override CXXNAME = unknown +endif +###$(info CXXNAME=$(CXXNAME)) +override CXXNAMESUFFIX = _$(CXXNAME) + +# Export CXXNAMESUFFIX (so that there is no need to check/define it again in cudacpp_test.mk) +export CXXNAMESUFFIX + +#------------------------------------------------------------------------------- + +#=== Configure PowerPC-specific compiler flags for C++ and CUDA/HIP + +# PowerPC-specific CXX compiler flags (being reviewed) +ifeq ($(UNAME_P),ppc64le) + CXXFLAGS+= -mcpu=power9 -mtune=power9 # gains ~2-3%% both for cppnone and cppsse4 + # Throughput references without the extra flags below: cppnone=1.41-1.42E6, cppsse4=2.15-2.19E6 + ###CXXFLAGS+= -DNO_WARN_X86_INTRINSICS # no change + ###CXXFLAGS+= -fpeel-loops # no change + ###CXXFLAGS+= -funroll-loops # gains ~1%% for cppnone, loses ~1%% for cppsse4 + ###CXXFLAGS+= -ftree-vectorize # no change + ###CXXFLAGS+= -flto # would increase to cppnone=4.08-4.12E6, cppsse4=4.99-5.03E6! +else + ###CXXFLAGS+= -flto # also on Intel this would increase throughputs by a factor 2 to 4... + ######CXXFLAGS+= -fno-semantic-interposition # no benefit (neither alone, nor combined with -flto) +endif + +# PowerPC-specific CUDA/HIP compiler flags (to be reviewed!) +ifeq ($(UNAME_P),ppc64le) + GPUFLAGS+= $(XCOMPILERFLAG) -mno-float128 +endif + +#------------------------------------------------------------------------------- + +#=== Configure defaults for OMPFLAGS + +# Set the default OMPFLAGS choice +ifneq ($(findstring hipcc,$(GPUCC)),) + override OMPFLAGS = # disable OpenMP MT when using hipcc #802 +else ifneq ($(shell $(CXX) --version | egrep '^Intel'),) + override OMPFLAGS = -fopenmp + ###override OMPFLAGS = # disable OpenMP MT on Intel (was ok without GPUCC but not ok with GPUCC before #578) +else ifneq ($(shell $(CXX) --version | egrep '^(clang)'),) + override OMPFLAGS = -fopenmp + ###override OMPFLAGS = # disable OpenMP MT on clang (was not ok without or with nvcc before #578) +###else ifneq ($(shell $(CXX) --version | egrep '^(Apple clang)'),) # AV for Mac (Apple clang compiler) +else ifeq ($(UNAME_S),Darwin) # OM for Mac (any compiler) + override OMPFLAGS = # AV disable OpenMP MT on Apple clang (builds fail in the CI #578) + ###override OMPFLAGS = -fopenmp # OM reenable OpenMP MT on Apple clang? (AV Oct 2023: this still fails in the CI) +else + override OMPFLAGS = -fopenmp # enable OpenMP MT by default on all other platforms + ###override OMPFLAGS = # disable OpenMP MT on all other platforms (default before #575) +endif + +#------------------------------------------------------------------------------- + +#=== Configure defaults and check if user-defined choices exist for RNDGEN (legacy!), HASCURAND, HASHIPRAND + +# If the legacy RNDGEN exists, this take precedence over any HASCURAND choice (but a warning is printed out) +###$(info RNDGEN=$(RNDGEN)) +ifneq ($(RNDGEN),) + $(warning Environment variable RNDGEN is no longer supported, please use HASCURAND instead!) + ifeq ($(RNDGEN),hasCurand) + override HASCURAND = $(RNDGEN) + else ifeq ($(RNDGEN),hasNoCurand) + override HASCURAND = $(RNDGEN) + else ifneq ($(RNDGEN),hasNoCurand) + $(error Unknown RNDGEN='$(RNDGEN)': only 'hasCurand' and 'hasNoCurand' are supported - but use HASCURAND instead!) + endif +endif + +# Set the default HASCURAND (curand random number generator) choice, if no prior choice exists for HASCURAND +# (NB: allow HASCURAND=hasCurand even if $(GPUCC) does not point to nvcc: assume CUDA_HOME was defined correctly...) +ifeq ($(HASCURAND),) + ifeq ($(GPUCC),) # CPU-only build + ifneq ($(CUDA_HOME),) + # By default, assume that curand is installed if a CUDA installation exists + override HASCURAND = hasCurand + else + override HASCURAND = hasNoCurand + endif + else ifeq ($(findstring nvcc,$(GPUCC)),nvcc) # Nvidia GPU build + override HASCURAND = hasCurand + else # non-Nvidia GPU build + override HASCURAND = hasNoCurand + endif +endif + +# Set the default HASHIPRAND (hiprand random number generator) choice, if no prior choice exists for HASHIPRAND +# (NB: allow HASHIPRAND=hasHiprand even if $(GPUCC) does not point to hipcc: assume HIP_HOME was defined correctly...) +ifeq ($(HASHIPRAND),) + ifeq ($(GPUCC),) # CPU-only build + override HASHIPRAND = hasNoHiprand + else ifeq ($(findstring hipcc,$(GPUCC)),hipcc) # AMD GPU build + override HASHIPRAND = hasHiprand + else # non-AMD GPU build + override HASHIPRAND = hasNoHiprand + endif +endif + +#------------------------------------------------------------------------------- + +#=== Set the CUDA/HIP/C++ compiler flags appropriate to user-defined choices of AVX, FPTYPE, HELINL, HRDCOD + +# Set the build flags appropriate to OMPFLAGS +$(info OMPFLAGS=$(OMPFLAGS)) +CXXFLAGS += $(OMPFLAGS) + +# Set the build flags appropriate to each BACKEND choice (example: "make BACKEND=cppnone") +# [NB MGONGPU_PVW512 is needed because "-mprefer-vector-width=256" is not exposed in a macro] +# [See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96476] +ifeq ($(UNAME_P),ppc64le) + ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -D__SSE4_2__ # Power9 VSX with 128 width (VSR registers) + else ifeq ($(BACKEND),cppavx2) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on PowerPC for the moment) + else ifeq ($(BACKEND),cpp512y) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on PowerPC for the moment) + else ifeq ($(BACKEND),cpp512z) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on PowerPC for the moment) + endif +else ifeq ($(UNAME_P),arm) + ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -D__SSE4_2__ # ARM NEON with 128 width (Q/quadword registers) + else ifeq ($(BACKEND),cppavx2) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on ARM for the moment) + else ifeq ($(BACKEND),cpp512y) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on ARM for the moment) + else ifeq ($(BACKEND),cpp512z) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on ARM for the moment) + endif +else ifneq ($(shell $(CXX) --version | grep ^nvc++),) # support nvc++ #531 + ifeq ($(BACKEND),cppnone) + override AVXFLAGS = -mno-sse3 # no SIMD + else ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -mno-avx # SSE4.2 with 128 width (xmm registers) + else ifeq ($(BACKEND),cppavx2) + override AVXFLAGS = -march=haswell # AVX2 with 256 width (ymm registers) [DEFAULT for clang] + else ifeq ($(BACKEND),cpp512y) + override AVXFLAGS = -march=skylake -mprefer-vector-width=256 # AVX512 with 256 width (ymm registers) [DEFAULT for gcc] + else ifeq ($(BACKEND),cpp512z) + override AVXFLAGS = -march=skylake -DMGONGPU_PVW512 # AVX512 with 512 width (zmm registers) + endif +else + ifeq ($(BACKEND),cppnone) + override AVXFLAGS = -march=x86-64 # no SIMD (see #588) + else ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -march=nehalem # SSE4.2 with 128 width (xmm registers) + else ifeq ($(BACKEND),cppavx2) + override AVXFLAGS = -march=haswell # AVX2 with 256 width (ymm registers) [DEFAULT for clang] + else ifeq ($(BACKEND),cpp512y) + override AVXFLAGS = -march=skylake-avx512 -mprefer-vector-width=256 # AVX512 with 256 width (ymm registers) [DEFAULT for gcc] + else ifeq ($(BACKEND),cpp512z) + override AVXFLAGS = -march=skylake-avx512 -DMGONGPU_PVW512 # AVX512 with 512 width (zmm registers) + endif +endif +# For the moment, use AVXFLAGS everywhere (in C++ builds): eventually, use them only in encapsulated implementations? +ifeq ($(GPUCC),) + CXXFLAGS+= $(AVXFLAGS) +endif + +# Set the build flags appropriate to each FPTYPE choice (example: "make FPTYPE=f") +$(info FPTYPE='$(FPTYPE)') +ifeq ($(FPTYPE),d) + CXXFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_DOUBLE + GPUFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_DOUBLE +else ifeq ($(FPTYPE),f) + CXXFLAGS += -DMGONGPU_FPTYPE_FLOAT -DMGONGPU_FPTYPE2_FLOAT + GPUFLAGS += -DMGONGPU_FPTYPE_FLOAT -DMGONGPU_FPTYPE2_FLOAT +else ifeq ($(FPTYPE),m) + CXXFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_FLOAT + GPUFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_FLOAT +else + $(error Unknown FPTYPE='$(FPTYPE)': only 'd', 'f' and 'm' are supported) +endif + +# Set the build flags appropriate to each HELINL choice (example: "make HELINL=1") +$(info HELINL='$(HELINL)') +ifeq ($(HELINL),1) + CXXFLAGS += -DMGONGPU_INLINE_HELAMPS + GPUFLAGS += -DMGONGPU_INLINE_HELAMPS +else ifneq ($(HELINL),0) + $(error Unknown HELINL='$(HELINL)': only '0' and '1' are supported) +endif + +# Set the build flags appropriate to each HRDCOD choice (example: "make HRDCOD=1") +$(info HRDCOD='$(HRDCOD)') +ifeq ($(HRDCOD),1) + CXXFLAGS += -DMGONGPU_HARDCODE_PARAM + GPUFLAGS += -DMGONGPU_HARDCODE_PARAM +else ifneq ($(HRDCOD),0) + $(error Unknown HRDCOD='$(HRDCOD)': only '0' and '1' are supported) +endif + +#=== Set the CUDA/HIP/C++ compiler and linker flags appropriate to user-defined choices of HASCURAND, HASHIPRAND + +$(info HASCURAND=$(HASCURAND)) +$(info HASHIPRAND=$(HASHIPRAND)) +override RNDCXXFLAGS= +override RNDLIBFLAGS= + +# Set the RNDCXXFLAGS and RNDLIBFLAGS build flags appropriate to each HASCURAND choice (example: "make HASCURAND=hasNoCurand") +ifeq ($(HASCURAND),hasNoCurand) + override RNDCXXFLAGS += -DMGONGPU_HAS_NO_CURAND +else ifeq ($(HASCURAND),hasCurand) + override RNDLIBFLAGS += -L$(CUDA_HOME)/lib64/ -lcurand # NB: -lcuda is not needed here! +else + $(error Unknown HASCURAND='$(HASCURAND)': only 'hasCurand' and 'hasNoCurand' are supported) +endif + +# Set the RNDCXXFLAGS and RNDLIBFLAGS build flags appropriate to each HASHIPRAND choice (example: "make HASHIPRAND=hasNoHiprand") +ifeq ($(HASHIPRAND),hasNoHiprand) + override RNDCXXFLAGS += -DMGONGPU_HAS_NO_HIPRAND +else ifeq ($(HASHIPRAND),hasHiprand) + override RNDLIBFLAGS += -L$(HIP_HOME)/lib/ -lhiprand +else ifneq ($(HASHIPRAND),hasHiprand) + $(error Unknown HASHIPRAND='$(HASHIPRAND)': only 'hasHiprand' and 'hasNoHiprand' are supported) +endif + +#$(info RNDCXXFLAGS=$(RNDCXXFLAGS)) +#$(info RNDLIBFLAGS=$(RNDLIBFLAGS)) + +#------------------------------------------------------------------------------- + +#=== Configure Position-Independent Code +CXXFLAGS += -fPIC +GPUFLAGS += $(XCOMPILERFLAG) -fPIC + +#------------------------------------------------------------------------------- + +#=== Configure build directories and build lockfiles === + +# Build lockfile "full" tag (defines full specification of build options that cannot be intermixed) +# (Rationale: avoid mixing of builds with different random number generators) +override TAG = $(patsubst cpp%%,%%,$(BACKEND))_$(FPTYPE)_inl$(HELINL)_hrd$(HRDCOD)_$(HASCURAND)_$(HASHIPRAND) + +# Export TAG (so that there is no need to check/define it again in cudacpp_src.mk) +export TAG + +# Build directory: current directory by default, or build.$(DIRTAG) if USEBUILDDIR==1 +override BUILDDIR = $(CUDACPP_BUILDDIR) +ifeq ($(USEBUILDDIR),1) + override LIBDIR = ../lib/$(BUILDDIR) + override LIBDIRRPATH = '$$ORIGIN/$(LIBDIR)' + $(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG) (USEBUILDDIR == 1)) +else + override LIBDIR = ../lib + override LIBDIRRPATH = '$$ORIGIN/$(LIBDIR)' + $(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG) (USEBUILDDIR != 1)) +endif +###override INCDIR = ../../include +###$(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG)) + +# On Linux, set rpath to LIBDIR to make it unnecessary to use LD_LIBRARY_PATH +# Use relative paths with respect to the executables or shared libraries ($ORIGIN on Linux) +# On Darwin, building libraries with absolute paths in LIBDIR makes this unnecessary +ifeq ($(UNAME_S),Darwin) + override CXXLIBFLAGSRPATH = + override GPULIBFLAGSRPATH = + override CXXLIBFLAGSRPATH2 = + override GPULIBFLAGSRPATH2 = +else + # RPATH to gpu/cpp libs when linking executables + override CXXLIBFLAGSRPATH = -Wl,-rpath=$(LIBDIRRPATH) + override GPULIBFLAGSRPATH = -Xlinker -rpath=$(LIBDIRRPATH) + # RPATH to common lib when linking gpu/cpp libs + override CXXLIBFLAGSRPATH2 = -Wl,-rpath='$$ORIGIN' + override GPULIBFLAGSRPATH2 = -Xlinker -rpath='$$ORIGIN' +endif + +# Setting LD_LIBRARY_PATH or DYLD_LIBRARY_PATH in the RUNTIME is no longer necessary (neither on Linux nor on Mac) +override RUNTIME = + +#=============================================================================== +#=== Makefile TARGETS and build rules below +#=============================================================================== + +.PHONY: all $(DIRS) + +DIRS := $(wildcard P*) + +# Construct the library paths +cxx_proclibs := $(shell for dir in $(DIRS); do basename $$dir | awk -F_ '{print "-l mg5amc_"$$(NF-1)"_"$$NF"_cpp"}'; done) +gpu_proclibs := $(shell for dir in $(DIRS); do basename $$dir | awk -F_ '{print "-l mg5amc_"$$(NF-1)"_"$$NF"_$(GPUSUFFIX)"}'; done) + +ifeq ($(GPUCC),) + cxx_rwgt=$(BUILDDIR)/rwgt_driver_cpp.exe + rwgtlib := $(addprefix ,$(addsuffix /librwgt_cpp.so,$(DIRS))) +else + gpu_rwgt=$(BUILDDIR)/rwgt_driver_gpu.exe + rwgtlib := $(addprefix ,$(addsuffix /librwgt_$(GPUSUFFIX).so,$(DIRS))) +endif + +# Explicitly define the default goal (this is not necessary as it is the first target, which is implicitly the default goal) +.DEFAULT_GOAL := all.$(TAG) + +# First target (default goal) +ifeq ($(GPUCC),) +all.$(TAG): $(BUILDDIR)/.build.$(TAG) $(rwgtlib) $(cxx_rwgt) +else +all.$(TAG): $(BUILDDIR)/.build.$(TAG) $(rwgtlib) $(gpu_rwgt) +endif + +# Target (and build options): debug +MAKEDEBUG= +debug: OPTFLAGS = -g -O0 +debug: CUDA_OPTFLAGS = -G +debug: MAKEDEBUG := debug +debug: all.$(TAG) + +# Target: tag-specific build lockfiles +override oldtagsb=`if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -maxdepth 1 -name '.build.*' ! -name '.build.$(TAG)' -exec echo $(shell pwd)/{} \; ; fi` +$(BUILDDIR)/.build.$(TAG): + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + @if [ "$(oldtagsb)" != "" ]; then echo "Cannot build for tag=$(TAG) as old builds exist for other tags:"; echo " $(oldtagsb)"; echo "Please run 'make clean' first\nIf 'make clean' is not enough: run 'make clean USEBUILDDIR=1 AVX=$(AVX) FPTYPE=$(FPTYPE)' or 'make cleanall'"; exit 1; fi + @touch $(BUILDDIR)/.build.$(TAG) + +# # Apply special build flags only to check_sa_.o (NVTX in timermap.h, #679) +$(BUILDDIR)/rwgt_driver_cpp.o: CXXFLAGS += $(USE_NVTX) $(CUDA_INC) +$(BUILDDIR)/rwgt_driver_gpu.o: CXXFLAGS += $(USE_NVTX) $(CUDA_INC) + +# Avoid "warning: builtin __has_trivial_... is deprecated; use __is_trivially_... instead" in GPUCC with icx2023 (#592) +ifneq ($(shell $(CXX) --version | egrep '^(Intel)'),) +ifneq ($(GPUCC),) +GPUFLAGS += -Wno-deprecated-builtins +endif +endif + +# Generic target and build rules: objects from C++ compilation +# (NB do not include CUDA_INC here! add it only for NVTX or curand #679) +$(BUILDDIR)/%%_cpp.o : %%.cc *.h ../src/*.h $(BUILDDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + $(CXX) $(CPPFLAGS) $(INCFLAGS) $(CXXFLAGS) -c $< -o $@ + +# Generic target and build rules: objects from CUDA or HIP compilation +ifneq ($(GPUCC),) +$(BUILDDIR)/%%_$(GPUSUFFIX).o : %%.cc *.h ../src/*.h $(BUILDDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + $(GPUCC) $(CPPFLAGS) $(INCFLAGS) $(GPUFLAGS) -c -x $(GPULANGUAGE) $< -o $@ +endif + +#------------------------------------------------------------------------------- + +# Target (and build rules): common (src) library +commonlib : $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so +$(LIBDIR)/lib$(MG5AMC_COMMONLIB).so: ../src/*.h ../src/*.cc $(BUILDDIR)/.build.$(TAG) + $(MAKE) -C ../src $(MAKEDEBUG) -f $(CUDACPP_SRC_MAKEFILE) + +#------------------------------------------------------------------------------- + +#HERE LOOP MAKE OVER P DIRECTORIES AND ADD RWGT_RUNNER_LIBS +# Ensure each librwgt.a depends on its directory being built +$(rwgtlib): $(commonlib) + @$(MAKE) -C $(@D) VARIABLE=true + +# Target (and build rules): C++ and CUDA/HIP standalone executables +$(cxx_rwgt): LIBFLAGS += $(CXXLIBFLAGSRPATH) # avoid the need for LD_LIBRARY_PATH +$(cxx_rwgt): $(BUILDDIR)/rwgt_driver.o $(rwgtlib) + $(CXX) -o $@ $(BUILDDIR)/rwgt_driver.o $(OMPFLAGS) -ldl -pthread $(LIBFLAGS) -L$(LIBDIR) $(rwgtlib) + +ifneq ($(GPUCC),) +ifneq ($(shell $(CXX) --version | grep ^Intel),) +$(gpu_rwgt): LIBFLAGS += -lintlc # compile with icpx and link with GPUCC (undefined reference to `_intel_fast_memcpy') +$(gpu_rwgt): LIBFLAGS += -lsvml # compile with icpx and link with GPUCC (undefined reference to `__svml_cos4_l9') +else ifneq ($(shell $(CXX) --version | grep ^nvc++),) # support nvc++ #531 +$(gpu_rwgt): LIBFLAGS += -L$(patsubst %%bin/nvc++,%%lib,$(subst ccache ,,$(CXX))) -lnvhpcatm -lnvcpumath -lnvc +endif +$(gpu_rwgt): LIBFLAGS += $(GPULIBFLAGSRPATH) # avoid the need for LD_LIBRARY_PATH +$(gpu_rwgt): $(BUILDDIR)/$(BUILDDIR)/rwgt_driver.o $(rwgtlib) + $(GPUCC) -o $@ $(BUILDDIR)/rwgt_driver.o $(CUARCHFLAGS) $(LIBFLAGS) -L$(LIBDIR) $(rwgtlib) +endif + +#------------------------------------------------------------------------------- + +# Target: build all targets in all BACKEND modes (each BACKEND mode in a separate build directory) +# Split the bldall target into separate targets to allow parallel 'make -j bldall' builds +# (Obsolete hack, no longer needed as there is no INCDIR: add a fbridge.inc dependency to bldall, to ensure it is only copied once for all BACKEND modes) +bldcuda: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cuda -f $(CUDACPP_MAKEFILE) + +bldhip: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=hip -f $(CUDACPP_MAKEFILE) + +bldnone: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cppnone -f $(CUDACPP_MAKEFILE) + +bldsse4: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cppsse4 -f $(CUDACPP_MAKEFILE) + +bldavx2: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cppavx2 -f $(CUDACPP_MAKEFILE) + +bld512y: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cpp512y -f $(CUDACPP_MAKEFILE) + +bld512z: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cpp512z -f $(CUDACPP_MAKEFILE) + +ifeq ($(UNAME_P),ppc64le) +###bldavxs: $(INCDIR)/fbridge.inc bldnone bldsse4 +bldavxs: bldnone bldsse4 +else ifeq ($(UNAME_P),arm) +###bldavxs: $(INCDIR)/fbridge.inc bldnone bldsse4 +bldavxs: bldnone bldsse4 +else +###bldavxs: $(INCDIR)/fbridge.inc bldnone bldsse4 bldavx2 bld512y bld512z +bldavxs: bldnone bldsse4 bldavx2 bld512y bld512z +endif + +ifneq ($(HIP_HOME),) +ifneq ($(CUDA_HOME),) +bldall: bldhip bldcuda bldavxs +else +bldall: bldhip bldavxs +endif +else +ifneq ($(CUDA_HOME),) +bldall: bldcuda bldavxs +else +bldall: bldavxs +endif +endif + +#------------------------------------------------------------------------------- + +# Target: clean the builds +.PHONY: clean clean-rwgtlib + +clean: clean-rwgtlib +ifeq ($(USEBUILDDIR),1) + rm -rf $(BUILDDIR) +else + rm -f $(BUILDDIR)/.build.* $(BUILDDIR)/*.o $(BUILDDIR)/*.exe + rm -f $(LIBDIR)/lib*.so +endif + $(MAKE) -C ../src clean -f $(CUDACPP_SRC_MAKEFILE) +### rm -rf $(INCDIR) + +clean-rwgtlib: + @for dir in $(DIRS); do $(MAKE) -C $$dir clean; done + +cleanall: + @echo + $(MAKE) USEBUILDDIR=0 clean -f $(CUDACPP_MAKEFILE) + @echo + $(MAKE) USEBUILDDIR=0 -C ../src cleanall -f $(CUDACPP_SRC_MAKEFILE) + rm -rf build.* + +# Target: clean the builds as well as the gtest installation(s) +distclean: cleanall +ifneq ($(wildcard $(TESTDIRCOMMON)),) + $(MAKE) -C $(TESTDIRCOMMON) clean +endif + $(MAKE) -C $(TESTDIRLOCAL) clean + +#------------------------------------------------------------------------------- + +# Target: show system and compiler information +info: + @echo "" + @uname -spn # e.g. Linux nodename.cern.ch x86_64 +ifeq ($(UNAME_S),Darwin) + @sysctl -a | grep -i brand + @sysctl -a | grep machdep.cpu | grep features || true + @sysctl -a | grep hw.physicalcpu: + @sysctl -a | grep hw.logicalcpu: +else + @cat /proc/cpuinfo | grep "model name" | sort -u + @cat /proc/cpuinfo | grep "flags" | sort -u + @cat /proc/cpuinfo | grep "cpu cores" | sort -u + @cat /proc/cpuinfo | grep "physical id" | sort -u +endif + @echo "" +ifneq ($(shell which nvidia-smi 2>/dev/null),) + nvidia-smi -L + @echo "" +endif + @echo USECCACHE=$(USECCACHE) +ifeq ($(USECCACHE),1) + ccache --version | head -1 +endif + @echo "" + @echo GPUCC=$(GPUCC) +ifneq ($(GPUCC),) + $(GPUCC) --version +endif + @echo "" + @echo CXX=$(CXX) +ifneq ($(shell $(CXX) --version | grep ^clang),) + @echo $(CXX) -v + @$(CXX) -v |& egrep -v '(Found|multilib)' + @readelf -p .comment `$(CXX) -print-libgcc-file-name` |& grep 'GCC: (GNU)' | grep -v Warning | sort -u | awk '{print "GCC toolchain:",$$5}' +else + $(CXX) --version +endif + @echo "" + @echo FC=$(FC) + $(FC) --version + +#------------------------------------------------------------------------------- + +# Target: 'make test' (execute runTest.exe, and compare check.exe with fcheck.exe) +# [NB: THIS IS WHAT IS TESTED IN THE GITHUB CI!] +# [NB: This used to be called 'make check' but the name has been changed as this has nothing to do with 'check.exe'] +test: runTest cmpFcheck + +# Target: runTest (run the C++ or CUDA/HIP test executable runTest.exe) +runTest: all.$(TAG) +ifeq ($(GPUCC),) + $(RUNTIME) $(BUILDDIR)/runTest_cpp.exe +else + $(RUNTIME) $(BUILDDIR)/runTest_$(GPUSUFFIX).exe +endif + +# Target: runCheck (run the C++ or CUDA/HIP standalone executable check.exe, with a small number of events) +runCheck: all.$(TAG) +ifeq ($(GPUCC),) + $(RUNTIME) $(BUILDDIR)/check_cpp.exe -p 2 32 2 +else + $(RUNTIME) $(BUILDDIR)/check_$(GPUSUFFIX).exe -p 2 32 2 +endif + +# Target: runFcheck (run the Fortran standalone executable - with C++ or CUDA/HIP MEs - fcheck.exe, with a small number of events) +runFcheck: all.$(TAG) +ifeq ($(GPUCC),) + $(RUNTIME) $(BUILDDIR)/fcheck_cpp.exe 2 32 2 +else + $(RUNTIME) $(BUILDDIR)/fcheck_$(GPUSUFFIX).exe 2 32 2 +endif + +# Target: cmpFcheck (compare ME results from the C++/CUDA/HIP and Fortran with C++/CUDA/HIP MEs standalone executables, with a small number of events) +cmpFcheck: all.$(TAG) + @echo +ifeq ($(GPUCC),) + @echo "$(BUILDDIR)/check_cpp.exe --common -p 2 32 2" + @echo "$(BUILDDIR)/fcheck_cpp.exe 2 32 2" + @me1=$(shell $(RUNTIME) $(BUILDDIR)/check_cpp.exe --common -p 2 32 2 | grep MeanMatrix | awk '{print $$4}'); me2=$(shell $(RUNTIME) $(BUILDDIR)/fcheck_cpp.exe 2 32 2 | grep Average | awk '{print $$4}'); echo "Avg ME (C++/C++) = $${me1}"; echo "Avg ME (F77/C++) = $${me2}"; if [ "$${me2}" == "NaN" ]; then echo "ERROR! Fortran calculation (F77/C++) returned NaN"; elif [ "$${me2}" == "" ]; then echo "ERROR! Fortran calculation (F77/C++) crashed"; else python3 -c "me1=$${me1}; me2=$${me2}; reldif=abs((me2-me1)/me1); print('Relative difference =', reldif); ok = reldif <= 2E-4; print ( '%%s (relative difference %%s 2E-4)' %% ( ('OK','<=') if ok else ('ERROR','>') ) ); import sys; sys.exit(0 if ok else 1)"; fi +else + @echo "$(BUILDDIR)/check_$(GPUSUFFIX).exe --common -p 2 32 2" + @echo "$(BUILDDIR)/fcheck_$(GPUSUFFIX).exe 2 32 2" + @me1=$(shell $(RUNTIME) $(BUILDDIR)/check_$(GPUSUFFIX).exe --common -p 2 32 2 | grep MeanMatrix | awk '{print $$4}'); me2=$(shell $(RUNTIME) $(BUILDDIR)/fcheck_$(GPUSUFFIX).exe 2 32 2 | grep Average | awk '{print $$4}'); echo "Avg ME (C++/GPU) = $${me1}"; echo "Avg ME (F77/GPU) = $${me2}"; if [ "$${me2}" == "NaN" ]; then echo "ERROR! Fortran calculation (F77/GPU) crashed"; elif [ "$${me2}" == "" ]; then echo "ERROR! Fortran calculation (F77/GPU) crashed"; else python3 -c "me1=$${me1}; me2=$${me2}; reldif=abs((me2-me1)/me1); print('Relative difference =', reldif); ok = reldif <= 2E-4; print ( '%%s (relative difference %%s 2E-4)' %% ( ('OK','<=') if ok else ('ERROR','>') ) ); import sys; sys.exit(0 if ok else 1)"; fi +endif + +# Target: cuda-memcheck (run the CUDA standalone executable gcheck.exe with a small number of events through cuda-memcheck) +cuda-memcheck: all.$(TAG) + $(RUNTIME) $(CUDA_HOME)/bin/cuda-memcheck --check-api-memory-access yes --check-deprecated-instr yes --check-device-heap yes --demangle full --language c --leak-check full --racecheck-report all --report-api-errors all --show-backtrace yes --tool memcheck --track-unused-memory yes $(BUILDDIR)/check_$(GPUSUFFIX).exe -p 2 32 2 + +#------------------------------------------------------------------------------- diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_rex_src.mk b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_rex_src.mk new file mode 100644 index 000000000..e190a246c --- /dev/null +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_rex_src.mk @@ -0,0 +1,188 @@ +# Copyright (C) 2020-2024 CERN and UCLouvain. +# Licensed under the GNU Lesser General Public License (version 3 or later). +# Created by: S. Roiser (Feb 2020) for the MG5aMC CUDACPP plugin. +# Further modified by: S. Hageboeck, O. Mattelaer, S. Roiser, J. Teig, A. Valassi, Z. Wettersten (2020-2024) for the MG5aMC CUDACPP plugin. + +#=== Determine the name of this makefile (https://ftp.gnu.org/old-gnu/Manuals/make-3.80/html_node/make_17.html) +#=== NB: assume that the same name (e.g. cudacpp.mk, Makefile...) is used in the Subprocess and src directories + +THISMK = $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) + +#------------------------------------------------------------------------------- + +#=== Use bash in the Makefile (https://www.gnu.org/software/make/manual/html_node/Choosing-the-Shell.html) + +SHELL := /bin/bash + +#------------------------------------------------------------------------------- + +#=== Configure common compiler flags for CUDA and C++ + +INCFLAGS = -I. + +#------------------------------------------------------------------------------- + +#=== Configure the C++ compiler (note: CXXFLAGS has been exported from cudacpp.mk) + +###$(info CXXFLAGS=$(CXXFLAGS)) + +# Note: AR, CXX and FC are implicitly defined if not set externally +# See https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html +###RANLIB = ranlib + +# Add -mmacosx-version-min=11.3 to avoid "ld: warning: object file was built for newer macOS version than being linked" +LDFLAGS = +ifneq ($(shell $(CXX) --version | egrep '^Apple clang'),) + LDFLAGS += -mmacosx-version-min=11.3 +endif + +#------------------------------------------------------------------------------- + +#=== Configure the GPU (CUDA or HIP) compiler (note: GPUCC including ccache, GPUFLAGS, GPULANGUAGE, GPUSUFFIX have been exported from cudacpp.mk) + +###$(info GPUCC=$(GPUCC)) +###$(info GPUFLAGS=$(GPUFLAGS)) +###$(info GPULANGUAGE=$(GPULANGUAGE)) +###$(info GPUSUFFIX=$(GPUSUFFIX)) + +#------------------------------------------------------------------------------- + +#=== Configure ccache for C++ builds (note: GPUCC has been exported from cudacpp.mk including ccache) + +# Enable ccache if USECCACHE=1 +ifeq ($(USECCACHE)$(shell echo $(CXX) | grep ccache),1) + override CXX:=ccache $(CXX) +endif +#ifeq ($(USECCACHE)$(shell echo $(AR) | grep ccache),1) +# override AR:=ccache $(AR) +#endif + +#------------------------------------------------------------------------------- + +#=== Configure build directories and build lockfiles === + +# Use the build directory exported from cudacpp.mk +###$(info CUDACPP_BUILDDIR=$(CUDACPP_BUILDDIR)) + +# Use the build lockfile "full" tag exported from cudacpp.mk +###$(info TAG=$(TAG)) + +# Build directory: current directory by default, or build.$(DIRTAG) if USEBUILDDIR==1 +###$(info Current directory is $(shell pwd)) +override BUILDDIR = $(CUDACPP_BUILDDIR) +ifeq ($(USEBUILDDIR),1) + override LIBDIRREL = ../lib/$(BUILDDIR) + ###$(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG) (USEBUILDDIR=1 is set)) +else + override LIBDIRREL = ../lib + ###$(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG) (USEBUILDDIR is not set)) +endif +######$(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG)) + +# Workaround for Mac #375 (I did not manage to fix rpath with @executable_path): use absolute paths for LIBDIR +# (NB: this is quite ugly because it creates the directory if it does not exist - to avoid removing src by mistake) +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + override LIBDIR = $(shell mkdir -p $(LIBDIRREL); cd $(LIBDIRREL); pwd) + ifeq ($(wildcard $(LIBDIR)),) + $(error Directory LIBDIR="$(LIBDIR)" should have been created by now) + endif +else + override LIBDIR = $(LIBDIRREL) +endif + +#=============================================================================== +#=== Makefile TARGETS and build rules below +#=============================================================================== + +# NB1: there are no CUDA targets in src as we avoid RDC! +# NB2: CUDA includes for curand.h are no longer needed in the C++ code anywhere in src! + +ifeq ($(GPUCC),) +MG5AMC_COMMONLIB = mg5amc_common_cpp +else +MG5AMC_COMMONLIB = mg5amc_common_$(GPUSUFFIX) +endif + +# Explicitly define the default goal (this is not necessary as it is the first target, which is implicitly the default goal) +.DEFAULT_GOAL := all.$(TAG) + +# First target (default goal) +all.$(TAG): $(BUILDDIR)/.build.$(TAG) $(LIBDIR)/.build.$(TAG) $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so + +# Target (and build options): debug +debug: all.$(TAG) + +# Target: tag-specific build lockfiles +override oldtagsb=`if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -maxdepth 1 -name '.build.*' ! -name '.build.$(TAG)' -exec echo $(shell pwd)/{} \; ; fi` +override oldtagsl=`if [ -d $(LIBDIR) ]; then find $(LIBDIR) -maxdepth 1 -name '.build.*' ! -name '.build.$(TAG)' -exec echo $(shell pwd)/{} \; ; fi` + +$(BUILDDIR)/.build.$(TAG): $(LIBDIR)/.build.$(TAG) + +$(LIBDIR)/.build.$(TAG): + @if [ "$(oldtagsl)" != "" ]; then echo -e "Cannot build for tag=$(TAG) as old builds exist in $(LIBDIR) for other tags:\n$(oldtagsl)\nPlease run 'make clean' first\nIf 'make clean' is not enough: run 'make cleanall'"; exit 1; fi + @if [ "$(oldtagsb)" != "" ]; then echo -e "Cannot build for tag=$(TAG) as old builds exist in $(BUILDDIR) for other tags:\n$(oldtagsb)\nPlease run 'make clean' first\nIf 'make clean' is not enough: run 'make cleanall'"; exit 1; fi + @if [ ! -d $(LIBDIR) ]; then echo "mkdir -p $(LIBDIR)"; mkdir -p $(LIBDIR); fi + @touch $(LIBDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + @touch $(BUILDDIR)/.build.$(TAG) + +#------------------------------------------------------------------------------- + +# Generic target and build rules: objects from C++ compilation +$(BUILDDIR)/%%_cpp.o : %%.cc *.h $(BUILDDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + $(CXX) $(CPPFLAGS) $(INCFLAGS) $(CXXFLAGS) -c $< -o $@ + +# Generic target and build rules: objects from CUDA compilation +ifneq ($(GPUCC),) +$(BUILDDIR)/%%_$(GPUSUFFIX).o : %%.cc *.h $(BUILDDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + $(GPUCC) $(CPPFLAGS) $(INCFLAGS) $(GPUFLAGS) -c -x $(GPULANGUAGE) $< -o $@ +endif + +#------------------------------------------------------------------------------- + +cxx_objects=$(addprefix $(BUILDDIR)/, read_slha_cpp.o) +cxx_objects+=$(addprefix $(BUILDDIR)/, REX_cpp.o) # ZW: not all functionality from REX needed for teaREX is in the header, so for now just include REX.cc in teaREX.cc +cxx_objects+=$(addprefix $(BUILDDIR)/, teaREX_cpp.o) +cxx_objects+=$(addprefix $(BUILDDIR)/, rwgt_instance_cpp.o) +ifeq ($(GPUCC),) + cxx_objects+=$(addprefix $(BUILDDIR)/, Parameters_%(model)s_cpp.o) +else + gpu_objects=$(addprefix $(BUILDDIR)/, Parameters_%(model)s_$(GPUSUFFIX).o) +endif + +# Target (and build rules): common (src) library +ifeq ($(GPUCC),) +$(LIBDIR)/lib$(MG5AMC_COMMONLIB).so : $(cxx_objects) + @if [ ! -d $(LIBDIR) ]; then echo "mkdir -p $(LIBDIR)"; mkdir -p $(LIBDIR); fi + $(CXX) -shared -o $@ $(cxx_objects) $(LDFLAGS) +else +$(LIBDIR)/lib$(MG5AMC_COMMONLIB).so : $(cxx_objects) $(gpu_objects) + @if [ ! -d $(LIBDIR) ]; then echo "mkdir -p $(LIBDIR)"; mkdir -p $(LIBDIR); fi + $(GPUCC) -shared -o $@ $(cxx_objects) $(gpu_objects) $(LDFLAGS) +endif + +#------------------------------------------------------------------------------- + +# Target: clean the builds +.PHONY: clean + +clean: +ifeq ($(USEBUILDDIR),1) + rm -rf $(LIBDIR) + rm -rf $(BUILDDIR) +else + rm -f $(LIBDIR)/.build.* $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so + rm -f $(BUILDDIR)/.build.* $(BUILDDIR)/*.o $(BUILDDIR)/*.exe +endif + +cleanall: + @echo + $(MAKE) clean -f $(THISMK) + @echo + rm -rf $(LIBDIR)/build.* + rm -rf build.* + +#------------------------------------------------------------------------------- diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_runner.mk b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_runner.mk new file mode 100644 index 000000000..b33ba52a1 --- /dev/null +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/cudacpp_runner.mk @@ -0,0 +1,875 @@ +# Copyright (C) 2020-2024 CERN and UCLouvain. +# Licensed under the GNU Lesser General Public License (version 3 or later). +# Created by: S. Roiser (Feb 2020) for the MG5aMC CUDACPP plugin. +# Further modified by: S. Hageboeck, O. Mattelaer, S. Roiser, J. Teig, A. Valassi, Z. Wettersten (2020-2024) for the MG5aMC CUDACPP plugin. + +#=== Determine the name of this makefile (https://ftp.gnu.org/old-gnu/Manuals/make-3.80/html_node/make_17.html) +#=== NB: use ':=' to ensure that the value of CUDACPP_MAKEFILE is not modified further down after including make_opts +#=== NB: use 'override' to ensure that the value can not be modified from the outside +override CUDACPP_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +###$(info CUDACPP_MAKEFILE='$(CUDACPP_MAKEFILE)') + +#=== NB: different names (e.g. cudacpp.mk and cudacpp_src.mk) are used in the Subprocess and src directories +override CUDACPP_SRC_MAKEFILE = cudacpp_src.mk + +#------------------------------------------------------------------------------- + +#=== Include cudacpp_config.mk + +# Check that the user-defined choices of BACKEND, FPTYPE, HELINL, HRDCOD are supported (and configure defaults if no user-defined choices exist) +# Stop with an error if BACKEND=cuda and nvcc is missing or if BACKEND=hip and hipcc is missing. +# Determine CUDACPP_BUILDDIR from a DIRTAG based on BACKEND, FPTYPE, HELINL, HRDCOD and from the user-defined choice of USEBUILDDIR +include ../../src/cudacpp_config.mk + +# Export CUDACPP_BUILDDIR (so that there is no need to check/define it again in cudacpp_src.mk) +#export CUDACPP_BUILDDIR + +#------------------------------------------------------------------------------- + +#=== Use bash in the Makefile (https://www.gnu.org/software/make/manual/html_node/Choosing-the-Shell.html) + +SHELL := /bin/bash + +#------------------------------------------------------------------------------- + +#=== Detect O/S and architecture (assuming uname is available, https://en.wikipedia.org/wiki/Uname) + +# Detect O/S kernel (Linux, Darwin...) +UNAME_S := $(shell uname -s) +###$(info UNAME_S='$(UNAME_S)') + +# Detect architecture (x86_64, ppc64le...) +UNAME_P := $(shell uname -p) +###$(info UNAME_P='$(UNAME_P)') + +#------------------------------------------------------------------------------- + +#=== Include the common MG5aMC Makefile options + +# OM: including make_opts is crucial for MG5aMC flag consistency/documentation +# AV: disable the inclusion of make_opts if the file has not been generated (standalone cudacpp) +ifneq ($(wildcard ../../Source/make_opts),) + include ../../Source/make_opts +endif + +#------------------------------------------------------------------------------- + +#=== Redefine BACKEND if the current value is 'cppauto' + +# Set the default BACKEND choice corresponding to 'cppauto' (the 'best' C++ vectorization available: eventually use native instead?) +ifeq ($(BACKEND),cppauto) + ifeq ($(UNAME_P),ppc64le) + override BACKEND = cppsse4 + else ifeq ($(UNAME_P),arm) + override BACKEND = cppsse4 + else ifeq ($(wildcard /proc/cpuinfo),) + override BACKEND = cppnone + ###$(warning Using BACKEND='$(BACKEND)' because host SIMD features cannot be read from /proc/cpuinfo) + else ifeq ($(shell grep -m1 -c avx512vl /proc/cpuinfo)$(shell $(CXX) --version | grep ^clang),1) + override BACKEND = cpp512y + else + override BACKEND = cppavx2 + ###ifneq ($(shell grep -m1 -c avx512vl /proc/cpuinfo),1) + ### $(warning Using BACKEND='$(BACKEND)' because host does not support avx512vl) + ###else + ### $(warning Using BACKEND='$(BACKEND)' because this is faster than avx512vl for clang) + ###endif + endif + $(info BACKEND=$(BACKEND) (was cppauto)) +else + $(info BACKEND='$(BACKEND)') +endif + +#------------------------------------------------------------------------------- + +#=== Configure the GPU compiler (CUDA or HIP) +#=== (note, this is done also for C++, as NVTX and CURAND/ROCRAND are also needed by the C++ backends) + +# Set CUDA_HOME from the path to nvcc, if it exists +#override CUDA_HOME = $(patsubst %%/bin/nvcc,%%,$(shell which nvcc 2>/dev/null)) + +# Set HIP_HOME from the path to hipcc, if it exists +#override HIP_HOME = $(patsubst %%/bin/hipcc,%%,$(shell which hipcc 2>/dev/null)) + +# Configure CUDA_INC (for CURAND and NVTX) and NVTX if a CUDA installation exists +# (FIXME? Is there any equivalent of NVTX FOR HIP? What should be configured if both CUDA and HIP are installed?) +ifneq ($(CUDA_HOME),) + USE_NVTX ?=-DUSE_NVTX + CUDA_INC = -I$(CUDA_HOME)/include/ +else + override USE_NVTX= + override CUDA_INC= +endif + + +#=== Configure common compiler flags for C++ and CUDA/HIP + +INCFLAGS = -I. +OPTFLAGS = -O3 # this ends up in GPUFLAGS too (should it?), cannot add -Ofast or -ffast-math here + +# Dependency on src directory +ifeq ($(GPUCC),) +MG5AMC_COMMONLIB = mg5amc_common_cpp +else +MG5AMC_COMMONLIB = mg5amc_common_$(GPUSUFFIX) +endif +LIBFLAGS = -L$(LIBDIR) -l$(MG5AMC_COMMONLIB) +INCFLAGS += -I../../src + +# Compiler-specific googletest build directory (#125 and #738) +ifneq ($(shell $(CXX) --version | grep '^Intel(R) oneAPI DPC++/C++ Compiler'),) + override CXXNAME = icpx$(shell $(CXX) --version | head -1 | cut -d' ' -f5) +else ifneq ($(shell $(CXX) --version | egrep '^clang'),) + override CXXNAME = clang$(shell $(CXX) --version | head -1 | cut -d' ' -f3) +else ifneq ($(shell $(CXX) --version | grep '^g++ (GCC)'),) + override CXXNAME = gcc$(shell $(CXX) --version | head -1 | cut -d' ' -f3) +else + override CXXNAME = unknown +endif +###$(info CXXNAME=$(CXXNAME)) +# override CXXNAMESUFFIX = _$(CXXNAME) + +# # Export CXXNAMESUFFIX (so that there is no need to check/define it again in cudacpp_test.mk) +# export CXXNAMESUFFIX + +# Dependency on test directory +# Within the madgraph4gpu git repo: by default use a common gtest installation in /test (optionally use an external or local gtest) +# Outside the madgraph4gpu git repo: by default do not build the tests (optionally use an external or local gtest) +###GTEST_ROOT = /cvmfs/sft.cern.ch/lcg/releases/gtest/1.11.0-21e8c/x86_64-centos8-gcc11-opt/# example of an external gtest installation +###LOCALGTEST = yes# comment this out (or use make LOCALGTEST=yes) to build tests using a local gtest installation +TESTDIRCOMMON = ../../../../../test +TESTDIRLOCAL = ../../test +ifneq ($(wildcard $(GTEST_ROOT)),) + TESTDIR = +else ifneq ($(LOCALGTEST),) + TESTDIR=$(TESTDIRLOCAL) + GTEST_ROOT = $(TESTDIR)/googletest/install$(CXXNAMESUFFIX) +else ifneq ($(wildcard ../../../../../epochX/cudacpp/CODEGEN),) + TESTDIR = $(TESTDIRCOMMON) + GTEST_ROOT = $(TESTDIR)/googletest/install$(CXXNAMESUFFIX) +else + TESTDIR = +endif +ifneq ($(GTEST_ROOT),) + GTESTLIBDIR = $(GTEST_ROOT)/lib64/ + GTESTLIBS = $(GTESTLIBDIR)/libgtest.a + GTESTINC = -I$(GTEST_ROOT)/include +else + GTESTLIBDIR = + GTESTLIBS = + GTESTINC = +endif +###$(info GTEST_ROOT = $(GTEST_ROOT)) +###$(info LOCALGTEST = $(LOCALGTEST)) +###$(info TESTDIR = $(TESTDIR)) + +#------------------------------------------------------------------------------- + +#=== Configure PowerPC-specific compiler flags for C++ and CUDA/HIP + +# PowerPC-specific CXX compiler flags (being reviewed) +ifeq ($(UNAME_P),ppc64le) + CXXFLAGS+= -mcpu=power9 -mtune=power9 # gains ~2-3%% both for cppnone and cppsse4 + # Throughput references without the extra flags below: cppnone=1.41-1.42E6, cppsse4=2.15-2.19E6 +else + ###CXXFLAGS+= -flto # also on Intel this would increase throughputs by a factor 2 to 4... + ######CXXFLAGS+= -fno-semantic-interposition # no benefit (neither alone, nor combined with -flto) +endif + +# PowerPC-specific CUDA/HIP compiler flags (to be reviewed!) +ifeq ($(UNAME_P),ppc64le) + GPUFLAGS+= $(XCOMPILERFLAG) -mno-float128 +endif + +#------------------------------------------------------------------------------- + +#=== Configure defaults for OMPFLAGS + +# Set the default OMPFLAGS choice +ifneq ($(findstring hipcc,$(GPUCC)),) + override OMPFLAGS = # disable OpenMP MT when using hipcc #802 +else ifneq ($(shell $(CXX) --version | egrep '^Intel'),) + override OMPFLAGS = -fopenmp + ###override OMPFLAGS = # disable OpenMP MT on Intel (was ok without GPUCC but not ok with GPUCC before #578) +else ifneq ($(shell $(CXX) --version | egrep '^(clang)'),) + override OMPFLAGS = -fopenmp + ###override OMPFLAGS = # disable OpenMP MT on clang (was not ok without or with nvcc before #578) +###else ifneq ($(shell $(CXX) --version | egrep '^(Apple clang)'),) # AV for Mac (Apple clang compiler) +else ifeq ($(UNAME_S),Darwin) # OM for Mac (any compiler) + override OMPFLAGS = # AV disable OpenMP MT on Apple clang (builds fail in the CI #578) + ###override OMPFLAGS = -fopenmp # OM reenable OpenMP MT on Apple clang? (AV Oct 2023: this still fails in the CI) +else + override OMPFLAGS = -fopenmp # enable OpenMP MT by default on all other platforms + ###override OMPFLAGS = # disable OpenMP MT on all other platforms (default before #575) +endif + +#------------------------------------------------------------------------------- + +#=== Configure defaults and check if user-defined choices exist for RNDGEN (legacy!), HASCURAND, HASHIPRAND + +# If the legacy RNDGEN exists, this take precedence over any HASCURAND choice (but a warning is printed out) +###$(info RNDGEN=$(RNDGEN)) +ifneq ($(RNDGEN),) + $(warning Environment variable RNDGEN is no longer supported, please use HASCURAND instead!) + ifeq ($(RNDGEN),hasCurand) + override HASCURAND = $(RNDGEN) + else ifeq ($(RNDGEN),hasNoCurand) + override HASCURAND = $(RNDGEN) + else ifneq ($(RNDGEN),hasNoCurand) + $(error Unknown RNDGEN='$(RNDGEN)': only 'hasCurand' and 'hasNoCurand' are supported - but use HASCURAND instead!) + endif +endif + +# Set the default HASCURAND (curand random number generator) choice, if no prior choice exists for HASCURAND +# (NB: allow HASCURAND=hasCurand even if $(GPUCC) does not point to nvcc: assume CUDA_HOME was defined correctly...) +ifeq ($(HASCURAND),) + ifeq ($(GPUCC),) # CPU-only build + ifneq ($(CUDA_HOME),) + # By default, assume that curand is installed if a CUDA installation exists + override HASCURAND = hasCurand + else + override HASCURAND = hasNoCurand + endif + else ifeq ($(findstring nvcc,$(GPUCC)),nvcc) # Nvidia GPU build + override HASCURAND = hasCurand + else # non-Nvidia GPU build + override HASCURAND = hasNoCurand + endif +endif + +# Set the default HASHIPRAND (hiprand random number generator) choice, if no prior choice exists for HASHIPRAND +# (NB: allow HASHIPRAND=hasHiprand even if $(GPUCC) does not point to hipcc: assume HIP_HOME was defined correctly...) +ifeq ($(HASHIPRAND),) + ifeq ($(GPUCC),) # CPU-only build + override HASHIPRAND = hasNoHiprand + else ifeq ($(findstring hipcc,$(GPUCC)),hipcc) # AMD GPU build + override HASHIPRAND = hasHiprand + else # non-AMD GPU build + override HASHIPRAND = hasNoHiprand + endif +endif + +#------------------------------------------------------------------------------- + +#=== Set the CUDA/HIP/C++ compiler flags appropriate to user-defined choices of AVX, FPTYPE, HELINL, HRDCOD + +# Set the build flags appropriate to OMPFLAGS +$(info OMPFLAGS=$(OMPFLAGS)) +CXXFLAGS += $(OMPFLAGS) + +# Set the build flags appropriate to each BACKEND choice (example: "make BACKEND=cppnone") +# [NB MGONGPU_PVW512 is needed because "-mprefer-vector-width=256" is not exposed in a macro] +# [See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96476] +ifeq ($(UNAME_P),ppc64le) + ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -D__SSE4_2__ # Power9 VSX with 128 width (VSR registers) + else ifeq ($(BACKEND),cppavx2) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on PowerPC for the moment) + else ifeq ($(BACKEND),cpp512y) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on PowerPC for the moment) + else ifeq ($(BACKEND),cpp512z) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on PowerPC for the moment) + endif +else ifeq ($(UNAME_P),arm) + ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -D__SSE4_2__ # ARM NEON with 128 width (Q/quadword registers) + else ifeq ($(BACKEND),cppavx2) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on ARM for the moment) + else ifeq ($(BACKEND),cpp512y) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on ARM for the moment) + else ifeq ($(BACKEND),cpp512z) + $(error Invalid SIMD BACKEND='$(BACKEND)': only 'cppnone' and 'cppsse4' are supported on ARM for the moment) + endif +else ifneq ($(shell $(CXX) --version | grep ^nvc++),) # support nvc++ #531 + ifeq ($(BACKEND),cppnone) + override AVXFLAGS = -mno-sse3 # no SIMD + else ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -mno-avx # SSE4.2 with 128 width (xmm registers) + else ifeq ($(BACKEND),cppavx2) + override AVXFLAGS = -march=haswell # AVX2 with 256 width (ymm registers) [DEFAULT for clang] + else ifeq ($(BACKEND),cpp512y) + override AVXFLAGS = -march=skylake -mprefer-vector-width=256 # AVX512 with 256 width (ymm registers) [DEFAULT for gcc] + else ifeq ($(BACKEND),cpp512z) + override AVXFLAGS = -march=skylake -DMGONGPU_PVW512 # AVX512 with 512 width (zmm registers) + endif +else + ifeq ($(BACKEND),cppnone) + override AVXFLAGS = -march=x86-64 # no SIMD (see #588) + else ifeq ($(BACKEND),cppsse4) + override AVXFLAGS = -march=nehalem # SSE4.2 with 128 width (xmm registers) + else ifeq ($(BACKEND),cppavx2) + override AVXFLAGS = -march=haswell # AVX2 with 256 width (ymm registers) [DEFAULT for clang] + else ifeq ($(BACKEND),cpp512y) + override AVXFLAGS = -march=skylake-avx512 -mprefer-vector-width=256 # AVX512 with 256 width (ymm registers) [DEFAULT for gcc] + else ifeq ($(BACKEND),cpp512z) + override AVXFLAGS = -march=skylake-avx512 -DMGONGPU_PVW512 # AVX512 with 512 width (zmm registers) + endif +endif +# For the moment, use AVXFLAGS everywhere (in C++ builds): eventually, use them only in encapsulated implementations? +ifeq ($(GPUCC),) + CXXFLAGS+= $(AVXFLAGS) +endif + +# Set the build flags appropriate to each FPTYPE choice (example: "make FPTYPE=f") +$(info FPTYPE='$(FPTYPE)') +ifeq ($(FPTYPE),d) + CXXFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_DOUBLE + GPUFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_DOUBLE +else ifeq ($(FPTYPE),f) + CXXFLAGS += -DMGONGPU_FPTYPE_FLOAT -DMGONGPU_FPTYPE2_FLOAT + GPUFLAGS += -DMGONGPU_FPTYPE_FLOAT -DMGONGPU_FPTYPE2_FLOAT +else ifeq ($(FPTYPE),m) + CXXFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_FLOAT + GPUFLAGS += -DMGONGPU_FPTYPE_DOUBLE -DMGONGPU_FPTYPE2_FLOAT +else + $(error Unknown FPTYPE='$(FPTYPE)': only 'd', 'f' and 'm' are supported) +endif + +# Set the build flags appropriate to each HELINL choice (example: "make HELINL=1") +$(info HELINL='$(HELINL)') +ifeq ($(HELINL),1) + CXXFLAGS += -DMGONGPU_INLINE_HELAMPS + GPUFLAGS += -DMGONGPU_INLINE_HELAMPS +else ifneq ($(HELINL),0) + $(error Unknown HELINL='$(HELINL)': only '0' and '1' are supported) +endif + +# Set the build flags appropriate to each HRDCOD choice (example: "make HRDCOD=1") +$(info HRDCOD='$(HRDCOD)') +ifeq ($(HRDCOD),1) + CXXFLAGS += -DMGONGPU_HARDCODE_PARAM + GPUFLAGS += -DMGONGPU_HARDCODE_PARAM +else ifneq ($(HRDCOD),0) + $(error Unknown HRDCOD='$(HRDCOD)': only '0' and '1' are supported) +endif + +#=== Set the CUDA/HIP/C++ compiler and linker flags appropriate to user-defined choices of HASCURAND, HASHIPRAND + +$(info HASCURAND=$(HASCURAND)) +$(info HASHIPRAND=$(HASHIPRAND)) +override RNDCXXFLAGS= +override RNDLIBFLAGS= + +# Set the RNDCXXFLAGS and RNDLIBFLAGS build flags appropriate to each HASCURAND choice (example: "make HASCURAND=hasNoCurand") +ifeq ($(HASCURAND),hasNoCurand) + override RNDCXXFLAGS += -DMGONGPU_HAS_NO_CURAND +else ifeq ($(HASCURAND),hasCurand) + override RNDLIBFLAGS += -L$(CUDA_HOME)/lib64/ -lcurand # NB: -lcuda is not needed here! +else + $(error Unknown HASCURAND='$(HASCURAND)': only 'hasCurand' and 'hasNoCurand' are supported) +endif + +# Set the RNDCXXFLAGS and RNDLIBFLAGS build flags appropriate to each HASHIPRAND choice (example: "make HASHIPRAND=hasNoHiprand") +ifeq ($(HASHIPRAND),hasNoHiprand) + override RNDCXXFLAGS += -DMGONGPU_HAS_NO_HIPRAND +else ifeq ($(HASHIPRAND),hasHiprand) + override RNDLIBFLAGS += -L$(HIP_HOME)/lib/ -lhiprand +else ifneq ($(HASHIPRAND),hasHiprand) + $(error Unknown HASHIPRAND='$(HASHIPRAND)': only 'hasHiprand' and 'hasNoHiprand' are supported) +endif + +#$(info RNDCXXFLAGS=$(RNDCXXFLAGS)) +#$(info RNDLIBFLAGS=$(RNDLIBFLAGS)) + +#------------------------------------------------------------------------------- + +#=== Configure Position-Independent Code +CXXFLAGS += -fPIC +GPUFLAGS += $(XCOMPILERFLAG) -fPIC + +#------------------------------------------------------------------------------- + +#=== Configure build directories and build lockfiles === + +# Build lockfile "full" tag (defines full specification of build options that cannot be intermixed) +# (Rationale: avoid mixing of builds with different random number generators) +override TAG = $(patsubst cpp%%,%%,$(BACKEND))_$(FPTYPE)_inl$(HELINL)_hrd$(HRDCOD)_$(HASCURAND)_$(HASHIPRAND) + +# Export TAG (so that there is no need to check/define it again in cudacpp_src.mk) +export TAG + +# Build directory: current directory by default, or build.$(DIRTAG) if USEBUILDDIR==1 +override BUILDDIR = $(CUDACPP_BUILDDIR) +ifeq ($(USEBUILDDIR),1) + override LIBDIR = ../../lib/$(BUILDDIR) + override LIBDIRRPATH = '$$ORIGIN/../$(LIBDIR)' + $(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG) (USEBUILDDIR == 1)) +else + override LIBDIR = ../../lib + override LIBDIRRPATH = '$$ORIGIN/$(LIBDIR)' + $(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG) (USEBUILDDIR != 1)) +endif +###override INCDIR = ../../include +###$(info Building in BUILDDIR=$(BUILDDIR) for tag=$(TAG)) + +# On Linux, set rpath to LIBDIR to make it unnecessary to use LD_LIBRARY_PATH +# Use relative paths with respect to the executables or shared libraries ($ORIGIN on Linux) +# On Darwin, building libraries with absolute paths in LIBDIR makes this unnecessary +ifeq ($(UNAME_S),Darwin) + override CXXLIBFLAGSRPATH = + override GPULIBFLAGSRPATH = + override CXXLIBFLAGSRPATH2 = + override GPULIBFLAGSRPATH2 = +else + # RPATH to gpu/cpp libs when linking executables + override CXXLIBFLAGSRPATH = -Wl,-rpath=$(LIBDIRRPATH) + override GPULIBFLAGSRPATH = -Xlinker -rpath=$(LIBDIRRPATH) + # RPATH to common lib when linking gpu/cpp libs + override CXXLIBFLAGSRPATH2 = -Wl,-rpath='$$ORIGIN' + override GPULIBFLAGSRPATH2 = -Xlinker -rpath='$$ORIGIN' +endif + +# Setting LD_LIBRARY_PATH or DYLD_LIBRARY_PATH in the RUNTIME is no longer necessary (neither on Linux nor on Mac) +override RUNTIME = + +#=============================================================================== +#=== Makefile TARGETS and build rules below +#=============================================================================== + + +ifeq ($(GPUCC),) + cxx_rwgtlib=$(BUILDDIR)/librwgt_cpp.so +else + gpu_rwgtlib=$(BUILDDIR)/librwgt_$(GPUSUFFIX).so +endif + +# Explicitly define the default goal (this is not necessary as it is the first target, which is implicitly the default goal) +.DEFAULT_GOAL := all.$(TAG) + +# First target (default goal) +ifeq ($(GPUCC),) +all.$(TAG): $(BUILDDIR)/.build.$(TAG) $(cxx_rwgtlib) +else +all.$(TAG): $(BUILDDIR)/.build.$(TAG) $(gpu_rwgtlib) +endif + +# Target (and build options): debug +MAKEDEBUG= +debug: OPTFLAGS = -g -O0 +debug: CUDA_OPTFLAGS = -G +debug: MAKEDEBUG := debug +debug: all.$(TAG) + +# Target: tag-specific build lockfiles +override oldtagsb=`if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -maxdepth 1 -name '.build.*' ! -name '.build.$(TAG)' -exec echo $(shell pwd)/{} \; ; fi` +$(BUILDDIR)/.build.$(TAG): + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + @if [ "$(oldtagsb)" != "" ]; then echo "Cannot build for tag=$(TAG) as old builds exist for other tags:"; echo " $(oldtagsb)"; echo "Please run 'make clean' first\nIf 'make clean' is not enough: run 'make clean USEBUILDDIR=1 AVX=$(AVX) FPTYPE=$(FPTYPE)' or 'make cleanall'"; exit 1; fi + @touch $(BUILDDIR)/.build.$(TAG) + +# Apply special build flags only to CrossSectionKernel_.o (no fast math, see #117 and #516) +# Added edgecase for HIP compilation +ifeq ($(shell $(CXX) --version | grep ^nvc++),) +$(BUILDDIR)/CrossSectionKernels_cpp.o: CXXFLAGS := $(filter-out -ffast-math,$(CXXFLAGS)) +$(BUILDDIR)/CrossSectionKernels_cpp.o: CXXFLAGS += -fno-fast-math +$(BUILDDIR)/CrossSectionKernels_$(GPUSUFFIX).o: GPUFLAGS += $(XCOMPILERFLAG) -fno-fast-math +endif + +# Apply special build flags only to check_sa_.o (NVTX in timermap.h, #679) +$(BUILDDIR)/check_sa_cpp.o: CXXFLAGS += $(USE_NVTX) $(CUDA_INC) +$(BUILDDIR)/rwgt_runner_cpp.o: CXXFLAGS += $(USE_NVTX) $(CUDA_INC) +$(BUILDDIR)/check_sa_$(GPUSUFFIX).o: CXXFLAGS += $(USE_NVTX) $(CUDA_INC) +$(BUILDDIR)/rwgt_runner_$(GPUSUFFIX).o: CXXFLAGS += $(USE_NVTX) $(CUDA_INC) + +# Apply special build flags only to check_sa_.o and (Cu|Hip)randRandomNumberKernel_.o +$(BUILDDIR)/check_sa_cpp.o: CXXFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/rwgt_runner_cpp.o: CXXFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/check_sa_$(GPUSUFFIX).o: GPUFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/rwgt_runner_$(GPUSUFFIX).o: GPUFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/CurandRandomNumberKernel_cpp.o: CXXFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/CurandRandomNumberKernel_$(GPUSUFFIX).o: GPUFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/HiprandRandomNumberKernel_cpp.o: CXXFLAGS += $(RNDCXXFLAGS) +$(BUILDDIR)/HiprandRandomNumberKernel_$(GPUSUFFIX).o: GPUFLAGS += $(RNDCXXFLAGS) +ifeq ($(HASCURAND),hasCurand) # curand headers, #679 +$(BUILDDIR)/CurandRandomNumberKernel_cpp.o: CXXFLAGS += $(CUDA_INC) +endif +ifeq ($(HASHIPRAND),hasHiprand) # hiprand headers +$(BUILDDIR)/HiprandRandomNumberKernel_cpp.o: CXXFLAGS += $(HIP_INC) +endif + +# Avoid "warning: builtin __has_trivial_... is deprecated; use __is_trivially_... instead" in GPUCC with icx2023 (#592) +ifneq ($(shell $(CXX) --version | egrep '^(Intel)'),) +ifneq ($(GPUCC),) +GPUFLAGS += -Wno-deprecated-builtins +endif +endif + +# Generic target and build rules: objects from C++ compilation +# (NB do not include CUDA_INC here! add it only for NVTX or curand #679) +$(BUILDDIR)/%%_cpp.o : %%.cc *.h ../../src/*.h $(BUILDDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + $(CXX) $(CPPFLAGS) $(INCFLAGS) $(CXXFLAGS) -c $< -o $@ + +# Generic target and build rules: objects from CUDA or HIP compilation +ifneq ($(GPUCC),) +$(BUILDDIR)/%%_$(GPUSUFFIX).o : %%.cc *.h ../../src/*.h $(BUILDDIR)/.build.$(TAG) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + $(GPUCC) $(CPPFLAGS) $(INCFLAGS) $(GPUFLAGS) -c -x $(GPULANGUAGE) $< -o $@ +endif + +#------------------------------------------------------------------------------- + +# Target (and build rules): common (src) library +commonlib : $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so + +$(LIBDIR)/lib$(MG5AMC_COMMONLIB).so: ../../src/*.h ../../src/*.cc $(BUILDDIR)/.build.$(TAG) + $(MAKE) -C ../../src $(MAKEDEBUG) -f $(CUDACPP_SRC_MAKEFILE) + +#------------------------------------------------------------------------------- + +processid_short=$(shell basename $(CURDIR) | awk -F_ '{print $$(NF-1)"_"$$NF}') +###$(info processid_short=$(processid_short)) + +MG5AMC_CXXLIB = mg5amc_$(processid_short)_cpp +cxx_objects_lib=$(BUILDDIR)/CPPProcess_cpp.o $(BUILDDIR)/MatrixElementKernels_cpp.o $(BUILDDIR)/BridgeKernels_cpp.o $(BUILDDIR)/CrossSectionKernels_cpp.o +cxx_objects_exe=$(BUILDDIR)/CommonRandomNumberKernel_cpp.o $(BUILDDIR)/RamboSamplingKernels_cpp.o + +ifneq ($(GPUCC),) +MG5AMC_GPULIB = mg5amc_$(processid_short)_$(GPUSUFFIX) +gpu_objects_lib=$(BUILDDIR)/CPPProcess_$(GPUSUFFIX).o $(BUILDDIR)/MatrixElementKernels_$(GPUSUFFIX).o $(BUILDDIR)/BridgeKernels_$(GPUSUFFIX).o $(BUILDDIR)/CrossSectionKernels_$(GPUSUFFIX).o +gpu_objects_exe=$(BUILDDIR)/CommonRandomNumberKernel_$(GPUSUFFIX).o $(BUILDDIR)/RamboSamplingKernels_$(GPUSUFFIX).o +endif + +# Target (and build rules): C++ and CUDA/HIP shared libraries +$(LIBDIR)/lib$(MG5AMC_CXXLIB).so: $(BUILDDIR)/fbridge_cpp.o +$(LIBDIR)/lib$(MG5AMC_CXXLIB).so: cxx_objects_lib += $(BUILDDIR)/fbridge_cpp.o +$(LIBDIR)/lib$(MG5AMC_CXXLIB).so: $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(cxx_objects_lib) + $(CXX) -shared -o $@ $(cxx_objects_lib) $(CXXLIBFLAGSRPATH2) -L$(LIBDIR) -l$(MG5AMC_COMMONLIB) + +ifneq ($(GPUCC),) +$(LIBDIR)/lib$(MG5AMC_GPULIB).so: $(BUILDDIR)/fbridge_$(GPUSUFFIX).o +$(LIBDIR)/lib$(MG5AMC_GPULIB).so: gpu_objects_lib += $(BUILDDIR)/fbridge_$(GPUSUFFIX).o +$(LIBDIR)/lib$(MG5AMC_GPULIB).so: $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(gpu_objects_lib) + $(GPUCC) --shared -o $@ $(gpu_objects_lib) $(GPULIBFLAGSRPATH2) -L$(LIBDIR) -l$(MG5AMC_COMMONLIB) +# Bypass std::filesystem completely to ease portability on LUMI #803 +#ifneq ($(findstring hipcc,$(GPUCC)),) +# $(GPUCC) --shared -o $@ $(gpu_objects_lib) $(GPULIBFLAGSRPATH2) -L$(LIBDIR) -l$(MG5AMC_COMMONLIB) -lstdc++fs +#else +# $(GPUCC) --shared -o $@ $(gpu_objects_lib) $(GPULIBFLAGSRPATH2) -L$(LIBDIR) -l$(MG5AMC_COMMONLIB) +#endif +endif + +#------------------------------------------------------------------------------- + +# Target (and build rules): C++ rwgt libraries +# ZW: the -Bsymbolic flag ensures that function calls will be handled internally by the library, rather than going to global context +cxx_rwgtfiles := $(BUILDDIR)/rwgt_runner_cpp.o $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(BUILDDIR)/fbridge_cpp.o $(cxx_objects_lib) $(cxx_objects_exe) $(BUILDDIR)/CurandRandomNumberKernel_cpp.o $(BUILDDIR)/HiprandRandomNumberKernel_cpp.o +$(cxx_rwgtlib): LIBFLAGS += $(CXXLIBFLAGSRPATH) +$(cxx_rwgtlib): $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(cxx_rwgtfiles) $(cxx_objects_lib) + $(CXX) -shared -Wl,-Bsymbolic -o $@ $(BUILDDIR)/rwgt_runner_cpp.o $(OMPFLAGS) -ldl -pthread $(LIBFLAGS) -L$(LIBDIR) $(BUILDDIR)/fbridge_cpp.o $(cxx_objects_lib) $(cxx_objects_exe) $(BUILDDIR)/CurandRandomNumberKernel_cpp.o $(BUILDDIR)/HiprandRandomNumberKernel_cpp.o $(RNDLIBFLAGS) + +ifneq ($(GPUCC),) +ifneq ($(shell $(CXX) --version | grep ^Intel),) +$(gpu_checkmain): LIBFLAGS += -lintlc # compile with icpx and link with GPUCC (undefined reference to `_intel_fast_memcpy') +$(gpu_checkmain): LIBFLAGS += -lsvml # compile with icpx and link with GPUCC (undefined reference to `__svml_cos4_l9') +else ifneq ($(shell $(CXX) --version | grep ^nvc++),) # support nvc++ #531 +$(gpu_checkmain): LIBFLAGS += -L$(patsubst %%bin/nvc++,%%lib,$(subst ccache ,,$(CXX))) -lnvhpcatm -lnvcpumath -lnvc +endif +$(gpu_checkmain): LIBFLAGS += $(GPULIBFLAGSRPATH) # avoid the need for LD_LIBRARY_PATH +$(gpu_checkmain): $(BUILDDIR)/check_sa_$(GPUSUFFIX).o $(gp_objects_lib) $(gpu_objects_exe) $(BUILDDIR)/CurandRandomNumberKernel_$(GPUSUFFIX).o $(BUILDDIR)/HiprandRandomNumberKernel_$(GPUSUFFIX).o + $(GPUCC) -o $@ $(BUILDDIR)/check_sa_$(GPUSUFFIX).o $(LIBFLAGS) -L$(LIBDIR) -l$(MG5AMC_GPULIB) $(gpu_objects_exe) $(BUILDDIR)/CurandRandomNumberKernel_$(GPUSUFFIX).o $(BUILDDIR)/HiprandRandomNumberKernel_$(GPUSUFFIX).o $(RNDLIBFLAGS) +ifneq ($(shell $(CXX) --version | grep ^Intel),) +$(gpu_rwgtlib): LIBFLAGS += -lintlc # compile with icpx and link with GPUCC (undefined reference to `_intel_fast_memcpy') +$(gpu_rwgtlib): LIBFLAGS += -lsvml # compile with icpx and link with GPUCC (undefined reference to `__svml_cos4_l9') +else ifneq ($(shell $(CXX) --version | grep ^nvc++),) # support nvc++ #531 +$(gpu_rwgtlib): LIBFLAGS += -L$(patsubst %%bin/nvc++,%%lib,$(subst ccache ,,$(CXX))) -lnvhpcatm -lnvcpumath -lnvc +endif +$(gpu_rwgtlib): LIBFLAGS += $(GPULIBFLAGSRPATH) # avoid the need for LD_LIBRARY_PATH +gpu_rwgtfiles := $(BUILDDIR)/rwgt_runner_$(GPUSUFFIX).o $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(gpu_objects_lib) $(gpu_objects_exe) $(BUILDDIR)/fbridge_$(GPUSUFFIX).o $(BUILDDIR)/CurandRandomNumberKernel_$(GPUSUFFIX).o $(BUILDDIR)/HiprandRandomNumberKernel_$(GPUSUFFIX).o +$(gpu_rwgtlib): $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(gpu_rwgtfiles) $(gpu_objects_lib) + $(GPUCC) -shared -Xcompiler \"-Wl,-Bsymbolic\" -o $@ $(BUILDDIR)/rwgt_runner_$(GPUSUFFIX).o $(LIBFLAGS) -L$(LIBDIR) $(BUILDDIR)/fbridge_$(GPUSUFFIX).o $(gpu_objects_exe) $(gpu_objects_lib) $(BUILDDIR)/CurandRandomNumberKernel_$(GPUSUFFIX).o $(BUILDDIR)/HiprandRandomNumberKernel_$(GPUSUFFIX).o $(RNDLIBFLAGS) +endif + +#------------------------------------------------------------------------------- + +# Target (and build rules): test objects and test executable +ifeq ($(GPUCC),) +$(BUILDDIR)/testxxx_cpp.o: $(GTESTLIBS) +$(BUILDDIR)/testxxx_cpp.o: INCFLAGS += $(GTESTINC) +$(BUILDDIR)/testxxx_cpp.o: testxxx_cc_ref.txt +$(cxx_testmain): $(BUILDDIR)/testxxx_cpp.o +$(cxx_testmain): cxx_objects_exe += $(BUILDDIR)/testxxx_cpp.o # Comment out this line to skip the C++ test of xxx functions +else +$(BUILDDIR)/testxxx_$(GPUSUFFIX).o: $(GTESTLIBS) +$(BUILDDIR)/testxxx_$(GPUSUFFIX).o: INCFLAGS += $(GTESTINC) +$(BUILDDIR)/testxxx_$(GPUSUFFIX).o: testxxx_cc_ref.txt +$(gpu_testmain): $(BUILDDIR)/testxxx_$(GPUSUFFIX).o +$(gpu_testmain): gpu_objects_exe += $(BUILDDIR)/testxxx_$(GPUSUFFIX).o # Comment out this line to skip the CUDA/HIP test of xxx functions +endif + +ifneq ($(UNAME_S),Darwin) # Disable testmisc on Darwin (workaround for issue #838) +ifeq ($(GPUCC),) +$(BUILDDIR)/testmisc_cpp.o: $(GTESTLIBS) +$(BUILDDIR)/testmisc_cpp.o: INCFLAGS += $(GTESTINC) +$(cxx_testmain): $(BUILDDIR)/testmisc_cpp.o +$(cxx_testmain): cxx_objects_exe += $(BUILDDIR)/testmisc_cpp.o # Comment out this line to skip the C++ miscellaneous tests +else +$(BUILDDIR)/testmisc_$(GPUSUFFIX).o: $(GTESTLIBS) +$(BUILDDIR)/testmisc_$(GPUSUFFIX).o: INCFLAGS += $(GTESTINC) +$(gpu_testmain): $(BUILDDIR)/testmisc_$(GPUSUFFIX).o +$(gpu_testmain): gpu_objects_exe += $(BUILDDIR)/testmisc_$(GPUSUFFIX).o # Comment out this line to skip the CUDA/HIP miscellaneous tests +endif +endif + +ifeq ($(GPUCC),) +$(BUILDDIR)/runTest_cpp.o: $(GTESTLIBS) +$(BUILDDIR)/runTest_cpp.o: INCFLAGS += $(GTESTINC) +$(cxx_testmain): $(BUILDDIR)/runTest_cpp.o +$(cxx_testmain): cxx_objects_exe += $(BUILDDIR)/runTest_cpp.o +else +$(BUILDDIR)/runTest_$(GPUSUFFIX).o: $(GTESTLIBS) +$(BUILDDIR)/runTest_$(GPUSUFFIX).o: INCFLAGS += $(GTESTINC) +ifneq ($(shell $(CXX) --version | grep ^Intel),) +$(gpu_testmain): LIBFLAGS += -lintlc # compile with icpx and link with GPUCC (undefined reference to `_intel_fast_memcpy') +$(gpu_testmain): LIBFLAGS += -lsvml # compile with icpx and link with GPUCC (undefined reference to `__svml_cos4_l9') +else ifneq ($(shell $(CXX) --version | grep ^nvc++),) # support nvc++ #531 +$(gpu_testmain): LIBFLAGS += -L$(patsubst %%bin/nvc++,%%lib,$(subst ccache ,,$(CXX))) -lnvhpcatm -lnvcpumath -lnvc +endif +$(gpu_testmain): $(BUILDDIR)/runTest_$(GPUSUFFIX).o +$(gpu_testmain): gpu_objects_exe += $(BUILDDIR)/runTest_$(GPUSUFFIX).o +endif + +ifeq ($(GPUCC),) +$(cxx_testmain): $(GTESTLIBS) +$(cxx_testmain): INCFLAGS += $(GTESTINC) +$(cxx_testmain): LIBFLAGS += -L$(GTESTLIBDIR) -lgtest # adding also -lgtest_main is no longer necessary since we added main() to testxxx.cc +else +$(gpu_testmain): $(GTESTLIBS) +$(gpu_testmain): INCFLAGS += $(GTESTINC) +$(gpu_testmain): LIBFLAGS += -L$(GTESTLIBDIR) -lgtest # adding also -lgtest_main is no longer necessary since we added main() to testxxx.cc +endif + +ifeq ($(GPUCC),) # if at all, OMP is used only in CXX builds (not in GPU builds) +ifneq ($(OMPFLAGS),) +ifneq ($(shell $(CXX) --version | egrep '^Intel'),) +$(cxx_testmain): LIBFLAGS += -liomp5 # see #578 (not '-qopenmp -static-intel' as in https://stackoverflow.com/questions/45909648) +else ifneq ($(shell $(CXX) --version | egrep '^clang'),) +$(cxx_testmain): LIBFLAGS += -L $(shell dirname $(shell $(CXX) -print-file-name=libc++.so)) -lomp # see #604 +###else ifneq ($(shell $(CXX) --version | egrep '^Apple clang'),) +###$(cxx_testmain): LIBFLAGS += ???? # OMP is not supported yet by cudacpp for Apple clang (see #578 and #604) +else +$(cxx_testmain): LIBFLAGS += -lgomp +endif +endif +endif + +# Test quadmath in testmisc.cc tests for constexpr_math #627 +###ifeq ($(GPUCC),) +###$(cxx_testmain): LIBFLAGS += -lquadmath +###else +###$(gpu_testmain): LIBFLAGS += -lquadmath +###endif + +# Bypass std::filesystem completely to ease portability on LUMI #803 +###ifneq ($(findstring hipcc,$(GPUCC)),) +###$(gpu_testmain): LIBFLAGS += -lstdc++fs +###endif + +ifeq ($(GPUCC),) # link only runTest_cpp.o +$(cxx_testmain): LIBFLAGS += $(CXXLIBFLAGSRPATH) # avoid the need for LD_LIBRARY_PATH +$(cxx_testmain): $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(cxx_objects_lib) $(cxx_objects_exe) $(GTESTLIBS) + $(CXX) -o $@ $(cxx_objects_lib) $(cxx_objects_exe) -ldl -pthread $(LIBFLAGS) +else # link only runTest_$(GPUSUFFIX).o (new: in the past, this was linking both runTest_cpp.o and runTest_$(GPUSUFFIX).o) +$(gpu_testmain): LIBFLAGS += $(GPULIBFLAGSRPATH) # avoid the need for LD_LIBRARY_PATH +$(gpu_testmain): $(LIBDIR)/lib$(MG5AMC_COMMONLIB).so $(gpu_objects_lib) $(gpu_objects_exe) $(GTESTLIBS) +ifneq ($(findstring hipcc,$(GPUCC)),) # link fortran/c++/hip using $FC when hipcc is used #802 + $(FC) -o $@ $(gpu_objects_lib) $(gpu_objects_exe) -ldl $(LIBFLAGS) -lstdc++ -lpthread -L$(shell dirname $(shell $(GPUCC) -print-prog-name=clang))/../../lib -lamdhip64 +else + $(GPUCC) -o $@ $(gpu_objects_lib) $(gpu_objects_exe) -ldl $(LIBFLAGS) -lcuda +endif +endif + +# Use target gtestlibs to build only googletest +ifneq ($(GTESTLIBS),) +gtestlibs: $(GTESTLIBS) +endif + +# Use flock (Linux only, no Mac) to allow 'make -j' if googletest has not yet been downloaded https://stackoverflow.com/a/32666215 +$(GTESTLIBS): +ifneq ($(shell which flock 2>/dev/null),) + @if [ ! -d $(BUILDDIR) ]; then echo "mkdir -p $(BUILDDIR)"; mkdir -p $(BUILDDIR); fi + flock $(BUILDDIR)/.make_test.lock $(MAKE) -C $(TESTDIR) +else + if [ -d $(TESTDIR) ]; then $(MAKE) -C $(TESTDIR); fi +endif + +#------------------------------------------------------------------------------- + +# Target: build all targets in all BACKEND modes (each BACKEND mode in a separate build directory) +# Split the bldall target into separate targets to allow parallel 'make -j bldall' builds +# (Obsolete hack, no longer needed as there is no INCDIR: add a fbridge.inc dependency to bldall, to ensure it is only copied once for all BACKEND modes) +bldcuda: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cuda -f $(CUDACPP_MAKEFILE) + +bldhip: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=hip -f $(CUDACPP_MAKEFILE) + +bldnone: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cppnone -f $(CUDACPP_MAKEFILE) + +bldsse4: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cppsse4 -f $(CUDACPP_MAKEFILE) + +bldavx2: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cppavx2 -f $(CUDACPP_MAKEFILE) + +bld512y: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cpp512y -f $(CUDACPP_MAKEFILE) + +bld512z: + @echo + $(MAKE) USEBUILDDIR=1 BACKEND=cpp512z -f $(CUDACPP_MAKEFILE) + +ifeq ($(UNAME_P),ppc64le) +###bldavxs: $(INCDIR)/fbridge.inc bldnone bldsse4 +bldavxs: bldnone bldsse4 +else ifeq ($(UNAME_P),arm) +###bldavxs: $(INCDIR)/fbridge.inc bldnone bldsse4 +bldavxs: bldnone bldsse4 +else +###bldavxs: $(INCDIR)/fbridge.inc bldnone bldsse4 bldavx2 bld512y bld512z +bldavxs: bldnone bldsse4 bldavx2 bld512y bld512z +endif + +ifneq ($(HIP_HOME),) +ifneq ($(CUDA_HOME),) +bldall: bldhip bldcuda bldavxs +else +bldall: bldhip bldavxs +endif +else +ifneq ($(CUDA_HOME),) +bldall: bldcuda bldavxs +else +bldall: bldavxs +endif +endif + +#------------------------------------------------------------------------------- + +# Target: clean the builds +.PHONY: clean + +clean: +ifeq ($(USEBUILDDIR),1) + rm -rf $(BUILDDIR) +else + rm -f $(BUILDDIR)/.build.* $(BUILDDIR)/*.o $(BUILDDIR)/*.so $(BUILDDIR)/*.exe + rm -f $(LIBDIR)/lib*.so +endif + $(MAKE) -C ../../src clean -f $(CUDACPP_SRC_MAKEFILE) +### rm -rf $(INCDIR) + +cleanall: + @echo + $(MAKE) USEBUILDDIR=0 clean -f $(CUDACPP_MAKEFILE) + @echo + $(MAKE) USEBUILDDIR=0 -C ../../src cleanall -f $(CUDACPP_SRC_MAKEFILE) + rm -rf build.* + +# Target: clean the builds as well as the gtest installation(s) +distclean: cleanall +ifneq ($(wildcard $(TESTDIRCOMMON)),) + $(MAKE) -C $(TESTDIRCOMMON) clean +endif + $(MAKE) -C $(TESTDIRLOCAL) clean + +#------------------------------------------------------------------------------- + +# Target: show system and compiler information +info: + @echo "" + @uname -spn # e.g. Linux nodename.cern.ch x86_64 +ifeq ($(UNAME_S),Darwin) + @sysctl -a | grep -i brand + @sysctl -a | grep machdep.cpu | grep features || true + @sysctl -a | grep hw.physicalcpu: + @sysctl -a | grep hw.logicalcpu: +else + @cat /proc/cpuinfo | grep "model name" | sort -u + @cat /proc/cpuinfo | grep "flags" | sort -u + @cat /proc/cpuinfo | grep "cpu cores" | sort -u + @cat /proc/cpuinfo | grep "physical id" | sort -u +endif + @echo "" +ifneq ($(shell which nvidia-smi 2>/dev/null),) + nvidia-smi -L + @echo "" +endif + @echo USECCACHE=$(USECCACHE) +ifeq ($(USECCACHE),1) + ccache --version | head -1 +endif + @echo "" + @echo GPUCC=$(GPUCC) +ifneq ($(GPUCC),) + $(GPUCC) --version +endif + @echo "" + @echo CXX=$(CXX) +ifneq ($(shell $(CXX) --version | grep ^clang),) + @echo $(CXX) -v + @$(CXX) -v |& egrep -v '(Found|multilib)' + @readelf -p .comment `$(CXX) -print-libgcc-file-name` |& grep 'GCC: (GNU)' | grep -v Warning | sort -u | awk '{print "GCC toolchain:",$$5}' +else + $(CXX) --version +endif + @echo "" + @echo FC=$(FC) + $(FC) --version + +#------------------------------------------------------------------------------- + +# Target: 'make test' (execute runTest.exe, and compare check.exe with fcheck.exe) +# [NB: THIS IS WHAT IS TESTED IN THE GITHUB CI!] +# [NB: This used to be called 'make check' but the name has been changed as this has nothing to do with 'check.exe'] +test: runTest cmpFcheck + +# Target: runTest (run the C++ or CUDA/HIP test executable runTest.exe) +runTest: all.$(TAG) +ifeq ($(GPUCC),) + $(RUNTIME) $(BUILDDIR)/runTest_cpp.exe +else + $(RUNTIME) $(BUILDDIR)/runTest_$(GPUSUFFIX).exe +endif + +# Target: runCheck (run the C++ or CUDA/HIP standalone executable check.exe, with a small number of events) +runCheck: all.$(TAG) +ifeq ($(GPUCC),) + $(RUNTIME) $(BUILDDIR)/check_cpp.exe -p 2 32 2 +else + $(RUNTIME) $(BUILDDIR)/check_$(GPUSUFFIX).exe -p 2 32 2 +endif + +# Target: runFcheck (run the Fortran standalone executable - with C++ or CUDA/HIP MEs - fcheck.exe, with a small number of events) +runFcheck: all.$(TAG) +ifeq ($(GPUCC),) + $(RUNTIME) $(BUILDDIR)/fcheck_cpp.exe 2 32 2 +else + $(RUNTIME) $(BUILDDIR)/fcheck_$(GPUSUFFIX).exe 2 32 2 +endif + +# Target: cmpFcheck (compare ME results from the C++/CUDA/HIP and Fortran with C++/CUDA/HIP MEs standalone executables, with a small number of events) +cmpFcheck: all.$(TAG) + @echo +ifeq ($(GPUCC),) + @echo "$(BUILDDIR)/check_cpp.exe --common -p 2 32 2" + @echo "$(BUILDDIR)/fcheck_cpp.exe 2 32 2" + @me1=$(shell $(RUNTIME) $(BUILDDIR)/check_cpp.exe --common -p 2 32 2 | grep MeanMatrix | awk '{print $$4}'); me2=$(shell $(RUNTIME) $(BUILDDIR)/fcheck_cpp.exe 2 32 2 | grep Average | awk '{print $$4}'); echo "Avg ME (C++/C++) = $${me1}"; echo "Avg ME (F77/C++) = $${me2}"; if [ "$${me2}" == "NaN" ]; then echo "ERROR! Fortran calculation (F77/C++) returned NaN"; elif [ "$${me2}" == "" ]; then echo "ERROR! Fortran calculation (F77/C++) crashed"; else python3 -c "me1=$${me1}; me2=$${me2}; reldif=abs((me2-me1)/me1); print('Relative difference =', reldif); ok = reldif <= 2E-4; print ( '%%s (relative difference %%s 2E-4)' %% ( ('OK','<=') if ok else ('ERROR','>') ) ); import sys; sys.exit(0 if ok else 1)"; fi +else + @echo "$(BUILDDIR)/check_$(GPUSUFFIX).exe --common -p 2 32 2" + @echo "$(BUILDDIR)/fcheck_$(GPUSUFFIX).exe 2 32 2" + @me1=$(shell $(RUNTIME) $(BUILDDIR)/check_$(GPUSUFFIX).exe --common -p 2 32 2 | grep MeanMatrix | awk '{print $$4}'); me2=$(shell $(RUNTIME) $(BUILDDIR)/fcheck_$(GPUSUFFIX).exe 2 32 2 | grep Average | awk '{print $$4}'); echo "Avg ME (C++/GPU) = $${me1}"; echo "Avg ME (F77/GPU) = $${me2}"; if [ "$${me2}" == "NaN" ]; then echo "ERROR! Fortran calculation (F77/GPU) crashed"; elif [ "$${me2}" == "" ]; then echo "ERROR! Fortran calculation (F77/GPU) crashed"; else python3 -c "me1=$${me1}; me2=$${me2}; reldif=abs((me2-me1)/me1); print('Relative difference =', reldif); ok = reldif <= 2E-4; print ( '%%s (relative difference %%s 2E-4)' %% ( ('OK','<=') if ok else ('ERROR','>') ) ); import sys; sys.exit(0 if ok else 1)"; fi +endif + +# Target: cuda-memcheck (run the CUDA standalone executable gcheck.exe with a small number of events through cuda-memcheck) +cuda-memcheck: all.$(TAG) + $(RUNTIME) $(CUDA_HOME)/bin/cuda-memcheck --check-api-memory-access yes --check-deprecated-instr yes --check-device-heap yes --demangle full --language c --leak-check full --racecheck-report all --report-api-errors all --show-backtrace yes --tool memcheck --track-unused-memory yes $(BUILDDIR)/check_$(GPUSUFFIX).exe -p 2 32 2 + +#------------------------------------------------------------------------------- diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/fbridge.h b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/fbridge.h new file mode 100644 index 000000000..fc83560b1 --- /dev/null +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/fbridge.h @@ -0,0 +1,51 @@ +// Copyright (C) 2020-2024 CERN and UCLouvain. +// Licensed under the GNU Lesser General Public License (version 3 or later). +// Created by: Z. Wettersten (Oct 2024) for the MG5aMC CUDACPP plugin. + +#include "Bridge.h" +#include "CPPProcess.h" +#include "GpuRuntime.h" + +#ifndef _FBRIDGE_H_ +#define _FBRIDGE_H_ + +extern "C" +{ +#ifdef MGONGPUCPP_GPUIMPL + using namespace mg5amcGpu; +#else + using namespace mg5amcCpu; +#endif + + using FORTRANFPTYPE = double; + + void fbridgecreate_( CppObjectInFortran** ppbridge, const int* pnevtF, const int* pnparF, const int* pnp4F ); + + void fbridgedelete_( CppObjectInFortran** ppbridge ); + + void fbridgesequence_( CppObjectInFortran** ppbridge, + const FORTRANFPTYPE* momenta, + const FORTRANFPTYPE* gs, + const FORTRANFPTYPE* rndhel, + const FORTRANFPTYPE* rndcol, + const unsigned int* channelIds, + FORTRANFPTYPE* mes, + int* selhel, + int* selcol, + const bool* pgoodHelOnly ); + + void fbridgesequence_nomultichannel_( CppObjectInFortran** ppbridge, + const FORTRANFPTYPE* momenta, + const FORTRANFPTYPE* gs, + const FORTRANFPTYPE* rndhel, + const FORTRANFPTYPE* rndcol, + FORTRANFPTYPE* mes, + int* selhel, + int* selcol, + const bool* pgoodHelOnly ); + + void fbridgegetngoodhel_( CppObjectInFortran** ppbridge, + unsigned int* pngoodhel, + unsigned int* pntothel ); +} +#endif // _FBRIDGE_H_ diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/process_cc.inc b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/process_cc.inc index 444c848e1..26959bf1e 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/process_cc.inc +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/madgraph/iolibs/template_files/gpu/process_cc.inc @@ -62,7 +62,8 @@ fpeEnable() //std::cout << "fpeEnable: FE_INVALID is" << ( ( fpes & FE_INVALID ) ? " " : " NOT " ) << "enabled" << std::endl; //std::cout << "fpeEnable: FE_OVERFLOW is" << ( ( fpes & FE_OVERFLOW ) ? " " : " NOT " ) << "enabled" << std::endl; //std::cout << "fpeEnable: FE_UNDERFLOW is" << ( ( fpes & FE_UNDERFLOW ) ? " " : " NOT " ) << "enabled" << std::endl; - constexpr bool enableFPE = true; // this is hardcoded and no longer controlled by getenv( "CUDACPP_RUNTIME_ENABLEFPE" ) + constexpr bool enableFPE = false; // this is hardcoded and no longer controlled by getenv( "CUDACPP_RUNTIME_ENABLEFPE" ) + // ZW: hardcode enableFPE to false if( enableFPE ) { std::cout << "INFO: The following Floating Point Exceptions will cause SIGFPE program aborts: FE_DIVBYZERO, FE_INVALID, FE_OVERFLOW" << std::endl; diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/model_handling.py b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/model_handling.py index 554c97974..78023d266 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/model_handling.py +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/model_handling.py @@ -1,7 +1,7 @@ # Copyright (C) 2020-2024 CERN and UCLouvain. # Licensed under the GNU Lesser General Public License (version 3 or later). # Created by: O. Mattelaer (Sep 2021) for the MG5aMC CUDACPP plugin. -# Further modified by: O. Mattelaer, J. Teig, A. Valassi (2021-2024) for the MG5aMC CUDACPP plugin. +# Further modified by: O. Mattelaer, J. Teig, A. Valassi, Z. Wettersten (2021-2024) for the MG5aMC CUDACPP plugin. import os import sys @@ -1174,9 +1174,12 @@ def get_process_class_definitions(self, write=True): replace_dict['noutcoming'] = nexternal - nincoming replace_dict['nbhel'] = self.matrix_elements[0].get_helicity_combinations() # number of helicity combinations replace_dict['ndiagrams'] = len(self.matrix_elements[0].get('diagrams')) # AV FIXME #910: elsewhere matrix_element.get('diagrams') and max(config[0]... - file = self.read_template_file(self.process_class_template) % replace_dict # HACK! ignore write=False case - file = '\n'.join( file.split('\n')[8:] ) # skip first 8 lines in process_class.inc (copyright) - return file + if( write ): # ZW: added dict return for uses in child exporters. Default argument is True so no need to modify other calls to this function + file = self.read_template_file(self.process_class_template) % replace_dict + file = '\n'.join( file.split('\n')[8:] ) # skip first 8 lines in process_class.inc (copyright) + return file + else: + return replace_dict # AV - replace export_cpp.OneProcessExporterGPU method (fix CPPProcess.cc) def get_process_function_definitions(self, write=True): diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py index 209f08831..f4f364177 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py @@ -117,7 +117,7 @@ class PLUGIN_ProcessExporter(PLUGIN_export_cpp.ProcessExporterGPU): s+'gpu/RandomNumberKernels.h', s+'gpu/CommonRandomNumberKernel.cc', s+'gpu/CurandRandomNumberKernel.cc', s+'gpu/HiprandRandomNumberKernel.cc', s+'gpu/Bridge.h', s+'gpu/BridgeKernels.cc', s+'gpu/BridgeKernels.h', - s+'gpu/fbridge.cc', s+'gpu/fbridge.inc', s+'gpu/fsampler.cc', s+'gpu/fsampler.inc', + s+'gpu/fbridge.cc', s+'gpu/fbridge.h', s+'gpu/fbridge.inc', s+'gpu/fsampler.cc', s+'gpu/fsampler.inc', s+'gpu/MadgraphTest.h', s+'gpu/runTest.cc', s+'gpu/testmisc.cc', s+'gpu/testxxx_cc_ref.txt', s+'gpu/valgrind.h', s+'gpu/perf.py', s+'gpu/profile.sh', @@ -140,7 +140,7 @@ class PLUGIN_ProcessExporter(PLUGIN_export_cpp.ProcessExporterGPU): 'RandomNumberKernels.h', 'CommonRandomNumberKernel.cc', 'CurandRandomNumberKernel.cc', 'HiprandRandomNumberKernel.cc', 'Bridge.h', 'BridgeKernels.cc', 'BridgeKernels.h', - 'fbridge.cc', 'fbridge.inc', 'fsampler.cc', 'fsampler.inc', + 'fbridge.cc', 'fbridge.h', 'fbridge.inc', 'fsampler.cc', 'fsampler.inc', 'MadgraphTest.h', 'runTest.cc', 'testmisc.cc', 'testxxx_cc_ref.txt', 'valgrind.h', 'cudacpp.mk', # this is generated from a template in Subprocesses but we still link it in P1 diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/trex.py b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/trex.py new file mode 100644 index 000000000..5f7c9bf7b --- /dev/null +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/trex.py @@ -0,0 +1,715 @@ +# Copyright (C) 2023-2024 CERN. +# Licensed under the GNU Lesser General Public License (version 3 or later). +# Created by: Z. Wettersten (Sep 2024) for the MG5aMC CUDACPP plugin. + +import os +import subprocess +import re +import sys +import importlib.util + + +# AV - PLUGIN_NAME can be one of PLUGIN/CUDACPP_OUTPUT or MG5aMC_PLUGIN/CUDACPP_OUTPUT +PLUGIN_NAME = __name__.rsplit('.',1)[0] + + +SPEC_EXPORTCPP = importlib.util.find_spec('madgraph.iolibs.export_cpp') +PLUGIN_export_cpp = importlib.util.module_from_spec(SPEC_EXPORTCPP) +SPEC_EXPORTCPP.loader.exec_module(PLUGIN_export_cpp) +sys.modules['%s.PLUGIN_export_cpp'%PLUGIN_NAME] = PLUGIN_export_cpp # allow 'import .PLUGIN_export_cpp' in model_handling.py +del SPEC_EXPORTCPP +###print('id(export_cpp)=%s'%id(export_cpp)) +###print('id(PLUGIN_export_cpp)=%s'%id(PLUGIN_export_cpp)) + +# AV - use template files from PLUGINDIR instead of MG5DIR +###from madgraph import MG5DIR +PLUGINDIR = os.path.dirname( __file__ ) + +# AV - model_handling includes the custom FileWriter, ALOHAWriter, UFOModelConverter, OneProcessExporter and HelasCallWriter, plus additional patches +#import PLUGIN.CUDACPP_OUTPUT.model_handling as model_handling +__import__('%s.model_handling'%PLUGIN_NAME) +model_handling = sys.modules['%s.model_handling'%PLUGIN_NAME] +#import PLUGIN.CUDACPP_OUTPUT.output as output +__import__('%s.output'%PLUGIN_NAME) +output = sys.modules['%s.output'%PLUGIN_NAME] + +# AV - create a plugin-specific logger +import logging +logger = logging.getLogger('madgraph.%s.output'%PLUGIN_NAME) +from madgraph import MG5DIR +#------------------------------------------------------------------------------------ + +from os.path import join as pjoin +import madgraph +import madgraph.iolibs.files as files +import madgraph.iolibs.export_v4 as export_v4 +import madgraph.various.misc as misc +import madgraph.interface.reweight_interface as rwgt_interface +import madgraph.various.banner as banner +import models.check_param_card as check_param_card +import madgraph.interface.extended_cmd as extended_cmd +import madgraph.interface.common_run_interface as common_run_interface + +from . import launch_plugin + +class TREX_OneProcessExporter(model_handling.PLUGIN_OneProcessExporter): + """A custom OneProcessExporter for the TREX reweighting""" + + rwgt_template = 'gpu/rwgt_runner.inc' + + # ZW - rwgt functions + def get_rwgt_legs(self, process): + """Return string with particle ids and status in the REX std::pair format""" + return ",".join(["{%i,%i}" % (leg.get('state'), leg.get('id')) \ + for leg in process.get('legs')]).replace('0', '-1') + + def get_rwgt_legs_vec(self, processes): + """Return string with vectors of particle ids and statuses""" + prtSets = [] + for k in range(len(processes)): + prtSets.append("{" + self.get_rwgt_legs(processes[k]) + "}") + return ",".join(prtSets) + + def get_init_prts_vec(self, process): + """Return string with initial state particle ids for use in REX event sorting""" + prts = ",".join(["%i" % leg.get('id') for leg in process.get('legs') if leg.get('state') == 0]) + return "{" + prts + "}" + + def get_init_prts_vecs(self, processes): + """Return string with vectors of initial state particle ids""" + prtSets = [] + for k in range(len(processes)): + prtSets.append(self.get_init_prts_vec(processes[k])) + return ",".join(prtSets) + + def get_fin_prts_vec(self, process): + """Return string with final state particle ids for use in REX event sorting""" + prts = ",".join(["%i" % leg.get('id') for leg in process.get('legs') if leg.get('state') == 1]) + return "{" + prts + "}" + + def get_fin_prts_vecs(self, processes): + """Return string with vectors of final state particle ids""" + prtSets = [] + for k in range(len(processes)): + prtSets.append(self.get_fin_prts_vec(processes[k])) + return ",".join(prtSets) + + def get_rwgt_procMap(self, process): + """Return string with particle states and order in the REX procMap format""" + currState = False + retString = "thisProc{{-1,{" + for leg in process.get('legs'): + if currState == leg.get('state'): + retString += "%i," % leg.get('id') + else: + currState = leg.get('state') + retString += "}},{1,{%i," % leg.get('id') + retString = retString[:-1] + "}}}" + return retString + + def get_proc_dir(self): + """Return process directory name for the current process""" + return "P%d_%s" % (self.process_number, self.process_name) + + def get_rwgt_runner(self): + """Return string to initialise the rwgtRunners in tRex""" + return "%s::runner" % (self.get_proc_dir()) + + def get_rwgt_includes(self): + """Return string with the include directives for the tRex reweighting""" + return "#include \"P%d_%s/rwgt_runner.cc\"" % (self.process_number, self.process_name) + + def write_rwgt_header(self): + """Writes a simple rwgt_runner.h file to forward declare the runner object""" + # Adjust the placeholders for use with `.format()` + rwgt_h = """#ifndef {namespace}_RWGT_RUNNER_H + #define {namespace}_RWGT_RUNNER_H + #include \"rwgt_instance.h\" + namespace {namespace} {{ + extern rwgt::instance runner; + }} + #endif""".format(namespace=self.get_proc_dir()) + + # Using `with` statement for better file handling + with open(os.path.join(self.path, 'rwgt_runner.h'), 'w') as ff: + ff.write(rwgt_h) + + def edit_rwgt_header(self): + """Adds process-specific details to the rwgt_runner.h template""" + replace_dict = super().get_process_class_definitions(write=False) + replace_dict['process_namespace'] = self.get_proc_dir() + replace_dict['info_lines'] = model_handling.PLUGIN_export_cpp.get_mg5_info_lines() + template = open(pjoin(self.template_path,'REX', 'rwgt_runner.h'),'r').read() + ff = open(pjoin(self.path, 'rwgt_runner.h'),'w') + ff.write(template % replace_dict) + ff.close() + + def edit_rwgt_runner(self): + """Create the rwgt_runner.cc file for the tRex reweighting""" + ###misc.sprint('Entering PLUGIN_OneProcessExporterRwgt.edit_rwgt_runner') + # Create the rwgt_runner.cc file +# replace_dict = {} + replace_dict = super().get_process_class_definitions(write=False) +# rwgt_runner = self.get_proc_dir() + self.rwgt_template + replace_dict['process_namespace'] = self.get_proc_dir() + replace_dict['info_lines'] = model_handling.PLUGIN_export_cpp.get_mg5_info_lines() + replace_dict['init_prt_ids'] = self.get_init_prts_vecs(self.matrix_elements[0].get('processes')) + replace_dict['fin_prt_ids'] = self.get_fin_prts_vecs(self.matrix_elements[0].get('processes')) + replace_dict['process_events'] = self.get_rwgt_legs_vec(self.matrix_elements[0].get('processes')) + replace_dict['no_events'] = len(self.matrix_elements[0].get('processes')) + template = open(pjoin(self.template_path,'REX', 'rwgt_runner.inc'),'r').read() + ff = open(pjoin(self.path, 'rwgt_runner.cc'),'w') + ff.write(template % replace_dict) + ff.close() + + # ZW - override the PLUGIN method to generate the rwgt_runner.cc file as well + # note: also generating standard check_sa.cc and gcheck_sa.cu files, which + # are not used in the REX reweighting + def generate_process_files(self): + """Generate mgOnGpuConfig.h, CPPProcess.cc, CPPProcess.h, check_sa.cc, gXXX.cu links""" + # misc.sprint('Entering RWGT_OneProcessExporter.generate_process_files') + super().generate_process_files() + # misc.sprint('Generating rwgt_runner files') + self.edit_rwgt_header() + self.edit_rwgt_runner() + # misc.sprint('Finished generating rwgt files') + +class TREX_ProcessExporter(output.PLUGIN_ProcessExporter): + + oneprocessclass = TREX_OneProcessExporter + + rwgt_names = [] + proc_lines = [] + + s = PLUGINDIR + '/madgraph/iolibs/template_files/' + from_template = dict(output.PLUGIN_ProcessExporter.from_template) + from_template['src'] = from_template['src'] + [s+'REX/REX.cc', s+'REX/teaREX.cc', + s+'REX/REX.h', s+'REX/teaREX.h', + s+'REX/rwgt_instance.h', s+'REX/rwgt_instance.cc'] + from_template['SubProcesses'] = from_template['SubProcesses'] + [s+'gpu/cudacpp_driver.mk', + s+'REX/rwgt_instance.h', s+'REX/REX.h', s+'REX/teaREX.h'] + + to_link_in_P = output.PLUGIN_ProcessExporter.to_link_in_P + ['rwgt_instance.h', 'REX.h', 'teaREX.h'] + + template_src_make = pjoin(PLUGINDIR, 'madgraph' ,'iolibs', 'template_files','gpu','cudacpp_rex_src.mk') + template_tst_make = pjoin(PLUGINDIR, 'madgraph', 'iolibs', 'template_files','gpu','cudacpp_test.mk') + template_Sub_make = pjoin(PLUGINDIR, 'madgraph', 'iolibs', 'template_files','gpu','cudacpp_runner.mk') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.template_path = PLUGINDIR + self.rwgt_names = [] + self.proc_lines = [] + + def generate_subprocess_directory(self, matrix_element, cpp_helas_call_writer, + proc_number=None): + """Generate the Pxxxxx directory for a subprocess in C++ standalone, + including the necessary .h and .cc files""" + + + process_exporter_cpp = self.oneprocessclass(matrix_element,cpp_helas_call_writer) + + self.rwgt_names.append("P%d_%s" % (process_exporter_cpp.process_number, + process_exporter_cpp.process_name)) + + process_lines = "\n".join([process_exporter_cpp.get_process_info_lines(me) for me in \ + process_exporter_cpp.matrix_elements]) + self.proc_lines.append(process_lines) + + # Create the directory PN_xx_xxxxx in the specified path + dirpath = pjoin(self.dir_path, 'SubProcesses', "P%d_%s" % (process_exporter_cpp.process_number, + process_exporter_cpp.process_name)) + try: + os.mkdir(dirpath) + except os.error as error: + logger.warning(error.strerror + " " + dirpath) + + with misc.chdir(dirpath): + logger.info('Creating files in directory %s' % dirpath) + process_exporter_cpp.path = dirpath + # Create the process .h and .cc files + process_exporter_cpp.generate_process_files() + for file in self.to_link_in_P: + files.ln('../%s' % file) + return + + def export_driver(self): + # misc.sprint("In export_driver") + # misc.sprint("Current working directory is: %s" % self.dir_path) + replace_dict = {} + replace_dict['info_lines'] = model_handling.PLUGIN_export_cpp.get_mg5_info_lines() + replace_dict['multiprocess_lines'] = "\n".join(self.proc_lines) + replace_dict['include_lines'] = '' + replace_dict['comparators'] = '' + #replace_dict['run_set'] = '' + replace_dict['fbridge_vec'] = '' + for name in self.rwgt_names: + replace_dict['include_lines'] += '#include "%s/rwgt_runner.h"\n' % name + # replace_dict['run_set'] += '%s::getEventSet(),' % name + replace_dict['comparators'] += '%s::getComp(),' % name + replace_dict['fbridge_vec'] += '%s::bridgeConstr(),' % name + #replace_dict['run_set'] = replace_dict['run_set'][:-1] + replace_dict['comparators'] = replace_dict['comparators'][:-1] + replace_dict['fbridge_vec'] = replace_dict['fbridge_vec'][:-1] + template_path = os.path.join( PLUGINDIR, 'madgraph', 'iolibs', 'template_files' ) + template = open(pjoin(template_path,'REX', 'rwgt_driver.inc'),'r').read() + ff = open(pjoin(self.dir_path, 'SubProcesses', 'rwgt_driver.cc'),'w') + ff.write(template % replace_dict) + ff.close() + + def link_makefile(self): + """Link the makefile for the tRex reweighting""" + files.ln(pjoin(self.dir_path, 'SubProcesses', 'cudacpp_driver.mk'), starting_dir=pjoin(self.dir_path, 'SubProcesses'), name='makefile') + + def finalize(self, matrix_element, cmdhistory, MG5options, outputflag): + self.export_driver() + self.link_makefile() + return super().finalize(matrix_element, cmdhistory, MG5options, outputflag) + +class TREX_ReweightInterface(rwgt_interface.ReweightInterface): + """A custom ReweightInterface for the tRex reweighting""" + + sa_class = 'standalone_trex' + + def __init__(self, *args, **kwargs): + """Initialise the tRex reweighting interface + Currently no (substantial) changes compared to upstream are necessary, + but adding an __init__ method allows for future modifications""" + super().__init__(*args, **kwargs) + self.debug_output = 'tRex_debug' + self.param_card = None + self.reweight_card = [] + self.reweight_names = [] + + def setup_f2py_interface(self): + """"Override native setup_f2py_interface to avoid parsing things not necessary for tRex reweighting""" + + self.create_standalone_directory() + self.compile() + + def launch_actual_reweighting(self, *args, **kwargs): + """override standard launch command to instead call the tRex reweighting""" + + import csv + + if self.rwgt_dir: + path_me =self.rwgt_dir + else: + path_me = self.me_dir + + if self.second_model or self.second_process or self.dedicated_path: + rw_dir = pjoin(path_me, 'rw_me_%s' % self.nb_library) + else: + rw_dir = pjoin(path_me, 'rw_me') + + run_path = pjoin(rw_dir, 'SubProcesses') + input_file = os.path.relpath(self.lhe_input.path, run_path) + output_file = input_file + 'rw' + output_path = self.lhe_input.path + 'rw' + param_card = pjoin(rw_dir, 'Cards', 'param_card.dat') + + #ZW: Exceptions, making sure all the necessary files for teaREX are accessible + if( misc.is_executable(pjoin(run_path,'rwgt_driver_gpu.exe')) ): + driver = pjoin(run_path, 'rwgt_driver_gpu.exe') + elif(misc.is_executable(pjoin(run_path,'rwgt_driver_cpp.exe')) ): + driver = pjoin(run_path,'rwgt_driver_cpp.exe') + else: + raise Exception('No tRex driver found for parallel reweighting') + if not os.path.exists(param_card): + try: + files.cp(os.path.join(path_me, 'Cards', 'param_card_default.dat'), param_card) + except: + raise Exception("No param_card.dat file found in %s" % pjoin(path_me, 'Cards')) + param_path = os.path.relpath(param_card, run_path) + + rwgt_card = os.path.join(path_me, 'Cards', 'reweight_card.dat') + + self.write_reweight_card(rwgt_card) + + if not os.path.exists(rwgt_card): + try: + files.cp(os.path.join(path_me, 'Cards', 'reweight_card_default.dat'), rwgt_card) + except: + raise Exception("No reweight_card.dat file found in %s" % pjoin(path_me, 'Cards')) + rwgt_path = os.path.relpath(rwgt_card, run_path) + target = '' + if not self.mother: + name, ext = self.lhe_input.name.rsplit('.',1) + target = '%s_out.%s' % (name, ext) + elif self.output_type != "default" : + target = pjoin(self.mother.me_dir, 'Events', self.mother.run_name, 'events.lhe') + else: + target = self.lhe_input.path + + #ZW: rwgt_driver is written and compiled properly, now just to figure out how to run it through MG + subprocess.call([driver, '-lhe=%s' % input_file, '-slha=%s' % param_card, '-rwgt=%s' % rwgt_card, '-out=%s' % output_file], cwd=run_path) + + # ZW: check if output exists, if not nicely raise an exception + if not os.path.exists(output_path): + if os.path.exists(target): + files.mv(self.lhe_input.path, target) + logger.info('Error in reweighting: output file not found. Returning original LHE file.') + return + else: + raise Exception('Error in reweighting: output file not found. Input file not found. Exiting.') + else: + files.mv(output_path, target) + csv_file = pjoin(run_path, 'rwgt_results.csv') + with open(csv_file, newline='') as results: + iters = csv.reader(results) + for row in iters: + self.all_cross_section[(row[0],'')] = (float(row[1]), float(row[2])) + + return + + def compile(self): + """override compile to use the TREX makefiles""" + + if self.multicore=='wait': + return + + if not self.rwgt_dir: + path_me = self.me_dir + else: + path_me = self.rwgt_dir + + rwgt_dir_possibility = ['rw_me','rw_me_%s' % self.nb_library,'rw_mevirt','rw_mevirt_%s' % self.nb_library] + for onedir in rwgt_dir_possibility: + if not os.path.isdir(pjoin(path_me,onedir)): + continue + pdir = pjoin(path_me, onedir, 'SubProcesses') + if self.mother: + nb_core = self.mother.options['nb_core'] if self.mother.options['run_mode'] !=0 else 1 + else: + nb_core = 1 + misc.compile(cwd=pdir, nb_core=nb_core,mode='cpp') + return + + def load_module(self): + """override load_module since we do not use it""" + return + + # def import_command_file(self, filepath): + # """override import_command_file to simply launch TREX""" + # self.exec_cmd('launch', precmd=True) + # return + + def do_launch(self, line): + """override do_launch to instead overwrite the reweight_card + to fit the expected input for TREX without having to extend TREX itself""" + args = self.split_arg(line) + opts = self.check_launch(args) + mgcmd = self.mg5cmd + if opts['rwgt_name']: + self.options['rwgt_name'] = opts['rwgt_name'] + if opts['rwgt_info']: + self.options['rwgt_info'] = opts['rwgt_info'] + model_line = self.banner.get('proc_card', 'full_model_line') + + # TV: Load model: needed for the combine_ij function: maybe not needed everyt time??? + model = self.banner.get('proc_card', 'model') + self.load_model( model, True, False) + + if not self.has_standalone_dir: + out = self.setup_f2py_interface() + if out: + return + + if not self.param_card: + s_orig = self.banner['slha'] + self.param_card = check_param_card.ParamCard(s_orig.splitlines()) + + # get the mode of reweighting #LO/NLO/NLO_tree/... + type_rwgt = self.get_weight_names() + + if self.rwgt_dir: + path_me =self.rwgt_dir + else: + path_me = self.me_dir + + + # get iterator over param_card and the name associated to the current reweighting. + param_card_iterator, tag_name = self.handle_param_card(model_line, args, type_rwgt) + + self.reweight_names.append(tag_name) + + # perform the scanning + if param_card_iterator: + if self.options['rwgt_name']: + reweight_name = self.options['rwgt_name'].rsplit('_',1)[0] # to avoid side effect during the scan + else: + reweight_name = None + for i,card in enumerate(param_card_iterator): + if reweight_name: + self.options['rwgt_name'] = '%s_%s' % (reweight_name, i+1) + self.new_param_card = card + #card.write(pjoin(rw_dir, 'Cards', 'param_card.dat')) + self.exec_cmd("launch --keep_card", printcmd=False, precmd=True) + + def check_multicore(self): + """override check_multicore to overloading the CPU (we never want to run TREX in multicore mode)""" + return False + + def handle_param_card(self, model_line, args, type_rwgt): + """override handle_param_card to get rid of all the unnecessary checks and file writing + now simply loads the param_card and uses get_diff to tranlate into internal format""" + + if self.rwgt_dir: + path_me =self.rwgt_dir + else: + path_me = self.me_dir + + if self.second_model or self.second_process or self.dedicated_path: + rw_dir = pjoin(path_me, 'rw_me_%s' % self.nb_library) + else: + rw_dir = pjoin(path_me, 'rw_me') + if not '--keep_card' in args: + if self.has_nlo and self.rwgt_mode != "LO": + rwdir_virt = rw_dir.replace('rw_me', 'rw_mevirt') + with open(pjoin(rw_dir, 'Cards', 'param_card.dat'), 'w') as fsock: + fsock.write(self.banner['slha']) + out, cmd = common_run_interface.CommonRunCmd.ask_edit_card_static(cards=['param_card.dat'], + ask=self.ask, pwd=rw_dir, first_cmd=self.stored_line, + write_file=False, return_instance=True + ) + self.stored_line = None + card = cmd.param_card + new_card = card.write() + elif self.new_param_card: + new_card = self.new_param_card.write() + else: + new_card = open(pjoin(rw_dir, 'Cards', 'param_card.dat')).read() + + # check for potential scan in the new card + pattern_scan = re.compile(r'''^(decay)?[\s\d]*scan''', re.I+re.M) + param_card_iterator = [] + if pattern_scan.search(new_card): + import madgraph.interface.extended_cmd as extended_cmd + try: + import internal.extended_cmd as extended_internal + Shell_internal = extended_internal.CmdShell + except: + Shell_internal = None + if not isinstance(self.mother, (extended_cmd.CmdShell, Shell_internal)): + raise Exception("scan are not allowed on the Web") + # at least one scan parameter found. create an iterator to go trough the cards + main_card = check_param_card.ParamCardIterator(new_card) + if self.options['rwgt_name']: + self.options['rwgt_name'] = '%s_0' % self.options['rwgt_name'] + + param_card_iterator = main_card + first_card = param_card_iterator.next(autostart=True) + new_card = first_card.write() + self.new_param_card = first_card + #first_card.write(pjoin(rw_dir, 'Cards', 'param_card.dat')) + + # check if "Auto" is present for a width parameter) + if 'block' not in new_card.lower(): + raise Exception(str(new_card)) + tmp_card = new_card.lower().split('block',1)[1] + if "auto" in tmp_card: + if param_card_iterator: + first_card.write(pjoin(rw_dir, 'Cards', 'param_card.dat')) + else: + ff = open(pjoin(rw_dir, 'Cards', 'param_card.dat'),'w') + ff.write(new_card) + ff.close() + + self.mother.check_param_card(pjoin(rw_dir, 'Cards', 'param_card.dat')) + new_card = open(pjoin(rw_dir, 'Cards', 'param_card.dat')).read() + + + # Find new tag in the banner and add information if needed + if 'initrwgt' in self.banner and self.output_type == 'default': + if 'name=\'mg_reweighting\'' in self.banner['initrwgt']: + blockpat = re.compile(r'''(?P.*?)''', re.I+re.M+re.S) + before, content, after = blockpat.split(self.banner['initrwgt']) + header_rwgt_other = before + after + pattern = re.compile('\\d+)|(?P[_\\w\\-\\.]+))(?P\\s*|_\\w+)\'>(?P.*?)', re.S+re.I+re.M) + mg_rwgt_info = pattern.findall(content) + maxid = 0 + for k,(i, fulltag, nlotype, diff) in enumerate(mg_rwgt_info): + if i: + if int(i) > maxid: + maxid = int(i) + mg_rwgt_info[k] = (i, nlotype, diff) # remove the pointless fulltag tag + else: + mg_rwgt_info[k] = (fulltag, nlotype, diff) # remove the pointless id tag + + maxid += 1 + rewgtid = maxid + if self.options['rwgt_name']: + #ensure that the entry is not already define if so overwrites it + for (i, nlotype, diff) in mg_rwgt_info[:]: + for flag in type_rwgt: + if 'rwgt_%s' % i == '%s%s' %(self.options['rwgt_name'],flag) or \ + i == '%s%s' % (self.options['rwgt_name'], flag): + logger.warning("tag %s%s already defines, will replace it", self.options['rwgt_name'],flag) + mg_rwgt_info.remove((i, nlotype, diff)) + + else: + header_rwgt_other = self.banner['initrwgt'] + mg_rwgt_info = [] + rewgtid = 1 + else: + self.banner['initrwgt'] = '' + header_rwgt_other = '' + mg_rwgt_info = [] + rewgtid = 1 + + # add the reweighting in the banner information: + #starts by computing the difference in the cards. + #s_orig = self.banner['slha'] + #self.orig_param_card_text = s_orig + s_new = new_card + self.new_param_card = check_param_card.ParamCard(s_new.splitlines()) + + #define tag for the run + if self.options['rwgt_name']: + tag = self.options['rwgt_name'] + else: + tag = str(rewgtid) + + if 'rwgt_info' in self.options and self.options['rwgt_info']: + card_diff = self.options['rwgt_info'] + for name in type_rwgt: + mg_rwgt_info.append((tag, name, self.options['rwgt_info'])) + elif not self.second_model and not self.dedicated_path: + old_param = self.param_card + new_param = self.new_param_card + card_diff = old_param.create_diff(new_param) + if card_diff == '' and not self.second_process: + logger.warning(' REWEIGHTING: original card and new card are identical.') + try: + if old_param['sminputs'].get(3)- new_param['sminputs'].get(3) > 1e-3 * new_param['sminputs'].get(3): + logger.warning("We found different value of alpha_s. Note that the value of alpha_s used is the one associate with the event and not the one from the cards.") + except Exception as error: + logger.debug("error in check of alphas: %s" % str(error)) + pass #this is a security + if not self.second_process: + for name in type_rwgt: + mg_rwgt_info.append((tag, name, card_diff)) + else: + str_proc = "\n change process ".join([""]+self.second_process) + for name in type_rwgt: + mg_rwgt_info.append((tag, name, str_proc + '\n'+ card_diff)) + else: + if self.second_model: + str_info = "change model %s" % self.second_model + else: + str_info ='' + if self.second_process: + str_info += "\n change process ".join([""]+self.second_process) + if self.dedicated_path: + for k,v in self.dedicated_path.items(): + str_info += "\n change %s %s" % (k,v) + card_diff = str_info + str_info += '\n' + s_new + for name in type_rwgt: + mg_rwgt_info.append((tag, name, str_info)) + + # re-create the banner. + self.banner['initrwgt'] = header_rwgt_other + if self.output_type == 'default': + self.banner['initrwgt'] += '\n\n' + else: + self.banner['initrwgt'] += '\n\n' + for tag, rwgttype, diff in mg_rwgt_info: + if self.inc_sudakov: + try: + sud_order = int(rwgttype[-1]) -1 + sud_order = '10' +rwgttype[-2:] + self.banner['initrwgt'] += '%sscale_%s_sud\n' % \ + (rwgttype, diff, sud_order) + except IndexError: + logger.critical('This is a reweighted event file! Do not reweight with ewsudakov twice') + sys.exit(1) + else: + if tag.isdigit(): + self.banner['initrwgt'] += '%s\n' % \ + (tag, rwgttype, diff) + else: + self.banner['initrwgt'] += '%s\n' % \ + (tag, rwgttype, diff) + self.banner['initrwgt'] += '\n\n' + self.banner['initrwgt'] = self.banner['initrwgt'].replace('\n\n', '\n') + + #logger.info('starts to compute weight for events with the following modification to the param_card:') + #logger.info(card_diff.replace('\n','\nKEEP:')) + try: + self.run_card = banner.Banner(self.banner).charge_card('run_card') + except Exception: + logger.debug('no run card found -- reweight interface') + self.run_card = None + + if self.options['rwgt_name']: + tag_name = self.options['rwgt_name'] + else: + tag_name = 'rwgt_%s' % rewgtid + + self.reweight_card.append(card_diff) + + return param_card_iterator, tag_name + + def write_reweight_card(self,rwgt_path): + """function for collecting all the reweight iterations from the parsed reweight card + and write it out with the explicit 'set BLOCK PARAM VALUE' format""" + if( len(self.reweight_names) != len(self.reweight_card) ): + raise Exception('Mismatch in number of reweight names and reweight cards') + + output_card = '' + + for i, card in enumerate(self.reweight_card): + output_card += 'launch --rwgt_name=%s\n' % self.reweight_names[i] + output_card += card + '\n' + + output_card = output_card.replace('param_card', '').replace(' ', ' ') + + with open(rwgt_path, 'w') as f: + f.write(output_card) + + return + + def do_quit(self, line): + if self.exitted: + return + try: + self.launch_actual_reweighting() + except: + raise Exception("Error in tRex reweighting. Exiting.") + return + + self.exitted = True + + if 'init' in self.banner: + cross = 0 + error = 0 + for line in self.banner['init'].split('\n'): + split = line.split() + if len(split) == 4: + cross += float(split[0]) + error += float(split[1])**2 + error = error**0.5 + if not self.multicore == 'create': + # No print of results for the multicore mode for the one printed on screen + if 'orig' not in self.all_cross_section: + logger.info('Original cross-section: %s +- %s pb' % (cross, error)) + else: + logger.info('Original cross-section: %s +- %s pb (cross-section from sum of weights: %s)' % (cross, error, self.all_cross_section['orig'][0])) + logger.info('Computed cross-section:') + keys = list(self.all_cross_section.keys()) + keys.sort(key=lambda x: str(x)) + for key in keys: + if key == 'orig': + continue + logger.info('%s : %s +- %s pb' % (key[0] if not key[1] else '%s%s' % key, + self.all_cross_section[key][0],self.all_cross_section[key][1] )) + self.terminate_fortran_executables() + + if self.rwgt_dir and self.multicore == False: + self.save_to_pickle() + + with misc.stdchannel_redirected(sys.stdout, os.devnull): + for run_id in self.calculator: + del self.calculator[run_id] + del self.calculator diff --git a/tools/REX/REX.cc b/tools/REX/REX.cc new file mode 100644 index 000000000..4ca91af55 --- /dev/null +++ b/tools/REX/REX.cc @@ -0,0 +1,4321 @@ +/*** + * ______ + * | ___ \ + * | |_/ /_____ __ + * | // _ \ \/ / + * | |\ \ __/> < + * \_| \_\___/_/\_\ + * + ***/ +// +// *R*apid *E*vent e*X*traction Version 0.9.0 +// Rex is a C++ library for parsing and manipulating Les Houches Event-format (LHE) files. +// It is designed to fast and lightweight, in comparison to internal parsers in programs like MadGraph. +// Currently, Rex is in development and may not contain all features necessary for full LHE parsing; +// particularly, it can only parse existing LHE files, rather than writing completely new ones. +// +// Copyright © 2023-2024 CERN, CERN Author Zenny Wettersten. +// Licensed under the GNU Lesser General Public License (version 3 or later). +// All rights not expressly granted are reserved. +// + +#ifndef _REX_CC_ +#define _REX_CC_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "REX.h" +#include + +// ZW: all fcns within the REX standard sit in the +// namespace REX +// Note that as a convention, std::string_view objects will be +// referred to as strings unless the difference is relevant +namespace REX +{ + // ZW: generic warning function for printing warnings without throwing anything + void warning( std::string message ){ + std::cout << "\n\033[1;33mWarning: "; + std::cout << message; + std::cout << "\033[0m\n"; + } + + // ZW: index sorting function, which returns vector + // of the indices of the original vector sorted + // by default in ascending order + // ie, for [5.0, 0.25, 2.0, 9.2] returns [1, 2, 0, 3] + template + std::shared_ptr> indSort(const std::vector &vector, std::function comp ) + { + auto sorted = std::make_shared>(vector.size()); + std::iota(sorted->begin(), sorted->end(), 0); + std::stable_sort(sorted->begin(), sorted->end(), [&](size_t i, size_t j) { return comp(vector[i], vector[j]); }); + return sorted; + } + template std::shared_ptr> indSort(const std::vector &vector, std::function comp ); + template std::shared_ptr> indSort(const std::vector &vector, std::function comp ); + + // ZW: generic fcn for converting string-like objects to integers + // Assumes input has no leading blankspace to check for a leading +, + // but should not fail as long as there is no + even if there is blankspace + // Note that Str needs to have a .compare(), .data() and .size() method + template + int ctoi( Str str ) + { + int ret; + if ( str.compare(0, 1, "+") == 0 ){ str = str.substr(1); } + auto result = std::from_chars( str.data(), str.data() + str.size(), ret ); + if( result.ec != std::errc() ){ throw std::invalid_argument("Invalid string-like object to convert to int"); } + return ret; + } + template int ctoi( std::string str ); + template int ctoi( std::string_view str ); + + // ZW: generic fcn for converting string-like objects to doubles + // Assumes input has no leading blankspace to check for a leading +, + // but should not fail as long as there is no + even if there is blankspace + // Note that Str needs to have a .compare(), .data() and .size() method + template + double ctod( Str str ) + { + double ret; + if ( str.compare(0, 1, "+") == 0 ){ str = str.substr(1); } + auto result = std::from_chars( str.data(), str.data() + str.size(), ret ); + if( result.ec != std::errc() ){ throw std::invalid_argument("Invalid string-like object to convert to double"); } + return ret; + } + template double ctod( std::string str ); + template double ctod( std::string_view str ); + + // ZW: wrapper for indSort for comparing string-type arguments representing integers + template + std::shared_ptr> stoiSort(const std::vector &vector) + { + std::function stoicomp = [](const T& i, const T& j) { + return std::stoi(std::string(i)) < std::stoi(std::string(j)); }; + return indSort(vector, stoicomp); + } + template std::shared_ptr> stoiSort(const std::vector &vector); + + // ZW: wrapper for indSort for comparing string-type arguments representing doubles + template + std::shared_ptr> stodSort(const std::vector &vector) + { + std::function stodcomp = [](const T& i, const T& j) { return std::stod(std::string(i)) < std::stod(std::string(j)); }; + return indSort(vector, stodcomp); + } + + // ZW: templated fcn for finding the order of elements in a vector to_sort + // based on their order in a reference vector reference + // Elements not found in reference are represented by npos, + // including if to_sort is longer than reference + // Note: If reference is longer than to_sort, the ordering does not + // respect the length of to_sort (may yield out_of_bounds errors or unexpected results) + template + std::shared_ptr> getRefOrder(const std::vector& reference, const std::vector& to_sort) { + std::unordered_map> indexMap; + + // Populate indexMap with indices from vec1 + for (size_t i = 0; i < reference.size(); ++i) { + indexMap[reference[i]].push(i); + } + + auto order = std::make_shared>(std::vector(to_sort.size(), npos)); + size_t pos = 0; + for (const auto& elem : to_sort) { + auto it = indexMap.find(elem); + if (it != indexMap.end() && !it->second.empty()) { + order->at(pos) = (it->second.front()); + it->second.pop(); + } else { + // Element in to_sort not found in reference + order->at(pos) = npos; + } + ++pos; + } + + return order; + } + template std::shared_ptr> getRefOrder(const std::vector& reference, const std::vector& to_sort); + template std::shared_ptr> getRefOrder(const std::vector& reference, const std::vector& to_sort); + + // ZW: minimal fcn for counting the amount of times + // a given searchTerm appears in searchString + int strCount( std::string_view searchString, std::string_view searchTerm ) + { + int count = 0; + size_t pos = 0; + while((pos = searchString.find(searchTerm, pos)) != npos ){ + ++count; + ++pos; + } + return count; + } + + // ZW: fcn for finding the location of each + // entry of seachTerm in the given string textFile + std::shared_ptr> findEach( std::string_view textFile, std::string_view searchTerm ) + { + auto eachPos = std::make_shared>(); + //eachPos->reserve( strCount(textFile, searchTerm) ); + eachPos->push_back( textFile.find( searchTerm ) ); + if( eachPos->at(0) == npos ){ return eachPos; } + size_t currPos = textFile.find( searchTerm, eachPos->at(0) + 1 ); + while( currPos != npos ) + { + eachPos->push_back( currPos ); + currPos = textFile.find( searchTerm, currPos + 1 ); + } + return eachPos; + } + + // ZW: fcn for splitting a string into a vector of strings, + // each element differentiated by linebreaks in the original string + // Removes sequential linebreaks, as well as lines with only whitespace + std::shared_ptr> lineSplitter( std::string_view currEvt ) + { + auto lineBreaks = findEach( currEvt, "\n" ); + auto splitLines = std::make_shared>(); + if( lineBreaks->at(0) == npos ){ splitLines->push_back( currEvt ); return splitLines; } + splitLines->reserve( lineBreaks->size() ); + auto strtLine = currEvt.substr( 0, lineBreaks->at(0) ); + if( strtLine.size() > 0 ){ splitLines->push_back( strtLine ); } + for( size_t k = 0 ; k < lineBreaks->size() - 1 ; ++k ) + { + auto strtPos = currEvt.substr( lineBreaks->at(k), lineBreaks->at(k+1) - lineBreaks->at(k) ).find_first_not_of(" \n\r\f\t\v"); + if( strtPos == npos ){ continue; } + splitLines->push_back( currEvt.substr( lineBreaks->at(k) + 1, lineBreaks->at(k+1) - lineBreaks->at(k) - 1 ) ); + } + size_t nuStrtPs = currEvt.substr( lineBreaks->at( lineBreaks->size() - 1 ) ).find_first_not_of(" \n\r\f\t\v"); + if( nuStrtPs != npos ) + { + size_t endPs = currEvt.find_last_not_of(" \n\r\f\t\v"); + splitLines->push_back( currEvt.substr( nuStrtPs, endPs - nuStrtPs ) ); + } + return splitLines; + } + + // ZW: fcn for finding each linebreak in a string, + // returning a vector of the positions of "\n" characters + // Ignores sequential linebreaks, ie would only return { } + // for the string "\n\n\n\n" + std::shared_ptr> lineFinder( std::string_view currEvt, size_t startPos = 0, size_t endPos = npos ) + { + auto lineBreaks = findEach( currEvt.substr( startPos, endPos - startPos), "\n" ); + auto truBreaks = std::make_shared>(); + truBreaks->reserve( lineBreaks->size() ); + for( size_t k = 0 ; k < lineBreaks->size() ; ++k ) + { + if( int( (*lineBreaks)[k+1] - (*lineBreaks)[k]) == 1){continue;} + truBreaks->push_back( (*lineBreaks)[k] ); + } + return truBreaks; + } + + // ZW: fcn for splitting a string into a vector of strings, + // elements separated by any form of blankspace in the original string + // Ignores sequential blankspaces of all forms + std::shared_ptr> blankSplitter( std::string_view currEvt ) + { + auto strtPos = currEvt.find_first_not_of(" \n\r\f\t\v"); + auto splitString = std::make_shared>(); + if( strtPos == npos ){ splitString->push_back( currEvt ); return splitString; } + auto endPos = currEvt.find_first_of(" \n\r\f\t\v", strtPos); + while( strtPos != npos ) + { + if( endPos == npos ){ splitString->push_back( currEvt.substr( strtPos ) ); break; } + splitString->push_back( currEvt.substr( strtPos, endPos - strtPos ) ); + strtPos = currEvt.find_first_not_of(" \n\r\f\t\v", endPos); + endPos = currEvt.find_first_of(" \n\r\f\t\v", strtPos); + } + return splitString; + } + + // ZW: caseless string comparison + // not templated --- had some issues with instantiation + bool clStringComp( std::string_view org, std::string comp ){ + return std::equal( org.begin(), org.end(), comp.begin(), comp.end(), + []( const char& x, char y ){ return (std::toupper(x) == std::toupper(y)); } ); + } + bool clStringComp( std::string_view org, std::string_view comp ){ + return std::equal( org.begin(), org.end(), comp.begin(), comp.end(), + []( const char& x, char y ){ return (std::toupper(x) == std::toupper(y)); } ); + } + bool clStringComp( std::string org, std::string_view comp ){ + return std::equal( org.begin(), org.end(), comp.begin(), comp.end(), + []( const char& x, char y ){ return (std::toupper(x) == std::toupper(y)); } ); + } + bool clStringComp( std::string org, std::string comp ){ + return std::equal( org.begin(), org.end(), comp.begin(), comp.end(), + []( const char& x, char y ){ return (std::toupper(x) == std::toupper(y)); } ); + } + + // ZW: templated fcn for finding a caseless substring searchTerm in srcFile + // On failure to find searchTerm, returns REX::npos + template + size_t clStringFind( const Str1& srcFile, const Str2& searchTerm, size_t strtPt = 0 ){ + size_t strLen = searchTerm.size(); + if( srcFile.size() == 0 || srcFile.size() < strLen ){ return npos; } + for( size_t k = strtPt ; k < srcFile.size() - strLen; ++k ) + { + if( clStringComp( srcFile.substr(k, strLen), searchTerm ) ){ return k; } + } + return npos; + } + + // ZW: templated fcn for finding a caseless substring searchTerm of srcFile + // fulfilling a particular predicate cond( size_t, string ) + template + size_t clStringFindIf( const Str1& srcFile, const Str2& searchTerm, std::function& cond, size_t strtPt = 0 ) + { + auto currPt = clStringFind( srcFile, searchTerm, strtPt ); + bool condStat = cond( currPt, srcFile ); + while( !( condStat ) && currPt != npos) + { + currPt = clStringFind( srcFile, searchTerm, currPt + 1 ); + condStat = cond( currPt, srcFile ); + } + return currPt; + } + + // ZW: templated fcn for counting the number of occurances of + // caseless substring searchTerm in string-like object srcFile + template + int clStrCount( Str1 srcFile, Str2 searchTerm ) + { + int count = 0; + size_t pos = 0; + while((pos = clStringFind( srcFile, searchTerm, pos ) ) != npos ){ + ++count; + ++pos; + } + return count; + } + + // ZW: templated fcn for finding each instance of + // of substring searchTerm of string-like object srcFile + template + std::shared_ptr> clFindEach( Str1 srcFile, Str2 searchTerm ) + { + auto eachPos = std::make_shared>(); + auto nos = clStrCount(srcFile, searchTerm); + if( nos == 0 ){ return eachPos; } + eachPos->reserve( nos ); + eachPos->push_back( clStringFind( srcFile, searchTerm ) ); + size_t currPos = clStringFind( srcFile, searchTerm, eachPos->at(0) + 1); + while( currPos != npos ) + { + eachPos->push_back( currPos ); + currPos = clStringFind( srcFile, searchTerm, currPos + 1 ); + } + return eachPos; + } + + // ZW: fcn for finding left angle bracket + // indicating the start of a new node in an XML file + size_t nodeStartFind( std::string_view parseFile, size_t strtPos ) + { + auto retPtr = parseFile.find("<", strtPos); + while( parseFile[retPtr + 1] == '!' || parseFile[retPtr +1] == '/' || parseFile[retPtr +1] == '?' ){ + retPtr = parseFile.find("<", retPtr +1); + } + return retPtr; + } + + size_t endNodeStartFind( std::string_view parseFile, size_t strtPos ) + { + return parseFile.find(">", nodeStartFind( parseFile, strtPos )); + } + + std::pair startNodePts( std::string_view parseFile, size_t strtPos ) + { + return { nodeStartFind( parseFile, strtPos ), endNodeStartFind( parseFile, strtPos ) }; + } + + // ZW: fcn for finding left angle bracket + // indicating an end of a node in an XML file + size_t nodeEndFind( std::string_view parseFile, size_t strtPos ) + { + auto retPtr = parseFile.find("<", strtPos); + while( parseFile[retPtr + 1] != '/' && retPtr != npos ){ + retPtr = parseFile.find("<", retPtr +1); + } + return retPtr; + } + + size_t endNodeEndFind( std::string_view parseFile, size_t strtPos ) + { + return parseFile.find(">", nodeEndFind( parseFile, strtPos )); + } + + std::pair endNodePts( std::string_view parseFile, size_t strtPos ) + { + return { nodeEndFind( parseFile, strtPos ), endNodeEndFind( parseFile, strtPos ) }; + } + + // ZW: struct for handling tags in XML node opening tags + void xmlTag::setVal( std::string_view valSet ){ modded = true; val = valSet; } + void xmlTag::setId( std::string_view idSet ){ modded = true; id = idSet; } + std::string_view xmlTag::getVal(){ return val; } + std::string_view xmlTag::getId(){ return id; } + bool xmlTag::isModded(){ return modded; } + xmlTag::xmlTag(){ modded = false; return; } + xmlTag::xmlTag( xmlTag& oldTag ){ + modded = false; val = oldTag.getVal(); id = oldTag.getId(); + } + xmlTag::xmlTag( std::string_view initId, std::string_view initVal){ + modded = false; val = initVal; id = initId; + } + + // ZW: function for parsing XML opening + // tags and returning the next header tag + std::shared_ptr xmlTagParser( std::string_view tagLine, size_t& equPt ) + { + auto tagBreaker = tagLine.find_first_not_of(" ", equPt+1); // ZW: need to determine what type of quotation marks are used + auto tagEnder = tagLine.find( tagLine[tagBreaker], tagBreaker+1); + auto attrEnd = tagLine.find_last_not_of(" ", equPt - 1) ; + auto attrStart = tagLine.find_last_of(" ", attrEnd) + 1; + auto tagPtr = std::make_shared(tagLine.substr(attrStart, attrEnd - attrStart + 1), tagLine.substr(tagBreaker + 1, tagEnder - tagBreaker - 1)); + equPt = tagLine.find("=", equPt + 1); // ZW: modifies input equPt to point to the next equality sign in tagLine + return tagPtr; + } + + // ZW: struct for handling the tree structure of XML files, + // essentially just giving the positions of the beginning and + // end of each node s.t. the proper node structures can accurately + // detail where children begin and end while allowing for personal + // content between child nodes + xmlTree::xmlTree(){ return; } + xmlTree::xmlTree( std::string_view file ){ + origin = file; + children = std::make_shared>>(); + start = file.find_first_not_of(" \n\r\f\t\v"); + if( file.compare(start, 1, "<") != 0 ) { + faux = true; + contSt = start; + end = std::min( nodeStartFind(file, start), nodeEndFind(file, start) ); + contEnd = end; + initialised = true; + return; + } + if( file.compare(start + 1, 1, "!") == 0 || file.compare(start + 1, 1, "?") == 0 ) { + faux = true; + contSt = start; + contEnd = file.find(">", start + 1); + end = std::min( nodeStartFind(file, contEnd), nodeEndFind(file, contEnd) ); + initialised = true; + return; + } + auto stEnd = file.find(">", start); + if( file.compare(stEnd - 1, 1, "/" ) == 0 ) { // ZW: self-closing tag (assuming no whitespace between / and >) + end = file.find_first_not_of(" \n\r\f\t\v", stEnd + 1); + contSt = npos; + contEnd = npos; + initialised = true; + return; + } + contSt = stEnd + 1; + auto stPos = nodeStartFind(file, start + 1); + stEnd = nodeEndFind(file, start + 1); + contEnd = std::min(stPos, stEnd); + while( stPos < stEnd ) + { + children->push_back( std::make_shared( file, stPos, stEnd ) ); + } + stEnd = endNodeEndFind(file, stEnd); + end = file.find_first_not_of(" \n\r\f\t\v", stEnd + 1); + initialised = true; + } + xmlTree::xmlTree( std::string_view file, size_t& strt, size_t& nd ){ + origin = file; + children = std::make_shared>>(); + start = file.find_first_not_of(" \n\r\f\t\v", strt); + if( file.compare(start, 1, "<") != 0) { + faux = true; + contSt = start; + strt = nodeStartFind(file, start); + nd = nodeEndFind(file, start); + end = std::min( strt, nd ); + contEnd = end; + initialised = true; + return; + } + if( file.compare(start + 1, 1, "!") == 0 ) { + faux = true; + contSt = start; + contEnd = file.find(">", start + 1); + strt = nodeStartFind(file, contEnd); + nd = nodeEndFind(file, contEnd); + end = std::min( strt, nd ); + initialised = true; + return; + } + auto stEnd = file.find(">", start); + if( file.compare(stEnd - 1, 1, "/" ) == 0 ) { + end = file.find_first_not_of(" \n\r\f\t\v", stEnd + 1); + contSt = npos; + contEnd = npos; + strt = nodeStartFind(file, start); + nd = nodeEndFind(file, start); + initialised = true; + return; + } + contSt = stEnd + 1; + strt = nodeStartFind(file, start + 1); + nd = nodeEndFind(file, start + 1); + contEnd = std::min(strt, nd); + while( strt < nd ) + { + children->push_back( std::make_shared( file, strt, nd ) ); + } + end = file.find_first_not_of(" \n\r\f\t\v", endNodeEndFind(file, nd) + 1); + initialised = true; + strt = end; + nd = nodeEndFind(file, strt); + } + + // ZW: struct for handling nodes in generic XML files + xmlNode::xmlNode(){ modded = false; return; } + xmlNode::xmlNode( const std::string_view originFile, const size_t& begin, const std::vector>& childs ){ + modded = false; + xmlFile = originFile.substr( begin ); + structure = xmlTree( originFile ); + faux = structure.isFaux(); + start = structure.getStart(); + end = structure.getEnd(); + size_t trueStart = xmlFile.find_first_not_of("< \n\r\f\t\v", start+1); + name = xmlFile.substr( trueStart, xmlFile.find_first_of(">/ \n\r\f\t\v", trueStart) - trueStart ); + auto contPts = structure.getCont(); + content = xmlFile.substr( contPts.first, contPts.second ); + for( auto child : *structure.getChildren() ){ + children.push_back( std::make_shared(*child) ); + } + for( auto child : childs ){ + children.push_back( child ); + } + } + xmlNode::xmlNode( xmlTree &tree ){ + modded = false; + structure = tree; + if( !structure.isInit() ){ return; } + xmlFile = structure.getOrigin(); + faux = structure.isFaux(); + start = structure.getStart(); + end = structure.getEnd(); + size_t trueStart = xmlFile.find_first_not_of("< \n\r\f\t\v", start); + name = xmlFile.substr( trueStart, xmlFile.find_first_of(">/ \n\r\f\t\v", trueStart) - trueStart ); + auto headPts = structure.getHeader(); + auto contPts = structure.getCont(); + auto possTags = xmlFile.substr(headPts.first + name.size(), headPts.second - name.size()); + if( possTags.find("=") != npos ){ + size_t eqSgn = possTags.find("="); + while( eqSgn < possTags.size() ){ tags.push_back( xmlTagParser( possTags, eqSgn ) ); } + } + content = xmlFile.substr( contPts.first, contPts.second ); + for( auto& child : *(structure.getChildren()) ){ + children.push_back( std::make_shared( *child ) ); + } + } + xmlNode::xmlNode( const xmlNode& original ){ + this->nodeHeader = original.nodeHeader; + this->nodeContent = original.nodeContent; + this->nodeEnd = original.nodeEnd; + this->structure = original.structure; + this->children = original.children; + this->tags = original.tags; + this->writtenSelf = original.writtenSelf; + this->deepMod = original.deepMod; + this->xmlFile = original.xmlFile; + this->name = original.name; + this->content = original.content; + this->start = original.start; + this->end = original.end; + this->modded = original.modded; + this->written = original.written; + this->parsed = original.parsed; + this->deepParsed = original.deepParsed; + this->faux = original.faux; + } + std::vector> xmlNode::getChildren(){ return children; } + std::vector> xmlNode::getTags(){ return tags; } + std::string_view xmlNode::getFile(){ return xmlFile; } + std::string_view xmlNode::getName(){ return name; } + std::string_view xmlNode::getContent(){ return content; } + size_t xmlNode::getStart(){ return start; } + size_t xmlNode::getEnd(){ return end; } + xmlTree xmlNode::getTree(){ return structure; } + bool xmlNode::isModded(){ return modded; } + bool xmlNode::isModded( bool deep ){ + bool modStat = isModded(); + if( !deep ){ return modStat; } + for( auto child : children ){ modStat = (modStat || child->isModded( deep )); } + return modStat; + } + bool xmlNode::isWritten(){ return written; } + bool xmlNode::isParsed(){ return parsed; } + bool xmlNode::isFaux(){ return faux; } + bool xmlNode::hasChildren(){ return children.size() > 0; } + void xmlNode::setModded( bool mod ){ modded = mod; } + bool xmlNode::deepModded(){ return deepMod; } + bool xmlNode::deepParse(){ return deepParsed; } + void xmlNode::parser( bool recursive ){ + parsed = parse( recursive ); + } + void xmlNode::addChild( std::shared_ptr child ){ modded = true; children.push_back(child); } + void xmlNode::addTag( std::shared_ptr tag ){ modded = true; tags.push_back(tag); } + void xmlNode::setFile( std::string_view file ){ modded = true; xmlFile = file; } + void xmlNode::setName( std::string_view newName ){ modded = true; name = newName; } + void xmlNode::setCont( std::string_view cont ){ modded = true; content = cont; } + + bool xmlNode::parse(){ + auto topStat = parseTop(); + auto contStat = parseContent(); + return ( topStat && contStat ); + } + bool xmlNode::parse( bool recurs ) + { + bool parseSt = parse(); + if( !recurs ){ return parseSt; } + bool childSt = parseChildren( recurs ); + deepMod = true; + return (parseSt && childSt ); + } + bool xmlNode::parseTop(){ + if( xmlFile == "" ){ return false; } + if( isFaux() ){ return true; } + size_t eqSgn = xmlFile.find( "=", start ); size_t nodeInitEnd = xmlFile.find( ">", start ); + while( eqSgn < nodeInitEnd ){ tags.push_back( xmlTagParser( xmlFile, eqSgn ) ); } + return true; + } + bool xmlNode::parseContent(){ + if( xmlFile == "" ){ return false; } + end = structure.getContEnd(); + for( auto branch : *(structure.getChildren()) ){ + children.push_back( std::make_shared( *branch ) ); + } + return true; + } + bool xmlNode::parseChildren( bool recursive ){ + bool status = true; + if( recursive ){ + for( auto child : children ) + { + status = (status && child->parse( true )); + deepParsed = true; + } + } else { + for( auto child : children ) + { + status = (status && child->parse()); + deepParsed = true; + } + } + return status; + } + void xmlNode::headWriter() { + if( isFaux() ){ return; } + nodeHeader = "<" + std::string(name) ; + for( auto tag : tags ){ + nodeHeader += " " + std::string(tag->getId()) + "=\"" + std::string(tag->getVal()) + "\""; + } + nodeHeader += ">"; + } + void xmlNode::endWriter() { + if( isFaux() ){ return; } + nodeEnd = "\n"; + } + void xmlNode::contWriter() { + if( hasChildren() ){ + nodeContent = std::string(content.substr(0, children[0]->start - 1 )); + } else { + nodeContent = std::string(content); + } + } + void xmlNode::childWriter() { + for(auto child : children){ + nodeContent += (*child->nodeWriter()); + } + } + void xmlNode::endFinder(){ + auto headEnd = xmlFile.find(">", start); + auto slashPos = xmlFile.find("/", start); + if( headEnd > slashPos ){ end = headEnd; } + else{ end = xmlFile.find( ">", xmlFile.find( "( nodeHeader + nodeContent + nodeEnd ); + written = true; + modded = false; + } else if( !isWritten() ){ + writtenSelf = std::make_shared( xmlFile.substr( start, end - start ) ); + written = true; + } + } + + void xmlNode::childCounter( int& noChilds ) + { + for( auto child : children ) + { + child->childCounter( noChilds ); + if( child->end == 0 || child->isFaux() ){ --noChilds; } + } + noChilds += children.size(); + } + int xmlNode::childCounter() { + int noChilds = 0; + childCounter( noChilds ); + return noChilds; + } + std::shared_ptr xmlNode::nodeWriter() { + if( isModded( true ) || !isWritten() ){ fullWriter(); } + return writtenSelf; + } + + + // ZW: function for large scale parsing of XML files + // sequentially goes through the document and + // recursively calls itself while the next node + // beginning is closer than the next node ending + std::shared_ptr xmlPtrParser( std::string_view parseFile, size_t& initPos, size_t& endPos ) + { + auto currNode = std::make_shared(parseFile, initPos); + size_t equalSign = parseFile.find("=", initPos); + size_t nodeInitEnd = parseFile.find(">", initPos); + initPos = nodeStartFind( parseFile, initPos + 1 ); + while( equalSign < nodeInitEnd ){ + currNode->addTag( xmlTagParser(parseFile, equalSign) ); + } + while( initPos < endPos ) + { + currNode->addChild(xmlPtrParser( parseFile, initPos, endPos )); + } + + initPos = nodeStartFind( parseFile, endPos ); + endPos = nodeEndFind( parseFile, endPos + 1 ); + return currNode; + } + + // ZW: struct for handling rwgt parameter sets + // in the LHE header initrwgt node + int headWeight::headWeight::getId(){ return id; } + std::string_view headWeight::getTag(){ return idTag; } + bool headWeight::hasTag(){ return (idTag.size() > 0); } + headWeight::headWeight(){ name = "weight"; return; } + headWeight::headWeight( std::string_view paramSet, const size_t& begin ) : xmlNode(){ name = "weight"; xmlFile = paramSet; content = paramSet.substr(begin); return; } + headWeight::headWeight( std::string_view paramSet, std::string_view idText, int idNo, const size_t& begin ) : xmlNode(){ + name = "weight"; xmlFile = paramSet; content = paramSet.substr(begin); idTag = idText; id = idNo; + } + headWeight::headWeight( xmlNode& node ) : xmlNode( node ){ + parser( false ); + name = "weight"; + for (auto tag : tags ){ + if( tag->getId() == "id" ){ + idTag = tag->getVal().substr(0, tag->getVal().find_last_of("_") - 1 ); + id = std::stoi( std::string( tag->getVal().substr( idTag.size() + 1 ) ) ); + } + } + } + headWeight::headWeight( xmlNode* node ) : xmlNode( *node ){ + parser( false ); + name = "weight"; + for (auto tag : tags ){ + if( tag->getId() == "id" ){ + idTag = tag->getVal().substr(0, tag->getVal().find_last_of("_") - 1 ); + id = std::stoi( std::string( tag->getVal().substr( idTag.size() + 1 ) ) ); + } + } + } + headWeight::headWeight( std::shared_ptr node ) : xmlNode( *node ){ + parser( false ); + name = "weight"; + for (auto tag : tags ){ + if( tag->getId() == "id" ){ + idTag = tag->getVal().substr(0, tag->getVal().find_last_of("_") - 1 ); + id = std::stoi( std::string( tag->getVal().substr( idTag.size() + 1 ) ) ); + } + } + } + headWeight::headWeight( xmlTree& tree ) : xmlNode( tree ){ + parser( false ); + name = "weight"; + for (auto tag : tags ){ + if( tag->getId() == "id" ){ + idTag = tag->getVal().substr(0, tag->getVal().find_last_of("_") - 1 ); + id = std::stoi( std::string( tag->getVal().substr( idTag.size() + 1 ) ) ); + } + } + } + headWeight::headWeight( xmlTree* tree ) : xmlNode( *tree ){ + parser( false ); + name = "weight"; + for (auto tag : tags ){ + if( tag->getId() == "id" ){ + idTag = tag->getVal().substr(0, tag->getVal().find_last_of("_") - 1 ); + id = std::stoi( std::string( tag->getVal().substr( idTag.size() + 1 ) ) ); + } + } + } + headWeight::headWeight( std::shared_ptr tree ) : xmlNode( *tree ){ + parser( false ); + name = "weight"; + for (auto tag : tags ){ + if( tag->getId() == "id" ){ + idTag = tag->getVal().substr(0, tag->getVal().find_last_of("_") - 1 ); + id = std::stoi( std::string( tag->getVal().substr( idTag.size() + 1 ) ) ); + } + } + } + headWeight::headWeight( std::string_view paramSet, std::string& idText, unsigned int idNo, const size_t& begin ) : xmlNode(){ + name = "weight"; xmlFile = paramSet; content = paramSet.substr(begin); idTag = idText; id = idNo; + } + headWeight::headWeight( std::string_view paramSet, std::string& idText){ + name = "weight"; xmlFile = paramSet; content = paramSet; idTag = idText; + } + void headWeight::setId( std::string identity ){ modded = true; idTag = identity; } + void headWeight::headWriter(){ + if( tags.size() == 0 ){ + if( idTag == "" ){ nodeHeader = ""; return; } + if( id == npos ){ nodeHeader = ""; return; } + nodeHeader = ""; + return; + } + nodeHeader = "getId()) + "=\"" + std::string(tag->getVal()) + "\""; + } + nodeHeader += ">"; + } + void headWeight::headWriter( bool incId ){ + if( !incId ){ headWriter(); return; } + if( idTag == "" ){ headWriter(); return; } + if( id == npos ){ nodeHeader = "getId() == "id" ){ continue; } + nodeHeader += " " + std::string(tag->getId()) + "=\"" + std::string(tag->getVal()) + "\""; + } + nodeHeader += ">"; + } + void headWeight::endWriter() { + nodeEnd = "\n"; + } + void headWeight::contWriter() { + nodeContent = std::string( content ); + } + void headWeight::childWriter() { + for( auto child : children){ + if( child->getName() == "weight" ){ continue; } + nodeContent += *(child->nodeWriter()); + } + } + void headWeight::childWriter( bool hasChildren ){ + if( hasChildren ){ childWriter(); } + } + void headWeight::fullWriter(){ + if( isModded() || !isWritten() ){ + headWriter(); + contWriter(); + childWriter(); + endWriter(); + writtenSelf = std::make_shared( nodeHeader + nodeContent + nodeEnd ); + written = true; + modded = false; + } + } + void headWeight::fullWriter( bool incId, bool hasChildren ){ + if( isModded() || !isWritten() ){ + headWriter( incId ); + contWriter(); + childWriter( hasChildren ); + endWriter(); + writtenSelf = std::make_shared( nodeHeader + nodeContent + nodeEnd ); + modded = false; + written = true; + } + } + + // ZW: struct for handling rwgt groups + // in the LHE header initrwgt node + bool weightGroup::getIncId(){ return includeId; } + void weightGroup::setIncId( bool nuIncId ){ includeId = nuIncId; } + std::vector> weightGroup::getWgts(){ return paramSets; } + void weightGroup::addWgt( headWeight nuWgt ){ modded = true; paramSets.push_back( std::make_shared( nuWgt ) ); if( nuWgt.hasTag() ){ includeId = true; } } + void weightGroup::addWgt( std::shared_ptr nuWgt ){ modded = true; paramSets.push_back( nuWgt); if( nuWgt->hasTag() ){ includeId = true; }} + weightGroup::weightGroup() : xmlNode(){ name = "weightgroup"; return; } + weightGroup::weightGroup( std::vector> nuWgts ) : xmlNode(){ name = "weightgroup"; paramSets = nuWgts; for( auto wgt : nuWgts ){ if( wgt->hasTag() ){ includeId = true; } } } + weightGroup::weightGroup( std::vector nuWgts ) : xmlNode(){ + name = "weightgroup"; + for( auto wgt : nuWgts ){ + paramSets.push_back( std::make_shared( wgt ) ); + } + for( auto wgt : paramSets ){ if( wgt->hasTag() ){ includeId = true; } } + } + weightGroup::weightGroup( xmlNode& wgtNode ) : xmlNode( wgtNode ){ + parser( true ); + name = "weightgroup"; + paramSets.reserve( children.size() ); + for( auto child : children ){ + if( child->getName() == "weight" ){ paramSets.push_back( std::make_shared( *child ) ); } + } + for( auto wgt : paramSets ){ if( wgt->hasTag() ){ includeId = true; } } + } + weightGroup::weightGroup( xmlNode* wgtNode ) : xmlNode( *wgtNode ){ + parser( true ); + name = "weightgroup"; + paramSets.reserve( children.size() ); + for( auto child : children ){ + if( child->getName() == "weight" ){ paramSets.push_back( std::make_shared( *child ) ); } + } + for( auto wgt : paramSets ){ if( wgt->hasTag() ){ includeId = true; } } + } + weightGroup::weightGroup( xmlTree& wgtTree ) : xmlNode( wgtTree ){ + parser( true ); + name = "weightgroup"; + paramSets.reserve( children.size() ); + for( auto child : children ){ + if( child->getName() == "weight" ){ paramSets.push_back( std::make_shared( *child ) ); } + } + for( auto wgt : paramSets ){ if( wgt->hasTag() ){ includeId = true; } } + } + weightGroup::weightGroup( xmlTree* wgtTree ) : xmlNode( *wgtTree ){ + parser( true ); + name = "weightgroup"; + paramSets.reserve( children.size() ); + for( auto child : children ){ + if( child->getName() == "weight" ){ paramSets.push_back( std::make_shared( *child ) ); } + } + for( auto wgt : paramSets ){ if( wgt->hasTag() ){ includeId = true; } } + } + weightGroup::weightGroup( std::shared_ptr wgtTree ) : xmlNode( *wgtTree ){ + parser( true ); + name = "weightgroup"; + paramSets.reserve( children.size() ); + for( auto child : children ){ + if( child->getName() == "weight" ){ paramSets.push_back( std::make_shared( *child ) ); } + } + for( auto wgt : paramSets ){ if( wgt->hasTag() ){ includeId = true; } } + } + weightGroup::weightGroup( const std::string_view originFile, const size_t& begin, const std::vector>& childs ) + : xmlNode( originFile, begin, childs ){ + name = "weightgroup"; + if( parseTop() ){ + int checker = 0; + for( auto tag : tags ){ + if( tag->getId() == "name" ){ ++checker; rwgtName = tag->getVal(); } + if( tag->getId() == "weight_name_strategy" ){ ++checker; wgtNamStrat = tag->getVal(); + if(wgtNamStrat == "includeIdInWeightName"){ includeId = true; } } + if( checker == 2 ){ break; } + } + } + } + void weightGroup::headWriter() { + nodeHeader = "nodeWriter()); + } + } + void weightGroup::childWriter() { + for(auto child : children){ + if( child->getName() == "weight" ){ continue; } + nodeContent += (*child->nodeWriter()); + } + } + void weightGroup::childWriter( bool hasChildren ){ + if( hasChildren ){ childWriter(); } + return; + } + void weightGroup::endWriter() { nodeEnd = "\n"; } + + std::vector> initRwgt::getGroups(){ return groups; } + size_t initRwgt::noGrps(){ return groups.size(); } + void initRwgt::addGroup( weightGroup nuGroup ){ + modded = true; + auto nuGrpPtr = std::make_shared( nuGroup ); + if( grpInit( nuGrpPtr ) ){ groups.push_back( std::make_shared( nuGroup ) ); } + } + void initRwgt::addGroup( std::shared_ptr nuGroup ){ + modded = true; + if( grpInit( nuGroup ) ){ groups.push_back( nuGroup ); } + } + void initRwgt::addWgt( unsigned int index, std::shared_ptr nuWgt ){ + if( index < groups.size() ){ modded = true; groups[index]->addWgt( nuWgt ); } + else throw std::range_error( "Appending weight to uninitialised weightgroup." ); + } + void initRwgt::addWgt( unsigned int index, headWeight nuWgt ){ + if( index < groups.size() ){ modded = true; groups[index]->addWgt( nuWgt ); } + else throw std::range_error( "Appending weight to uninitialised weightgroup." ); + } + initRwgt::initRwgt() : xmlNode(){ name = "initrwgt"; return; } + initRwgt::initRwgt( std::vector> nuGroups ) : xmlNode(){ + name = "initrwgt"; + for( auto group : nuGroups ){ + groups.push_back( std::make_shared( *group ) ); + } + } + initRwgt::initRwgt( xmlNode& wgtNode ) : xmlNode( wgtNode ){ + parser( true ); + name = "initrwgt"; + groups.reserve( children.size() ); + for( auto child : children ){ + groups.push_back( std::make_shared( *child ) ); + } + } + initRwgt::initRwgt( xmlNode* wgtNode ) : xmlNode( *wgtNode ){ + parser( true ); + name = "initrwgt"; + groups.reserve( children.size() ); + for( auto child : children ){ + groups.push_back( std::make_shared( *child ) ); + } + } + initRwgt::initRwgt( std::shared_ptr wgtNode ) : xmlNode( *wgtNode ){ + parser( true ); + name = "initrwgt"; + groups.reserve( children.size() ); + for( auto child : children ){ + groups.push_back( std::make_shared( *child ) ); + } + } + initRwgt::initRwgt( xmlTree& wgtTree ) : xmlNode( wgtTree ){ + parser( true ); + name = "initrwgt"; + groups.reserve( children.size() ); + for( auto child : children ){ + groups.push_back( std::make_shared( *child ) ); + } + } + bool initRwgt::grpInit( std::shared_ptr& wgt ){ + if( grpIsInit ){ return true; } + else{ + groups = std::vector>( 1, wgt ); + grpIsInit = true; + return false; + } + } + void initRwgt::contWriter(){ + nodeContent = "\n"; + for( auto group : groups ){ + nodeContent += (*group->nodeWriter()); + } + } + void initRwgt::childWriter(){ + for( auto child : children ){ + if( child->getName() == "weightgroup" ){ continue; } + nodeContent += (*child->nodeWriter()); + } + } + void initRwgt::childWriter( bool hasChildren ){ + if( hasChildren ){ childWriter(); } + return; + } + + // ZW: struct for handling weights + // in event blocks of LHE files + void bodyWgt::setComment( std::string_view nuComment ){ modded = true; comment = nuComment; } + void bodyWgt::setVal( std::string nuVal ){ modded = true; valS = nuVal; valD = std::stod(valS);} + void bodyWgt::setVal( std::string_view nuVal ){ modded = true; valS = std::string(nuVal); valD = std::stod(valS);} + void bodyWgt::setVal( double nuVal ){ modded = true; valD = nuVal; valS = std::to_string(valD);} + void bodyWgt::setId( std::string nuId ){ + modded = true; id = nuId; + for( auto tag : tags ){ + if( tag->getId() == "id" ){ tag->setVal( id ); return; } + } + addTag( std::make_shared( "id", id ) ); + } + void bodyWgt::setModded( bool nuModded ){ modded = nuModded; } + std::string_view bodyWgt::getComment(){ return comment; } + std::string_view bodyWgt::getValS(){ return valS; } + double bodyWgt::getValD(){ return valD; } + bodyWgt::bodyWgt() : xmlNode(){ return; } + bodyWgt::bodyWgt( std::string_view value ) : xmlNode() { setVal( value ); modded = false; } + bodyWgt::bodyWgt( double value ) : xmlNode() { setVal( value ); modded = false; } + bodyWgt::bodyWgt( std::string_view value, xmlTag rwgtId ) : xmlNode() { setVal( value ); addTag( std::make_shared(rwgtId) ); modded = false; } + bodyWgt::bodyWgt( double value, xmlTag rwgtId ) : xmlNode() { setVal( value ); addTag( std::make_shared(rwgtId) ); modded = false; } + bodyWgt::bodyWgt( std::string_view value, std::shared_ptr rwgtId ) : xmlNode() { setVal( value ); addTag( rwgtId ); modded = false; } + bodyWgt::bodyWgt( double value, std::shared_ptr rwgtId ) : xmlNode() { setVal( value ); addTag( rwgtId ); modded = false; } + bodyWgt::bodyWgt( const std::string_view originFile, const size_t& begin, const std::vector>& childs ) + : xmlNode( originFile, begin, childs ){ + auto strtPt = originFile.find_first_not_of(" >+", originFile.find(">", begin)+1); + valS = originFile.substr( strtPt, originFile.find(" ", strtPt) - strtPt ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( xmlNode& wgtNode ) : xmlNode( wgtNode ){ + parser( true ); + valS = xmlFile.substr( structure.getContStart(), structure.getContEnd() - structure.getContStart() ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( xmlNode* wgtNode ) : xmlNode( *wgtNode ){ + parser( true ); + valS = xmlFile.substr( structure.getContStart(), structure.getContEnd() - structure.getContStart() ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( std::shared_ptr wgtNode ) : xmlNode( *wgtNode ){ + parser( true ); + valS = xmlFile.substr( structure.getContStart(), structure.getContEnd() - structure.getContStart() ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( xmlTree& wgtTree ) : xmlNode( wgtTree ){ + parser( true ); + valS = xmlFile.substr( structure.getContStart(), structure.getContEnd() - structure.getContStart() ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( xmlTree* wgtTree ) : xmlNode( *wgtTree ){ + parser( true ); + valS = xmlFile.substr( structure.getContStart(), structure.getContEnd() - structure.getContStart() ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( std::shared_ptr wgtTree ) : xmlNode( *wgtTree ){ + parser( true ); + valS = xmlFile.substr( structure.getContStart(), structure.getContEnd() - structure.getContStart() ); + valD = std::stod( valS ); + } + bodyWgt::bodyWgt( double value, std::string& idTag ){ + setVal( value ); + id = idTag; + addTag( std::make_shared("id",id) ); + } + void bodyWgt::appendWgt( std::shared_ptr document ){ + if( !isWritten() ){ fullWriter(); } + *document += *writtenSelf; + } + void bodyWgt::appendWgt( std::string* document ){ + if( !isWritten() ){ fullWriter(); } + *document += *writtenSelf; + } + std::shared_ptr bodyWgt::appendWgt( std::string_view document ){ + if(!isWritten() ){ fullWriter(); } + auto retDoc = std::make_shared( document ); + *retDoc += *writtenSelf; + return retDoc; + } + void bodyWgt::fullWriter() { + writtenSelf = std::make_shared( "getId()) + "=\"" + std::string(tag->getVal()) + "\""; + } + *writtenSelf += ">" + std::string(valS) + "\n"; + modded = false; + written = true; + } + + // ZW: fcn for finding the next block in SLHA format + // parameter cards + size_t blockFinder( std::string_view parseFile, size_t startPt = 0 ){ + if( parseFile.size() > 5 ){ if( clStringComp(parseFile.substr(0,5), std::string("block") )){ return size_t(0); } } + return clStringFind( parseFile, std::string("\nblock"), startPt ); + } + + // ZW: fcn for finding each decay line in SLHA format + // parameter card + std::vector decBlockStractor( std::string_view parseFile ){ + auto allDs = findEach( parseFile, "\nd" ); + std::vector decLines; + decLines.reserve( allDs->size() ); + for( auto pos : *allDs ) + { + if( !(clStringComp(parseFile.substr( pos+1, 5 ), std::string("decay"))) ){ continue; } + decLines.push_back( parseFile.substr( pos + 1, parseFile.find( "\n", pos + 1 ) - pos - 1 ) ); + } + return decLines; + } + + // ZW: fcn for extracting the relevant lines of + // a block in SLHA format parameter card + // removes any comments between start of this block and next + // and also ignores lines with other information, + // eg DECAY lines + std::vector blockLineStractor( std::string_view parseFile, size_t startPt = 0){ + auto blockStrt = blockFinder( parseFile, startPt ); + auto newBlock = blockFinder( parseFile, blockStrt + 1 ); + std::vector paramLines; + paramLines.reserve( strCount( parseFile, "\n" ) ); + std::shared_ptr> parLines; + if( newBlock == npos ){ parLines = lineSplitter( parseFile.substr( blockStrt ) ); } + else{ parLines = lineSplitter( parseFile.substr( blockStrt, newBlock - blockStrt ) ); } + for( auto line : *parLines ) + { + if( line.size() == 0 ){ continue; } + if( line[0] != ' ' ){ continue; } + paramLines.push_back( line ); + } + return paramLines; + } + + // ZW: struct for handling the first line of + // LHE format event block + std::string_view evHead::getComment(){ return comment; } + double evHead::getWeight(){ return weight; } + double evHead::getScale(){ return scale; } + double evHead::getAQED(){ return aqed; } + double evHead::getAQCD(){ return aqcd; } + int evHead::getNprt(){ return nprt; } + int evHead::getProcID(){ return procid; } + bool evHead::isModded(){ return modded; } + bool evHead::isWritten(){ return written; } + void evHead::setComment( std::string_view nuCom ){ modded = true; comment = nuCom; } + void evHead::setWeight( double nuWgt ){ modded = true; weight = nuWgt; } + void evHead::setScale( double nuScale ){ modded = true; scale = nuScale; } + void evHead::setAQED( double nuAQED ){ modded = true; aqed = nuAQED; } + void evHead::setAQCD( double nuAQCD ){ modded = true; aqcd = nuAQCD; } + void evHead::setNprt( int nuNprt ){ modded = true; nprt = nuNprt; } + void evHead::setProcID( int nuProcID ){ modded = true; procid = nuProcID; } + std::shared_ptr evHead::getContent(){ + if( !isWritten() || isModded() ){ writer(); } + return content; + } + evHead::evHead(){ return; } + evHead::evHead( const std::string_view originFile, size_t beginLine, size_t endLine ) + { + if( originFile.size() == 0){ return; } + beginLine = originFile.find_first_not_of("\n \r\f\t\v", beginLine); + if( endLine == npos ){ endLine = originFile.find("\n", beginLine ) + 1; } + sourceFile = originFile.substr( beginLine, endLine - beginLine ); + auto evLine = blankSplitter( sourceFile ); + nprt = ctoi(evLine->at(0)); + procid = ctoi(evLine->at(1)); + weight = ctod(evLine->at(2)); + scale = ctod(evLine->at(3)); + aqed = ctod(evLine->at(4)); + aqcd = ctod(evLine->at(5)); + } + void evHead::writer(){ + if( isWritten() && !isModded() ){ return; } + if( !isModded() ){ content = std::make_shared( sourceFile ); return; } + auto retText = std::make_shared( " " ); + *content = " " + std::to_string( nprt ); + auto procIdStr = std::to_string( procid ); + for( size_t k = 0 ; k < 8 - procIdStr.size() ; ++k ){ *content += " "; } + *content += std::to_string( procid ) + " " + std::to_string( weight ) + " " + std::to_string( scale ) + " " + std::to_string( aqed ) + " " + std::to_string( aqcd ); + if( comment != "" ){ *content += " # " + std::string( comment ); } + *content += "\n"; + modded = false; + written = true; + } + + // ZW: struct for handling particle lines + // in LHE format event block + std::string_view lhePrt::getLine(){ return sourceFile; } + std::string_view lhePrt::getComment(){ return comment; } + std::vector lhePrt::getMom(){ return std::vector( std::begin( mom ), std::end( mom ) ); } + double lhePrt::getE(){ return energy; } + double lhePrt::getMass(){ return mass; } + double lhePrt::getVTim(){ return vtim; } + double lhePrt::getSpin(){ return spin; } + int lhePrt::getPDG(){ return pdg; } + int lhePrt::getStatus(){ return status; } + std::vector lhePrt::getMothers(){ return std::vector( std::begin( mothers ), std::end( mothers ) ); } + std::vector lhePrt::getColor(){ return std::vector( std::begin( icol ), std::end( icol ) ); } + void lhePrt::setComment( std::string_view nuCom ){ modded = true; comment = nuCom; } + void lhePrt::setMom( std::vector nuMom ){ modded = true; mom[0] = nuMom[0]; mom[1] = nuMom[1]; mom[2] = nuMom[2]; } + void lhePrt::setEnergy( double nuE ){ modded = true; energy = nuE; } + void lhePrt::setMass( double nuM ){ modded = true; mass = nuM; } + void lhePrt::setVTim( double nuVTim ){ modded = true; vtim = nuVTim; } + void lhePrt::setSpin( double nuSpin ){ modded = true; spin = nuSpin; } + void lhePrt::setPDG( int nuPDG ){ modded = true; pdg = nuPDG; } + void lhePrt::setStatus( int nuSt ){ modded = true; status = nuSt; } + void lhePrt::setMothers( std::vector nuMum ){ modded = true; mothers[0] = nuMum[0]; mothers[1] = nuMum[1]; } + void lhePrt::setColors( std::vector nuCol ){ modded = true; icol[0] = nuCol[0]; icol[1] = nuCol[1]; } + bool lhePrt::isModded(){ return modded; } + bool lhePrt::isWritten(){ return written; } + std::shared_ptr lhePrt::getContent(){ + if( !isWritten() || isModded() ){ writer(); } + return content; + } + lhePrt::lhePrt(){ return; } + lhePrt::lhePrt( std::pair& prtInfo ){ + status = prtInfo.first; + pdg = prtInfo.second; + } + lhePrt::lhePrt( std::pair& prtInfo ){ + status = ctoi(prtInfo.first); + pdg = ctoi( prtInfo.second ); + } + lhePrt::lhePrt( const std::string_view originFile, const size_t& beginLine, const size_t& endLine ) + { + sourceFile = originFile.substr( beginLine, endLine - beginLine ); + auto evLine = blankSplitter( sourceFile ); + pdg = ctoi(evLine->at(0)); + status = ctoi(evLine->at(1)); + mothers[0] = ctoi(evLine->at(2)); mothers[1] = ctoi(evLine->at(3)); + icol[0] = ctoi(evLine->at(4)); icol[1] = ctoi(evLine->at(5)); + for( int k = 6 ; k < 9 ; ++k){ + mom[k-6] = ctod(evLine->at(k)); + } + energy = ctod(evLine->at(9)); + mass = ctod(evLine->at(10)); + vtim = ctod(evLine->at(11)); + spin = ctod(evLine->at(12)); + if( evLine->size() > 13 ){ comment = sourceFile.substr( sourceFile.find( "#" ) + 1 ); } + } + void lhePrt::writer(){ + if( isWritten() && !isModded() ){ return; } + if( !isModded() ){ content = std::make_shared( sourceFile ); return; } + *content = ""; + std::string pdgStr = std::to_string( this->pdg ); + for( size_t k = 0; k < 10 - pdgStr.length() ; ++k ){ *content += " "; } + *content += pdgStr + " " + std::to_string(status); + for( auto mum : mothers ){ *content += " " + std::to_string( mum ); } + for( auto col : icol ){ *content += " " + std::to_string( col ); } + for( auto pval : mom ){ *content += " " + std::to_string(pval); } + *content += " " + std::to_string( energy ) + " " + std::to_string( mass ) + " " + std::to_string( vtim ) + " " + std::to_string( spin ); + if( comment != "" ){ *content += " # " + std::string( comment ); } + *content += "\n"; + modded = false; + written = true; + } + + // ZW: struct for handling LHE format event block + evHead event::getHead(){ return header; } + std::vector> event::getPrts(){ return prts; } + std::vector> event::getWgts(){ return rwgt; } + std::map>> event::getSortedPrts(){ return sortPrts; } + void event::setHead( evHead head ){ modded = true; header = head; } + void event::addPrt( std::shared_ptr prtcl ){ modded = true; prts.push_back( prtcl ); } + void event::addPrt( lhePrt prtcl ){ modded = true; prts.push_back( std::make_shared(prtcl) ); } + void event::setPrts( std::vector> prtcls ){ modded = true; prts = prtcls; } + void event::addWgt( bodyWgt nuWgt ){ addedWgt = true; rwgt.push_back( std::make_shared(nuWgt) ); } + void event::addWgt( std::shared_ptr nuWgt ){ modded = true; rwgt.push_back( nuWgt ); } + void event::addWgt( bodyWgt nuWgt, std::string& id ){ addedWgt = true; nuWgt.setId( id ); rwgt.push_back( std::make_shared(nuWgt) ); } + void event::addWgt( std::shared_ptr nuWgt, std::string& id ){ modded = true; nuWgt->setId( id ); rwgt.push_back( nuWgt ); } + bool event::newWeight(){ return addedWgt; } + int event::getNprt(){ return prts.size(); } + bool event::isModded() { return modded; } + bool event::isModded( bool deep ) { + if( !deep ){ return modded; } + bool modStat = modded; + for( auto child : children ){ if(modStat){ return modStat; }; modStat = (modStat || child->isModded( deep )); } + modStat = (modStat || header.isModded()); + for( auto prt : prts ){ if(modStat){ return modStat; }; modStat = (modStat || prt->isModded()); } + for( auto wgt : rwgt ){ if(modStat){ return modStat; }; modStat = (modStat || wgt->isModded()); } + return modStat; + } + event::event(){ return; } + event::event( std::vector>& prtInfo ){ + header.setNprt( prtInfo.size() ); + for( auto& prt : prtInfo ){ + prts.push_back( std::make_shared( prt ) ); + } + } + event::event( std::vector>& prtInfo ){ + header.setNprt( prtInfo.size() ); + for( auto& prt : prtInfo ){ + prts.push_back( std::make_shared( prt ) ); + } + } + event::event( std::vector> prtInfo ){ + header.setNprt( prtInfo.size() ); + prts = prtInfo; + } + event::event( const std::string_view originFile, const size_t& begin, const std::vector>& childs ) + : xmlNode(originFile, begin, childs) { + xmlFile = originFile; start = begin; children = childs; size_t trueStart = originFile.find_first_not_of(" \n\r\f\t\v", begin+1); + if( trueStart == npos ){ return; } + auto vals = lineFinder( originFile.substr( trueStart, originFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(originFile, vals->at(0) + trueStart, vals->at(1) + trueStart + 1 ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(originFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart + 1) ); + } + } + event::event( const xmlNode& originFile ) + : xmlNode( originFile ) { + size_t trueStart = xmlFile.find_first_not_of(" \n\r\f\t\v", start+1); + auto vals = lineFinder( xmlFile.substr( trueStart, xmlFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(xmlFile, vals->at(0) + trueStart, vals->at(1) + trueStart ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(xmlFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart) ); + } + } + event::event( const xmlNode* originFile ) + : xmlNode( *originFile ) { + size_t trueStart = xmlFile.find_first_not_of(" \n\r\f\t\v", structure.getContStart() + 1); + auto vals = lineFinder( xmlFile.substr( trueStart, xmlFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(xmlFile, vals->at(0) + trueStart, vals->at(1) + trueStart ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(xmlFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart) ); + } + } + event::event( const std::shared_ptr& originFile ) + : xmlNode( *originFile ) { + size_t trueStart = xmlFile.find_first_not_of(" \n\r\f\t\v", structure.getContStart() + 1); + auto vals = lineFinder( xmlFile.substr( trueStart, xmlFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(xmlFile, vals->at(0) + trueStart, vals->at(1) + trueStart ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(xmlFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart) ); + } + } + event::event( xmlTree& originFile ) + : xmlNode( originFile ) { + size_t trueStart = xmlFile.find_first_not_of(" \n\r\f\t\v", structure.getContStart() + 1); + auto vals = lineFinder( xmlFile.substr( trueStart, xmlFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(xmlFile, vals->at(0) + trueStart, vals->at(1) + trueStart ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(xmlFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart) ); + } + } + event::event( xmlTree* originFile ) + : xmlNode( *originFile ) { + size_t trueStart = xmlFile.find_first_not_of(" \n\r\f\t\v", structure.getContStart() + 1); + auto vals = lineFinder( xmlFile.substr( trueStart, xmlFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(xmlFile, vals->at(0) + trueStart, vals->at(1) + trueStart ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(xmlFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart) ); + } + } + event::event( std::shared_ptr originFile ) + : xmlNode( *originFile ) { + size_t trueStart = xmlFile.find_first_not_of(" \n\r\f\t\v", structure.getContStart() + 1); + auto vals = lineFinder( xmlFile.substr( trueStart, xmlFile.find("<", trueStart + 3 ) - trueStart + 3 )); + header = evHead(xmlFile, vals->at(0) + trueStart, vals->at(1) + trueStart ); + prts.reserve(vals->size()); + for( int k = 1 ; k < header.getNprt() + 1; ++k) + { + prts.push_back( std::make_shared(xmlFile, vals->at(k) + trueStart + 1, vals->at(k+1) + trueStart) ); + } + } + event::event( const event& original ) : xmlNode( original ){ + this->rwgt = original.rwgt; + this->header = original.header; + this->prts = original.prts; + this->procMap = original.procMap; + this->procOrder = original.procOrder; + } + event::event( event* original ) : event(*original){}; + event::event( std::shared_ptr original) : event(*original){}; + bool event::prtsAreMod(){ + for( auto prt : prts ){ if( prt->isModded() ){ return true; } } + return false; + } + bool event::headIsMod(){ + return header.isModded(); + } + bool event::isSpecSort() const { return specSorted; } + sortFcn event::getSortFcn() const { return eventSort; } + statSort event::getStatSort() const { return specSort; } + bool event::hasRwgt(){ + if( rwgt.size() > 0 ){ return true; } + return false; + } + bool event::rwgtChild(){ + if( childRwgt != nullptr ){ return true; } + for( auto child : children ){ if( clStringComp(child->getName(), std::string("rwgt") ) ){ childRwgt = child; return true; } } + return false; + } + bool event::bothRwgt(){ return (hasRwgt() && rwgtChild() ); } + bool event::eitherRwgt(){ return (hasRwgt() || rwgtChild() ); } + bool event::initProcMap(bool hard) + { + if(!hard){ if( procMap.size() > 0 ){ return true; } } + procMap.clear(); + procOrder.clear(); + sortPrts.clear(); + for( auto prt : prts ){ + procMap.insert({prt->getStatus(), std::vector()}); + procOrder.insert({prt->getStatus(), std::vector()}); + sortPrts.insert({prt->getStatus(), std::vector>()}); + } + for( auto prt : prts ){ + procMap[prt->getStatus()].push_back( prt->getPDG() ); + sortPrts[prt->getStatus()].push_back( prt ); + } + for( auto stat = procMap.begin(); stat!= procMap.end(); ++stat ){ + procOrder[stat->first] = *indSort( stat->second ); + } + hasBeenProc = true; + return true; + } + bool event::initProcMap( sortFcn sorter, bool hard ) + { + if(!hard){ if( procMap.size() > 0 ){ return true; } } + procMap.clear(); + procOrder.clear(); + sortPrts.clear(); + specSorted = false; + eventSort = sorter; + for( auto prt : prts ){ + procMap.insert({prt->getStatus(), std::vector()}); + procOrder.insert({prt->getStatus(), std::vector()}); + sortPrts.insert({prt->getStatus(), std::vector>()}); + } + for( auto prt : prts ){ + procMap[prt->getStatus()].push_back( prt->getPDG() ); + sortPrts[prt->getStatus()].push_back( prt ); + } + for( auto stat = procMap.begin(); stat!= procMap.end(); ++stat ){ + procOrder[stat->first] = *sorter( stat->second ); + } + hasBeenProc = true; + return true; + } + bool event::initProcMap( statSort sorter, bool hard ) + { + if(!hard){ if( procMap.size() > 0 ){ return true; } } + procMap.clear(); + procOrder.clear(); + sortPrts.clear(); + specSorted = true; + specSort = sorter; + for( auto prt : prts ){ + procMap.insert({prt->getStatus(), std::vector()}); + procOrder.insert({prt->getStatus(), std::vector()}); + sortPrts.insert({prt->getStatus(), std::vector>()}); + } + for( auto prt : prts ){ + procMap[prt->getStatus()].push_back( prt->getPDG() ); + sortPrts[prt->getStatus()].push_back( prt ); + } + for( auto stat = procMap.begin(); stat!= procMap.end(); ++stat ){ + procOrder[stat->first] = *sorter(stat->first, stat->second ); + } + hasBeenProc = true; + return true; + } + bool event::inRwgtChild( std::string_view nameIn ){ + for( auto child : childRwgt->getChildren() ){ + for( auto tag : child->getTags() ){ if(clStringComp(tag->getVal(), nameIn)){ return true; } } + } + return false; + } + bool event::checkRwgtOverlap(){ + for( auto wgt : rwgt ){ + for( auto tag : wgt->getTags() ){ if( inRwgtChild( tag->getVal() ) ){ return true; } } + } + return false; + } + void event::childRwgtWriter(){ + if( rwgtChild() ){ nodeContent += *childRwgt->nodeWriter(); } + } + void event::vecRwgtWriter( bool midNode ){ + if( !midNode ){ nodeContent += "\n"; } + for( auto wgt : rwgt ){ + nodeContent += *wgt->nodeWriter(); + } + nodeContent += "\n"; + } + void event::rwgtWriter(){ + if( bothRwgt() ){ if( checkRwgtOverlap() ){ childRwgtWriter(); return; } + childRwgtWriter(); + nodeContent.erase( nodeContent.size() - 8, 8 ); + vecRwgtWriter(); + return; + } else { + if( hasRwgt() ){ vecRwgtWriter(); return; } + if( rwgtChild() ){ childRwgtWriter(); return; } + } + } + void event::contWriter() { + nodeContent = "\n" + *header.getContent(); + if( nodeContent.back() != '\n' ){ nodeContent += "\n"; } + for( auto prt : prts ){ + nodeContent += *prt->getContent(); + if( nodeContent.back() != '\n' ){ nodeContent += "\n"; } + } + } + void event::childWriter() { + for( auto child : children ){ + if( clStringComp( child->getName(), std::string("wgt") ) ){ continue; } + nodeContent += *child->nodeWriter(); + } + } + void event::fullWriter() { + if( isModded( false ) ){ + headWriter(); + contWriter(); + childWriter(); + rwgtWriter(); + endWriter(); + writtenSelf = std::make_shared( nodeHeader + nodeContent + nodeEnd ); + modded = false; + } else if( !isWritten() ){ + writtenSelf = std::make_shared( xmlFile.substr( start, end - start ) ); + written = true; + } + } + void event::fullWriter( bool deep ){ + if( !deep ){ fullWriter(); return; } + if( isModded( true ) ){ + headWriter(); + contWriter(); + childWriter(); + rwgtWriter(); + endWriter(); + writtenSelf = std::make_shared( nodeHeader + nodeContent + nodeEnd ); + modded = false; + written = true; + } else if( !isWritten() ){ + writtenSelf = std::make_shared( xmlFile.substr( start, end - start ) ); + written = true; + } + } + void event::appendWgts(){ + if( !addedWgt ){ return; } + writtenSelf->erase( writtenSelf->size() - 17, 17 ); + for( auto wgt : rwgt ){ + if( !wgt->isWritten() ){ wgt->appendWgt( writtenSelf ); } + } + *writtenSelf += "\n\n"; + } + std::shared_ptr event::nodeWriter() { + if( isModded(false) || !isWritten() ){ fullWriter(); return writtenSelf; } + if( addedWgt ){ appendWgts(); } + return writtenSelf; + } + std::shared_ptr event::nodeWriter( bool recursive ){ + if( isModded( recursive ) || !isWritten() ){ fullWriter(); return writtenSelf; } + if( addedWgt ){ appendWgts(); } + return writtenSelf; + } + std::map> &event::getProc( bool hard ){ + if( initProcMap(hard) ){ return procMap; } + else throw std::runtime_error("Error while parsing event node."); + } + std::map> &event::getProcOrder( bool hard ){ + if( initProcMap(hard) ){ return procOrder; } + else throw std::runtime_error("Error while parsing event node."); + } + std::map> event::getProc() const { + if ( hasBeenProc ){ return procMap; } + else throw std::runtime_error("Const declaration of event node before it has been procesed."); + } + std::map> event::getProcOrder() const { + if ( hasBeenProc ){ return procOrder; } + else throw std::runtime_error("Const declaration of event node before it has been procesed."); + } + std::map> &event::getProc(sortFcn sorter, bool hard){ + if( initProcMap(sorter, hard) ){ return procMap; } + else throw std::runtime_error("Error while parsing event node."); + } + std::map> &event::getProcOrder(sortFcn sorter, bool hard){ + if( initProcMap(sorter, hard) ){ return procOrder; } + else throw std::runtime_error("Error while parsing event node."); + } + std::map> &event::getProc(statSort sorter, bool hard){ + if( initProcMap(sorter, hard) ){ return procMap; } + else throw std::runtime_error("Error while parsing event node."); + } + std::map> &event::getProcOrder(statSort sorter, bool hard){ + if( initProcMap(sorter, hard) ){ return procOrder; } + else throw std::runtime_error("Error while parsing event node."); + } + + eventSet::eventSet(){ + events = std::vector(); + } + eventSet::eventSet( const eventSet& nuEvents ){ + this->events = nuEvents.events; + this->relStats = nuEvents.relStats; + this->comp = nuEvents.comp; + } + eventSet::eventSet( std::vector& nuEvents ){ + events = std::vector(); + for( auto ev : nuEvents ){ + events.push_back( ev ); + } + } + eventSet::eventSet( std::vector>& nuEvents ){ + events = std::vector(); + for( auto ev : nuEvents ){ + events.push_back( *ev ); + } + } + void eventSet::setRelStats( std::vector& nuStats ){ + relStats = nuStats; + } + void eventSet::addEvent( event& nuEvent ){ + events.push_back( nuEvent ); + } + void eventSet::addEvent( std::shared_ptr nuEvent ){ + events.push_back( *nuEvent ); + } + void eventSet::addEvent( std::vector& nuEvents ){ + for( auto ev : nuEvents ){ + events.push_back( ev ); + } + } + void eventSet::addEvent( std::vector> nuEvents ){ + for( auto ev : nuEvents ){ + events.push_back( *ev ); + } + } + void eventSet::setComp( eventSetComp nuComp ){ + this->comp = nuComp; + } + bool eventSet::belongs( event& ev ){ + if( this->comp == nullptr ) throw std::runtime_error("No comparison function set for eventSet."); + return this->comp(ev, relStats); + } + bool eventSet::belongs( std::shared_ptr ev ){ + if( this->comp == nullptr ) throw std::runtime_error("No comparison function set for eventSet."); + return this->comp(*ev, relStats); + } + + // ZW: struct for handling the first line of + // LHE format init tag + bool lheInitHead::isWritten(){ return written; } + bool lheInitHead::isModded(){ return modded; } + std::shared_ptr lheInitHead::getContent(){ + if( isModded() || !isWritten() ){ writer(); } + return content; } + lheInitHead::lheInitHead( std::string_view initHead ){ + auto vals = *blankSplitter( initHead ); + if( vals.size() < 10 ){ return; } + idbmup[0] = vals[0]; idbmup[1] = vals[1]; + ebmup[0] = vals[2]; ebmup[1] = vals[3]; + pdfgup[0] = vals[4]; pdfgup[1] = vals[5]; + pdfsup[0] = vals[6]; pdfsup[1] = vals[7]; + idwtup = vals[8]; nprup = vals[9]; + } + lheInitHead::lheInitHead( xmlNode& initNode ) + { + if( initNode.getName() != "init" ){ return; } + auto startPos = initNode.getFile().find( ">", initNode.getStart() ) + 1; + auto endPos = initNode.getFile().find( "\n", startPos ); + auto vals = *blankSplitter( initNode.getFile().substr( startPos, endPos - startPos ) ); + idbmup[0] = vals[0]; idbmup[1] = vals[1]; + ebmup[0] = vals[2]; ebmup[1] = vals[3]; + pdfgup[0] = vals[4]; pdfgup[1] = vals[5]; + pdfsup[0] = vals[6]; pdfsup[1] = vals[7]; + idwtup = vals[8]; nprup = vals[9]; + } + void lheInitHead::writer(){ + *content = std::string(idbmup[0]) + " " + std::string(idbmup[1]) + " " + std::string(ebmup[0]) + " " + std::string(ebmup[1]) + " " + std::string(pdfgup[0]) + + " " + std::string(pdfgup[1]) + " " + std::string(pdfsup[0]) + " " + std::string(pdfsup[1]) + " " + std::string(idwtup) + " " + std::string(nprup) +"\n"; + written = true; + modded = false; + } + + // ZW: struct for handling process lines + // in LHE format init tag + bool lheInitLine::isWritten(){ return written; } + bool lheInitLine::isModded(){ return modded; } + std::shared_ptr lheInitLine::getContent(){ + if( isModded() || !isWritten() ){ writer(); } + return content; } + lheInitLine::lheInitLine(){} + lheInitLine::lheInitLine( std::string_view procLine ) + { + auto vals = *blankSplitter( procLine ); + if( vals.size() < 4 ){ return; } + xsecup = vals[0]; + xerrup = vals[1]; + xmaxup = vals[2]; + lprup = vals[3]; + } + void lheInitLine::writer(){ + *content = std::string(xsecup) + " " + std::string(xerrup) + " " + std::string(xmaxup) + " " + std::string(lprup) + "\n"; + written = true; + modded = false; + } + + // ZW: struct for handling single parameter line in + // SLHA format parameter card + void paramVal::parse(){ + id = std::stoi( std::string(idStr) ); + value = std::stod( std::string(valStr) ); + } + paramVal::paramVal(){ realLine = ""; idStr = ""; valStr = ""; } + paramVal::paramVal( std::string_view paramLine, bool parseOnline ) + { + if( paramLine.find("\n") != npos ){ + auto startPos = paramLine.find_first_not_of(" \n", paramLine.find("\n")); + if( startPos!= npos ){ + auto endPos = paramLine.find("\n", startPos); + realLine = paramLine.substr(startPos, endPos - startPos - 1); + } else{ + realLine = paramLine.substr( 0, paramLine.find("\n") - 1 ); + } + } + realLine = paramLine; + auto vals = *blankSplitter( realLine ); + if( vals.size() < 2 ){ return; } + if( clStringComp(vals[0],std::string("set")) ){ + if( vals.size() < 4 ) + throw std::runtime_error("Error while parsing SLHA parameter line --- this appears to be a reweight command, but insufficient arguments were provided."); + idStr = vals[2]; + valStr = vals[3]; + } else { + idStr = vals[0]; + valStr = vals[1]; + } + if( parseOnline ){ + if( vals.size() > 2 ) + { + auto comStart = realLine.find("#"); + comStart = realLine.find_first_not_of( " #", comStart ); + comment = realLine.substr( comStart, realLine.find("\n", comStart) - comStart ); + } + parse(); } + } + bool paramVal::isMod(){ return modded; } + std::shared_ptr paramVal::selfWrite(){ + auto writeVal = std::make_shared(""); + if( isMod() ) + { + for( int k = idStr.size() ; k < 5 ; ++k ){ *writeVal += " "; } + *writeVal += std::string( idStr ) + " " + std::string( valStr ); + if( comment.size() != 0 ){ + *writeVal += " # " + std::string( comment ); + } + *writeVal += "\n"; + } + else{ *writeVal = std::string( realLine ) + "\n"; } + return writeVal; + } + + // ZW: struct for handling single DECAY line + // in SLHA format parameter card + void decVal::parse() { + auto vals = *blankSplitter( realLine ); + id = std::stoi( std::string(vals[1]) ); + value = std::stod( std::string(vals[2]) ); + if( vals.size() > 3 ) + { + auto comStart = realLine.find("#"); + comment = realLine.substr( comStart, realLine.find("\n", comStart) - comStart ); + } + } + decVal::decVal( std::string_view paramLine, bool parseOnline ) : paramVal( paramLine, false ) + { + if( parseOnline ){ parse(); } + } + std::shared_ptr decVal::selfWrite() { + auto writeVal = std::make_shared(""); + if( isMod() ) + { + *writeVal += "DECAY " + std::string( idStr ) + " " + std::string( valStr ); + if( comment.size() != 0 ){ + *writeVal += " # " + std::string( comment ); + } + *writeVal += "\n"; + } + else{ *writeVal = std::string( realLine ) + "\n"; } + return writeVal; + } + + // ZW: struct for handling parameter block + // in SLHA format parameter card + void paramBlock::parse( bool parseOnline ){ + if( realBlock.size() == 0 ){ return; } + if( !(clStringComp(realBlock.substr(startPt+1, 5), std::string("block"))) ){ startPt = clStringFind( realBlock, std::string("\nblock") ); } + auto namePt = realBlock.find_first_not_of( " ", startPt + 7 ); + name = realBlock.substr( namePt, realBlock.find_first_of( " \n", namePt ) - namePt ); + if( realBlock.find( " ", namePt ) < realBlock.find( "\n", namePt ) ) + {comment = realBlock.substr( namePt + name.size(), realBlock.find( "\n", namePt ) - namePt - name.size() ); } + auto paramLines = blockLineStractor( realBlock.substr( startPt ) ); + params.reserve( paramLines.size() ); + for( auto line : paramLines ) + { + params.push_back( paramVal( line, parseOnline ) ); + } + } + paramBlock::paramBlock(){ return; } + paramBlock::paramBlock( std::string_view paramSet, bool parseOnline ) + { + realBlock = paramSet; + startPt = clStringFind( realBlock, std::string("\nB") ); + if( parseOnline ){ parse(parseOnline); } + } + bool paramBlock::isMod(){ return modded; } + std::shared_ptr paramBlock::selfWrite(){ + auto writeBlock = std::make_shared(""); + if( isMod() ) + { + *writeBlock += "\nBLOCK " + std::string(name); + if( comment.size() > 0 ){ + *writeBlock += " # " + std::string( comment ); + } + *writeBlock += "\n"; + for ( auto val : params ) + { + *writeBlock += *val.selfWrite(); + } + } + else{ if( startPt == npos ){ + *writeBlock += realBlock; + } else { + *writeBlock = realBlock.substr( startPt ); + } } + return writeBlock; + } + + // ZW: struct for handling DECAY lines + // in SLHA format parameter card + void decBlock::parse( bool parseOnline ){ + if( realBlock.size() == 0 ){ return; } + auto decLines = clFindEach( realBlock, std::string("\ndecay") ); + decays.reserve(decLines->size()); + if( realBlock.size() > 5 ){ if( clStringComp( realBlock.substr(0,5), std::string("decay")) ) + { decays.push_back( decVal(realBlock.substr( 0, realBlock.find("\n") ), parseOnline) ); } } + for( auto pts : *decLines ) + { + auto lineBr = realBlock.find( "\n", pts + 1 ); + if( lineBr == npos ){ decays.push_back( decVal( realBlock.substr( pts + 1), parseOnline ) ); continue; } + decays.push_back( decVal( realBlock.substr( pts + 1, lineBr - pts - 1 ), parseOnline ) ); + } + } + void decBlock::parse( std::shared_ptr> decLines, bool parseOnline ) { + decays.reserve(decLines->size()); + if( realBlock.size() > 5 ){ if( clStringComp( realBlock.substr(0,5), std::string("decay")) ) + { decays.push_back( decVal(realBlock.substr( 0, realBlock.find("\n") ), parseOnline) ); } } + for( auto pts : *decLines ) + { + auto lineBr = realBlock.find( "\n", pts + 1 ); + if( lineBr == npos ){ decays.push_back( decVal( realBlock.substr( pts + 1), parseOnline ) ); continue; } + decays.push_back( decVal( realBlock.substr( pts + 1, lineBr - pts - 1 ), parseOnline ) ); + } + } + decBlock::decBlock( std::string_view paramSet, bool parseOnline ) : paramBlock( paramSet, parseOnline ) + { + realBlock = paramSet; + if( parseOnline ){ parse(parseOnline); } + } + std::shared_ptr decBlock::selfWrite() { + auto writeBlock = std::make_shared(""); + *writeBlock += "\n"; + for ( auto val : decays ) + { + *writeBlock += *val.selfWrite(); + } + return writeBlock; + } + + // ZW: struct for handling SLHA parameter cards + void lesHouchesCard::parse( bool parseOnline ) + { + if( parsed ){ return; } + if( xmlFile.substr(start,1).find_first_of("BbDd#") == npos ){ start = clStringFindIf( xmlFile, std::string("\n"), lambdaNu ); } + auto blockPts = clFindEach( xmlFile, std::string("\nblock") ); + auto decLines = clFindEach( xmlFile, std::string("\ndecay") ); + header = xmlFile.substr( start, std::min( blockPts->at(0), decLines->at(0) ) - start ); + for( size_t k = 0 ; k < blockPts->size() - 1 ; ++k ) + { + blocks.push_back( paramBlock( xmlFile.substr( blockPts->at(k), blockPts->at(k+1) - blockPts->at(k) ), parseOnline ) ); + } + blocks.push_back(paramBlock(xmlFile.substr(blockPts->at(blockPts->size()-1), clStringFindIf( xmlFile, std::string("\n"), + lambda, blockPts->at(blockPts->size()-1) + 1) - blockPts->at(blockPts->size()-1)), parseOnline)); + decays = decBlock( xmlFile ); + decays.parse( decLines, parseOnline ); + parsed = true; + } + lesHouchesCard::lesHouchesCard( const std::string_view originFile, const size_t& begin, bool parseOnline ){ + xmlFile = originFile; start = begin; + modded = false; blockStart = clStringFindIf( xmlFile, std::string("\n"), lambda, start + 1); end = xmlFile.find(" lesHouchesCard::selfWrite(){ + auto writeCard = std::make_shared(header); + if( isMod() ) + { + for( auto block : blocks ) + { *writeCard += *block.selfWrite(); } + *writeCard += *decays.selfWrite(); } + else{ + if( end != npos ){ *writeCard += std::string( xmlFile.substr( blockStart, end - blockStart ) ); + } else{ *writeCard += std::string( xmlFile.substr( blockStart ) ); } + } + return writeCard; + } + + std::shared_ptr slhaNode::getParameters(){ + modded = true; + return parameterCard; + } + slhaNode::slhaNode() : xmlNode(){} + slhaNode::slhaNode( lesHouchesCard parameters ) : xmlNode(){ + parameterCard = std::make_shared( parameters ); + pCardInit = true; + } + slhaNode::slhaNode( std::shared_ptr parameters ) : xmlNode(){ + parameterCard = parameters; + pCardInit = true; + } + slhaNode::slhaNode( xmlNode& node, bool parseOnline ) : xmlNode( node ){ + parameterCard = std::make_shared( node.getFile(), node.getStart(), parseOnline ); + } + slhaNode::slhaNode( xmlNode* node, bool parseOnline ) : xmlNode( *node ){ + parameterCard = std::make_shared( node->getFile(), node->getStart(), parseOnline ); + } + slhaNode::slhaNode( std::shared_ptr node, bool parseOnline ) : xmlNode( *node ){ + parameterCard = std::make_shared( node->getFile(), node->getStart(), parseOnline ); + } + slhaNode::slhaNode( xmlTree tree, bool parseOnline ) : xmlNode( tree ){ + parameterCard = std::make_shared( tree.getOrigin(), tree.getStart(), parseOnline ); + } + slhaNode::slhaNode( std::shared_ptr tree, bool parseOnline ) : xmlNode( *tree ){ + parameterCard = std::make_shared( tree->getOrigin(), tree->getStart(), parseOnline ); + } + slhaNode::slhaNode( xmlTree* tree, bool parseOnline ) : xmlNode( *tree ){ + parameterCard = std::make_shared( tree->getOrigin(), tree->getStart(), parseOnline ); + } + slhaNode::slhaNode( const std::string_view originFile, const size_t& begin, bool parseOnline ) + : xmlNode( originFile, begin ){ + if( parse() ){ parameterCard = std::make_shared( content, begin, parseOnline ); pCardInit = true; } + } + void slhaNode::headWriter(){ + nodeHeader = "getId()) + "=\"" + std::string(tag->getVal()) + "\""; + } + nodeHeader += ">"; + } + void slhaNode::endWriter(){ nodeEnd += "\n"; } + void slhaNode::contWriter(){ + if( pCardInit ){ + nodeContent = *parameterCard->selfWrite(); + } else { + nodeContent = content; + } + } + + // ZW: struct for handling LHE init nodes + std::shared_ptr initNode::getHead(){ return initHead; } + std::vector> initNode::getLines(){ return initLines; } + void initNode::setHead( std::shared_ptr head ){ modded = true; initHead = head; } + void initNode::setLines( std::vector> lines ){ modded = true; initLines = lines; initHead->nprup = std::to_string( initLines.size() ); } + void initNode::addLine( std::shared_ptr line ){ modded = true; initLines.push_back( line ); initHead->nprup = std::to_string( initLines.size() ); } + initNode::initNode() : xmlNode(){ name = "init"; } + initNode::initNode( const std::string_view originFile, const size_t& begin, bool parseOnline ) + : xmlNode( originFile, begin ){ + if( parseOnline ){ parse( parseOnline ); } + } + initNode::initNode( xmlNode& node, bool parseOnline ) : xmlNode( node ){ + if( parseOnline ){ parse( parseOnline ); } + } + initNode::initNode( xmlNode* node, bool parseOnline ) : xmlNode( *node ){ + if( parseOnline ){ parse( parseOnline ); } + } + initNode::initNode( std::shared_ptr node, bool parseOnline ) : xmlNode( *node ){ + if( parseOnline ){ parse( parseOnline ); } + } + initNode::initNode( xmlTree tree, bool parseOnline ) : xmlNode( tree ){ + if( parseOnline ){ parse( parseOnline ); } + } + initNode::initNode( std::shared_ptr tree, bool parseOnline ) : xmlNode( *tree ){ + if( parseOnline ){ parse( parseOnline ); } + } + initNode::initNode( xmlTree* tree, bool parseOnline ) : xmlNode( *tree ){ + if( parseOnline ){ parse( parseOnline ); } + } + bool initNode::parseContent(){ + if( content.size() == 0 ){ return false; } + auto lines = lineSplitter( content ); + if( lines->size() == 0 ){ return false; } + initHead = std::make_shared(lines->at(0) ); + for( size_t k = 1 ; k < lines->size() ; ++k ){ + initLines.push_back( std::make_shared( lines->at(k) ) ); + } + return true; + } + void initNode::contWriter(){ + if( isModded() ){nodeContent = std::string( content ); return; } + nodeContent = *initHead->getContent(); + for( auto line : initLines ){ + nodeContent += *line->getContent(); + } + } + + // ZW: struct for explicitly handling LHE header nodes + size_t lheHead::addWgtGroup( std::shared_ptr& wgtGroup ){ + hasRwgt = true; + modded = true; + if( wgtGrpInit( wgtGroup ) ){ + rwgtNodes->addGroup( wgtGroup ); + } + return (rwgtNodes->noGrps() - 1); + } + size_t lheHead::addWgtGroup( weightGroup wgtGroup ){ + hasRwgt = true; + modded = true; + auto wgtGrpPtr = std::make_shared( wgtGroup ); + if( wgtGrpInit( wgtGrpPtr ) ){ + rwgtNodes->addGroup( std::make_shared( wgtGroup ) ); + } + return (rwgtNodes->noGrps() - 1); + } + void lheHead::addWgt( size_t index, std::shared_ptr nuWgt ){ + if( index >= (size_t)rwgtNodes->getGroups().size() ) + throw std::range_error( "Appending weight to uninitialised weightgroup." ); + hasRwgt = true; + modded = true; + rwgtNodes->addWgt( index, nuWgt ); + } + void lheHead::addWgt( size_t index, headWeight nuWgt ){ + if( index >= (size_t)rwgtNodes->getGroups().size() ) + throw std::range_error( "Appending weight to uninitialised weightgroup." ); + hasRwgt = true; + modded = true; + rwgtNodes->addWgt( index, nuWgt ); + } + void lheHead::addWgt( size_t index, std::shared_ptr nuWgt, std::string idTagg ){ + if( index >= (size_t)rwgtNodes->getGroups().size() ) + throw std::range_error( "Appending weight to uninitialised weightgroup." ); + hasRwgt = true; + modded = true; + nuWgt->setId( idTagg ); + rwgtNodes->addWgt( index, nuWgt ); + } + void lheHead::addWgt( size_t index, headWeight nuWgt, std::string idTagg ){ + if( index >= (size_t)rwgtNodes->getGroups().size() ) + throw std::range_error( "Appending weight to uninitialised weightgroup." ); + hasRwgt = true; + modded = true; + nuWgt.setId( idTagg ); + rwgtNodes->addWgt( index, nuWgt ); + } + void lheHead::setInitRwgt( initRwgt initWgt ){ hasRwgt = true; modded = true; rwgtNodes = std::make_shared(initWgt); } + void lheHead::setInitRwgt( std::shared_ptr initWgt ){ hasRwgt = true; modded = true; rwgtNodes = initWgt; } + std::vector> lheHead::getWgtGroups(){ return rwgtNodes->getGroups(); } + std::shared_ptr lheHead::getInitRwgt(){ return rwgtNodes; } + std::shared_ptr lheHead::getParameters(){ return parameters; } + void lheHead::setParameters( std::shared_ptr params ){ parameters = params; } + bool lheHead::rwgtInc(){ return hasRwgt; } + lheHead::lheHead(){ return; } + lheHead::lheHead( const std::string_view originFile, const size_t& begin, const std::vector>& childs ) + : xmlNode(originFile, begin, childs){ + xmlFile = originFile; start = begin; children = childs; size_t trueStart = originFile.find_first_not_of(" ", begin+1); + if( trueStart != npos ){name = originFile.substr( trueStart, originFile.find_first_of(">/ ", trueStart) - trueStart );} + for( auto child : children ){ + if (child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if (child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + lheHead::lheHead( xmlNode& node ) : xmlNode(node){ + for( auto child : node.getChildren() ){ + if ( child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if ( child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + lheHead::lheHead( xmlNode* node ) : xmlNode(*node){ + for( auto child : node->getChildren() ){ + if ( child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if ( child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + lheHead::lheHead( std::shared_ptr node ) : xmlNode( *node ){ + for( auto child : node->getChildren() ){ + if ( child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if ( child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + lheHead::lheHead( xmlTree tree ) : xmlNode( tree ){ + for( auto child : children ){ + if ( child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if ( child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + lheHead::lheHead( std::shared_ptr tree ) : xmlNode( *tree ){ + for( auto child : children ){ + if ( child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if ( child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + lheHead::lheHead( xmlTree* tree ) : xmlNode( *tree ){ + for( auto child : children ){ + if ( child->getName() == "slha" ){ parameters = std::make_shared( *child ); continue; } + if ( child->getName() == "initrwgt" ){ rwgtNodes = std::make_shared( *child ); continue; } + } + } + bool lheHead::wgtGrpInit( std::shared_ptr& wgtGrp ){ + if( wgtGrpIsInit ){ return true; } + if( rwgtNodes == nullptr ){ + rwgtNodes = std::make_shared(); + wgtGrpIsInit = true; + rwgtNodes->addGroup( wgtGrp ); + return false; + } else throw std::runtime_error( "Error while initiating return LHE file header (initrwgt node is defined in an unrecognised manner)." ); + } + void lheHead::setRelChild(){ + if( relChildSet ){ return; } + relChild.reserve( children.size() ); + for( size_t k = 0 ; k < children.size() ; ++k ){ + auto child = &children[k]; + if( (*child)->getName() == "slha" ){ continue; } + if( (*child)->getName() == "initrwgt" ){ continue; } + relChild.push_back( k ); + } + relChildSet = true; + } + bool lheHead::parseChildren( bool recursive ){ + bool status = true; + for( auto child : children ){ + if( child->getName() == "slha" || child->getName() == "initrwgt" ){ continue; } + child->parser( recursive ); + status = (status && child->isParsed() ); + deepParsed = true; + } + return status; + } + void lheHead::headWriter(){ + nodeHeader = "getId()) + "=\"" + std::string(tag->getVal()) + "\""; + } + nodeHeader += ">\n"; + } + void lheHead::childWriter(){ + setRelChild(); + for( auto relKid : relChild ){ + nodeContent += *(children[relKid]->nodeWriter()); + } + if( parameters != nullptr ){ nodeContent += *parameters->nodeWriter(); } + if( hasRwgt ){ + nodeContent += *rwgtNodes->nodeWriter(); + } + } + void lheHead::fullWriter(){ + if( isModded() ){ + headWriter(); + contWriter(); + childWriter(); + endWriter(); + writtenSelf = std::make_shared( nodeHeader + nodeContent + nodeEnd ); + written = true; + } + } + + // ZW: struct for keeping track of appended weights in LHE node, + // since weight information is stored both in the header + // and in the individual events + newWgt::newWgt( std::shared_ptr heaWgt, std::vector> bodWgts ){ + headWgt = heaWgt; bodyWgts = bodWgts; + } + newWgt::newWgt( std::shared_ptr heaWgt, std::shared_ptr> wgts ){ + headWgt = heaWgt; + bodyWgts = std::vector>(wgts->size()); + auto idTag = std::string(headWgt->getTag()); + if( idTag != "" ){ + for( size_t i = 0 ; i < wgts->size() ; ++i ){ + bodyWgts[i] = std::make_shared(wgts->at(i), idTag); + } + } else{ + for( size_t i = 0 ; i < wgts->size() ; ++i ){ + bodyWgts[i] = std::make_shared(wgts->at(i)); + } + } + } + newWgt::newWgt( std::string_view parameters, std::shared_ptr> wgts, std::string idTag ){ + headWgt = std::make_shared(parameters, idTag); + bodyWgts = std::vector>(wgts->size()); + for( size_t i = 0 ; i < wgts->size() ; ++i ){ + bodyWgts[i] = std::make_shared(wgts->at(i), idTag); + } + } + newWgt::newWgt( std::string_view parameters, int idNum, std::shared_ptr> wgts, std::string idTag ){ + std::string newTag = std::string( idTag ) + "_" + std::to_string( idNum ); + headWgt = std::make_shared(parameters, newTag); + bodyWgts = std::vector>(wgts->size()); + for( size_t i = 0 ; i < wgts->size() ; ++i ){ + bodyWgts[i] = std::make_shared(wgts->at(i), newTag); + } + } + newWgt::newWgt( std::string& parameters ){ + headWgt = std::make_shared(parameters); + } + newWgt::newWgt( std::string& parameters, std::string& idTag ){ + headWgt = std::make_shared(parameters, idTag); + } + std::shared_ptr newWgt::getHeadWgt(){ return headWgt; } + std::vector> newWgt::getBodyWgts(){ return bodyWgts; } + void newWgt::addBdyWgts( std::shared_ptr> wgts ){ + auto idTag = std::string(headWgt->getTag()); + if( idTag != "" ){ + for( size_t i = 0 ; i < wgts->size() ; ++i ){ + bodyWgts[i] = std::make_shared(wgts->at(i), idTag); + } + } else{ + for( size_t i = 0 ; i < wgts->size() ; ++i ){ + bodyWgts[i] = std::make_shared(wgts->at(i)); + } + } + } + + // ZW: general struct for handling LHE files explicitly + lheNode::lheNode() : xmlNode(){} + lheNode::lheNode( const std::string_view originFile, const size_t& begin, const std::vector>& childs ) + : xmlNode(originFile, begin, childs){ + for( auto child : children ){ + if( child->getName() == "header" ){ header = std::make_shared( *child ); continue; } + if( child->getName() == "init" ){ init = std::make_shared( *child, true ); continue; } + if( child->getName() == "event" ){ events.push_back( std::make_shared( *child ) ); continue; } + } + } + std::shared_ptr lheNode::getHeader(){ return header; } + std::shared_ptr lheNode::getInit(){ return init; } + std::vector> lheNode::getEvents(){ return events; } + bool lheNode::isModded(){ return modded; } + bool lheNode::isModded( bool deep ){ + if( !deep ){ return isModded(); } + bool modStat = isModded(); + for( auto child : children ){ modStat = ( modStat || child->isModded( deep ) ); } + for( auto event : events ){ modStat = ( modStat || event->isModded( deep ) ); } + return modStat; + } + void lheNode::setInit( std::shared_ptr initNod ){ init = initNod; } + void lheNode::setHeader( std::shared_ptr headNod ){ header = headNod; } + void lheNode::addWgt( size_t index, newWgt& addedWgt ){ + header->addWgt( index, addedWgt.getHeadWgt() ); + auto wgtsVec = addedWgt.getBodyWgts(); + for( size_t k = 0 ; k < wgtsVec.size() ; ++k ){ + events[k]->addWgt( wgtsVec[k] ); + } + } + void lheNode::addWgt( size_t index, newWgt& addedWgt, std::string& idTag ){ + header->addWgt( index, addedWgt.getHeadWgt(), idTag ); + auto wgtsVec = addedWgt.getBodyWgts(); + for( size_t k = 0 ; k < wgtsVec.size() ; ++k ){ + events[k]->addWgt( wgtsVec[k] ); + } + } + void lheNode::setRelStats( std::vector& particles ){ + relStat = particles; + } + std::vector& lheNode::getRelStats(){ + return relStat; + } + void lheNode::setSameSort( sortFcn& sortF ){ + particleSort = sortF; + } + sortFcn& lheNode::getSameSort(){ + return particleSort; + } + void lheNode::setStatSort( statSort& statS ){ + statParticleSort = statS; + } + statSort& lheNode::getStatSort(){ + return statParticleSort; + } + void lheNode::headerWriter(){ + nodeContent += "\n" + *header->nodeWriter(); + } + void lheNode::initWriter(){ + nodeContent += *init->nodeWriter(); + } + void lheNode::eventWriter(){ + for( auto event : events ){ + nodeContent += *event->nodeWriter(); + } + } + void lheNode::contWriter(){ + nodeContent = ""; + headerWriter(); + initWriter(); + eventWriter(); + } + void lheNode::fullWriter(){ + if( isModded( true ) ){ + headWriter(); + contWriter(); + endWriter(); + writtenSelf = std::make_shared( nodeHeader + nodeContent + nodeEnd ); + written = true; + modded = false; + } else if( !isWritten() ){ + writtenSelf = std::make_shared( xmlFile.substr(start, end - start ) ); + written = true; + } + } + std::shared_ptr lheNode::nodeWriter() { + if( isModded( true ) || !isWritten() ){ fullWriter(); } + return writtenSelf; + } + + // ZW: struct for treating individual HEP + // processes, formatted based on PDG codes + // and the LHE particle status standard + // struct lheProc { + // public: + // std::vector minusOne; + // std::vector plusOne; + // std::vector minusTwo; + // std::vector plusTwo; + // std::vector plusThree; + // std::vector minusNine; + // std::vector orderMOne; + // std::vector orderOne; + // std::vector orderMTwo; + // std::vector orderTwo; + // std::vector orderThree; + // std::vector orderNine; + // std::map> valVecs{{"-1", minusOne}, {"1", plusOne}, {"-2", minusTwo}, {"2", plusTwo}, {"3", plusThree}, {"-9", minusNine}}; + // std::map> orderVecs{{"-1", orderMOne}, {"1", orderOne}, {"-2", orderMTwo}, {"2", orderTwo}, {"3", orderThree}, {"9",orderNine}}; + // lheProc( event& eventNode ) + // { + // for( auto prt : eventNode.getPrts() ) + // { + // valVecs[prt->getStatus()].push_back(prt->getPDG()); + // } + // for( auto valVec = valVecs.begin() ; valVec!= valVecs.end() ; ++valVec ){ + // if( valVec->second.size() == 0 ){ continue; } + // orderVecs[valVec->first] = *stoiSort( valVec->second ); + // } + // } + // std::shared_ptr writer(){ + // auto written = std::make_shared(); + // for( auto inits : valVecs["-1"] ){ + // written->append(inits); + // written->append(" "); + // } + // if( valVecs["2"].size() > 0 ){ + // written->append("> "); + // for( auto inits : valVecs["2"] ){ + // written->append(inits); + // written->append(" "); + // } + // } + // written->append("> "); + // for( auto inits : valVecs["1"] ){ + // written->append(inits); + // written->append(" "); + // } + // return written; + // } + // }; + + // ZW: fcn for uploading text files to the program + std::shared_ptr filePuller( const std::string& fileLoc ) + { + std::ifstream fileLoad( fileLoc ); + std::stringstream buffer; + buffer << fileLoad.rdbuf(); + auto fileContent = std::make_shared(buffer.str()); + //std::transform( fileContent->begin(), fileContent->end(), fileContent->begin(), ::tolower ); + buffer.str(std::string()); + fileLoad.close(); + return fileContent; + } + + // ZW: fcn for saving std::string to disk + bool filePusher( std::string fileLoc, std::string fileCont ) + { + std::ofstream fileWrite( fileLoc ); + if(!fileWrite){return false;} + fileWrite << fileCont; + fileWrite.close(); + return true; + } + + // ZW: fcn for extracting the full + // process information from an LHE event + std::shared_ptr>> pdgXtract( event& currEv ) + { + auto currProc = std::make_shared>>(); + auto &useProc = *currProc; + for( auto prt : currEv.getPrts() ) + { + useProc[ prt->getStatus() ].push_back(prt->getPDG()); + } + return currProc; + } + + template + bool chaoticVecComp( const std::vector& vec1, const std::vector order1, const std::vector& vec2, const std::vector order2 ) + { + if( vec1.size()!= vec2.size() ){ return false; } + for( size_t i = 0; i < vec1.size(); i++ ){ + if( vec1[order1[i]]!= vec2[order2[i]] ){ return false; } + } + return true; + } + + // ZW: fcn for comparing two processes in the + // format output by pdgXtract + bool sameProcString( std::map>& firstVec, std::map>& secVec, const std::vector& statVec ) + { + if( firstVec.size() != secVec.size() ){return false;} + for(auto code : statVec ) + { + if( firstVec[code] != secVec[code] ){ return false; } + } + return true; + } + + bool sameProcString( std::map>& firstVec, std::map>& firstOrder, + std::map>& secVec, std::map>& secondOrder, + std::vector& statVec ) + { + if( firstVec.size() != secVec.size() ){return false;} + for(auto code : statVec ) + { + if( !chaoticVecComp(firstVec[code], firstOrder[code], secVec[code], secondOrder[code]) ){ return false; } + } + return true; + } + + // ZW: fcn for processes in the lheProc struct format + // bool procComp( lheProc& firstProc, lheProc& secProc, std::vector statVec ) + // { + // for( auto stat : statVec ) + // { + // if( firstProc.valVecs.at(stat).size() != secProc.valVecs.at(stat).size() ){ return false; } + // if( !chaoticVecComp( firstProc.valVecs[stat], firstProc.orderVecs[stat], secProc.valVecs[stat], secProc.orderVecs[stat] ) ){ return false; } + // } + // return true; + // } + + bool evProcComp( event& firstEv, event& secEv, std::vector statVec = {-1, 1} ) + { + for( auto stat : statVec ) + { + if( firstEv.getProc()[stat].size()!= secEv.getProc()[stat].size() ){ return false; } + if(!chaoticVecComp( firstEv.getProc()[stat], firstEv.getProcOrder()[stat], + secEv.getProc()[stat], secEv.getProcOrder()[stat] ) ){ return false; } + } + return true; + } + + bool evProcComp( event& firstEv, event& secEv, std::vector statVec, + sortFcn sorter ) + { + for( auto stat : statVec ) + { + if( firstEv.getProc(sorter)[stat].size()!= secEv.getProc(sorter)[stat].size() ){ return false; } + if(!chaoticVecComp( firstEv.getProc(sorter)[stat], firstEv.getProcOrder(sorter)[stat], + secEv.getProc(sorter)[stat], secEv.getProcOrder(sorter)[stat] ) ){ return false; } + } + return true; + } + + bool evProcComp( event& firstEv, event& secEv, std::vector statVec, + statSort sorter ) + { + for( auto stat : statVec ) + { + if( firstEv.getProc(sorter)[stat].size()!= secEv.getProc(sorter)[stat].size() ){ return false; } + if(!chaoticVecComp( firstEv.getProc(sorter)[stat], firstEv.getProcOrder(sorter)[stat], + secEv.getProc(sorter)[stat], secEv.getProcOrder(sorter)[stat] ) ){ return false; } + } + return true; + } + + bool evProcComp( const event& firstEv, const event& secEv, std::vector statVec = {-1, 1} ) + { + for( auto stat : statVec ) + { + if( firstEv.getProc().at(stat).size()!= secEv.getProc().at(stat).size() ){ return false; } + if(!chaoticVecComp( firstEv.getProc().at(stat), firstEv.getProcOrder().at(stat), + secEv.getProc().at(stat), secEv.getProcOrder().at(stat) ) ){ return false; } + } + return true; + } + + bool evProcComp( const event& firstEv, const event& secEv, std::vector statVec, + sortFcn sorter ) + { + for( auto stat : statVec ) + { + if( firstEv.getProc().at(stat).size()!= secEv.getProc().at(stat).size() ){ return false; } + if(!chaoticVecComp( firstEv.getProc().at(stat), firstEv.getProcOrder().at(stat), + secEv.getProc().at(stat), secEv.getProcOrder().at(stat) ) ){ return false; } + } + return true; + } + + bool evProcComp( const event& firstEv, const event& secEv, std::vector statVec, + statSort sorter ) + { + for( auto stat : statVec ) + { + if( firstEv.getProc().at(stat).size()!= secEv.getProc().at(stat).size() ){ return false; } + if(!chaoticVecComp( firstEv.getProc().at(stat), firstEv.getProcOrder().at(stat), + secEv.getProc().at(stat), secEv.getProcOrder().at(stat) ) ){ return false; } + } + return true; + } + + bool eventComp::operator()( event& firstEv, event& secEv){ + if( firstEv.isSpecSort() ) {return evProcComp( firstEv, secEv, {-1, 1}, firstEv.getStatSort());} + else {return evProcComp( firstEv, secEv, {-1, 1}, firstEv.getSortFcn() );} + } + bool eventComp::operator()( const event& firstEv, const event& secEv) const { + if( firstEv.isSpecSort() ) {return evProcComp( firstEv, secEv, {-1, 1}, firstEv.getStatSort());} + else {return evProcComp( firstEv, secEv, {-1, 1}, firstEv.getSortFcn() );} + } + bool eventComp::operator()(event& firstEv, event& secEv, std::vector statVec){ + if( firstEv.isSpecSort() ) {return evProcComp( firstEv, secEv, statVec, firstEv.getStatSort());} + else {return evProcComp( firstEv, secEv, statVec, firstEv.getSortFcn() );} + } + + // ZW: fcn for checking whether a list of pdgXtract format + // processes sourceProcList contains a given process newProc + bool procVecContains( std::vector>>>& sourceProcList, + std::map>& newProc, const std::vector& statVec ) + {\ + for( auto proc : sourceProcList ) + { + if( sameProcString( *proc, newProc, statVec ) ){ return true; } + } + return false; + } + + // ZW: fcn for checking whether a vector of lheProc structs + // procList contains a given lheProc nuProc + // bool procListComp( const std::vector>& procList, lheProc& nuProc, std::vector statVec ) + // { + // if( procList.size() != 0 ){ + // for(auto proc : procList ) + // { + // if( procComp( *proc, nuProc, statVec ) ){ return true; } + // } + // } + // return false; + // } + + bool evProcListComp( std::vector>& procList, event& nuEv, std::vector statVec ) + { + if( procList.size()!= 0 ){ + for( auto ev : procList ) + { + if( evProcComp( *ev, nuEv, statVec ) ){ return true; } + } + } + return false; + } + + bool evProcListComp( std::vector>& procList, event& nuEv, std::vector statVec, + sortFcn sorter ) + { + if( procList.size()!= 0 ){ + for( auto ev : procList ) + { + if( evProcComp( *ev, nuEv, statVec, sorter ) ){ return true; } + } + } + return false; + } + + bool evProcListComp( std::vector>& procList, event& nuEv, std::vector statVec, + statSort sorter ) + { + if( procList.size()!= 0 ){ + for( auto ev : procList ) + { + if( evProcComp( *ev, nuEv, statVec, sorter ) ){ return true; } + } + } + return false; + } + + // ZW: fcn for extracting the different processes + // in a given REX format LHE file in the pdgXtract format + // std::vector>>> procExtractor( lheNode& lheFile ) + // { + // std::vector>>> procList; + // const static std::vector statVec = { -1, 1, -2, 2, 3, -9 }; + // for( auto event : lheFile.getEvents() ) + // { + // auto currProc = pdgXtract( *event ); + // if( procVecContains( procList, *currProc, statVec ) ){ continue; } + // procList.push_back(currProc); + // } + // return procList; + // } + + // ZW: fcn for extracting the different processes + // in a given REX format LHE file in the lheProc format + // std::vector> processPull( lheNode& lheFile, + // std::vector statVec = { "-1", "1" } ) + // { + // //const static std::vector statVec = { "-1", "1", "-2", "2", "3", "-9" }; + // std::vector> procsList{}; + // for( auto event : lheFile.getEvents() ) + // { + // auto currProc = std::make_shared( *event ); + // if( procListComp( procsList, *currProc, statVec ) ){ continue; } + // procsList.push_back( currProc ); + // } + // return procsList; + // } + + std::vector> evProcessPull( lheNode& lheFile, std::vector statVec = { -1, 1 } ) + { + //const static std::vector statVec = { "-1", "1", "-2", "2", "3", "-9" }; + std::vector> procsList{}; + for( auto currEv : lheFile.getEvents() ) + { + if( evProcListComp( procsList, *currEv, statVec ) ){ continue; } + procsList.push_back( currEv ); + } + return procsList; + } + + std::vector> evProcessPull( lheNode& lheFile, + sortFcn sorter, + std::vector statVec = { -1, 1 }) + { + //const static std::vector statVec = { "-1", "1", "-2", "2", "3", "-9" }; + std::vector> procsList{}; + lheFile.setSameSort(sorter); + for( auto currEv : lheFile.getEvents() ) + { + if( evProcListComp( procsList, *currEv, statVec, sorter ) ){ continue; } + procsList.push_back( currEv ); + } + return procsList; + } + + std::vector> evProcessPull( lheNode& lheFile, + statSort sorter, + std::vector statVec = { -1, 1 }) + { + //const static std::vector statVec = { "-1", "1", "-2", "2", "3", "-9" }; + std::vector> procsList{}; + lheFile.setStatSort(sorter); + for( auto currEv : lheFile.getEvents() ) + { + if( evProcListComp( procsList, *currEv, statVec, sorter ) ){ continue; } + procsList.push_back( currEv ); + } + return procsList; + } + + // ZW: fcn for keeping track of subprocess ordering + // in LHE file + // size_t procPos( const std::vector>& evtSet, lheProc& currProc, + // std::vector& statVec ) + // { + // for( size_t k = 0 ; k < evtSet.size() ; ++k ) + // { + // for( auto stat : statVec ) + // { + // if( evtSet[k]->valVecs[stat] != currProc.valVecs[stat] ){ break; } + // } + // return k; + // } + // return evtSet.size(); + // } + + size_t evProcPos( const std::vector>& evtSet, event& currEv, + std::vector statVec = { -1, 1 } ) + { + for( size_t k = 0 ; k < evtSet.size() ; ++k ) + { + if( evProcComp(*evtSet[k], currEv, statVec) ){ return k; } + } + return evtSet.size(); + } + + size_t evProcPos( const std::vector>& evtSet, event& currEv, + sortFcn sorter, std::vector statVec = {-1, 1} ) + { + for( size_t k = 0 ; k < evtSet.size() ; ++k ) + { + if( evProcComp(*evtSet[k], currEv, statVec, sorter) ){ return k; } + } + return evtSet.size(); + } + + size_t evProcPos( const std::vector>& evtSet, event& currEv, + statSort sorter, std::vector statVec = {-1, 1} ) + { + for( size_t k = 0 ; k < evtSet.size() ; ++k ) + { + if( evProcComp(*evtSet[k], currEv, statVec, sorter) ){ return k; } + } + return evtSet.size(); + } + + // ZW: fcn for extracting the subprocess ordering + // of LHE file + // std::vector>> procOrder( lheNode& lheFile, const std::vector>& evtSet, + // std::vector statVec = { "-1", "1" } ) + // { + // //const static std::vector statVec = { "-1", "1", "-2", "2", "3", "-9" }; + // std::vector>> eventBools( evtSet.size(), std::make_shared> ( lheFile.getEvents().size() )); + // //std::vector> pracBools( evtSet.size(), std::vector ( lheFile.getEvents().size() )); + // for( auto boolSets : eventBools ){ + // std::fill( boolSets->begin(), boolSets->end(), false ); + // } + // for( size_t k = 0 ; k < lheFile.getEvents().size() ; ++k ) + // { + // auto currProc = lheProc(*lheFile.getEvents()[k]); + // eventBools[ procPos(evtSet, currProc, statVec) ]->at( k ) = true; + // } + // //for( size_t k = 0 ; k < eventBools.size() ; ++k ) + // //{ + // // eventBools[k] = std::make_shared>( pracBools[k] ); + // //} + // return eventBools; + // } + + std::vector>> evProcOrder( lheNode& lheFile, const std::vector>& evtSet, + std::vector statVec = { -1, 1 } ) + { + std::vector>> eventBools; + eventBools.reserve(evtSet.size()); + for (size_t i = 0; i < evtSet.size(); ++i) { + eventBools.push_back(std::make_shared>(lheFile.getEvents().size(), false)); + } + for( size_t k = 0 ; k < lheFile.getEvents().size() ; ++k ) + { + eventBools[ evProcPos(evtSet, *lheFile.getEvents()[k], statVec) ]->at( k ) = true; + } + return eventBools; + } + + std::vector>> evProcOrder( lheNode& lheFile, const std::vector>& evtSet, + sortFcn sorter, + std::vector statVec = { -1, 1 } ) + { + std::vector>> eventBools; + eventBools.reserve(evtSet.size()); + for (size_t i = 0; i < evtSet.size(); ++i) { + eventBools.push_back(std::make_shared>(lheFile.getEvents().size(), false)); + } + for( size_t k = 0 ; k < lheFile.getEvents().size() ; ++k ) + { + eventBools[ evProcPos(evtSet, *lheFile.getEvents()[k], sorter, statVec) ]->at( k ) = true; + } + return eventBools; + } + + std::vector>> evProcOrder( lheNode& lheFile, const std::vector>& evtSet, + statSort sorter, + std::vector statVec = { -1, 1 } ) + { + std::vector>> eventBools; + eventBools.reserve(evtSet.size()); + for (size_t i = 0; i < evtSet.size(); ++i) { + eventBools.push_back(std::make_shared>(lheFile.getEvents().size(), false)); + } + for( size_t k = 0 ; k < lheFile.getEvents().size() ; ++k ) + { + eventBools[ evProcPos(evtSet, *lheFile.getEvents()[k], sorter, statVec) ]->at( k ) = true; + } + return eventBools; + } + + std::vector>> evProcOrder( lheNode& lheFile, std::vector& evSet ){ + std::vector>> eventBools; + eventBools.reserve(evSet.size()); + for (size_t i = 0; i < evSet.size(); ++i) { + eventBools.push_back(std::make_shared>(lheFile.getEvents().size(), false)); + } + for( size_t k = 0 ; k < lheFile.getEvents().size() ; ++k ) + { + for( size_t i = 0 ; i < evSet.size() ; ++i ) + { + if( evSet[i].belongs( lheFile.getEvents()[k] ) ) + { + eventBools[i]->at(k) = true; + break; + } + } + } + return eventBools; + } + + // ZW: fcn for reordering LHE file based on subprocess + std::shared_ptr>> eventReOrder( lheNode& lheFile, std::vector relProc ) + { + auto reOrdered = std::make_shared>>(); + reOrdered->reserve( std::count( relProc.begin(), relProc.end(), true ) ); + for( size_t k = 0 ; k < relProc.size() ; ++k ) + { + if(!relProc[k]){continue;} + reOrdered->push_back( lheFile.getEvents()[k] ); + } + return reOrdered; + } + + // ZW: wrapper for eventReOrder + // std::vector>>> lheReOrder( lheNode& lheFile, + // std::vector statVec = { "-1", "1" } ) + // { + // auto procSets = processPull( lheFile, statVec ); + // auto relProcs = procOrder( lheFile, procSets, statVec ); + // std::vector>>> ordProcs(procSets.size()); + // for( size_t k = 0 ; k < relProcs.size() ; ++k ) + // { + // ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + // } + // return ordProcs; + // } + + std::vector>>> lheEvReOrder( lheNode& lheFile, + std::vector statVec = { -1, 1 } ) + { + auto procSets = evProcessPull( lheFile, statVec ); + auto relProcs = evProcOrder( lheFile, procSets, statVec ); + std::vector>>> ordProcs(procSets.size()); + for( size_t k = 0 ; k < relProcs.size() ; ++k ) + { + ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + } + return ordProcs; + } + + std::vector>>> lheEvReOrder( lheNode& lheFile, + std::vector> procSets, std::vector>> relProcs, + std::vector statVec = { -1, 1 } ) + { + //auto procSets = evProcessPull( lheFile, statVec ); + //auto relProcs = evProcOrder( lheFile, procSets, statVec ); + std::vector>>> ordProcs(procSets.size()); + for( size_t k = 0 ; k < relProcs.size() ; ++k ) + { + ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + } + return ordProcs; + } + + std::vector>>> lheEvReOrder( lheNode& lheFile, + sortFcn sorter, + std::vector statVec = { -1, 1 } ) + { + auto procSets = evProcessPull( lheFile, sorter, statVec ); + auto relProcs = evProcOrder( lheFile, procSets, sorter, statVec ); + std::vector>>> ordProcs(procSets.size()); + for( size_t k = 0 ; k < relProcs.size() ; ++k ) + { + ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + } + return ordProcs; + } + + std::vector>>> lheEvReOrder( lheNode& lheFile, + std::vector> procSets, std::vector>> relProcs, + sortFcn sorter, std::vector statVec = { -1, 1 } ) + { + //auto procSets = evProcessPull( lheFile, sorter, statVec ); + //auto relProcs = evProcOrder( lheFile, procSets, sorter, statVec ); + std::vector>>> ordProcs(procSets.size()); + for( size_t k = 0 ; k < relProcs.size() ; ++k ) + { + ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + } + return ordProcs; + } + + std::vector>>> lheEvReOrder( lheNode& lheFile, + statSort sorter, + std::vector statVec = { -1, 1 } ) + { + auto procSets = evProcessPull( lheFile, sorter, statVec ); + auto relProcs = evProcOrder( lheFile, procSets, sorter, statVec ); + std::vector>>> ordProcs(procSets.size()); + for( size_t k = 0 ; k < relProcs.size() ; ++k ) + { + ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + } + return ordProcs; + } + + std::vector>>> lheEvReOrder( lheNode& lheFile, + std::vector> procSets, std::vector>> relProcs, + statSort sorter, std::vector statVec = { -1, 1 } ) + { + //auto procSets = evProcessPull( lheFile, sorter, statVec ); + //auto relProcs = evProcOrder( lheFile, procSets, sorter, statVec ); + std::vector>>> ordProcs(procSets.size()); + for( size_t k = 0 ; k < relProcs.size() ; ++k ) + { + ordProcs[k] = eventReOrder( lheFile, *relProcs[k] ); + } + return ordProcs; + } + + // ZW: transposed event information struct + evtInfo::evtInfo( const std::vector>& lheFile ){ + int nEvt = lheFile.size(); + wgts.reserve(nEvt); scales.reserve(nEvt); aQEDs.reserve(nEvt); aQCDs.reserve(nEvt); nprts.reserve(nEvt); procIDs.reserve(nEvt); + for( auto evt : lheFile ) + { + wgts.push_back(evt->getHead().getWeight()); + scales.push_back(evt->getHead().getScale()); + aQEDs.push_back(evt->getHead().getAQED()); + aQCDs.push_back(evt->getHead().getAQCD()); + nprts.push_back(evt->getHead().getNprt()); + procIDs.push_back(evt->getHead().getProcID()); + } + } + evtInfo::evtInfo( const std::vector>& lheFile, const std::vector& statVec ){ + int nEvt = lheFile.size(); + wgts.reserve(nEvt); scales.reserve(nEvt); aQEDs.reserve(nEvt); aQCDs.reserve(nEvt); relNPrts.reserve(nEvt); procIDs.reserve(nEvt); + for( auto evt : lheFile ) + { + wgts.push_back(evt->getHead().getWeight()); + scales.push_back(evt->getHead().getScale()); + aQEDs.push_back(evt->getHead().getAQED()); + aQCDs.push_back(evt->getHead().getAQCD()); + size_t nPrt = 0; + for( auto stat : statVec ){ nPrt += evt->getProc()[stat].size(); } + relNPrts.push_back(nPrt); + procIDs.push_back(evt->getHead().getProcID()); + } + } + evtInfo::evtInfo( const std::vector>& lheFile, const std::vector& statVec, + sortFcn sorter ){ + int nEvt = lheFile.size(); + wgts.reserve(nEvt); scales.reserve(nEvt); aQEDs.reserve(nEvt); aQCDs.reserve(nEvt); relNPrts.reserve(nEvt); procIDs.reserve(nEvt); + for( auto evt : lheFile ) + { + wgts.push_back(evt->getHead().getWeight()); + scales.push_back(evt->getHead().getScale()); + aQEDs.push_back(evt->getHead().getAQED()); + aQCDs.push_back(evt->getHead().getAQCD()); + size_t nPrt = 0; + for( auto stat : statVec ){ nPrt += evt->getProc(sorter)[stat].size(); } + relNPrts.push_back(nPrt); + procIDs.push_back(evt->getHead().getProcID()); + } + } + evtInfo::evtInfo( const std::vector>& lheFile, const std::vector& statVec, + statSort sorter ){ + int nEvt = lheFile.size(); + wgts.reserve(nEvt); scales.reserve(nEvt); aQEDs.reserve(nEvt); aQCDs.reserve(nEvt); relNPrts.reserve(nEvt); procIDs.reserve(nEvt); + for( auto evt : lheFile ) + { + wgts.push_back(evt->getHead().getWeight()); + scales.push_back(evt->getHead().getScale()); + aQEDs.push_back(evt->getHead().getAQED()); + aQCDs.push_back(evt->getHead().getAQCD()); + size_t nPrt = 0; + for( auto stat : statVec ){ nPrt += evt->getProc(sorter)[stat].size(); } + relNPrts.push_back(nPrt); + procIDs.push_back(evt->getHead().getProcID()); + } + } + + // ZW: transposed particle information struct + prtInfo::prtInfo( const std::vector>& lheFile, const int nPrt ){ + int nEvt = lheFile.size(); + moms.reserve(4*nPrt*nEvt); vtims.reserve(nPrt*nEvt); masses.reserve(nPrt*nEvt); pdgs.reserve(nPrt*nEvt); + spins.reserve(nPrt*nEvt); statuses.reserve(nPrt*nEvt); mothers.reserve(2*nPrt*nEvt); icols.reserve(2*nPrt*nEvt); + for( auto evt : lheFile ) + { + for( auto prt : evt->getPrts() ) + { + moms.push_back( prt->getE() ); + masses.push_back( prt->getMass() ); + vtims.push_back( prt->getVTim() ); + spins.push_back( prt->getSpin() ); + statuses.push_back( prt->getStatus() ); + pdgs.push_back( prt->getPDG() ); + for( size_t k = 0 ; k < 2 ; ++k ) + { + moms.push_back( prt->getMom()[k] ); + mothers.push_back( prt->getMothers()[k] ); + icols.push_back( prt->getColor()[k] ); + } + moms.push_back( prt->getMom()[2] ); + } + } + } + prtInfo::prtInfo( const std::vector>& lheFile, const int nPrt, const std::vector& statVec ){ + int nEvt = lheFile.size(); + moms.reserve(4*nPrt*nEvt); vtims.reserve(nPrt*nEvt); masses.reserve(nPrt*nEvt); pdgs.reserve(nPrt*nEvt); + spins.reserve(nPrt*nEvt); statuses.reserve(nPrt*nEvt); mothers.reserve(2*nPrt*nEvt); icols.reserve(2*nPrt*nEvt); + for( auto evt : lheFile ) + { + for( auto stat : statVec ) + { + for( auto i : evt->getProcOrder()[stat] ) + { + auto prt = evt->getPrts()[i]; + moms.push_back( prt->getE() ); + masses.push_back( prt->getMass() ); + vtims.push_back( prt->getVTim() ); + spins.push_back( prt->getSpin() ); + statuses.push_back( prt->getStatus() ); + pdgs.push_back( prt->getPDG() ); + for( size_t k = 0 ; k < 2 ; ++k ) + { + moms.push_back( prt->getMom()[k] ); + mothers.push_back( prt->getMothers()[k] ); + icols.push_back( prt->getColor()[k] ); + } + moms.push_back( prt->getMom()[2] ); + } + } + } + } + prtInfo::prtInfo( const std::vector>& lheFile, const int nPrt, const std::vector& statVec, + sortFcn sorter ){ + int nEvt = lheFile.size(); + moms.reserve(4*nPrt*nEvt); vtims.reserve(nPrt*nEvt); masses.reserve(nPrt*nEvt); pdgs.reserve(nPrt*nEvt); + spins.reserve(nPrt*nEvt); statuses.reserve(nPrt*nEvt); mothers.reserve(2*nPrt*nEvt); icols.reserve(2*nPrt*nEvt); + for( auto evt : lheFile ) + { + for( auto stat : statVec ) + { + for( auto i : evt->getProcOrder(sorter)[stat] ) + { + auto prt = evt->getPrts()[i]; + moms.push_back( prt->getE() ); + masses.push_back( prt->getMass() ); + vtims.push_back( prt->getVTim() ); + spins.push_back( prt->getSpin() ); + statuses.push_back( prt->getStatus() ); + pdgs.push_back( prt->getPDG() ); + for( size_t k = 0 ; k < 2 ; ++k ) + { + moms.push_back( prt->getMom()[k] ); + mothers.push_back( prt->getMothers()[k] ); + icols.push_back( prt->getColor()[k] ); + } + moms.push_back( prt->getMom()[2] ); + } + } + } + } + prtInfo::prtInfo( const std::vector>& lheFile, const int nPrt, const std::vector& statVec, + statSort sorter ){ + int nEvt = lheFile.size(); + moms.reserve(4*nPrt*nEvt); vtims.reserve(nPrt*nEvt); masses.reserve(nPrt*nEvt); pdgs.reserve(nPrt*nEvt); + spins.reserve(nPrt*nEvt); statuses.reserve(nPrt*nEvt); mothers.reserve(2*nPrt*nEvt); icols.reserve(2*nPrt*nEvt); + for( auto evt : lheFile ) + { + for( auto stat : statVec ) + { + for( auto i : evt->getProcOrder(sorter)[stat] ) + { + auto prt = evt->getPrts()[i]; + moms.push_back( prt->getE() ); + masses.push_back( prt->getMass() ); + vtims.push_back( prt->getVTim() ); + spins.push_back( prt->getSpin() ); + statuses.push_back( prt->getStatus() ); + pdgs.push_back( prt->getPDG() ); + for( size_t k = 0 ; k < 2 ; ++k ) + { + moms.push_back( prt->getMom()[k] ); + mothers.push_back( prt->getMothers()[k] ); + icols.push_back( prt->getColor()[k] ); + } + moms.push_back( prt->getMom()[2] ); + } + } + } + } + + transSkel::transSkel(){ + this->procSets = std::vector>>(); + this->relProcs = std::vector>>(); + this->relEvSet = std::vector(); + } + transSkel::transSkel( transSkel& skeleton ){ + this->procSets = skeleton.procSets; + this->relProcs = skeleton.relProcs; + this->relEvSet = skeleton.relEvSet; + } + transSkel::transSkel( lheNode& lheFile, std::vector& evSet ){ + this->relProcs = evProcOrder( lheFile, evSet ); + this->relEvSet = std::vector(evSet.size(), false); + for ( size_t k = 0 ; k < this->relProcs.size() ; ++k ) + { + if( std::find(this->relProcs[k]->begin(), this->relProcs[k]->end(), true) != this->relProcs[k]->end() ) + { + this->relEvSet[k] = true; + } + } + this->procSets = std::vector>>(std::count(this->relEvSet.begin(), this->relEvSet.end(), true)); + auto evs = lheFile.getEvents(); + size_t j = 0; + for( size_t k = 0 ; k < this->relEvSet.size() ; ++k ) + { + if( this->relEvSet[k] ) + { + this->procSets[j] = std::vector>(); + for( size_t m = 0 ; m < relProcs[k]->size() ; ++m ) + { + if( relProcs[k]->at(m) ) + { + this->procSets[j].push_back(evs[m]); + } + } + ++j; + } + } + } + transSkel::transSkel( std::shared_ptr lheFile, std::vector& evSet ) { + this->relProcs = evProcOrder( *lheFile, evSet ); + this->relEvSet = std::vector(evSet.size(), false); + for ( size_t k = 0 ; k < this->relProcs.size() ; ++k ) + { + if( std::find(this->relProcs[k]->begin(), this->relProcs[k]->end(), true) != this->relProcs[k]->end() ) + { + this->relEvSet[k] = true; + } + } + this->procSets = std::vector>>(std::count(this->relEvSet.begin(), this->relEvSet.end(), true)); + auto evs = lheFile->getEvents(); + size_t j = 0; + for( size_t k = 0 ; k < this->relEvSet.size() ; ++k ) + { + if( this->relEvSet[k] ) + { + this->procSets[j] = std::vector>(); + for( size_t m = 0 ; m < relProcs[k]->size() ; ++m ) + { + if( relProcs[k]->at(m) ) + { + this->procSets[j].push_back(evs[m]); + } + } + ++j; + } + } + } + + // ZW: transposed LHE file with a single process type + transMonoLHE::transMonoLHE( const std::vector> lheFile , const int nPrt ){ + evtsHead = evtInfo(lheFile); + evtsData = prtInfo(lheFile, nPrt); + process = lheFile[0]; + } + transMonoLHE::transMonoLHE( const std::vector> lheFile, const int nPrt, const std::vector& statVec ){ + evtsHead = evtInfo(lheFile, statVec); + evtsData = prtInfo(lheFile, nPrt, statVec); + process = lheFile[0]; + } + transMonoLHE::transMonoLHE( const std::vector> lheFile, const int nPrt, + sortFcn sorter, + std::vector statVec ){ + evtsHead = evtInfo(lheFile, statVec); + evtsData = prtInfo(lheFile, nPrt, statVec, sorter); + process = lheFile[0]; + } + transMonoLHE::transMonoLHE( const std::vector> lheFile, const int nPrt, + statSort sorter, + std::vector statVec){ + evtsHead = evtInfo(lheFile, statVec); + evtsData = prtInfo(lheFile, nPrt, statVec, sorter); + process = lheFile[0]; + } + transMonoLHE::transMonoLHE( const transMonoLHE& lheFile ){ + evtsHead = lheFile.evtsHead; + evtsData = lheFile.evtsData; + process = lheFile.process; + } + + // ZW: transposed LHE file ordered by subprocess + transLHE::transLHE(){ return; } + transLHE::transLHE( lheNode& lheFile ) + { + procSets = evProcessPull( lheFile ); + relProcs = evProcOrder( lheFile, procSets ); + this->setRelEvSets(); + xmlFile = lheFile.getFile(); + auto procsOrdered = lheEvReOrder( lheFile, procSets, relProcs ); + subProcs = std::vector>( procsOrdered.size() ); + for( size_t k = 0 ; k < procsOrdered.size() ; ++k ) + { + subProcs[k] = std::make_shared( *procsOrdered[k], procsOrdered[k]->at(0)->getNprt() ); + } + } + transLHE::transLHE( lheNode& lheFile, + sortFcn sorter, + const std::vector& statVec ) + { + procSets = evProcessPull( lheFile, sorter, statVec ); + relProcs = evProcOrder( lheFile, procSets, sorter, statVec ); + this->setRelEvSets(); + xmlFile = lheFile.getFile(); + auto procsOrdered = lheEvReOrder( lheFile, procSets, relProcs, sorter, statVec ); + subProcs = std::vector>( procsOrdered.size() ); + for( size_t k = 0 ; k < procsOrdered.size() ; ++k ) + { + subProcs[k] = std::make_shared( *procsOrdered[k], procsOrdered[k]->at(0)->getNprt(), sorter, statVec ); + } + } + transLHE::transLHE( lheNode& lheFile, + statSort sorter, + const std::vector& statVec) + { + procSets = evProcessPull( lheFile, sorter, statVec ); + relProcs = evProcOrder( lheFile, procSets, sorter, statVec ); + this->setRelEvSets(); + xmlFile = lheFile.getFile(); + auto procsOrdered = lheEvReOrder( lheFile, procSets, relProcs, sorter, statVec ); + subProcs = std::vector>( procsOrdered.size() ); + for( size_t k = 0 ; k < procsOrdered.size() ; ++k ) + { + subProcs[k] = std::make_shared( *procsOrdered[k], procsOrdered[k]->at(0)->getNprt(), sorter, statVec ); + } + } + transLHE::transLHE( lheNode& lheFile, const std::vector& statVec ) + { + procSets = evProcessPull( lheFile, statVec ); + relProcs = evProcOrder( lheFile, procSets, statVec ); + this->setRelEvSets(); + xmlFile = lheFile.getFile(); + auto procsOrdered = lheEvReOrder( lheFile, procSets, relProcs, statVec ); + subProcs = std::vector>( procsOrdered.size() ); + for( size_t k = 0 ; k < procsOrdered.size() ; ++k ) + { + subProcs[k] = std::make_shared( *procsOrdered[k], procsOrdered[k]->at(0)->getNprt(), statVec ); + } + } + transLHE::transLHE( transSkel& skeleton ){ + relProcs = skeleton.relProcs; + subProcs = std::vector>( skeleton.procSets.size() ); + relEvSets = skeleton.relEvSet; + for( size_t k = 0 ; k < skeleton.procSets.size() ; ++k ) + { + subProcs[k] = std::make_shared( skeleton.procSets[k], skeleton.procSets[k].at(0)->getNprt() ); + } + } + transLHE::transLHE( const transLHE& lheFile ){ + relProcs = lheFile.relProcs; + subProcs = lheFile.subProcs; + } + void transLHE::setRelEvSets(){ + relEvSets = std::vector(relProcs.size(), false); + for ( size_t k = 0 ; k < this->relProcs.size() ; ++k ) + { + if( std::find(this->relProcs[k]->begin(), this->relProcs[k]->end(), true) != this->relProcs[k]->end() ) + { + this->relEvSets[k] = true; + } + } + } +// template + std::shared_ptr> transLHE::vectorFlat( std::vector>> vecVec ) + { + bool allRel = (vecVec.size() == relProcs.size()); + bool justRel = (vecVec.size() == size_t(std::count(relEvSets.begin(), relEvSets.end(), true))); + std::vector relInds; + if( !(allRel || justRel) ) throw std::range_error("vectorFlat: input vector size does not match number of (relevant) subprocesses"); + for( size_t k = 0; k < relEvSets.size(); ++k ) + { + if( allRel ){ relInds.push_back(k); } + else if( relEvSets[k] ){ relInds.push_back(k); } + } + size_t totVec = 0; + for( size_t k = 0 ; k < vecVec.size() ; ++k){ + totVec += vecVec[k]->size(); + } + if( totVec != relProcs[0]->size() ) throw std::range_error("vectorFlat: sum of input vector sizes does not match total number of events"); + auto flatVec = std::make_shared>(relProcs[0]->size()); + for( size_t k = 0 ; k < relInds.size() ; ++k ){ + size_t currInd = 0; + for( size_t j = 0 ; j < relProcs[k]->size() ; ++j ){ + if( relProcs[relInds[k]]->at(j) ){ + flatVec->at(j) = vecVec[k]->at(currInd); + ++currInd; + } + } + } + return flatVec; + } + + relEvArgs::relEvArgs(){ return; } + relEvArgs::relEvArgs( const relEvArgs& relData ){ + this->nUp = relData.nUp; + //this->idPrUp = relData.idPrUp; + this->xWgtUp = relData.xWgtUp; + this->scalUp = relData.scalUp; + this->aQEDUp = relData.aQEDUp; + this->aQCDUp = relData.aQCDUp; + this->idUp = relData.idUp; + this->iStUp = relData.iStUp; + for( size_t j = 0 ; j < 2 ; ++j ) + { + this->mothUp[j] = relData.mothUp[j]; + this->iColUp[j] = relData.iColUp[j]; + } + this->massUp = relData.massUp; + this->momUp = relData.momUp; + for( size_t j = 0 ; j < 5 ; ++j ) + { + this->pUp[j] = relData.pUp[j]; + } + this->vTimUp = relData.vTimUp; + this->spinUp = relData.spinUp; + } + + + // ZW: Set of fcns to set relEvArgs without having to access private members + relEvArgs& relEvArgs::setNUp( bool nuNUp ){ this->nUp = nuNUp; return *this; } + relEvArgs& relEvArgs::setIdPrUp( bool nuIdPrUp ){ this->idPrUp = nuIdPrUp; return *this; } + relEvArgs& relEvArgs::setXWgtUp( bool nuXWgtUp ){ this->xWgtUp = nuXWgtUp; return *this; } + relEvArgs& relEvArgs::setScalUp( bool nuScalUp ){ this->scalUp = nuScalUp; return *this; } + relEvArgs& relEvArgs::setAQEDUp( bool nuAQEDUp ){ this->aQEDUp = nuAQEDUp; return *this; } + relEvArgs& relEvArgs::setAQCDUp( bool nuAQCDUp ){ this->aQCDUp = nuAQCDUp; return *this; } + relEvArgs& relEvArgs::setIdUp( bool nuIdUp ){ this->idUp = nuIdUp; return *this; } + relEvArgs& relEvArgs::setIStUp( bool nuIStUp ){ this->iStUp = nuIStUp; return *this; } + relEvArgs& relEvArgs::setMothUp( bool nuMothUp[2] ){ this->mothUp[0] = nuMothUp[0]; this->mothUp[1] = nuMothUp[1]; return *this; } + relEvArgs& relEvArgs::setMothUp( bool nuMothUp ){ this->mothUp[0] = nuMothUp; this->mothUp[1] = nuMothUp; return *this; } + relEvArgs& relEvArgs::setMothUp( std::vector nuMothUp ){ + if ( nuMothUp.size() != 2 ){ + throw std::range_error("relEvArgs::setMothUp: input vector must have size 2"); + } + this->mothUp[0] = nuMothUp[0]; + this->mothUp[1] = nuMothUp[1]; + return *this; + } + relEvArgs& relEvArgs::setIColUp( bool nuIColUp[2] ){ this->iColUp[0] = nuIColUp[0]; this->iColUp[1] = nuIColUp[1]; return *this; } + relEvArgs& relEvArgs::setIColUp( bool nuIColUp ){ this->iColUp[0] = nuIColUp; this->iColUp[1] = nuIColUp; return *this; } + relEvArgs& relEvArgs::setIColUp( std::vector nuIColUp ){ + if ( nuIColUp.size() != 2 ){ + throw std::range_error("relEvArgs::setIColUp: input vector must have size 2"); + } + this->iColUp[0] = nuIColUp[0]; + this->iColUp[1] = nuIColUp[1]; + return *this; + } + relEvArgs& relEvArgs::setMassUp( bool nuMassUp ){ this->massUp = nuMassUp; return *this; } + relEvArgs& relEvArgs::setMomUp( bool nuMomUp ){ this->momUp = nuMomUp; return *this; } + relEvArgs& relEvArgs::setPUp( bool nuPUp[5] ){ + for( size_t j = 0 ; j < 5 ; ++j ) + { + this->pUp[j] = nuPUp[j]; + } + return *this; + } + relEvArgs& relEvArgs::setPUp( bool nuPUp ){ + for( size_t j = 0 ; j < 5 ; ++j ) + { + this->pUp[j] = nuPUp; + } + return *this; + } + relEvArgs& relEvArgs::setPUp( std::vector nuPUp ){ + if ( nuPUp.size() != 5 ){ + throw std::range_error("relEvArgs::setPUp: input vector must have size 5"); + } + for( size_t j = 0 ; j < 5 ; ++j ) + { + this->pUp[j] = nuPUp[j]; + } + return *this; + } + relEvArgs& relEvArgs::setVTimUp( bool nuVTimUp ){ this->vTimUp = nuVTimUp; return *this; } + relEvArgs& relEvArgs::setSpinUp( bool nuSpinUp ){ this->spinUp = nuSpinUp; return *this; } + relEvArgs& relEvArgs::setAll( bool nuAll ){ + this->nUp = nuAll; this->idPrUp = nuAll; this->xWgtUp = nuAll; this->scalUp = nuAll; this->aQEDUp = nuAll; this->aQCDUp = nuAll; + this->idUp = nuAll; this->iStUp = nuAll; this->mothUp[0] = nuAll; this->mothUp[1] = nuAll; this->iColUp[0] = nuAll; this->iColUp[1] = nuAll; + this->massUp = nuAll; this->momUp = nuAll; this->pUp[0] = nuAll; this->pUp[1] = nuAll; this->pUp[2] = nuAll; this->pUp[3] = nuAll; this->pUp[4] = nuAll; + this->vTimUp = nuAll; this->spinUp = nuAll; + return *this; + } + + procLine::procLine(){ + this->xSecUp = 0.0; this->xErrUp = 0.0; this->xMaxUp = 0.0; this->lPrUp = 0; + } + + procLine::procLine( const procLine& process ){ + this->xSecUp = process.xSecUp; this->xErrUp = process.xErrUp; this->xMaxUp = process.xMaxUp; this->lPrUp = process.lPrUp; + } + + procLine::procLine( double xSec, double xErr, double xMax, int lPr ){ + this->xSecUp = xSec; this->xErrUp = xErr; this->xMaxUp = xMax; this->lPrUp = lPr; + } + + procLine::procLine( lheInitLine& process ){ + this->xSecUp = ctod(process.xsecup); + this->xErrUp = ctod(process.xerrup); + this->xMaxUp = ctod(process.xmaxup); + this->lPrUp = ctoi(process.lprup); + } + + procLine::procLine( std::shared_ptr process ) : procLine( *process ) {} + + std::vector lheProcLines( lheNode& lheFile ){ + std::vector procLines; + for( auto line : lheFile.getInit()->getLines() ){ + procLines.push_back(procLine(line)); + } + return procLines; + } + + void procSoA::reset(){ + this->events = std::vector>(); + this->relStats = std::vector(); + this->relEvMap = std::vector(); + this->relEvents = std::vector>(); + this->nUp = std::vector(); + this->idPrUp = std::vector(); + this->xWgtUp = std::vector(); + this->scalUp = std::vector(); + this->aQEDUp = std::vector(); + this->aQCDUp = std::vector(); + this->idUp = std::vector(); + this->iStUp = std::vector(); + this->mothUp = std::vector>(2); + this->iColUp = std::vector>(2); + this->massUp = std::vector(); + this->momUp = std::vector(); + this->pUp = std::vector>(5); + this->vTimUp = std::vector(); + this->spinUp = std::vector(); + } + + bool procSoA::relevant( event& ev ){ + if( this->relevantEvent ) return relevantEvent(ev); + return true; + } + + bool procSoA::relevant( std::shared_ptr ev ){ + if( this->relevantEvent ) return relevantEvent(*ev); + return true; + } + + void procSoA::setRelEvs( std::vector> lheFile ){ + this->events = lheFile; + this->relEvMap = std::vector(lheFile.size()); + this->relEvents = std::vector>(); + this->relEvents.reserve(lheFile.size()); + for( size_t k = 0 ; k < lheFile.size() ; ++k ) + { + if( this->relevant(*lheFile[k]) ){ + this->relEvMap[k] = true; + this->relEvents.push_back(lheFile[k]); + } + } + } + + bool procSoA::extract( std::vector> lheFile, std::vector relevStats ){ + this->reset(); + this->relStats = relevStats; + this->setRelEvs(lheFile); + for( auto ev : lheFile ) + { + if( this->relevant(*ev) ) + { + if(this->relData.nUp) this->nUp.push_back(ev->getHead().getNprt()); + if(this->relData.idPrUp) this->idPrUp.push_back(ev->getHead().getProcID()); + if(this->relData.xWgtUp) this->xWgtUp.push_back(ev->getHead().getWeight()); + if(this->relData.scalUp) this->scalUp.push_back(ev->getHead().getScale()); + if(this->relData.aQEDUp) this->aQEDUp.push_back(ev->getHead().getAQED()); + if(this->relData.aQCDUp) this->aQCDUp.push_back(ev->getHead().getAQCD()); + auto prts = ev->getSortedPrts(); + auto order = ev->getProcOrder(); + for( auto stat : this->relStats ) + { + auto prt = prts.at(stat); + for( auto j : order[stat] ) + { + if(this->relData.idUp) this->idUp.push_back(prt[j]->getPDG()); + if(this->relData.iStUp) this->iStUp.push_back(prt[j]->getStatus()); + for( size_t k = 0 ; k < 2 ; ++k ) + { + if(this->relData.mothUp[k]) this->mothUp[k].push_back(prt[j]->getMothers()[k]); + if(this->relData.iColUp[k]) this->iColUp[k].push_back(prt[j]->getColor()[k]); + } + if(this->relData.massUp) this->massUp.push_back(prt[j]->getMass()); + if(this->relData.momUp) this->momUp.push_back(prt[j]->getE()); + for( size_t k = 0 ; k < 3 ; ++k ) + { + if(this->relData.momUp) this->momUp.push_back(prt[j]->getMom()[k]); + if(this->relData.pUp[k]) this->pUp[k].push_back(prt[j]->getMom()[k]); + } + if(this->relData.pUp[3]) this->pUp[3].push_back(prt[j]->getE()); + if(this->relData.pUp[4]) this->pUp[4].push_back(prt[j]->getMass()); + if(this->relData.vTimUp) this->vTimUp.push_back(prt[j]->getVTim()); + if(this->relData.spinUp) this->spinUp.push_back(prt[j]->getSpin()); + } + } + } + } + return true; + } + + bool procSoA::empty(){ + bool isEmpty = true; + if( this->relData.nUp ) isEmpty = isEmpty && (this->nUp.size() == 0); + if( this->relData.idPrUp ) isEmpty = isEmpty && (this->idPrUp.size() == 0); + if( this->relData.xWgtUp ) isEmpty = isEmpty && (this->xWgtUp.size() == 0); + if( this->relData.scalUp ) isEmpty = isEmpty && (this->scalUp.size() == 0); + if( this->relData.aQEDUp ) isEmpty = isEmpty && (this->aQEDUp.size() == 0); + if( this->relData.aQCDUp ) isEmpty = isEmpty && (this->aQCDUp.size() == 0); + if( this->relData.idUp ) isEmpty = isEmpty && (this->idUp.size() == 0); + if( this->relData.iStUp ) isEmpty = isEmpty && (this->iStUp.size() == 0); + if( this->relData.mothUp[0] ) isEmpty = isEmpty && (this->mothUp[0].size() == 0); + if( this->relData.mothUp[1] ) isEmpty = isEmpty && (this->mothUp[1].size() == 0); + if( this->relData.iColUp[0] ) isEmpty = isEmpty && (this->iColUp[0].size() == 0); + if( this->relData.iColUp[1] ) isEmpty = isEmpty && (this->iColUp[1].size() == 0); + if( this->relData.massUp ) isEmpty = isEmpty && (this->massUp.size() == 0); + if( this->relData.momUp ) isEmpty = isEmpty && (this->momUp.size() == 0); + if( this->relData.pUp[0] ) isEmpty = isEmpty && (this->pUp[0].size() == 0); + if( this->relData.pUp[1] ) isEmpty = isEmpty && (this->pUp[1].size() == 0); + if( this->relData.pUp[2] ) isEmpty = isEmpty && (this->pUp[2].size() == 0); + if( this->relData.pUp[3] ) isEmpty = isEmpty && (this->pUp[3].size() == 0); + if( this->relData.pUp[4] ) isEmpty = isEmpty && (this->pUp[4].size() == 0); + if( this->relData.vTimUp ) isEmpty = isEmpty && (this->vTimUp.size() == 0); + if( this->relData.spinUp ) isEmpty = isEmpty && (this->spinUp.size() == 0); + return isEmpty; + } + + procSoA::procSoA(){ this->reset(); } + + procSoA::procSoA( const relEvArgs& relArgs ){ + this->reset(); + this->relData = relData; + } + + procSoA::procSoA( const procSoA& process ){ + this->relData = process.relData; + this->relStats = process.relStats; + this->relevantEvent = process.relevantEvent; + this->events = process.events; + this->relEvMap = process.relEvMap; + this->relEvents = process.relEvents; + this->nUp = process.nUp; + this->idPrUp = process.idPrUp; + this->xWgtUp = process.xWgtUp; + this->scalUp = process.scalUp; + this->aQEDUp = process.aQEDUp; + this->aQCDUp = process.aQCDUp; + this->idUp = process.idUp; + this->iStUp = process.iStUp; + this->mothUp = process.mothUp; + this->iColUp = process.iColUp; + this->massUp = process.massUp; + this->momUp = process.momUp; + this->pUp = process.pUp; + this->vTimUp = process.vTimUp; + this->spinUp = process.spinUp; + } + + procSoA::procSoA( procSoA* process ) : procSoA(*process) {} + + procSoA::procSoA( std::shared_ptr process ) : procSoA(*process) {} + + procSoA::procSoA( std::vector> lheFile, relEvArgs relArgs, std::vector relevStats, std::function relFcn ){ + this->reset(); + this->relData = relArgs; + this->relevantEvent = relFcn; + this->relStats = relevStats; + if ( !this->extract(lheFile, relStats) ) throw std::runtime_error("procSoA: event extraction failed"); + return; + } + + procSoA& procSoA::setRelData( relEvArgs& relArgs ){ + this->relData = relArgs; + return *this; + } + + procSoA& procSoA::setRelStats( std::vector& relevStats ){ + this->relStats = relevStats; + return *this; + } + + procSoA& procSoA::setRelevant( evCheck& relFcn ){ + this->relevantEvent = relFcn; + return *this; + } + + procSoA& procSoA::setEvents( std::vector> lheFile ){ + this->events = lheFile; + return *this; + } + + void lheSoA::reset(){ + this->subProcesses = std::vector>(); + this->events = std::vector>(); + this->sortedEvents = std::vector>>(); + this->procLines = std::vector(); + this->relEvFcns = std::vector>(); + this->eventGrouping = std::vector(); + this->idBmUp[0] = 0; this->idBmUp[1] = 0; + this->eBmUp[0] = 0.; this->eBmUp[1] = 0.; + this->pdfGUp[0] = 0; this->pdfGUp[1] = 0; + this->pdfSUp[0] = 0; this->pdfSUp[1] = 0; + this->idWtUp = 0; + this->nPrUp = 0; + } + + size_t lheSoA::eventIndex( event& ev){ + if (relEvFcns.size() == 0) return npos; + for( size_t k = 0 ; k < relEvFcns.size() ; ++k ) + { + if( !relEvFcns[k] ) continue; + if( relEvFcns[k](ev) ) return k; + } + return npos; + } + + bool lheSoA::sortEvents( bool hard ){ + if( this->events.size() == 0 ) return false; + if( this->relEvFcns.size() == 0 ) return false; + if( !hard ){ + if( this->sortedEvents.size() == this->relEvFcns.size() ) return true; // if not doing hard sort, check if events have been sorted + if( this->sortedEvents.size() == this->relEvFcns.size() + 1 ) return true; // with as many subprocesses as there are sorters + } + this->eventGrouping = std::vector(this->events.size(), npos); + this->sortedEvents = std::vector>>(this->relEvFcns.size()); + bool hasUnsorted = false; + for( size_t k = 0 ; k < this->events.size() ; ++k ) + { + size_t currInd = this->eventIndex(*this->events[k]); + if (currInd == npos){ + hasUnsorted = true; + continue; + } + this->eventGrouping[k] = currInd; + this->sortedEvents[currInd].push_back(this->events[k]); + } + if ( hasUnsorted ) + { + this->sortedEvents.push_back(std::vector>()); + for( size_t k = 0 ; k < this->events.size() ; ++k ) + { + if( this->eventGrouping[k] == npos ) this->sortedEvents.back().push_back(this->events[k]); + } + } + return true; + } + + std::function lheSoA::evFcnIndex( size_t index ){ + if( relEvFcns.size() > index ) return relEvFcns[index]; + if( relEvFcns.size() > 0 ) return relEvFcns[0]; + return nullptr; + } + + relEvArgs lheSoA::evDataIndex( size_t index ){ + if( relEvData.size() > index ) return relEvData[index]; + if( relEvData.size() > 0 ) return relEvData[0]; + return relEvArgs(); + } + + std::vector lheSoA::evStatsIndex( size_t index ){ + if( relEvStats.size() > index ) return relEvStats[index]; + if( relEvStats.size() > 0 ) return relEvStats[0]; + return std::vector({-1,1}); + } + + bool lheSoA::extractEvents( bool hard ){ + if( !( this->sortEvents(hard) ) ) return false; + if( !hard ){ + if( this->subProcesses.size() == this->relEvFcns.size() ) return true; + } + this->subProcesses = std::vector>(); + this->subProcesses.reserve( this->relEvFcns.size() ); + for( size_t k = 0 ; k < this->sortedEvents.size() ; ++k ) + { + this->subProcesses.push_back( + std::make_shared(sortedEvents[k], evDataIndex(k), evStatsIndex(k), evFcnIndex(k) ) + ); + } + return true; + } + + lheSoA& lheSoA::setInit( lheInitHead& init ){ + if( !(init.idbmup[0].empty() || init.idbmup[1].empty() ) ){ + this->idBmUp[0] = ctoi(init.idbmup[0]); + this->idBmUp[1] = ctoi(init.idbmup[1]); + } + if( !(init.ebmup[0].empty() || init.ebmup[1].empty() ) ){ + this->eBmUp[0] = ctod(init.ebmup[0]); + this->eBmUp[1] = ctod(init.ebmup[1]); + } + if( !(init.pdfgup[0].empty() || init.pdfgup[1].empty() ) ){ + this->pdfGUp[0] = ctoi(init.pdfgup[0]); + this->pdfGUp[1] = ctoi(init.pdfgup[1]); + } + if( !(init.pdfsup[0].empty() || init.pdfsup[1].empty() ) ){ + this->pdfSUp[0] = ctoi(init.pdfsup[0]); + this->pdfSUp[1] = ctoi(init.pdfsup[1]); + } + if( !(init.idwtup.empty()) ){ + this->idWtUp = ctoi(init.idwtup); + } + if( !(init.nprup.empty()) ){ + this->nPrUp = ctoi(init.nprup); + } + return *this; + } + + lheSoA& lheSoA::setInit( lheNode& lheFile ){ + this->procLines = lheProcLines(lheFile); + return this->setInit(*lheFile.getInit()->getHead()); + } + + lheSoA::lheSoA(){ this->reset(); } + + lheSoA::lheSoA( const lheSoA& lheFile ){ + this->subProcesses = lheFile.subProcesses; + this->events = lheFile.events; + this->sortedEvents = lheFile.sortedEvents; + this->procLines = lheFile.procLines; + this->relEvFcns = lheFile.relEvFcns; + this->relEvData = lheFile.relEvData; + this->relEvStats = lheFile.relEvStats; + this->eventGrouping = lheFile.eventGrouping; + this->idBmUp[0] = lheFile.idBmUp[0]; this->idBmUp[1] = lheFile.idBmUp[1]; + this->eBmUp[0] = lheFile.eBmUp[0]; this->eBmUp[1] = lheFile.eBmUp[1]; + this->pdfGUp[0] = lheFile.pdfGUp[0]; this->pdfGUp[1] = lheFile.pdfGUp[1]; + this->pdfSUp[0] = lheFile.pdfSUp[0]; this->pdfSUp[1] = lheFile.pdfSUp[1]; + this->idWtUp = lheFile.idWtUp; + this->nPrUp = lheFile.nPrUp; + } + + lheSoA::lheSoA( std::vector> lheFile ){ + this->reset(); + this->events = lheFile; + } + + lheSoA::lheSoA( std::vector> lheFile, std::vector> evSort ) : lheSoA( lheFile ){ // delegating constructor + this->relEvFcns = evSort; + this->sortEvents(); + } + + lheSoA::lheSoA( lheNode& lheFile, std::vector> evSort ) : lheSoA( lheFile.getEvents(), evSort ){ // delegating constructor + this->procLines = std::vector(); + this->setInit(lheFile); + } + + lheSoA::lheSoA( std::vector> lheFile, std::vector> evSort, std::vector evData ) : lheSoA( lheFile, evSort ){ // delegating constructor + this->relEvData = evData; + } + + lheSoA::lheSoA( lheNode& lheFile, std::vector> evSort, std::vector evData ) : lheSoA( lheFile.getEvents(), evSort, evData ){ // delegating constructor + this->procLines = std::vector(); + this->setInit(lheFile); + } + + lheSoA::lheSoA( std::vector> lheFile, std::vector> evSort, std::vector> evStats ) : lheSoA( lheFile, evSort ){ // delegating constructor + this->relEvStats = evStats; + } + + lheSoA::lheSoA( lheNode& lheFile, std::vector> evSort, std::vector> evStats ) : lheSoA( lheFile.getEvents(), evSort, evStats ){ // delegating constructor + this->procLines = std::vector(); + this->setInit(lheFile); + } + + lheSoA::lheSoA( std::vector> lheFile, std::vector> evSort, std::vector evData, std::vector> evStats ) : lheSoA( lheFile, evSort, evData ){ // delegating constructor + this->relEvStats = evStats; + } + + lheSoA::lheSoA( lheNode& lheFile, std::vector> evSort, std::vector evData, std::vector> evStats ) : lheSoA( lheFile.getEvents(), evSort, evData, evStats ){ // delegating constructor + this->procLines = std::vector(); + this->setInit(lheFile); + } + + lheSoA& lheSoA::setEvents( std::vector> lheFile ){ + this->events = lheFile; + return *this; + } + + lheSoA& lheSoA::setEvents( lheNode& lheFile ){ + this->events = lheFile.getEvents(); + return *this; + } + + lheSoA& lheSoA::setProcLines( std::vector procLines ){ + this->procLines = procLines; + return *this; + } + + lheSoA& lheSoA::setProcLines( std::vector> procLines ){ + this->procLines = std::vector(); + for( auto line : procLines ) + { + this->procLines.push_back(procLine(*line)); + } + return *this; + } + + lheSoA& lheSoA::setProcLines( initNode& procLines ){ + return this->setProcLines(procLines.getLines()); + } + + lheSoA& lheSoA::setProcLines( lheNode& lheFile ){ + return this->setProcLines(lheFile.getInit()->getLines()); + } + + lheSoA& lheSoA::setRelEvFcns( std::vector> evSort ){ + this->relEvFcns = evSort; + return *this; + } + + lheSoA& lheSoA::setRelEvData( std::vector evData ){ + this->relEvData = evData; + return *this; + } + + // ZW: vector transformation string_to_double + std::shared_ptr> vecStoD( const std::vector dataVec ) + { + auto valVec = std::make_shared>( dataVec.size() ); + std::transform( dataVec.begin(), dataVec.end(), valVec->begin(), []( const std::string_view& stv ){ + return std::stod(std::string(stv)); + } ); + return valVec; + } + + // ZW: vector transformation string_to_int + std::shared_ptr> vecStoI( const std::vector dataVec ) + { + auto valVec = std::make_shared>( dataVec.size() ); + std::transform( dataVec.begin(), dataVec.end(), valVec->begin(), []( const std::string_view& stv ){ + return std::stoi(std::string(stv)); + } ); + return valVec; + } + + // ZW: bool struct to define which double values + // to extract transposed from LHE file + std::vector lheRetDs::getBools(){ + return { ebmup, xsecup, xerrup, xmaxup, xwgtup, scalup, aqedup, aqcdup, + pup, mass, vtimup, spinup }; + } + + // ZW: bool struct to define which int values + // to extract transposed from LHE file + std::vector lheRetInts::getBools(){ + return { idbmup, pdfgup, pdfsup, idwtup, nprup, lprup, + nup, idprup, idup, istup, mothup, icolup }; + } + + // ZW: function for extracting transposed double values + // from LHE file + std::shared_ptr>>> lheValDoubles( lheNode& lheFile, lheRetDs vals ) + { + // ZW: hard-setting returning g_S instead of a_S for now + bool aStogS = true; + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile ); + auto lheDos = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheDos; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoD( { lheFile.getInit()->getHead()->ebmup[0], lheFile.getInit()->getHead()->ebmup[1] } ); ++currInd; } + if( boolVec[1] ){ + std::vector xsecVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xsecVec.push_back(line->xsecup); + } + lheDs[currInd] = vecStoD( xsecVec ); + ++currInd; } + if( boolVec[2] ){ + std::vector xerrVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xerrVec.push_back(line->xerrup); + } + lheDs[currInd] = vecStoD( xerrVec ); + ++currInd; } + if( boolVec[3] ){ + std::vector xmaxVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xmaxVec.push_back(line->xmaxup); + } + lheDs[currInd] = vecStoD( xmaxVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[4] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.wgts ) ; ++currInd; } + if( boolVec[5] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.scales ); ++currInd; } + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQEDs ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQCDs ); + if( aStogS ){ + std::transform( lheDs[currInd]->begin(), lheDs[currInd]->end(), lheDs[currInd]->begin(), + []( double alphaS ){ + auto gS = std::sqrt( 4. * M_PI * alphaS ); + return gS; + } ); + } + ++currInd; + } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.moms ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.masses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.vtims ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.spins ); ++currInd; } + } + + return lheDos; + } + + std::shared_ptr>>> lheValDoubles(transLHE& lheAOS, lheRetDs vals ) + { + // ZW: hard-setting returning g_S instead of a_S for now + bool aStogS = true; + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + //auto lheAOS = transLHE( lheFile ); + auto lheDos = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheDos; + int currInd = 0; + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[4] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.wgts ); ++currInd; } + if( boolVec[5] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.scales ); ++currInd; } + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQEDs ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQCDs ); + if( aStogS ){ + std::transform( lheDs[currInd]->begin(), lheDs[currInd]->end(), lheDs[currInd]->begin(), + []( double alphaS ){ + auto gS = std::sqrt( 4. * M_PI * alphaS ); + return gS; + } ); + } + ++currInd; + } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.moms ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.masses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.vtims ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.spins ); ++currInd; } + } + return lheDos; + } + + std::shared_ptr>>> lheValDoubles( lheNode& lheFile, + const std::vector& statVec, lheRetDs vals = lheRetDs() ) + { + // ZW: hard-setting returning g_S instead of a_S for now + bool aStogS = true; + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile, statVec ); + auto lheDos = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheDos; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoD( { lheFile.getInit()->getHead()->ebmup[0], lheFile.getInit()->getHead()->ebmup[1] } ); ++currInd; } + if( boolVec[1] ){ + std::vector xsecVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xsecVec.push_back(line->xsecup); + } + lheDs[currInd] = vecStoD( xsecVec ); + ++currInd; } + if( boolVec[2] ){ + std::vector xerrVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xerrVec.push_back(line->xerrup); + } + lheDs[currInd] = vecStoD( xerrVec ); + ++currInd; } + if( boolVec[3] ){ + std::vector xmaxVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xmaxVec.push_back(line->xmaxup); + } + lheDs[currInd] = vecStoD( xmaxVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[4] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.wgts ); ++currInd; } + if( boolVec[5] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.scales ); ++currInd; } + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQEDs ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQCDs ); + if( aStogS ){ + std::transform( lheDs[currInd]->begin(), lheDs[currInd]->end(), lheDs[currInd]->begin(), + []( double alphaS ){ + auto gS = std::sqrt( 4. * M_PI * alphaS ); + return gS; + } ); + } + ++currInd; + } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.moms ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.masses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.vtims ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.spins ); ++currInd; } + } + + return lheDos; + } + + std::shared_ptr>>> lheValDoubles( lheNode& lheFile, + sortFcn sorter, + const std::vector& statVec = {-1, 1}, lheRetDs vals = lheRetDs() ) + { + // ZW: hard-setting returning g_S instead of a_S for now + bool aStogS = true; + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile, sorter, statVec ); + auto lheDos = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheDos; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoD( { lheFile.getInit()->getHead()->ebmup[0], lheFile.getInit()->getHead()->ebmup[1] } ); ++currInd; } + if( boolVec[1] ){ + std::vector xsecVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xsecVec.push_back(line->xsecup); + } + lheDs[currInd] = vecStoD( xsecVec ); + ++currInd; } + if( boolVec[2] ){ + std::vector xerrVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xerrVec.push_back(line->xerrup); + } + lheDs[currInd] = vecStoD( xerrVec ); + ++currInd; } + if( boolVec[3] ){ + std::vector xmaxVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xmaxVec.push_back(line->xmaxup); + } + lheDs[currInd] = vecStoD( xmaxVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[4] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.wgts ); ++currInd; } + if( boolVec[5] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.scales ); ++currInd; } + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQEDs ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQCDs ); + if( aStogS ){ + std::transform( lheDs[currInd]->begin(), lheDs[currInd]->end(), lheDs[currInd]->begin(), + []( double alphaS ){ + auto gS = std::sqrt( 4. * M_PI * alphaS ); + return gS; + } ); + } + ++currInd; + } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.moms ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.masses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.vtims ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.spins ); ++currInd; } + } + + return lheDos; + } + + std::shared_ptr>>> lheValDoubles( lheNode& lheFile, + statSort sorter, + const std::vector& statVec = {-1, 1}, lheRetDs vals = lheRetDs() ) + { + // ZW: hard-setting returning g_S instead of a_S for now + bool aStogS = true; + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile, sorter, statVec ); + auto lheDos = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheDos; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoD( { lheFile.getInit()->getHead()->ebmup[0], lheFile.getInit()->getHead()->ebmup[1] } ); ++currInd; } + if( boolVec[1] ){ + std::vector xsecVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xsecVec.push_back(line->xsecup); + } + lheDs[currInd] = vecStoD( xsecVec ); + ++currInd; } + if( boolVec[2] ){ + std::vector xerrVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xerrVec.push_back(line->xerrup); + } + lheDs[currInd] = vecStoD( xerrVec ); + ++currInd; } + if( boolVec[3] ){ + std::vector xmaxVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + xmaxVec.push_back(line->xmaxup); + } + lheDs[currInd] = vecStoD( xmaxVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[4] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.wgts ); ++currInd; } + if( boolVec[5] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.scales ); ++currInd; } + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQEDs ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.aQCDs ); + if( aStogS ){ + std::transform( lheDs[currInd]->begin(), lheDs[currInd]->end(), lheDs[currInd]->begin(), + []( double alphaS ){ + auto gS = std::sqrt( 4. * M_PI * alphaS ); + return gS; + } ); + } + ++currInd; + } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.moms ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.masses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.vtims ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.spins ); ++currInd; } + } + + return lheDos; + } + + // ZW: function for extracting transposed int values + // from LHE file + std::shared_ptr>>> lheValInts( lheNode& lheFile, lheRetInts vals = lheRetInts() ) + { + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile ); + auto lheIs = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheIs; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idbmup[0], lheFile.getInit()->getHead()->idbmup[1] } ); ++currInd; } + if( boolVec[1] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfgup[0], lheFile.getInit()->getHead()->pdfgup[1] } ); ++currInd; } + if( boolVec[2] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfsup[0], lheFile.getInit()->getHead()->pdfsup[1] } ); ++currInd; } + if( boolVec[3] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idwtup } ); ++currInd; } + if( boolVec[4] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->nprup } ); ++currInd; } + if( boolVec[5] ){ + std::vector lprVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + lprVec.push_back(line->lprup); + } + lheDs[currInd] = vecStoI( lprVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.nprts ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.procIDs ); ++currInd; } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.pdgs ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.statuses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.mothers ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.icols ); ++currInd; } + } + return lheIs; + } + + std::shared_ptr>>> lheValInts( lheNode& lheFile, std::vector statVec, + lheRetInts vals = lheRetInts() ) + { + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile, statVec ); + auto lheIs = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheIs; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idbmup[0], lheFile.getInit()->getHead()->idbmup[1] } ); ++currInd; } + if( boolVec[1] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfgup[0], lheFile.getInit()->getHead()->pdfgup[1] } ); ++currInd; } + if( boolVec[2] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfsup[0], lheFile.getInit()->getHead()->pdfsup[1] } ); ++currInd; } + if( boolVec[3] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idwtup } ); ++currInd; } + if( boolVec[4] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->nprup } ); ++currInd; } + if( boolVec[5] ){ + std::vector lprVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + lprVec.push_back(line->lprup); + } + lheDs[currInd] = vecStoI( lprVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.nprts ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.procIDs ); ++currInd; } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.pdgs ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.statuses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.mothers ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.icols ); ++currInd; } + } + return lheIs; + } + + std::shared_ptr>>> lheValInts( lheNode& lheFile, + sortFcn sorter, + std::vector statVec = {-1, 1}, lheRetInts vals = lheRetInts() ) + { + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile, sorter, statVec ); + auto lheIs = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheIs; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idbmup[0], lheFile.getInit()->getHead()->idbmup[1] } ); ++currInd; } + if( boolVec[1] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfgup[0], lheFile.getInit()->getHead()->pdfgup[1] } ); ++currInd; } + if( boolVec[2] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfsup[0], lheFile.getInit()->getHead()->pdfsup[1] } ); ++currInd; } + if( boolVec[3] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idwtup } ); ++currInd; } + if( boolVec[4] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->nprup } ); ++currInd; } + if( boolVec[5] ){ + std::vector lprVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + lprVec.push_back(line->lprup); + } + lheDs[currInd] = vecStoI( lprVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.nprts ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.procIDs ); ++currInd; } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.pdgs ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.statuses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.mothers ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.icols ); ++currInd; } + } + return lheIs; + } + + std::shared_ptr>>> lheValInts( lheNode& lheFile, + statSort sorter, + std::vector statVec = {-1, 1}, lheRetInts vals = lheRetInts() ) + { + auto boolVec = vals.getBools(); + const int noVals = std::count(boolVec.begin(), boolVec.end(), true); + auto lheAOS = transLHE( lheFile, sorter, statVec ); + auto lheIs = std::make_shared>>>(noVals * lheAOS.subProcs.size() ); + std::vector>> &lheDs = *lheIs; + int currInd = 0; + if( boolVec[0] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idbmup[0], lheFile.getInit()->getHead()->idbmup[1] } ); ++currInd; } + if( boolVec[1] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfgup[0], lheFile.getInit()->getHead()->pdfgup[1] } ); ++currInd; } + if( boolVec[2] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->pdfsup[0], lheFile.getInit()->getHead()->pdfsup[1] } ); ++currInd; } + if( boolVec[3] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->idwtup } ); ++currInd; } + if( boolVec[4] ){ lheDs[currInd] = vecStoI( { lheFile.getInit()->getHead()->nprup } ); ++currInd; } + if( boolVec[5] ){ + std::vector lprVec( lheFile.getInit()->getLines().size() ); + for( auto line : lheFile.getInit()->getLines() ) + { + lprVec.push_back(line->lprup); + } + lheDs[currInd] = vecStoI( lprVec ); + ++currInd; } + for( size_t k = 0 ; k < lheAOS.subProcs.size() ; ++k ) + { + if( boolVec[6] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.nprts ); ++currInd; } + if( boolVec[7] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsHead.procIDs ); ++currInd; } + if( boolVec[8] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.pdgs ); ++currInd; } + if( boolVec[9] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.statuses ); ++currInd; } + if( boolVec[10] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.mothers ); ++currInd; } + if( boolVec[11] ){ lheDs[currInd] = std::make_shared>( lheAOS.subProcs[k]->evtsData.icols ); ++currInd; } + } + return lheIs; + } +} + +#endif diff --git a/tools/REX/REX.h b/tools/REX/REX.h new file mode 100644 index 000000000..6a734e6c2 --- /dev/null +++ b/tools/REX/REX.h @@ -0,0 +1,1090 @@ +/*** + * ______ + * | ___ \ + * | |_/ /_____ __ + * | // _ \ \/ / + * | |\ \ __/> < + * \_| \_\___/_/\_\ + * + ***/ +// +// *R*apid *e*vent e*x*traction Version 0.9.0 +// Rex is a C++ library for parsing and manipulating Les Houches Event-format (LHE) files. +// It is designed to fast and lightweight, in comparison to internal parsers in programs like MadGraph. +// Currently, Rex is in development and may not contain all features necessary for full LHE parsing; +// particularly, it can only parse existing LHE files, rather than writing completely new ones. +// +// Copyright © 2023-2024 CERN, CERN Author Zenny Wettersten. +// Licensed under the GNU Lesser General Public License (version 3 or later). +// All rights not expressly granted are reserved. +// + +#ifndef _REX_H_ +#define _REX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ZW: all fcns within the REX standard sit in the +// namespace REX +// Note that as a convention, std::string_view objects will be +// referred to as strings unless the difference is relevant +namespace REX +{ + //#pragma warning( push ) + //#pragma warning( disable : 4101) + static const size_t npos = (size_t)-1; + #define UNUSED(x) (void)(x) + //#pragma warning( pop ) + + using sortFcn = std::function>(std::vector)>; + using statSort = std::function>(int, std::vector)>; + + // ZW: generic warning function for printing warnings without throwing anything + void warning( std::string message ); + + // ZW: generic fcns for converting string-like objects to integers and doubles + // Assumes input has no leading blankspace to check for a leading +, + // but should not fail as long as there is no + even if there is blankspace + // Note that Str needs to have a .compare(), .data() and .size() method + template + int ctoi( Str str ); + extern template int ctoi( std::string str ); + extern template int ctoi( std::string_view str ); + template + double ctod( Str str ); + extern template double ctod( std::string str ); + extern template double ctod( std::string_view str ); + + // ZW: index sorting function, which returns vector + // of the indices of the original vector sorted + // by default in ascending order + // ie, for [5.0, 0.25, 2.0, 9.2] returns [1, 2, 0, 3] + template + std::shared_ptr> indSort(const std::vector &vector, std::function comp = std::less()); + extern template std::shared_ptr> indSort(const std::vector &vector, std::function comp = std::less()); + extern template std::shared_ptr> indSort(const std::vector &vector, std::function comp = std::less()); + + template + std::shared_ptr> stoiSort(const std::vector &vector); + extern template std::shared_ptr> stoiSort(const std::vector &vector); + + template + std::shared_ptr> getRefOrder(const std::vector& reference, const std::vector& to_sort); + extern template std::shared_ptr> getRefOrder(const std::vector& reference, const std::vector& to_sort); + + std::shared_ptr> findEach( std::string_view textFile, std::string_view searchTerm ); + std::shared_ptr> lineSplitter( std::string_view currEvt ); + std::shared_ptr> blankSplitter( std::string_view currEvt ); + std::shared_ptr filePuller( const std::string& fileLoc ); + bool filePusher( std::string fileLoc, std::string fileCont ); + + // ZW: templated fcn for multiplying two vectors elementwise, + // assuming T has a multiplication operator* + template + std::shared_ptr> vecElemMult( const std::vector& vec1, const std::vector& vec2){ + if( vec1.size() < vec2.size() ){ return vecElemMult( vec2, vec1 ); } + auto valVec = std::make_shared>( vec1.size() ); + std::transform( vec1.begin(), vec1.end(), vec2.begin(), valVec->begin(), []( const T& v1, const T& v2 ){ + return v1 * v2; + } ); + return valVec; + } + struct xmlTree; + +// ZW: struct for handling tags in XML node opening tags + struct xmlTag { + public: + void setVal( std::string_view valSet ); + void setId( std::string_view idSet ); + std::string_view getVal(); + std::string_view getId(); + bool isModded(); + xmlTag(); + xmlTag( xmlTag& oldTag ); + xmlTag( std::string_view initId, std::string_view initVal); + protected: + bool modded; + std::string_view val; + std::string_view id; + }; + + struct xmlTree{ + public: + xmlTree(); + xmlTree( std::string_view file ); + xmlTree( std::string_view file, size_t& strt, size_t& nd ); + auto getChildren(){ return children; } + std::string_view& getOrigin(){ return origin; } + size_t getStart(){ return start; } + size_t getEnd(){ return end; } + size_t getContStart(){ return contSt; } + size_t getContEnd(){ return contEnd; } + std::pair getCont(){ if (contSt == npos || contEnd == npos){ return {0,0}; } return {contSt, contEnd - contSt}; } + std::pair getHeader(){ // ZW: pointers to beginning and end of node header, with some safety checks + if( start == npos || end == npos || faux ){ return {0,0}; } + if( contSt == npos ){ return {start, end - start }; } + return {start, contSt - start}; + } + bool isFaux(){ return faux; } + bool isInit(){ return initialised; } + bool hasChildren(){ return children->size() > 0; } + protected: + std::shared_ptr>> children; // vector of pointers to children nodes + std::string_view origin; + size_t start; // position of opening bracket of node opening + size_t end; // position of final character of ending node, including trailing blankspace + size_t contSt; + size_t contEnd; + bool faux = false; // boolean showing whether this item is a true node or content squeezed between nodes + bool initialised; + }; + + struct xmlNode { + public: + xmlNode(); + xmlNode( const std::string_view originFile, const size_t& begin = 0, const std::vector>& childs = {} ); + xmlNode( xmlTree &tree ); + xmlNode( const xmlNode& original ); + std::vector> getChildren(); + std::vector> getTags(); + std::string_view getFile(); + std::string_view getName(); + std::string_view getContent(); + size_t getStart(); + size_t getEnd(); + xmlTree getTree(); + virtual bool isModded(); + virtual bool isModded( bool deep ); + bool isWritten(); + bool isParsed(); + bool isFaux(); + bool hasChildren(); + void setModded( bool mod ); + bool deepModded(); + bool deepParse(); + void parser( bool recursive ); + void addChild( std::shared_ptr child ); + void addTag( std::shared_ptr tag ); + void setFile( std::string_view file ); + void setName( std::string_view newName ); + void setCont( std::string_view cont ); + protected: + virtual bool parse(); + virtual bool parse( bool recurs ); + bool parseTop(); + virtual bool parseContent(); + bool parseChildren( bool recursive ); + std::string nodeHeader; + std::string nodeContent; + std::string nodeEnd; + xmlTree structure; + std::vector> children; + std::vector> tags; + std::shared_ptr writtenSelf; + bool deepMod = false; + std::string_view xmlFile; + std::string_view name; + std::string_view content; + size_t start; + size_t end = npos; + bool modded = false; + bool written = false; + bool parsed = false; + bool deepParsed = false; + bool faux = false; + virtual void headWriter(); + virtual void endWriter(); + virtual void contWriter(); + virtual void childWriter(); + virtual void endFinder(); + virtual void fullWriter(); + public: + virtual int childCounter(); + virtual void childCounter( int& noChilds ); + virtual std::shared_ptr nodeWriter(); + }; + + struct lhePrt{ + public: + std::string_view getLine(); + std::string_view getComment(); + std::vector getMom(); + double getE(); + double getMass(); + double getVTim(); + double getSpin(); + int getPDG(); + int getStatus(); + std::vector getMothers(); + std::vector getColor(); + void setComment( std::string_view nuCom ); + void setMom( std::vector nuMom ); + void setEnergy( double nuE ); + void setMass( double nuM ); + void setVTim( double nuVTim ); + void setSpin( double nuSpin ); + void setPDG( int nuPDG ); + void setStatus( int nuSt ); + void setMothers( std::vector nuMum ); + void setColors( std::vector nuCol ); + bool isModded(); + bool isWritten(); + std::shared_ptr getContent(); + lhePrt(); + lhePrt( std::pair& prtInfo ); + lhePrt( int idup, int istup, int moth1, int moth2, int icol1, int icol2, double px, double py, double pz, double e, double m, double vt, double sp ); + lhePrt( std::pair& prtInfo ); + lhePrt( const std::string_view originFile, const size_t& beginLine = 0, const size_t& endLine = npos ); + protected: + std::shared_ptr content; + std::string_view sourceFile; + std::string_view comment; + double mom[3]; + double energy; + double mass; + double vtim; + double spin; + int pdg; + int status; + int mothers[2]; + int icol[2]; + bool modded = false; + bool written = false; + void writer(); + }; + + struct evHead { + public: + std::string_view getComment(); + double getWeight(); + double getScale(); + double getAQED(); + double getAQCD(); + int getNprt(); + int getProcID(); + bool isModded(); + bool isWritten(); + void setComment( std::string_view nuCom ); + void setWeight( double nuWgt ); + void setScale( double nuScale ); + void setAQED( double nuAQED ); + void setAQCD( double nuAQCD ); + void setNprt( int nuNprt ); + void setProcID( int nuProcID ); + std::shared_ptr getContent(); + evHead(); + evHead( const std::string_view originFile, size_t beginLine = 0, size_t endLine = npos ); + protected: + std::shared_ptr content; + std::string_view sourceFile; + std::string_view comment; + double weight; + double scale; + double aqed; + double aqcd; + int nprt; + //int nprtint; + //std::string nprtstr; + int procid; + bool modded = false; + bool written = false; + void writer(); + }; + + struct bodyWgt : public xmlNode { + public: + void setComment( std::string_view nuComment ); + void setVal( std::string nuVal ); + void setVal( std::string_view nuVal ); + void setVal( double nuVal ); + void setId( std::string nuId ); + void setModded( bool nuModded ); + std::string_view getComment(); + std::string_view getValS(); + double getValD(); + bodyWgt(); + bodyWgt( std::string_view value ); + bodyWgt( double value ); + bodyWgt( std::string_view value, xmlTag rwgtId ); + bodyWgt( double value, xmlTag rwgtId ); + bodyWgt( std::string_view value, std::shared_ptr rwgtId ); + bodyWgt( double value, std::shared_ptr rwgtId ); + bodyWgt( const std::string_view originFile, const size_t& begin = 0, const std::vector>& childs = {} ); + bodyWgt( xmlNode& wgtNode ); + bodyWgt( xmlNode* wgtNode ); + bodyWgt( std::shared_ptr wgtNode ); + bodyWgt( xmlTree& wgtTree ); + bodyWgt( xmlTree* wgtTree ); + bodyWgt( std::shared_ptr wgtTree ); + bodyWgt( double value, std::string& idTag ); + void appendWgt( std::shared_ptr document ); + void appendWgt( std::string* document ); + std::shared_ptr appendWgt( std::string_view document ); + protected: + std::string_view comment; + std::string valS; + std::string id; + double valD; + void fullWriter() override; + }; + + struct event : public xmlNode { + public: + evHead getHead(); + std::vector> getPrts(); + std::map> > getSortedPrts(); + std::vector> getWgts(); + void setHead( evHead head ); + void addPrt( std::shared_ptr prtcl ); + void addPrt( lhePrt prtcl ); + void setPrts( std::vector> prtcls ); + void addWgt( bodyWgt nuWgt ); + void addWgt( std::shared_ptr nuWgt ); + void addWgt( bodyWgt nuWgt, std::string& id ); + void addWgt( std::shared_ptr nuWgt, std::string& id ); + bool newWeight(); + int getNprt(); + bool isModded() override; + bool isModded( bool deep ) override ; + event(); + event( std::vector>& prtInfo ); + event( std::vector>& prtInfo ); + event( std::vector> prtInfo ); + event( const std::string_view originFile, const size_t& begin = 0, const std::vector>& childs = {} ) ; + event( const xmlNode& originFile ); + event( const xmlNode* originFile ); + event( const std::shared_ptr& originFile ); + event( xmlTree& originFile ); + event( xmlTree* originFile ); + event( std::shared_ptr originFile ); + event( const event& original ); + event( event* original ); + event( std::shared_ptr original ); + bool prtsAreMod(); + bool headIsMod(); + bool isSpecSort() const; + sortFcn getSortFcn() const; + statSort getStatSort() const; + protected: + std::vector> rwgt; + std::shared_ptr childRwgt; + bool hasRwgt(); + bool rwgtChild(); + bool bothRwgt(); + bool eitherRwgt(); + evHead header; + bool hasBeenProc = false; + std::vector> prts; + std::map> procMap; + std::map> procOrder; + std::map> > sortPrts; + sortFcn eventSort = []( std::vector vec ){ return indSort( vec ); }; + statSort specSort = []( int stat, std::vector vec ){ + UNUSED(stat); + return indSort( vec ); + }; + bool specSorted = false; + bool initProcMap(bool hard = false); + bool initProcMap( sortFcn sorter, bool hard = false ); + bool initProcMap( statSort sorter, bool hard = false ); + bool inRwgtChild( std::string_view name ); + bool checkRwgtOverlap(); + void childRwgtWriter(); + void vecRwgtWriter( bool midNode = false ); + void rwgtWriter(); + void contWriter() override; + void childWriter() override; + bool addedWgt = false; + void fullWriter() override; + void fullWriter( bool deep ); + void appendWgts(); + public: + std::shared_ptr nodeWriter() override; + std::shared_ptr nodeWriter( bool recursive ); + std::map> &getProc( bool hard = false ); + std::map> &getProcOrder( bool hard = false ); + std::map> getProc() const; + std::map> getProcOrder() const; + std::map> &getProc(sortFcn sorter, bool hard = true); + std::map> &getProcOrder(sortFcn sorter, bool hard = true); + std::map> &getProc(statSort sorter, bool hard = true); + std::map> &getProcOrder(statSort sorter, bool hard = true); + }; + + using eventComparison = std::function&)>; + + using eventSetComp = std::function&)>; + + struct eventSet{ + eventSet(); + eventSet( const eventSet& nuEvents ); + eventSet( std::vector& nuEvents ); + eventSet( std::vector>& nuEvents ); + void setRelStats( std::vector& nuStats ); + void addEvent( event& nuEvent ); + void addEvent( std::shared_ptr nuEvent ); + void addEvent( std::vector& nuEvents ); + void addEvent( std::vector> nuEvents ); + void setComp( eventSetComp nuComp ); + bool belongs( event& nuEvent ); + bool belongs( std::shared_ptr nuEvent ); + protected: + std::vector events; + std::vector relStats = {-1, 1}; + eventSetComp comp; + }; + + struct paramVal{ + public: + double value = 0; + int id = 0; + std::string_view realLine; + std::string_view comment; + std::string_view idStr; + std::string_view valStr; + virtual void parse(); + paramVal(); + paramVal( std::string_view paramLine, bool parseOnline = false ); + bool isMod(); + bool modded = false; + virtual std::shared_ptr selfWrite(); + }; + + struct paramBlock { + public: + std::string_view realBlock; + size_t startPt; + std::string_view comment; + std::string_view initComm; + std::string_view name; + std::vector params; + virtual void parse( bool parseOnline = false ); + paramBlock(); + paramBlock( std::string_view paramSet, bool parseOnline = false ); + bool isMod(); + bool modded = false; + virtual std::shared_ptr selfWrite(); + }; + + struct decVal : public paramVal{ + public: + void parse() override; + decVal( std::string_view paramLine = "", bool parseOnline = false ); + std::shared_ptr selfWrite() override; + }; + + struct decBlock : public paramBlock { + public: + std::vector decays; + void parse( bool parseOnline = false ) override; + void parse( std::shared_ptr> decLines, bool parseOnline = false ); + decBlock( std::string_view paramSet = "", bool parseOnline = false ); + std::shared_ptr selfWrite() override; + }; + + bool clStringComp( std::string_view str1, std::string str2 ); + bool clStringComp( std::string_view str1, std::string_view str2 ); + bool clStringComp( std::string str1, std::string str2 ); + bool clStringComp( std::string str1, std::string_view str2 ); + + struct lesHouchesCard { + public: + decBlock decays; + std::string_view xmlFile; + size_t start; + size_t end; + bool modded; + bool parsed; + std::string_view header; + std::vector blocks; + size_t blockStart; + std::function lambda = [&]( size_t& conPt, const std::string_view& file ) + { return !( file[conPt+1] == ' ' || file[conPt+1] == '#' || file[conPt+1] == '\n' ); }; + std::function lambdaNu = [&]( size_t& conPt, const std::string_view& file ) + { return !( file[conPt+1] == ' ' || file[conPt+1] == '\n' || file[conPt+1] == '<'); }; + std::function lambdaD = [&]( size_t& conPt, const std::string_view& file ) + { return !( clStringComp(file.substr(conPt+1, 1), std::string("d") ) ); }; + void parse( bool parseOnline = false ); + lesHouchesCard( const std::string_view originFile = "", const size_t& begin = 0, bool parseOnline = false ); + bool isMod(); + std::shared_ptr selfWrite(); + }; + + + struct headWeight : public xmlNode { + public: + int getId(); + std::string_view getTag(); + bool hasTag(); + headWeight(); + headWeight( std::string_view paramSet, const size_t& begin = 0 ); + headWeight( std::string_view paramSet, std::string_view idText, int idNo, const size_t& begin = 0 ); + headWeight( xmlNode& node ); + headWeight( xmlNode* node ); + headWeight( std::shared_ptr node ); + headWeight( xmlTree& tree ); + headWeight( xmlTree* tree ); + headWeight( std::shared_ptr tree ); + headWeight( std::string_view paramSet, std::string& idText, unsigned int idNo, const size_t& begin = 0 ); + headWeight( std::string_view paramSet, std::string& idText); + void setId( std::string identity ); + protected: + std::string idTag; + long unsigned int id = npos; + void headWriter() override; + void headWriter( bool incId ); + void endWriter() override; + void contWriter() override; + void childWriter() override; + void childWriter( bool hasChildren ); + void fullWriter() override; + void fullWriter( bool incId, bool hasChildren=true ); + }; + + + // ZW: struct for handling rwgt groups + // in the LHE header initrwgt node + struct weightGroup : public xmlNode { + public: + bool getIncId(); + void setIncId( bool nuIncId ); + std::vector> getWgts(); + void addWgt( headWeight nuWgt ); + void addWgt( std::shared_ptr nuWgt ); + weightGroup(); + weightGroup( std::vector> nuWgts ); + weightGroup( std::vector nuWgts ); + weightGroup( xmlNode& wgtNode ); + weightGroup( xmlNode* wgtNode ); + weightGroup( xmlTree& wgtTree ); + weightGroup( xmlTree* wgtTree ); + weightGroup( std::shared_ptr wgtTree ); + weightGroup( const std::string_view originFile, const size_t& begin = 0, const std::vector>& childs = {} ); + protected: + std::string_view rwgtName; + std::string_view wgtNamStrat; + bool includeId = false; + std::vector> paramSets; + bool nu; + std::string_view idTag; + int id; + void headWriter() override; + void contWriter() override; + void childWriter() override; + void childWriter( bool hasChildren ); + void endWriter() override; + }; + + + struct initRwgt : public xmlNode { + public: + std::vector> getGroups(); + size_t noGrps(); + void addGroup( weightGroup nuGroup ); + void addGroup( std::shared_ptr nuGroup ); + void addWgt( unsigned int index, std::shared_ptr nuWgt ); + void addWgt( unsigned int index, headWeight nuWgt ); + initRwgt(); + initRwgt( std::vector> nuGroups ); + initRwgt( xmlNode& wgtNode ); + initRwgt( xmlNode* wgtNode ); + initRwgt( std::shared_ptr wgtNode ); + initRwgt( xmlTree& wgtTree ); + protected: + bool grpIsInit = false; + bool grpInit( std::shared_ptr& wgt ); + std::vector> groups = {}; + void contWriter() override; + void childWriter() override; + void childWriter( bool hasChildren ); + }; + + struct lheInitHead{ + public: + std::string_view idbmup[2]; + std::string_view ebmup[2]; + std::string_view pdfgup[2]; + std::string_view pdfsup[2]; + std::string_view idwtup; + std::string_view nprup; + bool isWritten(); + bool isModded(); + std::shared_ptr getContent(); + lheInitHead( std::string_view initHead ); + lheInitHead( xmlNode& initNode ); + protected: + std::shared_ptr content; + bool written = false; + bool modded = false; + void writer(); + }; + + struct lheInitLine { + public: + std::string_view xsecup; + std::string_view xerrup; + std::string_view xmaxup; + std::string_view lprup; + bool isWritten(); + bool isModded(); + std::shared_ptr getContent(); + lheInitLine(); + lheInitLine( std::string_view procLine ); + protected: + std::shared_ptr content; + bool written = false; + bool modded = false; + void writer(); + }; + + + struct slhaNode : public xmlNode { + public: + std::shared_ptr getParameters(); + slhaNode(); + slhaNode( lesHouchesCard parameters ); + slhaNode( std::shared_ptr parameters ); + slhaNode( xmlNode& node, bool parseOnline = false ); + slhaNode( xmlNode* node, bool parseOnline = false ); + slhaNode( std::shared_ptr node, bool parseOnline = false ); + slhaNode( xmlTree tree, bool parseOnline = false ); + slhaNode( std::shared_ptr tree, bool parseOnline = false ); + slhaNode( xmlTree* tree, bool parseOnline = false ); + slhaNode( const std::string_view originFile, const size_t& begin = 0, bool parseOnline = false ); + protected: + std::shared_ptr parameterCard; + bool pCardInit = false; + void headWriter() override; + void endWriter() override; + void contWriter() override; + }; + + struct initNode : public xmlNode { + public: + std::shared_ptr getHead(); + std::vector> getLines(); + void setHead( std::shared_ptr head ); + void setLines( std::vector> lines ); + void addLine( std::shared_ptr line ); + initNode(); + initNode( const std::string_view originFile, const size_t& begin = 0, bool parseOnline = false ); + initNode( xmlNode& node, bool parseOnline = false ); + initNode( xmlNode* node, bool parseOnline = false ); + initNode( std::shared_ptr node, bool parseOnline = false ); + initNode( xmlTree tree, bool parseOnline = false ); + initNode( std::shared_ptr tree, bool parseOnline = false ); + initNode( xmlTree* tree, bool parseOnline = false ); + protected: + std::shared_ptr initHead; + std::vector> initLines; + bool parseContent() override; + void contWriter() override; + }; + + struct lheHead : public xmlNode { + public: + size_t addWgtGroup( std::shared_ptr& wgtGroup ); + size_t addWgtGroup( weightGroup wgtGroup ); + void addWgt( size_t index, std::shared_ptr nuWgt ); + void addWgt( size_t index, headWeight nuWgt ); + void addWgt( size_t index, std::shared_ptr nuWgt, std::string idTagg ); + void addWgt( size_t index, headWeight nuWgt, std::string idTagg ); + void setInitRwgt( initRwgt initWgt ); + void setInitRwgt( std::shared_ptr initWgt ); + std::vector> getWgtGroups(); + std::shared_ptr getInitRwgt(); + std::shared_ptr getParameters(); + void setParameters( std::shared_ptr params ); + bool rwgtInc(); + lheHead(); + lheHead( const std::string_view originFile, const size_t& begin = 0, const std::vector>& childs = {} ); + lheHead( xmlNode& node ); + lheHead( xmlNode* node ); + lheHead( std::shared_ptr node ); + lheHead( xmlTree tree ); + lheHead( std::shared_ptr tree ); + lheHead( xmlTree* tree ); + protected: + bool wgtGrpIsInit = false; + bool wgtGrpInit( std::shared_ptr& wgtGrp ); + std::shared_ptr parameters; + bool hasRwgt = false; + std::shared_ptr rwgtNodes; + std::vector> initrwgt; + bool relChildSet = false; + std::vector relChild; + void setRelChild(); + bool parseChildren( bool recursive ); + void headWriter() override; + void childWriter() override; + void fullWriter() override; + }; + + struct newWgt{ + protected: + std::shared_ptr headWgt; + std::vector> bodyWgts; + public: + newWgt( std::shared_ptr heaWgt, std::vector> bodWgts ); + newWgt( std::shared_ptr heaWgt, std::shared_ptr> wgts ); + newWgt( std::string_view parameters, std::shared_ptr> wgts, std::string idTag = "rex_rwgt" ); + newWgt( std::string_view parameters, int idNum, std::shared_ptr> wgts, std::string idTag = "rex_rwgt" ); + newWgt( std::string& parameters ); + newWgt( std::string& parameters, std::string& idTag ); + std::shared_ptr getHeadWgt(); + std::vector> getBodyWgts(); + void addBdyWgts( std::shared_ptr> wgts ); + }; + + + struct lheNode : public xmlNode { + public: + lheNode(); + lheNode( const std::string_view originFile, const size_t& begin = 0, const std::vector>& childs = {} ); + std::shared_ptr getHeader(); + std::shared_ptr getInit(); + std::vector> getEvents(); + bool isModded() override; + bool isModded( bool deep ) override; + void setInit( std::shared_ptr initNod ); + void setHeader( std::shared_ptr headNod ); + void addWgt( size_t index, newWgt& addedWgt ); + void addWgt( size_t index, newWgt& addedWgt, std::string& idTag ); + void setRelStats( std::vector& particles ); + std::vector& getRelStats(); + void setSameSort( sortFcn& sortF ); + sortFcn& getSameSort(); + void setStatSort( statSort& statS ); + statSort& getStatSort(); + protected: + std::vector> events = {}; + std::shared_ptr header = std::make_shared(xmlFile, start); + std::shared_ptr init = std::make_shared(xmlFile, start); + std::vector relStat = {"-1", "1"}; + sortFcn particleSort = []( std::vector prts ){ return indSort(prts); }; + statSort statParticleSort = []( int dummy, std::vector prts ){ UNUSED(dummy); return indSort(prts); }; + virtual void headerWriter(); + virtual void initWriter(); + virtual void eventWriter(); + void contWriter() override; + void fullWriter() override; + public: + virtual std::shared_ptr nodeWriter(); + }; + + struct evtInfo { + public: + std::vector wgts; + std::vector scales; + std::vector aQEDs; + std::vector aQCDs; + std::vector nprts; + std::vector relNPrts; + std::vector procIDs; + evtInfo( const std::vector>& lheFile = {} ); + evtInfo( const std::vector>& lheFile, const std::vector& statVec ); + evtInfo( const std::vector>& lheFile, const std::vector& statVec, + sortFcn sorter ); + evtInfo( const std::vector>& lheFile, const std::vector& statVec, + statSort sorter ); + }; + + struct prtInfo { + public: + std::vector moms; + std::vector masses; + std::vector vtims; + std::vector spins; + std::vector statuses; + std::vector mothers; + std::vector icols; + std::vector pdgs; + prtInfo( const std::vector>& lheFile = {}, const int nPrt = 8 ); + prtInfo( const std::vector>& lheFile, const int nPrt, const std::vector& statVec ); + prtInfo( const std::vector>& lheFile, const int nPrt, const std::vector& statVec, + sortFcn sorter ); + prtInfo( const std::vector>& lheFile, const int nPrt, const std::vector& statVec, + statSort sorter ); + }; + + struct transSkel { + public: + std::vector>> procSets; + std::vector>> relProcs; + std::vector relEvSet; + transSkel(); + transSkel( transSkel& skeleton ); + transSkel( lheNode& lheFile, std::vector& evSet ); + transSkel( std::shared_ptr lheFile, std::vector& evSet ); + }; + + struct transMonoLHE { + public: + evtInfo evtsHead; + prtInfo evtsData; + std::shared_ptr process; + transMonoLHE( const std::vector> lheFile = {}, const int nPrt = 8 ); + transMonoLHE( const std::vector> lheFile, const int nPrt, const std::vector& statVec ); + transMonoLHE( const std::vector> lheFile, const int nPrt, + sortFcn sorter, + std::vector statVec = { -1, 1 } ); + transMonoLHE( const std::vector> lheFile, const int nPrt, + statSort sorter, + std::vector statVec = { -1, 1 } ); + transMonoLHE( const transMonoLHE& lheFile ); + }; + + struct transLHE { + public: + std::string_view xmlFile; + std::vector> subProcs; + std::vector> procSets; + std::vector>> relProcs; + std::vector relEvSets; + void setRelEvSets(); + transLHE(); + transLHE( lheNode& lheFile ); + transLHE( lheNode& lheFile, + sortFcn sorter, + const std::vector& statVec = { -1, 1 } ); + transLHE( lheNode& lheFile, + statSort sorter, + const std::vector& statVec = { -1, 1 } ); + transLHE( lheNode& lheFile, const std::vector& statVec ); + transLHE( transSkel& skeleton ); + transLHE( const transLHE& lheFile ); + std::shared_ptr> vectorFlat( std::vector>> vecVec ); + }; + + struct lheRetDs{ + public: + bool ebmup = false; + bool xsecup = false; + bool xerrup = false; + bool xmaxup = false; + bool xwgtup = false; + bool scalup = false; + bool aqedup = false; + bool aqcdup = false; + bool pup = true; + bool mass = false; + bool vtimup = false; + bool spinup = false; + std::vector getBools(); + }; + + // ZW: bool struct to define which int values + // to extract transposed from LHE file + struct lheRetInts{ + public: + //bool maxpup = false; + bool idbmup = false; + bool pdfgup = false; + bool pdfsup = false; + bool idwtup = false; + bool nprup = false; + bool lprup = false; + //bool maxnup = false; + bool nup = true; + bool idprup = false; + bool idup = true; + bool istup = true; + bool mothup = false; + bool icolup = false; + std::vector getBools(); + }; + + struct eventComp{ + bool operator()( event& firstEv, event& secEv); + bool operator()( const event& firstEv, const event& secEv) const; + bool operator()(event& firstEv, event& secEv, std::vector statVec); + }; + + using evComp = std::function; + using evCheck = std::function; + + // ZW: struct to define which values to extract from events in + // an AoS LHE structure to an SoA structure + // Note: has two separate args for momenta, + // momUp denotes the physical 4-momentum (E, px, py, pz), + // whereas pUp denotes the SLHA/LHE style lab frame momenta (px, py, pz, E, m) + struct relEvArgs{ + bool nUp = true; // number of particles in this event + bool idPrUp = true; // process ID + bool xWgtUp = true; // event weight + bool scalUp = false; // event scale + bool aQEDUp = false; // alpha QED + bool aQCDUp = true; // alpha QCD + bool idUp = true; // particle PDG code + bool iStUp = true; // particle status + bool mothUp[2] = {false, false}; // particle mothers + bool iColUp[2] = {false, false}; // particle colour flow + bool massUp = false; // particle mass in GeV + bool momUp = true; // generic flag for particle momenta; momUp denotes full 4-momentum (excl mass), following flags denote individual components + bool pUp[5] = {false, false, false, false, false}; // lab frame momenta (px, py, pz, E, m) --- separates the different components + bool vTimUp = false; // invariant lifetime in mm + bool spinUp = false; // cosine of angle between spin and 3-momentum of particle in lab frame + relEvArgs(); + relEvArgs( const relEvArgs& relData ); + // ZW: self-returning setters for each flag, + // to allow for easy chaining of multiple flags + // in a single line (ie relEvArgs().setNUp(true).setIdPrUp(true) etc) + relEvArgs& setNUp( bool nuNUp ); + relEvArgs& setIdPrUp( bool nuIdPrUp ); + relEvArgs& setXWgtUp( bool nuXWgtUp ); + relEvArgs& setScalUp( bool nuScalUp ); + relEvArgs& setAQEDUp( bool nuAQEDUp ); + relEvArgs& setAQCDUp( bool nuAQCDUp ); + relEvArgs& setIdUp( bool nuIdUp ); + relEvArgs& setIStUp( bool nuIStUp ); + relEvArgs& setMothUp( bool nuMothUp[2] ); + relEvArgs& setMothUp( bool nuMothUp ); + relEvArgs& setMothUp( std::vector nuMothUp ); + relEvArgs& setIColUp( bool nuIColUp[2] ); + relEvArgs& setIColUp( bool nuIColUp ); + relEvArgs& setIColUp( std::vector nuIColUp ); + relEvArgs& setMassUp( bool nuMassUp ); + relEvArgs& setMomUp( bool nuMomUp ); + relEvArgs& setPUp( bool nuPUp[5] ); + relEvArgs& setPUp( bool nuPUp ); + relEvArgs& setPUp( std::vector nuPUp ); + relEvArgs& setVTimUp( bool nuVTimUp ); + relEvArgs& setSpinUp( bool nuSpinUp ); + relEvArgs& setAll( bool nuAll ); + }; + + // ZW: individual process lines + // keeping object oriented format due to size, + // but could easily be replaced with an SoA format if needed + // (turn arguments here into vectors, and replace the lheSoA->procLines vector with a single struct) + struct procLine{ + double xSecUp; // cross section in pb + double xErrUp; // cross section error in pb + double xMaxUp; // maximum event weight + int lPrUp; // process ID label (corresponds to idPrUp in the events) + procLine(); + procLine( const procLine& process ); + procLine( double xSec, double xErr, double xMax, int lPr ); + procLine( lheInitLine& process ); + procLine( std::shared_ptr process ); + }; + + std::vector lheProcLines( lheNode& lheFile ); + + struct procSoA{ + relEvArgs relData; + std::vector relStats; + evCheck relevantEvent; + std::vector> events; + std::vector relEvMap; + std::vector> relEvents; + std::vector nUp; + std::vector idPrUp; + std::vector xWgtUp; + std::vector scalUp; + std::vector aQEDUp; + std::vector aQCDUp; + std::vector idUp; + std::vector iStUp; + std::vector> mothUp; + std::vector> iColUp; + std::vector massUp; + std::vector momUp; + std::vector> pUp; + std::vector vTimUp; + std::vector spinUp; + virtual void reset(); + void setRelEvs( std::vector> lheFile ); + bool relevant( event& ev ); + bool relevant( std::shared_ptr ev ); + bool extract( std::vector> lheFile, std::vector relevStats = {-1,1} ); + bool empty(); + procSoA(); + procSoA( const relEvArgs& relData ); + procSoA( const procSoA& process ); + procSoA( procSoA* process ); + procSoA( std::shared_ptr process ); + procSoA( std::vector> lheFile, relEvArgs relArgs = relEvArgs(), + std::vector relevStats = {-1,1}, std::function relFcn = nullptr ); + procSoA& setRelData( relEvArgs& relArgs ); + procSoA& setRelStats( std::vector& relevStats ); + procSoA& setRelevant( evCheck& relFcn ); + procSoA& setEvents( std::vector> lheFile ); + }; + + struct lheSoA{ + std::vector> subProcesses; // transposed events grouped by arbitrary sorting functions + // ZW: note, at extraction events which fail all relEvFcns not extracted but are still stored in the events vectors + std::vector> events; // all events in the LHE file + std::vector>> sortedEvents; // events grouped by process before extraction. events that fail all relEvFcns are stored in the last entry + std::vector procLines; // individual process lines from the LHE file header + std::vector relEvFcns; // vector of event classifiers + std::vector eventGrouping; // indices to which subProcess each event belongs to + std::vector relEvData; // vector of relEvArgs for each subProcess + std::vector> relEvStats; // vector of relevant particle statuses for each subProcess + int idBmUp[2]; // beam IDs + double eBmUp[2]; // beam energies in GeV + int pdfGUp[2]; // Cernlib PDFlib specification authors + int pdfSUp[2]; // Cernlib PDFlib specification PDF sets + int idWtUp; // event weight model + int nPrUp; // number of different user subprocesses; note, not necessarily the same as subProcess vector + virtual void reset(); // reset all data but maintain instantiation + size_t eventIndex( event& ev ); // map an event to the correct subProcess, based on the order in relEvFcns + bool sortEvents( bool hard = false ); // sort events based on relEvFcns into sortedEvents + bool extractEvents( bool hard = false ); // extract events from sortedEvents into subProcesses + std::function evFcnIndex( size_t index ); // return the event classifier function at index if it exists, otherwise return 0-index + relEvArgs evDataIndex( size_t index ); // return the relEvArgs at index if it exists, otherwise return 0-index + std::vector evStatsIndex( size_t index ); // return the relevant particle statuses at index if it exists, otherwise return 0-index + lheSoA(); + lheSoA( const lheSoA& lheFile ); + lheSoA( std::vector> lheFile ); + lheSoA( std::vector> lheFile, std::vector> evSort ); + lheSoA( lheNode& lheFile, std::vector> evSort ); + lheSoA( std::vector> lheFile, std::vector> evSort, + std::vector relData ); + lheSoA( lheNode& lheFile, std::vector> evSort, + std::vector relData ); + lheSoA( std::vector> lheFile, std::vector> evSort, + std::vector> relStats ); + lheSoA( lheNode& lheFile, std::vector> evSort, + std::vector> relStats ); + lheSoA( std::vector> lheFile, std::vector> evSort, + std::vector relData, std::vector> relStats ); + lheSoA( lheNode& lheFile, std::vector> evSort, + std::vector relData, std::vector> relStats ); + lheSoA& setInit( lheInitHead& init ); + lheSoA& setInit( lheNode& lheFile ); + lheSoA& setEvents( std::vector> lheFile ); + lheSoA& setEvents( lheNode& lheFile ); + lheSoA& setProcLines( std::vector procLines ); + lheSoA& setProcLines( std::vector> procLines ); + lheSoA& setProcLines( initNode& init ); + lheSoA& setProcLines( lheNode& lheFile ); + lheSoA& setRelEvFcns( std::vector> evSort ); + lheSoA& setRelEvData( std::vector relData ); + }; + +std::shared_ptr>>> lheValDoubles( lheNode& lheFile, lheRetDs vals = lheRetDs() ); + +std::shared_ptr>>> lheValDoubles(transLHE& lheAOS, lheRetDs vals = lheRetDs() ); + +} + +#endif diff --git a/tools/REX/rwgt_driver.cc b/tools/REX/rwgt_driver.cc new file mode 100644 index 000000000..3454e80c1 --- /dev/null +++ b/tools/REX/rwgt_driver.cc @@ -0,0 +1,200 @@ +//========================================================================== +// Copyright (C) 2023-2024 CERN +// Licensed under the GNU Lesser General Public License (version 3 or later). +// Written by: Z. Wettersten (Jan 2024) for the MG5aMC CUDACPP plugin. +//========================================================================== +//========================================================================== +// This file has been automatically generated for C++ Standalone by +%(info_lines)s +//========================================================================== +//========================================================================== +// Driver for reweighting events for processes +%(multiprocess_lines)s +//-------------------------------------------------------------------------- + +#include "rwgt_instance.h" +#include +#include +#include +%(include_lines)s + +int usage( char* argv0, int ret = 1 ) +{ + std::cout << "Usage: " << argv0 + << " [--lhefile=\"/YOUR/PATH/HERE\"|-lhe=\"/YOUR/PATH/HERE\"] [--rwgtcard=/YOUR/PATH/HERE|-rwgt=\"/YOUR/PATH/HERE\"]\n" + << "[--output=/YOUR/PATH/HERE\"|-out=\"/YOUR/PATH/HERE\"]\n" << "[--param_card=/YOUR/PATH/HERE\"|-slha=\"/YOUR/PATH/HERE\"]\n"; + std::cout << "\n"; + std::cout << "The LHE file path should be with respect to the directory you are running\n"; + std::cout << "this program from, and similarly the rwgt_card should be as well.\n"; + return ret; +} + +void writeRwgtCsv( std::string path, std::shared_ptr> names, std::shared_ptr> xSecs, std::shared_ptr> errXSecs ) +{ + std::ofstream outFile; + outFile.open( path ); + if( !outFile.is_open() ) + throw std::runtime_error( "Failed to open output file for writing." ); + if( names->size() != xSecs->size() || names->size() != errXSecs->size() ) + throw std::runtime_error( "Mismatch in number of processes, cross-sections, and errors when logging results." ); + for( size_t k = 0 ; k < names->size() ; ++k ) + { + outFile << names->at(k) << ", " << xSecs->at(k) << ", " << errXSecs->at(k) << "\n"; + } + outFile.close(); + return; +} + +void writeRwgtCsv( std::string path, std::vector names, std::vector xSecs, std::vector errXSecs ) +{ + std::ofstream outFile; + outFile.open( path ); + if( !outFile.is_open() ) + throw std::runtime_error( "Failed to open output file for writing." ); + if( names.size() != xSecs.size() || names.size() != errXSecs.size() ) + throw std::runtime_error( "Mismatch in number of processes, cross-sections, and errors when logging results." ); + for( size_t k = 0 ; k < names.size() ; ++k ) + { + outFile << names.at(k) << ", " << xSecs.at(k) << ", " << errXSecs.at(k) << "\n"; + } + outFile.close(); + return; +} + +int main( int argc, char** argv ){ + std::cout << "Starting tRex driver...\n"; + std::string lheFilePath; + std::string rwgtCardPath; + std::string outputPath; + std::string slhaPath; + + if (argc < 2){ + return usage( argv[0] ); + } + + for( int i = 1; i < argc; i++ ) + { + auto currArg = std::string( argv[i] ); + if( currArg.substr(0,9) == "--lhefile" || currArg.substr(0,4) == "-lhe" ) + { + lheFilePath = currArg.substr( currArg.find( "=" ) + 1 ); + } + else if( currArg.substr(0,10) == "--rwgtcard" || currArg.substr(0,5) == "-rwgt" ) + { + rwgtCardPath = currArg.substr( currArg.find( "=" ) + 1 ); + } else if( currArg.substr(0,8) == "--output" || currArg.substr(0,4) == "-out" ){ + outputPath = currArg.substr( currArg.find( "=" ) + 1 ); + } else if (currArg.substr(0,12) == "--param_card" || currArg.substr(0,5) == "-slha" ){ + slhaPath = currArg.substr( currArg.find( "=" ) + 1 ); + } + else { + return usage( argv[0] ); + } + } + + if( lheFilePath.empty() || rwgtCardPath.empty() ){ + return usage( argv[0] ); + } + + std::string currPath = argv[0]; + + size_t slashPos = currPath.find_last_of( "/" ); + bool onWindows = false; + if( slashPos == std::string::npos ){ slashPos = currPath.find_last_of( "\\" ); onWindows = true; } + if( slashPos == std::string::npos ) + throw std::runtime_error( "Failed to determine current working directory -- need to know where program is run from to identify where to pull and push param_card.dat." ); + + if( slhaPath.empty() ){ + if( onWindows ){ + if( currPath.substr( currPath.find_last_of("\\", slashPos - 1) + 1, 2 ) == "P1" ){ + slhaPath = "..\\..\\Cards\\param_card.dat"; + } else if( currPath.substr( currPath.find_last_of("\\", slashPos - 1) + 1, 3 ) == "Sub" ){ + slhaPath = "..\\Cards\\param_card.dat"; + } else{ + slhaPath = "\\Cards\\param_card.dat"; + } + } else { + if( currPath.substr( currPath.find_last_of("/", slashPos - 1) + 1, 2 ) == "P1" ){ + slhaPath = "../../Cards/param_card.dat"; + } else if( currPath.substr( currPath.find_last_of("/", slashPos - 1) + 1, 3 ) == "Sub" ) { + slhaPath = "../Cards/param_card.dat"; + } else { + slhaPath = "/Cards/param_card.dat"; + } + }} + + + // static REX::tea::rwgtFiles fileCol( lheFilePath, slhaPath, rwgtCardPath ); + // static std::vector runSet = {(run_set)}; + // static REX::transSkel loadEvs = fileCol.initCards( runSet ); + // fileCol.initDoubles(); + // static std::vector fBridgeVec = {(fbridge_vec)}; + // static std::vector bridges; + // static std::vector amps; + // size_t relSet = 0; + // for( size_t k = 0 ; k < runSet.size() ; ++k ){ + // if( !loadEvs.relEvSet[k] ){ continue; } + // fBridgeVec[k].init( loadEvs.procSets[relSet], 32 ); + // bridges.push_back( fBridgeVec[k] ); + // auto currAmp = [bridge = bridges[relSet]](std::vector& momenta, std::vector& alphaS) mutable { + // return bridge.bridgeCall(momenta, alphaS); + // }; + // amps.push_back( currAmp ); + // ++relSet; + // } + + // REX::tea::rwgtRunner driver( fileCol, amps ); + + // driver.runRwgt( outputPath ); + + // auto rwgt_names = driver.getNames(); + // auto rwgt_xSecs = driver.getReXSecs(); + // auto rwgt_errXSecs = driver.getReXErrs(); + + auto lheInput = REX::filePuller( lheFilePath ); + auto lheRun = REX::lheNode( *lheInput ); + auto slhaInput = REX::filePuller( slhaPath ); + auto slhaRun = REX::lesHouchesCard( *slhaInput ); + auto rwgtInput = REX::filePuller( rwgtCardPath ); + auto rwgtRun = REX::tea::rwgtCard( *rwgtInput, slhaRun, true ); + + static std::vector> comparators = {%(comparators)s}; + static auto runner = REX::tea::reweightor( lheRun, comparators ); + + if( !runner.extractEvents() ) throw std::runtime_error( "Failed to extract events from LHE file." ); + + static std::vector fBridgeVec = {%(fbridge_vec)s}; + static std::vector amplitudes; + for( size_t k = 0 ; k < fBridgeVec.size() ; ++k ) + { + fBridgeVec[k].init(runner.sortedEvents[k], 32); + amplitudes.push_back( fBridgeVec[k].getAmp() ); + } + + runner.setAmps( amplitudes ); + + auto returnTrue = std::function([](){ return true; }); + auto writeSlha = std::function([slhaInput, slhaPath](){ + return REX::filePusher(slhaPath, *slhaInput); + }); + + runner.setAmps(amplitudes).setIterators(rwgtRun.getIterators(slhaPath)).setTerminator(writeSlha).setOriginator(returnTrue); + + runner.run(); + + runner.appendWgts( lheRun, rwgtRun.getProcs(), rwgtRun.getNames() ); + + std::cout << "\nReweighting procedure finished.\n"; + + bool lheWritten = REX::filePusher( outputPath, *lheRun.nodeWriter() ); + + if( !lheWritten ) + throw std::runtime_error( "Failed to write LHE file." ); + + std::cout << "Reweighted LHE file written to " << outputPath << ".\n"; + + writeRwgtCsv( "rwgt_results.csv", *rwgtRun.getNames(), runner.xSecs, runner.xErrs ); + + return 0; + +} \ No newline at end of file diff --git a/tools/REX/rwgt_driver.inc b/tools/REX/rwgt_driver.inc new file mode 120000 index 000000000..77a39010f --- /dev/null +++ b/tools/REX/rwgt_driver.inc @@ -0,0 +1 @@ +rwgt_driver.cc \ No newline at end of file diff --git a/tools/REX/rwgt_instance.cc b/tools/REX/rwgt_instance.cc new file mode 100644 index 000000000..aba44a7dc --- /dev/null +++ b/tools/REX/rwgt_instance.cc @@ -0,0 +1,166 @@ +//========================================================================== +// Copyright (C) 2023-2024 CERN +// Licensed under the GNU Lesser General Public License (version 3 or later). +// Written by: Z. Wettersten (Jan 2024) for the MG5aMC CUDACPP plugin. +//========================================================================== +//========================================================================== +// Library including generic functions and classes for event reweighting. +// Process-specific rwgt_runner files are generated by mg5amc@nlo and use +// this library, while the rwgt_driver file is a wrapping program that +// calls the process-specific runners for given subprocesses. +//========================================================================== + +#ifndef _RWGT_INSTANCE_CC_ +#define _RWGT_INSTANCE_CC_ + +#include "rwgt_instance.h" + +namespace rwgt{ + + //ZW: Function for calculating the number of remaining events in a warp + // in order to pad the input arrays to a multiple of the warp size + unsigned int warpRemain( unsigned int nEvt, unsigned int nWarp ){ + return (nWarp - ( nEvt % nWarp )) % nWarp; + } + + //ZW: Function for padding the input arrays to a multiple of the warp size + template + void warpPad( std::vector& input, unsigned int nWarp = 32 ){ + auto nEvt = input.size(); + auto nWarpRemain = warpRemain( nEvt, nWarp ); + input.reserve( nEvt + nWarpRemain ); + for( size_t k = nEvt - nWarpRemain ; k < nEvt ; ++k ){ + input.push_back( input[k] ); + } + return; + } + + fBridge::fBridge(){} + fBridge::fBridge( REX::event& process ){ + this->nPar = process.getPrts().size(); + this->goodHel = false; + } + fBridge::fBridge( std::vector& process, unsigned int warpSize){ + this->nPar = process[0].getPrts().size(); + this->nEvt = process.size(); + this->nWarp = warpSize; + this->nWarpRemain = warpRemain( nEvt, nWarp ); + this->fauxNEvt = nEvt + nWarpRemain; + this->rndHel = std::vector( fauxNEvt, 0. ); + this->rndCol = std::vector( fauxNEvt, 0. ); + this->selHel = std::vector( fauxNEvt, 0. ); + this->selCol = std::vector( fauxNEvt, 0. ); + this->goodHel = false; + } + fBridge::fBridge( std::vector> process, unsigned int warpSize){ + this->nPar = process[0]->getPrts().size(); + this->nEvt = process.size(); + this->nWarp = warpSize; + this->nWarpRemain = warpRemain( nEvt, nWarp ); + this->fauxNEvt = nEvt + nWarpRemain; + this->rndHel = std::vector( fauxNEvt, 0. ); + this->rndCol = std::vector( fauxNEvt, 0. ); + this->selHel = std::vector( fauxNEvt, 0. ); + this->selCol = std::vector( fauxNEvt, 0. ); + this->goodHel = false; + } + fBridge::fBridge( const fBridge& source ){ + this->rndHel = source.rndHel; + this->rndCol = source.rndCol; + this->selHel = source.selHel; + this->selCol = source.selCol; + this->chanId = source.chanId; + this->nMom = source.nMom; + this->nWarp = source.nWarp; + this->nWarpRemain = source.nWarpRemain; + this->nEvt = source.nEvt; + this->fauxNEvt = source.fauxNEvt; + this->nPar = source.nPar; + this->bridge = source.bridge; + this->goodHel = source.goodHel; + } + void fBridge::init( std::vector& process, unsigned int warpSize ){ + if( process.size() == 0 ){ + this->nWarp = warpSize; + return; + } + this->nPar = process[0].getPrts().size(); + this->nEvt = process.size(); + this->nWarp = warpSize; + this->nWarpRemain = warpRemain( nEvt, nWarp ); + this->fauxNEvt = nEvt + nWarpRemain; + this->rndHel = std::vector( fauxNEvt, 0. ); + this->rndCol = std::vector( fauxNEvt, 0. ); + this->selHel = std::vector( fauxNEvt, 0. ); + this->selCol = std::vector( fauxNEvt, 0. ); + } + void fBridge::init( std::vector> process, unsigned int warpSize ){ + if( process.size() == 0 ){ + this->nWarp = warpSize; + return; + } + this->nPar = process[0]->getPrts().size(); + this->nEvt = process.size(); + this->nWarp = warpSize; + this->nWarpRemain = warpRemain( nEvt, nWarp ); + this->fauxNEvt = nEvt + nWarpRemain; + this->rndHel = std::vector( fauxNEvt, 0. ); + this->rndCol = std::vector( fauxNEvt, 0. ); + this->selHel = std::vector( fauxNEvt, 0. ); + this->selCol = std::vector( fauxNEvt, 0. ); + } + void fBridge::bridgeSetup( unsigned int& noEvts, unsigned int warpSize ){ + this->nEvt = noEvts; + this->nWarp = warpSize; + this->nWarpRemain = warpRemain( nEvt, nWarp ); + this->fauxNEvt = nEvt + nWarpRemain; + this->rndHel = std::vector( fauxNEvt, 0. ); + this->rndCol = std::vector( fauxNEvt, 0. ); + this->selHel = std::vector( fauxNEvt, 0. ); + this->selCol = std::vector( fauxNEvt, 0. ); + } + void fBridge::bridgeSetup( std::vector& evVec, unsigned int warpSize ){ + this->nEvt = evVec.size(); + this->nWarp = warpSize; + this->nWarpRemain = warpRemain( nEvt, nWarp ); + this->fauxNEvt = nEvt + nWarpRemain; + this->rndHel = std::vector( fauxNEvt, 0. ); + this->rndCol = std::vector( fauxNEvt, 0. ); + this->selHel = std::vector( fauxNEvt, 0. ); + this->selCol = std::vector( fauxNEvt, 0. ); + } + void fBridge::bridgeSetup( std::shared_ptr>& evVec, unsigned int warpSize ){ + this->bridgeSetup( *evVec, warpSize ); + } + void fBridge::setBridge( bridgeWrapper& amp ){ + if( this->bridge == nullptr){ + this->bridge = amp; + } else throw std::runtime_error("fBridge object doubly defined."); + } + std::shared_ptr> fBridge::bridgeCall( std::vector& momenta, std::vector& alphaS ){ + if(this->nEvt == 0) this->bridgeSetup( alphaS ); + if( this->bridge == nullptr) throw std::runtime_error("fBridge object not defined."); + warpPad( alphaS, nWarp ); + warpPad( momenta, nWarp * nPar * nMom ); + auto evalScatAmps = this->bridge(fauxNEvt, nPar, nMom, momenta, alphaS, rndHel, rndCol, selHel, selCol, chanId, goodHel ); + alphaS.resize( nEvt ); + momenta.resize( nEvt * nPar * nMom ); + evalScatAmps->resize( nEvt ); + return evalScatAmps; + } + + std::shared_ptr> fBridge::bridgeCall( REX::procSoA& process ){ + return this->bridgeCall( process.momUp, process.aQCDUp ); + } + + REX::tea::weightor fBridge::getAmp(){ + if(this->bridge == nullptr) throw std::runtime_error("fBridge object not defined."); + REX::tea::weightor amp = [this](REX::procSoA& process){ + return this->bridgeCall( process ); + }; + return amp; + } + +} + +#endif diff --git a/tools/REX/rwgt_instance.h b/tools/REX/rwgt_instance.h new file mode 100644 index 000000000..76e4eac0f --- /dev/null +++ b/tools/REX/rwgt_instance.h @@ -0,0 +1,67 @@ +//========================================================================== +// Copyright (C) 2023-2024 CERN +// Licensed under the GNU Lesser General Public License (version 3 or later). +// Written by: Z. Wettersten (Jan 2024) for the MG5aMC CUDACPP plugin. +//========================================================================== +//========================================================================== +// Library including generic functions and classes for event reweighting. +// Process-specific rwgt_runner files are generated by mg5amc@nlo and use +// this library, while the rwgt_driver file is a wrapping program that +// calls the process-specific runners for given subprocesses. +//========================================================================== + +#ifndef _RWGT_INSTANCE_H_ +#define _RWGT_INSTANCE_H_ + +#include "teaREX.h" + +/** + * The floating point precision used in Fortran arrays. + * This is presently hardcoded to double precision (REAL*8). + */ +using FORTRANFPTYPE = double; // for Fortran double precision (REAL*8) arrays +//using FORTRANFPTYPE = float; // for Fortran single precision (REAL*4) arrays + + +namespace rwgt{ + + //ZW: Function for calculating the number of remaining events in a warp + // in order to pad the input arrays to a multiple of the warp size + unsigned int warpRemain( unsigned int nEvt, unsigned int nWarp = 32 ); + + // ZW: bridgeWrapper needs args: nEvs, nPar, nMom, moms, gs, rndhel, rndcol, selhel, selcol, chanId + using bridgeWrapper = std::function>( int&, int&, int&, std::vector&, std::vector&, std::vector&, std::vector&, std::vector&,std::vector&, unsigned int&, bool& )>; + + struct fBridge{ + std::vector rndHel; + std::vector rndCol; + std::vector selHel; + std::vector selCol; + unsigned int chanId = 0; + int nMom = 4; + int nWarp; + int nWarpRemain; + int nEvt; + int fauxNEvt; + int nPar; + bool goodHel = false; + bridgeWrapper bridge; + fBridge(); + fBridge( REX::event& process ); + fBridge( std::vector& process, unsigned int warpSize = 32 ); + fBridge( std::vector> process, unsigned int warpSize = 32 ); + fBridge( const fBridge& source ); + void init( std::vector& process, unsigned int warpSize = 32 ); + void init( std::vector> process, unsigned int warpSize = 32 ); + void bridgeSetup( unsigned int& noEvts, unsigned int warpSize = 32); + void bridgeSetup( std::vector& evVec, unsigned int warpSize = 32); + void bridgeSetup( std::shared_ptr>& evVec, unsigned int warpSize = 32); + void setBridge( bridgeWrapper& amp ); + std::shared_ptr> bridgeCall( std::vector& momenta, std::vector& alphaS ); + std::shared_ptr> bridgeCall( REX::procSoA& process ); + REX::tea::weightor getAmp(); + }; + +} + +#endif \ No newline at end of file diff --git a/tools/REX/rwgt_runner.cc b/tools/REX/rwgt_runner.cc new file mode 100644 index 000000000..716933320 --- /dev/null +++ b/tools/REX/rwgt_runner.cc @@ -0,0 +1,134 @@ +//========================================================================== +// Copyright (C) 2023-2024 CERN +// Licensed under the GNU Lesser General Public License (version 3 or later). +// Written by: Z. Wettersten (Jan 2024) for the MG5aMC CUDACPP plugin. +//========================================================================== +//========================================================================== +// This file has been automatically generated for the CUDACPP plugin by +%(info_lines)s +//========================================================================== +//========================================================================== +// A class for reweighting matrix elements for +%(process_lines)s +//-------------------------------------------------------------------------- +#ifndef _TREX_ +#define _TREX_ +#endif +#include "rwgt_instance.h" +#include "fbridge.h" + +// ZW: SET UP NAMESPACE +namespace %(process_namespace)s{ +//namespace dummy{ + + std::vector> getInitPrts(){ + static std::vector> initPrts = {%(init_prt_ids)s}; + return initPrts; + } + + std::vector> getFinPrts(){ + static std::vector> finPrts = {%(fin_prt_ids)s}; + return finPrts; + } + + std::shared_ptr> amp( int& nEvt, int& nPar, int& nMom, std::vector& momenta, std::vector& alphaS, std::vector& rndHel, std::vector& rndCol, std::vector& selHel, std::vector& selCol, unsigned int& chanId, bool& goodHel ){ + CppObjectInFortran *bridgeInst; + auto evalScatAmps = std::make_shared>( nEvt ); + fbridgecreate_( &bridgeInst, &nEvt, &nPar, &nMom ); + fbridgesequence_nomultichannel_( &bridgeInst, &momenta.at(0), &alphaS.at(0), &rndHel[0], &rndCol[0], &evalScatAmps->at(0), &selHel[0], &selCol[0], &goodHel ); + fbridgedelete_( &bridgeInst ); + return evalScatAmps; + } + + rwgt::fBridge bridgeConstr( std::vector& process, unsigned int warpSize = 32 ){ + rwgt::fBridge constrBridge = rwgt::fBridge( process, warpSize ); + rwgt::bridgeWrapper amplitude = amp; + constrBridge.setBridge( amplitude ); + return constrBridge; + } + + rwgt::fBridge bridgeConstr(){ + rwgt::fBridge constrBridge = rwgt::fBridge(); + rwgt::bridgeWrapper amplitude = amp; + constrBridge.setBridge( amplitude ); + return constrBridge; + } + + std::shared_ptr> procSort( int status, std::vector arguments, size_t index ){ + std::vector> initPrts = getInitPrts(); + std::vector> finPrts = getFinPrts(); + std::shared_ptr> refOrder; + if( index == REX::npos ){ + if( status == -1 ){ + for( auto& prts : initPrts ){ + refOrder = REX::getRefOrder( prts, arguments ); + if( std::find(refOrder->begin(), refOrder->end(), REX::npos) == refOrder->end() ){ break; } + } + return refOrder; + } + else if( status == 1 ){ + for( auto& prts : finPrts ){ + refOrder = REX::getRefOrder( prts, arguments ); + if( std::find(refOrder->begin(), refOrder->end(), REX::npos) == refOrder->end() ){ break; } + } + return refOrder; + } + return REX::indSort( arguments ); + } + else{ + if( index >= initPrts.size() || index >= finPrts.size() ) throw std::runtime_error( "procSort called for out-of-bounds event." ); + if( status == -1 ){ + refOrder = REX::getRefOrder( initPrts.at(index), arguments ); + return refOrder; + } + else if( status == 1 ){ + refOrder = REX::getRefOrder( finPrts.at(index), arguments ); + return refOrder; + } + return REX::indSort( arguments ); + } + } + + bool checkProc( REX::event& process, std::vector& relStats ){ + size_t no_evts = %(no_events)s; + auto finPrts = getFinPrts(); + for( size_t k = 0 ; k < no_evts ; ++k ){ + REX::statSort locSort = [ind = k](int status, std::vector arguments){ + return procSort( status, arguments, ind ); + }; + auto order = process.getProcOrder( locSort ); + if( order.at(1).size() != finPrts[k].size() ){ continue; } + for( size_t j = 0 ; j < relStats.size() ; ++j ){ + auto currPts = order.at( relStats[j] ); + if( std::find(currPts.begin(), currPts.end(), REX::npos) != currPts.end() ){ break; } + if( j == relStats.size() - 1 ){ return true; } + } + } + return false; + } + + std::function getComp(){ + std::function compar = [&]( REX::event& process ){ + std::vector relStats = {-1,1}; + return checkProc( process, relStats ); + }; + return compar; + } + + REX::eventSet eventSetConstr( std::vector& process ){ + REX::eventSet constrSet = REX::eventSet( process ); + REX::eventSetComp compar = checkProc; + constrSet.setComp( compar ); + return constrSet; + } + + REX::eventSet getEventSet(){ + std::vector>> eventVec = {%(process_events)s}; + std::vector process; + for( auto ev : eventVec ){ + process.push_back( REX::event( ev ) ); + } + return eventSetConstr( process ); + } + +} diff --git a/tools/REX/rwgt_runner.h b/tools/REX/rwgt_runner.h new file mode 100644 index 000000000..0228532bb --- /dev/null +++ b/tools/REX/rwgt_runner.h @@ -0,0 +1,35 @@ +//========================================================================== +// Copyright (C) 2023-2024 CERN +// Licensed under the GNU Lesser General Public License (version 3 or later). +// Written by: Z. Wettersten (June 2024) for the MG5aMC CUDACPP plugin. +//========================================================================== +//========================================================================== +// This file has been automatically generated for the CUDACPP plugin by +%(info_lines)s +//========================================================================== +//========================================================================== +// A class for reweighting matrix elements for +%(process_lines)s +//-------------------------------------------------------------------------- + +#ifndef _%(process_namespace)s_RUNNER_H_ +#define _%(process_namespace)s_RUNNER_H_ + +#include "rwgt_instance.h" + +namespace %(process_namespace)s { + + std::shared_ptr> amp( int& nEvt, int& nPar, int& nMom, std::vector& momenta, std::vector& alphaS, std::vector& rndHel, std::vector& rndCol, std::vector& selHel, std::vector& selCol, int& chanId, bool& goodHel ); + rwgt::fBridge bridgeConstr( std::vector& process, unsigned int warpSize ); + rwgt::fBridge bridgeConstr(); + std::shared_ptr> procSort( std::string_view status, std::vector arguments, size_t index = REX::npos ); + bool checkProc( REX::event& process, std::vector& relStats ); + std::function getComp(); + REX::eventSet eventSetConstruct( std::vector& process ); + REX::eventSet getEventSet(); + +} + + + +#endif \ No newline at end of file diff --git a/tools/REX/rwgt_runner.inc b/tools/REX/rwgt_runner.inc new file mode 120000 index 000000000..ff1267c3d --- /dev/null +++ b/tools/REX/rwgt_runner.inc @@ -0,0 +1 @@ +rwgt_runner.cc \ No newline at end of file diff --git a/tools/REX/teaREX.cc b/tools/REX/teaREX.cc new file mode 100644 index 000000000..da594bdc5 --- /dev/null +++ b/tools/REX/teaREX.cc @@ -0,0 +1,1252 @@ +/*** + * _ ______ + * | | | ___ \ + * | |_ ___ __ _| |_/ /_____ __ + * | __/ _ \/ _` | // _ \ \/ / + * | || __/ (_| | |\ \ __/> < + * \__\___|\__,_\_| \_\___/_/\_\ + * + ***/ +// +// *t*ensorial *e*vent *a*daption with *Rex* Version 0.9.0 +// teaRex is an extension to the Rex C++ library for parsing and manipulating Les Houches Event-format (LHE) files, +// designed for leading order event reweighting based on input LHE file(s) and (relative) weight evaluation functions. +// teaRex is in development and may not contain all features necessary for all desired features, +// and does not have documentation beyond the code itself. +// +// Copyright © 2023-2024 CERN, CERN Author Zenny Wettersten. +// Licensed under the GNU Lesser General Public License (version 3 or later). +// All rights not expressly granted are reserved. +// + +#ifndef _TEAREX_CC_ +#define _TEAREX_CC_ + +#include +#include +#include +#include +#include +#include +#include +#include "REX.h" +#include "teaREX.h" + +namespace REX::tea +{ + + template + std::shared_ptr> scatAmpEval(std::vector& momenta, std::function>(std::vector&)> evalFunc) + { return evalFunc(momenta); } + + template + std::shared_ptr> scatAmpEval(std::vector& momenta, std::function(std::vector&)> evalFunc) + { return evalFunc(momenta); } + + template + std::shared_ptr> scatAmpEval(std::vector& momenta, std::function>(std::vector&, std::vector&)> evalFunc) + { return evalFunc(momenta); } + + template + std::shared_ptr> scatAmpEval(std::vector& momenta, std::function(std::vector&, std::vector&)> evalFunc) + { return evalFunc(momenta); } + + void procRwgt::uniqueReset(){ + this->amplitude = std::vector(); + this->weights = std::vector>>(); + this->backlog = std::vector>>(); + this->iteration = 0; + } + + void procRwgt::reset(){ + this->proc = std::make_shared(); + this->proc->reset(); + this->uniqueReset(); + } + + procRwgt::procRwgt(){ this->reset(); return; } + + procRwgt::procRwgt( const REX::relEvArgs& relData ){ + this->proc = std::make_shared( relData ); + this->uniqueReset(); + } + + procRwgt::procRwgt( const procSoA& process ){ + this->proc = std::make_shared( process ); + this->uniqueReset(); + } + + procRwgt::procRwgt( procSoA* process ) : procRwgt( *process ){ return; } + + procRwgt::procRwgt( std::shared_ptr process ) + { + this->proc = process; + this->uniqueReset(); + return; + } + + procRwgt::procRwgt( const procRwgt& process ){ + this->proc = process.proc; + this->amplitude = process.amplitude; + this->weights = process.weights; + this->iteration = process.iteration; + return; + } + + procRwgt::procRwgt( procRwgt* process ) : procRwgt( *process ){ return; } + + procRwgt::procRwgt( std::shared_ptr process ) : procRwgt( *process ){ return; } + + procRwgt::procRwgt( std::vector> lheFile, REX::relEvArgs relArgs, + std::vector relevStats, std::function relFcn ) + { + this->proc = std::make_shared( lheFile, relArgs, relevStats, relFcn ); + this->uniqueReset(); + return; + } + + procRwgt::procRwgt( std::vector ampFcns ){ + this->uniqueReset(); + this->proc = std::make_shared(); + this->amplitude = ampFcns; + return; + } + + procRwgt& procRwgt::setAmplitude( weightor amp ){ + this->originalAmp = amp; + this->amplitude = std::vector({amp}); + return *this; + } + + procRwgt& procRwgt::setAmplitude( std::vector amp ){ + this->amplitude = amp; + return *this; + } + + procRwgt& procRwgt::setOriginalAmp( weightor amp ){ + this->originalAmp = amp; + return *this; + } + + bool procRwgt::initialise(){ + if( originalAmp == nullptr ) { + if ( this->amplitude.size() == 0 ) throw std::runtime_error("procRwgt::initialise() called with no amplitudes set."); + this->originalAmp = this->amplitude[0]; + REX::warning("procRwgt::initialise() called with no original amplitude set. Using first amplitude in list."); + } + if( this->proc->empty() ) return true; + this->invOriginalAmp = this->originalAmp( *(this->proc) ); + if( this->invOriginalAmp->size() == 0 ) return false; + if( this->invOriginalAmp->size() != this->proc->xWgtUp.size() ) throw std::runtime_error("procRwgt::initialise(): Number of event weights returned from procRwgt::originalAmp differs from number of weights in procRwgt::proc."); + std::transform( this->proc->xWgtUp.cbegin(), this->proc->xWgtUp.cend(), + this->invOriginalAmp->begin(), this->invOriginalAmp->begin(), + std::divides() ); + if( std::find_if( this->invOriginalAmp->begin(), this->invOriginalAmp->end(), + [](double val) { return (std::isnan(val) || std::isinf(val)); } ) != this->invOriginalAmp->end() ) + { + REX::warning("procRwgt::initialise(): Normalised initial weights contain NaN values."); + } + return true; + } + + bool procRwgt::normalise(){ + if( this->proc->empty() ) return true; + if( this->invOriginalAmp->size() == 0 ) + { + if( !this->initialise() ) return false; + } + if( this->weights.size() == 0 ) return false; + for( auto wgt : this->weights ) + { + if( wgt->size() != this->invOriginalAmp->size() ) throw std::runtime_error("procRwgt::normalise(): Number of event weights stored in procRwgt::weights differs from number of weights in procRwgt::proc."); + std::transform( wgt->cbegin(), wgt->cend(), + this->invOriginalAmp->cbegin(), wgt->begin(), + std::multiplies() ); + if( std::find_if( wgt->begin(), wgt->end(), + [](double val) { return std::isnan(val); } ) != wgt->end() ) + { + REX::warning("procRwgt::normalise(): Normalised weights contain NaN values."); + } + } + return true; + } + + bool procRwgt::evaluate(){ + if( this->proc->empty() ) return true; + if ( this->amplitude.size() == 0 ) throw std::runtime_error("procRwgt::evaluate() called with no amplitudes set."); + if ( this->iteration >= this->amplitude.size() ) this->iteration = 0; + auto newWgts = this->amplitude[this->iteration]( *(this->proc) ); + this->iteration++; + if( newWgts->size() != this->invOriginalAmp->size() ) return false; + this->backlog.push_back( newWgts ); + return true; + } + + void procRwgt::doBacklog( bool pass ){ + if( pass ){ + for( auto wgt : this->backlog ) + { + this->weights.push_back( wgt ); + } + // this->weights.push_back( this->backlog ); + } + this->backlog = std::vector>>(); + } + + void reweightor::uniqueReset(){ + this->amps = std::vector>(); + this->iterators = std::vector(); + this->success = std::vector(); + } + + void reweightor::reset(){ + this->lheSoA::reset(); + this->uniqueReset(); + } + + void reweightor::setAmpsFromSubprocs(){ + this->amps = std::vector>(); + for( auto subProc : this->subProcesses ) + { + this->amps.push_back( std::make_shared( subProc ) ); + } + } + + reweightor::reweightor() : lheSoA(){} + + reweightor::reweightor( const lheSoA& lheFile ) : lheSoA( lheFile ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( const reweightor& lheFile ) : lheSoA( lheFile ){ + this->amps = lheFile.amps; + } + + reweightor::reweightor( std::vector> lheFile ) : lheSoA( lheFile ){} + + reweightor::reweightor( std::vector> lheFile, std::vector> evSort ) : lheSoA( lheFile, evSort ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( lheNode& lheFile, std::vector> evSort ) : lheSoA( lheFile, evSort ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( std::vector> lheFile, std::vector> evSort, std::vector relData ) : lheSoA( lheFile, evSort, relData ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( lheNode& lheFile, std::vector> evSort, std::vector relData ) : lheSoA( lheFile, evSort, relData ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( std::vector> lheFile, std::vector> evSort, std::vector> relStats ) : lheSoA( lheFile, evSort, relStats ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( lheNode& lheFile, std::vector> evSort, std::vector> relStats ) : lheSoA( lheFile, evSort, relStats ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( std::vector> lheFile, std::vector> evSort, std::vector relData, std::vector> relStats ) : lheSoA( lheFile, evSort, relData, relStats ){ + this->setAmpsFromSubprocs(); + } + + reweightor::reweightor( lheNode& lheFile, std::vector> evSort, std::vector relData, std::vector> relStats ) : lheSoA( lheFile, evSort, relData, relStats ){ + this->setAmpsFromSubprocs(); + } + + reweightor& reweightor::setAmps( std::vector> newAmps ){ + this->amps = newAmps; + if( this->amps.size() != this->subProcesses.size() ){ + if( this->subProcesses.size() != 0 ) REX::warning("reweightor::setAmps(): Number of amplitudes does not match number of subprocesses.\nOverriding existing subprocesses with those in amplitudes."); + this->subProcesses = std::vector>(); + for( auto amp : this->amps ) + { + if( amp->proc == nullptr ) throw std::runtime_error("reweightor::setAmps(): Amplitude does not have a process set."); + this->subProcesses.push_back( amp->proc ); + } + } + for( size_t k = 0 ; k < this->amps.size() ; ++k ) + { + if( this->amps[k]->proc == nullptr ){ + REX::warning("reweightor::setAmps(): Amplitude does not have a process set. Setting to subprocess with same index."); + this->amps[k]->proc = this->subProcesses[k]; + } + } + return *this; + } + + reweightor& reweightor::setAmps( std::vector ampFcns ){ + this->amps = std::vector>(); + if( ampFcns.size() != this->subProcesses.size() ) throw std::runtime_error("reweightor::setAmps(): Number of amplitudes does not match number of subprocesses."); + for( size_t k = 0 ; k < ampFcns.size() ; ++k ) + { + this->amps.push_back( std::make_shared( this->subProcesses[k] ) ); + this->amps[k]->setAmplitude( ampFcns[k] ); + } + return *this; + } + + reweightor& reweightor::setAmps( std::vector> ampFcns ){ + this->amps = std::vector>(); + if( ampFcns.size() != this->subProcesses.size() ) throw std::runtime_error("reweightor::setAmps(): Number of amplitudes does not match number of subprocesses."); + for( size_t k = 0 ; k < ampFcns.size() ; ++k ) + { + this->amps.push_back( std::make_shared( this->subProcesses[k] ) ); + this->amps[k]->setAmplitude( ampFcns[k] ); + } + return *this; + } + + reweightor& reweightor::setIterators( std::vector iters ){ + this->iterators = iters; + return *this; + } + + reweightor& reweightor::setOriginator( iterator iter ){ + this->originator = iter; + return *this; + } + + reweightor& reweightor::setTerminator( iterator iter ){ + this->terminator = iter; + return *this; + } + + bool reweightor::runAmps(){ + if( this->amps.size() == 0 ) throw std::runtime_error("reweightor::runAmps(): No amplitudes set."); + bool success = true; + for( auto amp : this->amps ) + { + success = success && amp->evaluate(); + } + return success; + } + + void reweightor::runBacklog( bool success ){ + for( auto amp : this->amps ) + { + amp->doBacklog( success ); + } + } + + bool reweightor::doIterate( size_t index ){ + if( index >= this->iterators.size() ) throw std::runtime_error("reweightor::doIterate(): Index out of reweightor::iterators range."); + if( this->iterators[index] == nullptr ) return false; + if( !this->iterators[index]() ) return false; + bool archSuccess = true; + for( size_t k = 0 ; k < this->ampsPerIter ; ++k ) + { + // bool succ = this->runAmps(); + // this->runBacklog( succ ); + archSuccess = archSuccess && this->runAmps(); + } + std::cout << "."; + this->runBacklog( archSuccess ); + return archSuccess; + } + + void reweightor::doAllIterations(){ + for( size_t k = 0 ; k < this->iterators.size() ; ++k ) + { + if( this->doIterate(k) ){ + this->success.push_back( true ); + continue; + } + this->success.push_back( false ); + REX::warning("reweightor::doAllIterations(): Iteration " + std::to_string(k) + " failed. Discarding results from this iteration."); + } + } + + void reweightor::doInit(){ + if( this->amps.size() == 0 ) throw std::runtime_error("reweightor::doInit(): No amplitudes set."); + if( this->originator != nullptr ){ + if( !(this->originator()) ) throw std::runtime_error("reweightor::doInit(): Originator failed at initialisation."); + } + for( auto amp : this->amps ) + { + if ( !amp->initialise() ) throw std::runtime_error("reweightor::doInit(): Initialisation failed for amplitude."); + } + } + + void reweightor::doFin(){ + if( this->terminator == nullptr ){ + if( this->originator != nullptr ){ + this->terminator = this->originator; + REX::warning("reweightor::doFin(): No terminator set. Assuming originator as terminator."); + } else return; + } + if( this->terminator() ) return; + REX::warning("reweightor::doFin(): Terminator failed at finalisation."); + } + + void reweightor::flattenWeights(){ + if ( this->amps.size() == 0 ) throw std::runtime_error("reweightor::flattenWeights(): No amplitudes set."); + size_t noWgts = this->amps[0]->weights.size(); + for( auto amp : this->amps ) + { + if( amp->weights.size() != noWgts ) throw std::runtime_error("reweightor::flattenWeights(): Number of weights in amplitudes differ."); + amp->normalise(); + } + std::vector evInd = std::vector( this->amps.size(), 0 ); + this->wgts = std::vector>>(); + for( size_t k = 0 ; k < noWgts ; ++k ){ + this->wgts.push_back( std::make_shared>() ); + } + for( auto ind : this->eventGrouping ){ + if( ind == REX::npos ){ + for( auto wgt : this->wgts ){ + wgt->push_back( 0.0 ); + } + continue; + } + for( size_t k = 0 ; k < noWgts ; ++k ){ + this->wgts[k]->push_back( this->amps[ind]->weights[k]->at( evInd[ind])); + } + ++evInd[ind]; + } + } + + void reweightor::calcAmpNorm(){ + if( this->amps.size() == 0 ) throw std::runtime_error("reweightor::calcAmpNorm(): No amplitudes set."); + for( auto amp : amps ){ + if( amp->proc == nullptr ) throw std::runtime_error("reweightor::calcAmpNorm(): Amplitude does not have a process set."); + if( amp->proc->xWgtUp.size() == 0 && amp->proc->events.size() != 0 ) + throw std::runtime_error("reweightor::calcAmpNorm(): Amplitude process does not have any initial weights."); + } + this->ampNorm = 0.0; + for( auto proc : this->procLines ){ + this->ampNorm += proc.xSecUp; + } + if( this->ampNorm == 0.0 ) throw std::runtime_error("reweightor::calcAmpNorm(): Total cross section is zero."); + int noEvs = this->events.size(); + if( noEvs == 0 ){ + for( auto amp : this->amps ){ + noEvs += amp->proc->xWgtUp.size(); + } + } + if( noEvs == 0 ) throw std::runtime_error("reweightor::calcAmpNorm(): No original weights in reweightor."); + if( std::abs(this->idWtUp) == 3 ){ + this->ampNorm *= 1./ double(noEvs); + return; + } else if( std::abs(this->idWtUp) == 4 ){ + this->ampNorm = 1. / double(noEvs); + return; + } else { + if( std::abs(this->idWtUp) > 2 ){ + REX::warning("reweightor::calcAmpNorm(): Unknown weight ID " + std::to_string(this->idWtUp) + ". Assuming weighted events."); + } + double accumWgts = 0.0; + for( auto amp : this->amps ){ + accumWgts = std::accumulate( amp->proc->xWgtUp.cbegin(), amp->proc->xWgtUp.cend(), accumWgts ); + } + if( accumWgts == 0.0 ) throw std::runtime_error("reweightor::calcAmpNorm(): Total cross section is zero."); + this->ampNorm *= 1. / accumWgts; + } + } + + bool reweightor::setAmpNorm( bool hard ){ + if( hard || this->ampNorm == 0.0 ) this->calcAmpNorm(); + return true; + } + + void reweightor::calcXSecs(){ + if( !this->setAmpNorm() ) throw std::runtime_error("reweightor::calcXSecs(): Could not set amplitude normalisation."); + if( this->wgts.size() == 0 ) this->flattenWeights(); + this->xSecs = std::vector( this->wgts.size(), 0.0 ); + for( size_t k = 0 ; k < this->wgts.size() ; ++k ){ + this->xSecs[k] = std::accumulate( this->wgts[k]->cbegin(), this->wgts[k]->cend(), 0.0 ); + this->xSecs[k] *= this->ampNorm; + } + } + + void reweightor::calcXErrs(){ + if( this->xSecs.size() == 0 ) this->calcXSecs(); + double xSec = 0.0; + double xErr = 0.0; + for( auto proc : this->procLines ){ + xSec += proc.xSecUp; + xErr += std::pow( proc.xErrUp, 2 ); + } + xErr = std::sqrt(xErr); + int noEvs = this->events.size(); + if( noEvs == 0 ){ + for( auto amp : this->amps ){ + noEvs += amp->proc->xWgtUp.size(); + } + } + if( noEvs == 0 ) throw std::runtime_error("reweightor::calcXErrs(): No original weights in reweightor."); + double invNoEvs = 1. / double(noEvs); + double sqrtInvNoEvs = std::sqrt(invNoEvs); + this->xErrs = std::vector(); + for( size_t k = 0 ; k < this->xSecs.size() ; ++k ){ + double omega = 0.0; + double omegaSq = 0.0; + for( auto wgt : *(this->wgts[k]) ){ + double invWgt = 1. / wgt; + omega += invWgt; + omegaSq += std::pow( invWgt, 2 ); + } + double var = (omegaSq - std::pow(omega, 2) * invNoEvs) * invNoEvs * std::pow( this->xSecs[k], 2 ); + double err = std::sqrt( std::max( 0., sqrtInvNoEvs * var) )*xSec; + err += xErr * this->xSecs[k] * omega * invNoEvs; + if( std::isnan(err) || std::isinf(err) ){ + REX::warning("reweightor::calcXErrs(): Error propagation failed for weight " + std::to_string(k) + ". Approximating the error at cross section level."); + err = xErr * std::max( xSec / this->xSecs[k], this->xSecs[k] / xSec ); + } + this->xErrs.push_back(err); + } + } + + void reweightor::run(){ + this->doInit(); + this->setAmpNorm(); + this->doAllIterations(); + this->doFin(); + this->flattenWeights(); + this->calcXSecs(); + this->calcXErrs(); + } + + void reweightor::appendWgtsSimple( lheNode& lheFile, std::vector procs, std::shared_ptr> names ){ + if( lheFile.getEvents().size() == 0 ) throw std::runtime_error("reweightor::appendWgts(): No events in lheNode."); + if( this->wgts.size() == 0 ) throw std::runtime_error("reweightor::appendWgts(): No weights in reweightor."); + if( this->wgts[0]->size() != lheFile.getEvents().size() ) throw std::runtime_error("reweightor::appendWgts(): Number of weights in reweightor does not match number of events in lheNode."); + if( procs.size() == 0 ) throw std::runtime_error("reweightor::appendWgts(): No processes specified."); + if( procs.size() != this->wgts.size() ) throw std::runtime_error("reweightor::appendWgts(): Number of processes does not match number of weight sets in reweightor."); + if( names == nullptr ) names = std::make_shared>(); + if( names->size() == 0 ) names->resize( procs.size(), "" ); + auto rwgtGroup = std::make_shared(); + auto currInd = lheFile.getHeader()->addWgtGroup( rwgtGroup ); + for( size_t k = 0 ; k < procs.size() ; ++k ){ + REX::newWgt newWeight( procs[k], this->wgts[k], names->at(k) ); + lheFile.addWgt( currInd, newWeight ); + } + } + + void reweightor::appendWgts( lheNode& lheFile, std::vector procs, std::shared_ptr> names ){ + if( lheFile.getEvents().size() == 0 ) throw std::runtime_error("reweightor::appendWgts(): No events in lheNode."); + if( this->wgts.size() == 0 ) throw std::runtime_error("reweightor::appendWgts(): No weights in reweightor."); + if( this->wgts[0]->size() != lheFile.getEvents().size() ) throw std::runtime_error("reweightor::appendWgts(): Number of weights in reweightor does not match number of events in lheNode."); + if( procs.size() == 0 ) throw std::runtime_error("reweightor::appendWgts(): No processes specified."); + if( procs.size() != this->wgts.size() && procs.size() != this->success.size() * this->ampsPerIter ) throw std::runtime_error("reweightor::appendWgts(): Number of processes does not match number of weight sets in reweightor."); + if( names == nullptr ) names = std::make_shared>(); + if( names->size() == 0 ) names->resize( procs.size(), "" ); + if( procs.size() == this->wgts.size() ) this->appendWgtsSimple( lheFile, procs, names ); + else { + auto rwgtGroup = std::make_shared(); + auto currInd = lheFile.getHeader()->addWgtGroup( rwgtGroup ); + for( size_t k = 0 ; k < procs.size() ; ++k ){ + if( this->success[k] ){ + REX::newWgt newWeight( procs[k], this->wgts[k], names->at(k) ); + lheFile.addWgt( currInd, newWeight ); + } + } + } + } + + rwgtVal::rwgtVal() : paramVal(){ return; } + rwgtVal::rwgtVal( std::string_view paramLine ) + : paramVal( paramLine, false ){if( paramLine.size() == 0 ){ return; } + realLine = paramLine; + auto vals = *REX::blankSplitter( realLine ); + auto name = std::string(vals[1]); + std::transform( name.begin(), name.end(), name.begin(), ::tolower ); + this->blockName = name; + idStr = vals[2]; + valStr = vals[3]; + } + std::string_view rwgtVal::getLine(){ return realLine; } + bool rwgtVal::isAll(){ return (idStr == "all"); } + void rwgtVal::outWrite( REX::paramBlock& srcBlock ){ + if ( isAll() ) + { + for( auto param : srcBlock.params ) + { + param.valStr = valStr; + param.modded = true; + } + return; + } + auto currPar = std::find_if( srcBlock.params.begin(), srcBlock.params.end(), + [&]( const REX::paramVal& parPar ){ return (parPar.idStr == idStr ); } ); + if( currPar == srcBlock.params.end() ){ + srcBlock.params.push_back( REX::paramVal( realLine.substr(realLine.find("set") + 4) ) ); + srcBlock.params[ srcBlock.params.size() - 1 ].modded = true; + srcBlock.modded = true; + return; + } + currPar->valStr = valStr; + currPar->modded = true; + srcBlock.modded = true; + return; + } + + rwgtBlock::rwgtBlock( std::vector values, std::string_view title) + { + name = title; + rwgtVals.resize( values.size() ); + for( size_t k = 0 ; k < values.size() ; ++k ) + { + rwgtVals[k] = rwgtVal( values[k] ); + } + } + rwgtBlock::rwgtBlock( const std::vector& vals, std::string_view title ) + { + name = title; + rwgtVals = vals; + } + std::string_view rwgtBlock::getBlock(){ + if( written ){ return runBlock; } + runBlock = ""; + for( auto val : rwgtVals ){ + runBlock += std::string(val.getLine()) + "\n"; + } + written = true; + return runBlock; + } +// void rwgtBlock::outWrite( REX::paramBlock& srcBlock, const std::map& blocks ) + void rwgtBlock::outWrite( REX::paramBlock& srcBlock ) //, const std::map& blocks ) + { + for( auto parm : rwgtVals ) + { + parm.outWrite( srcBlock ); + } + srcBlock.modded = true; + return; + } + + void rwgtProc::parse(){ + std::vector blocks; + std::vector>> params; + auto procLines = REX::lineSplitter( procString ); + for( auto line : *procLines ) + { + if( line.find_first_not_of(" \n\r\f\t\v") == '#' ){ continue; } + auto strtPt = line.find("set"); + if( strtPt == REX::npos ){ continue; } + auto words = REX::blankSplitter( line.substr(strtPt) ); + auto currBlock = words->at(1); + auto loc = std::find_if( blocks.begin(), blocks.end(), + [&]( std::string_view block ){ return (block == currBlock); } ); + if( loc == blocks.end() ){ + blocks.push_back( currBlock ); + params.push_back( std::make_shared>( std::vector({rwgtVal( line )} ) )); } + else { + params[ std::distance( blocks.begin(), loc ) ]->push_back( rwgtVal( line ) ); + } + } + rwgtParams.reserve(blocks.size()); + for( size_t k = 0 ; k < blocks.size() ; ++k ) + { + rwgtParams.push_back( rwgtBlock( *params[k], blocks[k] ) ); + } + } + rwgtProc::rwgtProc( REX::lesHouchesCard slhaSet, std::string_view rwgtSet, bool parseOnline ) + { + if( rwgtSet == "" ){ return; } + auto strtLi = rwgtSet.find_first_not_of( " \n\r\f\n\v" ); + if( strtLi == REX::npos ){ return; } + auto launchPos = rwgtSet.find("launch", strtLi + 1); + auto commLinePos = rwgtSet.find("#*", strtLi + 1); + procString = rwgtSet.substr( strtLi, std::min(launchPos, commLinePos) - strtLi ); + if( parseOnline ){ parse(); } + } + std::shared_ptr rwgtProc::outWrite( const REX::lesHouchesCard& paramOrig ){ + auto slhaOrig = std::make_shared( paramOrig ); + std::vector> blockIds; + for( size_t k = 0 ; k < slhaOrig->blocks.size() ; ++k ) + { slhaOrig->blocks[k].parse( true ); + auto currBlock = std::pair( slhaOrig->blocks[k].name, k); + std::transform( currBlock.first.begin(), currBlock.first.end(), currBlock.first.begin(), ::tolower ); + blockIds.push_back( currBlock ); } + for( auto rwgts : rwgtParams ) + { + for( auto block : blockIds ){ + if( block.first == rwgts.name ){ rwgts.outWrite( slhaOrig->blocks[ block.second ] ); break; } + } + //rwgts.outWrite( slhaOrig->blocks[ blockIds.at( rwgts.name ) ], blockIds ); + } + slhaOrig->modded = true; + return slhaOrig; + } + std::string_view rwgtProc::comRunProc(){ return procString; } + + void rwgtCard::parse( bool parseOnline ){ + auto allLaunchPos = REX::findEach( this->srcCard, "launch" ); + std::vector lnchPos; + lnchPos.reserve( allLaunchPos->size() ); + for( auto pos : *allLaunchPos ) + { + if( pos == 0 ){ lnchPos.push_back(pos); continue; } + if( srcCard.find_last_of("#", pos) < srcCard.find_last_of("\n", pos) ){ lnchPos.push_back(pos); } + } + lnchPos.push_back( REX::npos ); + auto preamble = REX::lineSplitter( srcCard.substr( 0, lnchPos[0] - 1 ) ); + for( auto line : *preamble ) + { + if( line[line.find_first_not_of(" \n\r\f\t\v")] == '#' ){ continue; } + opts.push_back( line ); + } + rwgtNames = std::make_shared>(); + rwgtNames->reserve( lnchPos.size() - 1 ); + for( size_t k = 0 ; k < lnchPos.size() - 1 ; ++k ){ + auto setPos = srcCard.find( "set", lnchPos[k] ); + if( setPos == REX::npos ){ continue; } + rwgtRuns.push_back( rwgtProc( slhaCard, srcCard.substr( setPos, lnchPos[k+1] - setPos ), parseOnline ) ); + auto possNamePos = srcCard.find_first_of( "-\n#", lnchPos[k] ); + if( srcCard[possNamePos] == '-' ){ + auto endLine = srcCard.find( "\n", possNamePos ); + auto locOpts = srcCard.substr( possNamePos, endLine - possNamePos ); + rwgtRuns[ rwgtRuns.size() - 1 ].rwgtOpts.push_back( locOpts ); + auto namePos = locOpts.find( "rwgt_name" ); + if( namePos != REX::npos ){ + auto endName = locOpts.find_first_of( " \n\r\f\t\v", namePos ); + rwgtNames->push_back( std::string( locOpts.substr( namePos + 10, endName - namePos - 10 ) ) ); + } else { + rwgtNames->push_back( "rwgt_" + std::to_string( k + 1 ) ); + } + } else { + rwgtNames->push_back( "rwgt_" + std::to_string( k + 1 ) ); + } + rwgtRuns[ rwgtRuns.size() - 1 ].rwgtName = rwgtNames->at( rwgtNames->size() - 1 ); + } + rwgtProcs = std::vector(); rwgtProcs.reserve( rwgtRuns.size() ); + for( auto run : rwgtRuns ){ + rwgtProcs.push_back( run.comRunProc() ); + } + } + rwgtCard::rwgtCard( std::string_view reweight_card ){ + srcCard = reweight_card; + } + rwgtCard::rwgtCard( std::string_view reweight_card, REX::lesHouchesCard slhaParams, bool parseOnline ){ + srcCard = reweight_card; + slhaCard = slhaParams; + if( parseOnline ){ parse( parseOnline ); } + } + std::vector> rwgtCard::writeCards( REX::lesHouchesCard& slhaOrig ){ + if( this->writtenCards.size() != 0 ){ return this->writtenCards; } + std::vector> cardVec; + slhaOrig.parse(); + cardVec.reserve( rwgtRuns.size() ); + for( auto rwgt : rwgtRuns ) + { + cardVec.push_back( rwgt.outWrite( slhaOrig ) ); + } + this->writtenCards = cardVec; + return cardVec; + } + + std::shared_ptr> rwgtCard::getNames(){ + if( rwgtNames == nullptr ) this->parse( true ); + return rwgtNames; + } + + std::vector rwgtCard::getProcs(){ + if( rwgtProcs.size() == 0 ) this->parse( true ); + return rwgtProcs; + } + + std::vector rwgtCard::getIterators( std::string path ){ + if( this->cardWriters.size() != 0 ){ return this->cardWriters; } + std::vector iters; + auto cards = this->writeCards( this->slhaCard ); + iters.reserve( cards.size() ); + for( auto card : cards ) + { + iterator iter = [card, path](){ + if( !REX::filePusher( path, *(card->selfWrite()) )){ return false; } + return true; + }; + iters.push_back( iter ); + } + this->cardWriters = iters; + return iters; + } + + + void rwgtCollection::setRwgt( std::shared_ptr rwgts ){ + if( rwgtSet ){ return; } + rwgtSets = rwgts; + rwgtSet = true; + } + void rwgtCollection::setRwgt( rwgtCard rwgts ){ + if( rwgtSet ){ return; } + setRwgt( std::make_shared( rwgts ) ); rwgtSet = true; + } + void rwgtCollection::setSlha( std::shared_ptr slha ){ + if( slhaSet ){ return; } + slhaParameters = slha; + slhaParameters->parse(); + slhaSet = true; + } + void rwgtCollection::setSlha( REX::lesHouchesCard slha ){ + if( slhaSet ){ return; } + setSlha( std::make_shared( slha ) ); + slhaSet = true; + } + void rwgtCollection::setLhe( std::shared_ptr lhe ){ + if( lheFileSet ){ return; } + lheFile = lhe; + lheFileSet = true; + } + void rwgtCollection::setLhe( REX::lheNode& lhe ){ + if( lheFileSet ){ return; } + setLhe( std::make_shared( lhe ) ); + lheFileSet = true; + } + void rwgtCollection::setLhe( std::string_view lhe_file ){ + if( lheFileSet ){ return; } + lheFile = std::make_shared( REX::lheNode(lhe_file) ); + lheFileSet = true; + } + std::shared_ptr rwgtCollection::getRwgt(){ return rwgtSets; } + std::shared_ptr rwgtCollection::getSlha(){ return slhaParameters; } + std::shared_ptr rwgtCollection::getLhe(){ return lheFile; } + rwgtCollection::rwgtCollection(){ return; } + rwgtCollection::rwgtCollection( std::shared_ptr lhe, std::shared_ptr slha, std::shared_ptr rwgts ){ + setLhe( lhe ); + setSlha( slha ); + setRwgt( rwgts ); + } + rwgtCollection::rwgtCollection( const rwgtCollection& rwgts ){ + rwgtSets = rwgts.rwgtSets; + slhaParameters = rwgts.slhaParameters; + lheFile = rwgts.lheFile; + wgts = rwgts.wgts; + gS = rwgts.gS; + momenta = rwgts.momenta; + lheFileSet = rwgts.lheFileSet; + slhaSet = rwgts.slhaSet; + rwgtSet = rwgts.rwgtSet; + skeleton = rwgts.skeleton; + eventFile = rwgts.eventFile; + flatWgts = rwgts.flatWgts; + } + REX::transSkel& rwgtCollection::getSkeleton(){ + if( !this->skeleton ) + throw std::runtime_error( "Skeleton has not been set." ); + return this->lheSkeleton; + } + REX::transSkel& rwgtCollection::getSkeleton( std::vector& evSets ){ + if( this->skeleton ){ return this->lheSkeleton; } + setSkeleton( evSets ); + return this->lheSkeleton; + } + template + void rwgtCollection::setDoubles(Args&&... args){ + if( lheFile == nullptr || rwgtSets == nullptr || slhaParameters == nullptr ) + throw std::runtime_error( "One or more of the necessary files (SLHA parameter card, LHE event storage file, and MadGraph-format reweight card) have not been initialised." ); + if( this->doublesSet ){ return; } + if( this->skeleton ){ + this->setDoublesFromSkeleton(); + return; + } + REX::lheRetDs returnBools; returnBools.xwgtup = true; returnBools.aqcdup = true; returnBools.pup = true; + eventFile = REX::transLHE( *lheFile, args... ); + auto vecOfVecs = REX::lheValDoubles( eventFile, returnBools ); + if( vecOfVecs->size() != 3 * eventFile.subProcs.size() ) + throw std::runtime_error( "Incorrect number of parameters have been extracted from the LHE file." ); + for( size_t k = 0 ; k < eventFile.subProcs.size() ; ++k ) + { + wgts.push_back( vecOfVecs->at( 3*k ) ); + gS.push_back( vecOfVecs->at( 3*k + 1 ) ); + momenta.push_back( vecOfVecs->at( 3*k + 2 ) ); + } + flatWgts = eventFile.vectorFlat( wgts ); + this->doublesSet = true; + } + void rwgtCollection::setSkeleton( std::vector& evSets ){ + if( lheFile == nullptr || rwgtSets == nullptr || slhaParameters == nullptr ) + throw std::runtime_error( "One or more of the necessary files (SLHA parameter card, LHE event storage file, and MadGraph-format reweight card) have not been initialised." ); + this->lheSkeleton = REX::transSkel( this->lheFile, evSets ); + this->skeleton = true; + } + void rwgtCollection::setDoublesFromSkeleton(){ + if( !this->skeleton ) + throw std::runtime_error( "Skeleton has not been set." ); + if( this->doublesSet ){ return; } + REX::lheRetDs returnBools; returnBools.xwgtup = true; returnBools.aqcdup = true; returnBools.pup = true; + this->eventFile = REX::transLHE( this->lheSkeleton ); + auto vecOfVecs = REX::lheValDoubles( eventFile, returnBools ); + if( vecOfVecs->size() != 3 * eventFile.subProcs.size() ) + throw std::runtime_error( "Incorrect number of parameters have been extracted from the LHE file." ); + for( size_t k = 0 ; k < eventFile.subProcs.size() ; ++k ) + { + wgts.push_back( vecOfVecs->at( 3*k ) ); + gS.push_back( vecOfVecs->at( 3*k + 1 ) ); + momenta.push_back( vecOfVecs->at( 3*k + 2 ) ); + } + flatWgts = eventFile.vectorFlat( wgts ); + this->doublesSet = true; + } + std::shared_ptr> rwgtCollection::getNames(){ return rwgtSets->rwgtNames; } + + bool rwgtFiles::rwgtPulled(){ return (rewgtCard != nullptr); } + bool rwgtFiles::slhaPulled(){ return (slhaCard != nullptr); } + bool rwgtFiles::lhePulled(){ return (lheCard != nullptr); } + void rwgtFiles::setRwgtPath( std::string_view path ){ rwgtPath = path; } + void rwgtFiles::setSlhaPath( std::string_view path ){ slhaPath = path; } + void rwgtFiles::setLhePath( std::string_view path ){ lhePath = path; } + rwgtFiles::rwgtFiles() : rwgtCollection(){ return; } + rwgtFiles::rwgtFiles( std::string_view lhe_card, std::string_view slha_card, std::string_view reweight_card ) : rwgtCollection(){ + setRwgtPath( reweight_card ); + setSlhaPath( slha_card ); + setLhePath( lhe_card ); + } + rwgtFiles::rwgtFiles( const rwgtFiles& rwgts ) : rwgtCollection( rwgts ){ + rwgtPath = rwgts.rwgtPath; + slhaPath = rwgts.slhaPath; + lhePath = rwgts.lhePath; + rewgtCard = rwgts.rewgtCard; + slhaCard = rwgts.slhaCard; + lheCard = rwgts.lheCard; + initialised = rwgts.initialised; + } + REX::transSkel& rwgtFiles::initCards( std::vector& evSets ){ + if( initialised ){ return getSkeleton( evSets ); } + if( rwgtPath == "" || slhaPath == "" || lhePath == "" ) + throw std::runtime_error( "Paths to reweight card, parameter card, or LHE file have not been set" ); + this->pullRwgt(); this->pullSlha(); this->pullLhe(); + this->setLhe( *lheCard ); + this->setSlha( std::make_shared( *slhaCard ) ); + this->setRwgt( std::make_shared( *rewgtCard, *slhaParameters, true ) ); + this->initialised = true; + return this->getSkeleton( evSets ); + } + template + void rwgtFiles::initCards(Args&&... args){ + if( initialised ){ return; } + if( rwgtPath == "" || slhaPath == "" || lhePath == "" ) + throw std::runtime_error( "Paths to reweight card, parameter card, or LHE file have not been set" ); + pullRwgt(); pullSlha(); pullLhe(); + setLhe( *lheCard ); + setSlha( std::make_shared( *slhaCard ) ); + setRwgt( std::make_shared( *rewgtCard, *slhaParameters, true ) ); + setDoubles(args...); + initialised = true; + } + template + void rwgtFiles::initCards( std::string_view lhe_card, std::string_view slha_card, std::string_view reweight_card, Args&&... args ){ + setLhePath( lhe_card ); + setSlhaPath( slha_card ); + setRwgtPath( reweight_card ); + initCards(args...); + initialised = true; + } + void rwgtFiles::initDoubles(){ + if( !this->skeleton ) + throw std::runtime_error( "Skeleton has not been set." ); + this->setDoublesFromSkeleton(); + } + void rwgtFiles::pullRwgt(){ + if( this->rwgtPulled() ){ return; } + rewgtCard = REX::filePuller( rwgtPath ); + } + void rwgtFiles::pullSlha(){ + if( this->slhaPulled() ){ return; } + slhaCard = REX::filePuller( slhaPath ); + } + void rwgtFiles::pullLhe(){ + if( this->lhePulled() ){ return; } + lheCard = REX::filePuller( lhePath ); + } + + void rwgtRunner::setMeEval( amplitude eval ){ + meEval = eval; meInit = true; + } + void rwgtRunner::addMeEval( const REX::event& ev, const amplitude& eval ){}// meEvals.insert( std::pair( ev, eval ) ); meCompInit = true; } + rwgtRunner::rwgtRunner() : rwgtFiles(){ return; } + rwgtRunner::rwgtRunner( rwgtFiles& rwgts ) : rwgtFiles( rwgts ){ return; } + rwgtRunner::rwgtRunner( rwgtFiles& rwgts, amplitude meCalc ) : rwgtFiles( rwgts ){ + meEval = meCalc; + meInit = true; + } + rwgtRunner::rwgtRunner( rwgtFiles& rwgts, std::vector& meCalcs ) : rwgtFiles( rwgts ){ + meVec = meCalcs; + meCompInit = true; + } + rwgtRunner::rwgtRunner( std::string_view lhe_card, std::string_view slha_card, std::string_view reweight_card, + amplitude meCalc ) : rwgtFiles( lhe_card, slha_card, reweight_card ){ + meEval = meCalc; + meInit = true; + } + rwgtRunner::rwgtRunner( const rwgtRunner& rwgts ) : rwgtFiles( rwgts ){ + this->meInit = rwgts.meInit; + this->meCompInit = rwgts.meCompInit; + this->meSet = rwgts.meSet; + this->normWgtSet = rwgts.normWgtSet; + this->meEval = rwgts.meEval; + this->meVec = rwgts.meVec; + this->initMEs = rwgts.initMEs; + this->meNormWgts = rwgts.meNormWgts; + this->normWgt = rwgts.normWgt; + this->rwgtGroup = rwgts.rwgtGroup; + this->normXSecs = rwgts.normXSecs; + this->errXSecs = rwgts.errXSecs; + this->ampNorm = rwgts.ampNorm; + this->reWgts = rwgts.reWgts; + } + bool rwgtRunner::oneME(){ return (meInit != meCompInit); } + bool rwgtRunner::singAmp(){ return (meInit && !meCompInit); } + template + void rwgtRunner::setMEs(Args&&... args){ + initCards(args...); + normXSecs = std::make_shared>( ); + errXSecs = std::make_shared>( ); + if( !oneME() ) + throw std::runtime_error( "No or multiple function(s) for evaluating scattering amplitudes has been provided." ); + //ZW FIX THIS + initMEs = {}; + if( meVec.size() != 0 ){ + for( size_t k = 0 ; k < eventFile.subProcs.size() ; ++k ) + { + auto ins = meVec[k]( *(momenta[k]), *(gS[k]) ); + initMEs.push_back( std::make_shared>( ins->begin(), ins->begin() + wgts[k]->size() ) ); + } + } + else{ + // DO NOT ALLOW FOR SINGLE ME WITHOUT PASSING EVERYTHING THROUGH VECTOR + } + meSet = true; + } + bool rwgtRunner::setParamCard( std::shared_ptr slhaParams ){ + if( slhaPath == "" ) + throw std::runtime_error( "No parameter card path has been provided." ); + if( slhaParameters == nullptr ) + throw std::runtime_error( "No SLHA parameter card has been provided." ); + if( !REX::filePusher( slhaPath, *slhaParams->selfWrite() ) ) + throw std::runtime_error( "Failed to overwrite parameter card." ); + return true; + } + void rwgtRunner::setNormWgtsSingleME(){ + meNormWgts = {std::make_shared>( wgts[0]->size() )}; + for( size_t k = 0; k < initMEs[0]->size(); k++ ){ + meNormWgts[0]->at( k ) = wgts[0]->at( k ) / initMEs[0]->at( k ); + } + normWgt = meNormWgts[0]; + } + void rwgtRunner::setNormWgtsMultiME(){ + meNormWgts = std::vector>>( initMEs.size() ); + for( size_t k = 0 ; k < wgts.size() ; ++k ){ + meNormWgts[k] = std::make_shared>( wgts[k]->size() ); + for( size_t i = 0 ; i < wgts[k]->size() ; ++i ){ + meNormWgts[k]->at( i ) = wgts[k]->at( i ) / initMEs[k]->at( i ); + } + } + normWgt = eventFile.vectorFlat( meNormWgts ); + } + void rwgtRunner::setAmpNorm( double precision ){ + if( this->ampNorm != 0.0 ){ return; } + auto xSecLines = this->lheFile->getInit()->getLines(); + if( xSecLines.size() > 1 ){ + REX::warning("Multiple cross-section lines found in LHE file.\nAssuming total cross section given by sum of all cross sections."); + } + if( xSecLines.size() == 0 ) + throw std::runtime_error( "No cross-section information found in LHE file." ); + double xSec = 0.0; + for( size_t k = 0 ; k < xSecLines.size() ; ++k ){ + xSec += std::stod(std::string(xSecLines[k]->xsecup)); + } + double div = 0.0; + bool sameWeight = true; + for( size_t k = 1 ; k < this->flatWgts->size() - 1 ; k += size_t(flatWgts->size()/21) ){ + if( std::abs( flatWgts->at(k) - flatWgts->at(0) ) > precision ){ + sameWeight = false; + break; + } + } + if( sameWeight ){ + if( std::abs(xSec - flatWgts->at(0)) < precision ){ + this->ampNorm = 1. / double(flatWgts->size()); + return; + } + div = flatWgts->size() * flatWgts->at(0); + } + else{ + div = std::accumulate( flatWgts->begin(), flatWgts->end(), 0.0 ); + } + this->ampNorm = xSec / div; + } + template + void rwgtRunner::setNormWgts(Args&&... args){ + if( !oneME() ){ setMEs(args...); } + for( size_t k = 0; k < initMEs.size() ; ++k ){ + if( initMEs[k]->size() != wgts[k]->size() ) + throw std::runtime_error( "Inconsistent number of events and event weights." ); + } + if( initMEs.size() == 1 ){ setNormWgtsSingleME(); } + else { setNormWgtsMultiME(); } + normWgtSet = true; + } + bool rwgtRunner::singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheIn, size_t currId ){ + if( !normWgtSet ) + throw std::runtime_error( "Normalised original weights (wgt/|ME|) not evaluated -- new weights cannot be calculated." ); + if( !setParamCard( slhaParams ) ) + throw std::runtime_error( "Failed to rewrite parameter card." ); + std::shared_ptr> newWGTs; + if( singAmp() ){ + auto newMEs = meEval( *momenta[0], *gS[0] ); + newWGTs = REX::vecElemMult( *newMEs, *meNormWgts[0] ); + } + else{ + std::vector>> nuMEs = {}; + std::shared_ptr> newMEs = eventFile.vectorFlat( nuMEs ); + newWGTs = REX::vecElemMult( *newMEs, *normWgt ); + } + //ZW IF MULTIPLE TYPES + reWgts->push_back( newWGTs ); + REX::newWgt nuWgt( rwgtSets->rwgtRuns[currId].comRunProc(), reWgts->at(reWgts->size() - 1) ); + lheIn->addWgt( 0, nuWgt ); + return true; + } + bool rwgtRunner::singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheIn, size_t currId, std::string& id ){ + if( !normWgtSet ) + throw std::runtime_error( "Normalised original weights (wgt/|ME|) not evaluated -- new weights cannot be calculated." ); + if( !setParamCard( slhaParams ) ) + throw std::runtime_error( "Failed to rewrite parameter card." ); + std::shared_ptr> newWGTs; + if( singAmp() ){ + auto newMEs = meEval( *momenta[0], *gS[0] ); + newWGTs = REX::vecElemMult( *newMEs, *meNormWgts[0] ); + } + else{ + std::vector>> nuMEs = {}; + for( size_t k = 0 ; k < eventFile.subProcs.size() ; ++k ) + { + nuMEs.push_back(meVec[k]( *(momenta[k]), *(gS[k]) )); + } + std::shared_ptr> newMEs = eventFile.vectorFlat( nuMEs ); + newWGTs = REX::vecElemMult( *newMEs, *normWgt ); + } + //ZW IF MULTIPLE TYPES + reWgts->push_back( newWGTs ); + REX::newWgt nuWgt( rwgtSets->rwgtRuns[currId].comRunProc(), reWgts->at(reWgts->size() - 1), id ); + lheIn->addWgt( 0, nuWgt ); + return true; + } + bool rwgtRunner::singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheIn, size_t currId, REX::event& ev ){ + if( !normWgtSet ) + throw std::runtime_error( "Normalised original weights (wgt/|ME|) not evaluated -- new weights cannot be calculated." ); + if( !setParamCard( slhaParams ) ) + throw std::runtime_error( "Failed to rewrite parameter card." ); + std::shared_ptr> newWGTs; + if( singAmp() ){ + auto newMEs = meEval( *momenta[0], *gS[0] ); + newWGTs = REX::vecElemMult( *newMEs, *meNormWgts[0] ); + } + else{ + std::vector>> nuMEs = {}; + std::shared_ptr> newMEs = eventFile.vectorFlat( nuMEs ); + newWGTs = REX::vecElemMult( *newMEs, *normWgt ); + } + //ZW IF MULTIPLE TYPES + reWgts->push_back( newWGTs ); + REX::newWgt nuWgt( rwgtSets->rwgtRuns[currId].comRunProc(), reWgts->at(reWgts->size() - 1) ); + lheIn->addWgt( 0, nuWgt ); + return true; + } + bool rwgtRunner::singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheIn, size_t currId, + std::string& id, REX::event& ev ){ + if( !normWgtSet ) + throw std::runtime_error( "Normalised original weights (wgt/|ME|) not evaluated -- new weights cannot be calculated." ); + if( !setParamCard( slhaParams ) ) + throw std::runtime_error( "Failed to rewrite parameter card." ); + std::shared_ptr> newWGTs; + if( singAmp() ){ + auto newMEs = meEval( *momenta[0], *gS[0] ); + newWGTs = REX::vecElemMult( *newMEs, *meNormWgts[0] ); + } + else{ + std::vector>> nuMEs = {}; + std::shared_ptr> newMEs = eventFile.vectorFlat( nuMEs ); + newWGTs = REX::vecElemMult( *newMEs, *normWgt ); + } + //ZW IF MULTIPLE TYPES + reWgts->push_back( newWGTs ); + REX::newWgt nuWgt( rwgtSets->rwgtRuns[currId].comRunProc(), reWgts->at(reWgts->size() - 1), id ); + lheIn->addWgt( 0, nuWgt ); + return true; + } + bool rwgtRunner::lheFileWriter( std::shared_ptr lheIn, std::string outputDir ){ + bool writeSuccess = REX::filePusher( outputDir, *lheIn->nodeWriter() ); + if( !writeSuccess ) + throw std::runtime_error( "Failed to write LHE file." ); + return true; + } + bool rwgtRunner::calcXSecs(){ + if( normXSecs->size() != 0 ){ return true; } + if( ampNorm == 0.0 ) + throw std::runtime_error( "Normalisation factor for scattering amplitudes has not been calculated.\nReweighted LHE file has been written, but may contain errors." ); + if( reWgts->size() == 0 ) + throw std::runtime_error( "No reweighting has been performed, or new weights have not been stored properly.\nReweighted LHE file has been written, but may contain errors." ); + for( size_t k = 0 ; k < reWgts->size() ; ++k ){ + normXSecs->push_back( ampNorm * std::accumulate( reWgts->at(k)->begin(), reWgts->at(k)->end(), 0.0 ) ); + } + return true; + } + bool rwgtRunner::calcXErrs(){ + if( errXSecs->size() != 0 ){ return true; } + if( reWgts->size() == 0 ) + throw std::runtime_error( "No reweighting has been performed, or new weights have not been stored properly.\nReweighted LHE file has been written, but may contain errors." ); + if( normXSecs->size() != reWgts->size() ) + throw std::runtime_error( "Different number of reweighted event sets and reweighted cross sections internally.\nReweighted LHE file has been written, but may contain errors." ); + double invN = 1. / double(reWgts->at(0)->size()); + double sqrtInvN = std::sqrt( invN ); + auto xSecLines = this->lheFile->getInit()->getLines(); + double xSec = 0.0; + double xErr = 0.0; + for( size_t k = 0 ; k < xSecLines.size() ; ++k ){ + xSec += std::stod(std::string(xSecLines[k]->xsecup)); + xErr += std::pow(std::stod(std::string(xSecLines[k]->xerrup)),2); + } + xErr = std::sqrt( xErr ); + for( size_t k = 0 ; k < reWgts->size() ; ++k ){ + double xSecCurr = normXSecs->at(k); + auto locWgts = reWgts->at(k); + double omega = 0.0; + double omegaSqr = 0.0; + for( auto wgt : *locWgts ){ + double invWgt = 1. / wgt; + omega += invWgt; + omegaSqr += invWgt * invWgt; + } + double var = (omegaSqr - omega * omega * invN) * invN * xSecCurr * xSecCurr; + double error = std::sqrt( std::max( 0., sqrtInvN * var) )*xSec + xSecCurr * omega * invN * xErr; + if( std::isnan( error ) || std::isinf( error ) ){ + std::cout << "\033[1;33mWarning:Error propagation yielded NaN for " << rwgtSets->rwgtNames->at(k) << ". Approximating the error at cross section level.\033[0m\n"; + error = xErr * std::max( xSec / xSecCurr, xSecCurr / xSec ); + } + errXSecs->push_back( error ); + } + return true; + } + void rwgtRunner::runRwgt( const std::string& output, double precision ){ + reWgts = std::make_shared>>>( std::vector>>() ); + setAmpNorm( precision ); + setMEs(); + setNormWgts(); + rwgtGroup = std::make_shared(); + auto currInd = lheFile->getHeader()->addWgtGroup( rwgtGroup ); + auto paramSets = rwgtSets->writeCards( *slhaParameters ); + for( size_t k = 0 ; k < paramSets.size(); k++ ){ + singleRwgtIter( paramSets[k], lheFile, k, rwgtSets->rwgtNames->at(k) ); + std::cout << "."; + } + lheFileWriter( lheFile, output ); + REX::filePusher( slhaPath, *slhaCard ); + std::cout << "\nReweighting done.\n"; + } + std::shared_ptr> rwgtRunner::getReXSecs(){ + if(this->calcXSecs()){ return normXSecs; } + return nullptr; + } + std::shared_ptr> rwgtRunner::getReXErrs(){ + if(this->calcXErrs()){ return errXSecs; } + return nullptr; + } + +} + +#endif diff --git a/tools/REX/teaREX.h b/tools/REX/teaREX.h new file mode 100644 index 000000000..fe57098d5 --- /dev/null +++ b/tools/REX/teaREX.h @@ -0,0 +1,314 @@ +/*** + * _ ______ + * | | | ___ \ + * | |_ ___ __ _| |_/ /_____ __ + * | __/ _ \/ _` | // _ \ \/ / + * | || __/ (_| | |\ \ __/> < + * \__\___|\__,_\_| \_\___/_/\_\ + * + ***/ +// +// *t*ensorial *e*vent *a*daption with *Rex* Version 0.9.0 +// teaRex is an extension to the Rex C++ library for parsing and manipulating Les Houches Event-format (LHE) files, +// designed for leading order event reweighting based on input LHE file(s) and scattering amplitude functions. +// teaRex is in development and may not contain all features necessary for all desired features, +// and does not have documentation beyond the code itself. +// +// Copyright © 2023-2024 CERN, CERN Author Zenny Wettersten. +// Licensed under the GNU Lesser General Public License (version 3 or later). +// All rights not expressly granted are reserved. +// + +#ifndef _TEAREX_H_ +#define _TEAREX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "REX.h" + +namespace REX::tea +{ + + using amplitude = std::function>(std::vector&, std::vector&)>; + using vecMap = std::map>, REX::eventComp>; + + using iterator = std::function; + using weightor = std::function>( REX::procSoA& )>; + + // ZW: wrapper for procSoA and the reweighting process + // Not a derived class of procSoA, such that the full reweight + // class can be derived from lheSoA without having to deal with + // the procSoA objects stored in the lheSoA class + struct procRwgt { + std::shared_ptr proc; + std::vector amplitude; + weightor originalAmp; + std::shared_ptr> invOriginalAmp; + std::vector>> weights; + std::vector>> backlog; + size_t iteration; + void uniqueReset(); + void reset(); + procRwgt(); + procRwgt( const REX::relEvArgs& relData ); + procRwgt( const procSoA& process ); + procRwgt( procSoA* process ); + procRwgt( std::shared_ptr process ); + procRwgt( const procRwgt& process ); + procRwgt( procRwgt* process ); + procRwgt( std::shared_ptr process ); + procRwgt( std::vector> lheFile, REX::relEvArgs relArgs = REX::relEvArgs(), + std::vector relevStats = {-1,1}, std::function relFcn = nullptr ); + procRwgt( std::vector ampFcns ); + procRwgt& setAmplitude( weightor amp ); + procRwgt& setAmplitude( std::vector amp ); + procRwgt& setOriginalAmp( weightor amp ); + bool initialise(); + bool evaluate(); + bool normalise(); + void doBacklog( bool pass ); + }; + + struct reweightor : REX::lheSoA { + std::vector> amps; + std::vector iterators; + iterator originator; + iterator terminator; + std::vector>> wgts; + std::vector xSecs; + std::vector xErrs; + std::vector success; + double ampNorm; + size_t ampsPerIter = 1; + void uniqueReset(); + void reset() override; + void setAmpsFromSubprocs(); + reweightor(); + reweightor( const lheSoA& lheFile ); + reweightor( const reweightor& lheFile ); + reweightor( std::vector> lheFile ); + reweightor( std::vector> lheFile, std::vector> evSort ); + reweightor( lheNode& lheFile, std::vector> evSort ); + reweightor( std::vector> lheFile, std::vector> evSort, + std::vector relData ); + reweightor( lheNode& lheFile, std::vector> evSort, + std::vector relData ); + reweightor( std::vector> lheFile, std::vector> evSort, + std::vector> relStats ); + reweightor( lheNode& lheFile, std::vector> evSort, + std::vector> relStats ); + reweightor( std::vector> lheFile, std::vector> evSort, + std::vector relData, std::vector> relStats ); + reweightor( lheNode& lheFile, std::vector> evSort, + std::vector relData, std::vector> relStats ); + reweightor& setAmps( std::vector> newAmps ); + reweightor& setAmps( std::vector ampFcns ); + reweightor& setAmps( std::vector> ampFcns ); + reweightor& setIterators( std::vector iters ); + reweightor& setOriginator( iterator origin ); + reweightor& setTerminator( iterator term ); + // wrapper for running amps (skips iteration if not all amps succeed?) + bool runAmps(); + // wrapper for running iterator and then amps + void runBacklog( bool success ); + bool doIterate( size_t index ); + void doAllIterations(); + void doInit(); + void doFin(); + void flattenWeights(); + void calcAmpNorm(); + bool setAmpNorm( bool hard = false ); + void calcXSecs(); + void calcXErrs(); + void run(); + void appendWgtsSimple( lheNode& lheFile, std::vector procs, std::shared_ptr> names ); + void appendWgts( lheNode& lheFile, std::vector procs, std::shared_ptr> names ); + }; + + struct rwgtVal : REX::paramVal{ + public: + std::string_view blockName; + bool allStat; + bool isAll(); + rwgtVal(); + rwgtVal( std::string_view paramLine ); + std::string_view getLine(); + void outWrite( REX::paramBlock& srcBlock ); + }; + + struct rwgtBlock { + public: + std::string name; + std::vector rwgtVals; + rwgtBlock( std::vector values = {}, std::string_view title = "" ); + rwgtBlock( const std::vector& vals, std::string_view title = "" ); + std::string_view getBlock(); + //void outWrite( REX::paramBlock& srcBlock, const std::map& blocks ); + void outWrite( REX::paramBlock& srcBlock ); + protected: + std::string runBlock; + bool written = false; + }; + + struct rwgtProc { + public: + std::vector rwgtParams; + std::string_view procString; + std::string_view rwgtName; + std::vector rwgtOpts; + void parse(); + rwgtProc( REX::lesHouchesCard slhaSet, std::string_view rwgtSet = "", bool parseOnline = false ); + std::shared_ptr outWrite( const REX::lesHouchesCard& paramOrig ); + std::string_view comRunProc(); + }; + + struct rwgtCard{ + public: + REX::lesHouchesCard slhaCard; + std::vector cardWriters; + std::vector rwgtRuns; + std::vector rwgtProcs; + std::vector opts; + std::vector> writtenCards; + std::shared_ptr> rwgtNames; + std::string_view srcCard; + void parse( bool parseOnline = false ); + rwgtCard( std::string_view reweight_card ); + rwgtCard( std::string_view reweight_card, REX::lesHouchesCard slhaParams, bool parseOnline = false ); + std::vector> writeCards( REX::lesHouchesCard& slhaOrig ); + std::shared_ptr> getNames(); + std::vector getProcs(); + std::vector getIterators( std::string path ); + }; + + + struct rwgtCollection { + public: + void setRwgt( std::shared_ptr rwgts ); + void setRwgt( rwgtCard rwgts ); + void setSlha( std::shared_ptr slha ); + void setSlha( REX::lesHouchesCard slha ); + void setLhe( std::shared_ptr lhe ); + void setLhe( REX::lheNode& lhe ); + void setLhe( std::string_view lhe_file ); + std::shared_ptr getRwgt(); + std::shared_ptr getSlha(); + std::shared_ptr getLhe(); + REX::transSkel& getSkeleton(); + REX::transSkel& getSkeleton( std::vector& evSets ); + rwgtCollection(); + rwgtCollection( std::shared_ptr lhe, std::shared_ptr slha, std::shared_ptr rwgts ); + rwgtCollection( const rwgtCollection& rwgts ); + std::shared_ptr> getNames(); + protected: + template + void setDoubles(Args&&... args); + void setSkeleton( std::vector& evSets); + void setDoublesFromSkeleton(); + std::shared_ptr rwgtSets; + std::shared_ptr slhaParameters; + std::shared_ptr lheFile; + std::vector>> wgts; + std::vector>> gS; + std::vector>> momenta; + std::shared_ptr> flatWgts; + bool lheFileSet = false; + bool slhaSet = false; + bool rwgtSet = false; + bool skeleton = false; + bool doublesSet = false; + REX::transLHE eventFile; + REX::transSkel lheSkeleton; + }; + + struct rwgtFiles : rwgtCollection { + void setRwgtPath( std::string_view path ); + void setSlhaPath( std::string_view path ); + void setLhePath( std::string_view path ); + rwgtFiles(); + rwgtFiles( std::string_view lhe_card, std::string_view slha_card, std::string_view reweight_card ); + rwgtFiles( const rwgtFiles& rwgts ); + REX::transSkel& initCards( std::vector& evSets); + void initDoubles(); + template + void initCards(Args&&... args); + template + void initCards( std::string_view lhe_card, std::string_view slha_card, std::string_view reweight_card, Args&&... args ); + protected: + bool rwgtPulled(); + bool slhaPulled(); + bool lhePulled(); + void pullRwgt(); + void pullSlha(); + void pullLhe(); + bool initialised = false; + std::string rwgtPath; + std::string lhePath; + std::string slhaPath; + std::shared_ptr lheCard; + std::shared_ptr slhaCard; + std::shared_ptr rewgtCard; + }; + + struct rwgtRunner : rwgtFiles{ + public: + void setMeEval( amplitude eval ); + void addMeEval( const REX::event& ev, const amplitude& eval ); + rwgtRunner(); + rwgtRunner( rwgtFiles& rwgts ); + rwgtRunner( rwgtFiles& rwgts, amplitude meCalc ); + rwgtRunner( rwgtFiles& rwgts, std::vector& meCalcs ); + rwgtRunner( std::string_view lhe_card, std::string_view slha_card, std::string_view reweight_card, + amplitude meCalc ); + rwgtRunner(const rwgtRunner& rwgts); + bool oneME(); + bool singAmp(); + protected: + bool meInit = false; + bool meCompInit = false; + bool meSet = false; + bool normWgtSet = false; + amplitude meEval; + //ampCall meEvals; + std::vector meVec; + std::vector>> initMEs; + std::vector>> meNormWgts; + std::shared_ptr>>> reWgts; + std::shared_ptr> normWgt; + double ampNorm = 0.0; + std::shared_ptr> normXSecs; + std::shared_ptr> errXSecs; + std::shared_ptr rwgtGroup; + template + void setMEs(Args&&... args); + void setAmpNorm( double precision ); + bool setParamCard( std::shared_ptr slhaParams ); + void setNormWgtsSingleME(); + void setNormWgtsMultiME(); + bool calcXSecs(); + bool calcXErrs(); + template + void setNormWgts(Args&&... args); + bool singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheFile, size_t currId ); + bool singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheFile, size_t currId, std::string& id ); + bool singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheFile, size_t currId, REX::event& ev ); + bool singleRwgtIter( std::shared_ptr slhaParams, std::shared_ptr lheFile, size_t currId, + std::string& id, REX::event& ev ); + bool lheFileWriter( std::shared_ptr lheFile, std::string outputDir = "rwgt_evts.lhe" ); + public: + void runRwgt( const std::string& output, double precision = 1e-6 ); + std::shared_ptr> getReXSecs(); + std::shared_ptr> getReXErrs(); + }; + + + void rwgtRun( rwgtRunner& rwgt, const std::string& path ); + +} + +#endif \ No newline at end of file