Chapter 12
CLASS SIMULATION

The system class "simulation" may be considered an "application package" oriented towards simulation problems. It has the class "simset" as prefix, and set-handling facilities are thus immediately available.

The concepts defined in "simulation" are explained with respect to a prefixed block, whose prefix part is an instance of the body of "simulation" or of a subclass. The prefixed block instance acts as the head of a quasi-parallel system which may represent a "discrete event" simulation model.

 simset class simulation;
        begin  ref (head) SQS;
      link class EVENT_NOTICE (EVTIME, PROC);
                     long real EVTIME; ref (process) PROC;
           begin
              ref (EVENT_NOTICE) procedure suc;
                 suc:- if SUC is EVENT_NOTICE then SUC else none;

              ref (EVENT_NOTICE) procedure pred;   pred:- PRED;

              procedure RANK_IN_SQS (afore); Boolean afore;
              begin ref (EVENT_NOTICE) evt;
                 evt:- SQS.last;
                 while evt.EVTIME > EVTIME do    evt :- evt.pred;
                 if afore then
                    while evt.EVTIME = EVTIME do evt :- evt.pred;
                 follow(evt)
              end RANK_IN_SQS;
           end EVENT_NOTICE;

           ref (MAIN_PROGRAM) main;

           ref (EVENT_NOTICE) procedure FIRSTEV;  FIRSTEV :- SQS.first;
           ref (process) procedure current; current :- FIRSTEV.PROC;
           long real procedure time; time := FIRSTEV.EVTIME;

      link class process;  .................... 12.1;
           procedure ACTIVAT .................. 12.3;
           procedure hold ..................... 12.4;
           procedure passivate ................ 12.4;
           procedure wait ..................... 12.4;
           procedure cancel ................... 12.4;
   process class MAIN_PROGRAM ................. 12.5;
           procedure accum .................... 12.6;

           SQS        :- new head;
           main       :- new MAIN_PROGRAM;
           main.EVENT :- new EVENT_NOTICE(0,main);
           main.EVENT.into(SQS)
        end simulation;

When used as a prefix to a block or a class, "simulation" introduces simulation-oriented features through the class "process" and associated procedures.

The variable SQS refers to a set which is called the "sequencing set", and serves to represent the system time axis. The members of the sequencing set are event notices ranked according to increasing value of the attribute "EVTIME". An event notice refers through its attribute PROC to a "process" object and represents an event which is the next active phase of that object, scheduled to take place at system time EVTIME. There may be at most one event notice referencing any given process object.

The event notice at the lower end of the sequencing set refers to the currently active process object. The object can be referenced through the procedure "current". The value of EVTIME for this event notice is identified as the current value of system time. It may be accessed through the procedure "time".

Note: Since the statements and procedures introduced by "simulation" make implicit use of the sequencing procedures (detach, call and resume) explicit sequencing by these procedures should be done with care.

Class "process"

 link class process;
      begin ref (EVENT_NOTICE) EVENT;
         Boolean TERMINATED;
         Boolean procedure idle;              idle := EVENT==none;
         Boolean procedure terminated;  terminated := TERMINATED;

         long real procedure evtime;
            if idle then  error("..." ! No Evtime for idle process)
            else evtime := EVENT.EVTIME;

         ref (process) procedure nextev;
            nextev :- if  idle or else EVENT.suc == none then none
                     else EVENT.suc.PROC;

         detach;
         inner;
         TERMINATED:= true;
         passivate;
         error("..." ! Terminated process;)
      end process;

An object of a class prefixed by "process" is called a process object. A process object has the properties of "link" and, in addition, the capability to be represented in the sequencing set and to be manipulated by certain sequencing statements which may modify its "process state". The possible process states are: active, suspended, passive and terminated.

When a process object is generated it immediately becomes detached and its reactivation point positioned in front of the first statement of its user- defined operation rule. The process object remains detached throughout its dynamic scope.

The procedure "idle" has the value true if the process object is not currently represented in the sequencing set. It is said to be in the passive or terminated state depending on the value of the procedure "terminated". An idle process object is passive if its reactivation point is at a user-defined prefix level. If and when the PSC passes through the final end of the user-defined part of the body, it proceeds to the final operations at the prefix level of the class "process", and the value of the procedure "terminated" becomes true. (Although the process state "terminated" is not strictly equivalent to the corresponding basic concept defined in chapter 7, an implementation may treat a terminated process object as terminated in the strict sense). A process object currently represented in the sequencing set is said to be "suspended", unless it is represented by the event notice at the lower end of the sequencing set. In the latter case it is active. A suspended process is scheduled to become active at the system time indicated by the attribute EVTIME of its event notice. This time value may be accessed through the procedure "evtime". The procedure "nextev" references the process object, if any, represented by the next event notice in the sequencing set.

Activation statement

     activation-statement
         =  activation-clause  [ scheduling-clause ]

     activation-clause
         =  activator  object-expression

     activator
         =  activate
         |  reactivate

     scheduling-clause
         =  timing-clause
         |  ( before  |  after )  object-expression

     timing-clause
         =  simple-timing-clause  [ prior ]

     simple-timing-clause
         =  ( at  |  delay )  arithmetic-expression

An activation statement is only valid within an object of a class included in "simulation", or within a prefixed block whose prefix part is such an object.

The effect of an activation statement is defined as being that of a call on the sequencing procedure ACTIVAT local to "simulation", see 12.3.

The actual parameter list is determined from the form of the activation statement, by the following rules:

  1. The actual parameter corresponding to REAC is true if the activator is reactivate, false otherwise.
  2. The actual parameter corresponding to X is the object expression of the activation clause.
  3. The actual parameter corresponding to T is the arithmetic expression of the simple timing clause if present, otherwise it is zero.
  4. The actual parameter corresponding to PRIO is true if prior is in the timing clause, false if it is not used or there is no timing clause.
  5. The actual parameter corresponding to Y is the object expression of the scheduling clause if present, otherwise it is none.
  6. The actual parameter corresponding to CODE is defined from the scheduling clause as follows:
                 scheduling clause             actual text parameter
               -------------------------------------------------------
                 - absent -                    "direct"
                 at arithmetic expression      at
                 delay arithmetic expression   delay
                 before object expression      before
                 after object expression       after

Procedure ACTIVAT

     procedure ACTIVAT(REAC, X, CODE, T, Y, PRIO);
        value CODE; ref (process) X, Y; Boolean REAC, PRIO;
         text CODE; long real T;
     inspect X do if not TERMINATED then
     begin  ref (process) z; ref (EVENT_NOTICE) EV;
         if REAC then EV:- EVENT
         else if EVENT =/= none then goto exit;
         z:- current;
         if      CODE = "direct" then
 direct:     begin EVENT:- new EVENT_NOTICE(time,X);
                   EVENT.precede(FIRSTEV)                        end direct
         else if CODE = delay then
             begin T:= T + time;
                   goto at_                                      end delay
         else if CODE = at then
 at_:        begin if T < time then T:= time;
                  if T = time and PRIO then goto direct;
                     EVENT:- new EVENT_NOTICE(T, X);
                     EVENT.RANK_IN_SQS(PRIO)                     end at

         else if Y == none or else Y.EVENT == none
         then EVENT :- none
         else begin
            if X == Y then goto exit;
                 comment reactivate X before/after X;
            EVENT:- new EVENT_NOTICE(Y.EVENT.EVTIME, X);
            if CODE = before then EVENT.precede(Y.EVENT)
                               else EVENT.follow(Y.EVENT)
         end before or after;
         if EV =/= none
         then begin EV.out; if SQS.empty then error("...") end;
         if z =/= current then resume(current);
   exit:
     end ACTIVAT;

The procedure ACTIVAT represents an activation statement, as described in 12.2. The effects of a call on the procedure are described in terms of the corresponding activation statement. The purpose of an activation statement is to schedule an active phase of a process object.

Let X be the value of the object expression of the activation clause. If the activator is activate the statement has no effect (beyond that of evaluating its constituent expressions) unless the X is a passive process object. If the activator is reactivate and X is a suspended or active process object, the corresponding event notice is deleted (after the subsequent scheduling operation) and, in the latter case, the current active phase is terminated. The statement otherwise operates as an activate statement.

The scheduling takes place by generating an event notice for X and inserting it into the sequencing set. The type of scheduling is determined by the scheduling clause.

An empty scheduling clause indicates direct activation, whereby an active phase of X is initiated immediately. The event notice is inserted in front of the one currently at the lower end of the sequencing set and X becomes active.The system time remains unchanged. The formerly active process object becomes suspended.

A timing clause may be used to specify the system time of the scheduled active phase. The clause "delay T", where T is an arithmetic expression, is equivalent to "at time + T". The event notice is inserted into the sequencing set using the specified system time as the ranking criterion. It is normally inserted after any event notice with the same system time. The symbol "prior" may, however, be used to specify insertion in front of any event notice with the same system time.

Let Y be a reference to an active or suspended process object. Then the clause "before Y" or "after Y" may be used to insert the event notice in a position defined relation to (before or after) the event notice of Y. The generated event notice is given the same system time as that of Y. If Y is not an active or suspended process object, no scheduling takes place.

Example

The statements

                 activate X
                 activate X before current
                 activate X delay 0 prior
                 activate X at time prior

are equivalent. They all specify direct activation.

The statement

                 reactivate current delay T

is equivalent to "hold(T)".

Sequencing procedures

 HOLD      procedure hold(T); long real T;
           inspect FIRSTEV do begin
              if T > 0 then EVTIME:= EVTIME + T;
              if suc =/= none and then suc.EVTIME <= EVTIME
              then begin out; RANK_IN_SQS(false);
                 resume(current)
              end if
           end hold;


 PASSIVATE procedure passivate;
           begin
              inspect current do begin  EVENT.out; EVENT :- none  end;
              if SQS.empty then error("...") else resume(current)
           end passivate;


 WAIT      procedure wait(S); ref (head) S;
           begin current.into(S); passivate end wait;


 CANCEL    procedure cancel(X); ref (process) X;
           if X == current then passivate
           else inspect X do
              if EVENT =/= none
              then begin  EVENT.out;  EVENT :- none
           end cancel;

The sequencing procedures serve to organize the quasi-parallel operation of process objects in a simulation model. Explicit use of the basic sequencing facilities (call, detach, resume) should be made only after thorough consideration of its effects.

The statement "hold(T)", where T is a long real number greater than or equal to zero, halts the active phase of the currently active process object, and schedules its next active phase at the system time "time + T". The statement thus represents an inactive period of duration T. During the inactive period the reactivation point is positioned within the "hold" statement. The process object becomes suspended.

The statement "passivate" stops the active phase of the currently active process object and deletes its event notice. The process object becomes passive. Its next active phase must be scheduled from outside the process object. The statement thus represents an inactive period of indefinite duration. The reactivation point of the process object is positioned within the "passivate" statement.

The procedure "wait" includes the currently active process object in a referenced set, and then calls the procedure "passivate".

The statement "cancel(X)", where X is a reference to a process object, deletes the corresponding event notice, if any. If the process object is currently active or suspended, it becomes passive. Otherwise, the statement has no effect. The statement "cancel(current)" is equivalent to "passivate".

The main (simulation) program

     process class MAIN_PROGRAM;
             begin
                while true do detach
             end MAIN PROGRAM;

It is desirable that the main component of a simulation model, i.e. the "simulation" block instance, should respond to the sequencing procedures of 12.4 as if it were itself a process object. This is accomplished by having a process object of the class MAIN_PROGRAM as a permanent component of the quasi-parallel system.

The process object represents the main component with respect to the sequencing procedures. Whenever it becomes operative, the PSC (and OSC) immediately enter the main component as a result of the "detach" statement (cf. 7.3.1). The procedure "current" references this process object whenever the main component is active.

A simulation model is initialized by generating the MAIN_PROGRAM object and scheduling an active phase for it at system time zero. Then the PSC proceeds to the first user-defined statement of the "simulation" block.

The procedure "accum"

 ACCUM     procedure accum (a,b,c,d);  name a,b,c;  long real a,b,c,d;
           begin
              a:= a+c * (time-b);  b:= time;  c:= c + d
           end accum;

A statement of the form "accum (A,B,C,D)" may be used to accumulate the "system time integral" of the variable C, interpreted as a step function of system time. The integral is accumulated in the variable A. The variable B contains the system time at which the variables were last updated. The value of D is the current increment of the step function.