Edit items in a model¶
All CellML entities exist in an 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.
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.
About editing¶
Editing attributes¶
For any item type, each attribute xyz may be edited using the general setXyz function, whose arguments vary depending on the attribute.
Note that this is distinct from editing collections of items, which is discussed below.
Editing collections¶
General families of functions are available for editing collections, but take the form of addXyz and removeXyz, takeXyz and replaceXyz, as explained in Understanding collections.
Some example snippets are shown below:
Add a thing to a collection
When you add an item to a collection, you also set its parent. Effectively, adding an item which is already a member of one collection to another collection actually moves it from the first to the second. This means that:
the item’s parent will be the second collection parent;
the first collection will not contain the item any more; and
the second collection will contain the item.
#include <iostream>
#include <libcellml>
int main()
{
// Create a variable and a component.
auto myVariable = libcellml::Variable::create("myVariableName");
auto myComponent = libcellml::Component::create("myComponentName");
bool success = false;
// Before adding, the variable has no parent, and the component has no
// variables.
// Add the variable myVariable to component myComponent.
success = myComponent->addVariable(myVariable);
// At this point, if the operation was successful, the variable
// myVariable has a parent of myComponent, and exists in the collection
// of variables. If successful:
// - success = true
// - myComponent->variableCount() = 1
// - myVariable->parent() = myComponent
// - myComponent->variable(0) = myVariable.
// Now see what happens if that same variable is added to another
// Component item, myOtherComponent, which is initially empty.
auto myOtherComponent = libcellml::Component::create("myOtherComponentName");
success = myOtherComponent->addVariable(myVariable);
// If successful, the variable will have been moved to the new component, and
// removed from the old one, thus:
// - success = true
// - myOtherComponent->variableCount() = 1
// - myOtherComponent->variable(0) = myVariable
// - myVariable->parent() = myOtherComponent
// - myComponent->variableCount() == 0.
std::cout << "hello world!"<<std::endl;
}
from libcellml import Component, Variable
if __name__ == '__main__':
# Create a variable and a component.
my_variable = Variable()
my_variable.setName("myVariable")
my_component = Component()
my_component.setName("myComponent")
success = False
# Before adding, the variable has no parent, and the component has no
# variables.
assert(my_component.variableCount() == 0)
assert(my_variable.parent() == None)
# Add the variable my_variable to component my_variable.
success = my_component.addVariable(my_variable)
# At this point, if the operation was successful, the variable
# my_variable has a parent of my_component, and exists in the collection
# of variables. The component's variable count has been updated too.
assert(success == true)
assert(my_component.variableCount() == 1)
assert(my_variable.parent() == my_variable)
assert(my_component.variable(0) == my_variable)
# Now see what happens if that same variable is added to another
# component, my_other_component, which is initially empty.
my_other_component = Component()
my_other_component.setName('myOtherComponent')
assert(my_other_component.variableCount() == 0)
success = my_other_component.addVariable(my_variable)
# If successful, the variable will have been moved to the new component, and
# removed from the old one.
assert(success == true)
assert(my_other_component.variableCount() == 1)
assert(my_other_component.variable(0) == my_variable)
assert(my_variable.parent() == my_other_component)
assert(my_component.variableCount() == 0)
Remove a thing from a collection
This example demonstrates the addition and removal of items from a collection. Note that while removal of items from a collection can be done by pointer symbol, by name, and by index, adding them is only possible via pointer.
// Setup. Create four variables and a component.
auto variable1 = libcellml::Variable::create("variable1");
auto variable2 = libcellml::Variable::create("variable2");
auto variable3 = libcellml::Variable::create("variable3");
auto variable4 = libcellml::Variable::create("variable4");
auto component = libcellml::Component::create("component");
// Add variable1 into the component. Note that the argument is
// the symbol, not the name. Expect success to be true.
auto success = component->addVariable(variable1);
assert(success);
// Repeat for the others.
success = component->addVariable(variable2);
success = component->addVariable(variable3);
success = component->addVariable(variable4);
// Remove variable2 from the component by pointer.
success = component->removeVariable(variable2);
assert(success);
// Remove variable3 from the component by name.
success = component->removeVariable("variable3");
assert(success == true);
// Remove variable4 from the component by index.
success = component->removeVariable(1);
assert(success == true);
// Operations that will not succeed include:
// - Adding a variable more than once to the same component.
success = component->addVariable(variable1);
assert(success == false);
// - Removing a variable that doesn't exist in that component.
success = component->removeVariable(variable2);
assert(success == false);
// - Removing a variable by name that doesn't exist.
success = component->removeVariable("iDontExist");
assert(success == false);
// - Removing a variable by out-of-range index.
success = component->removeVariable(9999);
assert(success == false);
// - Adding a null pointer.
success = component->addVariable(nullptr);
assert(success == false);
// - Removing a null pointer.
success = component->removeVariable(nullptr);
assert(success == false);
# TODO
Take a thing from a collection
Replace a thing within a collection
Editing special relationships¶
Some parts of the CellML model require different treatment to those listed above; connections between equivalent variables, for example.
Editing variable equivalences¶
In the situation of equivalent variable collections there is no clear “ownership” of the equivalence as an attribute of any one variable, and neither is there a central parent item with curation ability over the set of variables. A different approach is required.
Adding and removing variable equivalence is accomplished using the addEquivalence and removeEquivalence functions as shown below.
These functions take arguments of the variable pointers to connect or dissociate, and return a boolean value indicating whether the operation was successful.
The success of the addEquivalence function depends on:
the variable pointers being non-null;
the two variables having parent components;
there being no previously existing connection between the two variables already.
Creating a valid connection also depends on the two variables having compatible units, appropriate interface types, and being in components which are accessible to one another (parent-child or sibling relationship).
The success of the removeEquivalence function depends on:
the variable pointer arguments being non-null; and
there being an existing connection between the two variables.
Note that the removeEquivalence function will only remove an equivalence which was previously set using the addEquivalence function; it does not remove any indirect connections via third-party variables between the two.
An additional removeAllEquivalences function will completely remove the variable from any connected set.
Please see the API Variable page for details of these functions.
// Assuming that variables A and B are in different components, and that those components are
// available for connection with one another (parent-child, or sibling relationship).
// Add variables A and B to the same equivalent variable set.
auto isAconnectedToB = libcellml::Variable::addEquivalence(A, B);
// Remove a pre-existing equivalence between variables C and D.
auto isCdisconnectedFromD = libcellml::Variable::removeEquivalence(C, D);
# Assuming that variables A and B are in different components, and that those components are
# available for connection with one another (parent-child, or sibling relationship).
# Add variables A and B to the same equivalent variable set.
isAconnectedToB = Variable.addEquivalence(A, B)
# Remove a pre-existing equivalence between variables C and D.
isCdisconnectedFromD = Variable.removeEquivalence(C, D)
There are some gotchas to be explained.
A variable can belong to only one set of equivalent variables; by having more than one equivalent variable, the variable itself merges the two sets of variables into one.
Consider this example.
Variable fruit is equivalent to variables apple, pear, and peach.
Another variable vegetable is equivalent to variables tomato, celery, and spinach.
At this time, there are two sets of equivalent variables: all of the fruit are equivalent to one another, and all of the vegetables are equivalent to one another.
Adding a variable produce with an equivalence to fruit and to vegetable effectively merges the two sets, so that now the variable peach is equivalent to spinach.
Editing an encapsulation hierarchy¶
Components may be added to a model or another component to create an encapsulation hierarchy, but there are a few things to watch out for in this situation. Components must belong to only one parent, so when adding a component from one location in the encapsulation to another, it is removed from the original parent.
This is shown in the code snippet below.
Edit encapsulation structure of a model
This example shows how the component tomatoes which was orininally a child of the vegetables component, could be moved to become a child of the fruit component instead.
When run, this snippet will give the output:
Model 'quickstart_editEncapsulation' has 2 components
- Component 'fruit' has 0 child components
- Component 'vegetables' has 1 child components
- Component 'tomatoes' has 0 child components
Model 'quickstart_editEncapsulation' has 2 components
- Component 'fruit' has 1 child components
- Component 'tomatoes' has 0 child components
- Component 'vegetables' has 0 child components
#include <fstream>
#include <sstream>
#include <libcellml>
#include "../../utilities/utilities.h"
int main()
{
// Parse example model and component from the file provided.
std::string inFileName = "../resources/quickstart_editEncapsulation1.cellml";
std::ifstream inFile(inFileName);
std::stringstream inFileContents;
inFileContents << inFile.rdbuf();
auto parser = libcellml::Parser::create();
auto model = parser->parseModel(inFileContents.str());
printEncapsulationStructureToTerminal(model);
auto fruit = model->component("fruit");
auto veges = model->component("vegetables");
// The tomatoes component starts off as a child of the vegetables component.
auto tomatoes = veges->component("tomatoes");
// Move the tomatoes into its correct fruit component, and check the return boolean is true.
auto success = fruit->addComponent(tomatoes);
assert(success);
printEncapsulationStructureToTerminal(model);
}
from libcellml import Parser
from utilities import print_encapsulation_structure_to_terminal
if __name__ == "__main__":
read_file = open("../resources/quickstart_editEncapsulation.cellml", "r")
# Create a parser and read the file.
parser = Parser()
model = parser.parseModel(read_file.read())
# Print the current encapsulation structure to the terminal.
print_encapsulation_structure_to_terminal(model)
# Retrieve the components. Note that the "tomatoes" component starts
# out as a child of the "vegetables" component.
vegetables = model.component("vegetables")
fruit = model.component("fruit")
tomatoes = model.component("tomatoes")
# Move the "tomatoes" component into "fruit".
success = fruit.addComponent(tomatoes)
# Print the structure and check that the components have
# been transferred as expected.
print_encapsulation_structure_to_terminal(model)