Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DFTJsonParser.cpp
Go to the documentation of this file.
1#include "DFTJsonParser.h"
2
3#include <boost/algorithm/string.hpp>
4#include <iostream>
5
12#include "storm/io/file.h"
14
15namespace storm::dft {
16namespace parser {
17
18template<typename ValueType>
20 std::ifstream file;
21 storm::io::openFile(filename, file);
22 Json jsonInput;
23 file >> jsonInput;
25 return parseJson(jsonInput);
26}
27
28template<typename ValueType>
30 Json jsonInput = Json::parse(jsonString);
31 return parseJson(jsonInput);
32}
33
34template<typename ValueType>
36 // Initialize DFT builder and value parser
39 std::string toplevelName = "";
41
42 std::string currentLocation;
43 try {
44 // Try to parse parameters
45 currentLocation = "parameters";
46 if (jsonInput.count("parameters") > 0) {
47 Json parameters = jsonInput.at("parameters");
48 STORM_LOG_THROW(parameters.empty() || (std::is_same<ValueType, storm::RationalFunction>::value), storm::exceptions::NotSupportedException,
49 "Parameters are only allowed when using rational functions.");
50 for (auto const& parameter : parameters) {
51 valueParser.addParameter(parseValue(parameter));
52 }
53 }
54
55 currentLocation = "nodes";
56 Json const& nodes = jsonInput.at("nodes");
57 // Start by building mapping from ids to their unique names
58 std::map<std::string, std::string> nameMapping;
59 std::set<std::string> names;
60 for (auto const& element : nodes) {
61 Json data = element.at("data");
62 std::string id = data.at("id");
63 std::string name = parseName(data.at("name"));
64 STORM_LOG_THROW(names.find(name) == names.end(), storm::exceptions::WrongFormatException, "Element '" << name << "' was already declared.");
65 names.insert(name);
66 nameMapping[id] = name;
67 }
68
69 // Parse nodes
70 for (auto const& element : nodes) {
71 currentLocation = parseValue(element);
72 Json const& data = element.at("data");
73 std::string name = parseName(data.at("name"));
74 // TODO: use contains() if modernjson is updated
75 if (data.count("relevant") > 0) {
76 bool isRelevant = data.at("relevant");
77 if (isRelevant) {
78 relevantEvents.insert(name);
79 }
80 }
81 // Create list of children
82 std::vector<std::string> childNames;
83 // TODO: use contains() if modernjson is updated
84 if (data.count("children") > 0) {
85 for (auto const& child : data.at("children")) {
86 STORM_LOG_THROW(nameMapping.find(child) != nameMapping.end(), storm::exceptions::WrongFormatException,
87 "Child '" << child << "' for element '" << name << "' was not defined.");
88 childNames.push_back(nameMapping.at(child));
89 }
90 }
91
92 std::string type = data.at("type");
93 if (type == "and") {
94 builder.addAndGate(name, childNames);
95 } else if (type == "or") {
96 builder.addOrGate(name, childNames);
97 } else if (type == "vot") {
98 STORM_LOG_THROW(data.count("voting") > 0, storm::exceptions::WrongFormatException, "Voting gate '" << name << "' requires parameter 'voting'.");
99 std::string votThreshold = parseValue(data.at("voting"));
100 builder.addVotingGate(name, storm::parser::parseNumber<size_t>(votThreshold), childNames);
101 } else if (type == "pand") {
102 if (data.count("inclusive") > 0) {
103 bool inclusive = data.at("inclusive");
104 builder.addPandGate(name, childNames, inclusive);
105 } else {
106 builder.addPandGate(name, childNames);
107 }
108 } else if (type == "por") {
109 if (data.count("inclusive") > 0) {
110 bool inclusive = data.at("inclusive");
111 builder.addPorGate(name, childNames, inclusive);
112 } else {
113 builder.addPorGate(name, childNames);
114 }
115 } else if (type == "spare") {
116 builder.addSpareGate(name, childNames);
117 } else if (type == "seq") {
118 builder.addSequenceEnforcer(name, childNames);
119 } else if (type == "mutex") {
120 builder.addMutex(name, childNames);
121 } else if (type == "fdep") {
122 builder.addPdep(name, childNames, storm::utility::one<ValueType>());
123 } else if (type == "pdep") {
124 STORM_LOG_THROW(data.count("probability") > 0, storm::exceptions::WrongFormatException,
125 "PDEP '" << name << "' requires parameter 'probability'.");
126 ValueType probability = valueParser.parseValue(parseValue(data.at("probability")));
127 builder.addPdep(name, childNames, probability);
128 } else if (boost::starts_with(type, "be")) {
129 parseBasicElement(name, type, data, builder, valueParser);
130 } else if (type == "compound") {
131 STORM_LOG_TRACE("Ignoring compound node '" << name << "'.");
132 } else {
133 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Type name '" << type << "' not recognized.");
134 }
135
136 if (element.find("position") != element.end()) {
137 // Set layout positions
138 Json position = element.at("position");
139 double x = position.at("x");
140 double y = position.at("y");
141 builder.addLayoutInfo(name, x / 7, y / 7);
142 }
143 }
144
145 STORM_LOG_THROW(jsonInput.count("toplevel") > 0, storm::exceptions::WrongFormatException,
146 "Top level element must be specified via parameter 'toplevel'.");
147 std::string topLevelId = parseValue(jsonInput.at("toplevel"));
148 STORM_LOG_THROW(nameMapping.find(topLevelId) != nameMapping.end(), storm::exceptions::WrongFormatException,
149 "Top level element with id '" << topLevelId << "' was not defined.");
150 builder.setTopLevel(nameMapping.at(topLevelId));
151
152 } catch (storm::exceptions::BaseException const& exception) {
153 STORM_LOG_THROW(false, storm::exceptions::FileIoException, "A parsing exception occurred in " << currentLocation << ": " << exception.what());
154 } catch (std::exception const& exception) {
155 STORM_LOG_THROW(false, storm::exceptions::FileIoException, "An exception occurred during parsing in " << currentLocation << ": " << exception.what());
156 }
157
158 // Build DFT
160 STORM_LOG_DEBUG("DFT elements:\n" << dft.getElementsString());
161 STORM_LOG_DEBUG("Spare modules:\n" << dft.getModulesString());
162 // Set relevant events
163 dft.setRelevantEvents(relevantEvents, false);
164 STORM_LOG_DEBUG("Relevant events: " << dft.getRelevantEventsString());
165 return dft;
166}
167
168template<typename ValueType>
169std::string DFTJsonParser<ValueType>::parseName(std::string const& name) {
170 std::string newName = name;
171 std::replace(newName.begin(), newName.end(), ' ', '_');
172 std::replace(newName.begin(), newName.end(), '-', '_');
173 return newName;
174}
175
176template<typename ValueType>
177std::string DFTJsonParser<ValueType>::parseValue(Json value) {
178 if (value.is_string()) {
179 return value.get<std::string>();
180 } else {
181 std::stringstream stream;
182 stream << value;
183 return stream.str();
184 }
185}
186
187template<typename ValueType>
188void DFTJsonParser<ValueType>::parseBasicElement(std::string const& name, std::string const& type, Json input,
190 std::string distribution = "exponential"; // Default is exponential distribution
191 if (input.count("distribution") > 0) {
192 distribution = input.at("distribution");
193 // Handle short-form for exponential distribution
194 if (distribution == "exp") {
195 distribution = "exponential";
196 }
197 }
198 STORM_LOG_THROW(type == "be" || (type == "be_exp" && distribution == "exponential"), storm::exceptions::WrongFormatException,
199 "BE type '" << type << "' and distribution '" << distribution << " do not agree.");
200
201 if (distribution == "const") {
202 // Constant failed/failsafe
203 STORM_LOG_THROW(input.count("failed") > 0, storm::exceptions::WrongFormatException, "Constant BE '" << name << "' requires parameter 'failed'.");
204 bool failed = input.at("failed");
205 builder.addBasicElementConst(name, failed);
206 } else if (distribution == "probability") {
207 // Constant probability distribution
208 STORM_LOG_THROW(input.count("prob") > 0, storm::exceptions::WrongFormatException,
209 "BE '" << name << "' with probability distribution requires parameter 'prob'.");
210 ValueType probability = valueParser.parseValue(parseValue(input.at("prob")));
211 ValueType dormancy = storm::utility::one<ValueType>();
212 if (input.count("dorm") > 0) {
213 dormancy = valueParser.parseValue(parseValue(input.at("dorm")));
214 } else {
215 STORM_LOG_WARN("No dormancy factor was provided for basic element '" << name << "'. Assuming dormancy factor of 1.");
216 }
217 builder.addBasicElementProbability(name, probability, dormancy);
218 } else if (distribution == "exponential") {
219 // Exponential distribution
220 STORM_LOG_THROW(input.count("rate") > 0, storm::exceptions::WrongFormatException,
221 "BE '" << name << "' with exponential distribution requires parameter 'rate'.");
222 ValueType rate = valueParser.parseValue(parseValue(input.at("rate")));
223 ValueType dormancy = storm::utility::one<ValueType>();
224 if (input.count("dorm") > 0) {
225 dormancy = valueParser.parseValue(parseValue(input.at("dorm")));
226 } else {
227 STORM_LOG_WARN("No dormancy factor was provided for basic element '" << name << "'. Assuming dormancy factor of 1.");
228 }
229 bool transient = false;
230 if (input.count("transient") > 0) {
231 transient = input.at("transient");
232 }
233 builder.addBasicElementExponential(name, rate, dormancy, transient);
234 } else if (distribution == "erlang") {
235 // Erlang distribution
236 STORM_LOG_THROW(input.count("rate") > 0, storm::exceptions::WrongFormatException,
237 "BE '" << name << "' with Erlang distribution requires parameter 'rate'.");
238 ValueType rate = valueParser.parseValue(parseValue(input.at("rate")));
239 STORM_LOG_THROW(input.count("phases") > 0, storm::exceptions::WrongFormatException,
240 "BE '" << name << "' with Erlang distribution requires parameter 'phases'.");
241 size_t phases = storm::parser::parseNumber<size_t>(parseValue(input.at("phases")));
242 ValueType dormancy = storm::utility::one<ValueType>();
243 if (input.count("dorm") > 0) {
244 dormancy = valueParser.parseValue(parseValue(input.at("dorm")));
245 } else {
246 STORM_LOG_WARN("No dormancy factor was provided for basic element '" << name << "'. Assuming dormancy factor of 1.");
247 }
248 builder.addBasicElementErlang(name, rate, phases, dormancy);
249 } else if (distribution == "weibull") {
250 // Weibull distribution
251 STORM_LOG_THROW(input.count("shape") > 0, storm::exceptions::WrongFormatException,
252 "BE '" << name << "' with Weibull distribution requires parameter 'shape'.");
253 ValueType shape = valueParser.parseValue(parseValue(input.at("shape")));
254 STORM_LOG_THROW(input.count("rate") > 0, storm::exceptions::WrongFormatException,
255 "BE '" << name << "' with Weibull distribution requires parameter 'rate'.");
256 ValueType rate = valueParser.parseValue(parseValue(input.at("rate")));
257 builder.addBasicElementWeibull(name, shape, rate);
258 } else if (distribution == "lognormal") {
259 // Log-normal distribution
260 STORM_LOG_THROW(input.count("mean") > 0, storm::exceptions::WrongFormatException,
261 "BE '" << name << "' with Log-normal distribution requires parameter 'mean'.");
262 ValueType mean = valueParser.parseValue(parseValue(input.at("mean")));
263 STORM_LOG_THROW(input.count("stddev") > 0, storm::exceptions::WrongFormatException,
264 "BE '" << name << "' with Log-normal distribution requires parameter 'stddev'.");
265 ValueType stddev = valueParser.parseValue(parseValue(input.at("stddev")));
266 builder.addBasicElementLogNormal(name, mean, stddev);
267 } else {
268 STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Distribution " << distribution << " not known.");
269 }
270}
271
272// Explicitly instantiate the class.
273template class DFTJsonParser<double>;
274template class DFTJsonParser<RationalFunction>;
275
276} // namespace parser
277} // namespace storm::dft
void addPdep(std::string const &name, std::vector< std::string > const &children, ValueType probability)
Create (probabilistic) dependency (PDEP) and add it to DFT.
void addOrGate(std::string const &name, std::vector< std::string > const &children)
Create OR-gate and add it to DFT.
void addVotingGate(std::string const &name, unsigned threshold, std::vector< std::string > const &children)
Create VOTing-gate and add it to DFT.
void addBasicElementErlang(std::string const &name, ValueType rate, unsigned phases, ValueType dormancyFactor)
Create BE with Erlang distribution and add it to DFT.
void addLayoutInfo(std::string const &name, double x, double y)
Add layout information for DFT element.
void addSpareGate(std::string const &name, std::vector< std::string > const &children)
Create SPARE-gate and add it to DFT.
void addBasicElementConst(std::string const &name, bool failed)
Create BE which is constant failed or constant failsafe and add it to DFT.
storm::dft::storage::DFT< ValueType > build()
Create DFT.
void addAndGate(std::string const &name, std::vector< std::string > const &children)
Create AND-gate and add it to DFT.
void addBasicElementProbability(std::string const &name, ValueType probability, ValueType dormancyFactor)
Create BE with constant (Bernoulli) distribution and add it to DFT.
void setTopLevel(std::string const &tle)
Set top level element.
void addBasicElementExponential(std::string const &name, ValueType rate, ValueType dormancyFactor, bool transient=false)
Create BE with exponential distribution and add it to DFT.
void addPorGate(std::string const &name, std::vector< std::string > const &children, bool inclusive=true)
Create POR-gate and add it to DFT.
void addSequenceEnforcer(std::string const &name, std::vector< std::string > const &children)
Create sequence enforcer (SEQ) and add it to DFT.
void addPandGate(std::string const &name, std::vector< std::string > const &children, bool inclusive=true)
Create PAND-gate and add it to DFT.
void addBasicElementWeibull(std::string const &name, ValueType shape, ValueType rate)
Create BE with Weibull distribution and add it to DFT.
void addBasicElementLogNormal(std::string const &name, ValueType mean, ValueType standardDeviation)
Create BE with log-normal distribution and add it to DFT.
void addMutex(std::string const &name, std::vector< std::string > const &children)
Create mutual exclusion-gate (MUTEX) and add it to DFT.
Parser for DFT in custom JSON format.
static storm::dft::storage::DFT< ValueType > parseJsonFromFile(std::string const &filename)
Parse DFT from JSON format given as file and build DFT.
static storm::dft::storage::DFT< ValueType > parseJsonFromString(std::string const &jsonString)
Parse DFT from JSON format given as a string and build DFT.
Represents a Dynamic Fault Tree.
Definition DFT.h:52
std::string getRelevantEventsString() const
Get a string containing the list of all relevant events.
Definition DFT.cpp:716
void setRelevantEvents(storm::dft::utility::RelevantEvents const &relevantEvents, bool const allowDCForRelevant) const
Set the relevance flag for all elements according to the given relevant events.
Definition DFT.cpp:690
std::string getModulesString() const
Definition DFT.cpp:527
std::string getElementsString() const
Definition DFT.cpp:511
void insert(std::string const &name)
Add relevant event.
This class represents the base class of all exception classes.
virtual const char * what() const NOEXCEPT override
Retrieves the message associated with this exception.
Parser for values according to their ValueType.
Definition ValueParser.h:23
void addParameter(std::string const &parameter)
Add declaration of parameter.
ValueType parseValue(std::string const &value) const
Parse ValueType from string.
#define STORM_LOG_WARN(message)
Definition logging.h:30
#define STORM_LOG_DEBUG(message)
Definition logging.h:23
#define STORM_LOG_TRACE(message)
Definition logging.h:17
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
SFTBDDChecker::ValueType ValueType
void closeFile(std::ofstream &stream)
Close the given file after writing.
Definition file.h:47
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
Definition file.h:18