Now we show the tank model using the object oriented component-based approach. The structure of the tank system model using this approach is clearly visible in Figure 1 below.
A tank system with a continuous PI controller and a source for liquid.
The three components of the tank system: the tank, the PI controller and the source of liquid are explicit in Figure 1 and in the declaration of the class TankPI below. Tank instances of tank, source and piContinuous are connected to controllers and liquid sources through their connectors.
model TankPI
LiquidSource source(flowLevel=0.02);
PIcontinuousController piContinuous(ref=0.25);
Tank tank(area=1);
equation
connect(source.qOut, tank.qIn);
connect(tank.tActuator, piContinuous.cOut);
connect(tank.tSensor, piContinuous.cIn);
end TankPI;
The tank has four connectors: qIn for input flow, qOut for output flow, tSensor for providing fluid level measurements, and tActuator for setting the position of the valve at the outlet of the tank.
The central equation regulating the behavior of the tank is the mass balance equation, which in the current simple form assumes constant pressure. The output flow is related to the valve position by a flowGain parameter, and by a limiter that guarantees that the flow does not exceed what corresponds to the open/closed positions of the valve.
model Tank
ReadSignal tSensor "Connector, sensor reading tank level (m)";
ActSignal tActuator "Connector, actuator controlling input flow";
LiquidFlow qIn "Connector, flow (m3/s) through input valve";
LiquidFlow qOut "Connector, flow (m3/s) through output valve";
parameter Real area(unit = "m2") = 0.5;
parameter Real flowGain(unit = "m2/s") = 0.05;
parameter Real minV= 0, maxV = 10; // Limits for output valve flow
Real h(start = 0.0,fixed=true, unit = "m") "Tank level";
equation
assert(minV>=0,"minV - minimum Valve level must be >= 0 ");
der(h) = (qIn.lflow - qOut.lflow)/area; // Mass balance equation
qOut.lflow = limitValue(minV, maxV, -flowGain*tActuator.act);
tSensor.val = h;
end Tank;
A limiter function is needed in the model to reflect minimum and maximum flows through the output valve:
function limitValue
input Real pMin;
input Real pMax;
input Real p;
output Real pLim;
algorithm
pLim := if p>pMax then pMax
else if p<pMin then pMin
else p;
end limitValue;
As already stated, the tank has four connectors. These are instances of the following three connector classes:
connector ReadSignal "Reading fluid level"
Real val(unit = "m");
end ReadSignal;
connector ActSignal "Signal to actuator for setting valve position"
Real act;
end ActSignal;
connector LiquidFlow "Liquid flow at inlets or outlets"
Real lflow(unit = "m3/s");
end LiquidFlow;
The fluid entering the tank must come from somewhere. Therefore we have a liquid source component in the tank system. The flow increases sharply at time = 150 to factor of three of the previous flow level, which creates an interesting control problem that the controller of the tank has to handle.
model LiquidSource
LiquidFlow qOut;
parameter Real flowLevel = 0.02;
equation
qOut.lflow = if time > 150 then 3*flowLevel else flowLevel;
end LiquidSource;
The controller needs to be specified. We will initially choose a PI controller but later replace this by other kinds of controllers.
model PIcontinuousController
extends BaseController(K = 2, T = 10);
Real x (fixed=true) "State variable of continuous PI controller";
equation
der(x) = error/T;
outCtr = K*(error + x);
end PIcontinuousController;
Both the PI controller and the PID controller to be defined later inherit the partial controller class BaseController, containing common parameters, state variables, and two connectors: one to read the sensor and one to control the valve actuator.
partial model BaseController
parameter Real Ts(unit = "s") = 0.1 "Time period between discrete samples";
parameter Real K = 2 "Gain";
parameter Real T(unit = "s") = 10 "Time constant";
ReadSignal cIn "Input sensor level, connector";
ActSignal cOut "Control to actuator, connector";
parameter Real ref "Reference level";
Real error "Deviation from reference level";
Real outCtr "Output control signal";
equation
error = ref - cIn.val;
cOut.act = outCtr;
end BaseController;
We simulate the TankPI model and obtain the same response as for the FlatTank model, which is not surprising given that both models have the same basic equations.
simulate( TankPI, stopTime=250 )
plot( tank.h )
We define a TankPID system which is the same as the TankPI system except that the PI controller has been the replaced by a PID controller. Here we see a clear advantage of the object oriented component-based approach over the traditional modeling approach, since system components can easily be replaced and changed in a plug-and-play manner.
The tank system with the PI controller replaced by a PID controller.
The Modelica class declaration for the TankPID system appears as follows:
model TankPID
LiquidSource source(flowLevel = 0.02);
PIDcontinuousController pidContinuous(ref = 0.25);
Tank tank(area = 1);
equation
connect(source.qOut, tank.qIn);
connect(tank.tActuator, pidContinuous.cOut);
connect(tank.tSensor, pidContinuous.cIn);
end TankPID;
We create a PIDcontinuousController class in Modelica containing the three defining equations:
model PIDcontinuousController
extends BaseController(K = 2, T = 10);
Real x(fixed=true); // State variable of continuous PID controller
Real y; // State variable of continuous PID controller
equation
der(x) = error/T;
y = T*der(error);
outCtr = K*(error + x + y);
end PIDcontinuousController;
We simulate the tank model once more but now including the PID controller:
simulate( TankPID, stopTime=250 )
plot( tank.h )
Two connected tanks with PI controllers and a source for liquid connected to the first tank.
The Modelica model TanksConnectedPI corresponding to Figure 3 appears as follows:
model TanksConnectedPI
LiquidSource source(flowLevel = 0.02);
Tank tank1(area = 1);
Tank tank2(area = 1.3);
PIcontinuousController piContinuous1(ref = 0.25);
PIcontinuousController piContinuous2(ref = 0.4);
equation
connect(source.qOut,tank1.qIn);
connect(tank1.tActuator,piContinuous1.cOut);
connect(tank1.tSensor,piContinuous1.cIn);
connect(tank1.qOut,tank2.qIn);
connect(tank2.tActuator,piContinuous2.cOut);
connect(tank2.tSensor,piContinuous2.cIn);
end TanksConnectedPI;
We simulate the connected tank system. We clearly see the tank level responses of the first and second tank to the changes in fluid flow from the source, where the response from the first tank of course appears earlier in time than the response of the second tank.
simulate( TanksConnectedPI, stopTime=400 )
plot( {tank1.h,tank2.h} )