7 views (last 30 days)

Over the years I have observed that Simulink usually uses 3 time steps to handle a zero-crossing, but in some cases it uses only 2 or even 1. I'm wondering whether this is a bug or intended behavior.

I'll demonstrate all three cases with minimal examples (see below), so everyone can reproduce this. Some thoughts and questions:

- Some people seem to rely on having 3 time steps there (very close to the zero-crossing) – e.g. when they use memory blocks –, so I need to know whether Simulink guarantees this behavior (i.e. 3 steps) under some circumstances but I cannot find anything in the documentation.
- The only difference between the 2-steps-case and the 3-steps-case is whether the output port of a Hit Crossing block is shown or not. Why should that influence the solver at all?
- My minimal example for the 1-step-case uses a Pulse Generator. I'm aware that this block uses Variable Sample Time but I don't see why that should influence the number of time steps at the zero-crossing.
- I'm not sure whether it is (technically/numerically) necessary to have more than one (major) time step at the zero-crossing at all? If yes, why/when?
- Depending on which criteria does Simulink decide how many time steps it needs to handle a zero-crossing (I'm not counting minor time steps!)?

Maybe only Mathworks can answer some of these questions (?).

Detailed steps to reproduce all three cases

Create a new blank model. (This one will cover both the 3-steps-case and the 2-steps-case.)

Place these blocks, set parameters, and connect them in this order:

- Clock
- Gain (parameter "Gain" = 0.9)
- Hit Crossing (offset = 1, direction = rising).

Open Configuration Parameters / Solver tab. Set "Max step size" = 0.5 and "Stop time" = 2.

Click the Run button.

Type "tout" in the command window. This prints:

0

0.5

1

1.11111111111111

1.11111111111113

1.11111111111114

1.61111111111114

2

Note: There are 3 time steps close to the zero-crossing (at t=1.1111111...).

Now uncheck the Hit Crossing block's "Show output port" checkbox and apply the change.

Run the simulation again.

Type "tout" in the command window. This prints:

0

0.5

1

1.11111111111111

1.11111111111113

1.61111111111113

2

Note: This is different from the first simulation. Now there are only 2 time steps close to the zero crossing (t=1.1111111...).

Now create a 2nd new blank model. (This will cover the 1-step-case.)

Place these blocks, set parameters, and connect them in this order:

- Pulse Generator (period = 1)
- Hit Crossing (offset = 0.5, direction = rising).

Add a Scope block and connect it to the output of the Pulse Generator (to prevent a warning about all blocks being either virtual or having been removed by block reduction optimization).

Open Configuration Parameters / Solver tab. Set "Max step size" = 0.5 and "Stop time" = 2.

Click the Run button.

Type "tout" in the command window. This prints:

0

0.05

0.55

1

1.05

1.55

2

Note: There is only one time step at the zero crossing (t=1).

Tested on R2018b and R2020a. I had to use "out.tout" instead of "tout" on R2020a. Don't know why.

Paul
on 25 Jun 2020

I will speculate on what is happening here.

One comment applicable to all three cases is that none of the models involve states or memory. So the only thing the solver needs to worry about is when certain events will occur and what action to take at those times. Also, using the default Nonadaptive zero crossing algorithm.

I'm going to consider the three cases in reverse order.

1-Step: The Pulse Generator has Variable Sample Time, which means "the block tells Simulink when to run it."

From t = 0, the solver has to figure out how far to take the next step. The candidates are t = 0.05 (trailing edge of the first pulse, which the block tells the solver) or t = 0.5 (max step). So it initially takes the smaller of the two t = 0.05. The solver can then do a final check to make sure that it didn't miss anything, like a zero crossing event. There are none, so it stays at 0.05.

From t = 0.05, going through the same process, it takes the max step to t = 0.55.

From t = 0.55, the choices are either step to t = 1, or t = 1.05. So it picks t = 1 as the candiate. At that time, it detects the hit crossing. So the question for the solver is whether or not it needs to go backwards to figure out when that crossing really occurred. I'm going to speculate it doesn't, beacause the Variable Sample Time of the root source block is telling the solver that nothing would have changed prior to t = 1. So it keeps the step at t = 1 and sets the Hit Crossing output high.

From t = 1, the solver determines that the next event is not earlier than t = 1.05 (trailing edge of the pulse). So it jumps to t = 1.05, at which point the hit crossing output goes low.

2-Step: The Clock has Continuous Sample Time.

From t = 0, solver takes the max step t = 0.5, no problem.

From t = 0.5, solver takes the max step to t = 1, no problem.

From t = 1, solver tries to take the max step to t = 1.5. Detects the hit crossing occured somewhere between t = 1 and t = 1.5. Now it has to go backwards. I don't have a clue how that NonAdaptive algorithm works, so don't know why it couldn't figure out that the threhold was reached at t = 1/0.9 (in general, finding the exact time of the crossing won't be so easy). In any case, it backs the solver up to t = 1/0.9 - eps (out.tout(4)), which is as close as it can get to the crossing. Maybe it purposefully tries to bracket the crossing point?

From t = 1/0.9 - eps, the solver knows that the next step should be small to get on the other side of the hit crossing. For whatever reason, it took a step of 64*eps

>> isequal(out.tout(5),out.tout(4)+64*eps)

ans =

logical

1

At this point, the input to the Hit Crossing at out.tout(5) exceeds the threshold, and the input at out.tout(4) is below, so it sets the output high. As required, the block found the hit crossing, to within some tolerance (over which the user has not control as far as I can tell for the NonAdaptive algorithm.

From t = 1/0.9 + 63*eps, the solver is free take the max step, because there is no reason not to.

BTW, I changed the min step size of the solver to 100*eps, and got the exact same result with no warnings or errors. Not sure what that's all about. Maybe the min step size doesn't apply for zero crossing algorithm.

3-Step Case: This case is the same as the 2-step case up to the point of out.tout(5) = 1/0.9 + 63*eps.

From t = out.tout(5) = out.tout(4) + 64*eps = 1/0.9 + 63*eps. Now the solver has to decide what its next step should be. My first thought was that it should try the max step as a candidate and see if any events occurred prior. But it doesn't do that. Instead it takes a step of 64*eps again (u is the input to the hit crossing and y is the output)

>> [out.tout out.u>=1 out.y]

ans =

0 0 0

5.000000000000000e-01 0 0

1.000000000000000e+00 0 0

1.111111111111111e+00 0 0

1.111111111111125e+00 1.000000000000000e+00 1.000000000000000e+00

1.111111111111139e+00 1.000000000000000e+00 0

1.611111111111139e+00 1.000000000000000e+00 0

2.000000000000000e+00 1.000000000000000e+00 0

>> isequal(out.tout(5)+64*eps,out.tout(6))

ans =

logical

1

My speculation is that the solver takes this small step because it can't predict when the next event will occur because of the Continuous Sample Time of the clock AND because there might be a customer of the output of the Hit Crossing. So it takes a small step and gives us the spike output. Assuming that's what's really happening here, I don't know if that's an intentional feature or not. But if it is, it is useful.

I did some additional experimentation that suggests the solver is smart enough to know that the function driving the hit crossing is Variable Time, even if the actual solver step size is being determined by other factors, like a small time constant filter. The solver even knows this if there is gain block between the Pulse Generator and the Hit Crossing, where the sample time of the output of the gain block is FiM.

Summary: If the ultimate signal driving the hit crossing is Continuous Sample Time and the Hit Crossing has an output, you get the 3-step case with one very small step prior to the Hit and one very small step after the Hit, with the output being 0-1-0 at those times. That seemed to be true with other inputs, as long as the hit crossing was detected (see Note below).

Disclaimer. This is all just guesswork.

Note: It's also easy to miss the hit crossing altogether. Go back to the 1-Step case and replace the Clock with a Sine Wave block with Amplitude 2 and Frequency 2*pi/0.5.

Opportunities for recent engineering grads.

Apply TodayFind the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!
## 2 Comments

## Direct link to this comment

https://in.mathworks.com/matlabcentral/answers/553960-number-of-necessary-time-steps-to-handle-a-zero-crossing#comment_913522

⋮## Direct link to this comment

https://in.mathworks.com/matlabcentral/answers/553960-number-of-necessary-time-steps-to-handle-a-zero-crossing#comment_913522

## Direct link to this comment

https://in.mathworks.com/matlabcentral/answers/553960-number-of-necessary-time-steps-to-handle-a-zero-crossing#comment_920569

⋮## Direct link to this comment

https://in.mathworks.com/matlabcentral/answers/553960-number-of-necessary-time-steps-to-handle-a-zero-crossing#comment_920569

Sign in to comment.