https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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.
 

Public Member Functions

 ~CapabilityRegistry ()=default
 
Capabilityadd (const std::string_view name, const Moose::Capability::Value &value, const std::string_view doc)
 Add a capability.
 
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.
 
const Capabilityquery (std::string capability) const
 Query a capability, if it exists, otherwise nullptr.
 
Capabilityquery (std::string capability)
 
const Capabilityget (const std::string &capability) const
 Get a capability.
 
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.
 

Protected Attributes

RegistryType _registry
 Registry storage.
 

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.

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}
RegistryType _registry
Registry storage.

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

◆ 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)
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 };
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)
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))
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 {
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);
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,
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,
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}
unsigned int count
Definition MortarUtils.C:53
if(!dmm->_nl) SETERRQ(PETSC_COMM_WORLD
const Capability * query(std::string capability) const
Query a capability, if it exists, otherwise nullptr.
bool tokenizeAndConvert(const std::string &str, std::vector< T > &tokenized_vector, const std::string &delimiter=" \t\n\v\f\r")
tokenizeAndConvert splits a string using delimiter and then converts to type T.
std::string toLower(std::string name)
Convert supplied string to lower case.
void checkException(const peg::SemanticValues &vs, const std::string &message, const std::optional< Capability > capability={})
std::string name(const ElemQuality q)

◆ get() [1/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}

◆ get() [2/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.

67{
68 if (const auto capability_ptr = query(capability))
69 return *capability_ptr;
70 throw CapabilityException("Capability '" + capability + "' not registered");
71}

Referenced by Moose::internal::Capabilities::isInstallationType(), and MooseApp::setupOptions().

◆ query() [1/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}

◆ query() [2/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.

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}

Referenced by check(), and get().

◆ 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(); }

Friends And Related Symbol 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: