Overload#

The resource overload of one evaluated stage, each value paired with its difference from the baseline. It's the Overload property of Metrics, so Project.Evaluate returns one for the baseline and one for every stage of every candidate.

Overload is the sum over buckets of max(0, load − capacity), in capacity × buckets. One unit is one bucket at 100 % over capacity. The bucket size is EvaluateOptions.Level.Basis when it's set; otherwise it's derived from SchedulingConfig.DurationUnit. The baseline and every stage of the call use the same basis.

Declaration#

namespace Plen;

public sealed class Overload : IDeepCloneable<Overload>, IEquatable<Overload>
{
    public Amount Total { get; set; }
    public List<ResourceOverload> Resources { get; set; } = [];
    public List<FocusOverload> Focus { get; set; } = [];

    public Overload DeepClone();
    public bool Equals(Overload? other);
}

Treat a returned Overload as read-only; call DeepClone() before changing it.

Properties#

Property Type Description
Total Amount Overload of all resources together.
Resources List<ResourceOverload> One entry per resource whose overload isn't 0 in this stage or in the baseline. Resources non-zero in the stage come first, in stage order, then resources non-zero only in the baseline.
Focus List<FocusOverload> One entry per task in EvaluateOptions.Focus, in that order. Empty when Focus is empty.

Per resource, ResourceOverload.Overload.Value equals the sum of that resource's OverallocationWindow.Overload values at the same basis.

Methods#

DeepClone#

public Overload DeepClone()

Returns a deep copy. Resources, Focus and every nested list are copied.

Equals#

public bool Equals(Overload? other)
public override bool Equals(object? obj)

Value equality: lists compare element by element, double values by ==. GetHashCode is consistent with it.

ToString#

public override string ToString()

Returns a readable rendering of every property, for logging.

ResourceOverload#

The overload of one resource.

public sealed class ResourceOverload : IDeepCloneable<ResourceOverload>, IEquatable<ResourceOverload>
{
    public required Id Resource { get; set; }
    public Amount Overload { get; set; }
}
Property Type Description
Resource Id The resource. required.
Overload Amount Its overload. Inside FocusOverload, only the buckets that intersect the focus task count.

FocusOverload#

The overload inside one focus task's span.

public sealed class FocusOverload : IDeepCloneable<FocusOverload>, IEquatable<FocusOverload>
{
    public required Id Task { get; set; }
    public List<ResourceOverload>? Resources { get; set; }
}
Property Type Description
Task Id The focus task. required.
Resources List<ResourceOverload>? Overload per resource over the buckets that intersect the task's [start, end) in this stage. Lists every resource that's non-zero there in the stage or in the baseline, in the same order as Overload.Resources. null when the task isn't in this stage, for example when the candidate removed it.

A milestone with zero span covers no bucket, so its resources are all 0. In the scheduled stage the values show the overload around the task. In the leveled stage they show what leveling couldn't resolve there.

ResourceOverload and FocusOverload have the same DeepClone, Equals and ToString members as Overload.

Amount#

A value and its difference from the baseline value. It's also the type of Metrics.Conflicts and Metrics.Cycles.

public record struct Amount(double Value, double Delta);
Property Type Description
Value double The value in this stage.
Delta double Value minus the baseline value. Negative when the stage has less than the baseline. 0 in the baseline's own metrics.

Amount is a value type. Equals compares Value and Delta by ==, and ToString returns a readable rendering of both.

Example#

Two tasks overload dev1 on the same days. Score moving one of them to dev2 and read the overload in total, per resource and around task 2.

using Plen;

var monday = Moment.FromYmdHm(2026, 8, 10, 8, 0)
    ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData(1) { StartDate = monday, Duration = 2.0 },
        new TaskData(2) { StartDate = monday, Duration = 2.0 },
    ],
    Resources = [new ResourceData { Id = "dev1" }, new ResourceData { Id = "dev2" }],
    Assignments =
    [
        new AssignmentData { Id = "a1", Task = 1, Resource = "dev1", Value = 1.0 },
        new AssignmentData { Id = "a2", Task = 2, Resource = "dev1", Value = 1.0 },
    ],
};

var project = new Project();
project.Solve(data, new SolveOptions());

var changes = new SchedulingChanges();
changes.Assignments.Update.Add(new AssignmentUpdate("a2") { Resource = Patch<Id>.Of("dev2") });

var result = project.Evaluate(
    [new Candidate { Id = "move", Changes = changes }],
    new EvaluateOptions { Focus = [2] });

Console.WriteLine($"baseline total: {result.Baseline.Overload.Total.Value}");

var overload = result.Candidates[0].Scheduled?.Overload;
if (overload is not null)
{
    Console.WriteLine($"candidate total: {overload.Total.Value} ({overload.Total.Delta:+0.##;-0.##;0})");
    foreach (var r in overload.Resources)
        Console.WriteLine($"  {r.Resource}: {r.Overload.Value} ({r.Overload.Delta:+0.##;-0.##;0})");
    foreach (var f in overload.Focus)
        Console.WriteLine($"  around task {f.Task}: {f.Resources?.Sum(r => r.Overload.Value)}");
}