Tutorial 1: Reading and writing CellML files

Welcome to the introductory tutorial for the use of the libCellML library. Using these tutorials in sequence will walk you through the functionality and build your knowledge step by step. If, however, you just need to jump in and figure out one specific idea, you can find a tutorial related to that subject using the How-to pages.

After completing this tutorial you will be able to:

  • Read the contents of a CellML file;

  • Deserialise its contents using the Parser to create a Model structure;

  • Investigate the hierarchical contents of the Model, including the Components, Variables, and MathML blocks; and

  • Serialise the model and write to another file using the Printer.

Requirements

C++ resources

Python resources

CellML resources

Step 0: Set-up

For each of the tutorials, there is template code provided for you to get started. If you’d rather see the completed code instead of working through the tasks yourself, simply rename the completed files so that they match the skeleton file names (that is, remove the _completed from their names).

0.a Navigate into the tutorial1 folder and confirm that you’re able to compile and run this template against the libCellML library.

cmake -DINSTALL_PREFIX=../../install
make -j

Running the template:

./tutorial1

… should give the output:

-----------------------------------------------
 TUTORIAL 1: READING AND WRITING CELLML FILES
-----------------------------------------------

Step 1: Deserialise a CellML2 file

CellML is a format in which all the information is stored in a strict hierarchical way. In order for this information to be transformed into a format which can be used for modelling and simulation it needs to be deserialised into the internal Model format. The hard work of this transformation is done by the Parser object which will take a raw CellML string which represents a model and return its corresponding Model item.

1.a You’ve been provided with a CellML file which we’ll be reading in this tutorial, tutorial1.cellml. Open the file and read its entire contents into a string.

1.b Create a Parser instance. Pass the string you read above into the parser using the parseModel function. This will return a newly-created Model instance.

Show C++ snippet

    //  1.a   
    //      Open the file and read its contents into a buffer stream.

    std::string inFileName = "tutorial1.cellml";
    std::ifstream inFile(inFileName);
    std::stringstream inFileContents;
    inFileContents << inFile.rdbuf();

    std::cout << "Opening the CellML file" << std::endl;

    //  1.b   
    //      Create a libCellML Parser, and use it to parse the fileContents
    //      string and convert it into a CellML Model structure.
    
    auto parser = libcellml::Parser::create();
    auto model = parser->parseModel(inFileContents.str());

Show Python snippet

    #  1.a
    #     Open the tutorial1.cellml file for reading
    read_file = open("tutorial1.cellml", "r")

    #  1.b   
    #     Create a libCellML Parser, and use it to parse the file
    #     string contents and convert it into a CellML Model structure
    parser = Parser()
    model = parser.parseModel(read_file.read())

Now we have a deserialised CellML model which we can manipulate using the libCellML library.

Step 2: Investigate the contents of the model

Now that we have a model, let’s see what’s inside it. All retrieval functions - where you want to read something about any item - are callable from simple functions naming the thing you want.

For example, to find the name of the model we simply call its name() function to return the string of its name.

2.a Find out the name and id of your model and print them to the terminal.

The model name is: tutorial_1_model
The model id is: tutorial_1_model_id_is_here

The Model itself stores two kinds of objects: a set of Units objects, and a set of Component objects. There are generic somethingCount() functions which will return the number of Something items within that object:

auto numberOfComponents = myFirstModel->componentCount();

2.b Find out the number of Component items in the model, and print it to the terminal.

The tutorial_1_model model has 1 component(s)

Items like components and units (and later, variables) which are stored in sets can be accessed by their index as well as by their name. At this stage, since we don’t know the name of the components, we’ll have to use their indices to access them for printing to the terminal. The indices start at 0.

Each Component itself (or later, Units or Variable) is retrieved from its parent item as a pointer (that is, a ComponentPtr type) using the same idiom as the names and ids:

// Retrieve the 33rd component from myFirstModel.  Note the indexing from zero.
auto thirtyThirdComponent = myFirstModel->component(32);

2.c Retrieve the first component from the model, and print its name and id to the terminal.

The tutorial_1_model model has 1 component(s):
    Component[0] has name: i_am_a_component
    Component[0] has id: my_component_id

Component items in turn contain four kinds of items, these being:

  • a set of Variable items;

  • a MathML string which controls the governing mathematical equations of the model behaviour;

  • (for later) a set of Reset items; and

  • (also for later) an optional nested subset of Component items too.

In this tutorial we’ll only look at the variables and maths.

2.d Retrieve the number of variables inside your component, and print it to the screen.

2.e Create a loop through the variables, retrieve each, and print their names to the screen.

The i_am_a_component component has 3 variable(s):
    Variable[0] has name: a
    Variable[1] has name: b
    Variable[2] has name: c

2.f We will look more at what the MathML string actually means in later tutorials, but for now, retrieve the MathML string from your component using the math() function, and print it to the screen.

Component i_am_a_component has a MathML string of:
    <math xmlns="http://www.w3.org/1998/Math/MathML">
    <apply>
        <eq/>
        <ci>a</ci>
        <apply>
        <plus/>
        <ci>b</ci>
        <ci>c</ci>
        </apply>
    </apply>
    </math>

Show C++ snippet

    //  2.a  
    //      Return the name and id of the new model, and print to the terminal.

    std::string modelName = model->name();
    std::string modelId = model->id();

    std::cout << "The model name is: " << modelName << std::endl;
    std::cout << "The model id is: " << modelId << std::endl;

    //  2.b
    //      Return the number of components contained in the model, and print
    //      to the screen.

    int numberOfComponents = model->componentCount();
    std::cout << "The " << modelName << " model has " << numberOfComponents
              << " component(s):" << std::endl;

    assert(numberOfComponents > 0);

    //  2.c   
    //      Return the name and id of the first component and print them to
    //      the screen.

    auto component = model->component(0);
    std::string componentName = component->name();
    std::string componentId = component->id();
    std::cout << "  Component[0] has name: " << componentName << std::endl;
    std::cout << "  Component[0] has id: " << componentId << std::endl;

    //  2.d   
    //      Retrieve the number of variables in this component, and print
    //      to the screen.

    int numberOfVariables = component->variableCount();
    std::cout << "  The " << componentName << " component has "
              << numberOfVariables << " variable(s):" << std::endl;

    //  2.e
    //      Loop through the variables and print their names to the terminal.

    for (size_t v = 0; v < numberOfVariables; ++v) {
        auto variable = component->variable(v);
        std::string variableName = variable->name();

        std::cout << "    Variable[" << v << "] has name: " << variableName
                  << std::endl;
    }

    //  2.f
    //      Investigate the maths which connects the variables in this
    //      component.  Note that maths in libCellML is stored as a
    //      single string, which could contain any number of MathML blocks.

    std::string mathsString = component->math();
    std::cout << "  Component " << componentName
              << " has a MathML string of: " << std::endl;
    std::cout << "  " << mathsString << std::endl;

Show Python snippet

    #  2.a
    #     Return the name and id of the new model, and print to the terminal.
    model_name = model.name()
    model_id = model.id()

    print("The model name is: " + model_name)
    print("The model id is: " + model_id)

    #  2.b
    #     Return the number of components contained in the model, and print to the screen.
    number_of_components = model.componentCount()
    print("The '{m}' model has {n} component(s):".format(
        m=model_name,
        n=number_of_components)
    )

    #  2.c
    #     Return the name and id of the first component and print them to the screen.
    component = model.component(0)
    component_name = component.name()
    component_id = component.id()
    print("  Component[0] has name: ", component_name)
    print("  Component[0] has id: ", component_id)

    #  2.d
    #     Retrieve the number of variables in the component, and print to the screen.
    number_of_variables = component.variableCount()
    print("  The '{c}' component has {n} variable(s):".format(
        c=component_name,
        n=number_of_variables)
    )
    #  2.e
    #     Loop through the variables in the component, and print their names to the terminal.
    for v in range(0, number_of_variables):
        print("    Variable[{v}] has name: {n}".format(v=v, n=component.variable(v).name()))

    #  2.f
    #     Investigate the maths which connects the variables in this component.  Note that
    #     maths in libCellML is stored as a single MathML2 string.
    maths_string = component.math()
    print("  Component '{c}' has a MathML string of: ".format(c=component_name))
    print("		"+maths_string)

Step 3: Serialise the model and output to a file

Now that you’ve seen how to deserialise a CellML file into a model using the Parser, it’s time to go in the other direction. This reverse operation is handled by the Printer, which will transform a model into a string for output to a file.

auto printer = libcellml::Printer::create();
std::string serialisedModelString = printer->printModel(myFirstModel);

3.a Create a printer and use it to serialise your model.

3.b Write that serialised string to a new .cellml file.

3.c Go and have a cuppa, you’re done :)

Show C++ snippet

    //  3.a
    //      Create a Printer and use it to serialise the model to a string.

    auto printer = libcellml::Printer::create();
    std::string serialisedModelString = printer->printModel(model);

    //  3.b
    //      Write the serialised string to a file.

    std::string outFileName = "tutorial1_printed.cellml";
    std::ofstream outFile(outFileName);
    outFile << serialisedModelString;
    outFile.close();

    std::cout << "The " << modelName << " has been printed to: " << outFileName
              << std::endl;

Show Python snippet

    #  3.a
    #     Create a Printer and use it to serialise the model to a string.
    printer = Printer()
    serialised_model = printer.printModel(model)

    #  3.b
    #     Write the serialised string to a file.
    write_file = open("tutorial1_printed.cellml", "w")
    write_file.write(serialised_model)
    print("The {} has been printed to tutorial1_printed.cellml".format(model_name))