Opened 4 years ago

Last modified 3 years ago

#6254 assigned defect

Backend fails with discrete-time first order model

Reported by: Rüdiger Franke Owned by: Andreas Heuermann
Priority: critical Milestone:
Component: Backend Version: 1.16.0
Keywords: Cc: Karim Adbdelhak

Description (last modified by Rüdiger Franke)

OpenModelica works with lots of discrete-time models, including also more sophisticated ones than here, see e.g. 4 states with implicit Euler:
https://github.com/OpenModelica/OpenModelica-testsuite/blob/master/openmodelica/cppruntime/fmu/modelExchange/2.0/testCSTR.mos

However, a simple model with one linear state fails. The below model samples a FirstOrder model. -d=backenddaeinfo says:

[7] 18:26:42 Symbolisch Meldung
Torn system details for strict tearing set:
 * Linear torn systems: 1 {(1,100.0%) 1}
 * Non-linear torn systems: 0

-d=dumpSimCode shows the following equation system -- without Jacobian and with index: -1 !?

10:  (LINEAR) index:0 jacobian: false 
        variables: 
index: -1: firstOrder.y (no alias)  initial: firstOrder.y_start no arrCref index:() []  
        b-vector: 
        8: $DER.firstOrder.y=(firstOrder.k * firstOrder.u - firstOrder.y) / firstOrder.T [Real] 
        9: interval() * (if firstTick() then 0.0 else $DER.firstOrder.y) + previous(firstOrder.y) - firstOrder.y (RESIDUAL) 

The C runtime reports "Failed to solve linear system of equations (no. 10) at time 0.000000, system is singular for U[2, 2].".
The Cpp runtime removes the equation during code generation.

model DiscreteTimeLoop
  Modelica.Blocks.Continuous.FirstOrder firstOrder(T = 1)  annotation(
    Placement(visible = true, transformation(origin = {18, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Modelica_Synchronous.RealSignals.Sampler.SampleClocked sample1 annotation(
    Placement(visible = true, transformation(origin = {-24, 10}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));
  Modelica_Synchronous.ClockSignals.Clocks.PeriodicRealClock periodicClock1(period = 0.1)  annotation(
    Placement(visible = true, transformation(origin = {-54, -44}, extent = {{-6, -6}, {6, 6}}, rotation = 0)));
  Modelica.Blocks.Interfaces.RealInput u(start = 1)  annotation(
    Placement(visible = true, transformation(origin = {-82, 10}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-72, 10}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
equation
  connect(u, sample1.u) annotation(
    Line(points = {{-82, 10}, {-32, 10}, {-32, 10}, {-32, 10}}, color = {0, 0, 127}));
  connect(sample1.y, firstOrder.u) annotation(
    Line(points = {{-18, 10}, {6, 10}, {6, 10}, {6, 10}}, color = {0, 0, 127}));
  connect(periodicClock1.y, sample1.clock) annotation(
    Line(points = {{-48, -44}, {-24, -44}, {-24, 2}, {-24, 2}}, color = {175, 175, 175}));

annotation(
    uses(Modelica(version = "3.2.3"), Modelica_Synchronous(version = "0.93.0")));
end DiscreteTimeLoop;

Change History (28)

comment:1 by Rüdiger Franke, 4 years ago

Description: modified (diff)

comment:2 by Francesco Casella, 4 years ago

Milestone: 1.16.11.17.0

@rfranke, 1.16.1 has been released already. If this is a critical fix for you, please assign it to 1.16.2

comment:3 by Rüdiger Franke, 4 years ago

Milestone: 1.17.01.16.2

comment:4 by Rüdiger Franke, 4 years ago

It requires workarounds in lot of our models, not really helping to motivate the use of OpenModelica.
We are speaking of a linear equation with one unknown variable here!?

comment:5 by Martin Sjölund, 4 years ago

A linear equation with one unknown variable is simply called an equation. These are 2x2 systems.
The model works fine on the C runtime using the 1.16.0 docker image though, so I'm not sure what the actual error is.
Is this a problem on Windows or Linux? Does it occur only on a particular CPU?

comment:6 by Rüdiger Franke, 4 years ago

Did you also check the result? Constant zero for firstOrder.y or a staircase?
Here is the equation from the opener again. There is no 2x2 system, just one unknown variable firstOrder.y. $DER.firstOrder.y can easily be eliminated.

10:  (LINEAR) index:0 jacobian: false 
        variables: 
index: -1: firstOrder.y (no alias)  initial: firstOrder.y_start no arrCref index:() []  
        b-vector: 
        8: $DER.firstOrder.y=(firstOrder.k * firstOrder.u - firstOrder.y) / firstOrder.T [Real] 
        9: interval() * (if firstTick() then 0.0 else $DER.firstOrder.y) + previous(firstOrder.y) - firstOrder.y (RESIDUAL) 

comment:7 by Francesco Casella, 4 years ago

Owner: changed from Karim Adbdelhak to Andreas Heuermann
Status: newassigned

Testing with v1.17.0-dev-220-g67bec2f83f, I get the same problem with the C-runtime

As you can see from the debugger, the system has two equations, the unknowns are

firstOrder.y
der(firstOrder.y)

The system is currently solved by tearing in the backend:

(torn) der(firstOrder.y) := (firstOrder.k*firstOrder.u - firstOrder.y)/firstOrder.T
(residual) interval()*(if firstTick() then 0.0 else $DER.firstOrder.y) +
           previous(firstOrder.y) - firstOrder.y = 0

When firstTick() is true the residual is zero for

firstOrder.y = previous(firstOrder.y)

then the torn equation computes

der(firstOrder.y) := (firstOrder.k*firstOrder.u - firstOrder.y)/firstOrder.T

This works fine, if you set StopTime = 0 the simulation is successful. At the next tick the variable $DER.firstOrder.y comes into play, which is quite confusing, but that's already the topic of #6150. I understand that variable actually means der(firstOrder.y), so the residual equation would eventually be equivalent to

0.1*firstOrder.u - 0.1*firstOrder.y + previous(firstOrder.y) - firstOrder.y = 0

which is not singular. The solution should be

firstOrder.y = (0.1*firstOrder + previous(firstOrder.y))/ 1.1

Maybe this is related to #6196?

@AnHeuermann, could you please have a look and check what is actually written in the A and b matrices for the linear solver?

Please back-port any fix that could solve this issue to the maintenance branch as well.

BTW, I'm still unsure what we'll do with linear tearing (see #6196). In any case, if the system is torn to just one linear equation, there is no need to solve that using dgesv(), we could solve that directly as

x := b/A

in reply to:  6 comment:8 by Vitalij Ruge, 4 years ago

Replying to rfranke:

Did you also check the result? Constant zero for firstOrder.y or a staircase?
Here is the equation from the opener again. There is no 2x2 system,

For the sake of completeness, the workaround --maxSizeSolveLinearSystem=2 work for this small model with c/cpp. Of course the risk is high, the workaround fail for real problem.

comment:9 by Karim Adbdelhak, 4 years ago

Resolution: fixed
Status: assignedclosed

The following .mos file works for me on the latest master (OMCompiler v1.17.0-dev.241+gf877feaade) for both C and C++:

loadFile("DiscreteTimeLoop.mo"); getErrorString();

setCommandLineOptions("--std=3.3 -d=newInst");
simulate(DiscreteTimeLoop); getErrorString();

This is the reported algebraic loop using -d=dumpLoops which seems perfectly fine. I guess this has been fixed by a fairly recent change i did in PR6985 or PR6988

########################################
                                                                                                     
 Algbraic Loops (Simulation):         
                                                                                                     
########################################                                                             
                                                                                                     
system 1                                                                                             
========================================
                                                                                                     
################################################################################
 dumpLoops: SORTED COMPONENT 
################################################################################
                                                  
torn linear Equationsystem:
                                                  
internal vars (1)                                                                                    
1: $DER.firstOrder.y:VARIABLE()  type: Real
                                                                                                     
residual vars (1)                                                                                    
1: firstOrder.y:CLOCKED_STATE(flow=false start = firstOrder.y_start fixed = true )  "Connector of Real output signal" type: Real
                                                                                                     
internal equations (1)                                                                               
1/1 (1): $DER.firstOrder.y = (firstOrder.k * firstOrder.u - firstOrder.y) / firstOrder.T   [dynamic |0|0|0|0|]
                                                                                                     
residual equations (1)
1/1 (1): (firstOrder.y - previous(firstOrder.y)) / interval() = if firstTick() then 0.0 else $DER.firstOrder.y   [dynamic |0|0|0|0|]

I will close this ticket, please reopen if there are still issues!

comment:10 by Karim Adbdelhak, 4 years ago

Note that it also works with --tearingMethod=minimalTearing, which should reflect the original problem:

########################################
                                                                                                     
 Algbraic Loops (Simulation):         
                                                                                                     
########################################                                                             
                                                                                                     
system 1                                                                                             
========================================
                                                                                                     
################################################################################
 dumpLoops: SORTED COMPONENT 
################################################################################
                                                  
torn linear Equationsystem:
                                                  
internal vars (0)                                                                                    
                                                  
residual vars (2)                                                                                    
1: $DER.firstOrder.y:VARIABLE()  type: Real                                                          
2: firstOrder.y:CLOCKED_STATE(flow=false start = firstOrder.y_start fixed = true )  "Connector of Real output signal" type: Real
                                                                                                     
internal equations (0)                                                                               
                                                                                                     
residual equations (2)                                                                               
1/1 (1): (firstOrder.y - previous(firstOrder.y)) / interval() = if firstTick() then 0.0 else $DER.firstOrder.y   [dynamic |0|0|0|0|]
2/2 (1): $DER.firstOrder.y = (firstOrder.k * firstOrder.u - firstOrder.y) / firstOrder.T   [dynamic |0|0|0|0|]

comment:11 by Francesco Casella, 4 years ago

Resolution: fixed
Status: closedreopened

@Karim, @rfranke needs this fix in a stable release, see comment:4, so it should be back-ported to the 1.16 maintenance branch, and then released as 1.16.2. Can you please check that patching your two PR's onto the maintenance branch resolve the issue and then push them there as well?

Thanks!

comment:12 by Francesco Casella, 4 years ago

Owner: changed from Andreas Heuermann to Karim Adbdelhak
Status: reopenedassigned

comment:13 by Karim Adbdelhak, 4 years ago

I tested it with the v1.16 maintenance branch (OMCompiler v1.16.1-v1.16.1.5+gc01d479c53) and it reports the same. Maybe it got fixed before? I cannot reproduce the problem.

comment:14 by Rüdiger Franke, 4 years ago

Strange: the C runtime generates no error when running your omc script. This confirms the comment by @sjoelund.se above.

The C runtime raises an error when running from OMEdit though!?

Failed to solve linear system of equations (no. 10) at time 0.000000, system is singular for U[2, 2].
The default linear solver fails, the fallback solver with total pivoting is started at time 0.000000. That might raise performance issues, for more information use -lv LOG_LS.
Matrix singular!

But also when running from the command line, the result of C and C++ runtime is wrong (firstOrder.y constant 1 instead of gradually raising from 0 to 0.67).

in reply to:  14 comment:15 by Francesco Casella, 4 years ago

So: runtime fails with OMEdit giving singular system, and gives wrong result from a script. Interesting.

@Karim, any clue what's going on?

comment:16 by Francesco Casella, 4 years ago

Ping!

Sorry to push you, can we fix this in 1.16.2?

comment:17 by Andreas Heuermann, 4 years ago

But also when running from the command line, the result of C and C++ runtime is wrong (firstOrder.y constant 1 instead of gradually raising from 0 to 0.67).

So I tested with the following script:

setCommandLineOptions("-d=newInst --simCodeTarget=Cpp --std=3.3"); getErrorString();
loadFile("DiscreteTimeLoop.mo"); getErrorString();
simulate(DiscreteTimeLoop); getErrorString();

This will simulate, but the result is wrong. firstOrder.y is constant 0.


So I tested with --simCodeTarget=C and got:

true
""
true
"Notification: Automatically loaded package Modelica 3.2.3 due to uses annotation.
Notification: Automatically loaded package Complex 3.2.3 due to uses annotation.
Notification: Automatically loaded package ModelicaServices 3.2.3 due to uses annotation.
Notification: Automatically loaded package Modelica_Synchronous 0.93.0 due to uses annotation.
"
record SimulationResult
    resultFile = "/home/andreas/workspace/Testitesttest/tickets/ticket6254/DiscreteTimeLoop_res.mat",
    simulationOptions = "startTime = 0.0, stopTime = 1.0, numberOfIntervals = 500, tolerance = 1e-06, method = 'dassl', fileNamePrefix = 'DiscreteTimeLoop', options = '', outputFormat = 'mat', variableFilter = '.*', cflags = '', simflags = ''",
    messages = "LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.
stdout            | warning | The default linear solver fails, the fallback solver with total pivoting is started at time 0.000000. That might raise performance issues, for more information use -lv LOG_LS.
LOG_SUCCESS       | info    | The simulation finished successfully.
",
    timeFrontend = 0.6425105,
    timeBackend = 0.0525949,
    timeSimCode = 0.0118169,
    timeTemplates = 0.0107241,
    timeCompile = 2.4457127,
    timeSimulation = 0.0308921,
    timeTotal = 3.1944813
end SimulationResult;
""
"/home/andreas/workspace/Testitesttest/tickets/ticket6254/DiscreteTimeLoop.fmu"
""

Here the result for firstOrder.y is constant 0 as well.
Since both Cpp and C doesn't seem to work I'll concentrate on the C runtime for now.


So looking at the generated C code we have a strange "empty" loop:

/* linear systems */
OMC_DISABLE_OPT
void setLinearMatrixA10(void *inData, threadData_t *threadData, void *systemData)
{
  const int equationIndexes[2] = {1,10};
  DATA* data = (DATA*) inData;
  LINEAR_SYSTEM_DATA* linearSystemData = (LINEAR_SYSTEM_DATA*) systemData;
}
OMC_DISABLE_OPT
void setLinearVectorb10(void *inData, threadData_t *threadData, void *systemData)
{
  const int equationIndexes[2] = {1,10};
  DATA* data = (DATA*) inData;
  LINEAR_SYSTEM_DATA* linearSystemData = (LINEAR_SYSTEM_DATA*) systemData;
}
OMC_DISABLE_OPT
void initializeStaticLSData10(void *inData, threadData_t *threadData, void *systemData)
{
  DATA* data = (DATA*) inData;
  LINEAR_SYSTEM_DATA* linearSystemData = (LINEAR_SYSTEM_DATA*) systemData;
  int i=0;
  /* static ls data for firstOrder.y */
  linearSystemData->nominal[i] = data->modelData->realVarsData[3].attribute /* firstOrder.y */.nominal;
  linearSystemData->min[i]     = data->modelData->realVarsData[3].attribute /* firstOrder.y */.min;
  linearSystemData->max[i++]   = data->modelData->realVarsData[3].attribute /* firstOrder.y */.max;
}

but -d=dumpLoops tells us we should have a loop generated from:

################################################################################
 dumpLoops: SORTED COMPONENT 
################################################################################

torn linear Equationsystem:

internal vars (1)
1: $DER.firstOrder.y:VARIABLE()  type: Real

residual vars (1)
1: firstOrder.y:CLOCKED_STATE(flow=false start = firstOrder.y_start fixed = true )  "Connector of Real output signal" type: Real

internal equations (1)
1/1 (1): $DER.firstOrder.y = (firstOrder.k * firstOrder.u - firstOrder.y) / firstOrder.T   [dynamic |0|0|0|0|]

residual equations (1)
1/1 (1): (firstOrder.y - previous(firstOrder.y)) / interval() = if firstTick() then 0.0 else $DER.firstOrder.y   [dynamic |0|0|0|0|]

And as @rfranke stated in the ticket description the simCodeDump is looking fishy:

*********************
* SimCode Equations *
*********************


allEquations:
========================================

========================================



odeEquations (0 systems):
========================================
========================================



algebraicEquations (0 systems):
========================================
========================================


clockPartitions (1 systems):

BaseClock:
========================================
Clock(RCONST 0.1
)========================================

SubPartition Vars:
========================================
index:-1: $DER.firstOrder.y (no alias)  initial: 	no arrCref index:() [] 
index:-1: firstOrder.u (no alias)  initial: 	no arrCref index:() [] 
index:-1: firstOrder.y (no alias)  initial: firstOrder.y_start	no arrCref index:() [] (PREVIOUS)
index:-1: sample1.y (no alias)  initial: 	no arrCref index:() [] 
partition equations:
========================================
11: $CLKPRE.firstOrder.y=firstOrder.y [Real]
6: sample1.y=$getPart(u) [Real]
7: firstOrder.u=sample1.y [Real]
10:  (LINEAR) index:0 jacobian: false
	variables:
index:-1: firstOrder.y (no alias)  initial: firstOrder.y_start	no arrCref index:() []
	b-vector:
	8: $DER.firstOrder.y=(firstOrder.k * firstOrder.u - firstOrder.y) / firstOrder.T [Real]
	9: interval() * (if firstTick() then 0.0 else $DER.firstOrder.y) + previous(firstOrder.y) - firstOrder.y (RESIDUAL)

So my guess: We are erroring during code generation but don't report the error.

comment:18 by Andreas Heuermann, 4 years ago

Cc: Andreas Heuermann added

comment:19 by Andreas Heuermann, 4 years ago

So when I dump the hash table crefToSimVarHT, that is created in createSimCode and which is passed to the templates, we get:

HashTable:
{$CLKPRE.firstOrder.y,{#SimVar(index=0,name=$CLKPRE.firstOrder.y)#}}
{$DER.firstOrder.y,{#SimVar(index=1,name=$DER.firstOrder.y)#}}
{firstOrder.u,{#SimVar(index=2,name=firstOrder.u)#}}
{firstOrder.y,{#SimVar(index=3,name=firstOrder.y)#}}
{sample1.y,{#SimVar(index=4,name=sample1.y)#}}
{u,{#SimVar(index=5,name=u)#}}
{firstOrder.T,{#SimVar(index=0,name=firstOrder.T)#}}
{firstOrder.k,{#SimVar(index=1,name=firstOrder.k)#}}
{firstOrder.y_start,{#SimVar(index=2,name=firstOrder.y_start)#}}
{periodicClock1.period,{#SimVar(index=3,name=periodicClock1.period)#}}
{firstOrder.initType,{#SimVar(index=0,name=firstOrder.initType)#}}
{periodicClock1.useSolver,{#SimVar(index=0,name=periodicClock1.useSolver)#}}
{sample1.u,{#SimVar(index=0,name=sample1.u)#}}
{periodicClock1.solverMethod,{#SimVar(index=0,name=periodicClock1.solverMethod)#}}

This dump is done directly before the output from -d=dumpSimCode are displayed. So the hash table itself is looking good, but it seems we still can't find some variables (all with index=-1).
If I dump the crefToClockIndexHT as well we get:

HashTable:
{sample1.y,{1}}
{$CLKPRE.sample1.y,{1}}
{firstOrder.y,{1}}
{$CLKPRE.firstOrder.y,{1}}
{firstOrder.u,{1}}
{$CLKPRE.firstOrder.u,{1}}
{$DER.firstOrder.y,{1}}
{$CLKPRE.$DER.firstOrder.y,{1}}

So the next step on debugging this would be to check why in function dumpSimCodeDebug we get index=-1.

comment:20 by Francesco Casella, 4 years ago

@AnHeuermann will work on that this week, if he can find a solution, we'll put it in 1.16.2, otherwise we'll try to fix that for 1.17.0.

comment:21 by Francesco Casella, 4 years ago

Cc: Karim Adbdelhak added; Andreas Heuermann removed
Owner: changed from Karim Adbdelhak to Andreas Heuermann

comment:22 by Andreas Heuermann, 4 years ago

@kabdelhak and myself managed to find the cause for the problem.
The function translateClockedEquations will translate equations from the Backend structure to the SimCode structure. On of it's sub-functions createEquationsForSystem1 is missing a case for BackendDAE.TORNSYSTEM() to handle this torn equation system.

We think we can add the missing case by using createTornSystem. It will probably need some adaptions to run, maybe some code generation is missing as well. But now we have a point to start working on it.

I'm working on it on my branch: https://github.com/AnHeuermann/OpenModelica/tree/ticket6254-clockedTornSystems

comment:23 by Francesco Casella, 4 years ago

Great, thanks!

comment:24 by Andreas Heuermann, 4 years ago

Today I'm not so optimistic that we actually found the root cause.
It seems the reported index=-1 is normal for torn systems, the simCodeDump just isn't very good.

Comparing to some random example with a torn linear system I noticed, that the simCodeDump is reporting index=-1 for the variables in the linear system as well.
One big difference though is that the working example has a Jacobian, while the clocked system has not.

Maybe the lack of a Jacobian in SimCode is the reason why we don't generate code for the system.

comment:25 by Andreas Heuermann, 4 years ago

Milestone: 1.16.21.17.0

It seems that the lack of the Jacobina is precisely the problem here. In the code generation we loop over list<tuple<Integer, Integer, SimEqSystem>> simJac; to generate the A matrix of a linear system A*x=b, but for our example this is an empty list.


In the meantime I also tried to see what will happen for a non-torn linear system. With that we will generate something, but it will faile during compilation of the C code:

DiscreteTimeLoop_03lsy.c:17:89: error: use of undeclared identifier 'clockIndex'
  linearSystemData->setAElement(1, 0, DIVISION_SIM(1.0,data->simulationInfo->clocksData[clockIndex].interval,\"interval()\",equationIndexes), 2, linearSystemData, threadData);
                                                                                        ^
DiscreteTimeLoop_03lsy.c:18:77: error: use of undeclared identifier 'clockIndex'
  linearSystemData->setAElement(1, 1, (-(((data->simulationInfo->clocksData[clockIndex].cnt == 0)?0.0:1.0))), 3, linearSystemData, threadData);
                                                                            ^
DiscreteTimeLoop_03lsy.c:27:152: error: use of undeclared identifier 'clockIndex'
  linearSystemData->setBElement(1, DIVISION_SIM(data->localData[0]->realVars[0] /* previous(firstOrder.y) variable */,data->simulationInfo->clocksData[clockIndex].interval,\"interval()\",equationIndexes), linearSystemData, threadData);
                                                                                                                                                       ^
3 errors generated.
make: *** [<builtin>: DiscreteTimeLoop_03lsy.o] Error 1
make: *** Waiting for unfinished jobs....

The Cpp runtime has a similar error.


What we could do is:

  • Fix code generation for non-torn linear systems with clocked state variables.
    • This could be a fix for simple models, but more complicated loops with discrete variables will need tearing.
  • Enable torn systems to work without symbolic Jacobians.
    • They are not supported at the moment. We would need to rewrite some parts on how we generate linear systems in code generation.

@kabdelhak Any idea how we could do this?
I have to understand clocked systems and how we are generating SimCode structure from BackendStructure before starting to code anything.

It will not be possible to fix this this week, so I'm postponing it to 1.17 for now.

comment:26 by Francesco Casella, 4 years ago

Priority: highcritical

comment:27 by Francesco Casella, 4 years ago

Milestone: 1.17.01.18.0

Retargeted to 1.18.0 because of 1.17.0 timed release.

comment:28 by Francesco Casella, 3 years ago

Milestone: 1.18.0

Ticket retargeted after milestone closed

Note: See TracTickets for help on using tickets.