https://mooseframework.inl.gov
Classes | Public Types | Public Member Functions | Static Public Attributes | Protected Attributes | Friends | List of all members
Moose::internal::CapabilityRegistry Class Reference

Registry of capabilities that checks capability requirements. More...

#include <CapabilityRegistry.h>

Inheritance diagram for Moose::internal::CapabilityRegistry:
[legend]

Classes

struct  CheckOptions
 Options for check(). More...
 
struct  CheckResult
 Storage for the result from check(). More...
 

Public Types

enum  CheckState {
  CERTAIN_FAIL = 0, POSSIBLE_FAIL = 1, UNKNOWN = 2, POSSIBLE_PASS = 3,
  CERTAIN_PASS = 4, IGNORE = 5
}
 Return state for check. More...
 
using RegistryType = std::map< std::string, Capability, std::less<> >
 Type for the registry. More...
 

Public Member Functions

 ~CapabilityRegistry ()=default
 
Capabilityadd (const std::string_view name, const Moose::Capability::Value &value, const std::string_view doc)
 Add a capability. More...
 
std::size_t size () const
 
CheckResult check (std::string requirements, const CapabilityRegistry::CheckOptions &options=CapabilityRegistry::CheckOptions()) const
 Checks if a set of requirements is satisified by the capabilities. More...
 
const Capabilityquery (std::string capability) const
 Query a capability, if it exists, otherwise nullptr. More...
 
Capabilityquery (std::string capability)
 
const Capabilityget (const std::string &capability) const
 Get a capability. More...
 
Capabilityget (const std::string &capability)
 

Static Public Attributes

static const std::set< std::string, std::less<> > augmented_capability_names
 Capabilities that are reserved and can only be augmented. More...
 

Protected Attributes

RegistryType _registry
 Registry storage. More...
 

Friends

class ::CapabilitiesTest
 

Detailed Description

Registry of capabilities that checks capability requirements.

This registry is used both within MOOSE (in framework/src/base/Capabilities.C) and within the python interface (in python/pycapabilities/_pycapabilities.C).

Definition at line 34 of file CapabilityRegistry.h.

Member Typedef Documentation

◆ RegistryType

using Moose::internal::CapabilityRegistry::RegistryType = std::map<std::string, Capability, std::less<> >

Type for the registry.

Definition at line 43 of file CapabilityRegistry.h.

Member Enumeration 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 
IGNORE 

Definition at line 61 of file CapabilityRegistry.h.

Constructor & Destructor Documentation

◆ ~CapabilityRegistry()

Moose::internal::CapabilityRegistry::~CapabilityRegistry ( )
default

Member Function Documentation

◆ add()

Capability & Moose::internal::CapabilityRegistry::add ( const std::string_view  name,
const Moose::Capability::Value value,
const std::string_view  doc 
)

Add a capability.

Parameters
registryThe registry
capabilityThe name of the capability
valueThe value of the capability
docThe documentation string
Returns
The capability

Definition at line 34 of file CapabilityRegistry.C.

Referenced by Registry::addDataFilePathCapability(), Moose::internal::Capabilities::augment(), AppFactory::registerAppCapability(), and Moose::internal::Capabilities::registerMooseCapabilities().

37 {
38  auto it_pair = _registry.lower_bound(name);
39  if (it_pair != _registry.end() && it_pair->first == name)
40  {
41  auto & capability = it_pair->second;
42  if (capability.getValue() != value || capability.getDoc() != doc)
43  throw CapabilityException("Capability '" + std::string(name) +
44  "' already exists and is not equal");
45  return capability;
46  }
47 
48  return _registry
49  .emplace_hint(it_pair,
50  std::piecewise_construct,
51  std::forward_as_tuple(name),
52  std::forward_as_tuple(name, value, doc))
53  ->second;
54 }
std::string name(const ElemQuality q)
RegistryType _registry
Registry storage.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)

◆ check()

CapabilityRegistry::CheckResult Moose::internal::CapabilityRegistry::check ( std::string  requirements,
const CapabilityRegistry::CheckOptions options = CapabilityRegistry::CheckOptions() 
) const

Checks if a set of requirements is satisified by the capabilities.

Parameters
requirementsThe requirement string
optionsOptions to apply to the check

This method is exposed to Python within pycapabilities.Capabilities.check in python/pycapabilities/_pycapabilities.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.

Definition at line 86 of file CapabilityRegistry.C.

89 {
90  using namespace peg;
91 
92  // unquote
93  while (true)
94  {
95  const auto len = requirements.length();
96  if (len >= 2 && ((requirements[0] == '\'' && requirements[len - 1] == '\'') ||
97  (requirements[0] == '"' && requirements[len - 1] == '"')))
98  requirements = requirements.substr(1, len - 2);
99  else
100  break;
101  }
102 
103  CheckResult result;
104  result.state = CheckState::CERTAIN_FAIL;
105 
106  if (requirements.length() == 0)
107  {
108  result.state = CheckState::CERTAIN_PASS;
109  return result;
110  }
111 
112  static parser parser(R"(
113  Expression <- _ Bool _ LogicOperator _ Expression / Bool _
114  Bool <- Comparison / '!' Bool / '!' Identifier / Identifier / '(' _ Expression _ ')'
115  Comparison <- Identifier _ Operator _ Version / Identifier _ Operator _ String
116  String <- [a-zA-Z0-9_-]+
117  Identifier <- [a-zA-Z][a-zA-Z0-9_]*
118  Operator <- [<>=!]+
119  LogicOperator <- [&|]
120  Version <- Number '.' Version / Number
121  Number <- [0-9]+
122  ~_ <- [ \t]*
123  )");
124 
125  if (!static_cast<bool>(parser))
126  throw CapabilityException("Capabilities parser build failure.");
127 
128  // Keep track of unknown capabilities in the event that
129  // the check must be certain
130  std::set<std::string> unknown_capabilities;
131  const auto add_unknown_capability = [&unknown_capabilities](const auto & name)
132  { unknown_capabilities.insert(MooseUtils::toLower(name)); };
133 
134  // Make sure that the capabilities to ignore are valid capabilities
135  for (const auto & name : options.ignore_capabilities)
136  if (!_registry.count(name))
137  throw CapabilityException("Capability to ignore '" + name + "' is not known");
138 
139  parser["Number"] = [](const SemanticValues & vs) { return vs.token_to_number<int>(); };
140 
141  parser["Version"] = [](const SemanticValues & vs)
142  {
143  switch (vs.choice())
144  {
145  case 0: // Number '.' Version
146  {
147  std::vector<int> ret{std::any_cast<int>(vs[0])};
148  const auto & vs1 = std::any_cast<std::vector<int>>(vs[1]);
149  ret.insert(ret.end(), vs1.begin(), vs1.end());
150  return ret;
151  }
152 
153  case 1: // Number
154  return std::vector<int>{std::any_cast<int>(vs[0])};
155  }
156 
157  checkException(vs, "unknown number match.");
158  };
159 
160  enum LogicOperator
161  {
162  OP_AND,
163  OP_OR
164  };
165 
166  parser["LogicOperator"] = [](const SemanticValues & vs)
167  {
168  const auto op = vs.token();
169  if (op == "&")
170  return OP_AND;
171  if (op == "|")
172  return OP_OR;
173  checkException(vs, "unknown logic operator.");
174  };
175 
176  enum Operator
177  {
178  OP_LESS_EQ,
179  OP_GREATER_EQ,
180  OP_LESS,
181  OP_GREATER,
182  OP_NOT_EQ,
183  OP_EQ
184  };
185 
186  parser["Operator"] = [](const SemanticValues & vs)
187  {
188  const auto op = vs.token();
189  if (op == "<=")
190  return OP_LESS_EQ;
191  if (op == ">=")
192  return OP_GREATER_EQ;
193  if (op == "<")
194  return OP_LESS;
195  if (op == ">")
196  return OP_GREATER;
197  if (op == "!=")
198  return OP_NOT_EQ;
199  if (op == "=" || op == "==")
200  return OP_EQ;
201  checkException(vs, "unknown operator.");
202  };
203 
204  parser["String"] = [](const SemanticValues & vs) { return vs.token_to_string(); };
205  parser["Identifier"] = [](const SemanticValues & vs) { return vs.token_to_string(); };
206 
207  parser["Comparison"] =
208  [this, &add_unknown_capability, &options, &result](const SemanticValues & vs)
209  {
210  const auto left = std::any_cast<std::string>(vs[0]);
211  const auto op = std::any_cast<Operator>(vs[1]);
212 
213  // check existence
214  const auto capability_ptr = query(left);
215  if (!capability_ptr)
216  {
217  // return an unknown if the capability does not exist, this is important as it
218  // stays unknown upon negation
219  add_unknown_capability(left);
220  return CheckState::UNKNOWN;
221  }
222 
223  // capability is registered by the app
224  const auto & capability = *capability_ptr;
225  const auto & name = capability.getName();
226 
227  // register capability as seen
228  result.capability_names.insert(name);
229 
230  // whether or not the capability is ignored
231  const auto is_ignored = [&name, &options]() { return options.ignore_capabilities.count(name); };
232 
233  // explicitly false causes any comparison to fail unless ignored
234  if (const auto bool_ptr = capability.queryBoolValue(); (bool_ptr && !(*bool_ptr)))
235  return is_ignored() ? CheckState::IGNORE : CheckState::CERTAIN_FAIL;
236 
237  // comparator
238  auto comp = [&is_ignored](const int i, const auto & a, const auto & b)
239  {
240  // early exit for ignored capabilities
241  if (is_ignored())
242  return CheckState::IGNORE;
243 
244  // do the comparison
245  const auto do_comp = [&i, &a, &b]()
246  {
247  switch (i)
248  {
249  case OP_LESS_EQ:
250  return a <= b;
251  case OP_GREATER_EQ:
252  return a >= b;
253  case OP_LESS:
254  return a < b;
255  case OP_GREATER:
256  return a > b;
257  case OP_NOT_EQ:
258  return a != b;
259  case OP_EQ:
260  return a == b;
261  }
262  return false;
263  };
264  return do_comp() ? CheckState::CERTAIN_PASS : CheckState::CERTAIN_FAIL;
265  };
266 
267  // version comparison
268  std::vector<int> app_value_version;
269 
270  switch (vs.choice())
271  {
272  case 0: // Identifier _ Operator _ Version
273  {
274  // int comparison
275  const auto right = std::any_cast<std::vector<int>>(vs[2]);
276  if (const auto int_ptr = capability.queryIntValue())
277  {
278  if (right.size() != 1)
279  checkException(vs, "cannot be compared to a version.", capability);
280 
281  return comp(op, *int_ptr, right[0]);
282  }
283 
284  const auto string_ptr = capability.queryStringValue();
285  if (!string_ptr)
286  checkException(vs,
287  "cannot be compared to a " +
288  std::string(right.size() == 1 ? "number" : "version number") + ".",
289  capability);
290 
291  if (!MooseUtils::tokenizeAndConvert(*string_ptr, app_value_version, "."))
292  checkException(vs, "cannot be compared to a version.", capability);
293 
294  // compare versions
295  return comp(op, app_value_version, right);
296  }
297 
298  case 1: // Identifier _ Operator _ String
299  {
300  // here we would check for valid options and throw if not valid
301  const auto right = MooseUtils::toLower(std::any_cast<std::string>(vs[2]));
302  // the capability value has to be a string
303  const auto string_ptr = capability.queryStringValue();
304  if (!string_ptr)
305  checkException(vs, "cannot be compared to a string.", capability);
306 
307  // If this capability has an enumeration, make sure a valid
308  // choice is used
309  if (!capability.hasEnumeration(right))
310  checkException(vs,
311  "'" + right + "' invalid for capability '" + left +
312  "'; valid values: " + capability.enumerationToString());
313 
314  // Capability is a version
315  if (MooseUtils::tokenizeAndConvert(*string_ptr, app_value_version, "."))
316  checkException(vs, "cannot be compared to a string.", capability);
317 
318  return comp(op, *string_ptr, right);
319  }
320  }
321 
322  checkException(vs, "failed comparison.", capability);
323  };
324 
325  parser["Bool"] = [this, &add_unknown_capability, &options, &result](const SemanticValues & vs)
326  {
327  switch (vs.choice())
328  {
329  case 0: // Comparison
330  case 4: // '(' _ Expression _ ')'
331  return std::any_cast<CheckState>(vs[0]);
332 
333  case 1: // '!' Bool
334  switch (std::any_cast<CheckState>(vs[0]))
335  {
336  case CheckState::CERTAIN_FAIL:
337  return CheckState::CERTAIN_PASS;
338  case CheckState::CERTAIN_PASS:
339  return CheckState::CERTAIN_FAIL;
340  case CheckState::POSSIBLE_FAIL:
341  return CheckState::POSSIBLE_PASS;
342  case CheckState::POSSIBLE_PASS:
343  return CheckState::POSSIBLE_FAIL;
344  case CheckState::IGNORE:
345  return CheckState::IGNORE;
346  default:
347  return CheckState::UNKNOWN;
348  }
349 
350  case 2: // '!' Identifier
351  case 3: // Identifier
352  {
353  const bool negated = vs.choice() == 2;
354  const auto identifier = std::any_cast<std::string>(vs[0]);
355  if (const auto capability_ptr = query(identifier))
356  {
357  const auto & capability = *capability_ptr;
358  const auto & name = capability.getName();
359 
360  // explicit; cannot be a bool expression
361  if (capability.getExplicit())
362  {
363  std::string message = "capability '" + name +
364  "' requires a value and cannot be used in a boolean expression";
365  if (capability.queryEnumeration())
366  message += "; valid values: " + capability.enumerationToString();
367  checkException(vs, message);
368  }
369 
370  // mark as used
371  result.capability_names.insert(name);
372  // is ignored
373  if (options.ignore_capabilities.count(name))
374  return CheckState::IGNORE;
375 
376  // helper for negating a passing value if needed
377  const auto bool_to_pass = [&negated](const bool val)
378  { return (val ^ negated) ? CheckState::CERTAIN_FAIL : CheckState::CERTAIN_PASS; };
379  // has a boolean value, so use it
380  if (const auto bool_ptr = capability.queryBoolValue())
381  return bool_to_pass(!*bool_ptr);
382  // not ignored and doesn't have a boolean value
383  return bool_to_pass(false);
384  }
385 
386  add_unknown_capability(identifier);
387  return negated ? CheckState::POSSIBLE_PASS : CheckState::POSSIBLE_FAIL;
388  }
389 
390  default:
391  throw CapabilityException("Unknown choice in Bool non-terminal");
392  }
393  };
394 
395  parser["Expression"] = [](const SemanticValues & vs)
396  {
397  switch (vs.choice())
398  {
399  case 0: // Bool _ LogicOperator _ Expression
400  {
401  const auto left = std::any_cast<CheckState>(vs[0]);
402  const auto right = std::any_cast<CheckState>(vs[2]);
403  const auto op = std::any_cast<LogicOperator>(vs[1]);
404 
405  switch (op)
406  {
407  case OP_AND:
408  if (left == CheckState::IGNORE)
409  return right;
410  if (right == CheckState::IGNORE)
411  return left;
412  for (const auto state : {CheckState::CERTAIN_FAIL,
413  CheckState::POSSIBLE_FAIL,
414  CheckState::UNKNOWN,
415  CheckState::POSSIBLE_PASS,
416  CheckState::CERTAIN_PASS})
417  if (left == state || right == state)
418  return state;
419  throw CapabilityException("Conjunction failure");
420 
421  case OP_OR:
422  if (left == CheckState::IGNORE || right == CheckState::IGNORE)
423  return CheckState::IGNORE;
424  for (const auto state : {CheckState::CERTAIN_PASS,
425  CheckState::POSSIBLE_PASS,
426  CheckState::UNKNOWN,
427  CheckState::POSSIBLE_FAIL,
428  CheckState::CERTAIN_FAIL})
429  if (left == state || right == state)
430  return state;
431  throw CapabilityException("Conjunction failure");
432 
433  default:
434  throw CapabilityException("Unknown logic operator");
435  }
436  }
437 
438  case 1: // Bool
439  return std::any_cast<CheckState>(vs[0]);
440 
441  default:
442  throw CapabilityException("Unknown choice in Expression non-terminal");
443  }
444  };
445 
446  // (4) Parse
447  parser.enable_packrat_parsing(); // Enable packrat parsing.
448 
449  if (!parser.parse(requirements, result.state))
450  throw CapabilityException("Unable to parse requested capabilities '", requirements, "'.");
451 
452  // If certain and unknown capabilities were found, throw accordingly
453  if (options.certain && unknown_capabilities.size())
454  throw UnknownCapabilitiesException({unknown_capabilities.begin(), unknown_capabilities.end()});
455 
456  // Consider an ignored state to be a pass
457  if (result.state == CheckState::IGNORE)
458  result.state = CheckState::CERTAIN_PASS;
459 
460  return result;
461 }
std::string name(const ElemQuality q)
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.
RegistryType _registry
Registry storage.
void checkException(const peg::SemanticValues &vs, const std::string &message, const std::optional< Capability > capability={})
std::string toLower(std::string name)
Convert supplied string to lower case.
const Capability * query(std::string capability) const
Query a capability, if it exists, otherwise nullptr.

◆ get() [1/2]

const Capability & Moose::internal::CapabilityRegistry::get ( const std::string &  capability) const

Get a capability.

Will convert the capability name to lowercase.

Definition at line 66 of file CapabilityRegistry.C.

Referenced by MooseApp::setupOptions().

67 {
68  if (const auto capability_ptr = query(capability))
69  return *capability_ptr;
70  throw CapabilityException("Capability '" + capability + "' not registered");
71 }
const Capability * query(std::string capability) const
Query a capability, if it exists, otherwise nullptr.

◆ get() [2/2]

Capability & Moose::internal::CapabilityRegistry::get ( const std::string &  capability)
inline

Definition at line 184 of file CapabilityRegistry.h.

185 {
186  return const_cast<Capability &>(std::as_const(*this).get(capability));
187 }

◆ query() [1/2]

const Capability * Moose::internal::CapabilityRegistry::query ( std::string  capability) const

Query a capability, if it exists, otherwise nullptr.

Will convert the capability name to lowercase.

Definition at line 57 of file CapabilityRegistry.C.

Referenced by check(), and get().

58 {
59  capability = MooseUtils::toLower(capability);
60  if (const auto it = _registry.find(capability); it != _registry.end())
61  return &it->second;
62  return nullptr;
63 }
RegistryType _registry
Registry storage.
std::string toLower(std::string name)
Convert supplied string to lower case.

◆ query() [2/2]

Capability * Moose::internal::CapabilityRegistry::query ( std::string  capability)
inline

Definition at line 176 of file CapabilityRegistry.h.

177 {
178  return const_cast<Capability *>(std::as_const(*this).query(capability));
179 }

◆ size()

std::size_t Moose::internal::CapabilityRegistry::size ( ) const
inline
Returns
The size of the registry (number of capabilities registered).

Definition at line 135 of file CapabilityRegistry.h.

135 { return _registry.size(); }
RegistryType _registry
Registry storage.

Friends And Related Function Documentation

◆ ::CapabilitiesTest

friend class ::CapabilitiesTest
friend

Definition at line 167 of file CapabilityRegistry.h.

Member Data Documentation

◆ _registry

RegistryType Moose::internal::CapabilityRegistry::_registry
protected

Registry storage.

Definition at line 171 of file CapabilityRegistry.h.

Referenced by add(), check(), Moose::internal::Capabilities::dump(), query(), and size().

◆ augmented_capability_names

const std::set< std::string, std::less<> > Moose::internal::CapabilityRegistry::augmented_capability_names
static
Initial value:
{
"hpc",
"machine",
"library_mode",
"mpi_procs",
"num_threads"}

Capabilities that are reserved and can only be augmented.

Definition at line 38 of file CapabilityRegistry.h.


The documentation for this class was generated from the following files: