Tech Note

Title:               Task Validation
Date:              March 3, 2003
Written by:     Peter C. Bosch
Number:        TN-003
Version:         0.1

Task validation, invalidation and revalidation are key to the functioning, of a model. This Tech Note describes the process of Validation, as well as the sequence of calls made by a SOMTask, and below that, the SOMOperation, in the process of validation. This information will help the user (a) understand how a model validates and invalidates itself, and (b) understand the call sequence each task utilizes in its own validation in order to enable hooking user code into this sequence.Note that for a SOMOperation, the sequence of calls is identical.

Recall that tasks are organized as a sequence, proceeding from a set of start tasks (those having no predecessors) to a set of finish tasks (those having no successors) and having, in some cases, child tasks. See Tech Note TN-001 for more details.

Task Validity

A task’s validity is an aggregation of three conditions. In order to be ‘valid’, it must be ‘self-valid’, which implies model-domain consistency, it must have no invalid predecessors, and it must have no invalid children. A task reports its validity to the model through an error submitted when the task becomes self-invalid and removed when the task becomes self-valid. A task’s having invalid upstream tasks, or invalid child tasks implies that some other task’s ‘self-valid’ state is false, so only tasks with a ‘self-valid’ state that is false, actually submit errors to the model. The model will not permit entry to the “Running” state unless it is fully valid.

Parent Tracking of Child Validity State

A task maintains a list of its child tasks, as well as a list of those children that are invalid. When a task gets a new member of its list of invalid children, it checks to see that task is its first invalid child. If it is, it makes itself invalid by reason of invalid children, and adds itself to its parent's list of invalid children.

When a task has an invalid child removed, it checks to see if that task was the last invalid child task. If it has no more invalid children, it removes the "Invalid Children" flag from its invalid state. If it is now valid (i.e. if it is self-valid and has no invalid predecessor), it removes itself from its parent task's invalid children list.

Single Revalidation after Multiple Invalidations

When a task is invalidated (invalidating a chain of downstream tasks,) and then another is invalidated (invalidating another chain of downstream tasks), a given downstream task may receive invalidation notifications from both upstream tasks. When subsequently being revalidated, it is important that downstream tasks receive only one request to revalidate. In support of this requirement, the Revalidate mechanism actually rides on the total task graph’s Execution path as an alternate mode of execution. The task graph is evaluated as though it were simulating, but in place of the Simulate method, we execute the Validate method. The task graph execution mechanism ensures that each task executes once, and only after all predecessors have been executed. If the self-valid state of the task is false when it is time for that task to validate, then the validation logic is called. If the task is self-valid, then the validate logic is skipped, and the downstream tasks are informed of its completion so that they can validate when the graph logic determines that it is appropriate to do so.

Validation logic involves reading the state of the model from the task’s PreExecution Snapshot, computing a new output state, and then snapshotting that state into the task’s PostExecution Snapshot. If this computation results in a changed (or new) PostExecution Snapshot, then the next task in the appropriate UnitTaskList is marked as self-invalid, forcing it to read the new snapshot, and recompute its own (new) output state. Changes in export conditions other than those reflected in the PostExecution state (e.g. materials exported to other units’ tasks as in transfer operations) must explicitly set their receiving tasks’ validity.

Validation Sequence

Figure 1, below, depicts the sequence of operations involved in a task’s self-validation. It starts with (1), in which a preceding task notifies our task’s preVertex that it has completed. Our task’s prevertex determines that all of the edges that precede it have executed, and therefore it is time for our task to run. The prevertex signifies this by (2), calling it’s edge’s PreVertexSatisfied(…) method. From PreVertexSatisfied(…), if there is an ExecutionDelegate specified for the task, that delegate is called. In our framework, this delegate is (4), OnEdgeExecution. (If not, the task signals its completion). The OnEdgeExecution method first (5) fires the TaskStartingEvent, (one effect of which is to snapshot the preexecution state) and then (6) calls DoTask(…).  In DoTask(…), the task (7) registers for a callback after its desired predelay, when the executive (8) calls into _AfterPreDelay(…) or if the desired predelay is 0, (8) the task calls _AfterPreDelay(…) directly.

_AfterPreDelay(…) either executes validation or simulation logic, depending on the state of the model. If the model is Validating, and the task is self-invalid, then (9) the ValidateEvent is fired, and (10) the DoValidate(…) method is called. User-specific logic that executes only on validation happens in these two calls.

Once validation has completed (successfully or not), (11) the task either registers for a callback on _AfterPostDelay(…), or, if the desired delay is zero, calls it directly. Whether by the executive in serving the requested callback or by the _AfterPreDelay(…) method, (12) _AfterPostDelay(…) is called. _AfterPostDelay (13) signals task completion by calling SignalTaskCompletion(…).  This method first (14) fires the TaskCompletedEvent, and then (15) calls the EdgeExecutionCompletionSignaler, which is a delegate that (16) informs its postVertex that the edge has completed.


Task Sequence Diagram for validation.

Figure 1 Task Sequence  Diagram – Validation

SOMOperation Validation Sequence

A SOMOperation is a task construct that services a set of possible activities, any or all of which can happen in a given MAI task execution. In both DoValidate and the corresponding DoSimulate, we call DoOperations to process the following sequence of activities.

In DoOperations(…) we first check to see if we are to acquire resources. If we are, then the resources[1] are requested. Once they are obtained, we proceed to subsequent steps.

After obtaining any desired resources, we check to see if we are expecting transfer-in operations by checking for input ports that are connected to other entities via a connection object. If such connections exist, then we check to see if the incoming material is available on the relevant port or its connection in the form of a transfer object, which encapsulates a material to transfer and a duration of transfer. If no transfer is available, then we throw an exception, since the incoming material must be present, and a failure to have the material present is likely a design or construction error. Assuming that transfers are to take place, and are available, the task suspends reactions and processes all charge and transfer-in operations (which are identical in all respects except the material source – in a charge, the material comes from a ChargeSource, in a TransferIn, the material comes from another operation. Then we resume reaction processing, resulting in temperature & mass-balance calculations being performed for the new mixture. Then, we calculate resultant temperatures from whatever temperature control operations need to be performed, perform separation and staging on output ports of discharge and transfer-out mixtures, then calculate duration of the overall operation, and delay for the remainder of the operation. Finally, we utilize post-delay to impose a sleep of the already calculated remainder of the task’s duration.

There are events preceding each of these operations that may be hooked to provide a trigger to use to explore task and unit state and set values that are to be used in subsequent operations. For example, the PrepareToProcessOutflowsEvent may be hooked with a handler that inspects the contents of the mixture (see below) and sets the materials to be transferred out. The ProcessOutflows method then would use those provided MaterialTransferSpecs to perform the tailored TransferOut operation.

First, one would hook into the PrepareToProcessOutflows method as follows:

// We are going to dynamically determine the output of this task.

// The output setting will be determined during the

// 'PreparingToProcessOutflowsEvent' event. The output port will be keyed

// to the string "Out_0", instead of a guid, for simplicity.

SOMOperation transferLiquid_1 = mb.CreateOperation("Transfer Liquid 1");


new TaskEvent(DetermineTL1Effluent);

// This was the hard-coded output, but we'll determine it on the fly.

transferLiquid_1.AddResourceRelease("Equipment1"); // We're done with the vessel


And then in the event handler:

// This is called before effluents are processed.

public void DetermineTL1Effluent(IDictionary graphContext, Task theTask){


    // Note - following is good if doing only one or the other. If acquiring

    // both equipment & mixture, it's slightly faster to do so explicitly

    // from the UnitContext, eliminating one hashtable lookup.


    Mixture mixture = ((SOMOperation)theTask).GetCurrentMixture(graphContext);


    // Since the transferOutSpecs are not vacated during transferOut,

    // For the second and subsequent time, we will need to clear them out

    // so that we can correctly re-add them in.   


    foreach ( Substance substance in mixture.Constituents ) {





Do Operations Processing

Figure 2 SOMOperation's DoOperations Processing

[1] Resource Acquisition is currently one-at-a-time. It is high-priority to achieve multiple simultaneous & interdependent acquisitions, and this is a near-term goal.