packages/engine/scram-node/src/event_tree_analysis.cc
Implementation of event tree analysis facilities.
Namespaces
| Name |
|---|
| scram |
| scram::core |
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 "event_tree_analysis.h"
#include "expression/numerical.h"
#include "ext/find_iterator.h"
#include "instruction.h"
namespace scram::core {
EventTreeAnalysis::EventTreeAnalysis(
const mef::InitiatingEvent& initiating_event, const Settings& settings,
mef::Context* context)
: Analysis(settings),
initiating_event_(initiating_event),
context_(context) {}
namespace { // The model cloning functions.
std::unique_ptr<mef::Formula>
Clone(const mef::Formula& formula,
const std::unordered_map<std::string, bool>& set_instructions,
std::vector<std::unique_ptr<mef::Event>>* clones) noexcept {
struct {
mef::Formula::ArgEvent operator()(mef::BasicEvent* arg) { return arg; }
mef::Formula::ArgEvent operator()(mef::HouseEvent* arg) {
if (auto it = ext::find(set_house, arg->id())) {
if (it->second == arg->state())
return arg;
auto clone = std::make_unique<mef::HouseEvent>(
arg->name(), "__clone__." + arg->id(),
mef::RoleSpecifier::kPrivate);
clone->state(it->second);
auto* ptr = clone.get();
event_register->emplace_back(std::move(clone));
return ptr;
}
return arg;
}
mef::Formula::ArgEvent operator()(mef::Gate* arg) {
if (set_house.empty())
return arg;
auto clone = std::make_unique<mef::Gate>(
arg->name(), "__clone__." + arg->id(), mef::RoleSpecifier::kPrivate);
clone->formula(Clone(arg->formula(), set_house, event_register));
auto* ptr = clone.get();
event_register->emplace_back(std::move(clone));
return ptr;
}
const std::unordered_map<std::string, bool>& set_house;
std::vector<std::unique_ptr<mef::Event>>* event_register;
} cloner{set_instructions, clones};
mef::Formula::ArgSet arg_set;
for (const mef::Formula::Arg& arg : formula.args())
arg_set.Add(std::visit(cloner, arg.event), arg.complement);
return std::make_unique<mef::Formula>(
formula.connective(), std::move(arg_set), formula.min_number(),
formula.max_number());
}
} // namespace
EventTreeAnalysis::PathCollector::PathCollector(const PathCollector& other)
: expressions(other.expressions), set_instructions(other.set_instructions) {
for (const mef::FormulaPtr& formula : other.formulas)
formulas.push_back(std::make_unique<mef::Formula>(*formula));
}
void EventTreeAnalysis::Analyze() noexcept {
assert(initiating_event_.event_tree());
int formula_id = 0; // Enumeration of collected formulas turned into gates.
// Creates an internal gate representing the formula.
auto make_gate = [&formula_id, this](mef::FormulaPtr formula) {
std::string gate_name = "___" + initiating_event_.name() + "__formula_" +
std::to_string(formula_id++) + "__";
auto gate = std::make_unique<mef::Gate>(gate_name);
gate->formula(std::move(formula));
auto* address = gate.get();
events_.emplace_back(std::move(gate));
return address;
};
SequenceCollector collector{initiating_event_, *context_};
CollectSequences(initiating_event_.event_tree()->initial_state(), &collector);
for (auto& sequence : collector.sequences) {
auto gate = std::make_unique<mef::Gate>("__" + sequence.first->name());
std::vector<mef::FormulaPtr> gate_formulas;
std::vector<mef::Expression*> arg_expressions;
for (PathCollector& path_collector : sequence.second) {
if (path_collector.formulas.size() == 1) {
gate_formulas.push_back(std::move(path_collector.formulas.front()));
} else if (path_collector.formulas.size() > 1) {
mef::Formula::ArgSet arg_set;
for (mef::FormulaPtr& arg_formula : path_collector.formulas)
arg_set.Add(make_gate(std::move(arg_formula)));
gate_formulas.push_back(
std::make_unique<mef::Formula>(mef::kAnd, std::move(arg_set)));
}
if (path_collector.expressions.size() == 1) {
arg_expressions.push_back(path_collector.expressions.front());
} else if (path_collector.expressions.size() > 1) {
expressions_.push_back(
std::make_unique<mef::Mul>(std::move(path_collector.expressions)));
arg_expressions.push_back(expressions_.back().get());
}
}
assert(gate_formulas.empty() || arg_expressions.empty());
bool is_expression_only = !arg_expressions.empty();
if (gate_formulas.size() == 1) {
gate->formula(std::move(gate_formulas.front()));
} else if (gate_formulas.size() > 1) {
mef::Formula::ArgSet arg_set;
for (mef::FormulaPtr& arg_formula : gate_formulas)
arg_set.Add(make_gate(std::move(arg_formula)));
gate->formula(
std::make_unique<mef::Formula>(mef::kOr, std::move(arg_set)));
} else if (!arg_expressions.empty()) {
auto event =
std::make_unique<mef::BasicEvent>("__" + sequence.first->name());
if (arg_expressions.size() == 1) {
event->expression(arg_expressions.front());
} else if (arg_expressions.size() > 1) {
expressions_.push_back(
std::make_unique<mef::Add>(std::move(arg_expressions)));
event->expression(expressions_.back().get());
}
gate->formula(std::make_unique<mef::Formula>(
mef::kNull, mef::Formula::ArgSet{event.get()}));
events_.push_back(std::move(event));
} else {
gate->formula(std::make_unique<mef::Formula>(
mef::kNull, mef::Formula::ArgSet{&mef::HouseEvent::kTrue}));
}
sequences_.push_back(
{*sequence.first, std::move(gate), is_expression_only});
}
}
void EventTreeAnalysis::CollectSequences(const mef::Branch& initial_state,
SequenceCollector* result) noexcept {
struct Collector {
class Visitor : public mef::InstructionVisitor {
public:
explicit Visitor(Collector* collector) : collector_(*collector) {}
void Visit(const mef::SetHouseEvent* house_event) override {
collector_.path_collector_.set_instructions[house_event->name()] =
house_event->state();
}
void Visit(const mef::Link* link) override {
is_linked_ = true;
Collector continue_connector(collector_);
auto save = std::move(collector_.result_->context.functional_events);
continue_connector(&link->event_tree().initial_state());
collector_.result_->context.functional_events = std::move(save);
}
void Visit(const mef::CollectFormula* collect_formula) override {
// clang-format off
collector_.path_collector_.formulas.push_back(
core::Clone(collect_formula->formula(),
collector_.path_collector_.set_instructions,
collector_.clones_));
// clang-format on
}
void Visit(const mef::CollectExpression* collect_expression) override {
collector_.path_collector_.expressions.push_back(
&collect_expression->expression());
}
bool is_linked() const { return is_linked_; }
private:
Collector& collector_;
bool is_linked_ = false;
};
void operator()(const mef::Sequence* sequence) {
Visitor visitor(this);
for (const mef::Instruction* instruction : sequence->instructions())
instruction->Accept(&visitor);
if (!visitor.is_linked())
result_->sequences[sequence].push_back(std::move(path_collector_));
}
void operator()(const mef::Fork* fork) const {
const std::string& name = fork->functional_event().name();
assert(result_->context.functional_events.count(name) == false);
std::string& state = result_->context.functional_events[name];
assert(state.empty());
for (const mef::Path& fork_path : fork->paths()) {
state = fork_path.state();
// clang-format off
Collector(*this)(&fork_path); // NOLINT(runtime/explicit)
// clang-format on
}
result_->context.functional_events.erase(name);
}
void operator()(const mef::Branch* branch) {
Visitor visitor(this);
for (const mef::Instruction* instruction : branch->instructions())
instruction->Accept(&visitor);
std::visit(*this, branch->target());
}
SequenceCollector* result_;
std::vector<std::unique_ptr<mef::Event>>* clones_;
PathCollector path_collector_;
};
context_->functional_events.clear();
context_->initiating_event = initiating_event_.name();
Collector{result, &events_}(&initial_state); // NOLINT(whitespace/braces)
}
} // namespace scram::coreUpdated on 2025-11-11 at 16:51:08 +0000
