Check a model¶
Two checker classes are provided in libCellML; the Validator and the Analyser classes.
These can be used to check the syntax and structure of your model is correct (validating), and to check that its mathematical representation is solvable (analysing).
Validate a model¶
The Validator is the equivalent of a spelling checker: it can check that each item in a model has all of the information it needs, but it can’t check whether it means what you intend it to.
Thus even if a model is valid, it could still be the equivalent of correctly-spelled nonsense.
Validate a model against the CellML specification
// Validate the model: check for syntactic and semantic errors.
// Create a Validator instance and pass the model for checking.
auto validator = libcellml::Validator::create();
validator->validateModel(model);
auto isValid = validator->errorCount() == 0;
printIssues(validator);
Full context: example_simulationToolDev.cpp
# Validate the model: check for syntactic and semantic errors.
# Create a Validator instance and pass the model for checking.
validator = Validator()
validator.validateModel(model)
print_issues_to_terminal(validator)
Full context: example_simulationToolDev.py
Once a model has been passed to a Validator instance, the validator’s internal logger will contain a list of any of the issues which have been encountered during the checking process.
A model can be said to be valid - that is, conforming to the CellML normative specification - if the validator’s logger contains no issues with a level of ERROR.
For more information on how to use any of the classes which record issues, please see the Get Issues section below.
Retrieve Issue items¶
Selected libCellML classes contain a Logger whose job it is to curate any issues encountered within the class, and return them to the user when asked.
The classes are:
the
Parserclass;the
Validatorclass;the
Printerclass;the
Analyserclass;the
Importerclass; andthe
Generatorclass.
Individual issues can be retrieved from the parent class by their index, an integer between 0 and issueCount()-1.
Each issue contains a severity level indicator, one of four levels (ERROR, WARNING, HINT, or MESSAGE):
ERRORlevel indicates issues that must be resolved before the model is valid and runnable;WARNINGlevel indicates a non-fatal issue, but one that may cause hidden or unintended consequences;HINTlevel indicates a commonly encountered valid-but-nonsense situation; andMESSAGElevel is for your information but does not require action.
Issues can also be retrieved from subgroups based on their severity, as shown in the examples below.
// Iterate through all the issues in a Validator, regardless of level, and print to the terminal.
for (size_t i = 0; i < validator->issueCount(); ++i) {
// Retrieve the i-th issue and store it in the variable "myIssue".
libcellml::IssuePtr myIssue = validator->issue(i);
// Print the issue's description:
std::cout << myIssue->description() << std::endl;
// Print the issue's URL. This is a URL at which more
// information could be found about the cause and corrections
// are needed to avoid it.
std::cout << myIssue->url() << std::endl;
// Validator only: Print the issue's reference. This is
// the heading reference in the normative specification which
// relates to this issue.
std::cout << myIssue->referenceHeading() << std::endl;
}
// Retrieve the second ERROR level issue from a Printer. Note indexing from 0.
auto secondError = printer->error(1);
// Retrieve the last WARNING level issue from a Parser.
auto lastWarning = parser->warning(parser->warningCount()-1);
// Iterate through all HINT level issues in a Generator.
for (size_t h = 0; h < generator->hintCount(); ++h) {
// Retrieve the h-th hint and store it in the variable "myHint".
auto myHint = generator->hint(h);
}
# Iterate through all the issues in a Validator, regardless of level, and print to the terminal.
for i in range(0, validator.issueCount()):
# Retrieve the i-th issue and store it in the variable "my_issue".
my_issue = validator.issue(i)
# Print the issue's description:
print(my_issue.description())
# Print the issue's URL. This is a URL at which more
# information could be found about the cause and corrections
# are needed to avoid it.
print(my_issue.url())
# Validator only: Print the issue's reference. This is
# the heading reference in the normative specification which
# relates to this issue.
print(my_issue.referenceHeading())
# Retrieve the second ERROR level issue from a Printer. Note indexing from 0.
second_error = printer.error(1)
# Retrieve the last WARNING level issue from a Parser.
last_warning = parser.warning(parser.warningCount() - 1)
# Iterate through all HINT level issues in a Generator.
for h in range(0, generator.hintCount()):
# Retrieve the h-th hint and store it in the variable "my_hint".
my_hint = generator.hint(h)
Each Issue also contains the following attributes:
A description: a brief statement about the problem and how it might be fixed;
A reference heading: a chapter number pertaining to the formal CellML 2.0 Specification document, for issues related to formatting and specification;
A URL: a web address at which more detailed information and examples pertaining to the issue are available;
A cause: an
enumwhich reports the type of item responsible for the issue. This is one of the enums:
COMPONENT,CONNECTION,ENCAPSULATION,IMPORT,MAP_VARIABLES,MATHML,MODEL,RESET,RESET_VALUE,TEST_VALUE,UNDEFINED,UNIT,UNITS,VARIABLE, andXML.
// Retrieve and print the description of the issue.
std::cout << issue->description() << std::endl;
// Retrieve and print the reference heading number, if related to CellML2.0 specification and format.
std::cout << issue->referenceHeading() << std::endl;
// Retrieve and print the URL for more help and information about the issue.
std::cout << issue->url() << std::endl;
// Retrieve the item type - a libcellml::CellmlElementType enum - for the issue.
auto myType = issue->CellmlElementType();
// Retrieve the level - a libcellml::Issue::LEVEL enum - for the issue.
auto myLevel = issue->level();
# Retrieve and print the description of the issue.
print(issue.description())
# Retrieve and print the reference heading number, if related to CellML2.0 specification and format.
print(issue.referenceHeading())
# Retrieve and print the URL for more help and information about the issue.
print(issue.url())
# Retrieve the cause - a libcellml.CellmlElementType enum - for the issue.
my_type = issue.cellmlElementType()
# Retrieve the level - a libcellml.Issue.LEVEL enum - for the issue.
my_level = issue.level()
Useful functions for dealing with Issues¶
Utility functions to retrieve issues
void printIssues(const libcellml::LoggerPtr &item) {
// Get the number of issues attached to the logger item. Note that this will
// return issues of all levels. To retrieve the total number of a specific level
// of issues, use the errorCount(), warningCount(), hintCount(), or messageCount() functions.
size_t num = item->issueCount();
std::cout << "Recorded " << num << " issues";
if (num != 0) {
std::cout << ":" << std::endl;
for (size_t i = 0; i < num; ++i) {
// Retrieve the issue at index i. Note that this is agnostic as to the level of issue.
// Specific issue levels can be retrieved using the functions item->error(i), item->warning(i)
// etc, where the index must be within appropriate limits.
libcellml::IssuePtr issue = item->issue(i);
// Issues created by the Validator class contain a reference heading number, which indicates
// the section reference within the normative specification relevant to the issue.
std::string errorReference = issue->referenceHeading();
// The level of an issue is retrieved using the level() function as an enum value.
std::cout << "Issue " << i << " is " << getIssueLevelFromEnum(issue->level()) << ":" << std::endl;
// Each issue has a descriptive text field, accessible through the description() function.
std::cout << " description: " << issue->description() << std::endl;
if (errorReference != "") {
std::cout << " see section " << errorReference
<< " in the CellML specification." << std::endl;
}
// An optional URL is given for some issues which directs the user to more detailed information.
if(!issue->url().empty()){
std::cout << " more information at: " <<issue->url() << std::endl;
}
// Each issue is associated with an item. In order to properly deal with the item stored, its type is
// recorded too in an enumeration.
std::cout << " stored item type: " << getCellmlElementTypeFromEnum(issue->cellmlElementType()) << std::endl;
}
std::cout << std::endl << std::endl;
}
else {
std::cout << "!" << std::endl << std::endl;
}
}
std::map<libcellml::CellmlElementType, std::string> itemTypeToString =
{{libcellml::CellmlElementType::COMPONENT, "COMPONENT"},
{libcellml::CellmlElementType::COMPONENT_REF, "COMPONENT_REF"},
{libcellml::CellmlElementType::CONNECTION, "CONNECTION"},
{libcellml::CellmlElementType::ENCAPSULATION, "ENCAPSULATION" },
{libcellml::CellmlElementType::IMPORT, "IMPORT"},
{libcellml::CellmlElementType::MAP_VARIABLES, "MAP_VARIABLES"},
{libcellml::CellmlElementType::MATH, "MATH"},
{libcellml::CellmlElementType::MODEL, "MODEL"},
{libcellml::CellmlElementType::RESET, "RESET"},
{libcellml::CellmlElementType::RESET_VALUE, "RESET_VALUE"},
{libcellml::CellmlElementType::TEST_VALUE, "TEST_VALUE"},
{libcellml::CellmlElementType::UNDEFINED, "UNDEFINED"},
{libcellml::CellmlElementType::UNIT, "UNIT"},
{libcellml::CellmlElementType::UNITS, "UNITS"},
{libcellml::CellmlElementType::VARIABLE, "VARIABLE"}};
std::string getCellmlElementTypeFromEnum(libcellml::CellmlElementType t) {
return itemTypeToString.at(t);
}
std::string getIssueLevelFromEnum(libcellml::Issue::Level myLevel)
{
std::string myTypeAsString = "dunno";
switch (myLevel) {
case libcellml::Issue::Level::ERROR:
myTypeAsString = "an ERROR";
break;
case libcellml::Issue::Level::WARNING:
myTypeAsString = "a WARNING";
break;
case libcellml::Issue::Level::HINT:
myTypeAsString = "a HINT";
break;
case libcellml::Issue::Level::MESSAGE:
myTypeAsString = "a MESSAGE";
break;
}
return myTypeAsString;
}
Full context: utilities.cpp
def print_issues(item):
# Get the number of issues attached to the logger item. Note that this will
# return issues of all levels. To retrieve the total number of a specific level
# of issues, use the errorCount(), warningCount(), hintCount(), or messageCount() functions.
number_of_issues = item.issueCount()
if number_of_issues != 0:
print("\nThe {t} has found {n} issues:".format(
t=type(item).__name__,
n=number_of_issues)
)
for e in range(0, number_of_issues):
# Retrieve the issue at index i. Note that this is agnostic as to the level of issue.
# Specific issue levels can be retrieved using the functions item.error(e), item.warning(e)
# etc, where the index must be within appropriate limits.
i = item.issue(e)
# The level of an issue is retrieved using the level() function as an enum value.
level = i.level()
print(" {l}[{e}]:".format(
l=level_as_string[level],
e=e))
# Each issue has a descriptive text field, accessible through the description() function.
print(" Description: {d}".format(
d=i.description()))
# Issues created by the Validator class contain a reference heading number, which indicates
# the section reference within the normative specification relevant to the issue.
specification = i.referenceHeading()
if specification != "":
print(" See section {s} in the CellML specification.".format(
s=specification))
# An optional URL is given for some issues which directs the user to more detailed information.
url = i.url()
if url != "":
print(" More information is available at {url}".format(
url=url))
# Each issue is associated with an item. In order to properly deal with the item stored, its type is
# recorded too in an enumeration.
print(" Stored item type: {}".format(get_cellml_element_type_from_enum(i.cellmlElementType())))
else:
print("\nThe {t} has not found any issues!".format(
t=type(item).__name__)
)
def get_cellml_element_type_from_enum(my_cause):
my_type_as_string = "dunno"
if my_cause == CellmlElementType.COMPONENT:
my_type_as_string = "COMPONENT"
elif my_cause == CellmlElementType.COMPONENT_REF:
my_type_as_string = "COMPONENT_REf"
elif my_cause == CellmlElementType.CONNECTION:
my_type_as_string = "CONNECTION"
elif my_cause == CellmlElementType.ENCAPSULATION:
my_type_as_string = "ENCAPSULATION"
elif my_cause == CellmlElementType.IMPORT:
my_type_as_string = "IMPORT"
elif my_cause == CellmlElementType.MATH:
my_type_as_string = "MATH"
elif my_cause == CellmlElementType.MAP_VARIABLES:
my_type_as_string = "MAP_VARIABLES"
elif my_cause == CellmlElementType.MODEL:
my_type_as_string = "MODEL"
elif my_cause == CellmlElementType.RESET:
my_type_as_string = "RESET"
elif my_cause == CellmlElementType.RESET_VALUE:
my_type_as_string = "RESET_VALUE"
elif my_cause == CellmlElementType.TEST_VALUE:
my_type_as_string = "TEST_VALUE"
elif my_cause == CellmlElementType.UNDEFINED:
my_type_as_string = "UNDEFINED"
elif my_cause == CellmlElementType.UNIT:
my_type_as_string = "UNIT"
elif my_cause == CellmlElementType.UNITS:
my_type_as_string = "UNITS"
elif my_cause == CellmlElementType.VARIABLE:
my_type_as_string = "VARIABLE"
return my_type_as_string
def get_issue_level_from_enum(my_level):
my_type_as_string = "dunno"
if my_level == Issue.Level.ERROR:
my_type_as_string = "ERROR"
elif my_level == Issue.Level.WARNING:
my_type_as_string = "WARNING"
elif my_level == Issue.Level.HINT:
my_type_as_string = "HINT"
elif my_level == Issue.Level.MESSAGE:
my_type_as_string = "MESSAGE"
return my_type_as_string
Full context: utilities.py
Analyse a Model item¶
The Analyser class takes an existing, valid Model item, and checks it for mathematical sense.
This includes checking things like that all variables requiring initial values have them, that equations do not conflict with one another, and that any variables used in equations are actually defined in the model.
The three basic steps to model analysis are:
Creating an
Analyseritem and passing in aModelfor analysis;Checking for any
Issuesraised; and(optional: for code generation only) Retrieving a
AnalysedModelitem to pass to aGenerator, if required.
// Analyse the model: check for mathematical and modelling errors.
auto analyser = libcellml::Analyser::create();
analyser->analyseModel(model);
printIssues(analyser);
Full context: example_simulationToolDev.cpp
# Analyse a model: check for mathematical and modelling errors.
analyser = Analyser()
analyser.analyseModel(model)
print_issues_to_terminal(analyser)
Full context: example_simulationToolDev.py
Any issues or messages raised are stored within the class’s logger.
More information about accessing Issue items can be found on the Common actions > Retrieve Issue items page.
Use of the Analyser class is a prerequisite for the Generator class.
The generator makes use of the structures created during the analysis process, so takes a AnalyserModel as an input.
// Generate runnable code in other language formats for this model.
// Create a Generator instance. Note that by default this uses the C language profile.
auto generator = libcellml::Generator::create();
// Pass the generator the model for processing.
generator->setModel(analyser->model());
printIssues(generator);
Full context: example_simulationToolDev.cpp
# Generate runnable code in other language formats for this model.
# Create a Generator instance. Note that by default this is the C language.
generator = Generator()
# Pass the generator the analysed model for processing.
generator.processModel(analyser.model())
print_issues_to_terminal(generator)
Full context: example_simulationToolDev.py
TODO