src/exec/allpix.cpp

Executable running the framework. More…

Functions

Name
void clean()
Clean the environment when closing application.
void abort_handler(int )
Handle user abort (CTRL+) which should stop the framework immediately.
void interrupt_handler(int )
Handle termination request (CTRL+C) as soon as possible while keeping the program flow.
int main(int argc, const char * argv[])
Main function running the application.

Attributes

Name
std::unique_ptr< Allpix > apx
std::atomic< bool > apx_ready

Detailed Description

Executable running the framework.

Copyright: Copyright (c) 2016-2024 CERN and the Allpix Squared authors. This software is distributed under the terms of the MIT License, copied verbatim in the file “LICENSE.md”. In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction. SPDX-License-Identifier: MIT

Functions Documentation

function clean

void clean()

Clean the environment when closing application.

function abort_handler

void abort_handler(
    int 
)

Handle user abort (CTRL+) which should stop the framework immediately.

Note: This handler is actually not fully reliable (but otherwise crashing is okay…)

function interrupt_handler

void interrupt_handler(
    int 
)

Handle termination request (CTRL+C) as soon as possible while keeping the program flow.

Handle termination request (CTRL+C)

function main

int main(
    int argc,
    const char * argv[]
)

Main function running the application.

Attributes Documentation

variable apx

std::unique_ptr< Allpix > apx;

variable apx_ready

std::atomic< bool > apx_ready {false};

Source code


#include <atomic>
#if defined(__linux__) || (defined(__APPLE__) && !defined(__arm64__))
#include <cpuid.h>
#endif

#include <csignal>
#include <cstdlib>
#include <fstream>
#include <memory>
#include <string>
#include <utility>

#include <boost/version.hpp>

#include "core/Allpix.hpp"
#include "core/config/ConfigManager.hpp"
#include "core/geometry/GeometryManager.hpp"
#include "core/utils/exceptions.h"
#include "core/utils/log.h"

#ifdef ALLPIX_GEANT4_AVAILABLE
#include <G4Version.hh>
#endif

using namespace allpix;

void clean();
void abort_handler(int);
void interrupt_handler(int);

std::unique_ptr<Allpix> apx;
std::atomic<bool> apx_ready{false};

void abort_handler(int) {
    // Output interrupt message and clean
    LOG(FATAL) << "Aborting!";
    clean();

    // Ignore any segmentation fault that may arise after this
    std::signal(SIGSEGV, SIG_IGN); // NOLINT
    std::abort();
}

void interrupt_handler(int) {
    // Stop the framework if it is loaded
    if(apx_ready) {
        LOG(STATUS) << "Interrupted! Finishing up active events...";
        apx->terminate();
    }
}

void clean() {
    Log::finish();
    if(apx_ready) {
        apx.reset();
    }
}

int main(int argc, const char* argv[]) {
    // Add cout as the default logging stream
    Log::addStream(std::cout);

    // Install abort handler (CTRL+\)
    std::signal(SIGQUIT, abort_handler);
    std::signal(SIGABRT, abort_handler);

    // Install interrupt handler (CTRL+C)
    std::signal(SIGINT, interrupt_handler);

    // Install termination handler (e.g. from "kill"). Gracefully exit, finish last event and quit
    std::signal(SIGTERM, interrupt_handler);

    // If no arguments are provided, print the help:
    bool print_help = false;
    int return_code = 0;
    if(argc == 1) {
        print_help = true;
        return_code = 1;
    }

    // Parse arguments
    std::string config_file_name;
    std::string log_file_name;
    std::vector<std::string> module_options;
    std::vector<std::string> detector_options;

    for(int i = 1; i < argc; i++) {
        std::string arg = argv[i];

        if(arg == "-h") {
            print_help = true;
        } else if(arg == "--version") {
            std::cout << "Allpix Squared version " << ALLPIX_PROJECT_VERSION;
#ifdef ALLPIX_BUILD_ENV
            std::cout << " (" << ALLPIX_BUILD_ENV << ")";
#endif
            std::cout << std::endl;
            std::cout << "               built on " << ALLPIX_BUILD_TIME << std::endl;
            std::cout << "               using Boost.Random " << BOOST_VERSION / 100000 << "." // major version
                      << BOOST_VERSION / 100 % 1000 << "."                                     // minor version
                      << BOOST_VERSION % 100                                                   // patch level
                      << std::endl;
            std::cout << "                     ROOT " << ROOT_RELEASE << std::endl;
#ifdef ALLPIX_GEANT4_AVAILABLE
            std::cout << "                     Geant4 " << G4VERSION_NUMBER / 100 << "." << G4VERSION_NUMBER / 10 % 10 << "."
                      << G4VERSION_NUMBER % 10 << std::endl;
#endif

            // The new apple m silicon does not include cpuid
#if defined(__linux__) || (defined(__APPLE__) && !defined(__arm64__))
            std::array<char, 0x40> cpu_string;
            std::array<unsigned int, 4> cpu_info = {0, 0, 0, 0};
            __cpuid(0x80000000, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
            unsigned int n_ex_ids = cpu_info[0];
            memset(cpu_string.data(), 0, sizeof(cpu_string));
            for(unsigned int j = 0x80000000; j <= n_ex_ids; ++j) {
                __cpuid(j, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
                if(j == 0x80000002) {
                    memcpy(cpu_string.data(), cpu_info.data(), sizeof(cpu_info));
                } else if(j == 0x80000003) {
                    memcpy(cpu_string.data() + 16, cpu_info.data(), sizeof(cpu_info));
                } else if(j == 0x80000004) {
                    memcpy(cpu_string.data() + 32, cpu_info.data(), sizeof(cpu_info));
                }
            }
            std::cout << "               running on " << std::thread::hardware_concurrency() << "x " << cpu_string.data()
                      << std::endl;

            std::cout << std::endl;
#endif

            std::cout << "Copyright (c) 2016-2024 CERN and the Allpix Squared authors." << std::endl << std::endl;
            std::cout << "This software is distributed under the terms of the MIT License." << std::endl;
            std::cout << "In applying this license, CERN does not waive the privileges and immunities" << std::endl;
            std::cout << "granted to it by virtue of its status as an Intergovernmental Organization" << std::endl;
            std::cout << "or submit itself to any jurisdiction." << std::endl;
            clean();
            return 0;
        } else if(arg == "-v" && (i + 1 < argc)) {
            try {
                LogLevel log_level = Log::getLevelFromString(std::string(argv[++i]));
                Log::setReportingLevel(log_level);
            } catch(std::invalid_argument& e) {
                LOG(ERROR) << "Invalid verbosity level \"" << std::string(argv[i]) << "\", ignoring overwrite";
            }
        } else if(arg == "-c" && (i + 1 < argc)) {
            config_file_name = std::string(argv[++i]);
        } else if(arg == "-l" && (i + 1 < argc)) {
            log_file_name = std::string(argv[++i]);
        } else if(arg == "-o" && (i + 1 < argc)) {
            module_options.emplace_back(argv[++i]);
        } else if(arg.find("-j") == 0) {
            module_options.emplace_back("multithreading=true");
            if(arg == "-j" && (i + 1 < argc)) {
                module_options.emplace_back("workers=" + std::string(argv[++i]));
            } else {
                module_options.emplace_back("workers=" + arg.substr(2));
            }
        } else if(arg == "-g" && (i + 1 < argc)) {
            detector_options.emplace_back(argv[++i]);
        } else {
            LOG(ERROR) << "Unrecognized command line argument \"" << argv[i] << "\"";
            print_help = true;
            return_code = 1;
        }
    }

    // Print help if requested or no arguments given
    if(print_help) {
        std::cout << "Allpix Squared" << std::endl;
        std::cout << "Generic Pixel Detector Simulation Framework" << std::endl;
        std::cout << std::endl;
        std::cout << "Usage: allpix -c <file> [OPTIONS]" << std::endl;
        std::cout << std::endl;
        std::cout << "Options:" << std::endl;
        std::cout << "  -c <file>    configuration file to be used" << std::endl;
        std::cout << "  -l <file>    file to log to besides standard output" << std::endl;
        std::cout << "  -o <option>  extra module configuration option(s) to pass" << std::endl;
        std::cout << "  -g <option>  extra detector configuration options(s) to pass" << std::endl;
        std::cout << "  -v <level>   verbosity level, overwriting the global level" << std::endl;
        std::cout << "  -j <workers> number of worker threads, equivalent to" << std::endl;
        std::cout << "               -o multithreading=true -o workers=<workers>" << std::endl;
        std::cout << "  --version    print version information and quit" << std::endl;
        std::cout << std::endl;
        std::cout << "For more help, please see <https://cern.ch/allpix-squared>" << std::endl;
        clean();
        return return_code;
    }

    // Check if we have a configuration file
    if(config_file_name.empty()) {
        LOG(FATAL) << "No configuration file provided! See usage info with \"allpix -h\"";
        clean();
        return 1;
    }

    // Add an extra file to log too if possible
    // NOTE: this stream should be available for the duration of the logging
    std::ofstream log_file;
    if(!log_file_name.empty()) {
        log_file.open(log_file_name, std::ios_base::out | std::ios_base::trunc);
        if(!log_file.good()) {
            LOG(FATAL) << "Cannot write to provided log file! Check if permissions are sufficient.";
            clean();
            return 1;
        }

        Log::addStream(log_file);
    }

    try {
        // Construct main Allpix object
        apx = std::make_unique<Allpix>(config_file_name, module_options, detector_options);
        apx_ready = true;

        // Load modules
        apx->load();

        // Initialize modules (pre-run)
        apx->initialize();

        // Run modules and event-loop
        apx->run();

        // Finalize modules (post-run)
        apx->finalize();
    } catch(ConfigurationError& e) {
        LOG(FATAL) << "Error in the configuration:" << std::endl
                   << e.what() << std::endl
                   << "The configuration needs to be updated. Cannot continue.";
        return_code = 1;
    } catch(RuntimeError& e) {
        LOG(FATAL) << "Error during execution of run:" << std::endl
                   << e.what() << std::endl
                   << "Please check your configuration and modules. Cannot continue.";
        return_code = 1;
    } catch(LogicError& e) {
        LOG(FATAL) << "Error in the logic of module:" << std::endl
                   << e.what() << std::endl
                   << "Module has to be properly defined. Cannot continue.";
        return_code = 1;
    } catch(std::exception& e) {
        LOG(FATAL) << "Fatal internal error" << std::endl << e.what() << std::endl << "Cannot continue.";
        return_code = 127;
    }

    // Finish the logging
    clean();

    return return_code;
}

Updated on 2024-12-13 at 08:31:37 +0000