LimIntegrator

block LimIntegrator "Integrator with limited value of the output and optional reset"
    import Modelica.Blocks.Types.Init;

    parameter Real k(unit = "1") = 1 "Integrator gain";
    parameter Real outMax(start = 1) "Upper limit of output";
    parameter Real outMin = -outMax "Lower limit of output";
    parameter Boolean use_reset = false "=true, if reset port enabled"
        annotation (
            Evaluate = true,
            HideResult = true,
            choices(checkBox = true));
    parameter Boolean use_set = false "=true, if set port enabled and used as reinitialization value when reset"
        annotation (
            Dialog(enable = use_reset),
            Evaluate = true,
            HideResult = true,
            choices(checkBox = true));
    parameter Modelica.Blocks.Types.Init initType = Modelica.Blocks.Types.Init.InitialState "Type of initialization (1: no init, 2: steady state, 3/4: initial output)"
        annotation (
            Evaluate = true,
            Dialog(group = "Initialization"));
    parameter Boolean limitsAtInit = true "= false, if limits are ignored during initialization (i.e., der(y)=k*u)"
        annotation (
            Evaluate = true,
            Dialog(group = "Initialization"));
    parameter Real y_start = 0 "Initial or guess value of output (must be in the limits outMin .. outMax)"
        annotation (Dialog(group = "Initialization"));
    parameter Boolean strict = false "= true, if strict limits with noEvent(..)"
        annotation (
            Evaluate = true,
            choices(checkBox = true),
            Dialog(tab = "Advanced"));

    extends Interfaces.SISO(y(start = y_start));

    Modelica.Blocks.Interfaces.BooleanInput reset if use_reset "Optional connector of reset signal"
        annotation (Placement(transformation(
            extent = {
                {-20, -20}, 
                {20, 20}},
            rotation = 90,
            origin = {60, -120})));
    Modelica.Blocks.Interfaces.RealInput set if use_reset and use_set "Optional connector of set signal"
        annotation (Placement(transformation(
            extent = {
                {-20, -20}, 
                {20, 20}},
            rotation = 270,
            origin = {60, 120})));
protected
    Modelica.Blocks.Interfaces.BooleanOutput local_reset annotation (HideResult = true);
    Modelica.Blocks.Interfaces.RealOutput local_set annotation (HideResult = true);
initial equation
    if initType == Init.SteadyState then 
        der(y) = 0;
    elseif initType == Init.InitialState or initType == Init.InitialOutput then 
        y = y_start;
    end if;
equation
    if use_reset then 
        connect(reset,local_reset);
        if use_set then 
            connect(set,local_set);
        else 
            local_set = y_start;
        end if;
        when local_reset then 
            reinit(y, if local_set < outMin then outMin else if outMax < local_set then outMax else local_set);
        end when;
    else 
        local_reset = false;
        local_set = 0;
    end if;
    if initial() and not limitsAtInit then 
        der(y) = k * u;
        assert(outMin - 0.001 * abs(outMax - outMin) <= y and y <= outMax + 0.001 * abs(outMax - outMin), "LimIntegrator: During initialization the limits have been ignored.\nHowever, the result is that the output y is not within the required limits:\n  y = " + String(y) + ", outMin = " + String(outMin) + ", outMax = " + String(outMax));
    elseif strict then 
        der(y) = noEvent(if y < outMin and k * u < 0 or outMax < y and 0 < k * u then 0 else k * u);
    else 
        der(y) = if y < outMin and k * u < 0 or outMax < y and 0 < k * u then 0 else k * u;
    end if;

    annotation (
        Documentation(info = "<html>\n<p>\nThis blocks computes <strong>y</strong> as <em>integral</em>\nof the input <strong>u</strong> multiplied with the gain <em>k</em>. If the\nintegral reaches a given upper or lower <em>limit</em> and the\ninput will drive the integral outside of this bound, the\nintegration is halted and only restarted if the input drives\nthe integral away from the bounds.\n</p>\n\n<p>\nIt might be difficult to initialize the integrator in steady state.\nThis is discussed in the description of package\n<a href=\"modelica://Modelica.Blocks.Continuous#info\">Continuous</a>.\n</p>\n\n<p>\nIf parameter <strong>limitAtInit</strong> = <strong>false</strong>, the limits of the\nintegrator are removed from the initialization problem which\nleads to a much simpler equation system. After initialization has been\nperformed, it is checked via an assert whether the output is in the\ndefined limits. For backward compatibility reasons\n<strong>limitAtInit</strong> = <strong>true</strong>. In most cases it is best\nto use <strong>limitAtInit</strong> = <strong>false</strong>.\n</p>\n<p>\nIf the <em>reset</em> port is enabled, then the output <strong>y</strong> is reset to <em>set</em>\nor to <em>y_start</em> (if the <em>set</em> port is not enabled), whenever the <em>reset</em>\nport has a rising edge.\n</p>\n</html>"),
        Icon(
            coordinateSystem(
                preserveAspectRatio = true,
                extent = {
                    {-100, -100}, 
                    {100, 100}}),
            graphics = {
                Line(
                    points = {
                        {-80, 78}, 
                        {-80, -90}},
                    color = {192, 192, 192}), 
                Polygon(
                    points = {
                        {-80, 90}, 
                        {-88, 68}, 
                        {-72, 68}, 
                        {-80, 90}},
                    lineColor = {192, 192, 192},
                    fillColor = {192, 192, 192},
                    fillPattern = FillPattern.Solid), 
                Line(
                    points = {
                        {-90, -80}, 
                        {82, -80}},
                    color = {192, 192, 192}), 
                Polygon(
                    points = {
                        {90, -80}, 
                        {68, -72}, 
                        {68, -88}, 
                        {90, -80}},
                    lineColor = {192, 192, 192},
                    fillColor = {192, 192, 192},
                    fillPattern = FillPattern.Solid), 
                Line(
                    points = DynamicSelect({
                        {-80, -80}, 
                        {20, 20}, 
                        {80, 20}}, if use_reset then {{-80, -80}, {20, 20}, {60, 20}, {60, -80}, {80, -60}} else {{-80, -80}, {20, 20}, {80, 20}}),
                    color = {0, 0, 127}), 
                Text(
                    extent = {
                        {0, -10}, 
                        {60, -70}},
                    lineColor = {192, 192, 192},
                    textString = "I"), 
                Text(
                    extent = {
                        {-150, -150}, 
                        {150, -110}},
                    textString = "k=%k"), 
                Line(
                    visible = strict,
                    points = DynamicSelect({
                        {20, 20}, 
                        {80, 20}}, if use_reset then {{20, 20}, {60, 20}} else {{20, 20}, {80, 20}}),
                    color = {255, 0, 0}), 
                Line(
                    visible = use_reset,
                    points = {
                        {60, -100}, 
                        {60, -80}},
                    color = {255, 0, 255},
                    pattern = LinePattern.Dot), 
                Text(
                    visible = use_reset,
                    extent = {
                        {-28, -62}, 
                        {94, -86}},
                    textString = "reset")}),
        Diagram(
            coordinateSystem(
                preserveAspectRatio = true,
                extent = {
                    {-100, -100}, 
                    {100, 100}}),
            graphics = {
                Rectangle(
                    extent = {
                        {-60, 60}, 
                        {60, -60}},
                    lineColor = {0, 0, 255}), 
                Text(
                    extent = {
                        {-54, 46}, 
                        {-4, -48}},
                    textString = "lim"), 
                Line(
                    points = {
                        {-100, 0}, 
                        {-60, 0}},
                    color = {0, 0, 255}), 
                Line(
                    points = {
                        {60, 0}, 
                        {100, 0}},
                    color = {0, 0, 255}), 
                Text(
                    extent = {
                        {-8, 60}, 
                        {60, 2}},
                    textString = "k"), 
                Text(
                    extent = {
                        {-8, -2}, 
                        {60, -60}},
                    textString = "s"), 
                Line(points = {
                    {4, 0}, 
                    {46, 0}})}));
end LimIntegrator;