modelica / ModelicaStandardLibrary

Free (standard conforming) library to model mechanical (1D/3D), electrical (analog, digital, machines), magnetic, thermal, fluid, control systems and hierarchical state machines. Also numerical functions and functions for strings, files and streams are included.
https://doc.modelica.org
BSD 3-Clause "New" or "Revised" License
452 stars 165 forks source link

Finding minimum and maximum values over time #4414

Closed ceraolo closed 3 weeks ago

ceraolo commented 4 weeks ago

Finding minimum and maximum of a signal over time has been discussed along the time, see: https://github.com/modelica/ModelicaSpecification/issues/109 and https://stackoverflow.com/questions/30732774/modelica-compute-minimum-maximum-of-continuous-variable-over-time.

It seems to me that no final solution was found. In particular It results to me that all solutions proposed in the above stackoverflow ticket do not work in OpenModelica, because of being ill-conditioned or invalid Modelica code.

I think the two models I propose below for min(t) and max(t) work well. I've checked them with Dymola and Openmodelica: they simulate very fast and do not show any sign of numerical difficulties.

In case it is agreed upon, I could create a PR to insert them somewhere in MSL Blocks.

The following tiny package contains the two blocks TimeMin and TimeMax, and a "Test" model, to show their behaviour. The following screenshot clarifies. a

package FindMinMax
  model Test
    TimeMax timeMax(y_start=1)
                    annotation (
      Placement(transformation(origin={30,30},   extent = {{-10, -10}, {10, 10}})));
    Modelica.Blocks.Sources.RealExpression signal(y = time*sin(time/one)) annotation (
      Placement(transformation(origin = {-25, 0}, extent = {{-13, -10}, {13, 10}})));
    TimeMin timeMin(y_start=-1)
      annotation (Placement(transformation(extent={{20,-40},{40,-20}})));
    Modelica.Units.SI.Time one=1;
  equation 
    connect(signal.y, timeMax.u) annotation (
      Line(points={{-10.7,0},{8,0},{8,30},{18,30}},
                                        color = {0, 0, 127}));
    connect(timeMin.u, signal.y) annotation (Line(points={{18,-30},{8,-30},{8,0},{
            -10.7,0}}, color={0,0,127}));
    annotation (
      experiment(
        StopTime=30,
        Interval=0.04,
        Tolerance=1e-06), Diagram(coordinateSystem(extent={{-60,-60},
              {60,60}})));
  end Test;

  block TimeMax
    extends Modelica.Blocks.Interfaces.SISO(y(start=y_start));
    parameter Real y_start=0 "Initial or guess value of output";
    Boolean derpos(start=true);
    Real prevy;
  equation 
    when der(u)>0 then
      prevy=pre(y);
      derpos=true;
    elsewhen der(u)<0 then
      prevy=pre(y);
      derpos=false;
    end when;

    if derpos then
      y=max(u,prevy);
    else
      y=prevy;
    end if;
    annotation (Icon(graphics={                                                                           Text(textColor = {160, 160, 164}, extent={{-100,32},
                {100,-26}},                                                                                                                                                  textString = "max(t)")}),
        Documentation(info="<html>
<p><span style=\"font-size: 9pt;\">Outputs the maximum value over time during the simulation.</span></p>
</html>",   revisions="<html>
<ul>
<li><em>June, 2024   </em>
       by Massimo Ceraolo <br>initially implemented<br>
</li>
</html>"));
  end TimeMax;

  block TimeMin
    extends Modelica.Blocks.Interfaces.SISO(y(start=y_start));
    parameter Real y_start=0 "Initial or guess value of output";
    Boolean derNeg(start=true);
    Real prev_y;
  equation 
    when der(u)<0 then
      prev_y=pre(y);
      derNeg=true;
    elsewhen der(u)>0 then
      prev_y=pre(y);
      derNeg=false;
    end when;

    if derNeg then
      y=min(u,prev_y);
    else
      y=prev_y;
    end if;
    annotation (Icon(graphics={   Text(textColor={160,160,
                164},   extent={{-100,30},
                {100,-28}},
            textString="min(t)")}), Documentation(info="<html>
<p><span style=\"font-size: 9pt;\">Outputs the minimum value over time during the simulation.</span></p>
</html>",   revisions="<html>
<ul>
<li><em>June, 2024   </em>
       by Massimo Ceraolo <br>initially implemented<br>
</li>
</html>"));
  end TimeMin;
  annotation (uses(Modelica(version="4.0.0")));
end FindMinMax;
beutlich commented 4 weeks ago

It seems to me that no final solution was found.

It actually is available since end of 2017: See https://github.com/modelica/ModelicaStandardLibrary/discussions/4381 for an alternative to the derivative-based or sample-based implementations.

ceraolo commented 4 weeks ago

I've had a look at the link.

It seems to me that is shows something different than this ticket's proposal: I see in the comment: here is one drawback, too: LastLib is not tool-agnostic and requires a tool-specific redeclaration of LastLib.Last for the imported FMU.

My implementation, instead, is tool-agnostic, does not require any lib, and is totally unrelated to FMI. It would offer two very simple blocks directly inside MSL. And, looking at the several discussions out there, its seems there is much need for them.

beutlich commented 3 weeks ago

It seems to me that is shows something different than this ticket's proposal

I am afraid I cannot follow. It seems to me that you did not check the references given by #4381. #4381 is a showcase to compensate the drawbacks of the existing sample-based implementation of Modelica.Blocks.Math.SignalExtrema (many artificial time-events) or derivative-based implementation of Modelica.Blocks.Math.ContinuousSignalExtrema (artificial state variable or need for approximation).

beutlich commented 3 weeks ago

Closing in favour of the available blocks or alternatives:

  1. Modelica.Blocks.Math.SignalExtrema: sample-based
  2. Modelica.Blocks.Math.ContinuousSignalExtrema: derivative-based
  3. MinimumTest.Minimum via LastLib: event-free and derivative-free