ModelicaStandardLibrary icon indicating copy to clipboard operation
ModelicaStandardLibrary copied to clipboard

MSL 4.1.0 Regressions: OpAmps/SignalGenerator

Open AHaumer opened this issue 1 year ago • 9 comments

Modelica/Electrical/Analog/Examples/OpAmps/SignalGenerator.mo set {opAmp1, opAmp2}.strict=true see #4333

AHaumer avatar Oct 21 '24 11:10 AHaumer

Unfortunately System Modeler has unrelated issues with this model, and therefore I cannot comment on the effect of this change.

Just to be sure - how do you know that those issues are unrelated?

The limits combined with the (optional) events are creating non-linear systems that can be difficult to solve, which can cause various problems that may seem unrelated.

HansOlsson avatar Oct 22 '24 07:10 HansOlsson

Just to be sure - how do you know that those issues are unrelated?

The limits combined with the (optional) events are creating non-linear systems that can be difficult to solve, which can cause various problems that may seem unrelated.

Thanks for pushing back, I just trusted what our internal issue tracker said was true, which may no longer be the case.

What I'm observing now, is that this PR makes no difference for System Modeler that I can see.

Our results (which come after a huge amount of warnings from nonlinear system solving) are similar to the current reference, but "delayed". Example variable:

image

If I decrease the interval size aggressively to 0.00001, our results match up with the reference, except System Modeler does not generate any events.

maltelenz avatar Oct 22 '24 08:10 maltelenz

Thanks for your feedback @maltelenz As far as I see the time drift should be avoided by "strict = true". Here you have a comparison between the rectangle out of the circuit and the analytic solution: SignalGenerator The deviation is caused by the finite amplification of the opAmps. opAmp2 is an integrator coupled with opAmp1 which switches between positive and negative supply. You might try the enclosed example. I could also include the analytic solution in the PR if desired. SignalGenerator.zip Analytic solution: f = 10 Hz Vps = -Vns = 15 V VAmp = 10 V desired amplitude of rectangle gain of integrator = 4 f VAmp/Vps

AHaumer avatar Oct 22 '24 09:10 AHaumer

Digging a bit further, our heuristics for when events should be generated inside smooth, already puts us in the strict case (no events), which explains why we don't see a difference from this PR.

Maybe strict is a misleading name here, since it means no events, and therefore step-length-dependent results?

maltelenz avatar Oct 22 '24 11:10 maltelenz

Now I am confused. Simulation with OM also reveals a time drift between the rectangle out of the circuit and the analytic solution. SG This example is just a simple circuit for students of electrical engineering. I have to admit: Originally I implemented the IdealizedOpAmpLimited, but just a naked version without homotopy and noEvent, as well as the examples. I do not know who implemented the improvements, and I have some doubts that we are trapped in those improvements ...

AHaumer avatar Oct 22 '24 13:10 AHaumer

The more advanced handling with strict was introduced in https://github.com/modelica/ModelicaStandardLibrary/pull/2561/files#diff-de34e2dc37ee35839e04f1a11a6f88260080407e14525f85d4839e06eca53b57 with the corresponding long discussion: https://github.com/modelica/ModelicaStandardLibrary/pull/2561

The homotopy was introduced in https://github.com/modelica/ModelicaStandardLibrary/commit/9d5bc998bbe08a153fd622622400e744704a4257 referring to https://github.com/modelica/ModelicaStandardLibrary/issues/2017 and https://github.com/modelica/ModelicaStandardLibrary/issues/2270

maltelenz avatar Oct 22 '24 14:10 maltelenz

I converted this to draft since it needs more investigation, and I'm afraid that there should be changes applied to the IdealizedOpAmpLimited - for sure not for that release. If we omit the homotopy we read: if strict then v_out = smooth(0, noEvent(if V0*v_in>vps then vps else if V0*v_in<vns then vns else V0*v_in)); else v_out = smooth(0, if V0*v_in>vps then vps else if V0*v_in<vns then vns else V0*v_in); end if; "smooth should be used instead of noEvent in order to avoid events for efficiency reasons. A tool is free to not generate events for expressions inside smooth. However, smooth does not guarantee that no events will be generated, and thus it can be necessary to use noEvent inside smooth." The result depends on event generation. As far as I see event generation is necessary to provide a result without time drift.
So this example with this implementation of the IdealizedOpAmpLimited is a bad example for comparison between tools. @casella should we remove the example from MSL until this issue is clarified?

AHaumer avatar Oct 22 '24 15:10 AHaumer

I converted this to draft since it needs more investigation, and I'm afraid that there should be changes applied to the IdealizedOpAmpLimited - for sure not for that release. If we omit the homotopy we read: if strict then v_out = smooth(0, noEvent(if V0*v_in>vps then vps else if V0*v_in<vns then vns else V0*v_in)); else v_out = smooth(0, if V0*v_in>vps then vps else if V0*v_in<vns then vns else V0*v_in); end if; "smooth should be used instead of noEvent in order to avoid events for efficiency reasons. A tool is free to not generate events for expressions inside smooth. However, smooth does not guarantee that no events will be generated, and thus it can be necessary to use noEvent inside smooth." The result depends on event generation. As far as I see event generation is necessary to provide a result without time drift. So this example with this implementation of the IdealizedOpAmpLimited is a bad example for comparison between tools. @casella should we remove the example from MSL until this issue is clarified?

To me this indicates that we should have the opposite of strict as an option for OpAmp (one that always generates event), and update the example to use that new flag. Whether it should be for this release or the next isn't clear, I can see arguments both ways:

  • We are passed the dead-line
  • But the example not working shows that the underlying model isn't working; it's not just an issue in the Example

HansOlsson avatar Oct 23 '24 07:10 HansOlsson

Yes @HansOlsson it is an issue in the IdealizedOpAmpLimited, and this needs some more investigation.

There are 3 groups of examples:

  1. The OpAmps-Examples {NonInvertingAmplifier, InvertingAmplifier, DifferentialAmplifier, Adder, Subtracter, Differentiator, Integrator, LowPass, HighPass, ControlCircuit, VoltageFollower, LCOscillator} are parameterized so that the operational amplifiers are used in the linear range without output saturation (only the Differentiator limits the output voltage). All these examples use homotopyType = NoHomotopy and strict = true.
  2. The OpAmps-Examples {Comparator, InvertingSchmittTrigger, SchmittTrigger} are designed such way that the output voltage switches between positive and negative supply, i.e. the output is (nearly) all the time saturated. All these examples use homotopyType = NoHomotopy and strict = true.
  3. The critical OpAmps-Examples {Multivibrator, SignalGenerator} are also designed such way that the output voltage switches between positive and negative supply, i.e. the output is (nearly) all the time saturated. SignalGenerator: The exact time when the output of opAmp1 gets saturated is of importance, since opAmp2 integrates the output of opAmp1 and its output is fed back to opAmp1 causing it to switch. Multivibrator uses homotopyType = LimiterHomotopy.LowerLimit and strict = true, SignalGenerator uses homotopyType = LimiterHomotopy.UpperLimit and strict = false.

The parameter strict = true performs saturation of the output with noEvent, i.e. the exact time when the output gets saturated is not properly defined. That is ok for the first group of examples. The second and the third group of examples should be used with strict = false. “smooth should be used instead of noEvent in order to avoid events for efficiency reasons. A tool is free to not generate events for expressions inside smooth. However, smooth does not guarantee that no events will be generated, and thus it can be necessary to use noEvent inside smooth.” Therefore the usage of smooth is a bad idea for the second and third group of examples, since the result is dependent on the tool (whether or not events are generated).

Enclosed you'll find a small library with an implementation of the OpAmp which allows to choose smooth and strict (noEvent) separately. Feel free to test different parameter combinations and give feedback. With Dymola and OM I can test myself, @maltelenz your test with SystemModeler is highly appreciated. OpAmps.zip

AHaumer avatar Oct 23 '24 17:10 AHaumer

I tested the SignalGenerator example from the above zip file, in System Modeler, using this test library:

package OpAmpSignalGeneratorTest
  model Base
  extends OpAmps.SignalGenerator;
    annotation(
      Documentation(figures = {
        Figure(
          title = "Compare OpAmps",
          identifier = "reverent-mandelbrot",
          preferred = true,
          plots = {
            Plot(curves = {Curve(y = opAmp1.v_out), Curve(y = pulse.y)}),
            Plot(curves = {Curve(y = opAmp2.v_out), Curve(y = integrator.y)}),
            Plot(curves = {Curve(y = opAmp1.smoothed), Curve(y = opAmp1.strict)})
        })
      }),
      experiment(StopTime = 1)
    );
  end Base;

  model SmoothStrict
    extends Base(opAmp1.smoothed = true, opAmp2.smoothed = true, opAmp1.strict = true, opAmp2.strict = true);
  end SmoothStrict;

  model SmoothNoStrict
    extends Base(opAmp1.smoothed = true, opAmp2.smoothed = true, opAmp1.strict = false, opAmp2.strict = false);
  end SmoothNoStrict;

  model NoSmoothStrict
    extends Base(opAmp1.smoothed = false, opAmp2.smoothed = false, opAmp1.strict = true, opAmp2.strict = true);
  end NoSmoothStrict;

  model NoSmoothNoStrict
    extends Base(opAmp1.smoothed = false, opAmp2.smoothed = false, opAmp1.strict = false, opAmp2.strict = false);
  end NoSmoothNoStrict;
end OpAmpSignalGeneratorTest;

with these results:

SmoothStrict: image

SmoothNoStrict: image

NoSmoothStrict: image

NoSmoothNoStrict: image

Let me know if I should try something more.

maltelenz avatar Oct 24 '24 09:10 maltelenz

Sorry if I couldn't keep up with the discussion. Here are my 2 cents. Op-Amps can be used for two kinds of application: one is linera amplifiers, where you try as much as you can to steer clear of saturation, and one is signal generators, where the outputs are often saturated and there are very fast swings from Vcc to -Vcc and vice-versa. For the first category, using smooth and avoiding events could be a good idea; for the second, it is pretty obvious that simulations will be inaccurate in terms, e.g., of predicting the oscillator frequency. Which is not a marginal issue when analyzing such circuits.

So, even though we are well past the deadline, I would be inclined to try to fix the problem by allowing both options in the OpAmp model, and setting up the example with the appropriate one. As others have already said, this is really a modelling issue, not a tool issue. I think this shouldn't take much effort.

I'll look at @AHaumer's smal library ASAP.

casella avatar Oct 24 '24 14:10 casella

Thanks a lot @maltelenz. That's really a pity, none of the results is correct: Either we get no output of the opAmps at all, or the solution drifts away. I would have expected that NoSmoothNoStrict should deliver correct results. I get similar results with Dymola and OM. @casella I don't know how to handle this, I'm afraid we won't get it fixed within reasonable time. My small library implemented all 4 combinations (smooth x strict), Malte and I tested with different tools. Maybe we should ignore the regression for this release ...

AHaumer avatar Oct 24 '24 18:10 AHaumer

Regarding smooth: I would more say that tools assume that a derivative using smooth is "smooth enough" for the solvers (so not only C0 but C5 or similarly as required by the theory underlying the ODE/DAE-solvers), and this isn't the case for those use-cases.

Regarding zero-solutions, apart from the smooth-ness and events there's another important aspect of the SignalGenerator-example: it has a "state" in the non-linear solver. Specifically in Dymola (also when generating events for smooth) depending on the start-value for r2.i (setting it to -Vps/R2, 0, +Vps/R2) you get inverted, zero, or normal result. If a tool does not use r2.i as iteration variable I don't know what happens.

HansOlsson avatar Nov 04 '24 09:11 HansOlsson

Since this issue seems to depend of event generation of the specific solver: Why not try a regularization function which is nearly linear in the middle and saturates left and right? Similar as used in Modelica.Mechanics.Rotational.Sources.SignTorque. I tried with Dymola 2024x: The results are ok! OpenModelica v1.24.0 has some problems - @casella could you pls. have a look? I wonder what are the results using System Modeler - @maltelenz could you pls. have a look? OpAmps.zip

AHaumer avatar Nov 10 '24 19:11 AHaumer

@AHaumer The new version of the SignalGenerator in System Modeler: image I'm guessing you were expecting a different initialization?

maltelenz avatar Nov 11 '24 08:11 maltelenz

@maltelenz Dymola shows a correct result, OpenModelica shows the same result as SystemModeler. SignalGenerator1 SignalGenerator2 I have no idea how to "cure" the IdealizedOpAmpLimited ...

Enclosed the whole package Modelica.Electrical.Analog.Examples.OpAmps from MSL with an enhanced version, see docu layer of IdealizedOpAmpLimited for the choices. OpAmps.zip All examples work fine with Dymola. OpenModelica fails for the last four {nvertingSchmittTrigger, SchmittTrigger, MultiVibrator, SignalGenerator}.

AHaumer avatar Nov 11 '24 09:11 AHaumer

@AHaumer The new version of the SignalGenerator in System Modeler: image I'm guessing you were expecting a different initialization?

How is System Modeler solving the system of equations?

  • (I assume it is a system of equations, right?)
  • What variable is iteration variable?
  • What is its guess-value?

HansOlsson avatar Nov 11 '24 13:11 HansOlsson

How is System Modeler solving the system of equations?

I think this conversation is becoming too inefficient. I'd prefer investigating this in an online meeting instead.

henrikt-ma avatar Nov 11 '24 15:11 henrikt-ma

Thank you @AHaumer for providing the new test implementation and the tests in your OpAmps package!

As I wrote above, I think that if you want to use the OpAmps as triggers and you want a precise estimation of the oscillator period, events should be used, otherwise we are depending on how ODE solvers react to signals that are not smooth enough and/or on which specific strategy each solver uses to handle smooth(), which I think it's not good. I hope there is agreement on this point.

That said, I checked OpAmps.SignalGenerator. Indeed, the op-amps output voltages remain at zero in OpenModelica, as I understand it is the case with System Modeller. The problem is, this is a perfectly legitimate solution for the system, which is strongly non-linear and has three valid initial solutions.

The initial voltage of the capacitor is zero. The first possible solution is the one found by OpenModelica and System Modeller: all node voltages are zero, the op-amp input voltages are both zero, so they are equal, so their output is zero, and the system is at equilibrium. We know this is an unstable equilibrium, so in the real system small fluctuations in the op-amp input voltages due to electronic noise fluctuations will drive the system out of this equilibrium onto a stable periodic trajectory. Unfortunately, this doesn't happen in a numerical simulation, which has no such random fluctuations. If you simulate:

model Unstable
  Real x(start = 0, fixed = true);
equation
  der(x) = x;
end Unstable;

which is obviously unstable, you get a nice x = 0 solution also with numerical solvers, because any integration formula will just compute a zero increment, add it to the zero state and obtain an exact zero.

The other two valid initial solutions correspond to opAmp1 ouptut being saturated either at +15 or -15 Volt; they are not equilibrium solutions, so the oscillations start from there. BTW, the choice of the actual initial solution by the circuit is completely non-deterministic. In practice, if you switch on such a circuit in real life, whether the intial output voltage of opAmp2 will be positive or negative will be due to chance.

This is actually a very instructive example if you want to learn the possible intricacies of Modelica models involving nonlinear equations. I am sure @dzimmer would love that.

How we deal with this, we'll discuss in the meeting.

casella avatar Nov 12 '24 09:11 casella

This is my proposal according to this discussion and the discussion in #4333 The parameter settings look reasonable. With Dymola2025x and the proposed parameter settings all examples wotk as expected. With OM v1.24.3 (latest official release) the critical examples {Comparator, InvertingSchmittTrigger, SchmittTrigger, Multivibrator, SignalGenerator} fail. @maltelenz how's this version with WSM?

I do not share the opinion to remove all critical examples from MSL. If it's possible to implement it in reality and you have a reasonable model, it should work ... @casella how do we proceed?

AHaumer avatar Jan 10 '25 11:01 AHaumer

For the release notes I suggest:

Modelica.Electrical.Analog.Ideal.IdealizedOpAmpLimited did show numerical issues. Therefore it has been extended in a backwards compatible way, choosing regularization, smooth and noEvent independently. Therefore the behaviour of the previous version is still available.

AHaumer avatar Jan 10 '25 11:01 AHaumer

@casella I have cleaned up, avoiding all cosmetic changes but removed all unnecessary initializations. I am using an implementation with {regularized, strict, smoothed}. We can easily drop regularized or change names, but "strict" has been used previously (and is used throughout MSL), Default parameter settings are {regularized=false, strict=false, smoothed=false}. With these parameter settings the following uncritical examples work both in Dymola2025x and OMv.1.24.3: {NonInvertingAmplifier, InvertingAmplifier, DifferentialAmplifier, Adder, Subtracter, Differentiator, Integrator, LowPass, HighPass, VoltageFollower, Comparator, LCOscillator}. For the critical examples {InvertingSchmittTrigger, SchmittTrigger, Multivibrator, SIgnalGenerator} I want to avoid strict=true and smoothed=true - this has been discussed before. Without regularized=true these examples fail in Dymola2025x, so I recommend using regularized=true. Unfortunately OMv1.24.3 failed for these critical examples. I have no better idea how to resolve this problem.

AHaumer avatar Jan 10 '25 18:01 AHaumer

@AHaumer Sorry for not getting back sooner, I lost the tab with this page among other work.

With this PR, WSM has issues with the same models as you list for OM: {InvertingSchmittTrigger, SchmittTrigger, Multivibrator, SignalGenerator}.

We either do not match the expected reference results, for Multivibrator, which differs at initialization:

image

or get stuck early during simulation (the other 3 models).

Remaining OpAmp examples work.

maltelenz avatar Jan 20 '25 12:01 maltelenz

Also see old discussion about noEVent #4067

AHaumer avatar Jan 21 '25 09:01 AHaumer

It seems that avoiding all fancy stuff (strict, smoothed, regularized) for normal OpAmp applications is the right solution. There are switching examples that are more tricky (SchmittTrigger, invertingSchmittTrigger. Multivibrator, SignalGenerator) but quite normal in real life. In Dymola they work fine with strict=false, smoothed=false, regularized=true. We should avoid noEvent (strict) and smoothed (since it's the tool's choice to generate an event or not).

AHaumer avatar Jan 21 '25 10:01 AHaumer

I've tested the IdealizedOpAmpLimited Examples with Dymola2025x and OpenModelica v1.25.0. All examples except {SchmittTrigger, InvertingSchmittTrigger, Multivibrator, SignalGenerator} with strict=false, smoothed=false, regul=false. I think we don't have to care about them any more. For the mentioned four examples events are essential, therefore setting strict=true, smoothed=false, regul=false: {SchmittTrigger, InvertingSchmittTrigger} work in both simulators despite some messages in OM. Multivibrator works fine in Dymola, fails in OM. SignalGenerator (opAmp1.strict=true, opAmp2.strict=false) works fine in Dymola, OM delivers wrong results. I would only keep strict (and homotopy) but remove completely smoothed (and regularized), although I don't like strict (noEvent). I feel uncomfortable with smooth (since a tool is free to generate events or not) and regularization leads to a challenging nonlinear equation system. I have no idea why {SchmittTrigger, InvertingSchmittTrigger, Multivibrator, SignalGenerator} with strict=false are problematic but work fine with strict=true (avoiding events!) in Dymola. Interesting: The core equations with homotopy, noEvent (strict) and smooth in Modelica.Electrical.Analog.Ideal.IdealizedOpAmpLimited and in Modelica.Blocks.NonLinear.Limiter are the same. @casella @HansOlsson @maltelenz what's your opinion?

AHaumer avatar Apr 24 '25 16:04 AHaumer

BTW, with an alternative implementation we could shift the problem and blame Modelica.Blocks.Nonlinear.VariableLimiter (-: IdealizedOpAmpLimited

AHaumer avatar Apr 24 '25 19:04 AHaumer

Dear all, especially @casella, @HansOlsson, @maltelenz, I really want to find a stable solution for this problem. This OpAmp model is the only one in the MSL with saturation of output and finite no-load amplification. I agree with @casella that we have to avoid noEvent (strict ist a bad name for the Boolean parameter). I dislike smooth since it's up to the tool whether events are generated or not - this is the cause for regressions between tools. Events (when saturation starts) are needed for "switching" applications.

I tried a different / simpler formulation (see enclosed small library):

  • mark the equation as smooth(0, ...)
  • use a term (AFAIK without event, i.e. min and max)
  • generate events with boolean variables

... and found the following:

  • Dymola (with Dassl) delivers correct results
  • Dymola (with other solvers) delivers wrong results
  • OM v1.25.0 delivers correct result for Integrator
  • OM v1.25.0 fails for SchmittTrigger and SignalGenerator

@maltelenz could you please test with WSM? The library is reduced to 3 test examples: OpAmpTest.zip

  • Integrator with (uncritical) saturation
  • SchmittTrigger with essential saturation
  • SignalGenerator consists of SchmittTriger + Integrator All 3 examples are common applications of OpAmps. All 3 examples contain an analytic solution with blocks, so at a first glance we don't need reference results.

After solving this problem I'll come up with a PR for MSL.

AHaumer avatar May 13 '25 10:05 AHaumer

@AHaumer In System Modeler (WSM):

  • Integrator matches the block output
  • SchmittTrigger matches the block output
  • SignalGenerator does not match the block output. Here is the first second (there was no experiment stop time): image

It glides further and further apart, here is second 9 to 10 in a 10 second simulation:

image

maltelenz avatar May 13 '25 11:05 maltelenz

I believe finally I found a solution: I have to take the "Rise Time" into account, i.e. a firstOrder between input and output. I tested a quick solution with Dymola, OM and WSM - all three run without problems and the results seem to be the same, although the time constant is really small (some µs which is realistic). Give me a few more days to implement a proper initialization and I'll replace this PR with a new one. Initialization is extremely important especially for the SignalGenerator - otherwise we fall to the trivial solution = 0. I've enclosed this quick-and-dirty test. @casella @HansOlsson @maltelenz what's your opinion? OpAmpTest.zip

AHaumer avatar May 14 '25 21:05 AHaumer