Skip to content

MPC Controller

Model predictive controller for dynamic simulations, based on Dynamic Matrix Control (DMC). It moves several manipulated variables at once to keep several controlled variables inside their ranges, using first-order plus dead time step response models between each pair.

DWSIM.UnitOperations.SpecialOps.MPCController
Assembly DWSIM.UnitOperations.dll · Object ← BaseClass ← SpecialOpBaseClass ← MPCController

At a glance

MPC Controller in the example flowsheet

This object has no stream connections; it acts on other objects through their properties.

Example

This code runs on every build of this site, and the output below is what it printed.

fs = (Flowsheet.Create("MPCExample")
      .WithCompound("Water")
      .WithPropertyPackage(PropertyPackages.SteamTables))

inlet = (fs.AddMaterialStream("Inlet").At(Q.Celsius(25.0), Q.Pascal(130000.0))
         .WithMassFlow(Q.KgPerSecond(10.0)).AsFlowSpec())
s1 = fs.AddMaterialStream("S-1").AsPressureSpec()
s2 = fs.AddMaterialStream("S-2").AsPressureSpec()
outlet = fs.AddMaterialStream("Outlet").AsPressureSpec()
(fs.AddValve("V-01").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(100.0)
   .WithOpeningPercent(50.0).ConnectFeed(inlet).ConnectProduct(s1))
tank = (fs.AddTank("T-01").WithVolume(Q.CubicMeters(2.0)).WithHeight(Q.Meters(2.0))
        .ConnectFeed(s1).ConnectProduct(s2))
valve = (fs.AddValve("V-02").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(400.0)
         .WithOpeningKvRelationship().WithOpeningPercent(29.0)
         .ConnectFeed(s2).ConnectProduct(outlet))
fs.AutoLayout()
fs.Solve()                                              # steady state to start from
outlet.Object.Phases[0].Properties.pressure = 101325.0  # discharge to atmosphere

builder = fs.AddUnitOperation(ObjectType.Controller_MPC, "LC-MPC")
mpc = builder.Object.__implementation__

cv = MPCVariable()                      # controlled variable: the tank level
cv.ObjectID, cv.Name = tank.Object.Name, "T-01 level"
cv.PropertyName, cv.Units = "Liquid Level", "m"
cv.MinValue, cv.MaxValue = 0.9, 1.1     # the target is the middle of the range, 1 m
mv = MPCVariable()                      # manipulated variable: the outlet valve opening
mv.ObjectID, mv.Name = valve.Object.Name, "V-02 opening"
mv.PropertyName, mv.Units = "PROP_VA_5", ""
mv.MinValue, mv.MaxValue = 0.0, 100.0   # %
mpc.ControlledVariables.Add(cv)
mpc.ManipulatedVariables.Add(mv)

model = StepResponseModel()             # level response to the opening
model.CVIndex, model.MVIndex = 0, 0
model.Integrating = True                # the level ramps after a step in the opening
model.Gain = -0.0004                    # m/s per %: 0.4 kg/s more outflow per % from a 1 m2 tank
model.TimeConstant = 0.0
mpc.StepResponseModels.Add(model)
mpc.SampleTime = 5.0                    # s, one integration step
mpc.PredictionHorizon = 30              # 30 samples, 150 s ahead
mpc.ControlHorizon = 5                  # five future moves
mpc.MoveSuppressionWeight = 0.1

(fs.Dynamics.DefineIntegrator("Int1")
   .WithIntegrationStep(TimeSpan.FromSeconds(5.0)).WithDuration(TimeSpan.FromMinutes(20.0))
   .Monitor("T-01", "Liquid Level", "m", "level")
   .Monitor("V-02", "PROP_VA_5", "", "opening"))
(fs.Dynamics.DefineEventSet("FeedStep")
   .AddStepChange("Inlet", "PROP_MS_2", 15.0, Q.Seconds(600.0), "kg/s", "feed 10 -> 15 kg/s"))
(fs.Dynamics.DefineSchedule("Run").WithIntegrator("Int1").WithEventSet("FeedStep")
   .UseCurrentStateAsInitial(True).MakeCurrent())
result = fs.RunDynamics("Run").Execute()

level, opening = result.GetSeries("level"), result.GetSeries("opening")
print(f"Level                 = {level.Initial:.3f} m at the start, {level.ValueAt(595.0):.3f} m before the step")
print(f"Peak level after step = {max(v for t, v in zip(level.TimeSeconds, level.Values) if t >= 600.0):.3f} m")
print(f"Final level           = {level.Final:.3f} m")
print(f"Valve opening         = {opening.ValueAt(595.0):.1f} % before, {opening.Final:.1f} % at the end")

# Measured disturbance as feedforward: a second tank, T-00, in front of T-01 delays the feed step
# by its own lag. Measuring the feed and telling the controller how it acts on the level lets
# the valve open before the level rises.
def two_tanks(feedforward):
    fs2 = (Flowsheet.Create("MPCTwoTanks")
           .WithCompound("Water")
           .WithPropertyPackage(PropertyPackages.SteamTables))
    feed = (fs2.AddMaterialStream("Inlet").At(Q.Celsius(25.0), Q.Pascal(130000.0))
            .WithMassFlow(Q.KgPerSecond(10.0)).AsFlowSpec())
    names = ["S-1", "S-A", "S-B", "S-2", "Outlet"]
    s = {n: fs2.AddMaterialStream(n).AsPressureSpec() for n in names}
    (fs2.AddValve("V-01").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(100.0)
        .WithOpeningPercent(50.0).ConnectFeed(feed).ConnectProduct(s["S-1"]))
    (fs2.AddTank("T-00").WithVolume(Q.CubicMeters(2.0)).WithHeight(Q.Meters(2.0))
        .ConnectFeed(s["S-1"]).ConnectProduct(s["S-A"]))
    (fs2.AddValve("V-00").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(330.0)
        .WithOpeningKvRelationship().WithOpeningPercent(50.0)          # fixed opening
        .ConnectFeed(s["S-A"]).ConnectProduct(s["S-B"]))
    t01 = (fs2.AddTank("T-01").WithVolume(Q.CubicMeters(2.0)).WithHeight(Q.Meters(2.0))
           .ConnectFeed(s["S-B"]).ConnectProduct(s["S-2"]))
    v02 = (fs2.AddValve("V-02").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(400.0)
           .WithOpeningKvRelationship().WithOpeningPercent(29.0)
           .ConnectFeed(s["S-2"]).ConnectProduct(s["Outlet"]))
    fs2.Solve()
    s["Outlet"].Object.Phases[0].Properties.pressure = 101325.0

    mpc2 = fs2.AddUnitOperation(ObjectType.Controller_MPC, "LC-MPC").Object.__implementation__
    for var, obj, prop, units, low, high, target in (
            (MPCVariable(), t01, "Liquid Level", "m", 0.9, 1.1, mpc2.ControlledVariables),
            (MPCVariable(), v02, "PROP_VA_5", "", 0.0, 100.0, mpc2.ManipulatedVariables)):
        var.ObjectID, var.Name, var.PropertyName, var.Units = obj.Object.Name, obj.Object.GraphicObject.Tag, prop, units
        var.MinValue, var.MaxValue = low, high
        target.Add(var)
    model = StepResponseModel()
    model.CVIndex, model.MVIndex, model.Integrating, model.Gain, model.TimeConstant = 0, 0, True, -0.0004, 0.0
    mpc2.StepResponseModels.Add(model)
    mpc2.SampleTime, mpc2.PredictionHorizon, mpc2.ControlHorizon, mpc2.MoveSuppressionWeight = 5.0, 30, 5, 0.1

    dv = MPCVariable()                  # measured disturbance: the feed flow
    dv.ObjectID, dv.Name = feed.Object.Name, "Inlet flow"
    dv.PropertyName, dv.Units = "PROP_MS_2", "kg/s"
    mpc2.DisturbanceVariables.Add(dv)
    if feedforward:
        dm = StepResponseModel()        # level response to the feed: a ramp behind the lag of T-00
        dm.CVIndex, dm.DVIndex = 0, 0
        dm.Integrating = True
        dm.Gain = 1.0 / 997.0           # m/s per kg/s into a 1 m2 tank
        dm.TimeConstant = 115.0         # s, from an open-loop feed step: 63 % of T-00's outflow change
        mpc2.DisturbanceModels.Add(dm)

    (fs2.Dynamics.DefineIntegrator("Int1")
        .WithIntegrationStep(TimeSpan.FromSeconds(5.0)).WithDuration(TimeSpan.FromMinutes(45.0))
        .Monitor("T-01", "Liquid Level", "m", "level"))
    (fs2.Dynamics.DefineEventSet("FeedStep")
        .AddStepChange("Inlet", "PROP_MS_2", 15.0, Q.Seconds(1500.0), "kg/s", "feed 10 -> 15 kg/s"))
    (fs2.Dynamics.DefineSchedule("Run").WithIntegrator("Int1").WithEventSet("FeedStep")
        .UseCurrentStateAsInitial(True).MakeCurrent())
    lv = fs2.RunDynamics("Run").Execute().GetSeries("level")
    return max(abs(v - 1.0) for t, v in zip(lv.TimeSeconds, lv.Values) if t >= 1500.0)

print(f"Two tanks, peak level deviation after the feed step = {two_tanks(False):.3f} m without the "
      f"disturbance model, {two_tanks(True):.3f} m with it")

Output

Level                 = 0.010 m at the start, 1.000 m before the step
Peak level after step = 1.080 m
Final level           = 1.000 m
Valve opening         = 28.8 % before, 43.3 % at the end
Two tanks, peak level deviation after the feed step = 0.027 m without the disturbance model, 0.009 m with it

DWSIM 10.2.11.0, generated 2026-10-08.

Properties

IDs accepted by GetPropertyValue, SetPropertyValue, the sensitivity analysis, the optimizer, the Adjust block and dynamic events. Units are SI; pass another unit system to GetPropertyValue to get them converted.

ID Name Unit (SI) Input
Prediction Horizon yes
Control Horizon yes
Sample Time s yes
Move Suppression Weight yes
Active yes
Use Measured Disturbances yes

Learn more

API members

Public members declared by this class. Inherited members are documented on the base classes.

Constructors

MPCController(): Initializes a new default instance of the MPCController class.

Initializes a new default instance of the MPCController class.

public MPCController()
Public Sub New()

MPCController(string, string): Initializes a new instance of the MPCController class with a name and description.

Initializes a new instance of the MPCController class with a name and description.

Parameter Type Description
name String The name of this controller.
description String A brief description of this controller.
public MPCController(string name, string description)
Public Sub New(name As String, description As String)

Properties

Active: Gets or sets whether the controller acts during a dynamic run.

Gets or sets whether the controller acts during a dynamic run. The integrator skips an inactive controller. Default True.

public bool Active { get; set; }
Public Property Active As Boolean

ControlHorizon: Control horizon M, in samples: the number of future moves computed for each manipulated variable (only the first is...

Control horizon M, in samples: the number of future moves computed for each manipulated variable (only the first is applied). Limited to the prediction horizon. Default 5.

public int ControlHorizon { get; set; }
Public Property ControlHorizon As Integer

ControlledVariables: Controlled variables.

Controlled variables. Each one is driven to the middle of its range (MinValue + MaxValue) / 2, with its Weight in the objective.

public List<MPCVariable> ControlledVariables { get; set; }
Public Property ControlledVariables As List(Of MPCVariable)

CVHistory: Controlled variable values recorded at each control step, one array per step in the variables' units, for the trend...

Controlled variable values recorded at each control step, one array per step in the variables' units, for the trend charts.

public List<double[]> CVHistory { get; set; }
Public Property CVHistory As List(Of Double())

DisturbanceModels: Disturbance models, one per controlled/disturbance variable pair, indexed by CVIndex and DVIndex in the variable...

Disturbance models, one per controlled/disturbance variable pair, indexed by CVIndex and DVIndex in the variable lists: the response of the controlled variable to a unit step in the measured disturbance, first order plus dead time or integrating. The disturbance is taken as constant over the prediction horizon. An empty list leaves the controller as it is without disturbances.

public List<StepResponseModel> DisturbanceModels { get; set; }
Public Property DisturbanceModels As List(Of StepResponseModel)

DisturbanceVariables: Measured disturbance variables.

Measured disturbance variables. The controller reads them at each control step; a disturbance with a model in DisturbanceModels acts as feedforward, its change since the last step entering the predicted trajectory of the controlled variable as a manipulated variable move does. MinValue, MaxValue and Weight are not used.

public List<MPCVariable> DisturbanceVariables { get; set; }
Public Property DisturbanceVariables As List(Of MPCVariable)

DVHistory: Measured disturbance values recorded at each control step, one array per step in the variables' units, for the trend...

Measured disturbance values recorded at each control step, one array per step in the variables' units, for the trend charts. Recorded only when the controller has disturbance variables.

public List<double[]> DVHistory { get; set; }
Public Property DVHistory As List(Of Double())

ExecutionOrder: Position of this controller in the order the dynamic integrator runs the MPC controllers, in ascending order.

Position of this controller in the order the dynamic integrator runs the MPC controllers, in ascending order. Default 0.

public int ExecutionOrder { get; set; }
Public Property ExecutionOrder As Integer

ManipulatedVariables: Manipulated variables moved by the controller, each clamped to its MinValue and MaxValue.

Manipulated variables moved by the controller, each clamped to its MinValue and MaxValue.

public List<MPCVariable> ManipulatedVariables { get; set; }
Public Property ManipulatedVariables As List(Of MPCVariable)

MobileCompatible: Gets a value indicating whether this controller is compatible with mobile interfaces.

Gets a value indicating whether this controller is compatible with mobile interfaces. Always False.

public override bool MobileCompatible { get; }
Public Overrides ReadOnly Property MobileCompatible As Boolean

MoveSuppressionWeight: Move suppression weight (lambda), dimensionless.

Move suppression weight (lambda), dimensionless. It is scaled by the mean diagonal of each manipulated variable's block of the dynamic matrix; larger values give smaller, smoother moves. Default 0.1.

public double MoveSuppressionWeight { get; set; }
Public Property MoveSuppressionWeight As Double

MVHistory: Manipulated variable values recorded at each control step, one array per step in the variables' units, for the trend...

Manipulated variable values recorded at each control step, one array per step in the variables' units, for the trend charts.

public List<double[]> MVHistory { get; set; }
Public Property MVHistory As List(Of Double())

ObjectClass: Gets or sets the simulation object class, which is always Logical for special operations.

Gets or sets the simulation object class, which is always Logical for special operations.

public override SimulationObjectClass ObjectClass { get; set; }
Public Overrides Property ObjectClass As SimulationObjectClass

PredictionHorizon: Prediction horizon P, in samples: the number of future samples over which the controlled variable error is minimized.

Prediction horizon P, in samples: the number of future samples over which the controlled variable error is minimized. Default 30.

public int PredictionHorizon { get; set; }
Public Property PredictionHorizon As Integer

SampleTime: Control interval, in s.

Control interval, in s. In dynamic mode the controller acts every n-th call, with n the sample time divided by the integrator call interval, rounded and at least 1. Default 1.

public double SampleTime { get; set; }
Public Property SampleTime As Double

StepResponseModels: Step response models, one per controlled/manipulated variable pair, indexed by CVIndex and MVIndex in the variable...

Step response models, one per controlled/manipulated variable pair, indexed by CVIndex and MVIndex in the variable lists.

public List<StepResponseModel> StepResponseModels { get; set; }
Public Property StepResponseModels As List(Of StepResponseModel)

UseMeasuredDisturbances: Gets or sets whether the disturbance models act as feedforward.

Gets or sets whether the disturbance models act as feedforward. With False the controller still reads and records the disturbances but predicts as if it had no disturbance models. Default True.

public bool UseMeasuredDisturbances { get; set; }
Public Property UseMeasuredDisturbances As Boolean

Methods

Calculate(object): Dynamic Matrix Control.

Dynamic Matrix Control. At each control step the predicted CV trajectory is shifted one sample, updated with the MV moves measured since the last step and moved onto the current measurement (model error correction). The first of the M moves that minimize the weighted squared error over P samples plus the move suppression term is applied, within the MV limits. For an integrating CV the model error is also extrapolated as a ramp, which removes the offset a load change leaves. The changes of the measured disturbances that have a disturbance model enter the prediction the same way as the MV moves (feedforward).

Parameter Type Description
args Object
public override void Calculate(object args = null)
Public Overrides Sub Calculate(args As Object = Nothing)

CloneXML(): Creates a deep copy of this object by round-tripping through XML serialization.

Creates a deep copy of this object by round-tripping through XML serialization.

public override object CloneXML()
Public Overrides Function CloneXML() As Object

CloseEditForm(): Closes the editor of this object, if it is open.

Closes the editor of this object, if it is open.

public override void CloseEditForm()
Public Overrides Sub CloseEditForm()

DisplayEditForm(): Opens the editor of this object.

Opens the editor of this object. A host that has no editor for it does nothing.

public override void DisplayEditForm()
Public Overrides Sub DisplayEditForm()

GetChartModel(string): Builds the trend chart with the given name.

Builds the trend chart with the given name.

Parameter Type Description
name String The chart name: "CV Trends" (also used for an empty name), "MV Trends" or "DV Trends".
public override object GetChartModel(string name)
Public Overrides Function GetChartModel(name As String) As Object

GetChartModelNames(): Returns the names of the charts this controller can embed in the flowsheet.

Returns the names of the charts this controller can embed in the flowsheet.

public override List<string> GetChartModelNames()
Public Overrides Function GetChartModelNames() As List(Of String)

GetDisplayDescription(): Returns the description string for this controller type.

Returns the description string for this controller type.

public override string GetDisplayDescription()
Public Overrides Function GetDisplayDescription() As String

GetDisplayName(): Returns the display name for this controller type.

Returns the display name for this controller type.

public override string GetDisplayName()
Public Overrides Function GetDisplayName() As String

GetEditingForm(): The editor window of this object, a window of the host's UI framework.

The editor window of this object, a window of the host's UI framework.

public override object GetEditingForm()
Public Overrides Function GetEditingForm() As Object

GetIconBitmapBytes(): Returns the raw bytes of the icon image for this controller.

Returns the raw bytes of the icon image for this controller.

public override byte[] GetIconBitmapBytes()
Public Overrides Function GetIconBitmapBytes() As Byte()

GetProperties(PropertyType): Get a list of all properties of the object.

Get a list of all properties of the object.

Parameter Type Description
proptype PropertyType Type of the property.
public override string[] GetProperties(PropertyType proptype)
Public Overrides Function GetProperties(proptype As PropertyType) As String()

GetPropertyUnit(string, IUnitsOfMeasure): Gets the units of a property.

Gets the units of a property.

Parameter Type Description
prop String Property identifier.
su IUnitsOfMeasure Units system to use. Null to use the default (SI) system.
public override string GetPropertyUnit(string prop, IUnitsOfMeasure su = null)
Public Overrides Function GetPropertyUnit(prop As String, su As IUnitsOfMeasure = Nothing) As String

GetPropertyValue(string, IUnitsOfMeasure): Gets the value of a property.

Gets the value of a property.

Parameter Type Description
prop String Property identifier.
su IUnitsOfMeasure Units system to use. Null to use the default (SI) system.
public override object GetPropertyValue(string prop, IUnitsOfMeasure su = null)
Public Overrides Function GetPropertyValue(prop As String, su As IUnitsOfMeasure = Nothing) As Object

InitializeModels(): Regenerates the step response coefficients of every model, disturbance models included, from its gain, time constant...

Regenerates the step response coefficients of every model, disturbance models included, from its gain, time constant and dead time, at the current sample time and a length that covers the prediction horizon and the settling of each model.

public void InitializeModels()
Public Sub InitializeModels()

LoadData(List<XElement>): Loads object data stored in a collection of XML elements.

Loads object data stored in a collection of XML elements.

Parameter Type Description
data List<XElement>
public override bool LoadData(List<XElement> data)
Public Overrides Function LoadData(data As List(Of XElement)) As Boolean

Reset(): Clears the controller state: the predicted trajectories, the last moves, the cached model and tuning, the call...

Clears the controller state: the predicted trajectories, the last moves, the cached model and tuning, the call counter, the histories and the last values of the variables.

public void Reset()
Public Sub Reset()

SaveData(): Saves object data in a collection of XML elements.

Saves object data in a collection of XML elements.

public override List<XElement> SaveData()
Public Overrides Function SaveData() As List(Of XElement)

SetPropertyValue(string, object, IUnitsOfMeasure): Sets the value of a property.

Sets the value of a property.

Parameter Type Description
prop String Property identifier.
propval Object Property value to set at the specified units.
su IUnitsOfMeasure Units system to use. Null to use the default (SI) system.
public override bool SetPropertyValue(string prop, object propval, IUnitsOfMeasure su = null)
Public Overrides Function SetPropertyValue(prop As String, propval As Object, su As IUnitsOfMeasure = Nothing) As Boolean

UpdateEditForm(): Redraws the editor of this object with the current values, if it is open.

Redraws the editor of this object with the current values, if it is open.

public override void UpdateEditForm()
Public Overrides Sub UpdateEditForm()