Introduction
MML events model discrete changes of physical and physiological state such as voltage transitions, heart beats, etc. Events also provide a mechanism for calculation of recursive functions, such as Fibonacci numbers. Event-driven variables can freely intermix with continuous (e.g. ODE-calculated) variables, allowing you to model hybrid discrete/continuous systems.
Prerequisites:
- Introductory Survey of MML (required)
- Introduction to the JSim GUI (recommended)
Contents:
- Overview
- State variables and a simple example
- Gated example 1
- Gated example 2
- Gated example 3
- Events using ODE variables
- Recursive functions
- Some cautions
- Comments or Questions?
Overview
An MML event consists of a realDomain t (which usually represents time), a boolean trigger expression which is a function of t and a series of action assignments. The trigger is tested for each value of t, and the event is fired if the value of the trigger evaluates to true. When the event fires, the action assignments are executed in the order specified.
Action assignments are of the form "v = expression;" where v is a realState, intState or ODE variable. A realState and intState variable v(t) must be assigned an initial value when t=t.min and subsequent values of v(t) are calculate by carrying the value from the previous time step forward, and then altering it based upon any triggered events. An ODE variable is assigned an initial value when t=t.min and subsequent values are calculated via an ODE starting with the previous t value. Once the ODE calculation is complete, v(t) is altered based upon any triggered events.
State variables and a simple example
Here is a simple example that counts the number of times an externally provided voltage V exceeds a threshold voltage V0:
// event to count voltages above threshhold math count { realDomain t; t.min=0; t.max=10; t.delta=1; real V0 = 0.5; extern real V(t); intState ct(t); when (t=t.min) ct=0; event (V>V0) ct=ct+1; }
The state variable ct(t) is initialized to 0 at time zero, and is incremented each time the event (V>V0) is triggered. The event contains a single event action "ct=ct+1". The statement "ct=ct+1" would be nonsense as an MML equation, but is acceptable as an event action.
Gate Example 1
The following model describes a gate that opens when an external voltage exceeds threshold V1, and closes when it drops below threshold V0:
// event models gate opening/closing math main { realDomain t; t.min=0; t.max=10; t.delta=0.1; extern real V(t); real V1 = 10; real V0 = 5; intState open(t); when (t=t.min) open = 0; event (open=0 and V>V1) open = 1; event (open>0 and V<V0) open = 0; }
By using two events, we have introduced memory into the system. The value of "open" for V between V0 and V1 is dependent not upon the current value of V, but upon previous values.
Gate example 2
The event triggers in gate example 1 are slightly redundant. The gate opening trigger "V>V1" would have the same effect as the one given "open=0 and V>V1". However, by using the longer form, we can ensure that the events "gate opening" and "gate closing" happen alternately, which is useful in the following model. A running count is kept of the number of gate openings is kept in variable ct. The time of the latest gate closure is kept in variable tlast. Multiple actions may be associated with a single event by enclosing them in curly braces.
// ensuring gate openings/closings occur alternately math main { realDomain t; t.min=0; t.max=10; t.delta=0.1; extern real V(t); real V1 = 10; real V0 = 5; intState open(t); intState ct(t); realState tlast(t); when (t=t.min) { open = 0; ct = 0; tlast = -1; } event (open=0 and V>V1) { open = 1; ct = ct+1; } event (open>0 and V<V0) { open = 0; tlast = t; } }
Gate example 3
Event-driven variables become more useful for modeling when their values drive the calculation of other system variables which, in turn, trigger events. In the following example, V either grows or shrinks exponentially based up whether the gate is open. Whether the gate is open depends upon current and previous values of V:
// gate opening/closing drives model ODE math main { realDomain t; t.min=0; t.max=10; t.delta=0.1; real V(t); real V1 = 10; real V0 = 5; intState open(t); when (t=t.min) { open = 0; V=1; } V:t = if (open>0) -V else V; event (open=0 and V>V1) open = 1; event (open>0 and V<V0) open = 0; }
Events using ODE variables
Events may act upon ODE variables (this is a new feature for JSim version 2). In the example below, the u decays exponentially starting a u(t.min)=1. When u fall below .2, an event resets it back to 1, resulting in a periodic wave form:
// Event on ODE variable math main { realDomain t; t.min=0; t.max=5; t.delta=.1; real u(t); when (t=t.min) u=1; u:t = -u; event (u<.2) u=1; }
Recursive functions
JSim events support calculation of recursive functions which are not supported elsewhere in MML. The following example calculates the Fibonacci numbers:
// event construct for recursive function call math main { realDomain n; n.min=1; n.max=10; n.delta=1; intState f(n); when (n=n.min) f=1; event (n>2) f = f(n-1) + f(n-2); }
Some cautions
Event triggers are tested once per time step. At any given time-step, zero, one or more than one events may be triggered. If multiple events are triggered at a time-step, the order in which they are triggered is undefined. However, the actions within an event always take place in the declared order when that event is triggered. It is up to the MML author to write their events and triggers in such a way so that the result is well defined.
Here's a simplified version of a problem that can easily occur. If the triggers in gated example 2:
event (open=0 and V>V1) { open = 1; ct = ct+1; }
had been written as:
event (open=0 and V>V1) open = 1; event (open=0 and V>V1) ct = ct+1;
then the value of ct would be unpredictable. If the first event trigger were tested first, the second event would never be triggered so ct would remain perpetually at 0. If the second event trigger were tested first, ct would be calculated as in the original example.
Comments or Questions?
Model development and archiving support at https://www.imagwiki.nibib.nih.gov/physiome provided by the following grants: NIH U01HL122199 Analyzing the Cardiac Power Grid, 09/15/2015 - 05/31/2020, NIH/NIBIB BE08407 Software Integration, JSim and SBW 6/1/09-5/31/13; NIH/NHLBI T15 HL88516-01 Modeling for Heart, Lung and Blood: From Cell to Organ, 4/1/07-3/31/11; NSF BES-0506477 Adaptive Multi-Scale Model Simulation, 8/15/05-7/31/08; NIH/NHLBI R01 HL073598 Core 3: 3D Imaging and Computer Modeling of the Respiratory Tract, 9/1/04-8/31/09; as well as prior support from NIH/NCRR P41 RR01243 Simulation Resource in Circulatory Mass Transport and Exchange, 12/1/1980-11/30/01 and NIH/NIBIB R01 EB001973 JSim: A Simulation Analysis Platform, 3/1/02-2/28/07.