In this notebook, we show some examples that use Myokit's IDE: a graphical user interface for model, protocol, and script development. In particular, we'll focus on methods to explore a model written in Myokit's mmt syntax. A brief overview of this syntax is provided in a later tutorial, and a full description is given in the online documentation.
On linux and windows, you may be able to start the IDE using an icon from the applications panel or start menu. If no icons are present, you may be able to install them by running the command python3 -m myokit icons
from the command line.
In all cases, the IDE can be run using myokit ide
, or python3 -m myokit ide
.
You can also specify a file to open, e.g.: myokit ide models/decker-2009.mmt
.
Once you have started the IDE and loaded a model, you should see something like this:
The loaded mmt
file is split over three panels, for "Model definition", "Protocol definition", and "Embedded script". These are the same parts you can access programmatically by typing:
import myokit
model, protocol, script = myokit.load('models/c/decker-2009.mmt')
You can validate the loaded model and protocol by htting "Ctrl+B" or selecting "Run > Validate model and protocol" from the menu. If successful, you should see a message appearing in the console at the bottom of the screen:
[14:44:14] No errors found in model definition.
[14:44:14] No errors found in protocol.
If there are errors, a detailed error message should appear here:
[15:04:28] Unresolved reference
Unknown variable: <q17>. Unknown variable: <q17>. Did you mean "ical.p17"?
On line 509 character 17
Is_Ca = p16 - (q17 / (1 + zz))
^
To jump to the position in the model where the error was detected, you can use "Run > Jump to last error" or hit "Ctrl+Space".
Another tool to navigate the model is the list of model components, which you can enable by selecting "View > Show model components" from the menu. This lets you jump to model components with a single click.
In many cases, the first thing you'll want to see from a model is its output. An easy way to do this is to run a simulation with the explorer. To open the explorer, hit "F6" or select "Run Explorer" from the toolbar or the "Run" menu.
Then hit the "Run" button to see the results of a simulation:
By default, the explorer will show a plot of the first state variable versus time, plotted over a time span guessed from the protocol. But you can choose other variables from the drop-down boxes:
You can also adapt the simulation duration, or add an unlogged (and unplotted) pre-pacing period.
Finally, you can see the impact of changing model variables by running an explorer simulation, the modifying the model (without closing the explorer), and running a second simulation:
A fun way to look at model structure is too use the visualisation options provided under the "Analysis" menu. For example, we can create a graph of the dependencies between a model's components using "Analysis > Show component dependency graph".
In this graph, the bottom row shows the components that don't use variables from any other component, although dependencies on state variables and on constant variables are excluded. For the Decker et al. model used in this example, we can see e.g. the phys
component, which provides physical constants, but also the nernst
component which calculates reversal potentials.
On the second row we see several current variables, which all depend on variables from the lower row, such as reversal potentials. Moving up again we find concentrations, which are all updated using calculated currents. At the top we see the membrane component, which depends on currents and concentrations alike, and the "irel" component, which calculates calcium release fluxes from the SR.
After some intense staring we may also discover that this particular graph does not contain any cycles: given the values of all constants/parameters, and the state at some time t, we can calculate the state derivatives for this model component by component.
This is a handy property when evaluating a model, and it certainly makes generated code look prettier. However, in most cases Myokit does not require models to have this property. The exception to this rule is the OpenCL based multi-cell simulation class, although there is a ticket open to remove this restriction. To see if model's can be evaluated component by component, select "Analysis > Show cyclical component dependencies" from the menu.
We can draw a similar graph for model variables using "Analysis > Show variable dependency graph".
For small models, we get a simple graph. But for bigger models the graph is terrifying:
A much more instructive graph in these cases can be obtained from "Analysis > Show state dependency matrix".
In this matrix-style graph, each row and column represents a state variable. A black box on position (i,j) indicates that the derivative of the state variable on row i depends on the value of the state variable on row j.
In other words, to calculate ddt of the variable on row i, you will need the value of the variable in column j. As a result, the black boxes in this graph can be read as a visual representation of the system's Jacobian: a black box indicates a non-zero (but not necessarily significant) entry in the Jacobian matrix.
We can see from this graph that the Decker et al. model is highly connected: nearly all variables depend on the membrane potential, which in turn depends on nearly every other state variable. This property holds true for most models of the cardiac action potential.
We can also see that the matrix is quite sparse: Membrane potential and concentrations (all listed near the top) interact with several variables, but many other variables only interact with their own value and with the membrane potential. Again this is a property shared by most other cardiac AP models.
We can see two exceptions to this highly connected squares: one for the ical
component and one for iks
.
These represent Markov models, in which each state's derivative depends on several other states.
Finally, if two variables have no direct connection, we might ask how many solver updates it takes before the two variables are connected indirectly. This question is answered by the coloured entries in the matrix. The darkest blue colour (indicated with a "2" in the legend on the right) shows variables that have a length-2 connection. Lighter colours indicate length-3, 4, 5, etc. he least connected variables in this model are shown in green, and have a length-13 connection.
Often when inspecting a model equation, it contains one or more variables whose meaning we don't know or can't guess. For example, we might see an equation like the below:
dot(uCa_sr) = (
inaca.INaCaSS * cell.AFC / cell.v_sr
+ irel.Irel * cell.v_jsr / cell.v_sr
- (diff.Ca + diff.CaL)
)
in [mM]
This contains several variables defined in other components (and not always with great names).
To get some information about a variable in the IDE, we can place the cursor on it (or select it), and hit Ctrl+R or click "Analysis > Show quick variable info".
For the variable diff.Ca
in the example above, this shows:
[19:14:48] Showing: diff.Ca (Intermediary variable)
in [mM/ms]
desc: Diffusion of Ca2+ between SS,SR and the bulk myoplasm
Defined on line 997
The first line tells us that this is (what Myokit calls) an intermediary variable: a variable that isn't a state, but which does depend on states. (The name derives from the fact that in each step of an ODE integration the intermediary variables must be recalculated, and the results of these calculations is used to calculate the derivatives at that step.)
We can also see its units, a desc
or description meta data annotation, and the line on which the variable was defined.
At this point, we might want to see how the variable is evaluated (and what it's current value is). This can be done by selecting the variable and clicking "Analysis > Show variable evaluation", or hitting Ctrl+E:
Showing: diff.Ca (Intermediary variable)
------------------------------------------------------------
in [mM/ms]
------------------------------------------------------------
desc: Diffusion of Ca2+ between SS,SR and the bulk myoplasm
------------------------------------------------------------
calciumb.Ca_i = 9.68326116826401240e-05
calciumb.Ca_sr = 1.38150560918259835e-04
diff.tau_diff = 0.2
------------------------------------------------------------
diff.Ca = (calciumb.Ca_sr - calciumb.Ca_i) / diff.tau_diff
= 2.06589746178098554e-04
This shows us the equation for diff.Ca
(at the bottom), and the current values of all variables appearing in this expression.
To get even more information, we can use "Analysis > Show variable dependencies" or Ctrl+D to obtain:
Showing: diff.Ca (Intermediary variable)
------------------------------------------------------------
in [mM/ms]
------------------------------------------------------------
desc: Diffusion of Ca2+ between SS,SR and the bulk myoplasm
------------------------------------------------------------
diff.Ca is a function of:
calcium.uCa_i
calcium.uCa_sr
------------------------------------------------------------
Expressions for diff.Ca:
calciumb.Km_cmdn = 0.00238 [mM]
calciumb.Km_trpn = 0.0005 [mM]
calciumb.cmdnBar = 0.05 [mM]
...
c = calciumb.pro_sum2 - calciumb.Km_sum2 * calcium.uCa_sr
calciumb.Ca_i = -b / 3 + 2 / 3 * sqrt(b * b - 3 * c) * cos(acos((9 * b * c - 2 * b^3 - 27 * d) / (2 * (b * b - 3 * c)^1.5)) / 3)
calciumb.Ca_sr = -b / 3 + 2 / 3 * sqrt(b * b - 3 * c) * cos(acos((9 * b * c - 2 * b^3 - 27 * d) / (2 * (b * b - 3 * c)^1.5)) / 3)
diff.Ca = (calciumb.Ca_sr - calciumb.Ca_i) / diff.tau_diff
This example shows the full evaluation of the variable (which we've abbreviated here for clarity), starting from the current state variables and/or bound variables. (Bound variables, in Myokit terminology, are variables that are expected to be "bound" to a simulation engine variable during simulation, e.g. the time variable.)
Near the top we can see the two state variables that this variable depends on: calcium.uCa
and calcium.uCa_sr
.
Instead of seeing the variables that our variable depends on, we might want to see its dependents, a.k.a. the "users" of this variable. This can be done by selecting "Analysis > Show variable users" from the menu, or with Ctrl+U.
For diff.Ca
we find:
[19:34:50] The following variables depend on diff.Ca:
calcium.uCa_i
calcium.uCa_sr
Note that, unlike the "Show variable dependencies" option, this function only shows variables related through direct dependency.
Of course, we might want to prefer to just have a look at the variable's defining code instead. We can read off the line number from the "quick" variable info, but we can also use "Analysis > Jump to variable definition" or Ctrl+J to jump directly to the line on which a variable is defined.
Last but not least, we can use "Analysis > Graph selected variable" or Ctrl+G.
Like the "dependencies" command before, this command will make a list of the selected variable's dependencies, but instead of displaying it it will use this information to generate some NumPy code that can evaluate the variable, as a function of the model state.
Next, it will make some guesses about the meaning of the used states (e.g. is in mV and called "V"?) and guess a sensible range to plot them over.
This works really well for e.g. Hodgkin-Huxley gating variables:
selecting ikr.inf
and using Ctrl+G we get:
However, there are lots of variables that can't easily be graphed this way (e.g. because they depend on more than 1 state variable).
For example, for the variable ito.ITo
in this model we get
This variable depends on 5 states: two of which were arbitrarily selected for variation, and three of which were fixed.
A handy part of the IDE that's not covered in this tutorial are the import and export options in the "Convert" menu.
This menu provides common import/export options. A full list can be obtained from the API documentation. This is covered in more detail in the "Importing and exporting" section of the tutorial.