Opened 3 years ago

Closed 2 years ago

#6195 closed defect (fixed)

Assert triggered incorrectly on variable computed with conditional expression

Reported by: casella Owned by: AnHeuermann
Priority: critical Milestone: 1.19.0
Component: Run-time Version:
Keywords: Cc: mwetter

Description (last modified by casella)

Please check Buildings.Electrical.AC.OnePhase.Loads.Examples.ParallelLoads. The model fails at runtime with

LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.
assert            | warning | The following assertion has been violated at time 0.700200
|                 | |       | load_y.y >= 0.0 and load_y.y <= 1.0000000001
assert            | error   | The power load fraction P (input of the model) must be within [0,1]

Now, load.y is computed with the following equation

  y = offset + (if time < startTime then 0 else if time < (startTime +
    duration) then (time - startTime)*height/duration else height);

and the upper limit of the assert is the value 1+eps with eps = 1e-10 defined in Buildings.Electrical.Interfaces.Load. In order to run the simulation successfully, one needs to increase it to 1e-2. What is interesting is that the transient computing at that point result is apparently not exceeding 1 by more than a much, much smaller value.

This raises the obvious question: why is this assert triggered in the first place?

My rough guess is that the assert is not checked on the accepted step value, which would be within the limits with a very tight tolerance, but is somehow being applied to the value of y during the process of finding the zero of the zero-crossing function, incorrectly aborting it.

I would recommend to move the assertion checking downstream of the zero-crossing solution, otherwise this kind of errors could happen in a number of cases.

Change History (12)

comment:1 Changed 3 years ago by casella

  • Milestone changed from 1.17.0 to 1.18.0

comment:2 Changed 3 years ago by AnHeuermann

My rough guess is that the assert is not checked on the accepted step value, which would be within the limits with a very tight tolerance, but is somehow being applied to the value of y during the process of finding the zero of the zero-crossing function, incorrectly aborting it.

This could very well be the case. Should be the first place to check.

Probably also roughly related to #6201.

Last edited 3 years ago by AnHeuermann (previous) (diff)

comment:3 follow-up: Changed 3 years ago by sjoelund.se

Isn't noEvent needed here? If-expressions cannot be used reliably since they will not switch until an event: https://specification.modelica.org/master/equations.html#events-and-synchronization

comment:4 in reply to: ↑ 3 Changed 3 years ago by casella

Replying to sjoelund.se:

Isn't noEvent needed here? If-expressions cannot be used reliably since they will not switch until an event: https://specification.modelica.org/master/equations.html#events-and-synchronization

This would be the case if we had something like y = if x > 0 then sqrt(x) else 0, where the expression in the first branch becomes invalid when x becomes negative, which could happen while the solver is looking for the exact time of the event, when the zero-crossing function vanishes.

Notice that using noEvent has a cost in terms of computational workload, because we are passing the solver a function which is not even C1, hence all the error estimations assuming a smooth function will not be very reliable. It is a bit of a brute-force solution.

However, this is not the case here. There is nothing wrong in the load going slightly above 1 during the solution process. I think the assert should be applied only on accepted steps.

Seems to me very much related to #6372. @AnHeuermann, what do you think?

comment:5 Changed 3 years ago by casella

  • Description modified (diff)

comment:6 Changed 3 years ago by casella

  • Cc mwetter added

comment:7 Changed 3 years ago by casella

  • Milestone 1.18.0 deleted

Ticket retargeted after milestone closed

comment:8 Changed 2 years ago by casella

  • Milestone set to 1.19.0

comment:9 Changed 2 years ago by casella

@AnHeuermann, some more additional information to pinpoint the issue.

If I simulate with 20 intervals instead of 500, I get:

The following assertion has been violated at time 0.750000
load_y.y >= 0.0 and load_y.y <= 1.0000000001
The power load fraction P (input of the model) must be within [0,1]

So, what seems to be happening is that the load reaches "almost" 1 at time 0.7, but then another step is taken without the event handling being triggered.

Even more interesting is to see what happens with 22 intervals. The following steps are reported: t = 0.045454, t = 0.090909, t = 0.1363636, 0.1818181 (i.e. 1/22, 2/22, 3/22, 4/22). Then, a first event is triggered at t = 0.2, after which we resume with 5/22, 6/22, etc., until t = 15/22 = 0.686868. At this point the simulation aborts with

The following assertion has been violated at time 0.727273
load_y.y >= 0.0 and load_y.y <= 1.0000000001
The power load fraction P (input of the model) must be within [0,1]
Integrator attempt to handle a problem with a called assert.
The following assertion has been violated at time 0.704545
load_y.y >= 0.0 and load_y.y <= 1.0000000001
The power load fraction P (input of the model) must be within [0,1]
model terminate | Simulation terminated by an assert at time: 0.704545

What happens here is pretty clear: the next step at t = 16/22 = 0.727273 is attempted, and of course the assertions is violated because, due to the large time step, the step is wey beyond the event time, i.e. t = 0.7.

Then the solver halves the interval, so it tries (15+1/2)/22 = 0.704545, which is still to far from t = 0.7 to stay within y <= 1.0000000001, and then it just quits.

It seems that we are trying to identify the event time by bisection, which is not particularly efficient but is just fine. However, we should let the solver iterate as many times as it needs to find out the event time without failing. I don't really know why only one iteration is allowed here before giving up.

In fact, it is fine to check for assertions during these iterations, in order to discard invalid points, but I wouldn't even report them on stdout, as they can generate anxiety on the end-user (like the model is numerically problematic or something), when this is absolutely not the case.

Last edited 2 years ago by casella (previous) (diff)

comment:10 Changed 2 years ago by casella

  • Resolution set to fixed
  • Status changed from new to closed

Fixed in PR #8118.

comment:11 Changed 2 years ago by casella

  • Resolution fixed deleted
  • Status changed from closed to reopened

Reopened because of some regressions, see #6195 on GitHub.

comment:12 Changed 2 years ago by casella

  • Resolution set to fixed
  • Status changed from reopened to closed

Fixed for now.

Note: See TracTickets for help on using tickets.