Solve equation system only once at initialization - modelica

I need modelica to solve an equation system for a variable only once at initialization. After that the variable 'turns' into a parameter and does not change any more. Is there any way to accomplish this?
As background information: I implemented a modelica model for a simple pump which has the input parameters maximum volume flow rate, pressure loss of the system at maximum flow rate, total pipe length and surface roughness. Now I need to calculate the corresponding (mean) hydraulic diameter of the pipes so that I can estimate the pressure loss at variable volume flow rate during the normal simulation. I'm using the Colebrook-White-Approach so I need to solve an equation system.
The code looks like this. The prefix var_ indicates its a variable, param_indicates it's a known parameter. I need var_d.
// calculation of velocity and reynolds number
var_w_max = param_Q_max/(Pi/4*var_d^2);
var_Re_max = var_w_max*var_d/param_my;
// Colebrook-White approach
1/sqrt(var_lambda_max) = -2*log10(2.51/(var_Re_max*var_lambda_max)+param_k/(3.71*var_d));
param_p_loss = var_lambda_max*param_l/var_d*param_rho_h2o*var_w_max^2/2;

If you want to compute a parameter based on values at the start and then freeze it you can use an initial equation.
E.g. if you want to compute param_p_loss and param_k based on the last two equations you do:
parameter Real param_p_loss(fixed=false);
parameter Real param_k(fixed=false);
initial equation
1/sqrt(var_lambda_max) = -2*log10(2.51/(var_Re_max*var_lambda_max)+param_k/(3.71*var_d));
param_p_loss = var_lambda_max*param_l/var_d*param_rho_h2o*var_w_max^2/2;
equation
...
The fixed=false mean that the parameter needs to be solved initially.

You can in fact solve for a parameter value during initialization. The clue lies in the modifier fixed=false.
Below is a simple example of a pressure drop where you solve for a hydraulic diameter during initialization to obtain a desired nominal mass flow.
model SolveParameter
parameter Modelica.SIunits.Diameter dh(fixed=false, start=0.1)
"Hydraulic diameter. Start attribute is guess value";
parameter Real k=0.06 "Roughness, pipe length etc. combined";
parameter Modelica.SIunits.MassFlowRate m_flow_nominal=2
"Nominal mass flow rate";
parameter Modelica.SIunits.PressureDifference dp=1e5
"Differential pressure (boundary condition)";
Modelica.SIunits.MassFlowRate m_flow "Time varying mass flow rate";
initial equation
m_flow = m_flow_nominal;
equation
m_flow = dh*k*sqrt(dp);
end SolveParameter;
If the diameter is a parameter within an instatiated class (pipe model) you can apply the fixed=false when you instantiate the model, i.e.
Modelica.Fluid.Pipes.DynamicPipe pipe(diameter(fixed=false));
Best regards,
Rene Just Nielsen

Related

Proper use of inStream() and and actualStream() in Modelica when no volume present

I have used the Modelica "stream" concept for connectors for some time. What I understand the functions inStream() and actualStream() are designed for use
when the model has a volume. But here are important cases where there is no
volume and you need for convenience stick to the connectors you have. One example is a ProbeSensor that is mounted into a reactor volume and measures one of the species in the liquid, but does not "consume" any liquid.
The code below works using inStream(). However, I am inclined to instead use actualStream() since it "handles zero flow". But if I do the change the model does not compile and I get translation error that here are more variable than equations.
Is the code with inStream() after all correct?
Or how should it be modified?
LiquidCon
stream Real[2] c;
flow Real F;
Real p;
end LiquidCon;
block ProbeSensor
LiquidCon probe;
output RealOutput out;
constant Integer component = 2 "The liquidphase component measured index";
parameter Real T (unit="h") = 0.05 "Time constant of measurement";
parameter Real x_0 = 0.0 "Initial state of measurement device";
Real x(start=x_0, fixed=true) "State variable measurement device";
Real p (unit="bar") "Pressure";
equation
probe.F = 0;
p = probe.p;
for i in 1:2 loop
if (i==component) then
T*der(x) + x = inStream(probe.c[component]);
inStream(probe.c[component]) = probe.c[component];
out = x;
else
inStream(probe.c[i]) = probe.c[i];
end if;
end for;
end ProbeSensor;
Yes, inStream() is the best solution for this type of sensor/probe model.
inStream() gives you a value for the hypothetical case of fluid streaming into the component model. No matter what the real flow direction is (in this case 0 flow). And that's perfectly right for sensors.
As a general rule: If you can do something with inStream() than go for it. Only use actualStream() if you really need it.
Reason for that: actualStream() is basically an if expression. And that is always nonlinear, which can easily produce ugly nonlinear systems in the overall system model.

Modelica - freezing a specific time value during simulation

I am having a problem that could be easily solved in a causal environment like Fortran, but has proved difficult in Modelica, considering my limited knowledge
Consider a volume with an inlet and outlet. The inlet mass flow rate is specified, while the outlet mass flow is calculated based on pressure in the volume. When pressure in the volume goes above a set point, the outlet area starts to increase linearly from its initial value to a max value and remains fixed afterwards. In other words:
A = min( const * (t - t*) + A_0, A_max)
if p > p_set
where t* = the time at which pressure in the volume exceeds the set pressure.
The question is: there's a function to capture t* during the simulation? OR how could the model be programmed to do it? I have tried a number of ways, but models are never closed. Thoughts are welcome and appreciated!
Happy holidays/New Year!
Mohammad
You may find the sample and hold example in my book useful. It uses sampling based on time whereas you probably want it based on your pressure value. But the principle is the same. That will allow you to record the time at which your event occurred.
Addressing your specific case, the following (untested) code is probably pretty close to what you want:
...
Modelica.SIunits.Time t_star=-1;
equation
when p >= p_set then
t_star = time;
end when;
A = if t_star<0 then A_max else min(const*(t - t_star) + A_0, A_max);

Inversing the modelica simulation model: steady state model

I want to know if a model can be inversed in modelica. (here inverse means: if in causal statement y= x +a; x and a are input and y is output; but if I want to find 'x' as output and 'y' and 'a' as input, the model is called reversed/inversed model) For example, if I have compressor with input air port and output air port, and port has variables associated with it are pressure(P), temperature(T) and mass flow rate(mdot). I have simple steady state model containing three equations as follow:
OutPort.mdot = InPort.mdot
OutPort.P = rc * InPort.P
OutPort.T = InPort.T * (1 + rc[ (gamma-1)/gamma) - 1][/sup] / eta);
Here, rc, gamma and eta are compression ratio, ratio of specific heat capacitites and efficiency of compressor respectively.
I want to know, if I know values of : gamma, eta, OutPort.mdot, OutPort.P and OutPort.T and InPort.P and InPort.T, can I find the value of rc.
Can I find values of rc and how should be the model of compressor with above equation in Modelica. As far as I know, there are some variables designated as parameters which can not be changed during simulation. How the modelica model should be with above equations
Thanks
Yes, this should not be a problem as long as you make sure that rc is not a parameter, but a normal variable, and you supply the appropriate number of known quantities to achieve a balanced system (roughly, number of unknowns matches number of equations).
E.g. in your case if you know/supply OutPort.P and InPort.P, rc is already determined from eq 2. Then, in the third equation, there are no unknowns left, so either the temperature values are consistent with the equation or you (preferably) leave one temperature value undetermined.
In addition if you only want to compute the parameter rc during steady-state initialization i.e. that nothing changes with time that is also possible:
...
parameter Real rc(fixed=false);
initial equation
Inport.mdot=12; // Or something else indirectly determining rc.
The fixed=false means that rc is indirectly determined from the initialization. However, if the model is not completely stationary it will only find the correct rc during the initialization and then use that afterwards.

Simulink - Building custom components

I have one "Thermal Mass" block in Simulink, which represents a thermal mass, which is the ability of a material or combination of materials to store internal energy. In this standard block of Simulink, the initial temperature must be entered. Only one signal can be connected to the block. The source code of the block looks like following:
component mass
% Thermal Mass
% The block represents a thermal mass, which is the ability of a material
% or combination of materials to store internal energy. The property is
% characterized by mass of the material and its specific heat.
%
% The block has one thermal conserving port.
% The block positive direction is from its port towards the block. This
% means that the heat flow is positive if it flows into the block.
% Copyright 2005-2013 The MathWorks, Inc.
nodes
M = foundation.thermal.thermal; % :top
end
parameters
mass = { 1, 'kg' }; % Mass
sp_heat = { 447, 'J/(kg*K)' }; % Specific heat
end
variables
Q = { 0, 'J/s' }; % Heat flow
end
variables(Conversion=absolute)
T = { 300, 'K' }; % Temperature
end
function setup
% Parameter range checking
if mass <= 0
pm_error('simscape:GreaterThanZero','Mass')
end
if sp_heat <= 0
pm_error('simscape:GreaterThanZero','Specific heat')
end
end
branches
Q : M.Q -> *;
end
equations
T == M.T;
Q == mass * sp_heat * T.der;
assert(T>0, 'Temperature must be greater than absolute zero')
end
end
I would like to build another component, whose initial temperature can come from another block, so that it can be also calculated somewhere else. So, one input parameter and everything else should be the same. I am new to Simulink and don't know much about the domains. Any idea, how this can be done?
Thank you!
Parameters entered on a Simulink block are usually utilized for initial values and tuning of block behavior. While newer versions of Simulink will allow you to tune some parameters during simulation, others will be locked down and un-modifiable. This may mean that you need to first execute a model to calculate the initial value for your Thermal Mass, and then start up a second simulation using that temperature as an initial value.
I believe the Simulink help on how to control block parameters will be useful. Depending on the specific design of your model, different techniques found here may be more or less applicable, but generally speaking I know of 2 easy and simple ways to accomplish modifying a mask value.
Set the value to a variable in your Matlab base workspace.
Place the block inside a Masked subsystem. The mask can be used to define a variable that accessible to all the blocks inside it.
This is not possible, while you can execute some pre-processing to determine initial temperature you can not have this as an input from other blocks.
The workaround described by Jared is probably what you're looking for.
It's actually pretty rare to need to do this, if you tell us why you'r looking to set this up, we may be able to help.

Simulating spatial PDEs in Modelica - Accessing variable values at specific times

This question is somewhat related to a previous question of mine, where I didn't quite get the right solution. Link: Earlier SO-thread
I am solving PDEs which are time variant with one spatial dimension (e.g. the heat equation - see link below). I'm using the numerical method of lines, i.e. discretizing the spatial derivatives yielding a system of ODEs which are readily solved in Modelica (using the Dymola tool). My problems arise when I simulate the system, or when I plot the results, to be precise. The equations themselves appear to be solved correctly, but I want to express the spatial changes in all the discretized state variables at specific points in time rather than the individual time-varying behavior of each discrete state.
The strategy leading up to my problems is illustrated in this Youtube tutorial, which by the way is not made by me. As you can see at the very end of the tutorial, the time-varying behavior of the temperature is plotted for all the discrete points in the rod, individually. What I would like is a plot showing the temperature through the rod at a specific time, that is the temperature as a function of the spatial coordinate. My strategy to achieve this, which I'm struggling with, is: Given a state vector of N entries:
Real[N] T "Temperature";
..I would use the plotArray Dymola function as shown below.
plotArray( {i for i in 1:N}, {T[i] for i in 1:N} )
Intuitively, this would yield a plot showing the temperature as a function of the spatial coordiate, or the number in the line of discrete units, to be precise. Although this command yields a result, all T-values appear to be 0 in the plot, which is definitely not the case. My question is: How can I successfully obtain and plot the temperatures at all the discrete points at a given time? Thanks in advance for your help.
The code for the problem is as indicated below.
model conduction
parameter Real rho = 1;
parameter Real Cp = 1;
parameter Real L = 1;
parameter Real k = 1;
parameter Real Tlo = 0;
parameter Real Thi = 100;
parameter Real Tinit = 30;
parameter Integer N = 10 "Number of discrete segments";
Real T[N-1] "Temperatures";
Real deltaX = L/N;
initial equation
for i in 1:N-1 loop
T[i] = Tinit;
end for;
equation
rho*Cp*der(T[1]) = k*( T[2] - 2*T[1] + Thi) /deltaX^2;
rho*Cp*der(T[N-1]) = k*( Tlo - 2*T[N-1] + T[N-2]) /deltaX^2;
for i in 2:N-2 loop
rho*Cp*der(T[i]) = k*( T[i+1] - 2*T[i] + T[i-1]) /deltaX^2;
end for
annotation (uses(Modelica(version="3.2")));
end conduction;
Additional edit: The simulations show clearly that for example T[3], that is the temperature of discrete segment no. 3, starts out from 30 and ends up at 70 degrees. When I write T[3] in my command window, however, I get T3 = 0.0 in return. Why is that? This is at the heart of the problem, because the plotArray function would be working if I managed to extract the actual variable values at specific times and not just 0.0.
Suggested solution: This is a rather tedious solution to achieve what I want, and I hope someone knows a better solution. When I run the simulation in Dymola, the software generates a .mat-file containing the values of the variables throughout the time of the simulation. I am able to load this file into MATLAB and manually extract the variables of my choice for plotting. For the problem above, I wrote the following command:
plot( [1:9]' , data_2(2:2:18 , 10)' )
This command will plot the temperatures (as the temperatures are stored together with their derivates in the data_2 array in the .mat-file) against the respetive number of the discrete segment/element. I was really hoping to do this inside Dymola, that is avoid using MATLAB for this. For this specific problem, the amount of variables was low on account of the simplicity of this problem, but I can easily image a .mat-file which is signifanctly harder to navigate through manually like I just did.
Although you do not mention it explicitly I assume that you enter your plotArray command in Dymola's command window. That won't work directly, since the variables you see there do not include your simulation results: If I simulate your model, and then enter T[:] in Dymola's command window, then the printed result is
T[:]
= {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}
I'm not a Dymola expert, and the only solution I've found (to actively store and load the desired simulation results) is quite cumbersome:
simulateModel("conduction", resultFile="conduction.mat")
n = readTrajectorySize("conduction.mat")
X = readTrajectory("conduction.mat", {"Time"}, n)
Y = readTrajectory("conduction.mat", {"T[1]", "T[2]", "T[3]"}, n)
plotArrays(X[1, :], transpose(Y))