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.
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:
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);
}
}