{ "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", "math_footer = ''\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", " \n", " \n", " t\n", " X\n", " \n", " \n", " \n", " alpha_X\n", " \n", " 1\n", " X\n", " \n", " \n", " \n", " beta_X\n", " X\n", " \n", " \n", " \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", " \n", " \n", " t\n", " X\n", " \n", " \n", " \n", " alpha_X\n", " \n", " 1\n", " X\n", " \n", " \n", " \n", " beta_X\n", " X\n", " \n", " \n", " \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 }