https://mooseframework.inl.gov
Classes | Typedefs | Enumerations | Functions
CapabilityUtils Namespace Reference

Shared code for the Capabilities Registry and the python bindings to the Capabilities system. More...

Classes

class  CapabilityException
 

Typedefs

typedef std::variant< bool, int, std::string > Type
 A capability can have a bool, int, or string value. More...
 
typedef std::tuple< CheckState, std::string, std::string > Result
 Result from a capability check: the state, the reason, and the documentation. More...
 
typedef std::map< std::string, std::pair< Type, std::string > > Registry
 The registry that stores the registered capabilities. More...
 

Enumerations

enum  CheckState {
  CERTAIN_FAIL = 0, POSSIBLE_FAIL = 1, UNKNOWN = 2, POSSIBLE_PASS = 3,
  CERTAIN_PASS = 4, PARSE_FAIL = 5
}
 Return state for check. More...
 

Functions

Result check (std::string requirements, const Registry &capabilities)
 Checks if a set of requirements is satisified by the given capability registry. More...
 

Detailed Description

Shared code for the Capabilities Registry and the python bindings to the Capabilities system.

Typedef Documentation

◆ Registry

typedef std::map<std::string, std::pair<Type, std::string> > CapabilityUtils::Registry

The registry that stores the registered capabilities.

Definition at line 79 of file CapabilityUtils.h.

◆ Result

typedef std::tuple<CheckState, std::string, std::string> CapabilityUtils::Result

Result from a capability check: the state, the reason, and the documentation.

Definition at line 77 of file CapabilityUtils.h.

◆ Type

typedef std::variant<bool, int, std::string> CapabilityUtils::Type

A capability can have a bool, int, or string value.

Definition at line 75 of file CapabilityUtils.h.

Enumeration Type Documentation

◆ CheckState

Return state for check.

We use a plain enum because we rely on implicit conversion to int. Capability checks are run in the test harness using the JSON dump exported from the executable using --show-capabilities. This static check does not take dynamic loading into account, as capabilities that would be registered after initializing the dynamically loaded application will not exist with --show-capabilities.

A requested capability that is not registered at all is considered in a "possible" state, as we cannot guarantee that it does or not exist with a dynamic application. If no dynamic application loading is used, the possible states can be considered certain states.

When the test harness Tester specification "dynamic_capabilities" is set to True, it will run the test unless the result of the check is CERTAIN_FAIL. In this case, the runtime check in the executable will terminate if the result is either CERTAIN_FAIL or POSSIBLE_FAIL.

Enumerator
CERTAIN_FAIL 
POSSIBLE_FAIL 
UNKNOWN 
POSSIBLE_PASS 
CERTAIN_PASS 
PARSE_FAIL 

Definition at line 64 of file CapabilityUtils.h.

Function Documentation

◆ check()

Result CapabilityUtils::check ( std::string  requirements,
const Registry capabilities 
)

Checks if a set of requirements is satisified by the given capability registry.

Parameters
requirementsThe requirement string
capabilitiesThe registry that contains the capabilities

This method is exposed to Python within the capabilities_check method in framework/contrib/capabilities/capabilities.C. This external method is used significantly by the TestHarness to check capabilities for individual test specs.

Additionally, this method is used by the MooseApp command line option "--required-capabilities ...".

Requirements can use comparison operators (>,<,>=,<=,=!,=), where the name of the capability must always be on the left hand side. Comparisons can be performed on strings "compiler!=GCC" (which are case insensitive), integer numbers "ad_size>=50", and version numbers "petsc>3.8.0". The state of a boolean valued capability can be tested by just specifying the capability name "chaco". This check can be inverted using the ! operator as "!chaco".

The logic operators & and | can be used to chain multiple checks as "thermochimica & thermochimica>1.0". Parenthesis can be used to build complex logic expressions.

See the description for CheckState for more information on why a certain state would be returned.

Definition at line 20 of file CapabilityUtils.C.

Referenced by CommandLine::buildHitParams(), Moose::Capabilities::check(), BoundaryNodeIntegrityCheckThread::onNode(), and BoundaryElemIntegrityCheckThread::operator()().

21 {
22  using namespace peg;
23 
24  // unquote
25  while (true)
26  {
27  const auto len = requirements.length();
28  if (len >= 2 && ((requirements[0] == '\'' && requirements[len - 1] == '\'') ||
29  (requirements[0] == '"' && requirements[len - 1] == '"')))
30  requirements = requirements.substr(1, len - 2);
31  else
32  break;
33  }
34  if (requirements.length() == 0)
35  return {CapabilityUtils::CERTAIN_PASS, "Empty requirements", ""};
36 
37  parser parser(R"(
38  Expression <- _ Bool _ LogicOperator _ Expression / Bool _
39  Bool <- Comparison / '!' Bool / '!' Identifier / Identifier / '(' _ Expression _ ')'
40  Comparison <- Identifier _ Operator _ Version / Identifier _ Operator _ String
41  String <- [a-zA-Z0-9_-]+
42  Identifier <- [a-zA-Z][a-zA-Z0-9_]*
43  Operator <- [<>=!]+
44  LogicOperator <- [&|]
45  Version <- Number '.' Version / Number
46  Number <- [0-9]+
47  ~_ <- [ \t]*
48  )");
49 
50  if (!static_cast<bool>(parser))
51  throw CapabilityException("Capabilities parser build failure.");
52 
53  parser["Number"] = [](const SemanticValues & vs) { return vs.token_to_number<int>(); };
54 
55  parser["Version"] = [](const SemanticValues & vs)
56  {
57  switch (vs.choice())
58  {
59  case 0: // Number '.' Version
60  {
61  std::vector<int> ret{std::any_cast<int>(vs[0])};
62  const auto & vs1 = std::any_cast<std::vector<int>>(vs[1]);
63  ret.insert(ret.end(), vs1.begin(), vs1.end());
64  return ret;
65  }
66 
67  case 1: // Number
68  return std::vector<int>{std::any_cast<int>(vs[0])};
69 
70  default:
71  throw CapabilityException("Unknown Number match");
72  }
73  };
74 
75  enum LogicOperator
76  {
77  OP_AND,
78  OP_OR
79  };
80 
81  parser["LogicOperator"] = [](const SemanticValues & vs)
82  {
83  const auto op = vs.token();
84  if (op == "&")
85  return OP_AND;
86  if (op == "|")
87  return OP_OR;
88  throw CapabilityException("Unknown logic operator.");
89  };
90 
91  enum Operator
92  {
93  OP_LESS_EQ,
94  OP_GREATER_EQ,
95  OP_LESS,
96  OP_GREATER,
97  OP_NOT_EQ,
98  OP_EQ
99  };
100 
101  parser["Operator"] = [](const SemanticValues & vs)
102  {
103  const auto op = vs.token();
104  if (op == "<=")
105  return OP_LESS_EQ;
106  if (op == ">=")
107  return OP_GREATER_EQ;
108  if (op == "<")
109  return OP_LESS;
110  if (op == ">")
111  return OP_GREATER;
112  if (op == "!=")
113  return OP_NOT_EQ;
114  if (op == "=" || op == "==")
115  return OP_EQ;
116  throw CapabilityException("Unknown operator '", op, "'.");
117  };
118 
119  parser["String"] = [](const SemanticValues & vs) { return vs.token_to_string(); };
120  parser["Identifier"] = [](const SemanticValues & vs) { return vs.token_to_string(); };
121 
122  parser["Comparison"] = [&app_capabilities](const SemanticValues & vs)
123  {
124  const auto left = std::any_cast<std::string>(vs[0]);
125  const auto op = std::any_cast<Operator>(vs[1]);
126 
127  // check existence
128  const auto it = app_capabilities.find(left);
129  if (it == app_capabilities.end())
130  // return an unknown if the capability does not exist, this is important as it
131  // stays unknown upon negation
132  return CheckState::UNKNOWN;
133 
134  // capability is registered by the app
135  const auto & [app_value, doc] = it->second;
136 
137  // explicitly false causes any comparison to fail
138  if (std::holds_alternative<bool>(app_value) && std::get<bool>(app_value) == false)
140 
141  // comparator
142  auto comp = [](int i, auto a, auto b)
143  {
144  switch (i)
145  {
146  case OP_LESS_EQ:
147  return a <= b;
148  case OP_GREATER_EQ:
149  return a >= b;
150  case OP_LESS:
151  return a < b;
152  case OP_GREATER:
153  return a > b;
154  case OP_NOT_EQ:
155  return a != b;
156  case OP_EQ:
157  return a == b;
158  }
159  return false;
160  };
161 
162  switch (vs.choice())
163  {
164  case 0: // Identifier _ Operator _ Version
165  {
166  // int comparison
167  const auto right = std::any_cast<std::vector<int>>(vs[2]);
168  if (std::holds_alternative<int>(app_value))
169  {
170  if (right.size() != 1)
171  throw CapabilityException("Expected an integer value in comparison");
172 
173  return comp(op, std::get<int>(app_value), right[0]) ? CheckState::CERTAIN_PASS
175  }
176 
177  // version comparison
178  std::vector<int> app_value_version;
179 
180  if (!std::holds_alternative<std::string>(app_value))
181  throw CapabilityException(
182  right.size() == 1 ? "Cannot compare capability " + left + " to a number."
183  : "Cannot compare capability " + left + " to a version number.");
184 
186  std::get<std::string>(app_value), app_value_version, "."))
187  throw CapabilityException("Expected a version number.");
188 
189  // compare versions
190  return comp(op, app_value_version, right) ? CheckState::CERTAIN_PASS
192  }
193 
194  case 1: // Identifier _ Operator _ String
195  {
196  const auto right = std::any_cast<std::string>(vs[2]);
197  // the app value has to be a string
198  if (!std::holds_alternative<std::string>(app_value))
199  throw CapabilityException("Unexpected comparison to a string.");
200 
201  return comp(op, std::get<std::string>(app_value), MooseUtils::toLower(right))
204  }
205  }
206 
207  throw CapabilityException("Failed comparison.");
208  };
209 
210  parser["Bool"] = [&app_capabilities](const SemanticValues & vs)
211  {
212  switch (vs.choice())
213  {
214  case 0: // Comparison
215  case 4: // '(' _ Expression _ ')'
216  return std::any_cast<CheckState>(vs[0]);
217 
218  case 1: // '!' Bool
219  switch (std::any_cast<CheckState>(vs[0]))
220  {
229  default:
230  return CheckState::UNKNOWN;
231  }
232 
233  case 2: // '!' Identifier
234  {
235  const auto it = app_capabilities.find(std::any_cast<std::string>(vs[0]));
236  if (it != app_capabilities.end())
237  {
238  const auto app_value = it->second.first;
239  if (std::holds_alternative<bool>(app_value) && std::get<bool>(app_value) == false)
242  }
244  }
245 
246  case 3: // Identifier
247  {
248  const auto it = app_capabilities.find(std::any_cast<std::string>(vs[0]));
249  if (it != app_capabilities.end())
250  {
251  const auto app_value = it->second.first;
252  if (std::holds_alternative<bool>(app_value) && std::get<bool>(app_value) == false)
255  }
257  }
258 
259  default:
260  throw CapabilityException("Unknown choice in Bool non-terminal");
261  }
262  };
263 
264  parser["Expression"] = [](const SemanticValues & vs)
265  {
266  switch (vs.choice())
267  {
268  case 0: // Bool _ LogicOperator _ Expression
269  {
270  const auto left = std::any_cast<CheckState>(vs[0]);
271  const auto op = std::any_cast<LogicOperator>(vs[1]);
272  const auto right = std::any_cast<CheckState>(vs[2]);
273 
274  switch (op)
275  {
276  case OP_AND:
277  for (const auto state : {CheckState::CERTAIN_FAIL,
279  CheckState::UNKNOWN,
282  if (left == state || right == state)
283  return state;
284  throw CapabilityException("Conjunction failure");
285 
286  case OP_OR:
287  for (const auto state : {CheckState::CERTAIN_PASS,
289  CheckState::UNKNOWN,
292  if (left == state || right == state)
293  return state;
294  throw CapabilityException("Conjunction failure");
295 
296  default:
297  throw CapabilityException("Unknown logic operator");
298  }
299  }
300 
301  case 1: // Bool
302  return std::any_cast<CheckState>(vs[0]);
303 
304  default:
305  throw CapabilityException("Unknown choice in Expression non-terminal");
306  }
307  };
308 
309  // (4) Parse
310  parser.enable_packrat_parsing(); // Enable packrat parsing.
311 
313  if (!parser.parse(requirements, state))
314  throw CapabilityException("Unable to parse requested capabilities '", requirements, "'.");
315 
316  std::string reason;
317  std::string doc;
318 
319  return {state, reason, doc};
320 }
std::string toLower(const std::string &name)
Convert supplied string to lower case.
bool tokenizeAndConvert(const std::string &str, std::vector< T > &tokenized_vector, const std::string &delimiter=" \\\)
tokenizeAndConvert splits a string using delimiter and then converts to type T.
CheckState
Return state for check.