Skip to content

packages/engine/scram-node/src/scram.cc

Main entrance.

Functions

Name
intmain(int argc, char * argv[])<br>Command-line SCRAM entrance.

Defines

Name
OPT_VALUE(type) <br>Provides an options value type.
SET(tag, type, member) <br>Helper macro for ConstructSettings to set the flag in "settings" only if provided by "vm" arguments.

Functions Documentation

function main

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

Command-line SCRAM entrance.

Parameters:

  • argc Argument count.
  • argv Argument vector.

Return:

  • 0 for success.
  • 1 for errored state.

Macros Documentation

define OPT_VALUE

cpp
#define OPT_VALUE(
    type
)
po::value<type>()->value_name(#type)

Provides an options value type.

define SET

cpp
#define SET(
    tag,
    type,
    member
)
  if (vm.count(tag)) settings->member(vm[tag].as<type>())

Helper macro for ConstructSettings to set the flag in "settings" only if provided by "vm" arguments.

Source code

cpp
/*
 * Copyright (C) 2014-2018 Olzhas Rakhimov
 * Copyright (C) 2023 OpenPRA ORG Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <omp.h>

#include <cstdarg>
#include <cstdio>  // vsnprintf
#include <cstring>  // strerror

#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include <boost/core/typeinfo.hpp>
#include <boost/exception/all.hpp>
#include <boost/program_options.hpp>
#include <boost/version.hpp>

#include <libxml/parser.h>  // xmlInitParser, xmlCleanupParser
#include <libxml/xmlerror.h>  // initGenericErrorDefaultFunc
#include <libxml/xmlversion.h>  // LIBXML_TEST_VERSION, LIBXML_DOTTED_VERSION

#include "error.h"
#include "ext/scope_guard.h"
#include "initializer.h"
#include "logger.h"
#include "reporter.h"
#include "risk_analysis.h"
#include "serialization.h"
#include "settings.h"

namespace po = boost::program_options;

namespace {

#define OPT_VALUE(type) po::value<type>()->value_name(#type)

po::options_description ConstructOptions() {
  using path = std::string;  // To print argument type as path.

  po::options_description desc("Options");
  // clang-format off
  desc.add_options()
      ("help", "Display this help message")
      ("version", "Display version information")
      ("project", OPT_VALUE(path), "Project file with analysis configurations")
      ("allow-extern", "**UNSAFE** Allow external libraries")
      ("validate", "Validate input files without analysis")
      ("bdd", "Perform qualitative analysis with BDD")
      ("zbdd", "Perform qualitative analysis with ZBDD")
      ("mocus", "Perform qualitative analysis with MOCUS")
      ("prime-implicants", "Calculate prime implicants")
      ("probability", "Perform probability analysis")
      ("importance", "Perform importance analysis")
      ("uncertainty", "Perform uncertainty analysis")
      ("ccf", "Perform common-cause failure analysis")
      ("sil", "Compute the Safety Integrity Level metrics")
      ("rare-event", "Use the rare event approximation")
      ("mcub", "Use the MCUB approximation")
      ("limit-order,l", OPT_VALUE(int), "Upper limit for the product order")
      ("cut-off", OPT_VALUE(double), "Cut-off probability for products")
      ("mission-time", OPT_VALUE(double), "System mission time in hours")
      ("time-step", OPT_VALUE(double),
       "Time step in hours for probability analysis")
      ("num-trials", OPT_VALUE(int),
       "Number of trials for Monte Carlo simulations")
      ("num-quantiles", OPT_VALUE(int),
       "Number of quantiles for distributions")
      ("num-bins", OPT_VALUE(int), "Number of bins for histograms")
      ("seed", OPT_VALUE(int), "Seed for the pseudo-random number generator")
      ("output,o", OPT_VALUE(path), "Output file for reports")
      ("no-indent", "Omit indentation whitespace in output XML")
      ("verbosity", OPT_VALUE(int), "Set log verbosity");
#ifndef NDEBUG
  po::options_description debug("Debug Options");
  debug.add_options()
      ("serialize", "Serialize the input model without further analysis")
      ("preprocessor", "Stop analysis after the preprocessing step")
      ("print", "Print analysis results in a terminal friendly way")
      ("no-report", "Don't generate analysis report");
  desc.add(debug);
#endif
  // clang-format on
  return desc;
}
#undef OPT_VALUE

int ParseArguments(int argc, char* argv[], po::variables_map* vm) {
  const char* usage = "Usage:    scram [options] input-files...";
  po::options_description desc = ConstructOptions();
  try {
    po::store(po::parse_command_line(argc, argv, desc), *vm);
  } catch (std::exception& err) {
    std::cerr << "Option error: " << err.what() << "\n\n"
              << usage << "\n\n"
              << desc << std::endl;
    return 1;
  }
  po::options_description options("All options with positional input files.");
  options.add(desc).add_options()("input-files",
                                  po::value<std::vector<std::string>>(),
                                  "MEF input files with analysis constructs");
  po::positional_options_description p;
  p.add("input-files", -1);  // All input files are implicit.
  po::store(
      po::command_line_parser(argc, argv).options(options).positional(p).run(),
      *vm);
  po::notify(*vm);

  auto print_help = [&usage, &desc](std::ostream& out) {
    out << usage << "\n\n" << desc << std::endl;
  };

  // Process command-line arguments.
  if (vm->count("help")) {
    print_help(std::cout);
    return -1;
  }
  if (vm->count("version")) {
    std::cout << "SCRAM "
              << "\n\nDependencies:\n"
              << "   Boost       " << BOOST_LIB_VERSION << "\n"
              << "   libxml2     " << LIBXML_DOTTED_VERSION << std::endl;
    return -1;
  }

  if (vm->count("verbosity")) {
    int level = (*vm)["verbosity"].as<int>();
    if (level < 0 || level > scram::kMaxVerbosity) {
      std::cerr << "Log verbosity must be between 0 and "
                << scram::kMaxVerbosity << ".\n\n";
      print_help(std::cerr);
      return 1;
    }
  }

  if (!vm->count("input-files") && !vm->count("project")) {
    std::cerr << "No input or configuration file is given.\n\n";
    print_help(std::cerr);
    return 1;
  }
  if ((vm->count("bdd") + vm->count("zbdd") + vm->count("mocus")) > 1) {
    std::cerr << "Mutually exclusive qualitative analysis algorithms.\n"
              << "(MOCUS/BDD/ZBDD) cannot be applied at the same time.\n\n";
    print_help(std::cerr);
    return 1;
  }
  if (vm->count("rare-event") && vm->count("mcub")) {
    std::cerr << "The rare event and MCUB approximations cannot be "
              << "applied at the same time.\n\n";
    print_help(std::cerr);
    return 1;
  }
  return 0;
}

// clang-format off
#define SET(tag, type, member) \
  if (vm.count(tag)) settings->member(vm[tag].as<type>())
// clang-format on

void ConstructSettings(const po::variables_map& vm,
                       scram::core::Settings* settings) {
  if (vm.count("bdd")) {
    settings->algorithm(scram::core::Algorithm::kBdd);
  } else if (vm.count("zbdd")) {
    settings->algorithm(scram::core::Algorithm::kZbdd);
  } else if (vm.count("mocus")) {
    settings->algorithm(scram::core::Algorithm::kMocus);
  }
  settings->prime_implicants(vm.count("prime-implicants"));
  // Determine if the probability approximation is requested.
  if (vm.count("rare-event")) {
    assert(!vm.count("mcub"));
    settings->approximation(scram::core::Approximation::kRareEvent);
  } else if (vm.count("mcub")) {
    settings->approximation(scram::core::Approximation::kMcub);
  }
  SET("time-step", double, time_step);
  settings->safety_integrity_levels(vm.count("sil"));

  settings->probability_analysis(vm.count("probability"));
  settings->importance_analysis(vm.count("importance"));
  settings->uncertainty_analysis(vm.count("uncertainty"));
  settings->ccf_analysis(vm.count("ccf"));
  SET("seed", int, seed);
  SET("limit-order", int, limit_order);
  SET("cut-off", double, cut_off);
  SET("mission-time", double, mission_time);
  SET("num-trials", int, num_trials);
  SET("num-quantiles", int, num_quantiles);
  SET("num-bins", int, num_bins);
#ifndef NDEBUG
  settings->preprocessor = vm.count("preprocessor");
  settings->print = vm.count("print");
#endif
}
#undef SET

void RunScram(const po::variables_map& vm) {
  scram::core::Settings settings;  // Analysis settings.
  std::vector<std::string> input_files;
  ConstructSettings(vm, &settings);
  if (vm.count("input-files")) {
    auto cmd_input = vm["input-files"].as<std::vector<std::string>>();
    input_files.insert(input_files.end(), cmd_input.begin(), cmd_input.end());
  }
  // Process input files
  // into valid analysis containers and constructs.
  // Throws if anything is invalid.
  std::unique_ptr<scram::mef::Model> model =
      scram::mef::Initializer(input_files, settings, vm.count("allow-extern"))
          .model();
#ifndef NDEBUG
  if (vm.count("serialize"))
    return Serialize(*model, stdout);
#endif
  if (vm.count("validate"))
    return;  // Stop if only validation is requested.

  // Initiate risk analysis with the given information.
  scram::core::RiskAnalysis analysis(model.get(), settings);
  analysis.Analyze();
#ifndef NDEBUG
  if (vm.count("no-report") || vm.count("preprocessor") || vm.count("print"))
    return;
#endif
  scram::Reporter reporter;
  bool indent = vm.count("no-indent") ? false : true;
  if (vm.count("output")) {
    reporter.Report(analysis, vm["output"].as<std::string>(), indent);
  } else {
    reporter.Report(analysis, stdout, indent);
  }
}

extern "C" void LogXmlError(void* /*ctx*/, const char* msg, ...) noexcept {
  std::va_list args;
  va_start(args, msg);
  SCOPE_EXIT([&args] { va_end(args); });

  std::va_list args_for_nchar;  // Only used to determine the string length.
  va_copy(args_for_nchar, args);
  int nchar = std::vsnprintf(nullptr, 0, msg, args_for_nchar);
  va_end(args_for_nchar);
  if (nchar < 0) {
    LOG(scram::ERROR) << "String formatting failure: " << std::strerror(errno);
    return;
  }

  std::vector<char> buffer(nchar + /*null terminator*/ 1);
  std::vsnprintf(buffer.data(), buffer.size(), msg, args);
  LOG(scram::WARNING) << buffer.data();
}

template <class Tag>
void PrintErrorInfo(const char* tag_string, const scram::Error& err) {
  if (const auto* value = boost::get_error_info<Tag>(err))
    std::cerr << tag_string << ": " << *value << "\n";
}

}  // namespace

int main(int argc, char* argv[]) {
  double wtime = omp_get_wtime(); //measuring the elapsed time
  LIBXML_TEST_VERSION
  xmlInitParser();
  SCOPE_EXIT(&xmlCleanupParser);

  xmlGenericErrorFunc xml_error_printer = LogXmlError;
  initGenericErrorDefaultFunc(&xml_error_printer);

  try {
    // Parse command-line options.
    po::variables_map vm;
    int ret = ParseArguments(argc, argv, &vm);
    if (ret == 1)
      return 1;

    if (vm.count("verbosity")) {
      scram::Logger::report_level(
          static_cast<scram::LogLevel>(vm["verbosity"].as<int>()));
    }

    if (ret == 0)
      RunScram(vm);
  } catch (const scram::LogicError& err) {
    LOG(scram::ERROR) << "Logic Error:\n" << boost::diagnostic_information(err);
    return 1;
  } catch (const scram::IOError& err) {
    LOG(scram::DEBUG1) << boost::diagnostic_information(err);
    std::cerr << boost::core::demangled_name(typeid(err)) << "\n\n";
    PrintErrorInfo<boost::errinfo_file_name>("File", err);
    PrintErrorInfo<boost::errinfo_file_open_mode>("Open mode", err);
    if (const int* errnum = boost::get_error_info<boost::errinfo_errno>(err)) {
      std::cerr << "Error code: " << *errnum << "\n";
      std::cerr << "Error string: " << std::strerror(*errnum) << "\n";
    }
    std::cerr << "\n" << err.what() << std::endl;
    return 1;
  } catch (const scram::Error& err) {
    using namespace scram;  // NOLINT
    LOG(DEBUG1) << boost::diagnostic_information(err);
    std::cerr << boost::core::demangled_name(typeid(err)) << "\n\n";
    PrintErrorInfo<errinfo_value>("Value", err);
    PrintErrorInfo<boost::errinfo_file_name>("File", err);
    PrintErrorInfo<boost::errinfo_at_line>("Line", err);
    PrintErrorInfo<mef::errinfo_connective>("MEF Connective", err);
    PrintErrorInfo<mef::errinfo_reference>("MEF reference", err);
    PrintErrorInfo<mef::errinfo_base_path>("MEF base path", err);
    PrintErrorInfo<mef::errinfo_element_id>("MEF Element ID", err);
    PrintErrorInfo<mef::errinfo_element_type>("MEF Element type", err);
    PrintErrorInfo<mef::errinfo_container_id>("MEF Container", err);
    PrintErrorInfo<mef::errinfo_container_type>("MEF Container type", err);
    PrintErrorInfo<mef::errinfo_attribute>("MEF Attribute", err);
    PrintErrorInfo<mef::errinfo_cycle>("Cycle", err);
    PrintErrorInfo<xml::errinfo_element>("XML element", err);
    PrintErrorInfo<xml::errinfo_attribute>("XML attribute", err);
    std::cerr << "\n" << err.what() << std::endl;
    return 1;
  } catch (const boost::exception& boost_err) {
    LOG(scram::ERROR) << "Unexpected Boost Exception:\n"
                      << boost::diagnostic_information(boost_err);
    return 1;
  } catch (const std::exception& err) {
    LOG(scram::ERROR) << "Unexpected Exception: "
                      << boost::core::demangled_name(typeid(err)) << ":\n"
                      << err.what();
    return 1;
  }
  wtime = omp_get_wtime() - wtime; //measuring the elapsed time
  std::cout<<"Elapsed wall clock time= "<<wtime<<" seconds!"<<std::endl;
}  // End of main.

Updated on 2025-11-11 at 16:51:09 +0000