I have developed a dynamic phasor model of a grid-connected single phase inverter. Running the open loop model of the simulation (with modulation index and initial angle constant) for a duration of 0.3 seconds with a step-size of 1e-05 takes about 45 seconds.
However, implementing a discrete PI controller with sampling rate of 1e-4 leads to a simulation duration of about 30 minutes.
My suspicion is the sample function in my PI code. Would be glad if anyone could help with identifying the cause and a possible workaround. The controller code is below. Thanks in advance.
model LinearController_DQ_InverterSP
// reference currents
parameter Real Id_ref[3] = {0.0, 10.0, 20.0};
parameter Real Iq_ref[3] = {0.0, 5.0, 10.0};
//step times
parameter Real step_times[2] = {0.08, 0.12};
// current reference currents
Real Id_ref_curr;
Real Iq_ref_curr;
parameter Real Kp_cc = 0.0541 "Proportional term of controller";
parameter Real Ki_cc = 55.577 "Integral term of controller";
parameter Real T_sample = 1e-4 "Sampling period";
// previous time step terms
Real ud_prev(start = 0.0, fixed = true);
Real uq_prev(start = 0.0, fixed = true);
Real ed_prev(start = 0.0, fixed = true);
Real eq_prev(start = 0.0, fixed = true);
Real Vd_pcc_prev(start = 220*sqrt(2), fixed = true);
Real Vq_pcc_prev(start = 0.0, fixed = true);
// current time step terms
Real ud_curr, uq_curr;
Real ed_curr, eq_curr;
Real Vin_d, Vin_q;
Real a_const = Kp_cc + Ki_cc * T_sample / 2;
Real b_const = -Kp_cc + Ki_cc * T_sample / 2;
Modelica.Blocks.Interfaces.RealOutput Mr(start = 0.87, fixed=true); // modulation index
Modelica.Blocks.Interfaces.RealOutput th(start=0.0, fixed=true) ; // angle
Modelica.Blocks.Interfaces.RealInput Id ; // measured Id current
Modelica.Blocks.Interfaces.RealInput Iq ; // measure Iq current
Modelica.Blocks.Interfaces.RealInput Vd_pcc ; // Vd of measured grid voltage
Modelica.Blocks.Interfaces.RealInput Vq_pcc ; // Vq of measured grid voltage
algorithm
if time < step_times[1] then
Id_ref_curr := Id_ref[1];
Iq_ref_curr := Iq_ref[1];
elseif (time > step_times[1]) and (time < step_times[2]) then
Id_ref_curr := Id_ref[2];
Iq_ref_curr := Iq_ref[2];
else
Id_ref_curr := Id_ref[3];
Iq_ref_curr := Iq_ref[3];
end if;
when sample(0, T_sample) then
//Calc error
ed_curr := Id_ref_curr - Id;
eq_curr := Iq_ref_curr - Iq;
//Calc pid controller output
ud_curr := ud_prev + a_const * ed_curr + b_const * ed_prev;
uq_curr := uq_prev + a_const * eq_curr + b_const * eq_prev;
//Apply feedforward term
Vin_d := (Vd_pcc_prev + ud_prev);
Vin_q := (Vq_pcc_prev + uq_prev);
//Compute control signals
th := atan(Vin_q / Vin_d);
Mr := sqrt(Vin_d^2 + Vin_q^2) / 360;
// Update values
Vd_pcc_prev := Vd_pcc;
Vq_pcc_prev := Vq_pcc;
ed_prev := ed_curr;
eq_prev := eq_curr;
ud_prev := ud_curr;
uq_prev := uq_curr;
end when;
end LinearController_DQ_InverterSP;
One issue is that you are trying to do the Modelica translator's job, by manually handling the previous variables, and writing the assignments in order in an algorithm.
The normal equation-oriented way would be:
model LinearController_DQ_InverterSP
// reference currents
parameter Real Id_ref[3] = {0.0, 10.0, 20.0};
parameter Real Iq_ref[3] = {0.0, 5.0, 10.0};
//step times
parameter Real step_times[2] = {0.08, 0.12};
// current reference currents
Real Id_ref_curr;
Real Iq_ref_curr;
parameter Real Kp_cc = 0.0541 "Proportional term of controller";
parameter Real Ki_cc = 55.577 "Integral term of controller";
parameter Real T_sample = 1e-4 "Sampling period";
// current time step terms
Real ud_curr(start=0, fixed=true), uq_curr(start=0, fixed=true);
Real ed_curr(start=0, fixed=true), eq_curr(start=0, fixed=true);
Real Vin_d, Vin_q;
Real a_const = Kp_cc + Ki_cc * T_sample / 2;
Real b_const = -Kp_cc + Ki_cc * T_sample / 2;
Modelica.Blocks.Interfaces.RealOutput Mr(start = 0.87, fixed=true); // modulation index
Modelica.Blocks.Interfaces.RealOutput th(start=0.0, fixed=true); // angle
Modelica.Blocks.Interfaces.RealInput Id; // measured Id current
Modelica.Blocks.Interfaces.RealInput Iq; // measure Iq current
Modelica.Blocks.Interfaces.RealInput Vd_pcc(start = 220*sqrt(2), fixed = true); // Vd of measured grid voltage
Modelica.Blocks.Interfaces.RealInput Vq_pcc(start = 0.0, fixed = true); // Vq of measured grid voltage
equation
if time < step_times[1] then
Id_ref_curr = Id_ref[1];
Iq_ref_curr = Iq_ref[1];
elseif (time > step_times[1]) and (time < step_times[2]) then
Id_ref_curr = Id_ref[2];
Iq_ref_curr = Iq_ref[2];
else
Id_ref_curr = Id_ref[3];
Iq_ref_curr = Iq_ref[3];
end if;
when sample(0, T_sample) then
//Calc error
ed_curr = Id_ref_curr - Id;
eq_curr = Iq_ref_curr - Iq;
//Calc pid controller output
ud_curr = pre(ud_curr) + a_const * ed_curr + b_const * pre(ed_curr);
uq_curr = pre(uq_curr) + a_const * eq_curr + b_const * pre(eq_curr);
//Apply feedforward term
Vin_d = (pre(Vd_pcc) + pre(ud_curr));
Vin_q = (pre(Vq_pcc) + pre(uq_curr));
//Compute control signals
th = atan(Vin_q / Vin_d);
Mr = sqrt(Vin_d^2 + Vin_q^2) / 360;
end when;
end LinearController_DQ_InverterSP;
I haven't checked if there are odd interactions in terms of how the algorithm interacts with the assignments - but at least it is avoided in this variant.
Did you take a look at the step-size of the integrator and how it changes between the two scenarios? Usually, sampling (or any kind of discretization) forces the solver to take smaller steps by causing events. So e.g. enlarging the step-size from 1e-4s to 1e-3s should increase the performance by about a factor of 10.
Some references regarding this:
https://mbe.modelica.university/behavior/discrete/events/
http://people.inf.ethz.ch/~fcellier/Lect/NSDS/Ppt/nsds_21_engl.pdf.
Without having a closer look at the example (it doesn't work just by copy/paste), this would should be the explanation for your suspicion.
Related
Following this question, I'm trying to generate two time-dependent random functions omega1 and tau using this example. The difference is that I need to have two different sample periods of 0.05 and 0.17 for omega1 and tau respectively. I just duplicated the parts I thought would do the job:
model testData
extends Modelica.Icons.Example;
import Modelica.Math.Random.Generators;
import Modelica.Math.Random.Utilities;
parameter Real k = 50.0;
parameter Real J = 0.001;
Real theta1;
Real theta2;
Real omega2;
parameter Modelica.SIunits.Period samplePeriod1 = 0.05;
parameter Integer globalSeed1 = 30020;
parameter Integer localSeed1 = 614657;
output Real omega1;
parameter Modelica.SIunits.Period samplePeriod2 = 0.17;
parameter Integer globalSeed2 = 30020;
parameter Integer localSeed2 = 614657;
output Real tau;
protected
discrete Integer state1024[33](each start=0, each fixed = true);
algorithm
when initial() then
state1024 := Generators.Xorshift1024star.initialState(localSeed1, globalSeed1);
omega1 := 0;
elsewhen sample(0, samplePeriod1) then
(omega1, state1024) := Generators.Xorshift1024star.random(pre(state1024));
omega1 := (omega1 - 0.5) * 13;
end when;
when initial() then
state1024 := Generators.Xorshift1024star.initialState(localSeed2, globalSeed2);
omega1 := 0;
elsewhen sample(0, samplePeriod2) then
(tau, state1024) := Generators.Xorshift1024star.random(pre(state1024));
tau := (tau - 0.5) * 3;
end when;
public
parameter Integer id1 = Utilities.initializeImpureRandom(globalSeed1);
discrete Real rImpure1;
Integer iImpure1;
parameter Integer id2 = Utilities.initializeImpureRandom(globalSeed2);
discrete Real rImpure2;
Integer iImpure2;
algorithm
when initial() then
rImpure1 := 0;
iImpure1 := 0;
elsewhen sample(0, samplePeriod1) then
rImpure1 := Utilities.impureRandom(id=id1);
iImpure1 := Utilities.impureRandomInteger(
id=id1,
imin=-1234,
imax=2345);
end when;
when initial() then
rImpure2 := 0;
iImpure2 := 0;
elsewhen sample(0, samplePeriod2) then
rImpure2 := Utilities.impureRandom(id=id2);
iImpure2 := Utilities.impureRandomInteger(
id=id2,
imin=-1234,
imax=2345);
end when;
initial equation
theta1 = 0;
theta2 = 0;
der(theta2) = 0;
equation
der(theta1) = omega1;
der(theta2) = omega2;
J * der(omega2) = tau + k * (theta1 - theta2);
annotation(experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.02));
end testData;
however I get the error messages:
Symbolic Error
The given system is mixed-determined. [index > 3]
Please checkout the option "--maxMixedDeterminedIndex".
Translation Error
No system for the symbolic initialization was generated
I would appreciate if you could help me know what is the problem and how I can solve it.
P.S. considering that this code is apparantly compiling fine on Dymola, this could be a problem with OpenModelica. So I'm adding th JModelica tag in the case those guys can help me know if this compiles over there or not.
You have omega1 := 0; in two when initial()statements. Replace it by tau := 0; in the second one and the example will work.
I recommend to cleanup your code a bit. I found various smaller issues and needless code lines.
everything related to the impure random numbers can be removed
localSeed2 and globalSeed2 are useless when they are initialized like the other seed variables
state1024 is initialized at 3 different places (even though it works with OpenModelica): with start values and fixed=true and in two different when initial() statements
omega2 and tau2 don't need to be outputs. The Tool determines by itself what it has to compute.
And finally: Modelica models are a lot easier to debug and understand if existing blocks and physical components are used instead of writing lengthy code in a single class. Your model can also be built graphically with blocks from Modelica.Blocks.Noise and components from Modelica.Mechanics.Rotational.
Below is an updated version of your code with units, only one section for initialization and removed algorithm section (not necessary anymore due to the additional variables rand_omega and rand_tau).
model testData2
extends Modelica.Icons.Example;
import Modelica.Math.Random.Generators;
import Modelica.Math.Random.Utilities;
import SI = Modelica.SIunits;
parameter SI.RotationalSpringConstant k = 50.0;
parameter SI.Inertia J = 0.001;
parameter SI.Period samplePeriod_tau = 0.17;
parameter SI.Period samplePeriod_omega = 0.05;
parameter Integer globalSeed = 30020;
parameter Integer localSeed_tau = 614657;
parameter Integer localSeed_omega = 45613;
SI.Angle theta1, theta2;
SI.AngularVelocity omega1, omega2, rand_omega;
SI.Torque tau, rand_tau;
protected
discrete Integer state1024_tau[33];
discrete Integer state1024_omega[33];
initial equation
state1024_omega = Generators.Xorshift1024star.initialState(localSeed_omega, globalSeed);
state1024_tau = Generators.Xorshift1024star.initialState(localSeed_tau, globalSeed);
theta1 = 0;
theta2 = 0;
der(theta2) = 0;
equation
when sample(0, samplePeriod_omega) then
(rand_omega, state1024_omega) = Generators.Xorshift1024star.random(pre(state1024_omega));
end when;
when sample(0, samplePeriod_tau) then
(rand_tau, state1024_tau) = Generators.Xorshift1024star.random(pre(state1024_tau));
end when;
der(theta1) = omega1;
der(theta2) = omega2;
omega1 = (rand_omega - 0.5) * 13;
tau = (rand_tau - 0.5) * 3;
J * der(omega2) = 0 + k * (theta1 - theta2);
annotation(experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.02));
end testData2;
I'm trying to follow this example to generate a random function of time:
model testData
extends Modelica.Icons.Example;
parameter Real k = 1.0;
Real theta1;
Real theta2;
parameter Real tau = 1.0;
parameter Modelica.SIunits.Period samplePeriod = 0.05;
parameter Integer globalSeed = 30020;
output Real omega1;
algorithm
when initial() then
state1024 := Generators.Xorshift1024star.initialState(localSeed, globalSeed);
omega1 := 0;
elsewhen sample(0,samplePeriod) then
(omega1,state1024) := Generators.Xorshift1024star.random(pre(state1024));
end when;
public
parameter Integer id = Utilities.initializeImpureRandom(globalSeed);
discrete Real rImpure;
Integer iImpure;
algorithm
when initial() then
rImpure := 0;
iImpure := 0;
elsewhen sample(0,samplePeriod) then
rImpure := Utilities.impureRandom(id=id);
iImpure := Utilities.impureRandomInteger(
id=id,
imin=-1234,
imax=2345);
end when;
initial equation
theta1 = 0;
theta2 = 0;
der(theta2) = 0;
equation
der(theta1) = omega1;
der(der(theta2)) = tau + k * (theta1 - theta2);
annotation(experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.02));
end testData;
however, I get the error message:
Translation Error
Class Utilities.initializeImpureRandom not found in scope testData (looking for a function or record).
Translation Error
Error occurred while flattening model testData
I would appreciate if you could help me understand what is the problem and how I can solve it.
You were missing some imports, see below, some variable declarations and you were using der(der(...)) which doesn't work, you need to bind the internal der to a variable. This model below compiles and simulates (I don't know if the results are fine or not).
model testData
extends Modelica.Icons.Example;
import Modelica.Math.Random.Generators;
import Modelica.Math.Random.Utilities;
parameter Real k = 1.0;
Real theta1;
Real theta2;
Real der_theta2;
parameter Real tau = 1.0;
parameter Modelica.SIunits.Period samplePeriod = 0.05;
parameter Integer globalSeed = 30020;
parameter Integer localSeed = 614657;
output Real omega1;
discrete Integer state1024[33](each start=0, each fixed = true);
algorithm
when initial() then
state1024 := Generators.Xorshift1024star.initialState(localSeed, globalSeed);
omega1 := 0;
elsewhen sample(0,samplePeriod) then
(omega1,state1024) := Generators.Xorshift1024star.random(pre(state1024));
end when;
public
parameter Integer id = Utilities.initializeImpureRandom(globalSeed);
discrete Real rImpure;
Integer iImpure;
algorithm
when initial() then
rImpure := 0;
iImpure := 0;
elsewhen sample(0,samplePeriod) then
rImpure := Utilities.impureRandom(id=id);
iImpure := Utilities.impureRandomInteger(
id=id,
imin=-1234,
imax=2345);
end when;
initial equation
theta1 = 0;
theta2 = 0;
der(theta2) = 0;
der_theta2 = 0;
equation
der(theta1) = omega1;
der(theta2) = der_theta2;
der(der_theta2) = tau + k * (theta1 - theta2);
annotation(experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.02));
end testData;
The example Modelica.Math.Random.Examples.GenerateRandomNumbers uses relative class paths.
Utilities.initializeImpureRandom for example points to Modelica.Math.Random.Utilities.initializeImpureRandom, which works due to the package hierarchy
Modelica
|- Math
|- Random
|- Examples
|- Utilities
If you copy the code of the example to a different location, the relative paths will not work anymore.
Dymola updates relative paths when models are duplicated (via New > Duplicate Class). Openmodelica apparently not.
Just add the following two imports to the top of your code and the class paths will work:
import Modelica.Math.Random.Generators;
import Modelica.Math.Random.Utilities;
But your model contains additonal errors:
The declaration of the variables localSeed and state1024 is missing. Just copy them from the original example
der(der(theta2)) is not supported. Create an intermediate variable der_theta2 = der(theta2)
Following this question, I modified my code to:
model test
// types
type Mass = Real(unit = "Kg", min = 0);
type Length = Real(unit = "m");
type Area = Real(unit = "m2", min = 0);
type Force = Real(unit = "Kg.m/s2");
type Pressure = Real(unit = "Kg/m/s2");
type Torque = Real(unit = "Kg.m2/s2");
type Velocity = Real(unit = "m/s");
type Time = Real(unit = "s");
// constants
constant Real pi = 2 * Modelica.Math.asin(1.0);
parameter Mass Mp = 0.01;
parameter Length r1 = 0.010;
parameter Length r3 = 0.004;
parameter Integer n = 3;
parameter Area A = 0.020 * 0.015;
parameter Time Stepping = 1.0;
parameter Real DutyCycle = 1.0;
parameter Pressure Pin = 500000;
parameter Real Js = 1;
//parameter Real Muk = 0.0;
parameter Real Muk = 0.158;
// variables
Length x[n];
Velocity vx[n];
Real theta;
Real vt;
Pressure P[n];
Force Fnsp[n];
Torque Tfsc;
initial equation
theta = 0;
vt = 0;
algorithm
for i in 1:n loop
if noEvent((i - 1) * Stepping < mod(time, n * Stepping)) and noEvent(mod(time, n * Stepping) < Stepping * ((i - 1) + DutyCycle)) then
P[i] := Pin;
else
P[i] := 0;
end if;
end for;
Tfsc := -r3 * Muk * sign(vt) * abs(sum(Fnsp));
equation
vx = der(x);
vt = der(theta);
x = r1 * {sin(theta + (i - 1) * 2 * pi / n) for i in 1:n};
Mp * der(vx) + P * A = Fnsp;
Js * der(theta) = Tfsc - r1 * Fnsp * {cos(theta + (i - 1) * 2 * pi / n) for i in 1:n};
// Js * der(theta) = - r1 * Fnsp * {cos(theta + (i - 1) * 2 * pi / n) for i in 1:n};
annotation(
experiment(StartTime = 0, StopTime = 30, Tolerance = 1e-06, Interval = 0.03),
__OpenModelica_simulationFlags(lv = "LOG_STATS", outputFormat = "mat", s = "dassl"));
end test;
However, I get the preprocessing warning of
[1] .... Translation Warning
Iteration variables with default zero start attribute in torn nonlinear equation system:
Fnsp[3]:VARIABLE(unit = "Kg.m/s2" ) type: Real [3]
Fnsp[2]:VARIABLE(unit = "Kg.m/s2" ) type: Real [3]
Fnsp[1]:VARIABLE(unit = "Kg.m/s2" ) type: Real [3]
$DER.vt:VARIABLE() type: Real
which doesn't make sense but I assume I can safely ignore, and the compiling error of:
Matrix singular!
under-determined linear system not solvable
which had also been previously reported here. if I remove the lines
Torque Tfsc;
and
Tfsc := -r3 * Muk * sign(vt) * abs(sum(Fnsp));
and changing
Js * der(theta) = - r1 * Fnsp * {cos(theta + (i - 1) * 2 * pi / n) for i in 1:n};
works perfectly fine. However, setting Muk to zero, which theoretically the same thing leads to the same error as above! I would appreciate if you could help me know what is the problem and how I can resolve it.
P.S.1. On the demo version of Dymola the simulation test finishes with no errors, only the warning:
Some variables are iteration variables of the initialization problem:
but they are not given any explicit start values. Zero will be used.
Iteration variables:
der(theta, 2)
P[1]
P[2]
P[3]
P.S.2. Using JModelica, removing the noEvent and using the python code:
model_name = 'test'
mo_file = 'test.mo'
from pymodelica import compile_fmu
from pyfmi import load_fmu
my_fmu = compile_fmu(model_name, mo_file)
myModel = load_fmu('test.fmu')
res = myModel.simulate(final_time=30)
theta = res['theta']
t = res['time']
import matplotlib.pyplot as plt
plt.plot(t, theta)
plt.show()
it solves the model blazingly fast for small values (e.g. 0.1) of Muk. But again it gets stuck for bigger values. The only warnings are:
Warning at line 30, column 3, in file 'test.mo':
Iteration variable "Fnsp[2]" is missing start value!
Warning at line 30, column 3, in file 'test.mo':
Iteration variable "Fnsp[3]" is missing start value!
Warning in flattened model:
Iteration variable "der(_der_theta)" is missing start value!
You do not need to use algorithm for the assignments of equations (even if they are in a for-loop and an if). I moved them to the equation section and removed your algorithm section completely:
model test
// types
type Mass = Real(unit = "Kg", min = 0);
type Length = Real(unit = "m");
type Area = Real(unit = "m2", min = 0);
type Force = Real(unit = "Kg.m/s2");
type Pressure = Real(unit = "Kg/m/s2");
type Torque = Real(unit = "Kg.m2/s2");
type Velocity = Real(unit = "m/s");
type Time = Real(unit = "s");
// constants
constant Real pi = 2 * Modelica.Math.asin(1.0);
parameter Mass Mp = 0.01;
parameter Length r1 = 0.010;
parameter Length r3 = 0.004;
parameter Integer n = 3;
parameter Area A = 0.020 * 0.015;
parameter Time Stepping = 1.0;
parameter Real DutyCycle = 1.0;
parameter Pressure Pin = 500000;
parameter Real Js = 1;
//parameter Real Muk = 0.0;
parameter Real Muk = 0.158;
// variables
Length x[n];
Velocity vx[n];
Real theta;
Real vt;
Pressure P[n];
Force Fnsp[n];
Torque Tfsc;
initial equation
theta = 0;
vt = 0;
equation
for i in 1:n loop
if noEvent((i - 1) * Stepping < mod(time, n * Stepping)) and noEvent(mod(time, n * Stepping) < Stepping * ((i - 1) + DutyCycle)) then
P[i] = Pin;
else
P[i] = 0;
end if;
end for;
Tfsc = -r3 * Muk * sign(vt) * abs(sum(Fnsp));
vx = der(x);
vt = der(theta);
x = r1 * {sin(theta + (i - 1) * 2 * pi / n) for i in 1:n};
Mp * der(vx) + P * A = Fnsp;
Js * der(theta) = Tfsc - r1 * Fnsp * {cos(theta + (i - 1) * 2 * pi / n) for i in 1:n};
// Js * der(theta) = - r1 * Fnsp * {cos(theta + (i - 1) * 2 * pi / n) for i in 1:n};
end test;
This makes it far easier for the compiler to find a sensible sorting and tearing for the strong components. This still breaks at 19s but before that it may just be what you are looking for. The newton solver diverges after that threshold, since i don't really know what you are doing here i unfortunately cannot provide any analysis of the results.
Also it seems like your event triggered by your if-equation could be cleanly replaced by a Sample operator. You might want to have a look at that.
I'm trying to solve the code
model modelTest
// types
type Mass = Real (unit = "Kg", min = 0);
type Length = Real (unit = "m");
type Area = Real (unit = "m2", min = 0);
type Force = Real (unit = "Kg.m/s2");
type Pressure = Real (unit = "Kg/m/s2");
type Torque = Real (unit = "Kg.m2/s2");
type Velocity = Real (unit = "m/s");
type Time = Real (unit = "s");
// constants
constant Real pi = 2 * Modelica.Math.asin(1.0);
parameter Mass Mp = 0.01;
parameter Length r1 = 0.010;
parameter Integer n = 3;
parameter Area A = 0.020 * 0.015;
parameter Time Stepping = 0.1;
parameter Real DutyCycle = 0.5;
parameter Pressure Pin = 5000;
parameter Real Js = 1;
// variables
Length x[n];
Velocity vx[n];
Real theta;
Real vt;
Pressure P[n];
initial equation
theta = 0;
vt = 0;
algorithm
for i in 1:n loop
if noEvent((i - 1) * Stepping < mod(time, Stepping)) and noEvent(mod(time, Stepping) < (i - 1) * Stepping + Stepping * DutyCycle) then
P[i] := Pin;
else
P[i] := 0;
end if;
end for;
equation
vx = der(x);
vt = der(theta);
x = r1 * {sin(theta + (i -1) * 2 * pi / n) for i in 1:n};
Js * der(theta) = r1 * sum((Mp * der(vx) + P * A) .* {cos(theta + (i -1) * 2 * pi / n) for i in 1:n});
annotation(
experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.01),
__OpenModelica_simulationFlags(lv = "LOG_STATS", outputFormat = "mat", s = "dassl"));
end modelTest;
but the solver never finishes showing the error:
Failed to solve linear system of equations (no. 51) at time ... Residual norm is ...
The default linear solver fails, the fallback solver with total pivoting at time ... that might riase plv LOG_LS.
I would appreciate if you could help me know what is the problem and how I can solve it. Thanks for your support in advance.
P.S.1. I found this similar issue from 15 months ago.
P.S.2. There were several mistakes in the code. A modified version can be found here.
I have been trying to model flow through a pipe that can be partially full, or totally full in modelica and running it in OpenModelica. I have finally reduced the example to essentially just use the area of a circle, and to have no outflow until it is full, then complete outflow. But, I am still getting errors. I have tried it a few different ways. The first way gives an error about solving a nonlinear system once the pipe becomes "filled up". Instead I want it to switch over:
model SimplePipe1
Modelica.SIunits.Area A;
Modelica.SIunits.Mass mass;
Modelica.SIunits.Height level(start = 0.5, fixed = true, min = 0.0, max = 2 * R) "Liquid level (m)";
parameter Modelica.SIunits.Radius R = 1.0 "pipe inner radius (m)";
parameter Real flow_in = 1.0;
Real flow_out;
Modelica.SIunits.Angle phi(start = 3.1, min = 0.0, max = 7.2832) "Angle from center to surface level";
Modelica.SIunits.Area A;
Modelica.SIunits.Mass mass;
Modelica.SIunits.Height level(start = 0.5, fixed = true, min = 0.0, max = 2 * R) "Liquid level (m)";
parameter Modelica.SIunits.Radius R = 1.0 "pipe inner radius (m)";
parameter Real flow_in = 1.0;
Real flow_out;
Modelica.SIunits.Angle phi(start = 3.1, min = 0.0, max = 7.2832) "Angle from center to surface level";
equation
mass = A;
//Assume unit length pipe and unit density
flow_in + flow_out = der(mass);
A = 0.5 * R ^ 2 * (phi - sin(phi));
// phi = if noEvent(level <= 0) then 0 elseif noEvent(level >= 2 * R) then 2 * Modelica.Constants.pi else 2 * acos((R - level) / R);
if noEvent(level <= 0) then
phi = 0;
flow_out = 0;
elseif noEvent(level >= 2 * R) then
phi = 2 * Modelica.Constants.pi;
flow_out = -flow_in;
else
flow_out = 0;
//Partially full pipe has no out outflow
phi = 2 * acos((R - level) / R);
end if;
annotation(Icon, Diagram, experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.02));
end SimplePipe1;
This version seems to give results that are closer to what I want, but it still doesn't work. In this case, the problem is that phi is supposed to be limited to 2*pi. Instead it keeps increasing. Meanwhile, I don't actually see flowmode change. I do see the outflow go negative for a single cycle, then it jumps back to zero. I don't understand what is changing the flowmode back from channel to full, since there is no corresponding "when" to change it back.
model SimplePipe2
type modetype = enumeration(empty, full, channel);
modetype flowmode(start = modetype.channel);
Modelica.SIunits.Area A;
Modelica.SIunits.Mass mass;
Modelica.SIunits.Height level(start = 0.5, fixed = true, min = 0.0, max = 2 * R) "Liquid level (m)";
Modelica.SIunits.Height level_limit;
parameter Modelica.SIunits.Radius R = 1.0 "pipe inner radius (m)";
parameter Real flow_in = 1.0;
Real flow_out;
Modelica.SIunits.Angle phi(start = 3.1, min = 0.0, max = 7.2832) "Angle from center to surface level";
Real flow_out;
initial equation
flowmode = modetype.channel;
equation
mass = A;
//Assume unit length pipe and unit density
flow_in + flow_out = der(mass);
A = 0.5 * R ^ 2 * (phi - sin(phi));
cos(phi / 2) = (R - level) / R;
if flowmode == modetype.empty then
flow_out = 0;
elseif flowmode == modetype.full then
flow_out = -flow_in;
else
flow_out = 0;
//Partially full pipe has no out outflow
end if;
when noEvent(phi >= 2 * Modelica.Constants.pi) then
reinit(flow_out, -flow_in);
reinit(level, 2 * R);
flowmode = modetype.full;
end when;
annotation(Icon, Diagram, experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.02));
end SimplePipe2;
This question that I asked is related to solving the same problem, but doesn't have the issue of a circle/cylinder. And, my second example above is based somewhat on this question.
I am using the newest beta of OpenModelica. My complete model will have other feature that are not included in either of these examples. But, hopefully if I can get this simple version working, I can expand from there.
Your code ends up with a non-linear equation for phi (after level_limit and one flow_out was removed from the model).
0 = 0.5 * R ^ 2.0 * (phi - sin(phi)) - mass
OM solves this without adding constraints for the variable phi. The assertion is instead checked after the solution is found. If you use non-linear solver=kinsol in OpenModelica, the constraints are added to the non-linear equation, but it does not help in this case. I am also a bit unsure if when noEvent() would ever trigger.