Skip to content

Invalid smooth() statement in SlewRateLimiter #4723

@casella

Description

@casella

Description of the problem

The core equation of the Modelica.Blocks.Nonlinear.SlewRateLimiter block reads:

if strict then
der(y) = smooth(1, (if noEvent(val<Falling) then Falling else if noEvent(val>Rising) then Rising else val));
else
der(y) = if val<Falling then Falling else if val>Rising then Rising else val;
end if;

This is likely inspired by the code of the Limiter block

if strict then
if homotopyType == Types.LimiterHomotopy.NoHomotopy then
y = smooth(0, noEvent(if u > uMax then uMax else if u < uMin then uMin else u));
else
y = homotopy(actual = smooth(0, noEvent(if u > uMax then uMax else if u < uMin then uMin else u)),
simplified=simplifiedExpr);
end if;
else
if homotopyType == Types.LimiterHomotopy.NoHomotopy then
y = smooth(0,if u > uMax then uMax else if u < uMin then uMin else u);
else
y = homotopy(actual = smooth(0,if u > uMax then uMax else if u < uMin then uMin else u),
simplified=simplifiedExpr);
end if;
end if;

In the case of the Limiter block, the strict = true version makes sense when the block output should never exceed the upper and lower limit, e.g. to avoid division-by-zero, sqrt-neg-number or log-neg-number issues. Regular event detection necessarily implies computing out-of-bounds values during the solver iterations to find the root of the zero-crossing functions, so it is necessary to disable event detection to avoid that; luckily, since the noEvent expression is continuous, this can be done without too much damage for the ODE integrator.

The SlewRateLimiter block also has a strict = true version that disables event generation and introduces the smooth() operator. Unfortunately, in this case the expression on the RHS of the differential equation is not continuously differentiable, as it is incorrectly declared by the smooth() operator; in fact, when the input undergoes large enough step changes, that expression is discontinuous, as demonstrated by this simple MWE:

model TestSlewRate
  Modelica.Blocks.Nonlinear.SlewRateLimiter slewRateLimiter(Rising = 300, Td = 0.03);
  Modelica.Blocks.Sources.Step step(height = 1e5, startTime = 100);
equation
  connect(step.y, slewRateLimiter.u);
annotation(
    uses(Modelica(version = "4.1.0")),
  experiment(StartTime = 0, StopTime = 500, Tolerance = 1e-06, Interval = 10));
end TestSlewRate;
Image

As F. Cellier very clearly discusses in Chapter 9.2 of the book Continuous System Modelling, relying on error estimation and step-size adaption algorithms to handle discontinuous variables without explicit event handling is not a good idea and can lead to completely wrong results. On the other hand, if the output of the derivative block has a derivative that slightly exceeds the given limits during the iterations to find the roots of the zc-function generating the event, nothing harmful happens.

Suggested solution

Based on the analyis reported above I would deprecate the strict = true behaviour, by moving the strict parameter to a Deprecated tab and changing the code to

der(y) = if val<Falling then Falling else if val>Rising then Rising else val; 
if strict then 
   assert("strict = true is deprecated and has no effect in this version of the library; the strict parameter will be removed in future versions of the library", AssertionLevel.warning);
end if; 

Metadata

Metadata

Labels

L: BlocksIssue addresses Modelica.BlocksbugCritical/severe issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions