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:

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?
    Give feedback

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?

Give feedback

 

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.