10  Modeled events

Modeled events are interventions that you can introduce into your simulation from within your model. These aren’t any different in substance to the dosing records (EVID=1) or other intervention type records (EVID=2) that you might include in your input data set when you know what they are before you run the simulation. Modeled events do the same thing (stop the simulation and execute some event at some time) but you don’t need to know about them prior to running the simulation. These are similar to the MTIME functionality that you get in NONMEM but they have a very different syntax and there is more functionality provided.

Note that there is no way to get additional records in your simulated output. Regardless of the approach or level of complexity, you will not see modeled events in your simulated output. These are always executed under the hood and the number of rows in the simulated output and their times will be the same regardless of what modeled events you set up as discussed here.

10.1 Simple MTIME

Use this when you just want to introduce a non-dose discontinuity in your simulation at a specific time. For example, you want a parameter to change value at a specific time and you don’t know about the values or times prior to simulating.

To schedule a discontinuity, call the mtime() member (Section 2.3.22) of the self object (Section 2.3.12). This is typically done in the $MAIN block.

[ main ]
double mt = self.mtime(14.12);

if(TIME >= mt) {
  // do something  
}

Here, we have told mrgsolve to stop at 14.12 hours so we can do something. Notice that self.mtime() returns the value of the modeled even time so you can check it later.

We can also schedule an event to happen some amount of time in the future

[ main ]
if(NEWIND <= 1) {
  double mt = 1e9;  
}

if(EVID==1) {
  mt = self.mtime(TIME + 8.2);
}

if(TIME >= mt) {
  // do something  
}

10.2 MTIME with specific EVID

You can call self.mevent() and pass both time and evid and then check for when that EVID comes around again. For example

self.mevent(TIME + 8.2, 33);

if(EVID==33) {
  // do something  
}

This is similar in functionality to self.mevent().

10.3 Modeled doses

The previous examples showed you how to get the simulation to stop so you can do something in $MAIN. In this section, we show you how to schedule doses in a similar way. This will take some extra coding and will also serve to uncover how self.mtime() and self.mevent() work.

You can set up the following code in either $MAIN or $TABLE.

Create an evdata object

Once you know when you want the dose, create an evdata object.

mrg::evdata ev(14.2, 1);

This will create (construct) an object called ev with class evdata. The constructor takes two arguments:

  1. the TIME the event should happen
  2. the EVID for the event

This is the only available constructor for evdata objects. You can browse the source code for the evdata object here.

Modify the evdata object Once the object is created, you can modify the following public members

  • time: the event time (double)
  • evid: the event ID (int)
  • amt: the dose amount (double)
  • cmt: the compartment number (int)
  • rate: the rate to infuse amt (double)
  • now: should the dose be given immediately? (bool)
  • check_unique: should the event log be checked for identical events? see Section 10.4 (bool)

If you are using this (lower-level) interface, chances are you will want to set at least amt and cmt. As an example, we will dose 100 mg into compartment 2 immediately (now)

ev.amt = 100;
ev.cmt = 2;
ev.now = true;

The other members are set in a similar way.

Push the evdata object into the self object

After the object has been created and modified, you have to attach this object to the self object in order to make it available to mrgsolve. Do this by calling push_back() on self.mevector

self.mevector.push_back(ev);

Starting with mrgsolve 1.4.1, you can also push the event object back via the push member

self.push(ev);

Again, this sequence should get called in either $MAIN or $TABLE. When that code block finishes running (for the current record), mrgsolve will find the event record and add that event to the simulation sequence.

10.4 Event log - tracking duplicate events

The way modeled events are implemented in mrgsolve, it is possible to inadvertently ask to execute the same event a large number of times.

For example, when we have the following code

$PK
double mt = self.mtime(244, 33);

mrgsolve only needs to know once to stop the model at time = 244 with evid = 33. Once that event is processed, we don’t need more records piling up at time = 244 with that evid.

To make sure we only get one record with evid = 33 at time = 244, mrgsolve keeps a log of modeled events and checks that log whenever a new event comes back from the model code. If there is already a record with time = 244 and evid = 33, it declines to schedule an additional / identical record in the record stack; only one is needed. Once mrgsolve starts working on a new individual, the event log is reset to allow events to get scheduled at any time for that individual.

Currently mrgsolve checks the following event attributes for uniqueness

  • time - event time
  • evid - event id
  • amt - dose amount
  • cmt - dose compartment

An exception to this check for uniqueness is when the now attribute is set on modeled events (Section 10.3); this most commonly happens with doses that the user wants to trigger immediately. Because these events happen “now”, we assume that you really want this event to happen; that is, we assume that some code is in place that has checked to make sure it is the right time to execute this event. A similar argument could be made for any dosing event, but for the time being, doses that are scheduled for the future (even doses that we want to happen at the current time but are not marked with the now attribute).

I think this is the behavior we want most of the time. In case you do want multiple doses into the same compartment with the same amount at the same time, there is a check_unique attribute that you can set to false. This will bypass the check of the event log and execute that event without checking for duplicates. The check_unique attribute is only needed for events scheduled for the future (not now). Using the check_unique attribute to bypass the check of the event log could theoretically help simulation speed when a large number of events are added to the log. I don’t expect a huge speed difference, but it might be worth a try.