Scripting

Prev Next

Scripting

In some situations, the standard Risk Model is just not enough to model a scenario. That’s when the Safran Risk scripting feature might come in handy. It allows you to change the behavior of the standard model. You can for example cancel an activity if the project is running late or you can make sure that certain risks can never occur in the same iteration.

The scripting syntax is javascript. Note that variable names in javascript are case-sensitive.

The scripting access points

You can affect the risk analysis at three points during the analysis. The illustration below shows these points in the context of one iteration.

Safran Risk Scripting Diagram

In order to create good models with scripting it’s important to understand what happens during the risk analysis. Here’s an explanation of each phase:

Random Values Generated

During this part random values are generated for all the risks and uncertainties. This is where the risk is set to occur or not and where the impacts get their values.

Activity Durations Calculated

This is where the new activity durations are calculated. The durations will be based on the state of the risk (calculated in previous step) and the mappings between the risks and activities.

Schedule Analyzed

This is where all the dates in the schedule are calculated using the CPM method.

Costs Analyzed

This is where all the costs in the cost tab are calculated. This calculation will be based on the schedule and the risks.

When you chose which script insert point you’re going to use you need to consider what’s happening in these phases. If you for example want to do something based on the finish date of an activity, this script need to run after the schedule analyzed phase since before that the dates haven’t been calculated.

Note that if you create an “After Schedule analysis” script the dates will be recalculated after the script. This can also be done in the script using the Schedule.Recalculate method.

The object model

Safran Risk exposes certain object that you can access.


Schedule

The schedule can be used to get hold of parts of the schedule.

Properties

Name Description
Activities Gets a list of all activities

Methods

Name Parameters Returns Description
Activity(string) Activity Id Activity
ReCalculate() Recalculates the early dates in the schedule.

Model

The model object can be used to get hold of parts of the risk model.

Properties

Name Description
Costs Gets a list of all cost elements
Risks Gets a list of all risks

Methods

Name Parameters Returns
Cost(string) Cost Element Id Cost Element
RiskEvent(string, ElementType, string) Risk Id, ElementType(Optional. Can be either ElementType.Activity or ElementType.Cost), Element Id (Optional) Risk Event
MappedActivities(string) Risk Id List of activities mapped to risk

Activity

Properties

Name Description
ActualStart Gets the Actual Start
ActualFinish Gets the Actual Finish
Description Gets the Description
Duration Gets or Sets Duration
EarlyStart Gets Early Start
EarlyFinish Gets Early Finish
Existence Get or Sets Existence
Id Gets the Id
MustStartOn Gets or Sets the Must Start On Constraint.
Type Gets the Type

Methods

Name Parameters Returns Description
TurnOffBranch(Activity) Activity Turns off all the activities in the branch, starting with the passed-in activity. Using this, you can implement conditional branching.

Cost Element

Properties

Name Description
Input Field Id (One Property per Input field) Gets the Input Field of the Cost Element. The id is the one defined in the Input field dialog. In order to get the value of an Input field with id Base you would write: Model.Cost[“CostId”].Base.Value
Description Gets the Description
Finish Gets the calculated Finish Date
Id Gets the Id
IterationValue Gets or Sets the Iteration value
Name Gets the Name
Start Get the calculated Start Date

Cost Element Input Field

Properties

Name Description
Value Gets the value of the input field.

File

A file object can be created to write information to a file. It can be created outside the script points and be used everywhere.

Methods

Name Parameters Returns
File(string) (Constructor) Path A file object
WriteLine(string) Text to write to file Void

Risk

Properties

Name Description
Id Gets the Id
Description Gets the Description

Risk Event

A risk event is different from a risk! A risk can generate more than one Risk Event per iteration if it is mapped to more than one activity or cost element and it impacts them differently. This is the case when “Impact independently” is set to true or when the impact is overridden at the cost or activity mapping.

Properties

Name Description
Occurred Gets or Sets whether the risk occurred
ScheduleImpact Gets or Sets the Schedule Impact
CostImpact Gets or Sets the Cost Impact

Examples

One risk will not occur if another occurs

This example shows how you can change the state of a risk during the risk analysis.

function BeforeScheduleAnalysis()
{
    if(Model.RiskEvent("Risk-001").Occured && Model.RiskEvent("Risk-002").Occured)
    {
        // Risk 2 can't occur if risk 1 occurs
        Model.RiskEvent("Risk-002").Occured = false;
    }
}

Do something on activities of a specific type

This example shows how you can get the type of the activity.

function BeforeScheduleAnalysis()
{
    var activities = Schedule.Activities;
    var nbrOfActivities = activities.Count;

    for (var i = 0; i < nbrOfActivities; i++)
    {
         var a = activities[i];
         if(a.Type == ActivityType.Hammock)
         {
             logFile.WriteLine(a.Id + " is of type: Hammock");
         }
        else
        {
             logFile.WriteLine(a.Type.oString());
        }
     }
}

Change the duration of activity based on a finish date and update successors by using ReCalculate

This example shows how you can change the duration of an activity based on the scheduled date of another activity. It is important to note that the successors of the activity with the updated duration won’t be recalculated automatically if you do the change after the “Schedule Analyzed” stage (see picture at the start of this chapter). If you use the step through functionality, it looks like the schedule is updated automatically, but for this to work in the scripting ReCalculate must be called.

function AfterScheduleAnalysis()
{
    if(Schedule.Activity("A1").EarlyFinish > new Date("2020-08-15"))
    {
        Schedule.Activity("A2").Duration = 50;
        // A2 has some successors, which dates won't by updated unless we re-calculate them.
        Schedule.ReCalculate();
    }
}

Logging activities that end after a specific date

This example shows how you can write the names of activities ending after a certain date to a log each iteration.

var logFile = new File("c:\\Temp\\LateLog.txt");
var iter = 0;

function AfterScheduleAnalysis()
{
    iter++;
    logFile.WriteLine("Iteration " + iter);
    
    var activities = Schedule.Activities;
    var nbrOfActivities = activities.Count;

    for (var i = 0; i < nbrOfActivities; i++)
    {
        activity = activities[i];
        if(activity.EarlyFinish > new Date("2015-9-1"))
        {
            logFile.WriteLine(activity.Id + " - " + activity.Description + " finishes on : " + activity.EarlyFinish.toLocaleDateString());
        }
    }
}

Setting the existence of activities based on the outcome of a risk

In this example we want to use one out of three activities in each iteration. We’re doing that by creating a discrete risk that has the outcomes one, two or three. It looks like this:

Schedule impact analysis showing probabilities and values based on existence.

We can then use the existence property on the activities and turn them on off during the analysis. This method can be used to do conditional branching. Instead of the risks outcome the condition could for example be the finish date of some activity.

function BeforeScheduleAnalysis()
{
    Schedule.Activity("B1").Existence  = false;
    Schedule.Activity("B2").Existence  = false;
    Schedule.Activity("B3").Existence  = false;
    
    var path = Model.RiskEvent("D1").ScheduleImpact;
    
    switch(path)
    {
        case 1:
            Schedule.Activity("B1").Existence  = true;
            break;
        case 2:
            Schedule.Activity("B2").Existence  = true;
            break;
        case 3:
            Schedule.Activity("B3").Existence  = true;
            break;
    }
}

Outsourcing an activity when the plan is delayed

This example shows how you can do something special when a certain activity is delayed. It also shows how you can use file to output information. This can for example be helpful when debugging a script. Note that you need the double backslash for the file path.

The dates in the script are javascript dates. Please see

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

For more info on different ways to create and manipulate them.

var logFile = new File("c:\\Temp\\ScriptLog.txt");
var iter = 0;
var cutOff = new Date("2019-7-1");
var outsource = false;

function AfterScheduleAnalysis()
{
    iter++;

    if(Schedule.Activity("00010").EarlyFinish > cutOff)
    {
        // Half Duration
        var halfDur =  Schedule.Activity("00010").Duration / 2;
        Schedule.Activity("00010").Duration = Math.round(halfDur); // Need to use round here since duration should be an integer.
        outsource = true;
    }
    else
    {
        outsource = false;
    }
}

function AfterCostAnalysis()
{
    // If we're outsourcing then we have an additional cost!
    if(outsource)
    {
        // Outsource
        Model.Cost("Out1").IterationValue = 10000;
        
        logFile.WriteLine("Outsourced in iteration: " + iter);
    }
}