LevelingOptions#

Options for Project.Level. new LevelingOptions() levels the whole project with the defaults below. EvaluateOptions.Level and SuggestOptions.Level take the same type.

Declaration#

namespace Plen;

public sealed class LevelingOptions : IDeepCloneable<LevelingOptions>
{
    public LevelingScope? Scope { get; set; }
    public Basis? Basis { get; set; }
    public LevelingOrder Order { get; set; } = LevelingOrder.Standard;
    public bool WithinSlackOnly { get; set; } = false;
    public bool ClearFirst { get; set; } = true;
    public bool AllowSplits { get; set; } = true;
    public LevelingStep Step { get; set; } = LevelingStep.Minimal;

    public LevelingOptions DeepClone();
}

LevelingOptions doesn't implement IEquatable<T>, so two instances compare by reference.

Properties#

Property Type Default Description
Scope LevelingScope? null Which resources, tasks and dates leveling acts on. null levels the whole project.
Basis Basis? null Bucket size for detecting overallocation. null derives it from SchedulingConfig.DurationUnit.
Order LevelingOrder LevelingOrder.Standard Order in which contending tasks give way.
WithinSlackOnly bool false true caps every delay or pause at the task's total slack, so leveling never pushes the plan past the end it had before leveling.
ClearFirst bool true true resets LevelingDelay to 0 and clears Pauses on in-scope tasks before leveling starts. false keeps them and levels on top.
AllowSplits bool true true lets leveling pause a task part-way through (Pauses) instead of delaying it whole. false keeps existing pauses but creates or extends none.
Step LevelingStep LevelingStep.Minimal How far one correction moves a task.

Leveling writes its corrections to TaskData.LevelingDelay and TaskData.Pauses. For how the leveler selects and moves tasks, see Leveling resources.

Validation#

A call that reads these options throws an InputException for an unknown id in the scope:

Path When
scope.resources Scope.Resources names a resource that isn't in the project
scope.tasks Scope.Tasks names a task that isn't in the project

Methods#

DeepClone#

public LevelingOptions DeepClone()

Returns a copy whose Scope is a deep copy too, so the two instances share no list.

LevelingScope#

Limits what leveling acts on. Every property defaults to null, which means no limit.

namespace Plen;

public sealed class LevelingScope : IDeepCloneable<LevelingScope>
{
    public List<Id>? Resources { get; set; }
    public List<Id>? Tasks { get; set; }
    public Moment? From { get; set; }
    public Moment? To { get; set; }

    public LevelingScope DeepClone();
}
Property Type Default Description
Resources List<Id>? null Resources whose overallocation is resolved. null means every resource.
Tasks List<Id>? null Tasks leveling may move. null means every task. Tasks outside the list are never delayed or paused.
From Moment? null Start of the range that triggers leveling. A bucket is in range when its start lies in [From, To). null means no lower bound.
To Moment? null End of the triggering range (exclusive). null means no upper bound.

From and To limit which overallocated buckets get resolved, not where a task may land: a delayed task can end up after To.

DeepClone() returns a copy that shares no list with this one.

LevelingOrder#

Decides which contending task gives way in an overallocated bucket.

namespace Plen;

public enum LevelingOrder
{
    Id = 1,
    Standard = 0,
    Priority = 2,
}
Value Description
Standard Default. A task already running stays, then the standard chain decides: dependency (the successor moves), total slack (more slack moves), start date (later start moves), TaskData.Priority (lower priority moves), constraints (a task with a dated constraint stays), input order (the later task moves).
Id A task already running stays, then the task later in input order moves.
Priority TaskData.Priority decides first (lower priority moves), then the same rules as Standard.

LevelingStep#

Sets how far one correction moves the selected task.

namespace Plen;

public enum LevelingStep
{
    Minimal,
    Skip,
}
Value Description
Minimal Default. Each correction moves the task past one bucket. The choice of which task moves runs again for the next bucket.
Skip Each correction moves the task past the whole overallocated window in one step. Takes fewer rounds and can give a different result than Minimal.

Example#

using Plen;

static Moment Day(int day) =>
    Moment.FromYmdHm(2026, 1, day, 0, 0) ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData("a") { StartDate = Day(5), Duration = 3.0 },
        new TaskData("b") { StartDate = Day(5), Duration = 2.0 },
    ],
    Resources = [new ResourceData { Id = "dev" }],
    Assignments =
    [
        new AssignmentData { Task = "a", Resource = "dev", Value = 1.0 },
        new AssignmentData { Task = "b", Resource = "dev", Value = 1.0 },
    ],
};

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

var result = project.Level(new LevelingOptions
{
    Scope = new LevelingScope { Resources = ["dev"] },
    Order = LevelingOrder.Priority,
    AllowSplits = false,
});