View the contents of a Model¶
About viewing models¶
All CellML entities exist in a hierarchical structure as shown below. In some circumstances additional links are made between items (equivalent variables, for example), but on the whole it follows a basic tree structure.
Model
│
├─ Units item(s)
│ └─ Unit item(s)
│
└─ Component item(s)
│
├─ Variable item(s)
│
├─ Reset item(s)
│ ├─ ResetValue item
│ └─ TestValue item
│
├─ Math item
│
└─ Child Component item(s)
│
└─ Grandchild Component item(s)
Within the structure each item has two parts:
A set of attributes specific to itself. Some of these attribute exist for many items (for example, the
nameattribute), and others are specific to the item type (for example, theinitialValueattribute on aVariableitem).A set of collections which this - the parent item - curates. For example, the collection of
Variableitems owned by a parentComponent.
View attributes of an item¶
Retrieving the value of an item’s attribute is simple, and follows the same general pattern throughout libCellML.
Get the attribute xyz using the camelCase function xyx() without arguments.
For example:
// Retrieving the name attribute from the myModel item:
std::string myModelName = myModel->name();
// Retrieving the initial value of the myVariable item:
std::string myInitialValue = myVariable->initialValue();
# Retrieving the name attribute from the my_model item:
my_model_name = my_model.name()
# Retrieving the initial value of the myVariable item:
my_initial_value = my_variable.initialValue()
Note that the return value’s type will vary depending on the function.
For example, a Variable item pointer is returned as the testVariable() attribute value for a Reset item:
// Retrieve the test variable from the myReset item:
libcellml::VariablePtr myTestVariable = myReset->testVariable();
# Retrieve the test variable from the my_reset item:
my_test_varible = my_reset->testVariable()
View collections owned by an item¶
In order to access items within a collection use the same general format as above, but with an indicator (name or index) of the child item to be retrieved from the collection.
In other words, get the child item of type xyz using the camelCase function xyx(myIndex) or xyz(myName).
Before accessing a collection item using an index, you can use the xyzCount() function to return the number of items in the collection.
The following example shows how all variables in a component can be listed.
// Loop through variables in the component myComponent and retrieve their names.
// NOTE that indexing starts from zero.
for(size_t v = 0; v < myComponent->variableCount(); ++v) {
// Retrieve the Variable item at index v:
auto myVariable = myComponent->variable(v);
// Retrieve the name of the myVariable item:
auto myVariableName = myVariable->name();
}
// Retrieve a variable called "helloThere" by name.
// NOTE that a nullptr will be returned if no variable of that name is found.
auto myHelloThereVariable = myComponent->variable("helloThere");
// In this case, the myMissingVariable will be a nullptr:
auto myMissingVariable = myComponent->variable("nameThatDoesntExist");
// This will cause a segfault as myMissingVariable is null:
auto myMissingName = myMissingVariable->name();
# Loop through variables in the component myComponent and retrieve their names.
# NOTE that indexing starts from zero:
for v in range(0, my_component.variableCount()):
# Retrieve the Variable item at index v:
my_variable = my_component.variable(v)
# Retrieve the name of the myVariable item:
my_variable_name = my_variable.name()
# Retrieve a variable called "helloThere" by name.
# NOTE that None will be returned if no variable of that name is found.
my_hello_there_variable = my_component.variable("helloThere")
# In this case, the my_missing_variable will be None:
my_missing_variable = myComponent.variable("nameThatDoesntExist")
# This will cause a segfault as my_missing_variable is None:
my_missing_name = my_missing_variable.name()
Some gotchas¶
The ownership of some collections can be a little counter-intuitive.
One example is that Units items are referenced by Variable items, but are owned by the Model; this is so that units can be reused across more than one component.
Another example involving encapsulation and Component item ownership is shown below.
Consider the following model:
model: Grandfather
component: Uncle
component: Mother
component: Daughter
component: Son
The raw CellML syntax stores each component individually as children of the model, and separately stores the encapsulation structure of the nested components.
See CellML syntax
<model>
<!-- The components are listed individually as children of the model block. -->
<component name="Uncle"/>
<component name="Mother"/>
<component name="Daughter"/>
<component name="Son"/>
<!-- The encapsulation structure is stored separate from the components. -->
<encapsulation>
<component_ref component="Mother">
<component_ref component="Daughter"/>
<component_ref component="Son"/>
</component_ref>
</encapsulation>
</model>
In libCellML, the encapsulation structure is embedded in the ownership of the components, so that one component can be a parent of another.
This can be confusing if the simple componentCount() function on a model is called naively, as shown below.
// The number of components owned by the grandfather model refers *only* to its direct children:
auto grandfatherHasTwoKids = grandfather->componentCount(); // returns 2
// Each component must be interrogated individually to determine its children.
// Note that the uncle component is the 0th child of the grandfather model.
auto uncleHasNoKids = grandfather->component(0)->componentCount(); // returns 0
auto motherHasTwoKids = grandfather->component("Mother")->componentCount(); // returns 2
# The number of components owned by the grandfather model refers *only* to its direct children:
grandfather_has_two_kids = grandfather->componentCount() # returns 2
# Each component must be interrogated individually to determine its children.
# Note that the uncle component is the 0th child of the grandfather model.
uncle_has_no_kids = grandfather.component(0).componentCount() # returns 0
mother_has_two_kids = grandfather.component("Mother").componentCount() # returns 2
Useful snippets for viewing a model¶
Some useful snippets for viewing parts of your model are shown below.
Retrieve units needed by a component: variables and mathematics
There are two places that need a reference to Units items.
The first is the set of Variable items stored in the Component: the units name for each of these is accessible in the name() attribute of its units() item.
// This example assumes you already have a component defined.
// You will need to #include <unordered_set> in your #include statements.
std::unordered_set<std::string> unitsNames;
// Iterate through the variables in this component, and add their units' names to the set.
for (size_t v = 0; v < component->variableCount(); ++v) {
// Get the units for this variable:
auto myUnits = component->variable(v)->units();
// Check that this is not the nullptr, otherwise skip.
if (myUnits != nullptr) {
// Add name to set if not already there.
unitsNames.insert(myUnits->name());
}
}
// Parse the MathML string to find any units used by <cn> constants:
std::string delimiter = "cellml:units=";
std::string maths = component->math();
size_t pos = maths.find(delimiter); // Start looking for the name after the first delimiter.
size_t pos2;
std::string segment;
std::string name;
while ((pos = maths.find(delimiter)) != std::string::npos) {
segment = maths.substr(0, pos);
segment.erase(0, segment.find("\"")); // Remove the first quote mark after the delimiter.
pos2 = segment.find("\""); // Find the second quote mark.
name = segment.substr(0, pos2); // Units name is between the two quotes.
if (name.length()) { // Sanity check that the string is not empty.
unitsNames.insert(name);
}
maths.erase(0, pos + delimiter.length()); // Remove this segment from the main string.
}
// Search the final remaining segment.
segment = maths;
segment.erase(0, 1);
pos2 = segment.find("\"");
name = segment.substr(0, pos2);
if (name.length()) {
unitsNames.insert(name);
}
// Print the unique units for this component to the terminal.
for (const auto &name : unitsNames) {
std::cout << " - " << name << std::endl;
}
# This example assumes that you have a component already, containing variables and MathML.
# Initialise an empty set to save the units names.
units_names = set()
# Iterate through the variables in this component, and add their units' names to the set.
for v in range(0, component.variableCount()):
# Get the units for this variable:
my_units = component.variable(v).units()
# Check that this is not the nullptr, otherwise skip.
if my_units is not None:
# Add name to set if not already there.
units_names.add(my_units.name())
# Parse the MathML string to find any units used by <cn> constants:
delimiter = "cellml:units="
maths_string = component.math()
segments = maths_string.split(delimiter)
# Start looking for the name after the first delimiter.
for segment in segments[1:]:
# Split the segment at quotation marks, and take the one at index 1
name = segment.split('"')[1]
if (len(name)): # Sanity check that the string is not empty.
units_names.add(name)
# Print the unique units for this component to the terminal.
print("The units needed by component {c} are:".format(c=component.name()))
for name in units_names:
print(" - {n}".format(n=name))
Edit MathML in a component
MathML is stored as a single string within a component.
Retrieving it is simple enough using the math() function, but any manipulation (change units used, changing variable names, adding additional relationships, etc) are a little more complicated.
TODO: Need more examples of useful mathml manipulation. Variable names? Need to remove whitespace first?
void switchUnitsInMaths(std::string &maths, std::string in, std::string out)
{
// Note that this function will replace any and all occurrences of the "in"
// string within the "maths" string with the "out" string. It's worth noting that
// in order to be sure that only full name matches for units are replaced, we exploit
// the fact that the units names in the MathML string will be in quotation marks, and include
// these quotation marks on either side of the in and out strings for safety.
std::string::size_type n = 0;
std::string in_with_quotes = "\"" + in + "\"";
std::string out_with_quotes = "\"" + out + "\"";
while ((n = maths.find(in_with_quotes, n)) != std::string::npos) {
maths.replace(n, in_with_quotes.size(), out_with_quotes);
n += out_with_quotes.size();
}
std::cout << "Switched units '" << in << "' for units '" << out << "'" << std::endl;
}
def switch_units_in_maths(maths, units_in, units_out):
# Note that this function will replace any and all occurrences of the "units_in"
# string within the "maths" string with the "units_out" string. It's worth noting that
# in order to be sure that only full name matches for units are replaced, we exploit
# the fact that the units names in the MathML string will be in quotation marks, and include
# these quotation marks on either side of the in and out strings for safety.
in_with_quotes = "\"" + units_in + "\""
out_with_quotes = "\"" + units_out + "\""
new_maths = maths.replace(in_with_quotes, out_with_quotes)
return new_maths
Print encapsulation structure
Because components may be nested to any depth within an encapsulation hierarchy, we need to use recursive functions to be sure of reaching the bottom level. The examples below define two functions - one to initiate the recursion, and one to carry it out.
// This function is called to initiate the recursion:
void printEncapsulationStructureToTerminal(libcellml::ModelPtr &model)
{
// Prints the encapsulation structure of the model to the terminal
std::string spacer = " - ";
std::cout << "Model '" << model->name() << "' has " << model->componentCount()
<< " components" << std::endl;
for (size_t c = 0; c < model->componentCount(); ++c) {
auto child = model->component(c);
printComponentOnlyToTerminal(child, spacer);
}
}
// This function performs the recursion to the full depth of the encapsulation:
void printComponentOnlyToTerminal(libcellml::ComponentPtr &component, std::string spacer)
{
std::cout << spacer << "Component '" << component->name() << "' has " << component->componentCount() << " child components" << std::endl;
for (size_t c = 0; c < component->componentCount(); c++) {
std::string anotherSpacer = " " + spacer;
auto child = component->component(c);
printComponentOnlyToTerminal(child, anotherSpacer);
}
}
# Prints the encapsulation structure of the model to the terminal. This
# function intitiates the recursion through the encapsulation structure.
def print_encapsulation_structure_to_terminal(model):
spacer = " - "
print("Model '{m}' has {c} components".format(m=model.name(), c=model.componentCount()))
for c in range(0, model.componentCount()):
child_component = model.component(c)
print_component_only_to_terminal(child_component, spacer)
print()
# This function performs the recursion, and explores all child components.
def print_component_only_to_terminal(component, spacer):
print("{s}Component '{c}' has {n} child components".format(
s=spacer,
c=component.name(),
n=component.componentCount()))
for c in range(0, component.componentCount()):
another_spacer = " " + spacer
child_component = component.component(c)
print_component_only_to_terminal(child_component, another_spacer)
print()
Trace equivalent variables
This example traces variable equivalences throughout the model using recursion.
The CellML file read contains a model as shown below, where two pairs of equivalent variables (A to B, and B to C) connect three variables together.
model:
├─ component: componentA
│ └─ variable: A [dimensionless] <╴╴╴╮
│ ╷
│ equivalent
├─ component: componentB ╵
│ └─ variable: B [dimensionless] <╴╴╴╯<╴╴╴╮
│ ╷
│ equivalent
└─ component: componentC ╵
└─ variable: C [dimensionless] <╴╴╴╴╴╴╴╴╯
Show CellML syntax
<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="http://www.cellml.org/cellml/2.0#"
xmlns:cellml="http://www.cellml.org/cellml/2.0#" name="quickstart_traceEquivalence">
<component name="componentA">
<variable units="dimensionless" name="A" interface="public" />
</component>
<component name="componentB">
<variable units="dimensionless" name="B" interface="public" />
</component>
<component name="componentC">
<variable units="dimensionless" name="C" interface="public" />
</component>
<connection component_1="componentA" component_2="componentB">
<map_variables variable_1="A" variable_2="B"/>
</connection>
<connection component_1="componentB" component_2="componentC">
<map_variables variable_1="B" variable_2="C"/>
</connection>
</model>
The example should output the connections between the variables, including discerning that A is connected to C, even though no direct relationship is specified in the model.
Variable 'A' in component 'componentA' is connected to:
- variable 'B' [dimensionless] in component 'componentB'
- variable 'C' [dimensionless] in component 'componentC'
// Function to initiate a list of variables in the connected set, to start the recursion, and to print
// the list to the terminal.
void printEquivalentVariableSet(const libcellml::VariablePtr &variable)
{
if (variable == nullptr) {
std::cout << "NULL variable submitted to printEquivalentVariableSet." << std::endl;
return;
}
std::vector<libcellml::VariablePtr> variableList;
variableList.push_back(variable);
listEquivalentVariables(variable, variableList);
// The parent() function returns an EntityPtr, which must be cast to a Component before its name()
// function can be called.
libcellml::ComponentPtr component = std::dynamic_pointer_cast<libcellml::Component>(variable->parent());
if (component != nullptr) {
std::cout << "Tracing: " << component->name() << " -> "
<< variable->name();
if (variable->units() != nullptr) {
std::cout << " [" << variable->units()->name() << "]";
}
if (variable->initialValue() != "") {
std::cout << ", initial = " << variable->initialValue();
}
std::cout << std::endl;
}
// The variableList contains the variable that was submitted for testing originally, so
// if it's connected to other variables, it must have a length greater than 1.
if (variableList.size() > 1) {
for (auto &e : variableList) {
component = std::dynamic_pointer_cast<libcellml::Component>(e->parent());
if (component != nullptr) {
std::cout << " - " << component->name() << " -> " << e->name();
if (e->units() != nullptr) {
std::cout << " [" << e->units()->name() << "]";
}
if (e->initialValue() != "") {
std::cout << ", initial = " << e->initialValue();
}
std::cout << std::endl;
} else {
std::cout << "Variable " << e->name() << " does not have a parent component." << std::endl;
}
}
} else {
std::cout << " - Not connected to any equivalent variables." << std::endl;
}
}
// This function performs the recursive search through all connections until the set
// has been completely covered.
void listEquivalentVariables(const libcellml::VariablePtr &variable,
std::vector<libcellml::VariablePtr> &variableList) {
if (variable == nullptr) {
return;
}
for (size_t i = 0; i < variable->equivalentVariableCount(); ++i) {
libcellml::VariablePtr equivalentVariable = variable->equivalentVariable(i);
if (std::find(variableList.begin(), variableList.end(), equivalentVariable) == variableList.end()) {
variableList.push_back(equivalentVariable);
listEquivalentVariables(equivalentVariable, variableList);
}
}
}
from libcellml import Parser
# This function will initialise the information strings to test, start
# the search, and print the results.
def print_equivalent_variable_set(variable):
if variable is None:
print("None variable submitted to print_equivalent_variable_set.")
return
variable_list = list()
variable_list.append([variable.name(),
variable.parent().name(),
variable.units().name(),
variable.initialValue()])
list_equivalent_variables(variable, variable_list)
if len(variable_list) > 1:
print("Variable '{v}' in component '{c}' is connected to:".format(
v=variable.name(), c=variable.parent().name()))
for e in variable_list[1:]:
if e[3] != '':
print(" - variable '{v}'(t=0)={i} [{u}] in component '{c}'".format(
v=e[0], i=e[3], u=e[2], c=e[1]))
else:
print(" - variable '{v}' [{u}] in component '{c}'".format(
v=e[0], u=e[2], c=e[1]))
else:
print("Variable '{v}' is not connected to other variables.".format(
v=variable.name()))
# This function performs the recursive search through all connections until the set
# has been completely covered.
def list_equivalent_variables(variable, variable_list):
if variable is None:
return
for i in range(0, variable.equivalentVariableCount()):
equivalent_variable = variable.equivalentVariable(i)
# Form a list of strings that describe the equivalent variable.
test = [equivalent_variable.name(),
equivalent_variable.parent().name(),
equivalent_variable.units().name(),
equivalent_variable.initialValue()]
# If the equivalent variable has not already been checked, then start another recursion.
if test not in variable_list:
variable_list.append(test)
list_equivalent_variables(equivalent_variable, variable_list)
if __name__ == "__main__":
read_file = open("../resources/quickstart_traceEquivalence.cellml", "r")
# Create a parser and read the file.
parser = Parser()
model = parser.parseModel(read_file.read())
# Retrieve a variable from the parsed model
A = model.component("componentA").variable("A")
# Initiate the tracing for equivalent variables of variable "A"
print_equivalent_variable_set(A)