The sequence and sleep statements

Sequential execution is where there is a sequence of statements A, B, C, etc., where A executes first in time, and only after A completes does B get to start, and so on. If control is sequential, then one can have “if” statements and loops, and variables that act like counters. One can say “x = x + 1” and know what it means, because it takes place at a point in time.

By contrast, in a PML model, the statements are not sequential, they are descriptive of the problem. They do not take action at a point in time. Rather they apply “all the time”. So in PML code, outside of sequence statements (and assignment statements), relative order of statements does not matter.

The sequence statement denotes a series of expressions to evaluate or statements to process at certain times in a time-based model. The incorporation of sequence blocks into PML allows handling of models like enterohepatic circulation (see “Modeling discontinuous events”) in a general way.

There can be multiple sequence statements in a model and they are executed as if they were run­ning in parallel. No assumption should be made about which sequence statement is processed first. For example, if two sequence blocks both initialize things, one cannot depend on which one does the initialization first. When a reset is encountered in the data, due to mapping a reset column, the sequence statement(s) are restarted.

Processing a block of expressions and statements in a sequence statement is started at the initial time in the model and continues until the end of the sequence block, or until a sleep statement is encountered. The sleep statement, with syntax is given by:

  sleep(number)

instructs the processing of the statements and expressions in the current sequence block to stop for the amount of time specified by the number argument. The number argument is a relative time, not an absolute time. It is important to use the sleep statement rather than tests against the time vari­able to ensure the stability of the algorithms. For example, write {sleep(10); A0=0} and not {if(t=10);A0=0}. The latter does not function as is intended. For a model to work, the sequence statement must be used any time a user would otherwise intend to write something like: if(t>=Tmax){do stuff}.

Initializing state variables: 

The sequence statement can be used to define initial conditions for ODEs. For example, one can say:

  sequence {
     Aa=some_expression
     A1=some_other_expression
  }

This sets the two state variables Aa and A1 to initial values, because the sequence block starts exe­cuting immediately when the subject begins.

Defining constants: 

If numeric constants are needed in the code, and there is a concern about possible performance of calculating them, a simple way to include them is the following:

  double(pi, e)
  sequence {
     pi=3.1415926535897932384626433832795
     e=2.7182818284590452353602874713527
  }

These are executed only once per subject, take essentially zero time to execute, and are far more precise than can possibly be required.

Do not be concerned about the performance of secondary parameter calculations, such as half-life, because those are only calculated once, after all model fitting is completed.

Take some action after some time has passed: 

For example, to put in an extra dose at three time units after the subject has begun, and then yet another three time units later, one could say:

  sequence {
     sleep(3)
     Aa=Aa+some_dose_amount
     sleep(3)
     Aa=Aa+some_dose_amount
  }

The sequence statement starts when the subject starts, but the first thing it does is hit the sleep(3) statement, which means “wake up again after three time units”.

When the three time units have passed, then it does the next thing, which is to add a dose to Aa. Notice that an integrator variable like Aa can only be modified at particular points in time, so it can only be modified inside a sequence statement.

Then it sleeps for three more time units, gives another dose, and does nothing more.

Do different things depending on different conditions: 

The following sequence statement begins with sleeping for three time units, then checks if the amount in compartment Aa is less than two. If the amount is less than two, then add a dose to com­partment Aa. Otherwise, do something else or do nothing, depending on code that would appear after the else line.

  sequence {
      sleep(3)
      if (Aa < 2){
          Aa=Aa+some_dose_amount
      } else {
      }
  }

Do things repeatedly: 

The following sequence statement says that, as long as t is less than 10, sleep three time units and then add a dose.

  sequence {
     while(t < 10){
        sleep(3)
        Aa=Aa+some_dose_amount
     }
  }

Alternatively, a variable can be declared that can be set to different values. Then a sequence state­ment can be used to repeat the sleep + dose cycle ten times, as shown below:

  real(i)
  sequence {
     i=1
     while(i <= 10){
        sleep(3)
        Aa=Aa+some_dose_amount
        i=i+1
     }
  }

The block inside a sequence statement can consist of multiple blocks controlled by logical switches. For instance, in the example above, the expressions and statements inside the sequence statement are contained within a while block which instructs the sequence to repeat itself through the entire time period that observations are made.

Time event switching in PML versus ASCII models

The while or if statements that were required to switch between time events in the ASCII model do not work as effectively as the sleep statement in the PML. Using the sequence statement in conjunction with the sleep statement is the best way to switch between time events in Phoenix text models.

If a user uses if statements to perform these tasks in the PML, the integrator can jump over the time point of interest. The Jacobian matrix it computes will be very inaccurate because the derivatives are different on each side of the discontinuity. The integrator goes back and forth trying to reduce the compounding errors that it sees, which can cause the integrator to miss the time point of interest.

The stability that the sequence statement offers allows models to be solved more quickly and accu­rately.

What the sequence statement with sleep statements does is stop the integrator at the appropriate times to make state changes, like RESET or DOSING, or set time discontinuous variables.

 


Last modified date:7/9/20
Certara USA, Inc.
Legal Notice | Contact Certara
© 2020 Certara USA, Inc. All rights reserved.