Creating a model in Antimony is designed to be very straightforward and simple. Model elements are created and defined in text, with a simple syntax.
The most common way to use Antimony is to create a reaction network, where processes are defined wherein some elements are consumed and other elements are created. Using the language of SBML, the processes are called ‘reactions’ and the elements are called ‘species’, but any set of processes and elements may be modeled in this way. The syntax for defining a reaction in Antimony is to list the species being consumed, separated by a ‘
+‘, followed by an arrow (‘
->‘), followed by another list of species being created, followed by a semicolon. If this reaction has a defined mathematical rate at which this happens, that rate can be listed next:
S1 -> S2; k1*S1
The above model defines a reaction where ‘
S1‘ is converted to ‘
S2‘ at a rate of ‘
This model cannot be simulated, however, because a simulator would not know what the conditions are to start the simulation. These values can be set by using an equals sign:
S1 -> S2; k1*S1 S1 = 10 S2 = 0 k1 = 0.1
The above, then, is a complete model that can be simulated by any software that understands SBML (to which Antimony models can be converted).
If you want to give your model a name, you can do that by wrapping it with the text: ‘
model [name] [reactions, etc.] end‘:
# Simple UniUni reaction with first-order mass-action kinetics model example1 S1 -> S2; k1*S1 S1 = 10 S2 = 0 k1 = 0.1 end
In subsequent examples in this tutorial, we’ll be using this syntax to name the examples, but for simple models, the name is optional. Later, when we discuss submodels, this will become more important.
There are many more complicated options in Antimony, but the above has enough power to define a wide variety of models, such as this oscillator:
model oscli #Reactions: J0: -> S1; J0_v0 J1: S1 -> ; J1_k3*S1 J2: S1 -> S2; (J2_k1*S1 - J2_k2*S2)*(1 + J2_c*S2^J2_q) J3: S2 -> ; J3_k2*S2 # Species initializations: S1 = 0 S2 = 1 # Variable initializations: J0_v0 = 8 J1_k3 = 0 J2_k1 = 1 J2_k2 = 0 J2_c = 1 J2_q = 3 J3_k2 = 5 end
Example Antimony Scripts
Single-line comments in Antimony can be created using the
# symbol, and multi-line comments can be created by surrounding them with
/* [comments] */.
/* This is an example of a multi-line comment for this tutorial */ model example2 J0: S1 -> S2 + S3; k1*S1 #Mass-action kinetics S1 = 10 #The initial concentration of S1 S2 = 0 #The initial concentration of S2 S3 = 3 #The initial concentration of S3 k1 = 0.1 #The value of the kinetic parameter from J0. end
The names of the reaction and the model are saved in SBML, but any comments are not.
Reactions can be created with multiple reactants and/or products, and the stoichiometries can be set by adding a number before the name of the species:
-> S1; k0 S1 -> S2; k1*S1 S1 + S2 -> S3; k2*S1*S2 2 S1 -> S2; k3*S1*S1 S1 + 2 S2 -> 3 S3 + 5 S4; k4*S1*S2*S2
Rate Laws and Initializing Values
Reactions can be defined with a wide variety of rate laws
model pathway() # Examples of different rate laws and initialization S1 -> S2; k1*S1 S2 -> S3; k2*S2 - k3*S3 S3 -> S4; Vm*S3/(Km + S3) S4 -> S5; Vm*S4^n/(Km + S4)^n S1 = 10 S2 = 0 S3 = 0 S4 = 0 S5 = 0 k1 = 0.1 k2 = 0.2 Vm = 6.7 Km = 1E-3 n = 4 end
Boundary species are those species which are unaffected by the model. Usually this means they are fixed. There are two ways to declare boundary species.
1) Using a dollar sign to indicate that a particular species is fixed:
model pathway() # Example of using $ to fix species $S1 -> S2; k1*S1 S2 -> S3; k2*S2 S3 -> $S4; k3*S3 end
2) Using the
const keyword to declare species are fixed:
model pathway() # Examples of using the const keyword to fix species const S1, S4 S1 -> S2; k1*S1 S2 -> S3; k2*S2 S3 -> S4; k3*S3 end
For multi-compartment models, or models where the compartment size changes over time, you can define the compartments in Antimony by using the ‘compartment’ keyword, and designate species as being in particular compartments with the ‘in’ keyword:
model pathway() # Examples of different compartments compartment cytoplasm = 1.5, mitochondria = 2.6 const S1 in mitochondria var S2 in cytoplasm var S3 in cytoplasm const S4 in cytoplasm S1 -> S2; k1*S1 S2 -> S3; k2*S2 S3 -> S4; k3*S3 end
You can also initialize elements with more complicated formulas than simple numbers:
model pathway() # Examples of different assignments A = 1.2 k1 = 2.3 + A k2 = sin(0.5) k3 = k2/k1 S1 -> S2; k1*S1 S2 -> S3; k2*S2 S3 -> S4; k3*S3 end
Assignments in Time
If you want to define some elements as changing in time, you can either define the formula a variable equals at all points in time with a
:=, or you can define how a variable changes in time with
X', in which case you’ll also need to define its initial starting value. The keyword ‘time’ represents time.
model pathway() # Examples of assignments that change in time k1 := sin(time) # k1 will always equal the sine of time k2 = 0.2 k2' = k1 #' k2 starts at 0.2, and changes according to the value # of k1: d(k2)/dt = k1 S1 -> S2; k1*S1 S2 -> S3; k2*S2 end
Events are discontinuities in model simulations that change the definitions of one or more symbols at the moment when certain conditions apply. The condition is expressed as a boolean formula, and the definition changes are expressed as assignments, using the keyword ‘
at (x>5): y=3, x=r+2
In a model with this event, at any moment when x transitions from being less than or equal to 5 to being greater to five, y will be assigned the value of 3, and x will be assigned the value of r+2, using whatever value r has at that moment. The following model sees the conversion of S1 to S2 until a threshold is reached, at which point the cycle is reset.
model reset() S1 -> S2; k1*S1 E1: at (S2>9): S2=0, S1=10 S1 = 10 S2 = 0 k1 = 0.5 end
For more advanced options with events, see Antimony’s full documentation.
You may create user-defined functions in a similar fashion to the way you create modules, and then use these functions in Antimony equations. These functions must be basic single equations, and act in a similar manner to macro expansions. As an example, you might define the quadratic equation and use it in a later equation as follows:
function quadratic(x, a, b, c) a*x^2 + b*x + c end model quad1 S3 := quadratic(s1, k1, k2, k3); end
This effectively defines S3 to always equal the equation “k1*s1^2 + k2*s1 + k3”.
Antimony was actually originally designed to allow the modular creation of models, and has a basic syntax set up to do so. For a full discussion of Antimony modularity, see the full documentation, but at the most basic level, you define a re-usable module with the ‘model’ syntax, followed by parentheses where you define the elements you wish to expose, then import it by using the model’s name, and the local variables you want to connect to that module
# This creates a model 'side_reaction', exposing the variables 'S' and 'k1': model side_reaction(S, k1) J0: S + E -> SE; k1*k2*S*E - k2*ES; E = 3; SE = E+S; k2 = 0.4; end # In this model, 'side_reaction' is imported twice: model full_pathway -> S1; k1 S1 -> S2; k2*S1 S2 -> ; k3*S2 A: side_reaction(S1, k4) B: side_reaction(S2, k5) S1 = 0 S2 = 0 k1 = 0.3 k2 = 2.3 k3 = 3.5 k4 = 0.0004 k5 = 1 end
In this model, ‘A’ is a submodel that creates a side-reaction of S1 with A.E and A.SE, and ‘B’ is a submodel that creates a side-reaction of S2 with B.E and B.SE. It is important to note that there is no connection between A.E and B.E (nor A.ES and B.ES): they are completely different species in the model.
More than one file may be used to define a set of modules in Antimony through the use of the ‘
import‘ keyword. At any point in the file outside of a module definition, use the word ‘
import‘ followed by the name of the file in quotation marks, and Antimony will include the modules defined in that file as if they had been cut and pasted into your file at that point. SBML files may also be included in this way:
import "models1.txt" import "oscli.xml" model mod2() A: mod1(); B: oscli(); end
In this example, the file ‘
models1.txt‘ is an Antimony file that defines the module ‘
mod1‘, and the file ‘
oscli.xml‘ is an SBML file that defines a model named ‘
oscli‘. The Antimony module ‘
mod2‘ may then use modules from either or both of the other imported files.
While units do not affect the mathematics of SBML or Antimony models, you can define them in Antimony for annotation purposes by using the ‘
unit substance = 1e-6 mole; unit hour = 3600 seconds;
Adding an ‘s’ to the end of a unit name to make it plural is fine when defining a unit: ‘
3600 second‘ is the same as ‘
3600 seconds‘. Compound units may be created by using formulas with ‘
/‘, and ‘
^‘. However, you must use base units when doing so (‘base units’ defined as those listed in Table 2 of the SBML Level 3 Version 1 specification, which mostly are SI and SI-derived units).
unit micromole = 10e-6 mole / liter; unit daily_feeding = 1 item / 86400 seconds unit voltage = 1000 grams * meters^2 / seconds^-3 * ampere^-1
You may use units when defining formulas using the same syntax as above: any number may be given a unit by writing the name of the unit after the number. When defining a symbol (of any numerical type: species, parameter, compartment, etc.), you can either use the same technique to give it an initial value and a unit, or you may just define its units by using the ‘has’ keyword:
unit foo = 100 mole/5 liter; x = 40 foo/3 seconds; # '40' now has units of 'foo' and '3' units of 'seconds'. y = 3.3 foo; # 'y' is given units of 'foo' and an initial # value of '3.3'. z has foo; # 'z' is given units of 'foo'.