diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 9d374e7..f3d33df 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -8,48 +8,53 @@ on: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Debug + BUILD_TYPE: Release permissions: id-token: write contents: read # This is required for actions/checkout@v2 jobs: - build_centos7: - container: kreisl/rootcpp17 - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Configure CMake - run: source /opt/rh/devtoolset-8/enable && cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DAnalysisTreeQA_BUILD_TESTS=ON -DAnalysisTreeQA_BUNDLED_AT=ON - - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - name: Test - run: cd ${{github.workspace}}/build && ctest -C ${{env.BUILD_TYPE}} - - build_fedora35: - container: rootproject/root:6.24.06-fedora35 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DAnalysisTreeQA_BUILD_TESTS=ON -DAnalysisTreeQA_BUNDLED_AT=ON - - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - name: Test - run: cd ${{github.workspace}}/build && ctest -C ${{env.BUILD_TYPE}} - build_fedora34: container: rootproject/root:6.22.08-fedora34 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DAnalysisTreeQA_BUILD_TESTS=ON -DAnalysisTreeQA_BUNDLED_AT=ON - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Test run: cd ${{github.workspace}}/build && ctest -C ${{env.BUILD_TYPE}} - - + +# build_fedora35: +# container: rootproject/root:6.24.06-fedora35 +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# with: +# fetch-depth: 0 +# - name: Configure CMake +# run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DAnalysisTreeQA_BUILD_TESTS=ON -DAnalysisTreeQA_BUNDLED_AT=ON +# - name: Build +# run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} +# - name: Test +# run: cd ${{github.workspace}}/build && ctest -C ${{env.BUILD_TYPE}} +# + + +# build_centos7: +# container: kreisl/rootcpp17 +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# with: +# fetch-depth: 0 +# - name: Configure CMake +# run: source /opt/rh/devtoolset-8/enable && cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DAnalysisTreeQA_BUILD_TESTS=ON -DAnalysisTreeQA_BUNDLED_AT=ON +# - name: Build +# run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} +# - name: Test +# run: cd ${{github.workspace}}/build && ctest -C ${{env.BUILD_TYPE}} diff --git a/AnalysisTreeQAConfig.cmake.in b/AnalysisTreeQAConfig.cmake.in index aef4778..6baabb7 100644 --- a/AnalysisTreeQAConfig.cmake.in +++ b/AnalysisTreeQAConfig.cmake.in @@ -1,11 +1,14 @@ -include("${CMAKE_CURRENT_LIST_DIR}/AnalysisTreeQATargets.cmake") - -@PACKAGE_INIT@ - -message ("-- Found AnalysisTreeQA...") - -set(AnalysisTreeQA_INCLUDE_DIR "@PACKAGE_AnalysisTreeQA_INCLUDE_DIR@") -message ("AnalysisTreeQA_INCLUDE_DIR = ${AnalysisTreeQA_INCLUDE_DIR}") - -set(AnalysisTreeQA_LIBRARY_DIR "@PACKAGE_AnalysisTreeQA_LIBRARY_DIR@") -message ("AnalysisTreeQA_LIBRARY_DIR = ${AnalysisTreeQA_LIBRARY_DIR}") +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + URL "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip" + SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index aa15a76..948f400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,25 +1,25 @@ -# Minimum 3.5 version is determined by cmake -E copy_if_different command -# that was used to copy headers to the build directory: -# multiple files are supported since cmake 3.5 -cmake_minimum_required(VERSION 3.5) -project(AnalysisTreeQA CXX) -set(PROJECT_VERSION 1.1) - -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules) -# -# Options -# -set(AnalysisTreeQA_BUILD_TASKS OFF CACHE BOOL "Build user' AnalysisTreeQA tasks (tasks/)") -set(AnalysisTreeQA_BUILD_EXAMPLES ON CACHE BOOL "Build AnalysisTreeQA examples (examples/)") -set(AnalysisTreeQA_BUILD_TESTS OFF CACHE BOOL "Build tests for AnalysisTreeQA") -set(AnalysisTreeQA_BUNDLED_AT ON CACHE BOOL "Get and build AnalysisTree") -set(AnalysisTreeQA_BUNDLED_AT_VERSION "v2.2.5" CACHE STRING "Bundled AnalysisTree version") -set(AnalysisTreeQA_BUNDLED_CUTS ON CACHE BOOL "Get and build AnalysisTreeCuts") -set(AnalysisTreeQA_BUNDLED_CUTS_VERSION "v0.0.1" CACHE STRING "Bundled AnalysisTreeCuts version") +cmake_minimum_required(VERSION 3.14) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +include(MunkeiVersionFromGitATQA) +version_from_git( + LOG ON + TIMESTAMP "%Y%m%d%H%M%S" +) + +project(AnalysisTreeQA VERSION ${VERSION} LANGUAGES CXX) + +option(AnalysisTreeQA_BUILD_EXAMPLES "Build AnalysisTreeQA examples (examples/)" ON) +option(AnalysisTreeQA_BUILD_TESTS "Build tests for AnalysisTreeQA" ON) +option(AnalysisTreeQA_BUNDLED_AT "Get and build AnalysisTree" ON) +option(yaml-cpp_BUNDLED "Build bundled yaml-cpp" ON) + +set(AnalysisTreeQA_BUNDLED_AT_VERSION "v2.2.6" CACHE STRING "Bundled AnalysisTree version") # by default build optimized code if(NOT DEFINED CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE RELEASE) + set(CMAKE_BUILD_TYPE Release) endif() if(NOT DEFINED CMAKE_CXX_STANDARD) @@ -27,53 +27,28 @@ if(NOT DEFINED CMAKE_CXX_STANDARD) endif() # in DEBUG mode make verbose Makefile -if (CMAKE_BUILD_TYPE MATCHES DEBUG) +if (CMAKE_BUILD_TYPE MATCHES Debug) set(CMAKE_VERBOSE_MAKEFILE ON) endif () -if(CMAKE_CXX_STANDARD LESS 17) - find_package(Boost REQUIRED) -endif() - -if(Boost_FOUND) - message(STATUS "Boost version ${Boost_VERSION_STRING} is found!") - message(STATUS "Boost include dir: ${Boost_INCLUDE_DIRS}") -endif() set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb -g -DDEBUG -D__DEBUG -Wall -Wextra") +set(CMAKE_CXX_FLAGS_Debug "-O0 -ggdb -g -DDEBUG -D__DEBUG -Wall -Wextra") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ftree-vectorize -ffast-math -DNODEBUG") +set(CMAKE_CXX_FLAGS_Release "-O3 -ftree-vectorize -ffast-math -DNODEBUG") message(STATUS "Using CXX flags for ${CMAKE_BUILD_TYPE}: ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") -list(APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS}) -list(APPEND CMAKE_PREFIX_PATH ${ROOTSYS}) -find_package(ROOT REQUIRED RIO) - -message(STATUS "Using ROOT: ${ROOT_VERSION} <${ROOT_CONFIG}>") -include_directories(${CMAKE_SOURCE_DIR} ${ROOT_INCLUDE_DIR} ${ROOT_INCLUDE_DIRS}) -include(${ROOT_USE_FILE}) - -set(EXTERNAL_DIR ${CMAKE_BINARY_DIR}/external) -set(EXTERNAL_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/external) - -include_directories(${CMAKE_SOURCE_DIR} ${AnalysisTree_INCLUDE_DIR} ${PROJECT_INCLUDE_DIRECTORIES}) +include(ROOT) +include(AnalysisTree) +include(YamlCpp) +include(Boost) add_subdirectory(src) - -if(AnalysisTreeQA_BUILD_TASKS) - add_subdirectory(tasks) -endif() - +add_subdirectory(test) if(AnalysisTreeQA_BUILD_EXAMPLES) add_subdirectory(examples) endif() -if(AnalysisTreeQA_BUILD_TESTS) - message("Compiling tests") - include(GoogleTest) - enable_testing() - add_subdirectory(test) -endif() - include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/AnalysisTreeQA/AnalysisTreeQAConfigVersion.cmake" diff --git a/README.md b/README.md index 1be7add..7f3e241 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,13 @@ To apply the flag use -D{Name}={value}, for example, if you want to compile usin cmake -DCMAKE_CXX_STANDARD=11 ../ | Name | Default value | Possible values | -| ------------- | ------------- | ---------- | -| CMAKE_BUILD_TYPE | RELEASE | RELEASE/DEBUG | -| CMAKE_CXX_STANDARD | 17 | 11/14/17 | -| AnalysisTreeQA_BUILD_TESTS | ON | ON/OFF | -| AnalysisTreeQA_BUILD_TASKS | OFF | ON/OFF | -| AnalysisTreeQA_BUNDLED_AT | ON | ON/OFF | -| AnalysisTreeQA_BUNDLED_AT_VERSION | master | master/v2.0.1/... | +| ------------- |---------------| ---------- | +| CMAKE_BUILD_TYPE | RELEASE | RELEASE/DEBUG | +| CMAKE_CXX_STANDARD | 17 | 11/14/17 | +| AnalysisTreeQA_BUILD_TESTS | OFF | ON/OFF | +| AnalysisTreeQA_BUILD_TASKS | OFF | ON/OFF | +| AnalysisTreeQA_BUNDLED_AT | ON | ON/OFF | +| AnalysisTreeQA_BUNDLED_AT_VERSION | master | master/v2.0.1/... | ## Usage diff --git a/cmake_modules/AnalysisTree.cmake b/cmake/AnalysisTree.cmake similarity index 54% rename from cmake_modules/AnalysisTree.cmake rename to cmake/AnalysisTree.cmake index bd4e0c3..a4e8607 100644 --- a/cmake_modules/AnalysisTree.cmake +++ b/cmake/AnalysisTree.cmake @@ -4,14 +4,19 @@ if (AnalysisTreeQA_BUNDLED_AT) FetchContent_Declare( AnalysisTree - GIT_REPOSITORY "https://github.com/HeavyIonAnalysis/AnalysisTree.git" - GIT_TAG ${AnalysisTreeQA_BUNDLED_AT_VERSION} + GIT_REPOSITORY "https://github.com/viktorklochkov/AnalysisTree.git" +# GIT_TAG ${AnalysisTreeQA_BUNDLED_AT_VERSION} + GIT_TAG cmake GIT_SHALLOW ON ) FetchContent_MakeAvailable(AnalysisTree) + list(APPEND PROJECT_INCLUDE_DIRECTORIES ${AnalysisTree_BINARY_DIR}/include) + message(STATUS "AT: ${AnalysisTree_BINARY_DIR} ${AnalysisTree_SOURCE_DIR}") else() + find_package(AnalysisTree REQUIRED) list(APPEND CMAKE_PREFIX_PATH ${ANALYSISTREE_HOME}) list(APPEND CMAKE_PREFIX_PATH $ENV{ANALYSISTREE_HOME}) - find_package(AnalysisTree REQUIRED) list(APPEND PROJECT_INCLUDE_DIRECTORIES ${AnalysisTree_INCLUDE_DIR}) endif() + +list(APPEND PROJECT_LINK_LIBRARIES AnalysisTreeBase AnalysisTreeInfra) diff --git a/cmake/Boost.cmake b/cmake/Boost.cmake new file mode 100644 index 0000000..a0eb4c6 --- /dev/null +++ b/cmake/Boost.cmake @@ -0,0 +1,11 @@ +if(CMAKE_CXX_STANDARD LESS 17) + find_package(Boost REQUIRED) +endif() + +if(Boost_FOUND) + message(STATUS "Boost version ${Boost_VERSION_STRING} is found!") + message(STATUS "Boost include dir: ${Boost_INCLUDE_DIRS}") + message(STATUS "Boost lib dir: ${Boost_LIBRARY_DIRS}") + list(APPEND PROJECT_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR}}) + list(APPEND PROJECT_LINK_DIRECTORIES ${Boost_LIBRARY_DIRS}}) +endif() \ No newline at end of file diff --git a/cmake/MunkeiVersionFromGitATQA.cmake b/cmake/MunkeiVersionFromGitATQA.cmake new file mode 100644 index 0000000..15ab0d2 --- /dev/null +++ b/cmake/MunkeiVersionFromGitATQA.cmake @@ -0,0 +1,167 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016-2017 Theo Willows +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required( VERSION 3.0.0 ) + +include( CMakeParseArguments ) + +function( version_from_git ) + # Parse arguments + set( options OPTIONAL FAST ) + set( oneValueArgs + GIT_EXECUTABLE + INCLUDE_HASH + LOG + TIMESTAMP + ) + set( multiValueArgs ) + cmake_parse_arguments( ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + # Defaults + if( NOT DEFINED ARG_INCLUDE_HASH ) + set( ARG_INCLUDE_HASH ON ) + endif() + + if( DEFINED ARG_GIT_EXECUTABLE ) + set( GIT_EXECUTABLE "${ARG_GIT_EXECUTABLE}" ) + else () + # Find Git or bail out + find_package( Git ) + if( NOT GIT_FOUND ) + message( FATAL_ERROR "[MunkeiVersionFromGit] Git not found" ) + endif( NOT GIT_FOUND ) + endif() + + # Git describe + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_describe + ERROR_VARIABLE git_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ) + if( NOT git_result EQUAL 0 ) + message( FATAL_ERROR + "[MunkeiVersionFromGit] Failed to execute Git: ${git_error}" + ) + endif() + + # Get Git tag + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags --abbrev=0 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_tag + ERROR_VARIABLE git_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ) + if( NOT git_result EQUAL 0 ) + message( FATAL_ERROR + "[MunkeiVersionFromGit] Failed to execute Git: ${git_error}" + ) + endif() + + if( git_tag MATCHES "^v(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$" ) + set( version_major "${CMAKE_MATCH_1}" ) + set( version_minor "${CMAKE_MATCH_2}" ) + set( version_patch "${CMAKE_MATCH_3}" ) + set( identifiers "${CMAKE_MATCH_4}" ) + set( metadata "${CMAKE_MATCH_5}" ) + else() + message( FATAL_ERROR + "[MunkeiVersionFromGit] Git tag isn't valid semantic version: [${git_tag}]" + ) + endif() + + if( "${git_tag}" STREQUAL "${git_describe}" ) + set( git_at_a_tag ON ) + endif() + + if( NOT git_at_a_tag ) + # Extract the Git hash (if one exists) + string( REGEX MATCH "g[0-9a-f]+$" git_hash "${git_describe}" ) + endif() + + # Construct the version variables + set( version ${version_major}.${version_minor}.${version_patch} ) + set( semver ${version} ) + + # Identifiers + if( identifiers MATCHES ".+" ) + string( SUBSTRING "${identifiers}" 1 -1 identifiers ) + set( semver "${semver}-${identifiers}") + endif() + + # Metadata + # TODO Split and join (add Git hash inbetween) + if( metadata MATCHES ".+" ) + string( SUBSTRING "${metadata}" 1 -1 metadata ) + # Split + string( REPLACE "." ";" metadata "${metadata}" ) + endif() + + if( NOT git_at_a_tag ) + + if( ARG_INCLUDE_HASH ) + list( APPEND metadata "${git_hash}" ) + endif( ARG_INCLUDE_HASH ) + + # Timestamp + if( DEFINED ARG_TIMESTAMP ) + string( TIMESTAMP timestamp "${ARG_TIMESTAMP}" ${ARG_UTC} ) + list( APPEND metadata "${timestamp}" ) + endif( DEFINED ARG_TIMESTAMP ) + + endif() + + # Join + string( REPLACE ";" "." metadata "${metadata}" ) + + if( metadata MATCHES ".+" ) + set( semver "${semver}+${metadata}") + endif() + + # Log the results + if( ARG_LOG ) + message( STATUS + "[MunkeiVersionFromGit] Version: ${version} + Git tag: [${git_tag}] + Git hash: [${git_hash}] + Decorated: [${git_describe}] + Identifiers: [${identifiers}] + Metadata: [${metadata}] + SemVer: [${semver}]" + ) + endif( ARG_LOG ) + + # Set parent scope variables + set( GIT_TAG ${git_tag} PARENT_SCOPE ) + set( SEMVER ${semver} PARENT_SCOPE ) + set( VERSION ${version} PARENT_SCOPE ) + set( VERSION_MAJOR ${version_major} PARENT_SCOPE ) + set( VERSION_MINOR ${version_minor} PARENT_SCOPE ) + set( VERSION_PATCH ${version_patch} PARENT_SCOPE ) + +endfunction( version_from_git ) diff --git a/cmake/ROOT.cmake b/cmake/ROOT.cmake new file mode 100644 index 0000000..cb38a21 --- /dev/null +++ b/cmake/ROOT.cmake @@ -0,0 +1,10 @@ +find_package(ROOT REQUIRED RIO) + +list(APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS}) +list(APPEND CMAKE_PREFIX_PATH ${ROOTSYS}) + +message(STATUS "Using ROOT: ${ROOT_VERSION} <${ROOT_CONFIG}>") + +list(APPEND PROJECT_INCLUDE_DIRECTORIES ${ROOT_INCLUDE_DIR} ${ROOT_INCLUDE_DIRS}) + +include(${ROOT_USE_FILE}) diff --git a/cmake/YamlCpp.cmake b/cmake/YamlCpp.cmake new file mode 100644 index 0000000..f0a6dc1 --- /dev/null +++ b/cmake/YamlCpp.cmake @@ -0,0 +1,23 @@ +if (yaml-cpp_BUNDLED) + message("-- Building bundled yaml-cpp package...") + include(FetchContent) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + + FetchContent_Declare( + YAML_CPP + GIT_REPOSITORY "https://github.com/jbeder/yaml-cpp.git" + GIT_TAG "yaml-cpp-0.6.3" + GIT_SHALLOW ON + ) + set(YAML_CPP_BUILD_TESTS OFF) + set(YAML_CPP_INSTALL ON) + FetchContent_MakeAvailable(YAML_CPP) + + list(APPEND PROJECT_INCLUDE_DIRECTORIES ${YAML_CPP_SOURCE_DIR}/include) +else() + find_package(yaml-cpp REQUIRED) + list(APPEND PROJECT_INCLUDE_DIRECTORIES ${YAML_CPP_INCLUDE_DIR}) + message(STATUS "YAML_CPP_INCLUDE_DIR: ${YAML_CPP_INCLUDE_DIR}") +endif () + +#list(APPEND PROJECT_LINK_LIBRARIES yaml-cpp) diff --git a/cmake_modules/AnalysisTreeCuts.cmake b/cmake_modules/AnalysisTreeCuts.cmake deleted file mode 100644 index 619c85b..0000000 --- a/cmake_modules/AnalysisTreeCuts.cmake +++ /dev/null @@ -1,20 +0,0 @@ -if (AnalysisTreeQA_BUNDLED_CUTS) - message("-- Building bundled AnalysisTreeCuts") - - include(FetchContent) - - FetchContent_Declare(AnalysisTreeCuts - GIT_REPOSITORY "https://git.cbm.gsi.de/pwg-c2f/analysis/cuts.git" - # GIT_VERSION ${AnalysisTreeQA_BUNDLED_CUTS_VERSION} - GIT_SHALLOW ON - ) - FetchContent_GetProperties(AnalysisTreeCuts) - if(NOT analysistreecuts_POPULATED) - FetchContent_Populate(AnalysisTreeCuts) - add_subdirectory(${analysistreecuts_SOURCE_DIR} ${analysistreecuts_BINARY_DIR}) - endif() - - add_library(AnalysisTreeCuts_Static INTERFACE) - target_include_directories(AnalysisTreeCuts_Static INTERFACE ${analysistreecuts_SOURCE_DIR}) - -endif () \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6ecb436..6851ce3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,10 +1,8 @@ -include(AnalysisTree) - if(Boost_FOUND) include_directories(${PROJECT_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIR}) link_directories(${PROJECT_LINK_DIRECTORIES} ${Boost_LIBRARY_DIRS}) else() - include_directories(${PROJECT_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR}) + include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_INCLUDE_DIRECTORIES}) link_directories(${PROJECT_LINK_DIRECTORIES}) endif() @@ -17,4 +15,8 @@ add_executable(example example.cpp) add_dependencies(example AnalysisTreeQA) target_link_libraries(example ${ROOT_LIBRARIES} AnalysisTreeQA AnalysisTreeBase AnalysisTreeInfra) +add_executable(example_yaml example_yaml.cpp) +add_dependencies(example_yaml AnalysisTreeQA) +target_link_libraries(example_yaml ${ROOT_LIBRARIES} AnalysisTreeQA AnalysisTreeBase AnalysisTreeInfra ${YAML_CPP_LIBRARIES}) + install (TARGETS example RUNTIME DESTINATION bin) \ No newline at end of file diff --git a/examples/example.cpp b/examples/example.cpp index ff2c463..bbdce5e 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -17,7 +17,7 @@ void example(const std::string& filelist){ task->AddH1({"p_{T}, GeV/c", Variable::FromString("VtxTracks.pT"), {100, 0, 3}}); // 1D histo with cut - Cuts* pT_cut = new Cuts("pT_cut", {RangeCut("VtxTracks.pT", 1, 1.5)}); + Cuts* pT_cut = new Cuts("pT_cut", {RangeCut("VtxTracks.pT", 0, 1.5)}); task->AddH1({"p_{T}, GeV/c", Variable::FromString("VtxTracks.pT"), {100, 0, 3}}, pT_cut); // AnalysisTree::Variable in case of more complicated plot diff --git a/examples/example.yaml b/examples/example.yaml new file mode 100644 index 0000000..d5c9dc1 --- /dev/null +++ b/examples/example.yaml @@ -0,0 +1,44 @@ +1D: +# Simplest way to define plot +- x_axis: + field: "SimParticles.pT" + bins: [100, 0, 3] +# Better way to define plot (cuts are defined in C++ code) +- name: "sim_pT_y_1.5_3" + x_axis: &sim_pT # anchor could be reused later + title: "p_{T}, GeV/c" + field: "SimParticles.pT" + bins: [100, 0, 3] + cuts: "sim_y_cut" +# Plot by variable name (defined in C++ code) +- name: "pxpy" + x_axis: + field: "pxpy" + bin_edges: [-3, -1.5, -0.5, 0, 0.5, 1.5, 3] +2D: +- name: "sim_pT_y" + x_axis: &sim_y + title: "#it{y}" + field: "SimParticles.rapidity" + bins: [100, -2, 2] + y_axis: *sim_pT +Profile: +- name: "pT_y_profile" + x_axis: *sim_pT + y_axis: *sim_y +Integral: +- name: "pT_integral" + x_axis: &sim_pT_integral + title: "#sum{p_{T}, GeV/c}" + field: "SimParticles.pT" + bins: [100, 0, 30] + cuts: "sim_y_cut" +Integral2D: +- name: "pT_M_integral" + x_axis: + title: "M" + field: "SimParticles.ones" + bins: [100, 70, 130] + y_axis: *sim_pT_integral + y_cuts: "sim_y_cut" + diff --git a/examples/example_yaml.cpp b/examples/example_yaml.cpp new file mode 100644 index 0000000..9b8300e --- /dev/null +++ b/examples/example_yaml.cpp @@ -0,0 +1,44 @@ +#include + +#include "AnalysisTree/TaskManager.hpp" +#include "AnalysisTree/Variable.hpp" + +#include "Task.hpp" +#include "YamlReader.hpp" + +using namespace AnalysisTree; + +void example(const std::string& filelist){ + auto* man = TaskManager::GetInstance(); + + auto* task = new QA::Task; + task->SetOutputFileName("cbm_qa.root"); + + Variable pxpy("pxpy", {{"RecTracks", "px"}, {"RecTracks", "py"}}, []( std::vector& var ) { return var.at(0)*var.at(1); }); + + task->AddCut(new Cuts("sim_y_cut", {RangeCut("SimParticles.rapidity", 1.5, 3)})); + task->AddCut(new Cuts("rec_pT_cut", {RangeCut("RecTracks.pT", 1, 1.5)})); + task->AddVariable(pxpy); + + QA::YamlReader r("/Users/viktor/Soft/AnalysisTreeQA/examples/example.yaml"); + r.AddPlotsFromYaml(task); + + man->AddTask(task); + + man->Init({filelist}, {"tTree"}); + man->Run(-1); + man->Finish(); +} + +int main(int argc, char* argv[]){ + if (argc < 2) { + std::cout << "Error! Please use " << std::endl; + std::cout << " ./example filename" << std::endl; + exit(EXIT_FAILURE); + } + + const std::string filename = argv[1]; + example(filename); + + return 0; +} \ No newline at end of file diff --git a/src/Axis.cpp b/src/Axis.cpp new file mode 100644 index 0000000..45c5477 --- /dev/null +++ b/src/Axis.cpp @@ -0,0 +1,16 @@ +#include "Axis.hpp" + +namespace AnalysisTree { +namespace QA { + +using std::cout; +using std::endl; +using std::setw; + +void Axis::Print(Option_t*) const { + cout << setw(25) << this->name_ << " "; + cout << setw(6) << this->GetNbins() << setw(6) << this->GetXmin() << setw(6) << this->GetXmax(); +} + +} +} \ No newline at end of file diff --git a/src/Axis.hpp b/src/Axis.hpp new file mode 100644 index 0000000..1f04ca8 --- /dev/null +++ b/src/Axis.hpp @@ -0,0 +1,28 @@ +#ifndef ANALYSISTREEQA_AXIS_HPP +#define ANALYSISTREEQA_AXIS_HPP + +#include "TAxis.h" +#include "AnalysisTree/Variable.hpp" + +namespace AnalysisTree { +namespace QA { + +class Axis : public Variable, public TAxis { + public: + Axis() = default; + Axis(const std::string& title, const Variable& var, const TAxis& a) : Variable(var), TAxis(a) { + this->SetTitle(title.c_str()); + if(this->GetFields().size() == 1 && this->GetFields().at(0).GetName() == "ones"){ + this->lambda_ = [](const std::vector& ){ return 1; }; + this->name_ = this->GetBranchName() + ".ones"; + } + } + const char* GetName() const override { return Variable::GetName().c_str(); } + void Print(Option_t *option="") const override ; + + protected: + ClassDefOverride(Axis, 1); +}; +} +} +#endif//ANALYSISTREEQA_AXIS_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee90134..0c787c9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,39 +1,25 @@ -include(AnalysisTree) - set(SOURCES EntryConfig.cpp Task.cpp -) + YamlReader.cpp + Axis.cpp) string(REPLACE ".cpp" ".hpp" HEADERS "${SOURCES}") list(APPEND HEADERS "BasicQA.hpp") -add_library(AnalysisTreeQA SHARED ${SOURCES} G__AnalysisTreeQA.cxx) - -if (Boost_FOUND) - include_directories(${CMAKE_SOURCE_DIR}/src ${PROJECT_INCLUDE_DIRECTORIES} ${Boost_INCLUDE_DIR}) - link_directories(${PROJECT_LINK_DIRECTORIES} ${Boost_LIBRARY_DIRS}) -else () - include_directories(${CMAKE_SOURCE_DIR}/src ${PROJECT_INCLUDE_DIRECTORIES}) - link_directories(${PROJECT_LINK_DIRECTORIES}) -endif () +include_directories(${PROJECT_INCLUDE_DIRECTORIES}) -#add_dependencies(AnalysisTreeQA ${PROJECT_DEPENDENCIES}) +add_library(AnalysisTreeQA SHARED ${SOURCES} G__AnalysisTreeQA.cxx) +target_link_libraries(AnalysisTreeQA PUBLIC ${PROJECT_LINK_LIBRARIES} yaml-cpp) +add_dependencies(AnalysisTreeQA AnalysisTreeInfra) +target_include_directories(AnalysisTreeQA PUBLIC $) ROOT_GENERATE_DICTIONARY(G__AnalysisTreeQA ${HEADERS} LINKDEF AnalysisTreeQALinkDef.h OPTIONS -I${CMAKE_BINARY_DIR}/include ) -target_link_libraries(AnalysisTreeQA - PUBLIC - AnalysisTreeBase - AnalysisTreeInfra - ) -target_include_directories(AnalysisTreeQA - PUBLIC - $ - ) + install(TARGETS AnalysisTreeQA EXPORT AnalysisTreeQATargets LIBRARY DESTINATION lib diff --git a/src/EntryConfig.cpp b/src/EntryConfig.cpp index 2b56c6d..38c78b2 100644 --- a/src/EntryConfig.cpp +++ b/src/EntryConfig.cpp @@ -8,6 +8,11 @@ namespace AnalysisTree { namespace QA { +using std::cout; +using std::endl; +using std::setw; +using std::left; + struct fill_struct : public Utils::Visitor { fill_struct(double val1, double val2) : val1_(val1), val2_(val2) {} void operator()(TH1*) const { throw std::runtime_error("Cannot apply Fill(va1, val2) to TH1"); } @@ -23,66 +28,71 @@ struct write_struct : public Utils::Visitor { std::string name_; }; -EntryConfig::EntryConfig(const Axis& axis, Cuts* cuts, bool is_integral) - : name_(axis.GetName()), +EntryConfig::EntryConfig(const Axis& axis, Cuts* cuts, bool is_integral, const std::string& name) + : name_(name.empty() ? axis.GetName() : name), type_(is_integral ? PlotType::kIntegral1D : PlotType::kHisto1D), axes_({axis}), entry_cuts_(cuts) { - if (cuts) + if (cuts && name.empty()) name_ += "_" + cuts->GetName(); - if(is_integral){ + if(is_integral && name.empty()){ name_ += "_integral"; } InitPlot(); + Print(); } -EntryConfig::EntryConfig(const Axis& x, const Axis& y, Cuts* cuts, bool is_profile) : type_(is_profile ? PlotType::kProfile : PlotType::kHisto2D), - axes_({x, y}), - entry_cuts_(cuts) { - Set2DName(); +EntryConfig::EntryConfig(const Axis& x, const Axis& y, Cuts* cuts, bool is_profile, const std::string& name) + : type_(is_profile ? PlotType::kProfile : PlotType::kHisto2D), + axes_({x, y}), + entry_cuts_(cuts) +{ + name_ = name.empty() ? Construct2DName() : name; InitPlot(); + Print(); } -EntryConfig::EntryConfig(const Axis& x, Cuts* cuts_x, const Axis& y, Cuts* cuts_y) : type_(PlotType::kIntegral2D), - axes_({x, y}), - entry_cuts_(cuts_x) { - Set2DName(); +EntryConfig::EntryConfig(const Axis& x, Cuts* cuts_x, const Axis& y, Cuts* cuts_y, const std::string& name) + : type_(PlotType::kIntegral2D), + axes_({x, y}), + entry_cuts_(cuts_x) +{ + name_ = name.empty() ? Construct2DName() : name; InitPlot(); + Print(); } - - TH1* EntryConfig::CreateHisto1D() const { - auto* ret = new TH1F(name_.c_str(), title_.c_str(), - axes_.at(0).GetNbins(), axes_.at(0).GetXmin(), axes_.at(0).GetXmax()); - ret->SetXTitle(axes_.at(0).GetTitle()); + auto x = axes_.at(0); + TH1* ret = x.IsVariableBinSize() ? + new TH1F(name_.c_str(), title_.c_str(), x.GetNbins(), x.GetXbins()->GetArray()) : + new TH1F(name_.c_str(), title_.c_str(), x.GetNbins(), x.GetXmin(), x.GetXmax()); + ret->SetXTitle(x.GetTitle()); ret->SetYTitle("Entries"); return ret; } -// TODO fix axes + TProfile* EntryConfig::CreateProfile() const { + auto x{axes_.at(0)}, y{axes_.at(1)}; + auto ret = x.IsVariableBinSize() ? + new TProfile(name_.c_str(), title_.c_str(), x.GetNbins(), x.GetXbins()->GetArray()) : + new TProfile(name_.c_str(), title_.c_str(), x.GetNbins(), x.GetXmin(), x.GetXmax()); - TProfile* ret{nullptr}; - if (axes_[1].GetNbins() == 1 && axes_[1].GetXmax() == 1. && axes_[1].GetXmin() == 0.) {// Not init by user - ret = new TProfile(name_.c_str(), title_.c_str(), - axes_.at(0).GetNbins(), axes_.at(0).GetXmin(), axes_.at(0).GetXmax()); - } else if (axes_.size() == 2) { - ret = new TProfile(name_.c_str(), title_.c_str(), - axes_.at(0).GetNbins(), axes_.at(0).GetXmin(), axes_.at(0).GetXmax(), - axes_.at(1).GetXmin(), axes_.at(1).GetXmax()); - } - ret->SetYTitle(axes_.at(1).GetTitle()); - ret->SetXTitle(axes_.at(0).GetTitle()); + ret->SetXTitle(x.GetTitle()); + ret->SetYTitle(y.GetTitle()); return ret; } TH2* EntryConfig::CreateHisto2D() const { + auto x{axes_.at(0)}, y{axes_.at(1)}; + assert(x.IsVariableBinSize() && y.IsVariableBinSize() || !x.IsVariableBinSize() && !y.IsVariableBinSize()); + + auto* ret = x.IsVariableBinSize() ? + new TH2F(name_.c_str(), title_.c_str(), x.GetNbins(), x.GetXbins()->GetArray(), y.GetNbins(), y.GetXbins()->GetArray()) : + new TH2F(name_.c_str(), title_.c_str(), x.GetNbins(), x.GetXmin(), x.GetXmax(), y.GetNbins(), y.GetXmin(), y.GetXmax()); - auto* ret = new TH2F(name_.c_str(), title_.c_str(), - axes_.at(0).GetNbins(), axes_.at(0).GetXmin(), axes_.at(0).GetXmax(), - axes_.at(1).GetNbins(), axes_.at(1).GetXmin(), axes_.at(1).GetXmax()); - ret->SetXTitle(axes_.at(0).GetTitle()); - ret->SetYTitle(axes_.at(1).GetTitle()); + ret->SetXTitle(x.GetTitle()); + ret->SetYTitle(y.GetTitle()); ret->SetZTitle("Entries"); ret->SetMinimum(1); return ret; @@ -116,14 +126,15 @@ void EntryConfig::InitPlot() { } } -void EntryConfig::Set2DName() { - name_ = Form("%s_%s", axes_[0].GetName(), axes_[1].GetName()); +std::string EntryConfig::Construct2DName() { + std::string name = Form("%s_%s", axes_[0].GetName(), axes_[1].GetName()); if (entry_cuts_ != nullptr) - name_ += "_" + entry_cuts_->GetName(); + name += "_" + entry_cuts_->GetName(); if (type_ == PlotType::kProfile) { - name_ += "_profile"; + name += "_profile"; } + return name; } void EntryConfig::Fill(double value1, double value2) { @@ -155,5 +166,23 @@ std::string EntryConfig::GetDirectoryName() const { return name; } +void EntryConfig::Print() const { + cout << left; + switch (type_) { + case PlotType::kHisto1D : cout << setw(12) << "1D histo"; break; + case PlotType::kHisto2D : cout << setw(12) << "2D histo"; break; + case PlotType::kProfile : cout << setw(12) << "Profile"; break; + case PlotType::kIntegral1D : cout << setw(12) << "Integral"; break; + case PlotType::kIntegral2D : cout << setw(12) << "2D integral"; break; + } + for(const auto& axis : axes_){ + axis.Print(); + } + if(entry_cuts_){ + cout << setw(15) << entry_cuts_->GetName(); + } + cout << endl; +} + }// namespace QA }// namespace AnalysisTree \ No newline at end of file diff --git a/src/EntryConfig.hpp b/src/EntryConfig.hpp index 411d2d2..e359de6 100644 --- a/src/EntryConfig.hpp +++ b/src/EntryConfig.hpp @@ -4,11 +4,11 @@ #include #include -#include - #include "AnalysisTree/Cuts.hpp" #include "AnalysisTree/Utils.hpp" +#include + class TH1; class TH2; class TProfile; @@ -16,24 +16,6 @@ class TProfile; namespace AnalysisTree { namespace QA { -class Axis : public Variable, public TAxis { - public: - Axis() = default; - Axis(const std::string& title, const Variable& var, const TAxis& a) : Variable(var), TAxis(a) { - this->SetTitle(title.c_str()); - if(this->GetFields().size() == 1 && this->GetFields().at(0).GetName() == "ones"){ -// fields_[0] = Field(fields_[0].GetBranchName(), "ones"); -// fields_.clear(); - this->lambda_ = [](const std::vector& ){ return 1; }; - this->name_ = "Ones"; - } - } - const char* GetName() const override { return Variable::GetName().c_str(); } - - protected: - ClassDefOverride(Axis, 1); -}; - class EntryConfig { using PlotPointer = ANALYSISTREE_UTILS_VARIANT; @@ -48,9 +30,9 @@ class EntryConfig { }; EntryConfig() = default; - explicit EntryConfig(const Axis& axis, Cuts* cuts = nullptr, bool is_integral = false); - EntryConfig(const Axis& x, const Axis& y, Cuts* cuts = nullptr, bool is_profile = false); - EntryConfig(const Axis& x, Cuts* cuts_x, const Axis& y, Cuts* cuts_y); + explicit EntryConfig(const Axis& axis, Cuts* cuts = nullptr, bool is_integral = false, const std::string& name=""); + EntryConfig(const Axis& x, const Axis& y, Cuts* cuts = nullptr, bool is_profile = false, const std::string& name=""); + EntryConfig(const Axis& x, Cuts* cuts_x, const Axis& y, Cuts* cuts_y, const std::string& name=""); EntryConfig(const EntryConfig&) = default; EntryConfig(EntryConfig&&) = default; @@ -86,12 +68,14 @@ class EntryConfig { PlotPointer GetPlot() { return plot_; } + void Print() const; + protected: void InitPlot(); ANALYSISTREE_ATTR_NODISCARD TH1* CreateHisto1D() const; ANALYSISTREE_ATTR_NODISCARD TH2* CreateHisto2D() const; ANALYSISTREE_ATTR_NODISCARD TProfile* CreateProfile() const; - void Set2DName(); + std::string Construct2DName(); PlotPointer plot_; std::string name_;///< Name of the plot diff --git a/src/Task.hpp b/src/Task.hpp index 307a6f0..246231a 100644 --- a/src/Task.hpp +++ b/src/Task.hpp @@ -20,36 +20,38 @@ class Task : public AnalysisTask { void Exec() override; void Finish() override; - size_t AddH1(const Axis& x, Cuts* cuts = nullptr) { - entries_.emplace_back(EntryConfig(x, cuts, false)); + size_t AddH1(const Axis& x, Cuts* cuts = nullptr, const std::string& name = "") { + entries_.emplace_back(EntryConfig(x, cuts, false, name)); auto var_id = AddEntry(AnalysisEntry(entries_.back().GetVariables(), entries_.back().GetEntryCuts())); entries_.back().SetVariablesId({{var_id.first, var_id.second.at(0)}}); return entries_.size() - 1; } - size_t AddH2(const Axis& x, const Axis& y, Cuts* cuts = nullptr) { - entries_.emplace_back(EntryConfig(x, y, cuts)); + size_t AddH2(const Axis& x, const Axis& y, Cuts* cuts = nullptr, const std::string& name = "") { + entries_.emplace_back(EntryConfig(x, y, cuts, false, name)); auto var_id = AddEntry(AnalysisEntry(entries_.back().GetVariables(), entries_.back().GetEntryCuts())); entries_.back().SetVariablesId({ {var_id.first, var_id.second.at(0)}, {var_id.first, var_id.second.at(1)} }); return entries_.size() - 1; } - size_t AddProfile(const Axis& x, const Axis& y, Cuts* cuts = nullptr) { - entries_.emplace_back(EntryConfig(x, y, cuts, true)); + size_t AddProfile(const Axis& x, const Axis& y, Cuts* cuts = nullptr, const std::string& name = "") { + entries_.emplace_back(EntryConfig(x, y, cuts, true, name)); auto var_id = AddEntry(AnalysisEntry(entries_.back().GetVariables(), entries_.back().GetEntryCuts())); entries_.back().SetVariablesId({ {var_id.first, var_id.second.at(0)}, {var_id.first, var_id.second.at(1)} }); return entries_.size() - 1; } - size_t AddIntegral(const Axis& x, Cuts* cuts = nullptr) { - entries_.emplace_back(EntryConfig(x, cuts, true)); + size_t AddIntegral(const Axis& x, Cuts* cuts = nullptr, const std::string& name = "") { + entries_.emplace_back(EntryConfig(x, cuts, true, name)); auto var_id = AddEntry(AnalysisEntry(entries_.back().GetVariables(), entries_.back().GetEntryCuts())); entries_.back().SetVariablesId({{var_id.first, var_id.second.at(0)}}); return entries_.size() - 1; } - size_t AddIntegral(const Axis& x, const Axis& y, Cuts* cuts_x = nullptr, Cuts* cuts_y = nullptr) { - entries_.emplace_back(EntryConfig(x, cuts_x, y, cuts_y)); + size_t AddIntegral(const Axis& x, const Axis& y, + Cuts* cuts_x = nullptr, Cuts* cuts_y = nullptr, + const std::string& name = "") { + entries_.emplace_back(EntryConfig(x, cuts_x, y, cuts_y, name)); auto var_id_x = AddEntry(AnalysisEntry({entries_.back().GetVariables()[0]}, cuts_x)); auto var_id_y = AddEntry(AnalysisEntry({entries_.back().GetVariables()[1]}, cuts_y)); entries_.back().SetVariablesId({ {var_id_x.first, var_id_x.second.at(0)}, {var_id_y.first, var_id_y.second.at(0)} }); @@ -59,6 +61,42 @@ class Task : public AnalysisTask { std::vector& Entries() { return entries_; } void SetOutputFileName(std::string name) { out_file_name_ = std::move(name); } + void AddCut(Cuts* cut){ + auto lb = cuts_.lower_bound(cut->GetName()); + if(lb != cuts_.end() && !(cuts_.key_comp()(cut->GetName(), lb->first))) { + throw std::runtime_error("Cut already exists: " + cut->GetName()); + } + else { + cuts_.insert(lb, {cut->GetName(), cut}); + } + } + + void AddVariable(const Variable& variable){ + auto lb = vars_.lower_bound(variable.GetName()); + if(lb != vars_.end() && !(vars_.key_comp()(variable.GetName(), lb->first))) { + throw std::runtime_error("Variable already exists: " + variable.GetName()); + } + else { + vars_.insert(lb, {variable.GetName(), variable}); + } + } + + [[nodiscard]] Cuts* GetCut(const std::string& name) const { + auto it = cuts_.find(name); + if(it == cuts_.end()){ + throw std::runtime_error("Cut is not found: " + name); + } + return it->second; + } + + [[nodiscard]] Variable GetVariable(const std::string& name) const { + auto it = vars_.find(name); + if(it == vars_.end()){ + return Variable::FromString(name); + } + return it->second; + } + private: void FillIntegral(EntryConfig& plot); size_t AddAnalysisEntry(const Axis& a, Cuts* cuts, bool is_integral); @@ -68,6 +106,9 @@ class Task : public AnalysisTask { std::string out_file_name_{"QA.root"}; TFile* out_file_{nullptr}; + std::map cuts_{}; + std::map vars_{}; + ClassDefOverride(Task, 1); }; diff --git a/src/YamlReader.cpp b/src/YamlReader.cpp new file mode 100644 index 0000000..fec03f4 --- /dev/null +++ b/src/YamlReader.cpp @@ -0,0 +1,8 @@ +#include "YamlReader.hpp" + +namespace AnalysisTree { +namespace QA { + +//void YamlReader::Add1DHistos(const YAML::Node& node, Task* task) +} +} \ No newline at end of file diff --git a/src/YamlReader.hpp b/src/YamlReader.hpp new file mode 100644 index 0000000..c623d15 --- /dev/null +++ b/src/YamlReader.hpp @@ -0,0 +1,91 @@ +#ifndef ANALYSISTREEQA_YAMLREADER_HPP +#define ANALYSISTREEQA_YAMLREADER_HPP + +#include +#include +#include +#include +#include +#include "Task.hpp" + +namespace AnalysisTree { +namespace QA { + +class YamlReader { + public: + explicit YamlReader(std::string filename) : filename_(std::move(filename)) {} + + static Axis ConstructAxis(const YAML::Node& yaml_axis, Task* task){ + auto field = yaml_axis["field"].as(); + auto title = yaml_axis["title"] ? yaml_axis["title"].as() : field; + if(yaml_axis["bins"]){ + auto bins = yaml_axis["bins"].as>(); + return {title, task->GetVariable(field), + {static_cast(bins.at(0)), bins.at(1), bins.at(2)}}; + } + else if(yaml_axis["bin_edges"]){ + auto bins = yaml_axis["bin_edges"].as>(); + return {title, task->GetVariable(field), + TAxis(bins.size()-1, &(bins[0]))}; + } + throw std::runtime_error("No axis bins are specified: " + title); + } + + void AddPlotsFromYaml(Task* task){ + assert(!filename_.empty()); + YAML::Node config = YAML::LoadFile(filename_); + + Add1DPlot(config["1D"], task); + Add1DPlot(config["Integral"], task, true); + + Add2DPlot(config["2D"], task, EntryConfig::PlotType::kHisto2D); + Add2DPlot(config["Profile"], task, EntryConfig::PlotType::kProfile); + Add2DPlot(config["Integral2D"], task, EntryConfig::PlotType::kIntegral2D); + + } + + static void Add1DPlot(const YAML::Node& node, Task* task, bool is_integral = false) { + assert(task && node); + + for(auto plot : node){ + auto name = plot["name"] ? plot["name"].as() : ""; + auto cuts = plot["cuts"] ? plot["cuts"].as() : ""; + auto a = ConstructAxis(plot["x_axis"], task); + Cuts* c = !cuts.empty() ? task->GetCut(cuts) : nullptr; + is_integral ? task->AddIntegral(a, c, name) : task->AddH1(a, c, name); + } + } + + static void Add2DPlot(const YAML::Node& node, Task* task, EntryConfig::PlotType type) { + assert(task && node); + + for(auto plot : node) { + auto name = plot["name"] ? plot["name"].as() : ""; + auto x = ConstructAxis(plot["x_axis"], task); + auto y = ConstructAxis(plot["y_axis"], task); + + auto cuts = plot["cuts"] ? plot["cuts"].as() : ""; + auto cuts_x = plot["x_cuts"] ? plot["x_cuts"].as() : ""; + auto cuts_y = plot["y_cuts"] ? plot["y_cuts"].as() : ""; + + Cuts* c = !cuts.empty() ? task->GetCut(cuts) : nullptr; + Cuts* c_x = !cuts_x.empty() ? task->GetCut(cuts_x) : nullptr; + Cuts* c_y = !cuts_y.empty() ? task->GetCut(cuts_y) : nullptr; + + switch (type) { + case EntryConfig::PlotType::kHisto2D: task->AddH2(x, y, c, name); break; + case EntryConfig::PlotType::kIntegral2D: task->AddIntegral(x, y, c_x, c_y, name); break; + case EntryConfig::PlotType::kProfile: task->AddProfile(x, y, c, name); break; + default: throw std::runtime_error("Wrong plot type"); + } + } + } + + + protected: + std::string filename_; +}; + +} +} +#endif//ANALYSISTREEQA_YAMLREADER_HPP diff --git a/tasks/CMakeLists.txt b/tasks/CMakeLists.txt deleted file mode 100644 index 56fcc76..0000000 --- a/tasks/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ - -if(Boost_FOUND) - include_directories(${PROJECT_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIR}) - link_directories(${PROJECT_LINK_DIRECTORIES} ${Boost_LIBRARY_DIRS}) -else() - include_directories(${PROJECT_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR}) - link_directories(${PROJECT_LINK_DIRECTORIES}) -endif() - -#add_executable(cbm_qa cbm_qa.cpp) -#add_dependencies(cbm_qa AnalysisTreeQA ${PROJECT_DEPENDENCIES}) -#target_link_libraries(cbm_qa ${ROOT_LIBRARIES} AnalysisTreeQA AnalysisTreeBase AnalysisTreeInfra) -# -#install (TARGETS cbm_qa RUNTIME DESTINATION bin) \ No newline at end of file diff --git a/tasks/na61_qa.cpp b/tasks/na61_qa.cpp deleted file mode 100644 index 1b5a52d..0000000 --- a/tasks/na61_qa.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "Manager.hpp" -#include "Task.hpp" -#include -#include - -int main(int argc, char** argv) { - using namespace AnalysisTree; - using QA::Manager; - using QA::Task; - - const std::string event_header_branch{"RecEventHeader"}; - - assert(argc == 2); - - std::string filelist{argv[1]}; - std::string tree_name{"aTree"}; - - Manager manager({filelist}, {tree_name}); - manager.SetOutFileName("qa.root"); - - auto cuts_t2 = new Cuts("t2",{AnalysisTree::SimpleCut(Variable(event_header_branch,"t2"), 1.)}); - auto cuts_t4 = new Cuts("t4",{AnalysisTree::SimpleCut(Variable(event_header_branch,"t4"), 1.)}); - - auto cuts_t4_wfa = new Cuts("t4_wfa",{ - AnalysisTree::SimpleCut(Variable(event_header_branch,"t4"), 1.), - AnalysisTree::SimpleCut(Variable(event_header_branch,"wfa_s1"), 4000., 1e9), - AnalysisTree::SimpleCut(Variable(event_header_branch,"wfa_t4"), 4000., 1e9), - }); - auto cuts_t4_vtx_z_def = new Cuts("t4_vtx_z_def",{ - AnalysisTree::SimpleCut(Variable(event_header_branch,"t4"), 1.), - AnalysisTree::SimpleCut(Variable(event_header_branch,"vtx_z"), -594., -590.), - }); - - - auto task = new Task; - task->AddH1({"vertex fit OK", {event_header_branch, "fitted_vtx"}, {11, -1, 10}}); - task->AddH1({"x_{vertex} (cm)", {event_header_branch, "vtx_x"}, {200, -1., 1.}}); - task->AddH1({"y_{vertex} (cm)", {event_header_branch, "vtx_y"}, {200, -1., 1.}}); - task->AddH1({"z_{vertex} (cm)", {event_header_branch, "vtx_z"}, {2000, -600., -580.}}); - task->AddH2( - {"x_{vertex} (cm)", {event_header_branch, "vtx_x"}, {200, -1., 1.}}, - {"y_{vertex} (cm)", {event_header_branch, "vtx_y"}, {200, -1., 1.}}); - task->AddH2( - {"z_{vertex} (cm)", {event_header_branch, "vtx_z"}, {2000, -600., -580.}}, - {"x_{vertex} (cm)", {event_header_branch, "vtx_x"}, {200, -1., 1.}}); - task->AddH1({"x_{BPD3} (cm)", {event_header_branch, "bpd3_x"}, {200, -1., 1.}}); - task->AddH1({"y_{BPD3} (cm)", {event_header_branch, "bpd3_y"}, {200, -1., 1.}}); - task->AddH2( - {"x_{BPD3} (cm)", {event_header_branch, "bpd3_x"}, {200, -1., 1.}}, - {"x_{vertex} (cm)", {event_header_branch, "vtx_x"}, {200, -1., 1.}}); - task->AddH2( - {"y_{BPD3} (cm)", {event_header_branch, "bpd3_y"}, {200, -1., 1.}}, - {"y_{vertex} (cm)", {event_header_branch, "vtx_y"}, {200, -1., 1.}}); - task->AddH1({"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}); - task->AddH1({"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, cuts_t2); - task->AddH1({"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, cuts_t4); - task->AddH1({"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, cuts_t4_wfa); - task->AddH1({"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, cuts_t4_vtx_z_def); - task->AddH2( - {"z_{vertex} (cm)", {event_header_branch, "vtx_z"}, {2000, -600., -580.}}, - {"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}); - task->AddH1({"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}}); - task->AddH1({"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}}, cuts_t2); - task->AddH1({"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}}, cuts_t4); - task->AddH1({"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}}, cuts_t4_wfa); - task->AddH1({"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}}, cuts_t4_vtx_z_def); - task->AddH2( - {"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, - {"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}}); - task->AddH2( - {"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, - {"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}},cuts_t2); - task->AddH2( - {"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, - {"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}},cuts_t4); - task->AddH2( - {"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, - {"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}},cuts_t4_wfa); - task->AddH2( - {"TPC multiplicity", {event_header_branch, "M"}, {1000, 0, 1000}}, - {"E_{PSD} (GeV/c)", {event_header_branch, "Epsd"}, {1000, 0, 10000}},cuts_t4_vtx_z_def); - task->AddH1({"WFA(S1)", {event_header_branch, "wfa_s1"}, {100, 0, 30000}}); - task->AddH1({"WFA(T4)", {event_header_branch, "wfa_t4"}, {100, 0, 100 * 1e6}}); - task->AddH1({"T2", {event_header_branch, "t2"}, {6, -1, 5}}); - task->AddH1({"T4", {event_header_branch, "t4"}, {6, -1, 5}}); - manager.AddTask(task); - - manager.Init(); - manager.Run(-1); - manager.Finish(); - return 0; -} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 474b7c8..d7faf96 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,39 +1,34 @@ -include(AnalysisTree) +if (AnalysisTreeQA_BUILD_TESTS) + enable_testing() + include(GoogleTest) + # Download and unpack googletest at configure time + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists-GTest.txt.in googletest-download/CMakeLists.txt) + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/googletest-download ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/googletest-download ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() -# Download and unpack googletest at configure time -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists-GTest.txt.in googletest-download/CMakeLists.txt) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/googletest-download ) -if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") -endif() -execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/googletest-download ) -if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") -endif() + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -# Prevent overriding the parent project's compiler/linker -# settings on Windows -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src + ${CMAKE_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) -# Add googletest directly to our build. This defines -# the gtest and gtest_main targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) + add_executable(AnalysisTreeQA_UnitTests test_runner.cxx) + include_directories(${PROJECT_INCLUDE_DIRECTORIES}) + target_link_libraries(AnalysisTreeQA_UnitTests PRIVATE AnalysisTreeQA gtest_main) + gtest_discover_tests(AnalysisTreeQA_UnitTests TEST_PREFIX AnalysisTreeQA_) -# The gtest/gtest_main targets carry header search path -# dependencies automatically when using CMake 2.8.11 or -# later. Otherwise we have to add them here ourselves. -if (CMAKE_VERSION VERSION_LESS 2.8.11) - include_directories("${gtest_SOURCE_DIR}/include") -endif() - -add_executable(tests test_runner.cxx) -include_directories(${CMAKE_SOURCE_DIR}/src) -add_dependencies(tests AnalysisTreeQA) -target_link_libraries(tests gtest AnalysisTreeQA AnalysisTreeBase AnalysisTreeInfra ${ROOT_LIBRARIES}) -gtest_discover_tests(tests TEST_PREFIX new:) \ No newline at end of file +endif() # AnalysisTree_BUILD_TESTS diff --git a/test/integration/Manager.test.hpp b/test/integration/Manager.test.hpp index 7a44f50..3740ed9 100644 --- a/test/integration/Manager.test.hpp +++ b/test/integration/Manager.test.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "AnalysisTree/ToyMC.hpp" @@ -32,7 +33,7 @@ TEST(Test_AnalysisTreeQA, Test_Manager) { man->Finish(); std::ofstream fl(filelist); - fl << filename << "\n"; + fl << std::string(gSystem->pwd()) + "/" + filename << "\n"; fl.close(); delete toy_mc; @@ -68,13 +69,13 @@ TEST(Test_AnalysisTreeQA, Test_Manager) { auto* h_sim_y = (TH1*)f_qa.Get("SimParticles/SimParticles_rapidity"); - EXPECT_NEAR(h_sim_y->GetEntries(), 100*n_events, 100); + EXPECT_NEAR(h_sim_y->GetEntries(), 100*n_events, 1000); // EXPECT_NEAR(h_sim_y->GetMean(), 0, 0.01); // EXPECT_NEAR(h_sim_y->GetStdDev(), 1., 0.05); auto* h_rec_px = (TH1*)f_qa.Get("RecTracks/RecTracks_px"); - EXPECT_NEAR(h_rec_px->GetEntries(), 100*n_events, 100); + EXPECT_NEAR(h_rec_px->GetEntries(), 100*n_events, 1000); EXPECT_NEAR(h_rec_px->GetMean(), 0, 0.01); // EXPECT_NEAR(h_rec_px->GetStdDev(), 1., 0.5);