What-ifs and other schedules
A formula can read a schedule other than the one in the grid: the schedule as CPM would date it with something changed (a what-if), the baseline, or a named revision or scenario. You read any of them with IN: IN(BASELINE(), [Finish date]) is each task's baseline finish.
A what-if is a real CPM run, not an estimate. A delay doesn't travel through a schedule as simple arithmetic once calendars, constraints, progress and level-of-effort tasks are involved, so Owl doesn't add numbers up along paths. It dates a copy of the schedule with the project's own settings and calendars, and the formula reads the copy. A what-if that changes nothing reads exactly what the grid shows.
| You want to know | Read |
|---|---|
| What happens if something changes | A what-if: IN(WHATIF(…), …) |
| What the plan said when it was baselined | IN(BASELINE(), …) |
| What a named revision or a scenario says | IN(SCHEDULE("Rev 0 — submitted plan"), …) |
| What a field was at each past update | HISTORY(…): see Values over time |
Ask CPM: WHATIF and IN
WHATIF names the tasks to change, then each field and its new value. IN works out an expression in it:
Formula=IN(WHATIF(Task("BDC_001"), [Duration], [Duration] + 10d), [Finish date])
Each task's finish date if BDC_001 took ten more working days.
Compare outside IN to see the change:
=WORKDAYS([Finish date], IN(WHATIF(Task("BDC_001"), [Duration], [Duration] + 10d), [Finish date]))
How far each task would move.
Everything inside IN is read in the what-if, so a field there is the what-if's value, and names you gave outside with LET aren't visible inside it.
What a what-if can change
After the tasks, pairs of a field and its new value. The new value is worked out for each task being changed: in WHATIF(All, [Duration], [Duration] * 1.2), [Duration] is each task's own.
| Field | New value |
|---|---|
[Duration] | A duration. Before a task starts, its remaining work follows |
[Remaining duration] | A duration, for work in progress |
[Constraint] | A constraint, "Start on or after", or BLANK() for none |
[Constraint date] | A date, or BLANK() |
[Calendar] | One of the project's calendars, by name |
The tasks can be one task, Task("A1000"), or a set: All, FILTER(All, All[Discipline] = "Electrical"). Several groups of tasks and changes can follow each other; where two touch the same task and field, the later one wins.
=IN(WHATIF(FILTER(All, All[Discipline] = "Electrical"), [Remaining duration], [Remaining duration] * 1.2), Task("FINISH")[Finish date])
When the project finishes if electrical work is 20% slower.
A project setting is changed with SETTING and its value:
| Setting | Value |
|---|---|
"Data date" | A date |
"Out-of-sequence progress" | "Retained logic", "Progress override" or "Actual dates" |
"Critical path" | "Longest path" or "Total float" |
"Must finish by" | A date, or BLANK() |
=IN(WHATIF(SETTING("Must finish by"), DATE(2026, 1, 20)), [Total float])
Each task's float against a deadline: negative where the deadline can't be met.
Float to a milestone rather than to the project's end is FLOATTO, and it reads in a what-if like any other value:
Formula=IN(WHATIF(Task("BDC_001"), [Duration], [Duration] + 5d), FLOATTO(Task("MS_SUB")))
Each task's float to MS_SUB if BDC_001 took five more working days.
One what-if, or one per row
A what-if is the same for every row unless it names the row's own tasks or a value from the row. Then each row gets a what-if of its own, with its own change:
Formula=WORKDAYS(Project[Finish date], IN(WHATIF([Task], [Remaining duration], [Remaining duration] + 1h), Project[Finish date]))
For each task: how far the project finish moves if this task takes an hour longer. That is DCMA's critical path test.
| The column's table | The tasks a what-if per row can change |
|---|---|
| Task | [Task], [Driver], or one step away: Predecessors, Successors, their driving forms, a FILTER of those |
| WBS node | Tasks: every task in the node, changed together |
| Resource | Tasks: every task it's assigned to |
| Assignment | Task |
| Relationship | Predecessor or Successor |
A what-if per row only re-dates what its change reaches, so it is fast. To keep it so, the editor refuses the shapes that would date a whole schedule for every row: changing Upstream or All per row, reading All inside IN, a project setting that differs by row, and a what-if inside another. Each refusal says what to write instead.
Only the rows that take a branch are dated: IF([Critical], IN(WHATIF([Task], …), …)) runs one what-if per critical task.
The baseline
BASELINE() is the project's baseline as a schedule. Tasks line up by task code; a task that isn't in the baseline is blank there.
=WORKDAYS(IN(BASELINE(), [Finish date]), [Finish date])
How far each task has slipped against its baseline finish: positive is late.
Anything can be read in the baseline, formula columns included: IN(BASELINE(), [Budget]) is your Budget column worked out on the baseline, and IN(BASELINE(), COUNT(Successors)) reads the baseline's own logic.
=LET(m, FILTER(Tasks, Tasks[Type] = "Milestone"), COUNT(FILTER(m, m[Finish date] > IN(BASELINE(), m[Finish date]))))
A measure: how many milestones finish later than baselined. A set named outside IN is read in the baseline member by member.
With no baseline set, IN(BASELINE(), …) is blank on every row, with the reason.
Revisions and scenarios
SCHEDULE("name") is another schedule of the project by its name: a named revision (from the Revisions tab) or a scenario. It reads exactly as the baseline does:
=WORKDAYS([Finish date], IN(SCHEDULE("Rev 0 — submitted plan"), [Finish date]))
How far each task has moved since Rev 0.
The name is checked when you save. A revision removed later makes the formula fail its check again, saying so.
| A what-if | The baseline, a revision, a scenario | |
|---|---|---|
| Is | This schedule, re-dated now with a few inputs changed | Another stored schedule, read as it is |
| Can differ by | Durations, constraints, calendars, settings | Anything: tasks added and removed, logic changed |
Formula columns inside IN | Not read: a what-if re-dates the schedule, not its columns | Read, worked out on that schedule |
Common questions
Why can't I just add the slip along the path?
You can, with a column that reads itself, and it matches CPM only while every task on the path shares a calendar and nothing else intervenes. A what-if asks CPM itself, so it is right with mixed calendars, constraints and progress.
Can I change [Finish date] in a what-if?
No: dates are what CPM works out. Change what CPM reads instead, for example a constraint: “[Finish date] can't be changed in a what-if. These can: Duration, Remaining duration, Constraint, Constraint date, Calendar”.
Can I put one what-if inside another?
No. Make both side by side and compare them outside:
Formula=LET(a, WHATIF(Task("BDC_001"), [Duration], [Duration] + 5d), b, WHATIF(Task("E100"), [Duration], [Duration] + 5d), WORKDAYS(IN(a, [Finish date]), IN(b, [Finish date])))
Which of two delays moves each task further.
Does a what-if change my schedule?
Never. It is a copy that lives only while the formula is worked out. To try a change for real, make a scenario.
