packages/engine/scram/src/project.cc
Implementation of project configuration facilities.
Namespaces
| Name |
|---|
| scram |
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 "project.h"
#include <cassert>
#include <array>
#include <memory>
#include <optional>
#include <string_view>
#include <boost/predef.h>
#include <boost/exception/errinfo_at_line.hpp>
#include <boost/exception/errinfo_file_name.hpp>
#include <boost/filesystem.hpp>
#include <boost/range/algorithm.hpp>
#include "env.h"
#include "error.h"
#include "ext/version.h"
#include "version.h"
namespace fs = boost::filesystem;
namespace scram {
namespace { // Path normalization helper.
std::string normalize(const std::string& file_path, const fs::path& base_path) {
fs::path abs_path = fs::absolute(file_path, base_path).generic_string();
#if BOOST_OS_WINDOWS
return abs_path.generic_string();
#else
std::string abnormal_path = abs_path.string();
boost::replace(abnormal_path, '\\', '/');
return abnormal_path;
#endif
}
} // namespace
Project::Project(const std::string& project_file) {
static xml::Validator validator = xml::Validator::from_memory(env::project_schema());
if (fs::exists(project_file) == false) {
SCRAM_THROW(IOError("The configuration file does not exist."))
<< boost::errinfo_file_name(project_file);
}
xml::Document document(project_file);
xml::Element root = document.root();
std::string_view version = root.attribute("version");
if (root.name() == "scram" && !version.empty()) {
try {
std::optional<std::array<int, 3>> numbers = ext::extract_version(version);
if (!numbers)
SCRAM_THROW(xml::ValidityError("Invalid version string"));
std::array<int, 3> current_numbers = {
SCRAM_VERSION_MAJOR, SCRAM_VERSION_MINOR, SCRAM_VERSION_MICRO};
if (numbers > current_numbers)
SCRAM_THROW(VersionError("Version incompatibility"));
} catch (Error& err) {
err << errinfo_value(std::string(version))
<< xml::errinfo_element("scram") << xml::errinfo_attribute("version")
<< boost::errinfo_at_line(root.line())
<< boost::errinfo_file_name(root.filename());
throw;
}
}
validator.validate(document);
assert(root.name() == "scram");
fs::path base_path = fs::path(project_file).parent_path();
GatherInputFiles(root, base_path);
try {
GatherOptions(root);
} catch (Error& err) {
err << boost::errinfo_file_name(project_file);
throw;
}
}
void Project::GatherInputFiles(const xml::Element& root,
const fs::path& base_path) {
std::optional<xml::Element> input_files = root.child("model");
if (!input_files)
return;
for (xml::Element input_file : input_files->children()) {
assert(input_file.name() == "file");
input_files_.push_back(
normalize(std::string(input_file.text()), base_path));
}
}
void Project::GatherOptions(const xml::Element& root) {
std::optional<xml::Element> options_element = root.child("options");
if (!options_element)
return;
// The loop is used instead of query
// because the order of options matters,
// yet this function should not know what the order is.
for (xml::Element option_group : options_element->children()) {
try {
std::string_view name = option_group.name();
if (name == "algorithm") {
settings_.algorithm(option_group.attribute("name"));
} else if (name == "prime-implicants") {
settings_.prime_implicants(true);
} else if (name == "approximation") {
settings_.approximation(option_group.attribute("name"));
} else if (name == "limits") {
SetLimits(option_group);
}
} catch (SettingsError& err) {
err << boost::errinfo_at_line(option_group.line());
throw;
}
}
if (std::optional<xml::Element> analysis_group =
options_element->child("analysis")) {
try {
SetAnalysis(*analysis_group);
} catch (SettingsError& err) {
err << boost::errinfo_at_line(analysis_group->line());
throw;
}
}
}
void Project::SetAnalysis(const xml::Element& analysis) {
auto set_flag = [&analysis](const char* tag, auto setter) {
if (std::optional<bool> flag = analysis.attribute<bool>(tag))
setter(*flag);
};
set_flag("probability",
[this](bool flag) { settings_.probability_analysis(flag); });
set_flag("importance",
[this](bool flag) { settings_.importance_analysis(flag); });
set_flag("uncertainty",
[this](bool flag) { settings_.uncertainty_analysis(flag); });
set_flag("ccf", [this](bool flag) { settings_.ccf_analysis(flag); });
set_flag("sil",
[this](bool flag) { settings_.safety_integrity_levels(flag); });
}
void Project::SetLimits(const xml::Element& limits) {
for (xml::Element limit : limits.children()) {
std::string_view name = limit.name();
if (name == "product-order") {
settings_.limit_order(limit.text<int>());
} else if (name == "cut-off") {
settings_.cut_off(limit.text<double>());
} else if (name == "mission-time") {
settings_.mission_time(limit.text<double>());
} else if (name == "time-step") {
settings_.time_step(limit.text<double>());
} else if (name == "number-of-trials") {
settings_.num_trials(limit.text<int>());
} else if (name == "number-of-quantiles") {
settings_.num_quantiles(limit.text<int>());
} else if (name == "number-of-bins") {
settings_.num_bins(limit.text<int>());
} else if (name == "seed") {
settings_.seed(limit.text<int>());
}
}
}
} // namespace scramUpdated on 2026-01-09 at 21:59:13 +0000
