Resource-Constrained Project Scheduling Problem (RCPSP)
In this section, we consider the resource-constrained project scheduling problem (RCPSP). In RCPSP, the activities of a project have to be scheduled such that the makespan of the project is minimized. Thereby, technological precedence constraints must be observed, as well as limited capacities of the renewable resources that are required to accomplish the activities. The RCPSP for the scheduling of maintenance and surveillance activities of a nuclear power plant can be summarized as follows.
We consider a project that consists of a set of \(J\) jobs (or tasks). Due to technological requirements, precedence relations among some of the jobs enforce that job \(j = 2,3,\dots,J\) may not be started before all its predecessors, denoted by \(P_j\), are finished. Here, \(j = 1\) indexes an artificial job with zero duration, which precedes all jobs that can start at time zero, and \(j = J\) indexes an artificial final job, again with zero duration, which represents the end of the project. Executing job \(j\) takes \(d_j\) time periods and is supported by a set, \(R\), of renewable resources.
Consider a horizon with an upper bound \(T\) on the project’s makespan, i.e., the time at which the final job is completed. We assume \(K_r^p\) units of renewable resource \(r \in R\) are available in each time period \(t = 1, 2, \dots, T\). Job \(j\) requires \(k_{jr}^p\) units of the renewable resource \(r \in R\) for each period of the job’s duration, i.e., for time periods when the job is in process.
The objective is to find a schedule that minimizes the project’s makespan while respecting the constraints imposed by the precedence relations and the limited resource availability.
Indexes and parameters
\(t = 1, 2, \dots, T\): time periods, where \(T\) is an upper bound on the project’s makespan.
\(j = 1, 2, \dots, J\): jobs, with \(j = 1\) and \(j = J\) denoting artificial jobs.
\(r \in R\): set of renewable resources.
\(d_j\): duration of job \(j\).
\(K_r^p\): number of units of renewable resource \(r\) available in period \(t\).
\(k_{jr}^p\): number of units of renewable resource \(r\) consumed by job \(j\) while in process.
\(P_j\): set of immediate predecessors of job \(j\).
Decision variables
\(x_{jt}\): equals 1 if job \(j\) completes in period \(t\); 0 otherwise.
Mathematical formulation
The RCPSP model in LOGOS consists of the following modeling components:
<Sets>, <Parameters>, and <Settings>. Each of
these components is illustrated in the following sections.
Sets
This subsection contains information regarding the XML nodes used to
define the <Sets> of the RCPSP model being performed through
LOGOS. <Sets> specifies a collection of data, possibly
including numeric data (e.g., real or integer values) as well as
symbolic data (e.g., strings) typically used to specify the valid
indices for indexed components.
Note
Numeric data provided in <Sets> is treated as strings.
<Sets> accepts the following sub-nodes:
<tasks>, comma/space-separated string, required Specifies the valid indices for tasks.<resources>, comma/space-separated string, required Specifies the indices for renewable resources.<predecessors>, comma/space-separated string, required Specifies the indices for preceding tasks.<successors>, comma/space-separated string, required Specifies indices for successors. This sub-node accepts the following attribute:index, string, required Specifies the index dependence. The valid index is'predecessors'.
Example XML:
<Sets>
<tasks>
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
</tasks>
<resources>
r1 r2 r3 r4
</resources>
<predecessors>
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
</predecessors>
<successors index="predecessors">
s1 s2 s3 s4;
s1;
s1;
s1;
s1;
s1;
s1;
s1;
s1;
s1;
s1
</successors>
</Sets>
Parameters
This subsection contains information regarding the XML nodes used to
define the <Parameters> of the RCPSP optimization model being
performed through LOGOS:
<available_resources>, comma/space-separated string, required Specifies the available renewable resources. This node accepts the following attribute:index, string, required Specifies the indices of this parameter; keywords should be predefined in<Sets>. Valid keywords are'resources'.
<task_resource_consumption>, comma/space-separated string, required Specifies the resource consumption for each task. This node accepts the following attribute:index, comma-separated string, required Specifies the indices of this parameter; keywords should be predefined in<Sets>. Valid keywords are'tasks, resources'.
<task_duration>, comma/space-separated string, required Specifies the duration for each task. This node accepts the following attribute:index, string, required Specifies the indices of this parameter; keywords should be predefined in<Sets>. Valid keywords are'tasks'.
<task_successors>, comma/space-separated string, required Specifies the successors for each predecessor. This node accepts the following attributes:index, string, required Specifies the indices of this parameter; keywords should be predefined in<Sets>. Valid keywords are'successors'.type, string, optional Specifies the text type of the provided node, i.e., integer, float, or string. Valid values are'int','float', or'str'. Default:'float'.
Example XML:
<Parameters>
<available_resources index="resources">
13 13 13 12
</available_resources>
<task_resource_consumption index="tasks, resources">
0 0 0 0
10 0 0 0
0 7 0 0
0 9 0 0
0 4 0 0
0 0 0 6
10 0 0 0
0 0 6 0
0 0 0 8
0 6 0 0
0 0 0 5
0 0 0 0
</task_resource_consumption>
<task_duration index="tasks">
0
8
1
10
6
5
8
9
1
9
8
0
</task_duration>
<task_successors index="successors" type="str">
2 3 4 9
5
7
8
6
10
11
10
12
9
12
</task_successors>
</Parameters>
Settings
This subsection contains information regarding the XML nodes used to
define the <Settings> of the RCPSP optimization model being
performed through LOGOS:
<problem_type>, string, required Specifies the type of optimization problem. Currently, the only available type is'rcpsp'.<solver>, string, optional Represents available solvers including:'cbc'fromhttps://github.com/coin-or/Cbc.git'glpk'fromhttps://www.gnu.org/software/glpk/
<sense>, string, optional Specifies'minimize'or'maximize'for minimization or maximization, respectively. Default:'minimize'.<makespan_upperbound>, integer, required Specifies an upper bound on the makespan.
Example LOGOS input XML for RCPSP model:
<?xml version="1.0" encoding="UTF-8"?>
<Logos>
...
<Settings>
<makespan_upperbound>65</makespan_upperbound>
<solver>cbc</solver>
<sense>minimize</sense>
<problem_type>rcpsp</problem_type>
</Settings>
...
</Logos>
The Pert Class
While the formulation above is solved exactly as a mixed-integer program,
LOGOS also provides an event-driven scheduling engine — the Pert class —
for large, richly constrained schedules (such as full nuclear-outage models)
where an exact MILP is impractical. The Pert class is the core scheduling
engine of the CPM module. It represents a project schedule as a directed
acyclic graph of Activity objects bounded by a START and an END
node, computes the classical CPM/PERT timing quantities (early start, early
finish, late start, late finish, and slack) and the critical path, and — when
resource, equipment, and location pools are provided — solves the
resource-constrained project scheduling problem heuristically using an
event-driven scheduling engine. It also supports Critical Chain Project
Management (CCPM) buffering, mid-outage replanning, and several
schedule-generation strategies.
Construction
A Pert object can be built in two ways.
1. From an OutageData object (recommended).
When an OutageData instance is supplied, the activity graph and all
resource, equipment, location, consumable, and system-state pools are built
automatically from the planning data (typically loaded from a JSON file). This
is the path used for full nuclear-outage models.
2. From a manual graph dictionary.
For simple CPM-only problems, the graph can be provided directly as a
dictionary mapping each Activity to the list of its outgoing activities.
The constructor accepts the following parameters:
graph (dictionary, optional): mapping of the form
{Activity: [Activity, ...]}giving the successor activities of each activity. Provide this for manual construction.outage_data (
OutageData, optional): object containing all planning data. When supplied, the graph is built automatically from its tasks.priorities (dictionary, optional): mapping of activity names to external priority values used to order candidates during resource-constrained scheduling.
seed (integer, optional): random seed for reproducibility (default:
2506178).
Either graph or outage_data must be provided. A convenience class
method, Pert.from_json_file(filepath, schema_path), loads and validates an
OutageData object from a JSON file and returns the corresponding Pert
instance.
Example — manual graph construction:
from LOGOS.src.CPM.pert import Pert
from LOGOS.src.CPM.activity import Activity
start = Activity("start", 10)
b = Activity("b", 20)
c = Activity("c", 5)
end = Activity("end", 20)
graph = {start: [b],
b : [c],
c : [end],
end : []}
schedule = Pert(graph=graph)
print(schedule.getProjectDuration())
print(schedule.getCriticalPathSymbolic())
Example — construction from a JSON outage definition:
from LOGOS.src.CPM.pert import Pert
schedule = Pert.from_json_file("outage.json", "outage_schema.json")
result = schedule.calculateScheduleWithResources(sgs='max_use_res_ranked')
Modeled constraints
When constructed from an OutageData object, the Pert scheduler honors
the following constraints (each is optional and activated by the corresponding
fields in the JSON task definitions):
Precedence with optional finish-to-start lags between activities.
Renewable crews organized by skill type, with skill substitution (a shortfall in one skill can be covered by qualified alternative skills).
Equipment requirements, including zone-locked equipment that may only be used within a specific location.
Locations / zones, including multi-zone activities that must occupy several zones simultaneously.
Consumables that are permanently depleted when an activity starts, with optional mid-outage restock deliveries.
Plant system states (isolation locks): activities requiring conflicting states of the same system are serialized.
Radiation dose budgets tracked per worker-hour against a global limit.
Regulatory time windows (earliest start / latest finish) per activity.
Shift calendars restricting when work may be performed.
Mobilization lead times for activities that need advance preparation.
Hold points whose downstream (blocked) tasks cannot start until the hold point completes.
Multi-mode execution (MMRCPSP): alternative duration/resource profiles per activity, selectable through
set_modes.WBS priority roll-up, elevating every member of a work-breakdown package when any member becomes critical.
CPM timing analysis
These methods compute and expose the unconstrained CPM/PERT quantities:
generateInfo(): compute early start, early finish, late start, late finish, and slack for every activity.
getProjectDuration(): return the unconstrained project (critical-path) duration in hours.
getCriticalPath() / getCriticalPathSymbolic(): return the critical path(s) as
Activityobjects, or as activity-name strings.getCriticalPathWithLength(): return the critical path as a dictionary including per-activity durations.
returnScheduleEndTime(): return the absolute end time of the schedule.
Resource-constrained scheduling
Once the resource, equipment, and location pools are populated, the schedule can be solved as an RCPSP:
result = schedule.calculateScheduleWithResources(sgs='max_use_res_ranked')
The calculateScheduleWithResources method advances time only to the next
meaningful event (activity completion, availability-period boundary, or the
early-start time of a waiting activity) rather than stepping hour by hour. It
accepts the following parameters:
sgs (string): the Schedule Generation Scheme used to select activities from the available candidates at each event (default:
max_use_res_ranked).max_time_hours (float, optional): safety cutoff, in hours from the start time. Defaults to a multiple of the CPM duration.
priority_rule (string, optional): priority rule used to order candidates when no external priorities are supplied.
Allowed values for the sgs parameter are:
first: serial SGS — try candidates in priority order and start the single highest-priority activity that is resource-feasible.
max_use_res_ranked: rank candidates by float/value and start as many of the highest-ranked activities as the available resources allow.
max_use_res_shuffled: as above but with a randomly shuffled candidate order.
md_knapsack: select the set of activities through a multi-dimensional knapsack optimization over the currently available resources.
look_ahead: rank candidates by their immediate value plus a discounted estimate of the future opportunities they unlock within a finite horizon.
The method returns a dictionary with the keys scheduled_duration (hours
from start to the last activity end), cpm_duration (unconstrained CPM
duration), delay_hours (total accumulated resource-wait delay),
n_activities, n_completed, and iterations (number of event-loop
steps). A companion method, calculateSerialScheduleWithResources, runs a
strictly serial schedule-generation scheme.
Scenario updates, multi-mode, and replanning
set_durations(new_durations): update activity durations (e.g. from a RAVEN sample) and recompute all CPM values.
set_modes(mode_assignments): apply named execution modes to activities and recompute all CPM values.
set_priorities(priorities): update the external priority map used to order candidates.
replan(…): replan the remaining schedule from a mid-outage snapshot, freezing completed and in-progress work and rescheduling the rest.
clone_for_analysis(): return an independent copy of the
Pertobject suitable for what-if analysis without disturbing the original.
Critical Chain (CCPM) buffering
insert_project_buffer(…): insert a CCPM project buffer at the end of the resource-constrained chain.
insert_feeding_buffers(…): insert feeding buffers where non-critical paths merge into the constrained chain.
get_buffer_status(): report the consumption status of all buffer activities.
Validation, reporting, and visualization
validate_schedule(): run post-schedule feasibility checks and return a
ValidationResult(precedence, resources, equipment, zones, consumables, dose, system states, time windows, shift calendar, and hold points).check_dependency_violations(): check whether the computed schedule violates any job-precedence constraint.
explain_idle_on_chain() / explain_idle_on_chain_detailed(): for each activity on the constrained chain, explain why it waited.
get_schedule_dataframe(): return the schedule as a pandas
DataFrame.export_schedule_to_csv(…) / print_summary(): export or print the computed schedule.
plot_activity_dag(…): render the activity network as a DAG with rich tooltips. The module-level function
plot_gantt_chart(pert, ...)renders an interactive Gantt chart, coloring activities by criticality, to an HTML file.
Note
The Pert class works both standalone and inside a RAVEN workflow
(through the BaseCPMmodel external model). See Critical Path Model (CPM) for the
RAVEN input format; the exact mathematical formulation of the
resource-constrained scheduling problem is given at the top of this page.