[ main ]
double mt = self.mtime(14.12);
if(TIME >= mt) {
// do something
}
11 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 as separate rows 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.
11.1 evtools
plugin
evtools
is a plugin that runs on top of the more basic functionality described here. This API can make working with modeled events, including doses, much easier to implement. For example, the plugin allows you to
- Execute single bolus or infusion doses
- Replace the amount in a given compartment
- Work with event objects in C++, including setting a new time for when the event should happen
- Execute a regular event regiment
See Section 10.4 for details on the evtools
functionality.
11.2 Note about when events are processed
It’s important to note that events are currently only processed after $ERROR
(or $TABLE
) is called. For this reason, it’s very likely that you’ll want to write code for implementing model events in $TABLE
, not $PK
(or $MAIN
).
11.3 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.
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) {
= self.mtime(TIME + 8.2);
mt }
if(TIME >= mt) {
// do something
}
11.4 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
.mevent(TIME + 8.2, 33);
self
if(EVID==33) {
// do something
}
This is similar in functionality to self.mevent()
.
11.5 Modeled doses
Note: This user guide section provides some low-level information on working with event objects to issue doses from inside your model code. The evtools
plugin described in Section 10.4 provides a higher-level interface to these objects and should be used whenever possible.
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 $EVENT
or $ERROR
.
11.5.1 Create an evdata object
Once you know when you want the dose, create an evdata
object.
::evdata ev(14.2, 1); mrg
This will create (construct) an object called ev
with class evdata
. The constructor takes two arguments:
- the
TIME
the event should happen - 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.
11.5.2 Event object public members
Once the object is created, you can modify or access 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 infuseamt
(double
)ii
: the dosing interval (double
)addl
: additional doses to give (int
)ss
: steady state flag (int
)now
: should the dose be given immediately? (bool
)check_unique
: should the event log be checked for identical events? see Section 11.6 (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)
.amt = 100;
ev.cmt = 2;
ev.now = true; ev
A similar syntax is used to access the public members in these objects
if(ev.amt > 50) {
// Do something
}
The evtools
plugin described in Section 10.4 provides a higher-level interface to these objects. We recommend using the evtools
interface whenever possible.
11.5.3 Push the evdata object back to mrgsolve
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
.mevector.push_back(ev); self
Starting with mrgsolve 1.4.1, you can also push the event object back via the push
member
.push(ev); self
Again, this sequence should get called in either $EVENT
or $ERROR
. When that code block finishes running (for the current record), mrgsolve
will find the event record and add that event to the simulation sequence.
11.5.4 evtools
plugin
evtools
is a plugin for executing modeled doses, running on top of the machinery here. The goal of the plugin is to create simplified workflows for executing doses or other discontinuities from inside your model. See Section 10.4 for details on how to use this plugin.
11.6 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
$PKdouble 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 timeevid
- event idamt
- dose amountcmt
- dose compartment
An exception to this check for uniqueness is when the now
attribute is set on modeled events (Section 11.5); 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.