diff --git a/.gitignore b/.gitignore index 0261db2..bf3dd86 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.hepmc3 .DS_Store test* +.cache diff --git a/cjetreconstruction/.gitignore b/cjetreconstruction/.gitignore new file mode 100644 index 0000000..6ffa289 --- /dev/null +++ b/cjetreconstruction/.gitignore @@ -0,0 +1,6 @@ +*.d +*.dSYM +*.out +*.json +build +results/ diff --git a/cjetreconstruction/CMakeLists.txt b/cjetreconstruction/CMakeLists.txt new file mode 100644 index 0000000..afc1555 --- /dev/null +++ b/cjetreconstruction/CMakeLists.txt @@ -0,0 +1,27 @@ +# CMake file for benchmark and validation of JetReconstruction.jl C-bindings +# +# (C) CERN, 2025 +# MIT License + +cmake_minimum_required(VERSION 3.12) +project(JetReconstructionBenchmarks) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +find_package(HepMC3 REQUIRED) +find_package(JetReconstruction REQUIRED) +find_package(Julia REQUIRED) # Find Julia for GC utilities + +set(CMAKE_CXX_STANDARD 17) + +# JetReconstruction.jl C-bindings executable runs reconstruction then outputs either +# exclusive or inclusive jets +add_executable(cjetreconstruction-finder + src/cjetreconstruction-finder.cc + src/cjetreconstruction-utils.cc +) + +target_include_directories(cjetreconstruction-finder PRIVATE ${Julia_INCLUDE_DIRS}) +target_link_libraries(cjetreconstruction-finder + HepMC3::HepMC3 + JetReconstruction::JetReconstruction ${Julia_LIBRARY} +) diff --git a/cjetreconstruction/README.md b/cjetreconstruction/README.md new file mode 100644 index 0000000..9369f7a --- /dev/null +++ b/cjetreconstruction/README.md @@ -0,0 +1,24 @@ +# JetReconstruction.jl C-bindings Test Applications + +Benchmark application using the JetReconstruction.jl statically compiled to a shared library. + +## Compilation + +Configure and compile using CMake in the standard way, e.g., + +```sh +cmake -S . -B build +# or if libjectreconstruction is installed in non-standard location +# JetReconstruction_DIR= cmake -S . -B build +cmake --build build +``` + +> [!NOTE] +> Make sure to find the same version of Julia that was used to compile the JetReconstruction package. +> Custom search path can be set with `Julia_ROOT`, for instance: +> +> ```sh +> Julia_ROOT=${HOME}/.julia/juliaup/julia-1.12/ cmake -S . -B build +> ``` +> +> Also make sure that for Julia 1.12 at least GLIBCXX_3.4.30 (gcc 12.1.0) is used. diff --git a/cjetreconstruction/cmake/FindJulia.cmake b/cjetreconstruction/cmake/FindJulia.cmake new file mode 100644 index 0000000..36f9113 --- /dev/null +++ b/cjetreconstruction/cmake/FindJulia.cmake @@ -0,0 +1,185 @@ +# File taken from JuliaInteroplibcxxwrap-julia v0.13.4 +# MIT License +# Copyright (c) 2015: Bart Janssens + +# Original FindJulia.cmake from https://github.com/QuantStack/xtensor-julia-cookiecutter/blob/master/%7B%7Bcookiecutter.github_project_name%7D%7D/cmake/FindJulia.cmake + +if(Julia_FOUND) + return() +endif() + +#################### +# Julia Executable # +#################### + +if(Julia_PREFIX) + message(STATUS "Adding path ${Julia_PREFIX} to search path") + list(APPEND CMAKE_PREFIX_PATH ${Julia_PREFIX}) + message(STATUS "THIS BRANCH") +else() + find_program(Julia_EXECUTABLE julia DOC "Julia executable") + message(STATUS "Found Julia executable: " ${Julia_EXECUTABLE}) +endif() + +################# +# Julia Version # +################# + +if(Julia_EXECUTABLE) + execute_process( + COMMAND "${Julia_EXECUTABLE}" --startup-file=no --version + OUTPUT_VARIABLE Julia_VERSION_STRING + ) +else() + find_file(Julia_VERSION_INCLUDE julia_version.h PATH_SUFFIXES include/julia) + file(READ ${Julia_VERSION_INCLUDE} Julia_VERSION_STRING) + string(REGEX MATCH "JULIA_VERSION_STRING.*" Julia_VERSION_STRING ${Julia_VERSION_STRING}) +endif() + +string( + REGEX REPLACE ".*([0-9]+\\.[0-9]+\\.[0-9]+).*" "\\1" + Julia_VERSION_STRING "${Julia_VERSION_STRING}" +) + +MESSAGE(STATUS "Julia_VERSION_STRING: ${Julia_VERSION_STRING}") + +################## +# Julia Includes # +################## + +set(JULIA_HOME_NAME "Sys.BINDIR") +if(${Julia_VERSION_STRING} VERSION_LESS "0.7.0") + set(JULIA_HOME_NAME "JULIA_HOME") +else() + set(USING_LIBDL "using Libdl") +endif() + +if(DEFINED ENV{JULIA_INCLUDE_DIRS}) + set(Julia_INCLUDE_DIRS $ENV{JULIA_INCLUDE_DIRS} + CACHE STRING "Location of Julia include files") +elseif(Julia_EXECUTABLE) + execute_process( + COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "julia_include_dir = joinpath(match(r\"(.*)(bin)\",${JULIA_HOME_NAME}).captures[1],\"include\",\"julia\")\n + if !isdir(julia_include_dir) # then we're running directly from build\n + julia_base_dir_aux = splitdir(splitdir(${JULIA_HOME_NAME})[1])[1] # useful for running-from-build\n + julia_include_dir = joinpath(julia_base_dir_aux, \"usr\", \"include\" )\n + julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\", \"support\" )\n + julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\" )\n + end\n + julia_include_dir" + OUTPUT_VARIABLE Julia_INCLUDE_DIRS + ) + + string(REGEX REPLACE "\"" "" Julia_INCLUDE_DIRS "${Julia_INCLUDE_DIRS}") + string(REGEX REPLACE "\n" "" Julia_INCLUDE_DIRS "${Julia_INCLUDE_DIRS}") + set(Julia_INCLUDE_DIRS ${Julia_INCLUDE_DIRS} + CACHE PATH "Location of Julia include files") +elseif(Julia_PREFIX) + set(Julia_INCLUDE_DIRS ${Julia_PREFIX}/include/julia) +endif() +set(Julia_INCLUDE_DIRS ${Julia_INCLUDE_DIRS};$ENV{includedir}) +MESSAGE(STATUS "Julia_INCLUDE_DIRS: ${Julia_INCLUDE_DIRS}") + +################### +# Julia Libraries # +################### + +if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES} .a;.dll) +endif() + +if(Julia_EXECUTABLE) + execute_process( + COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "${USING_LIBDL}\nabspath(Libdl.dlpath((ccall(:jl_is_debugbuild, Cint, ()) != 0) ? \"libjulia-debug\" : \"libjulia\"))" + OUTPUT_VARIABLE Julia_LIBRARY + ) + + string(REGEX REPLACE "\"" "" Julia_LIBRARY "${Julia_LIBRARY}") + string(REGEX REPLACE "\n" "" Julia_LIBRARY "${Julia_LIBRARY}") + string(STRIP "${Julia_LIBRARY}" Julia_LIBRARY) + + if(WIN32) + get_filename_component(Julia_LIBRARY_DIR ${Julia_LIBRARY} DIRECTORY) + get_filename_component(Julia_LIBRARY_DIR ${Julia_LIBRARY_DIR} DIRECTORY) + find_library(win_Julia_LIBRARY + NAMES libjulia.dll.a + PATHS "${Julia_LIBRARY_DIR}/lib" + NO_DEFAULT_PATH + ) + set(Julia_LIBRARY "${win_Julia_LIBRARY}") + endif() + + set(Julia_LIBRARY "${Julia_LIBRARY}" + CACHE PATH "Julia library") +else() + find_library(Julia_LIBRARY NAMES libjulia.${Julia_VERSION_STRING}.dylib julia libjulia.dll.a libjulia CMAKE_FIND_ROOT_PATH_BOTH) +endif() + +get_filename_component(Julia_LIBRARY_DIR ${Julia_LIBRARY} DIRECTORY) + +MESSAGE(STATUS "Julia_LIBRARY_DIR: ${Julia_LIBRARY_DIR}") +MESSAGE(STATUS "Julia_LIBRARY: ${Julia_LIBRARY}") + +############## +# JULIA_HOME # +############## + +if(Julia_EXECUTABLE) + execute_process( + COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "${JULIA_HOME_NAME}" + OUTPUT_VARIABLE JULIA_HOME + ) + + string(REGEX REPLACE "\"" "" JULIA_HOME "${JULIA_HOME}") + string(REGEX REPLACE "\n" "" JULIA_HOME "${JULIA_HOME}") + + MESSAGE(STATUS "JULIA_HOME: ${JULIA_HOME}") + +################### +# libLLVM version # +################### + + execute_process( + COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "Base.libllvm_version" + OUTPUT_VARIABLE Julia_LLVM_VERSION + ) + + string(REGEX REPLACE "\"" "" Julia_LLVM_VERSION "${Julia_LLVM_VERSION}") + string(REGEX REPLACE "\n" "" Julia_LLVM_VERSION "${Julia_LLVM_VERSION}") + + MESSAGE(STATUS "Julia_LLVM_VERSION: ${Julia_LLVM_VERSION}") +endif() + +################################## +# Check for Existence of Headers # +################################## + +find_path(Julia_MAIN_HEADER julia.h HINTS ${Julia_INCLUDE_DIRS}) + +####################################### +# Determine if we are on 32 or 64 bit # +####################################### + +if(Julia_EXECUTABLE) + execute_process( + COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "Sys.WORD_SIZE" + OUTPUT_VARIABLE Julia_WORD_SIZE + ) + string(REGEX REPLACE "\n" "" Julia_WORD_SIZE "${Julia_WORD_SIZE}") + MESSAGE(STATUS "Julia_WORD_SIZE: ${Julia_WORD_SIZE}") +endif() + +if($ENV{target} MATCHES "^i686.*") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse -msse2") +endif() + +########################### +# FindPackage Boilerplate # +########################### + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Julia + REQUIRED_VARS Julia_LIBRARY Julia_LIBRARY_DIR Julia_INCLUDE_DIRS Julia_MAIN_HEADER + VERSION_VAR Julia_VERSION_STRING + FAIL_MESSAGE "Julia not found" +) diff --git a/cjetreconstruction/src/cjetreconstruction-finder.cc b/cjetreconstruction/src/cjetreconstruction-finder.cc new file mode 100644 index 0000000..56c8af4 --- /dev/null +++ b/cjetreconstruction/src/cjetreconstruction-finder.cc @@ -0,0 +1,271 @@ +// MIT Licenced, Copyright (c) 2023-2025 CERN +// +// Code to run and time the jet finding of against various +// HepMC3 input files + +// Original version of this code Philippe Gras, IRFU +// Modified by Graeme A Stewart, CERN + +#include +#include +#include +#include +#include // needed for io +#include // needed for io +#include +#include +#include +#include + +// Program Options Parser Library (https://github.com/badaix/popl) +#include "popl.hpp" + +#include "JetReconstruction.h" +#ifdef JETRECONSTRUCTION_COMPILER_PACKAGECOMPILER +extern "C" { +#include "julia_init.h" +} +#endif + +#include "HepMC3/GenEvent.h" +#include "HepMC3/GenParticle.h" +#include "HepMC3/ReaderAscii.h" + +#include "cjetreconstruction-utils.hh" + +#include // for jl_gc functions + +using namespace std; +using namespace popl; +using Time = std::chrono::high_resolution_clock; +using us = std::chrono::microseconds; + +// sorted into decreasing transverse momentum as in fastjet::sorted_by_pt +void sorted_by_pt(jetreconstruction_JetsResult &jets) { + std::sort(jets.data, jets.data + jets.length, + [](const auto &left, const auto &right) { + return left._pt2 > right._pt2; + }); +} + +jetreconstruction_ClusterSequence +run_clustering(std::vector& input_particles, + jetreconstruction_RecoStrategy strategy, + jetreconstruction_JetAlgorithm algorithm, + jetreconstruction_RecombinationScheme recombine_scheme, + double R, double p) { + + auto clust_seq = jetreconstruction_ClusterSequence{}; + auto retv = jetreconstruction_jet_reconstruct( + input_particles.data(), input_particles.size(), algorithm, p, R, strategy, recombine_scheme, + &clust_seq); + assert(retv == jetreconstruction_StatusCode::JETRECONSTRUCTION_STATUSCODE_OK); + return clust_seq; +} + +int main(int argc, char *argv[]) { +#ifdef JETRECONSTRUCTION_COMPILER_PACKAGECOMPILER + init_julia(0, nullptr); +#endif + + // Default values + int maxevents = -1; + int skip_events = 0; + int trials = 1; + string mystrategy = "Best"; + double power = -1.0; + string alg = "AntiKt"; + string recombine = ""; + double R = 0.4; + string dump_file = ""; + + OptionParser opts("Allowed options"); + auto help_option = opts.add("h", "help", "produce help message"); + auto max_events_option = opts.add>("n", "maxevents", "Maximum events in file to process (-1 = all events)", maxevents, &maxevents); + auto skip_events_option = opts.add>("", "skipevents", "Number of events to skip over (0 = none)", skip_events, &skip_events); + auto trials_option = opts.add>("m", "nsamples", "Number of repeated samples / trials", trials, &trials); + auto strategy_option = opts.add>("s", "strategy", "Valid values are 'Best' (default), 'N2Plain', 'N2Tiled'", mystrategy, &mystrategy); + auto power_option = opts.add>("p", "power", "Algorithm p value for generalised Kt algorithm, otherwise ignored", power, &power); + auto alg_option = opts.add>("A", "algorithm", "Algorithm: AntiKt (default) CA Kt GenKt", alg, &alg); + auto radius_option = opts.add>("R", "radius", "Algorithm R parameter", R, &R); + auto recombine_option = opts.add>("", "recombine", "Recombination scheme for jet merging", recombine, &recombine); + auto ptmin_option = opts.add>("", "ptmin", "pt cut for inclusive jets"); + auto dijmax_option = opts.add>("", "dijmax", "dijmax value for exclusive jets"); + auto njets_option = opts.add>("", "njets", "njets value for exclusive jets"); + auto dump_option = opts.add>("d", "dump", "Filename to dump jets to"); + auto debug_clusterseq_option = opts.add("c", "debug-clusterseq", "Dump cluster sequence jet and history content"); + auto gc_option = opts.add("", "gc", "Enable garbage collector"); + + + + opts.parse(argc, argv); + + if (help_option->count() == 1) { + cout << argv[0] << " [options] HEPMC3_INPUT_FILE" << endl; + cout << endl; + cout << opts << "\n"; + cout << "Note the only one of ptmin, dijmax or njets can be specified!\n" << endl; + exit(EXIT_SUCCESS); + } + + const auto extra_args = opts.non_option_args(); + std::string input_file{}; + if (extra_args.size() == 1) { + input_file = extra_args[0]; + } else if (extra_args.size() == 0) { + std::cerr << "No argument after options" << std::endl; + } else { + std::cerr << "Only one supported" << std::endl; + } + + // Check we only have 1 option for final jet selection + auto sum = int(njets_option->is_set()) + int(dijmax_option->is_set()) + int(ptmin_option->is_set()); + if (sum != 1) { + cerr << "One, and only one, of ptmin, dijmax or njets needs to be specified (currently " << + sum << ")" << endl; + exit(EXIT_FAILURE); + } + + // read in input events + //---------------------------------------------------------- + auto events = read_input_events(input_file.c_str(), maxevents); + + // Set strategy + auto strategy = + jetreconstruction_RecoStrategy::JETRECONSTRUCTION_RECOSTRATEGY_BEST; + if (mystrategy == string("N2Plain")) { + strategy = + jetreconstruction_RecoStrategy::JETRECONSTRUCTION_RECOSTRATEGY_N2PLAIN; + } else if (mystrategy == string("N2Tiled")) { + strategy = + jetreconstruction_RecoStrategy::JETRECONSTRUCTION_RECOSTRATEGY_N2TILTED; + } + + auto algorithm = + jetreconstruction_JetAlgorithm::JETRECONSTRUCTION_JETALGORITHM_ANTIKT; + if (alg == "AntiKt") { + algorithm = + jetreconstruction_JetAlgorithm::JETRECONSTRUCTION_JETALGORITHM_ANTIKT; + power = -1.0; + } else if (alg == "CA") { + algorithm = + jetreconstruction_JetAlgorithm::JETRECONSTRUCTION_JETALGORITHM_CA; + power = 0.0; + } else if (alg == "Kt") { + algorithm = + jetreconstruction_JetAlgorithm::JETRECONSTRUCTION_JETALGORITHM_KT; + power = 1.0; + } else if (alg == "GenKt") { + algorithm = + jetreconstruction_JetAlgorithm::JETRECONSTRUCTION_JETALGORITHM_GENKT; + //} else if (alg == "Durham") { + // algorithm = fastjet::ee_kt_algorithm; + // power = 1.0; + //} else if (alg == "EEKt") { + // algorithm = fastjet::ee_genkt_algorithm; + } else { + std::cout << "Unknown algorithm type: " << alg << std::endl; + exit(1); + } + + auto recombine_scheme = JETRECONCSTRUCTION_RECOMBINATIONSCHEME_ESCHEME; + std::cout << recombine << std::endl; + if (recombine == "pt_scheme") { + recombine_scheme = JETRECONCSTRUCTION_RECOMBINATIONSCHEME_PTSCHEME; + } else if (recombine == "pt2_scheme") { + recombine_scheme = JETRECONCSTRUCTION_RECOMBINATIONSCHEME_PT2SCHEME; + } + + std::cout << "Strategy: " << mystrategy << "; Power: " << power << "; Algorithm " << algorithm << + "; Recombine " << recombine_scheme << std::endl; + + auto dump_fh = stdout; + if (dump_option->is_set()) { + if (dump_option->value() != "-") { + dump_fh = fopen(dump_option->value().c_str(), "w"); + } + } + + double time_total = 0.0; + double time_total2 = 0.0; + double sigma = 0.0; + double time_lowest = 1.0e20; + for (long trial = 0; trial < trials; ++trial) { + std::cout << "Trial " << trial << " "; + if (!gc_option->count()) { + jl_gc_enable(1); // collect actually sweeps only with enabled gc + jl_gc_collect(JL_GC_AUTO); // FULL takes longer without any visible change to timing of measured code + jl_gc_enable(0); + } + auto start_t = std::chrono::steady_clock::now(); + for (size_t ievt = skip_events_option->value(); ievt < events.size(); ++ievt) { + auto cluster_sequence = + run_clustering(events[ievt], strategy, algorithm, recombine_scheme, R, power); + auto final_jets = jetreconstruction_JetsResult{nullptr,0}; + if (ptmin_option->is_set()) { + auto retv = jetreconstruction_inclusive_jets( + &cluster_sequence, ptmin_option->value(), &final_jets); + assert(retv == + jetreconstruction_StatusCode::JETRECONSTRUCTION_STATUSCODE_OK); + } else if (dijmax_option->is_set()) { + auto retv = jetreconstruction_exclusive_jets_dcut( + &cluster_sequence, dijmax_option->value(), &final_jets); + assert(retv == + jetreconstruction_StatusCode::JETRECONSTRUCTION_STATUSCODE_OK); + } else if (njets_option->is_set()) { + auto retv = jetreconstruction_exclusive_jets_njets( + &cluster_sequence, njets_option->value(), &final_jets); + assert(retv == + jetreconstruction_StatusCode::JETRECONSTRUCTION_STATUSCODE_OK); + } + + if (dump_option->is_set() && trial == 0) { + // sort by pt so files can be compared + sorted_by_pt(final_jets); + fprintf(dump_fh, "Jets in processed event %zu\n", ievt + 1); + + // print out the details for each jet + for (unsigned int i = 0; i < final_jets.length; i++) { + const auto &jet = final_jets.data[i]; + auto perp = std::sqrt(jet.px * jet.px + jet.py * jet.py); + fprintf(dump_fh, "%5u %15.10f %15.10f %15.10f\n", i, jet._rap, + jet._phi, perp); + } + + // Dump the cluster sequence history content as well? + if (debug_clusterseq_option->is_set()) { + // dump_clusterseq(cluster_sequence); + } + } + jetreconstruction_ClusterSequence_free_members(&cluster_sequence); + jetreconstruction_JetsResult_free_members(&final_jets); + } + auto stop_t = std::chrono::steady_clock::now(); + auto elapsed = stop_t - start_t; + auto us_elapsed = double(chrono::duration_cast(elapsed).count()); + std::cout << us_elapsed << " us" << endl; + time_total += us_elapsed; + time_total2 += us_elapsed*us_elapsed; + if (us_elapsed < time_lowest) time_lowest = us_elapsed; + } + time_total /= trials; + time_total2 /= trials; + if (trials > 1) { + sigma = std::sqrt(double(trials)/(trials-1) * (time_total2 - time_total*time_total)); + } else { + sigma = 0.0; + } + double mean_per_event = time_total / events.size(); + double sigma_per_event = sigma / events.size(); + time_lowest /= events.size(); + std::cout << "Processed " << events.size() << " events, " << trials << " times" << endl; + std::cout << "Total time " << time_total << " us" << endl; + std::cout << "Time per event " << mean_per_event << " +- " << sigma_per_event << " us" << endl; + std::cout << "Lowest time per event " << time_lowest << " us" << endl; + + const int ret_code = 0; +#ifdef JETRECONSTRUCTION_COMPILER_PACKAGECOMPILER + shutdown_julia(ret_code); +#endif + return ret_code; +} diff --git a/cjetreconstruction/src/cjetreconstruction-utils.cc b/cjetreconstruction/src/cjetreconstruction-utils.cc new file mode 100644 index 0000000..29271ab --- /dev/null +++ b/cjetreconstruction/src/cjetreconstruction-utils.cc @@ -0,0 +1,64 @@ +// MIT Licenced, Copyright (c) 2023-2025 CERN +// +// Utilities common to all fastjet codes used for benchmarking and validation + +// Original version of this code Philippe Gras, IRFU +// Modified by Graeme A Stewart, CERN +#include "cjetreconstruction-utils.hh" +#include "JetReconstruction.h" +#include +#include // needed for io +#include +#include + +#include + +#include "HepMC3/GenEvent.h" +#include "HepMC3/GenParticle.h" +#include "HepMC3/ReaderAscii.h" + + +std::vector> read_input_events(const char* fname, long maxevents) { + // Read input events from a HepMC3 file, return the events in a vector + // Each event is a vector of initial particles + + HepMC3::ReaderAscii input_file (fname); + + int events_parsed = 0; + + std::vector> events; + + while(!input_file.failed()) { + if (maxevents >= 0 && events_parsed >= maxevents) break; + + std::vector input_particles; + + HepMC3::GenEvent evt(HepMC3::Units::GEV, HepMC3::Units::MM); + + // Read event from input file + input_file.read_event(evt); + + // If reading failed - exit loop + if (input_file.failed()) break; + + ++events_parsed; + input_particles.clear(); + input_particles.reserve(evt.particles().size()); + long history_index = 0 ; // particle history index must correspond to their index+1 in array + for(auto p: evt.particles()){ + if(p->status() == 1){ + auto jet = jetreconstruction_PseudoJet{}; + auto retv = jetreconstruction_PseudoJet_init( + &jet, p->momentum().px(), p->momentum().py(), p->momentum().pz(), + p->momentum().e(), ++history_index); + assert(retv == + jetreconstruction_StatusCode::JETRECONSTRUCTION_STATUSCODE_OK); + input_particles.push_back(std::move(jet)); + } + } + events.push_back(input_particles); + } + + std::cout << "Read " << events_parsed << " events from " << fname << std::endl; + return events; +} diff --git a/cjetreconstruction/src/cjetreconstruction-utils.hh b/cjetreconstruction/src/cjetreconstruction-utils.hh new file mode 100644 index 0000000..4b46f70 --- /dev/null +++ b/cjetreconstruction/src/cjetreconstruction-utils.hh @@ -0,0 +1,8 @@ +#ifndef C_JETRECONSTRUCTION_UTILS_H_ +#define C_JETRECONSTRUCTION_UTILS_H_ +#include "JetReconstruction.h" +#include + +std::vector> read_input_events(const char* fname, long maxevents = -1); + +#endif // C_JETRECONSTRUCTION_UTILS_H_ diff --git a/cjetreconstruction/src/popl.hpp b/cjetreconstruction/src/popl.hpp new file mode 100644 index 0000000..8fd668b --- /dev/null +++ b/cjetreconstruction/src/popl.hpp @@ -0,0 +1,1331 @@ +/*** + ____ __ ____ __ + ( _ \ / \( _ \( ) + ) __/( O )) __// (_/\ + (__) \__/(__) \____/ + version 1.3.0 + https://github.com/badaix/popl + + This file is part of popl (program options parser lib) + Copyright (C) 2015-2021 Johannes Pohl + + This software may be modified and distributed under the terms + of the MIT license. See the LICENSE file for details. +***/ + +/// checked with clang-tidy: +/// run-clang-tidy-3.8.py -header-filter='.*' +/// -checks='*,-misc-definitions-in-headers,-google-readability-braces-around-statements,-readability-braces-around-statements,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-google-build-using-namespace,-google-build-using-namespace' + +#ifndef POPL_HPP +#define POPL_HPP + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WINDOWS +#include +#endif + + +namespace popl +{ + +#define POPL_VERSION "1.3.0" + + +/// Option's argument type +/** + * Switch has "no" argument + * Value has "required" argument + * Implicit has "optional" argument + */ +enum class Argument +{ + no = 0, // option never takes an argument + required, // option always requires an argument + optional // option may take an argument +}; + + +/// Option's attribute +/** + * inactive: Option is not set and will not be parsed + * hidden: Option is active, but will not show up in the help message + * required: Option must be set on the command line. Otherwise an exception will be thrown + * optional: Option must not be set. Default attribute. + * advanced: Option is advanced and will only show up in the advanced help message + * expoert: Option is expert and will only show up in the expert help message + */ +enum class Attribute +{ + inactive = 0, + hidden = 1, + required = 2, + optional = 3, + advanced = 4, + expert = 5 +}; + + +/// Option name type. Used in invalid_option exception. +/** + * unspecified: not specified + * short_name: The option's short name + * long_name: The option's long name + */ +enum class OptionName +{ + unspecified, + short_name, + long_name +}; + + +/// Abstract Base class for Options +/** + * Base class for Options + * holds just configuration data, no runtime data. + * Option is not bound to a special type "T" + */ +class Option +{ + friend class OptionParser; + +public: + /// Construct an Option + /// @param short_name the options's short name. Must be empty or one character. + /// @param long_name the option's long name. Can be empty. + /// @param description the Option's description that will be shown in the help message + Option(const std::string& short_name, const std::string& long_name, std::string description); + + /// Destructor + virtual ~Option() = default; + + /// default copy constructor + Option(const Option&) = default; + + /// default move constructor + Option(Option&&) = default; + + /// default assignement operator + Option& operator=(const Option&) = default; + + /// default move assignement operator + Option& operator=(Option&&) = default; + + /// Get the Option's short name + /// @return character of the options's short name or 0 if no short name is defined + char short_name() const; + + /// Get the Option's long name + /// @return the long name of the Option. Empty string if no long name is defined + std::string long_name() const; + + /// Get the Option's long or short name + /// @param what_name the option's name to return + /// @param what_hyphen preced the returned name with (double-)hypen + /// @return the requested name of the Option. Empty string if not defined. + std::string name(OptionName what_name, bool with_hypen = false) const; + + /// Get the Option's description + /// @return the description + std::string description() const; + + /// Get the Option's default value + /// @param out stream to write the default value to + /// @return true if a default value is available, false if not + virtual bool get_default(std::ostream& out) const = 0; + + /// Set the Option's attribute + /// @param attribute + void set_attribute(const Attribute& attribute); + + /// Get the Option's attribute + /// @return the Options's attribute + Attribute attribute() const; + + /// Get the Option's argument type + /// @return argument type (no, required, optional) + virtual Argument argument_type() const = 0; + + /// Check how often the Option is set on command line + /// @return the Option's count on command line + virtual size_t count() const = 0; + + /// Check if the Option is set + /// @return true if set at least once + virtual bool is_set() const = 0; + + +protected: + /// Parse the command line option and fill the internal data structure + /// @param what_name short or long option name + /// @param value the value as given on command line + virtual void parse(OptionName what_name, const char* value) = 0; + + /// Clear the internal data structure + virtual void clear() = 0; + + std::string short_name_; + std::string long_name_; + std::string description_; + Attribute attribute_; +}; + + + +/// Value option with optional default value +/** + * Value option with optional default value + * If set, it requires an argument + */ +template +class Value : public Option +{ +public: + /// Construct an Value Option + /// @param short_name the option's short name. Must be empty or one character. + /// @param long_name the option's long name. Can be empty. + /// @param description the Option's description that will be shown in the help message + Value(const std::string& short_name, const std::string& long_name, const std::string& description); + + /// Construct an Value Option + /// @param short_name the option's short name. Must be empty or one character. + /// @param long_name the option's long name. Can be empty. + /// @param description the Option's description that will be shown in the help message + /// @param default_val the Option's default value + /// @param assign_to pointer to a variable to assign the parsed command line value to + Value(const std::string& short_name, const std::string& long_name, const std::string& description, const T& default_val, T* assign_to = nullptr); + + size_t count() const override; + bool is_set() const override; + + /// Assign the last parsed command line value to "var" + /// @param var pointer to the variable where is value is written to + void assign_to(T* var); + + /// Manually set the Option's value. Deletes current value(s) + /// @param value the new value of the option + void set_value(const T& value); + + /// Get the Option's value. Will throw if option at index idx is not available + /// @param idx the zero based index of the value (if set multiple times) + /// @return the Option's value at index "idx" + T value(size_t idx = 0) const; + + /// Get the Option's value, return default_value if not set. + /// @param default_value return value if value is not set + /// @param idx the zero based index of the value (if set multiple times) + /// @return the Option's value at index "idx" or the default value or default_value + T value_or(const T& default_value, size_t idx = 0) const; + + /// Set the Option's default value + /// @param value the default value if not specified on command line + void set_default(const T& value); + + /// Check if the Option has a default value + /// @return true if the Option has a default value + bool has_default() const; + + /// Get the Option's default value. Will throw if no default is set. + /// @return the Option's default value + T get_default() const; + bool get_default(std::ostream& out) const override; + + Argument argument_type() const override; + +protected: + void parse(OptionName what_name, const char* value) override; + std::unique_ptr default_; + + virtual void update_reference(); + virtual void add_value(const T& value); + void clear() override; + + T* assign_to_; + std::vector values_; +}; + + + +/// Value option with implicit default value +/** + * Value option with implicit default value + * If set, an argument is optional + * -without argument it carries the implicit default value + * -with argument it carries the explicit value + */ +template +class Implicit : public Value +{ +public: + Implicit(const std::string& short_name, const std::string& long_name, const std::string& description, const T& implicit_val, T* assign_to = nullptr); + + Argument argument_type() const override; + +protected: + void parse(OptionName what_name, const char* value) override; +}; + + + +/// Value option without value +/** + * Value option without value + * Does not require an argument + * Can be either set or not set + */ +class Switch : public Value +{ +public: + Switch(const std::string& short_name, const std::string& long_name, const std::string& description, bool* assign_to = nullptr); + + void set_default(const bool& value) = delete; + Argument argument_type() const override; + +protected: + void parse(OptionName what_name, const char* value) override; +}; + + + +using Option_ptr = std::shared_ptr