Skip to content

Conversation

henrikt-ma
Copy link
Collaborator

In #3640 it was noted that both delay and spatialDistribution should be event-generating, so that they can preserve discontinuities. This is particularly useful for dealing with discrete-valued signals, and opens up for also supporting non-Real signals.

We have now a test implementation of event-generating delay, and I'd say it works great. At the moment, describing delay accordingly is the only thing this PR does. For the much less frequently used spatialDistribution we don't have a test implementation at the moment, but would be ready to also go ahead and change the specification based on test implementations in other tools.

Some questions to think about when making this change:

  • Do we need to give more detailed semantics than before?
  • Do we also need to give expression variability rules for constant and evaluable expressions? For example, this could make delay(1.0, delayTime, delayMax) a constant expression.
  • Are we ready to maintain the pseudo-code for spatialDistribution if we were to add event generating semantics?

@gkurzbach
Copy link
Collaborator

@henrikt-ma You looking for an implementation in other tools: in SimulationX both delay and spatialDistribution reproduce events, as there were present in the input, unless they are called within noEvent(). delay() is also allowed for non-Real input (but strings are not supported).

Copy link
Collaborator

@gkurzbach gkurzbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fine for me.

@HansOlsson
Copy link
Collaborator

For me there are two different use cases:

  • delay for Boolean, Integer or enumeration. To me they should generate events, and preserve the type of the operand (the latter requires the first outside of noEvent); as in Don't require discrete-time Real variables to be assigned in when-clauses #3247 (comment)
  • delay for Reals with discontinuities; here I see a number of challenges (I will list them later), and to me the simplest solution is to allow (but not require) it to generate events to handle discontinuities.

I don't see a need to generalize spatialDistribution to non-Reals yet; but I see that the second case also applies for spatialDistribution.

The issues with events for Reals as I see it are:

  • Currently those events are not observable within the Modelica language. Thus saying that tools can generate events seems like a good compromise of compatibility and efficiency.
  • The discontinuities can be of varying size, at some size they are small enough that they can be ignored.
  • Whether a "large" discontinuity matters also depend on how it impacts the rest of the system; sometimes even a large discontinuity might be insignificant.
  • Additionally, it is possible to have a signal that is a sum of discontinuity and a continuous signal. Saying that it is non-discrete time and thus shouldn't generate events, seem counter-intuitive - but it could also be a minor discontinuity from discretization that should be ignored. This is similar to the idea of "input smoothing".

If we later refine the delay-operator's description we might revisit this.

@henrikt-ma
Copy link
Collaborator Author

I don't see a need to generalize spatialDistribution to non-Reals yet; but I see that the second case also applies for spatialDistribution.

While I don't have a test implementation for this, I don't see that it would be more difficult than supporting it for delay, and then I think it should be supported for the sake of completeness.

@henrikt-ma
Copy link
Collaborator Author

The issues with events for Reals as I see it are:

  • Currently those events are not observable within the Modelica language. Thus saying that tools can generate events seems like a good compromise of compatibility and efficiency.
  • The discontinuities can be of varying size, at some size they are small enough that they can be ignored.

If the Real expression being delayed is discrete-time, I would say it is a very bad idea to not preserve the discrete-time variability. For tiny discontinuities this leaves two options, namely to preserve also the tiny discontinuity, or to completely ignore the change in the value. I can see that the latter can be desirable in some situations, but I believe that this can be left as a tool optimization that we don't have to describe in more detail in the specification.

  • Whether a "large" discontinuity matters also depend on how it impacts the rest of the system; sometimes even a large discontinuity might be insignificant.

Yes, so it is important that a tool doesn't forget to support noEvent also for delay and spatialDistribution.

  • Additionally, it is possible to have a signal that is a sum of discontinuity and a continuous signal. Saying that it is non-discrete time and thus shouldn't generate events, seem counter-intuitive - but it could also be a minor discontinuity from discretization that should be ignored. This is similar to the idea of "input smoothing".

It was not my intention that the text should be interpreted this way. The way I see it, the key change here is that delay (and spatialDistribution) will preserve discrete-time expression variability, and in order to be able to do that we need to make these operators event-generating.

@henrikt-ma
Copy link
Collaborator Author

@henrikt-ma You looking for an implementation in other tools: in SimulationX both delay and spatialDistribution reproduce events, as there were present in the input, unless they are called within noEvent(). delay() is also allowed for non-Real input (but strings are not supported).

Since both you and @HansOlsson bring up the non-Real cases, I will add this to the PR.

@HansOlsson
Copy link
Collaborator

I don't see a need to generalize spatialDistribution to non-Reals yet; but I see that the second case also applies for spatialDistribution.

While I don't have a test implementation for this, I don't see that it would be more difficult than supporting it for delay, and then I think it should be supported for the sake of completeness.

My experience with handling spatialDistribution is that it is already quite complicated to get a good result; and since I see no real use case of non-Real spatialDistribution I think that will be an excessive burden for something for no real gain.

@henrikt-ma
Copy link
Collaborator Author

I just realized that delay is becoming the first event-generating function which is automatically vectorized. Perhaps we should consider whether also some of the other event-generating functions should do the same?

@HansOlsson
Copy link
Collaborator

The issues with events for Reals as I see it are:

  • Currently those events are not observable within the Modelica language. Thus saying that tools can generate events seems like a good compromise of compatibility and efficiency.
  • The discontinuities can be of varying size, at some size they are small enough that they can be ignored.

If the Real expression being delayed is discrete-time, I would say it is a very bad idea to not preserve the discrete-time variability. For tiny discontinuities this leaves two options, namely to preserve also the tiny discontinuity, or to completely ignore the change in the value. I can see that the latter can be desirable in some situations, but I believe that this can be left as a tool optimization that we don't have to describe in more detail in the specification.

For me there are some issues:

  • Instead of specifying one thing, and relying on tools to optimize to get the desired result, I think it is better to be clear about the allowed behavior.
  • Discrete-time variability of Reals as currently defined in Modelica is quite restrictive; so in many cases the Real expression that would seem "discrete-time" isn't. It may still makes sense to propagate the discontinuity, regardless of whether the expression is formally discrete-time or not.
  • Looking at MSL I don't see any uses of delay for Reals that are sort of discrete-time (except for something that is really an enumeration).

Since it is a trivial to write cases where generating events for discrete variables will cause an infinite explosion in smaller and smaller discontinuities, I think we should be careful and not add something "just in case".

  • Additionally, it is possible to have a signal that is a sum of discontinuity and a continuous signal. Saying that it is non-discrete time and thus shouldn't generate events, seem counter-intuitive - but it could also be a minor discontinuity from discretization that should be ignored. This is similar to the idea of "input smoothing".

It was not my intention that the text should be interpreted this way. The way I see it, the key change here is that delay (and spatialDistribution) will preserve discrete-time expression variability, and in order to be able to do that we need to make these operators event-generating.

I can understand that delay of a non-Real needs to be a discrete-time expression (even if the delayTime is continuous-time) to not generate non-discrete-time Integer (etc), but I don't see a similar need for Reals.

Copy link
Collaborator

@HansOlsson HansOlsson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As indicated should not require an event for Reals, and support non-Real.
Could change to "next significant discontinuous change" and saying that changes for non-Reals are always significant.


These expression for \lstinline!div!, \lstinline!ceil!, \lstinline!floor!, and \lstinline!integer! are event generating expression.
The event generating expression for \lstinline!mod(x,y)! is \lstinline!floor(x/y)!, and for \lstinline!rem(x,y)! it is \lstinline!div(x,y)! -- i.e., events are not generated when \lstinline!mod! or \lstinline!rem! changes continuously in an interval, but when they change discontinuously from one interval to the next.
The event generating expression for \lstinline!delay! is the time remaining until the next discontinuity in the operator value.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The event generating expression for \lstinline!delay! is the time remaining until the next discontinuity in the operator value.
The event generating expression for \lstinline!delay! is the time remaining until the next significant discontinuous change in the operator value.'
If the delayed expression is non-Real the change is always significant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would miss the point. When there is a discontinuous change in the operator value, no matter how small, time must have been stopped for an event. Instead, note that the current text is not stressing that even the tiniest discontinuity in expr must be preserved -- this is up to tools to interpret intelligently.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would miss the point. When there is a discontinuous change in the operator value, no matter how small, time must have been stopped for an event. Instead, note that the current text is not stressing that even the tiniest discontinuity in expr must be preserved -- this is up to tools to interpret intelligently.

I don't fully follow this:
Clearly the discontinuous change of the expression to be delayed happened during an event.
However, to me "discontinuous change in the operator value" mean a discontinuous change in the result of delay-operator (after the delay), which requires that an event was triggered.

If "discontinuous change" is intended to be interpreted somewhat loosely, I would say that it isn't clear at the moment.

And I would say that adding "significant" and stating that changes of discrete-valued types clarifies that intent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have now added a paragraph about event generation, and it includes significant in a statement about quality of implementation. OK?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@HansOlsson
Copy link
Collaborator

I just realized that delay is becoming the first event-generating function which is automatically vectorized. Perhaps we should consider whether also some of the other event-generating functions should do the same?

I would say all of them should be automatically vectorized, and see also: #3728

@henrikt-ma
Copy link
Collaborator Author

henrikt-ma commented Sep 10, 2025

How about expressing the value simply as expr(max(time.start, time - delayTime)) -- it would be an expression small enough to fit nicely as inline math? Do we need to point out that expr(time.start) shall be interpreted as just expr in the initialization problem, or is this a reason to avoid the rewrite with max?

@henrikt-ma
Copy link
Collaborator Author

  • Looking at MSL I don't see any uses of delay for Reals that are sort of discrete-time (except for something that is really an enumeration).

This is not a good reason to not let delay preserve variability when it is an event generating operator, especially considering that it isn't difficult to implement.

I can understand that delay of a non-Real needs to be a discrete-time expression (even if the delayTime is continuous-time) to not generate non-discrete-time Integer (etc), but I don't see a similar need for Reals.

But this is not the way it will work for non-Real signals either. A non-discrete-time Integer expression will not become discrete time just because you send it through delay. Discrete-time variability shall neither be introduced nor destroyed by delay, only preserved (as long as you don't wrap the whole thing in noEvent(…)).

@HansOlsson
Copy link
Collaborator

How about expressing the value simply as expr(max(time.start, time - delayTime)) -- it would be an expression small enough to fit nicely as inline math? Do we need to point out that expr(time.start) shall be interpreted as just expr in the initialization problem, or is this a reason to avoid the rewrite with max?

Interesting question - it's more complicated, at least Dymola seem to x.start for delay(x, 1) - and not the value of x at the start-time. I think that should be discussed separately.

@henrikt-ma
Copy link
Collaborator Author

While I don't have a test implementation for this, I don't see that it would be more difficult than supporting it for delay, and then I think it should be supported for the sake of completeness.

My experience with handling spatialDistribution is that it is already quite complicated to get a good result; and since I see no real use case of non-Real spatialDistribution I think that will be an excessive burden for something for no real gain.

I am just extrapolating from what I learnt when working on delay. Indeed, the continuous-time Real case is a challenge to implement with good results, but if we allow spatialDistribution to preserve discontinuity I think all the heavy lifting will already have been done by the time this has been implemented for Real. I believe the rest needed to support non-Real types would be a simple matter of carrying over a few simple ideas from delay?

@henrikt-ma
Copy link
Collaborator Author

henrikt-ma commented Sep 10, 2025

Interesting question - it's more complicated, at least Dymola seem to x.start for delay(x, 1) - and not the value of x at the start-time. I think that should be discussed separately.

OK. System Modeler is also making the (a little bit stretched) interpretation that x(time.start) actually means x.start in the case of delay semantics.

Edit: Reported this as #3733.

@HansOlsson
Copy link
Collaborator

HansOlsson commented Sep 10, 2025

But this is not the way it will work for non-Real signals either. A non-discrete-time Integer expression will not become discrete time just because you send it through delay. Discrete-time variability shall neither be introduced nor destroyed by delay, only preserved (as long as you don't wrap the whole thing in noEvent(…)).

Discrete-time Integer and discrete-time Real variables are quite different, and thus many Real expression that only change at events are not formally discrete-time.
Consider:

  discrete Integer i1=integer(time), final i2=-i1; // Both i1 and i2 are discrete-time
  discrete Real r1, final r2=-r1; // Error, r2 cannot be discrete as it isn't assigned in a when-clause

Additionally, I thought we wanted delay(i1, 2+sin(time), 3) to be discrete-time - even though the second argument is continuous time.

Added So a Real expression are often sort of discrete-time without being formally discrete-time; that's not the case for Integer expressions.

@henrikt-ma
Copy link
Collaborator Author

Additionally, I thought we wanted delay(i1, 2+sin(time), 3) to be discrete-time - even though the second argument is continuous time.

Yes, absolutely! The variability of i1 shall be preserved. The proposed rule should capture this:

Unless inside \lstinline!noEvent!: \lstinline!delay($x$, $\ldots$)!, if $x$ is a discrete-time expression.

Added So a Real expression are often sort of discrete-time without being formally discrete-time; that's not the case for Integer expressions.

I know this is the case, but to me there is only one natural way of defining the variability semantics of delay, and that is that the variability has nothing to do with the type of the delayed expression. Natural and easy to implement, not necessarily a super-useful feature considering the overall poor support for discrete-time Real we have due to #3247.

@HansOlsson
Copy link
Collaborator

Added So a Real expression are often sort of discrete-time without being formally discrete-time; that's not the case for Integer expressions.

I know this is the case, but to me there is only one natural way of defining the variability semantics of delay, and that is that the variability has nothing to do with the type of the delayed expression. Natural and easy to implement, not necessarily a super-useful feature considering the overall poor support for discrete-time Real we have due to #3247.

To me the important part for Real expressions is that discontinuities are propagated (with an event that cannot be observed internally), not that the call is discrete.
For non-Reals it has to have discrete variability - but I view those as different issues.

@henrikt-ma
Copy link
Collaborator Author

To me the important part for Real expressions is that discontinuities are propagated (with an event that cannot be observed internally), not that the call is discrete.

Oh, so you mean that we should state more clearly that discontinuities should be preserved in the non-discrete-time case? Clarifying that wouldn't sound like a bad idea to me.

@casella
Copy link
Collaborator

casella commented Sep 16, 2025

@henrikt-ma You looking for an implementation in other tools: in SimulationX both delay and spatialDistribution reproduce events, as there were present in the input, unless they are called within noEvent(). delay() is also allowed for non-Real input (but strings are not supported).

Also OMC reproduces events that were present at the input of delay() and spatialDistribution() when they reach the output. In principle, adding noEvent() should suppress them, but that's not implemented correctly yet. @AnHeuermann, this will need to be fixed.

To me the important part for Real expressions is that discontinuities are propagated (with an event that cannot be observed internally), not that the call is discrete.

Oh, so you mean that we should state more clearly that discontinuities should be preserved in the non-discrete-time case? Clarifying that wouldn't sound like a bad idea to me.

To me this was the primary motivation to add events to delay() and spatialDistribution(). If I apply a Real (non-discrete) input with a step change to a pure time delay, I expect to see the same discontinuous step change at the outlet, not some smoothed-out, noEvent() version of it.

Copy link
Collaborator

@casella casella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines +1639 to +1640
\item
\lstinline!delay($x$, $\ldots$)! where $x$ is a parameter expression.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need delay of a parameter expression to be a parameter expression, and especially have an exception so that delay(p, 1+time/2) is a parameter expression despite the time-argument?

As a user or developer I would wonder what the practical applications are of this; and I simply don't see any cases where users want to delay a parameter-expression.

Note that if we add some variant of more advanced initialization as in #3733 (comment) this will no longer hold; even when all of the arguments are parameter-expressions.

A simpler alternative is to add delay to the same exception-list as initial, terminal, etc (since delay has an implicit time-dependency).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For delay I see two sets of reasons to not add the parameter-rule for variability (but the rest of the PR is good).

  1. When understanding delay it makes sense to view it as having time as implicit argument, and the explanation even that makes that clear by writing that it evaluates to expr(time-timeDelay).

For fixed time-delays we can even compare it to spatialDistribution where we have to use an explicit dependency:

model CC3
  extends Modelica.Mechanics.Rotational.Examples.CoupledClutches;
  Real J1_w_del=delay(J1.w, 0.1);
  Real J1_w_sp1, J1_w_sp2=spatialDistribution(0 , J1.w, -time/0.1, false, {0,1},{10,10});
equation 
  (,J1_w_sp1)=spatialDistribution(J1.w, 0, time/0.1, true, {0,1},{10,10});
  annotation (uses(Modelica(version="4.1.0")));
end CC3;

Here the three delayed signals are basically all the same, and it is just syntactic sugar that has removed time from the delay-call for J1_w_del. If it helps the discussion we could state that delay has time as hidden argument.

  1. It is just confusing and not usable.

Basically I don't see anyone writing: parameter Real p1, p2=delay(p1, 1+time/2); which is the case that we explicitly make an exception for. I don't see any benefit in making an explicit exception for something that will not be used by people making models, and I just expect it to confuse them - and make them wonder why we are spending time on this.

To me even allowing parameter Real p1, p2=delay(p1, 1); is weird (see previous item), but as long as we don't give it as example it just follows from the normal rules, so I don't care.

If we change the rules to state that delay is never a parameter-expression that works as well, and would work even if we add add initialization for the delay.

@HansOlsson HansOlsson added this to the 2025-October milestone Oct 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants