How to describe media along a continuous production line with different operations? - modelica

I would like to describe a continuous production line with a media flow and with different process units along the line. Outflow of media from one unit is inflow to the next unit. The media can typically be described by a flow rate, pressure and the concentration of a number of substances. A core group of substances go through the whole production line but each unit may need to take into account some extra substances. The easiest way is to have a common media definition and keep substances zero for unit of operation where not relevant. It would be more concise and practical i think, to change media description for each unit to avoid a number of zero-variables.
So how do we in Modelica describe such a translation of core media components from one kind of media to another? And we would like to keep the connectors "balanced".
Let us take a look at a basic example. A feedtank is connected with a pipe to a harvesttank. Then feedtank has media components A and B while harvesttank has components B and C. The adaptation of media is done in the pipe-model. The code below works in both JModelica and OpenModelica and do not give any compiler complaints. Still I wonder if this approach is sound?
package DEMO_v52
// ---------------------------------------------------------------------------------------------
// Interfaces
// ---------------------------------------------------------------------------------------------
partial package MediumBase
constant String name "Medium name";
constant Integer nc = 1 "Number of substances";
replaceable type Concentration = Real[nc] "Substance conc";
end MediumBase;
// ---------------------------------------------------------------------------------------------
// Media specified
// ---------------------------------------------------------------------------------------------
package MediumAB
extends MediumBase
(name="Two component medium A, B",
nc=2);
constant Real[nc] mw = {10, 20} "Substance weight";
constant Integer A = 1 "Substance index";
constant Integer B = 2 "Substance index";
end MediumAB;
package MediumBC
extends MediumBase
(name="Two component medium B, C",
nc=2);
constant Real[nc] mw = {20, 30} "Substance weight";
constant Integer B = 1 "Substance index";
constant Integer C = 2 "Substance index";
end MediumBC;
// ---------------------------------------------------------------------------------------------
// Equipment dependent on the medium but written in a general way
// ---------------------------------------------------------------------------------------------
package EquipmentLib
import DEMO_v52.MediumAB;
import DEMO_v52.MediumBC;
connector LiquidConAB
Real p "Pressure";
flow Real F (unit="m3/s") "Flow rate";
stream MediumAB.Concentration c "Substance conc";
end LiquidConAB;
connector LiquidConBC
Real p "Pressure";
flow Real F (unit="m3/s") "Flow rate";
stream MediumBC.Concentration c "Substance conc";
end LiquidConBC;
model PipeType
LiquidConAB inlet;
LiquidConBC outlet;
parameter Real area = 1;
equation
inlet.F = -outlet.F;
outlet.F = area^2*(outlet.p - inlet.p); // Linearized Bernoulli equation
outlet.c[1] = inStream(inlet.c[2]);
outlet.c[2] = 0; // Reduction of media components
inlet.c[2] = inStream(outlet.c[1]);
inlet.c[1] = 0; // Reduction of media components
end PipeType;
model FeedtankType
LiquidConAB outlet;
parameter Real p = 0.1 "Pressure";
parameter Real V_0 (unit="m3") = 100 "Initial feed volume";
parameter Real[MediumAB.nc] c_in (each unit="kg/m3")
= {1.0*k for k in 1:MediumAB.nc} "Feed inlet conc";
Real V(start=V_0, fixed=true, unit="m3") "Feed volume";
equation
for i in 1:MediumAB.nc loop
outlet.c[i] = c_in[i];
end for;
outlet.p = p;
der(V) = outlet.F;
end FeedtankType;
model HarvesttankType
LiquidConBC inlet;
parameter Real p = 0.0 "Pressure";
parameter Real V_0 (unit="m3") = 1.0 "Initial harvest liquid volume";
parameter Real[MediumBC.nc] m_0
(each unit="kg/m3") = zeros(MediumBC.nc) "Initial substance mass";
Real[MediumBC.nc] m
(start=m_0, each fixed=true) "Substance mass";
Real[MediumBC.nc] c "Substance conc";
Real V(start=V_0, fixed=true, unit="m3") "Harvest liquid volume";
equation
inlet.p = p;
inlet.c = c;
der(V) = inlet.F;
for i in 1:MediumBC.nc loop
der(m[i]) = actualStream(inlet.c[i])*inlet.F;
c[i] = m[i]/V;
end for;
end HarvesttankType;
end EquipmentLib;
// ---------------------------------------------------------------------------------------------
// Example of a system
// ---------------------------------------------------------------------------------------------
model Test
EquipmentLib.FeedtankType feedtank;
EquipmentLib.HarvesttankType harvesttank;
EquipmentLib.PipeType pipe;
equation
connect(feedtank.outlet, pipe.inlet);
connect(pipe.outlet, harvesttank.inlet);
end Test;
end DEMO_v52;
In my immediate applications I do not need to model flow reversibility which would require special care if number of substances change between process units. Handling of flow reversibility is one important motivation for using the Modelica stream concept. Still I generally would like to keep using the stream concept even though I do not focus on flow reversibility. But is it good and proper use of the concept?
The alternative approach is, as mentioned above, to have a common media definition for all process units, and then describe describe that these substances are not affected in most units. This introduce a lot of states z _i of type dz_i/dt = 0 and is not only distracting to see, but may also bring a burden to numerical solvers, but I am not too sure. Can someone confirm or reject my worry here?
How is the need for varying detail of media in a continuous production line addressed in Modelica Standard Library?
In a more realistic example you may have an increasing number of substances to describe more downstream of the production line. For instances there are typically more substances related to quality at the end than in the beginning. It is awkward to introduce description of such substances early in the production since not relevant and would be described as zero.

I would go with the 'standardized' way to be able to use existing pipe, pump tank models etc. which are based on the FluidPort connector — if possible.
Probably, the biggest challenge is to create a medium model with all the different substances that you need in your process. However, if the fluid generally behaves like liquid water (density, specific enthalpy etc.) and the processes along the line only modify the "substances" without affecting the liquid behaviour significantly, you could consider using the trace substance properties of the medium and create a new medium based on water. For example for a water treatment plan:
package DHWater "District heating water with trace substances for desalination plant"
extends Modelica.Media.Water.ConstantPropertyLiquidWater(extraPropertiesNames
={"solids","anions","cations","carbonDioxide","oxygen"});
end DHWater;
In the MSL boundary components you should specify the contents of the trace substance vector C and in your own component models (digesters, filters etc.) you modify the C_outflow with your corresponding equations. Standard pipes, pumps etc. will simply pass on the trace substances.

There are several aspects of this question and I perceive some uncertainty around how to think about the impact of the stream concept in the situation of mixed media which it is not designed for really. Media can be much more complex than in the example above used for illustration of the problem. Below I try to summarize what I have got from you and some other knowledgeable people outside this forum.
Here is clear support for the "safe" advice given by Hans and Rene Just, to have a common media definition for many process units along the production line although some substances will be zero all the time.
The extra computational burden for these "zero-substances" are usually small, but may need analysis. There are models where keeping these "zero-substance" becomes a significant computational burden.
There is also in certain applications a tradition to use different media and construct adapter code to allow for transport of media between units with different media definition, as Rene Just describe. To allow for reversible flow you need careful design of the adapter code to make proper approximations. For equipment with the same media the stream concept handle reversible flow automatically in a much more exact way.
In applications where you can guarantee there is no reversible flow the adapter code can likely be simplified. Especially with a media of say incompressible water and trace substances, like my example above. It is some theoretical guidance here I was looking for.

Related

TIL-library EffCompressor: getInputRotary is structurally singular when Inertia

I am fairly new at working with the TIL-library (v. 3.12.0) in Dymola2022. I am trying to include some inertia in the current compressor models of the TIL-library. To start simple, I choose the EffCompressor as base model. The base model already has following equations using tau and w.
w = 2 PI n
tau = shaftPower / max(w, 1e-6)
The shafpower is calculated based on refrigerant mass flow and enthalpy at inlet and outlet.
If no mechanical port is used n is given a constant value using a model parameter and getInputsRotary.rotaryFlange.phi =0. In this case w is calculated using the first equation and than used to calculate tau using the second.
If a mechanical port is used, w=der(getInputsRotary.rotaryFlange.phi) and tau = getInputsRotary.rotatoryFlange.tau; (These values can be given by use of a RotaryBoundary)
My goal is to impose the inertia of the compressor (by defining its moment of inertia) and as such influence the change of rotational speed. I thus extended this model and added one unknown and 1 equation:
parameter Modelica.Units.SI.Inertia J = 1; (Parameter because I want to insert its value. Random value to test equations)
equation
tau=J*der(w);
The problem is this causes a singularity in the getInputsRotary-model, which only contains a rotational flange (with passes the phi and tau value from the RotaryBoundary).
I do not understand why this model because structurally singular (as I added one unknown and one equation). Defining J as a variable solves the problem with the model check, but gives a singularity error when running the model in a simple tester. Besides, the J should not be variable as it is a compressor property.)
I added the parameter J representing the moment of inertia. This results in a structurally singular problem. Also variants on this, by defining it as a variable, giving it an imposed value or not, etc. give the same problem.
What I would like to happen is that the change in rotational speed of the compressor accounts for the inserted moment of inertia without making the problem structurally singular.
Does anyone know why the problem becomes structurally singular and how I could address this? Thanks in advance!
Instead of modifying equations of a component model out of a library I would always prefer to extend the system model (graphically) by using additional components.
In your case you can easily do that by mixing TIL components with MSL (Modelica Standard Library) components. The mechanical connector from TIL is compatible to Modelica.Mechanics.Rotational components.
Adding inertia would look like that:

Density of the StandardWater - Modelica

How can I know what is the density of the Medium of StandardWater used in Modelica? With which function can I directly specify the density of the StandardWater?
First, instantiate the medium package in your model, e.g.
package Medium = Modelica.Media.Water.StandardWater;
Then, create a thermodynamic state, given e.g. pressure/temperature or pressure/enthalpy;
Medium.ThermodynamicState state = Medium.setState_pT(p=2e5, T=80+273.15);
And, finally, look up the density with the state
Medium.Density rho = Medium.density(state);

Variables interdependency terminates Dymola simulation and produces nonlinear system of equations

I’m developing a model to describe the behavior of a two-pipe network. The network is connected to a tank where heat is injected or extracted from the system depending on external mass flow rates. The mass flow rates for both heating and cooling are arbitrarily assumed to vary over time.
The initial value of PipeTemp is associated with the parameter StartTemp. However, at different time points PipeTemp is computed from a max function.
The problem is that because the variable PipeTemp depends on other time varying variables that are computed using the value of PipeTemp, Dymola terminates the simulation and produces the following error: Failed to solve nonlinear system using Newton solver.
This simple model can be easily simulated in Excel because it’s capable of handling interdependency between cell variables. What could be the workaround for this model in Dymola in order to avoid the nonlinear system of equations?
model FullyMixedTemperature
parameter Real StartTemp = 20; //Assumed mixed temperature in the pipes
parameter Real dTpipe = 10; //Temperature difference between the two pipes
parameter Real TankVol = 150; //Total volume
Real DecreasingTemp; //Mixed temperature in the pipe due to additional cooling mass flow rate
Real IncreasingTemp; //Mixed temperature in the pipe due to additional heating mass flow rate
Real PipeTemp(start=StartTemp); //Mixed temperature in the pipe
Real CoolFlowRate; //Additional cooling flow rate from external sources
Real HeatFlowRate; //Additional heating flow rate from external sources
equation
CoolFlowRate=0.5*time;
HeatFlowRate=2*time;
PipeTemp = max(DecreasingTemp, IncreasingTemp);
DecreasingTemp= PipeTemp-(dTpipe*CoolFlowRate/TankVol);
IncreasingTemp= PipeTemp+(dTpipe*HeatFlowRate/TankVol);
end FullyMixedTemperature;
The model as written doesn't make sense.
Since dTPipe, HeatFlowRate, CoolFlowRate and TankVol are all non-negative IncreasingTemp is greater than DecreasingTemp, and thus the equation collapses to:
PipeTemp=PipeTemp+dTPipe*HeatFlowRate/TankVol;
and you cannot compute PipeTemp from that.
The closest variant would be that in each sampling point we compute a new PipeTemp, and that would be:
model FullyMixedTemperature
parameter Real StartTemp = 20; //Assumed mixed temperature in the pipes
parameter Real dTpipe = 10; //Temperature difference between the two pipes
parameter Real TankVol = 150; //Total volume
Real DecreasingTemp; //Mixed temperature in the pipe due to additional cooling mass flow rate
Real IncreasingTemp; //Mixed temperature in the pipe due to additional heating mass flow rate
Real PipeTemp(start=StartTemp); //Mixed temperature in the pipe
Real CoolFlowRate; //Additional cooling flow rate from external sources
Real HeatFlowRate; //Additional heating flow rate from external sources
equation
CoolFlowRate=0.5*time;
HeatFlowRate=2*time;
when sample(1,1) then
PipeTemp = max(pre(DecreasingTemp), pre(IncreasingTemp));
end when;
DecreasingTemp= PipeTemp-(dTpipe*CoolFlowRate/TankVol);
IncreasingTemp= PipeTemp+(dTpipe*HeatFlowRate/TankVol);
end FullyMixedTemperature;
But to me it seems more likely you want a differential equation where both flows contribute:
model FullyMixedTemperature
parameter Real StartTemp = 20; //Assumed mixed temperature in the pipes
parameter Real dTpipe = 10; //Temperature difference between the two pipes
parameter Real TankVol = 150; //Total volume
Real PipeTemp(start=StartTemp); //Mixed temperature in the pipe
Real CoolFlowRate; //Additional cooling flow rate from external sources
Real HeatFlowRate; //Additional heating flow rate from external sources
equation
CoolFlowRate=0.5*time;
HeatFlowRate=2*time;
der(PipeTemp) =(dTpipe*HeatFlowRate/TankVol)-(dTpipe*CoolFlowRate/TankVol);
end FullyMixedTemperature;

First order Modelica model with null time constant

I'd like to figure out whether it's possible (and semantically legal) in Modelica to simulate a model of a first-order transfer function, with a time constant equal to zero (T below). I'm using OpenModelica 1.15.0~dev-48-g3656b95, but I'm asking the question in a general Modelica context.
My context is experimenting Model Order Reduction on Modelica models, which brings me to try to use the Modelica.Blocks.Continuous.FirstOrder with a sometimes null time constant. However, to make the discussion simpler, here is the flat model I'm working on (a simplification and adaptation of the standard FirstOrder block):
model FirstOrderZero
import Modelica.SIunits;
Real u "input";
Real y "output";
parameter Real k(unit="1")=1 "Gain";
constant SIunits.Time T=0 "Time Constant";
equation
u = 1;
der(y) = (k*u - y)/T;
end FirstOrderZero;
I understand that the way Modelica tools operate a symbolic equation analysis, the time constant T should be constant rather than a parameter. Indeed, for T=0, the differential equation gets degenerated into an algebraic equation y = k*u. Unless the Modelica simulation tool can generate different code pathways for different values of T (which I think no Modelica tool does, except maybe Modia in the future?), the fact that T is null or not should be decided at the beginning of the equation analysis.
What I don't understand is why the above model fails to simulate ("division by zero at time 0 [...] where divisor expression is 0.0" with OM 1.15 dev) whereas it works when the last equation is rewritten as:
T*der(y) = (k*u - y);
I would assume that the symbolic equation analysis should reformulate the equation automatically? (I can see with OM Transformational Debugger that the equation becomes der(y) = (k - y)/0.0 which, of course, breaks at simulation).
Or perhaps, is it syntactically illegal to write Modelica equations with a division by a null constant?
Variability of T
If the time constant T is constant, a parameter, or maybe a (discrete) variable depends on what you want to do.
the value of constants gets fixed during translation
parameters can be changed after translation (so before a simulation is started), but not during simulation
discrete variables can change their value during simulation, but only at event instances
continuous variables can change their value during simulation
See 4.4.4 Component Variability Prefixes discrete, parameter, constant in the Modelica Specification 3.4 for details.
For first order elements you usually use a transfer function that will not change during simulation, but the user should be able to set the value of T. Therefore parameter would be the natural choice.
Why your simulation fails
By using a constant for T, the Modelica tool can optimize your equations more than it can when you use a parameter. And depending on how you write your equations, you will end up with a different optimized equation.
For constant T=0 your original model reduces to
model FirstOrderZero
Real u, y;
parameter Real k=1;
equation
u = 1;
der(y) = (k*u - y)/0;
end FirstOrderZero;
To solve y its derivative der(y) is needed - but it can not be computed, as a division by zero always occurs.
In the second case with T*der(y) = (k*u - y); your model reduces to
model FirstOrderZero
Real u, y;
parameter Real k=1;
equation
u = 1;
0 * der(y) = (k*u - y);
end FirstOrderZero;
The equation 0 * der(y) = (k*u - y) results in 0 = (k*u - y) and therefore y = k*u. There is no division by zero and the model can be simulated.
You see, even though Modelica is an acausal language, it can matter how you write your equations.
What you can do
A first order element with T=0 is not a first order element anymore, but only a proportional gain. To model that, use the block Modelica.Blocks.Continuous.TransferFunction.
If T is not zero, parametrize it like this:
Modelica.Blocks.Continuous.TransferFunction transferFunction(b={k}, a={T,1})
and if its zero use
Modelica.Blocks.Continuous.TransferFunction transferFunction(b={k}, a={1})

how to use Partial derivative in modelica?

If i have to use a partial derivative in modelica, how can that be used. I am not sure if partial derivative can be solved in modelica but i would like to know, if it can be used then, how should it be implemented.
There are two different potential "partial derivatives" you might want. One is the partial derivative with respect to spatial variables (if you are interested in solving PDEs) or you might want the partial derivative of an expression with respect to a simulation variable.
But it doesn't matter, because you cannot express either of these in Modelica.
If your motivation is to solve PDEs, then I'm afraid you will simply have to process the spatial aspects in your models (using some kind of discretization, weak formulation, etc) so that the resulting equations are simple ODEs.
If you want to compute the derivative of expressions with respect to variables other than time, the question would be ... why? I'm hard pressed to think of an application where this is really necessary. But if you can explain your use case, I could comment further on how to handle it.
I've discretized PDE systems for solution in Modelica: heat equation, wave equation, PDEs from double-pipe heat exchangers, PDEs from water hammer to model pressure surges in pipelines etc
At a simple level, you can replace the spatial derivative with a central difference approximation, and then generate the entire set of ODEs with a for loop. For example. here's a Modelica code snippet for a simple discretization of the heat equation.
parameter Real L = 1 "Length";
parameter Integer n = 50 "Number of sections";
parameter Real alpha = 1;
Real dL = L/n "Section length";
Real[n] u(each start = 0);
equations
u[1] = 273; //boundary condition
u[n] =0; //boundary condition
for i in 2:n-1 loop
der(u[i]) = alpha * (u[i+1] - 2 * u[i] + u[i-1]) / dL^2;
end for;
This is just a simple example entered from the top of my head, so excuse any mistakes.
Do you have a specific example or application?