{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"https://libcellml.org/documentation/v0.4.0/user/tutorials/hh_tutorial1/index"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from libcellml import Analyser, Component, Model, Printer, Units, Validator, Variable, cellmlElementTypeAsString\n",
"from combine_notebooks.cellml_utilities import print_model"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Setup the model"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MODEL: 'GateModel'\n",
" UNITS: 0 custom units\n",
" COMPONENTS: 1 components\n",
" [0]: 'gate'\n",
" VARIABLES: 0 variables\n"
]
}
],
"source": [
"# Setup useful things.\n",
"math_header = ''\n",
"\n",
"# The first step is to create a Model item which will later contain the component and \n",
"# the units it needs. \n",
"model = Model()\n",
"\n",
"# Each CellML element must have a name, which is set using the setName() function.\n",
"model.setName('GateModel')\n",
"\n",
"# We'll create a wrapper component whose only job is to encapsulate the other components.\n",
"# This makes is a lot easier for this model to be reused, as the connections between\n",
"# components internal to this one won't need to be re-established.\n",
"# Note that the constructor for all named CellML entities is overloaded, so \n",
"# you can pass it the name string at the time of creation.\n",
"# Create a component named 'gate'.\n",
"gate = Component('gate')\n",
"\n",
"# Finally we need to add the component to the model. This sets it at the top-level of \n",
"# the components' encapsulation hierarchy. All other components need to be added \n",
"# to this component, rather than the model.\n",
"# Add the component to the model using the Model::addComponent() function.\n",
"model.addComponent(gate)\n",
"\n",
"# Print the model to the terminal using the print_model helper function and \n",
"# check it is what you'd expect.\n",
"print_model(model)\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the gateEquations component"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MODEL: 'GateModel'\n",
" UNITS: 0 custom units\n",
" COMPONENTS: 1 components\n",
" [0]: 'gate'\n",
" VARIABLES: 0 variables\n",
" COMPONENT gate has 1 child components:\n",
" [0]: 'gateEquations'\n",
" VARIABLES: 0 variables\n",
" Maths in the component is:\n",
"\n"
]
}
],
"source": [
"\n",
"# Create a gateEquations component and name it 'gateEquations'.\n",
"gateEquations = Component('gateEquations')\n",
"\n",
"# Add the new gateEquations component to the gate component.\n",
"gate.addComponent(gateEquations)\n",
"\n",
"# Add the mathematics to the gateEquations component.\n",
"equation = \\\n",
" ' \\n'\\\n",
" ' \\n'\\\n",
" ' t\\n'\\\n",
" ' X\\n'\\\n",
" ' \\n'\\\n",
" ' \\n'\\\n",
" ' \\n'\\\n",
" ' alpha_X\\n'\\\n",
" ' \\n'\\\n",
" ' 1\\n'\\\n",
" ' X\\n'\\\n",
" ' \\n'\\\n",
" ' \\n'\\\n",
" ' \\n'\\\n",
" ' beta_X\\n'\\\n",
" ' X\\n'\\\n",
" ' \\n'\\\n",
" ' \\n'\\\n",
" ' \\n'\n",
"\n",
"gateEquations.setMath(math_header)\n",
"gateEquations.appendMath(equation)\n",
"gateEquations.appendMath(math_footer)\n",
"\n",
"# Print the model to the terminal using the print_model helper function and \n",
"# check it is what you'd expect. Include the second argument as True so that \n",
"# the maths is included.\n",
"print_model(model, True)\n",
"\n",
"# Once the mathematics has been added to the component, and the component to the \n",
"# model, we can make use of the diagnostic messages within the Validator class\n",
"# to tell us what else needs to be done. "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Validate the model"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Create a Validator instance, and pass it your model for processing using the \n",
"# validateModel function. \n",
"validator = Validator()\n",
"validator.validateModel(model)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Add the variables"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Create items for the missing variables and add them to the gateEquations component.\n",
"# You will need to be sure to give them names which match exactly those reported by the\n",
"# validator, or are present in the MathML string. \n",
"gateEquations.addVariable(Variable('t'))\n",
"gateEquations.addVariable(Variable('alpha_X'))\n",
"gateEquations.addVariable(Variable('beta_X'))\n",
"gateEquations.addVariable(Variable('X'))\n",
"\n",
"# Validate again, and expect errors relating to missing units.\n",
"# Note that you can use the helper function print_issues(validator) to print your\n",
"# issues to the screen instead of repeating the code from 3.b.\n",
"validator.validateModel(model)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Add the units"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MODEL: 'GateModel'\n",
" UNITS: 4 custom units\n",
" [0]: ms\n",
" [1]: per_ms\n",
" [2]: ms\n",
" [3]: per_ms\n",
" COMPONENTS: 1 components\n",
" [0]: 'gate'\n",
" VARIABLES: 0 variables\n",
" COMPONENT gate has 1 child components:\n",
" [0]: 'gateEquations'\n",
" VARIABLES: 4 variables\n",
" [0]: t [ms]\n",
" [1]: alpha_X [per_ms]\n",
" [2]: beta_X [per_ms]\n",
" [3]: X [dimensionless]\n",
" Maths in the component is:\n",
"\n"
]
}
],
"source": [
"# The validator has reported that the four variables are missing units attributes. \n",
"# In this example none of the units exist yet, so we need to create all of them. \n",
"# The variables' units should be:\n",
"# - t, time has units of *milliseconds*\n",
"# - X, gate status has units of *dimensionless*\n",
"# - alpha_X and beta_X, rates, have units of *per millisecond*.\n",
"\n",
"# Create the units which will be needed by your variables and add them to the model.\n",
"ms = Units('ms')\n",
"per_ms = Units('per_ms')\n",
"\n",
"# Add Unit items to the units you created to define them.\n",
"ms.addUnit('second', 'milli')\n",
"per_ms.addUnit('second', 'milli', -1)\n",
"\n",
"# Add the Units to the model (not the component) so that other components can make \n",
"# use of them too.\n",
"model.addUnits(ms)\n",
"model.addUnits(per_ms)\n",
"\n",
"# Use the setUnits function to associate them with the appropriate variables. \n",
"gateEquations.variable('t').setUnits(ms)\n",
"gateEquations.variable('alpha_X').setUnits(per_ms)\n",
"gateEquations.variable('beta_X').setUnits(per_ms)\n",
"gateEquations.variable('X').setUnits('dimensionless')\n",
"\n",
"# Validate again, and expect no errors.\n",
"validator.validateModel(model)\n",
"\n",
"# Print the model to the terminal and include the optional second argument of true\n",
"# to include the MathML.\n",
"print_model(model, True)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Analyse the model"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"# Create an Analyser item and submit the model for processing. \n",
"analyser = Analyser()\n",
"analyser.analyseModel(model)\n",
"\n",
"# In order to avoid hard-coding values here, we will need to connect to external \n",
"# values to initialise the X variable and provide the value for alpha_X and beta_X.\n",
"# This means that:\n",
"# - we need to create an external component to hold variable values\n",
"# - we need to create external variables in that component \n",
"# - we need to specify the connections between variables and\n",
"# - we need to permit external connections on the variables.\n",
"\n",
"# Create a component which will store the hard-coded values for initialisation.\n",
"# Name it 'gateParameters', and add it to the top-level gate component as a sibling\n",
"# of the gateEquations component.\n",
"gateParameters = Component('gateParameters')\n",
"gate.addComponent(gateParameters)\n",
"\n",
"# Create appropriate variables in this component, and set their units.\n",
"# Use the setInitialValue function to initialise them.\n",
"X = Variable('X')\n",
"X.setUnits('dimensionless')\n",
"X.setInitialValue(0)\n",
"gateParameters.addVariable(X)\n",
"\n",
"alpha = Variable('alpha')\n",
"alpha.setUnits(per_ms)\n",
"alpha.setInitialValue(0.1)\n",
"gateParameters.addVariable(alpha)\n",
"\n",
"beta = Variable('beta')\n",
"beta.setUnits(per_ms)\n",
"beta.setInitialValue(0.5)\n",
"gateParameters.addVariable(beta)\n",
"\n",
"# Specify a variable equivalence between the gateEquations variables and the parameter variables.\n",
"# Validate the model again, expecting errors related to the variable interface types.\n",
"Variable.addEquivalence(gateEquations.variable('X'), gateParameters.variable('X'))\n",
"Variable.addEquivalence(gateEquations.variable('alpha_X'), gateParameters.variable('alpha'))\n",
"Variable.addEquivalence(gateEquations.variable('beta_X'), gateParameters.variable('beta'))\n",
"\n",
"validator.validateModel(model)\n",
"\n",
"# Set the variable interface type according to the recommendation from the validator.\n",
"# This can either be done individually using the Variable::setInterfaceType() function, or \n",
"# en masse for all the model's interfaces using the Model::fixVariableInterfaces() function.\n",
"# Validate and analyse again, expecting no errors. \n",
"model.fixVariableInterfaces()\n",
"\n",
"validator.validateModel(model)\n",
"\n",
"analyser.analyseModel(model)\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Sanity check"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MODEL: 'GateModel'\n",
" UNITS: 4 custom units\n",
" [0]: ms\n",
" [1]: per_ms\n",
" [2]: ms\n",
" [3]: per_ms\n",
" COMPONENTS: 1 components\n",
" [0]: 'gate'\n",
" VARIABLES: 0 variables\n",
" COMPONENT gate has 4 child components:\n",
" [0]: 'gateEquations'\n",
" VARIABLES: 4 variables\n",
" [0]: t [ms]\n",
" [1]: alpha_X [per_ms]\n",
" └──> gateParameters:alpha [per_ms], gateParameters:alpha [per_ms], gateParameters:alpha [per_ms]\n",
" [2]: beta_X [per_ms]\n",
" └──> gateParameters:beta [per_ms], gateParameters:beta [per_ms], gateParameters:beta [per_ms]\n",
" [3]: X [dimensionless]\n",
" └──> gateParameters:X [dimensionless], gateParameters:X [dimensionless], gateParameters:X [dimensionless]\n",
" [1]: 'gateParameters'\n",
" VARIABLES: 3 variables\n",
" [0]: X [dimensionless], initial = 0\n",
" └──> gateEquations:X [dimensionless]\n",
" [1]: alpha [per_ms], initial = 0.1\n",
" └──> gateEquations:alpha_X [per_ms]\n",
" [2]: beta [per_ms], initial = 0.5\n",
" └──> gateEquations:beta_X [per_ms]\n",
" [2]: 'gateParameters'\n",
" VARIABLES: 3 variables\n",
" [0]: X [dimensionless], initial = 0\n",
" └──> gateEquations:X [dimensionless]\n",
" [1]: alpha [per_ms], initial = 0.1\n",
" └──> gateEquations:alpha_X [per_ms]\n",
" [2]: beta [per_ms], initial = 0.5\n",
" └──> gateEquations:beta_X [per_ms]\n",
" [3]: 'gateParameters'\n",
" VARIABLES: 3 variables\n",
" [0]: X [dimensionless], initial = 0\n",
" └──> gateEquations:X [dimensionless]\n",
" [1]: alpha [per_ms], initial = 0.1\n",
" └──> gateEquations:alpha_X [per_ms]\n",
" [2]: beta [per_ms], initial = 0.5\n",
" └──> gateEquations:beta_X [per_ms]\n"
]
}
],
"source": [
"# Print the model to the terminal using the helper function print_model.\n",
"print_model(model)\n",
"\n",
"# Looking at the printout we see that the top-level component has no variables. \n",
"# Even though this is clearly a valid situation (as proved by 4.f), it's not\n",
"# going to make this model easy to reuse. We need to make sure that any input and\n",
"# output variables are also connected into the top level gate component. \n",
"\n",
"# Create intermediate variables for time t and gate status X in the gate component,\n",
"# and ensure they have a public and private interface to enable two-way connection.\n",
"# You may also need to set a public and private connection onto t and X in the\n",
"# equations component too.\n",
"gate.addVariable(gateEquations.variable('t').clone())\n",
"gate.addVariable(gateEquations.variable('X').clone())\n",
"\n",
"gate.variable('t').setInterfaceType('public_and_private')\n",
"gate.variable('X').setInterfaceType('public_and_private')\n",
"gateEquations.variable('t').setInterfaceType('public_and_private')\n",
"gateEquations.variable('X').setInterfaceType('public_and_private')\n",
"\n",
"# Connect the intermediate variables to their respective partners in the equations\n",
"# component, and recheck the model.\n",
"Variable.addEquivalence(gate.variable('t'), gateEquations.variable('t'))\n",
"Variable.addEquivalence(gate.variable('X'), gateEquations.variable('X'))\n",
"\n",
"validator.validateModel(model)\n",
"analyser.analyseModel(model)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Serialise and output the model"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The created model has been written to hello_world_cellml.cellml\n"
]
}
],
"source": [
"from combine_notebooks import RESULTS_DIR\n",
"from pathlib import Path\n",
"\n",
"cellml_path: Path = RESULTS_DIR / 'hello_world_cellml.cellml'\n",
"\n",
"# Create a Printer instance and use it to serialise the model. This creates a string\n",
"# containing the CellML-formatted version of the model. Write this to a file called\n",
"# 'hello_world_cellml.cellml'.\n",
"printer = Printer()\n",
"write_file = open(cellml_path, 'w')\n",
"write_file.write(printer.printModel(model))\n",
"write_file.close()\n",
"\n",
"print('The created model has been written to hello_world_cellml.cellml')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}