The schedule in the figure above was leveled in Project using the standard leveling order. Even though it appears to be discontinuous, the critical path is correct. It takes into account the leveling delay (lag) between tasks 4 and 5, but, what is causing that leveling delay?

The start of task 2 depends on the availability of resources from the completion of task 4, as well as task 1. The start of task 5 depends on the availability of resources from the completion of task 3, as well as task 4. The resource constraints between tasks 4 and 2 and between tasks 3 and 5 drive the overall schedule length. When these resource constraints are considered, along with the logical constraints, a resource critical path emerges involving tasks 4, 2, 3, and 5, in that order.

Further, delay of tasks 2 or 3 would extend the overall schedule length. Hence, these tasks do not have float (slack). The erroneous float on task 2 and 3 is known as phantom float, an anomalous by-product of resource-constrained scheduling.

In this article, I will describe a process which eliminates phantom float and identifies resource critical paths that drive schedule duration. The process involves four steps:

- Generate a resource-leveled schedule.
- Generate a set of resource paths that describe the resource flow in the leveled schedule.
- Extract a set of resource constraints (task dependencies) from the set of resource paths.
- Using the critical path method, compute the schedule using the union of the sets of logical and resource constraints.

The result is a resource leveled schedule with float correctly calculated and resource critical paths identified. Further, there is no need for the insertion of Leveling Delay. This eliminates the discontinuity in the critical path noted above.

Here are two examples.

### First Example

The schedule in Figure 1 is a resource-leveled schedule. In a prior MPUG article, Scheduling Schemes and Heuristics for Leveling, this leveling was derived manually using a parallel scheduling scheme and the minimum slack leveling heuristic. As noted above, it can be generated in Project using the standard leveling order.

In this simple example, it is possible to identify manually the sets of resource paths that describe resource flow in the leveled schedule. There are two units of one resource type. One resource could be assigned, in order, to tasks 1, 2, and 5, while the second could be assigned, in order, to tasks 4, 2, 3, and 5. The set of resource paths would be {(1,2,5), (4,2,3,5)}. An alternative set of resource paths is {(1,2,3,5), (4,2,5)}. In this particular example, it does not matter which set we use. The set of resource constraints derived from either set of resource paths is {(1,2), (2,3), (2,5), (3,5), (4,2)}. The set of logical constraints is {(1,2), (2,3), (4,5)}. The schedule in Figure 2 is the result of the union of the sets of logical and resource constraints. See the three additional resource constraints added in the Successors field.

The schedule length is driven by resource critical path (4,2,3,5). Task 1 has one day of float. The phantom float is eliminated.

### Second Example

The schedule in Figure 3 is the optimal leveling of the schedule used above. It can be generated in Project using the ID only leveling option. In the article mentioned above, this leveling was manually derived using a serial scheduling scheme and the task ID leveling heuristic. It exhibits the discontinuous critical path and phantom float issues similar to those found in the first example.

As in the first example, it is possible to identify manually the sets of resource paths that describe resource flow in the leveled schedule. One resource could be assigned, in order, to tasks 1, 2, 3, and 5, while the second could be assigned, in order, to tasks 2, 4, and 5. The set of resource paths would be {(1,2,3,5), (2,4,5)}. An alternative set of resource paths is {(1,2,4,5), (2,3,5)}. Again, in this particular example, it does not matter which set we use. The set of resource constraints derived from either set of resource paths is {(1,2), (2,3), (2,4), (3,5), (4,5)}. The set of logical constraints remains {(1,2), (2,3), (4,5)}. The schedule in Figure 4 is the result of the union of the sets of logical and resource constraints. See the two additional resource constraints added in the Successors field.

The schedule length is driven by the set of two resource critical paths {(1,2,3,5), (1,2,4,5)}. Float is correct.

### Caveats

Resource-constrained scheduling becomes intractable as schedules become more complex. The example schedule is simple. It could be leveled manually, and the set of resource paths could be enumerated manually. Application of the process to more complex schedules requires automation.

In each of the examples above, the alternative sets of resource paths produced the same set of resource constraints, and hence, the same resource critical path. However, this will not always be the case. For any resource-leveled schedule, there may be multiple sets of resource paths that define different sets of resource critical paths. This ambiguity means that for a particular leveling of a schedule, there could be more than one possible set of resource critical paths that would determine how task scheduling is driven.

While logical constraints are permanent, resource constraints are ephemeral. They are subject to change during project execution depending upon varying resource capacity levels and other schedule perturbations. While critical paths can change during project execution, resource critical paths may be more susceptible to change. This needs to be investigated further.

The process described above does not take into account schedules with incomplete network logic or imposed date constraints. Further, the leveling techniques of task preemption and resource assignment adjustment are not considered. The process has not been evaluated against these more complicated scheduling techniques and issues.

### Summary

The process I’ve presented for identifying resource critical paths and eliminating phantom float should help us understand what is driving task scheduling in resource-constrained schedules.

I would be very interested in hearing about your experiences with resource-driven scheduling and about how you deal with phantom float. Please share your thoughts and insights in the comments below.