I'm a beginner at openmodelica.
I am trying to combine the following two MSL components to create a sensor that outputs mole fraction. I think that to calculate the mole fraction(using(2)), it is necessary to refer to all the molecular weights of the components of the mixed gas specified in media, but I don't know how to code this.
(1)Modelica.Fluid.Sensors.MassFractions (2)Modelica.Media.Interfaces.PartialMixtureMedium.massToMoleFractions
It would be helpful if you could provide an example.
add screenshot. Gas data was created based on "Modelica.Media.IdealGases" and its Ar(argon). In this model, it is like written for example,
Modelica.Fluid.Sources.MassFlowSource_T inlet_A(redeclare package Medium = example2025.mixABC, use_m_flow_in = true, use_T_in = false, use_X_in = false, use_C_in = false, nPorts = 1, T = 373.15, X = {1.0, 0.0, 0.0})
Modelica.Fluid.Sensors.MassFractionsTwoPort XC2(redeclare package Medium = example2025.mixABC, substanceName = "gasC")
Thank you for your answer. After seeing this answer, I tried the following, but it didn't work. (1) Automatically get the size of the MMX array (2) Automatically get the MMX elements.
model MolarFractionsTwoPort
extends Modelica.Fluid.Sensors.BaseClasses.PartialFlowSensor;
extends Modelica.Icons.RoundSensor;
Modelica.Blocks.Interfaces.RealOutput Xi "Mass fraction in port medium"
annotation (Placement(transformation(extent={{-10,-10},{10,10}},
rotation=90,
origin={0,110}), iconTransformation(
extent={{-10,-10},{10,10}},
rotation=90,
origin={0,110})));
parameter String substanceName = "water" "Name of mass fraction";
Real MMX[Medium.nXi]; //***(1)***
protected
parameter Integer ind(fixed=false)
"Index of species in vector of independent mass fractions";
initial algorithm
ind:= -1;
for i in 1:Medium.nXi loop
if ( Modelica.Utilities.Strings.isEqual(Medium.substanceNames[i], substanceName)) then
ind := i;
end if;
//MMX[i]:=Medium.fluidConstants[i].molarMass; //***(2)***
MMX[i] := Modelica.Media.Interfaces.Types.Basic.FluidConstants[i].molarMass;//***(2)***
end for;
.......
end MolarFractionsTwoPort;
I'm sure there's a better way, but I don't know it.
I'm a beginner at openmodelica.
I am trying to combine the following two MSL components to create a sensor that outputs mole fraction. I think that to calculate the mole fraction(using(2)), it is necessary to refer to all the molecular weights of the components of the mixed gas specified in media, but I don't know how to code this.
(1)Modelica.Fluid.Sensors.MassFractions (2)Modelica.Media.Interfaces.PartialMixtureMedium.massToMoleFractions
It would be helpful if you could provide an example.
add screenshot. Gas data was created based on "Modelica.Media.IdealGases" and its Ar(argon). In this model, it is like written for example,
Modelica.Fluid.Sources.MassFlowSource_T inlet_A(redeclare package Medium = example2025.mixABC, use_m_flow_in = true, use_T_in = false, use_X_in = false, use_C_in = false, nPorts = 1, T = 373.15, X = {1.0, 0.0, 0.0})
Modelica.Fluid.Sensors.MassFractionsTwoPort XC2(redeclare package Medium = example2025.mixABC, substanceName = "gasC")
Thank you for your answer. After seeing this answer, I tried the following, but it didn't work. (1) Automatically get the size of the MMX array (2) Automatically get the MMX elements.
model MolarFractionsTwoPort
extends Modelica.Fluid.Sensors.BaseClasses.PartialFlowSensor;
extends Modelica.Icons.RoundSensor;
Modelica.Blocks.Interfaces.RealOutput Xi "Mass fraction in port medium"
annotation (Placement(transformation(extent={{-10,-10},{10,10}},
rotation=90,
origin={0,110}), iconTransformation(
extent={{-10,-10},{10,10}},
rotation=90,
origin={0,110})));
parameter String substanceName = "water" "Name of mass fraction";
Real MMX[Medium.nXi]; //***(1)***
protected
parameter Integer ind(fixed=false)
"Index of species in vector of independent mass fractions";
initial algorithm
ind:= -1;
for i in 1:Medium.nXi loop
if ( Modelica.Utilities.Strings.isEqual(Medium.substanceNames[i], substanceName)) then
ind := i;
end if;
//MMX[i]:=Medium.fluidConstants[i].molarMass; //***(2)***
MMX[i] := Modelica.Media.Interfaces.Types.Basic.FluidConstants[i].molarMass;//***(2)***
end for;
.......
end MolarFractionsTwoPort;
I'm sure there's a better way, but I don't know it.
Share Improve this question edited Mar 31 at 15:06 Bocchi asked Mar 21 at 13:51 BocchiBocchi 214 bronze badges 5- Essentially I think that your chosen component Modelica.Fluid.MassFractions should do the calculation for you, provided you enter a "complete" media as parameter to the MassFraction - sensor, i.e. "partial media" is not ok. Could you provide some screenshot of how you combine components? – janpeter Commented Mar 24 at 10:05
- 2 Thank you for your comment. I have attached a screenshot of OMedit. What is "complete" media? – Bocchi Commented Mar 24 at 16:42
- Now I understand your original problem much better. The item (2) you refer to is a function which do the conversion you are looking for. The molecular weight are, what I understand, present in the declared media How to bring this important function in to your process, I am not sure of. (What I meant with "complete media" was just to say that "partial class" in general cannot be used directly but serves as a template to make a class (model or package) that can be used, i.e. "complete". The comment is not really useful in your problem). – janpeter Commented Mar 25 at 8:05
- In Modelica/Meida/IdealGases/Common/SingleGasNasa here is density function and that make me think that you can extract the molar weight of at least this kind of IdealGases. I suspect one needs to make your own "sensor" model that includes the suggested function, but I am not sure. I also tend to think that your request is vary basic and should be more easily available in MSL. – janpeter Commented Mar 25 at 9:00
- I think about your code for "MolarFractionTwoPort" and try to find out how to fix it. The package Medium should be accessible although not declared in your code. It is inherited from "PartialFlowSensor" and below that "Interfaces/PartialTwoPort". The Medium needs to be redeclared in your code though when you make an instance of your sensor. – janpeter Commented Apr 7 at 8:00
2 Answers
Reset to default 1I think I have solved your problem by coding up "our own sensor" as I suggested before.
The solution is rather pedestrian and can certainly be improved.
I have worked with a somewhat simpler system that highlight your problem and solve it, I think.
I work with a mixture of six gases you find in Modelica.Media.IdealGases.MixtureGases
The process has only one flow and one sensor
The key is to make "your own sensor" based on the standard sensor. In "your own sensor" you include the function massToMolarFractions(). Here I copied it in simply. Better would be to import it but I got complaint about that the function is "partial" and is mainly due to that it is included in a partial package. There is likely a way to avoid copying in the code, but I do not know how to do that.
The GUI icon for "our own sensor" is missing, but I skipped that. The code works.
I hope you from this example get inspiration of how to solve your own slightly different problem.
model Test21
inner Modelica.Fluid.System system annotation(
Placement(transformation(origin = {74, -80}, extent = {{-10, -10}, {10, 10}})));
Modelica.Fluid.Vessels.ClosedVolume volume(
nPorts = 1,
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
use_portsData = true,
V = 1,
portsData(each diameter = 0.01))
annotation(Placement(transformation(origin = {-49, 1}, extent = {{-33, -33}, {33, 33}})));
Modelica.Fluid.Pipes.StaticPipe pipe(
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
length = 1,
diameter = 0.01)
annotation(Placement(transformation(origin = {-20, -52}, extent = {{-10, -10}, {10, 10}})));
Modelica.Fluid.Pipes.StaticPipe pipe2(
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
length = 1,
diameter = 0.01)
annotation(Placement(transformation(origin = {46, -24}, extent = {{-10, -10}, {10, 10}})));
Modelica.Fluid.Sources.FixedBoundary boundary(
nPorts = 1,
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas)
annotation(Placement(transformation(origin = {80, -24}, extent = {{-10, -10}, {10, 10}}, rotation = 180)));
model MassFraction1 "our own sensor"
extends Modelica.Fluid.Sensors.MassFractionsTwoPort(
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
substanceName = "Methane")
annotation(Placement(transformation(origin = {14, -24}, extent = {{-10, -10}, {10, 10}})));
Modelica.Blocks.Interfaces.RealOutput Mi "Molar fraction in port medium";
parameter Real MMX[6] = {Medium.fluidConstants[1].molarMass, Medium.fluidConstants[2].molarMass,
Medium.fluidConstants[3].molarMass, Medium.fluidConstants[4].molarMass,
Medium.fluidConstants[5].molarMass, Medium.fluidConstants[6].molarMass};
// Modelica.Media.Interfaces.PartialMixtureMedium.massToMoleFractions f;
function massToMoleFractions "Return mole fractions from mass fractions X"
extends Modelica.Icons.Function;
import Modelica.Units.SI;
input SI.MassFraction X[:] "Mass fractions of mixture";
input SI.MolarMass[:] MMX "Molar masses of components";
output SI.MoleFraction moleFractions[size(X, 1)] "Mole fractions of gas mixture";
protected
Real invMMX[size(X, 1)] "Inverses of molar weights";
SI.MolarMass Mmix "Molar mass of mixture";
algorithm
for i in 1:size(X, 1) loop
invMMX[i] := 1/MMX[i];
end for;
Mmix := 1/(X*invMMX);
for i in 1:size(X, 1) loop
moleFractions[i] := Mmix*X[i]/MMX[i];
end for;
annotation (smoothOrder=5);
end massToMoleFractions;
Real c[6], X[6];
equation
// I assume allowFlowReversal = false
X = {port_b.Xi_outflow[1], port_b.Xi_outflow[2], port_b.Xi_outflow[3],
port_b.Xi_outflow[4], port_b.Xi_outflow[5], port_b.Xi_outflow[6]};
c = massToMoleFractions(X, MMX);
Mi = c[1];
end MassFraction1;
Real a, b, c, d, e, f;
MassFraction1 massFraction1;
equation
a = massFraction1.c[1];
b = massFraction1.c[2];
c = massFraction1.c[3];
d = massFraction1.c[4];
e = massFraction1.c[5];
f = massFraction1.c[6];
connect(volume.ports[1], pipe.port_a) annotation(
Line(points = {{-49, -32}, {-49, -52}, {-30, -52}}, color = {0, 127, 255}));
connect(pipe.port_b, massFraction1.port_a) annotation(
Line(points = {{-10, -52}, {2, -52}, {2, -24}, {4, -24}}, color = {0, 127, 255}));
connect(massFraction1.port_b, pipe2.port_a) annotation(
Line(points = {{24, -24}, {36, -24}}, color = {0, 127, 255}));
connect(pipe2.port_b, boundary.ports[1]) annotation(
Line(points = {{56, -24}, {70, -24}}, color = {0, 127, 255}));
annotation(uses(Modelica(version = "4.0.0")));
end Test21;
I have taken inspiration from Bocchis structure and provided a better solution than my previous one.
I think you can copy my solution of the sensor and bring in to your problem with a different media and process setup, and should work.
Key aspects are that the "Medium" is inherited from the underlying base model we extend from "two steps down". Further, we redeclare the "Medium" when we make an instance of the sensor for the actual applications, as we need to do for all the other components.
Now the code shows how the function "MassToMoleFractions" can be referred to without copying! The trick is to embed the "PartialMixtureMedium" that contains the function of interest in another package I call "MixtureMedium".
The code could likely be further improved. Seems not necessary to calculate the molar mass fraction numbers for all components when we only need one. Further I have not fixed the GUI of the sensor. I leave this for someone else to address.
For clarity, I use MSL 4.0.0 and run OpenModelica ver 1.25.
model Test25
inner Modelica.Fluid.System system annotation(
Placement(transformation(origin = {74, -80}, extent = {{-10, -10}, {10, 10}})));
Modelica.Fluid.Vessels.ClosedVolume volume(
nPorts = 1,
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
use_portsData = true,
V = 1,
portsData(each diameter = 0.01))
annotation(Placement(transformation(origin = {-49, 1}, extent = {{-33, -33}, {33, 33}})));
Modelica.Fluid.Pipes.StaticPipe pipe(
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
length = 1,
diameter = 0.01)
annotation(Placement(transformation(origin = {-20, -52}, extent = {{-10, -10}, {10, 10}})));
Modelica.Fluid.Pipes.StaticPipe pipe2(
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
length = 1,
diameter = 0.01)
annotation(Placement(transformation(origin = {46, -24}, extent = {{-10, -10}, {10, 10}})));
Modelica.Fluid.Sources.FixedBoundary boundary(
nPorts = 1,
redeclare package Medium = Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas)
annotation(Placement(transformation(origin = {80, -24}, extent = {{-10, -10}, {10, 10}}, rotation = 180)));
model MolarFractionsTwoPort
extends Modelica.Fluid.Sensors.BaseClasses.PartialFlowSensor;
package MixtureMedium extends Modelica.Media.Interfaces.PartialMixtureMedium; end MixtureMedium;
extends Modelica.Icons.RoundSensor;
Modelica.Blocks.Interfaces.RealOutput MMXi "Molar Mass fraction in port medium"
annotation (Placement(transformation(extent={{-10,-10},{10,10}},rotation=90,
origin={0,110}), iconTransformation, extent={{-10,-10},{10,10}},rotation=90, origin={0,110}));
parameter String substanceName = "water" "Name of mass fraction";
Real[Medium.nXi] X;
Real[Medium.nXi] MMX;
// function massToMoleFractions "Return mole fractions from mass fractions X"
// extends Modelica.Icons.Function;
// import Modelica.Units.SI;
// input SI.MassFraction X[:] "Mass fractions of mixture";
// input SI.MolarMass[:] MMX "Molar masses of components";
// output SI.MoleFraction moleFractions[size(X, 1)] "Mole fractions of gas mixture";
// protected
// Real invMMX[size(X, 1)] "Inverses of molar weights";
// SI.MolarMass Mmix "Molar mass of mixture";
// algorithm
// for i in 1:size(X, 1) loop
// invMMX[i] := 1/MMX[i];
// end for;
// Mmix := 1/(X*invMMX);
// for i in 1:size(X, 1) loop
// moleFractions[i] := Mmix*X[i]/MMX[i];
// end for;
// annotation (smoothOrder=5);
// end massToMoleFractions;
protected
parameter Integer ind(fixed=false)
"Index of species in vector of independent mass fractions";
initial algorithm
ind:= -1;
for i in 1:Medium.nXi loop
if ( Modelica.Utilities.Strings.isEqual(Medium.substanceNames[i], substanceName)) then
ind := i;
end if;
end for;
assert(ind > 0, "Mass fraction '" + substanceName + "' is not present in medium '"
+ Medium.mediumName + "'.\n"
+ "Check sensor parameter and medium model.");
equation
if allowFlowReversal then
for i in 1:Medium.nXi loop
X[i] = Modelica.Fluid.Utilities.regStep(port_a.m_flow, port_b.Xi_outflow[i], port_a.Xi_outflow[i], m_flow_small);
end for;
else
for i in 1:Medium.nXi loop
X[i] = port_b.Xi_outflow[I];
end for;
end if;
// MMX = massToMoleFractions(X, Medium.MMX);
MMX = MixtureMedium.massToMoleFractions(X, Medium.MMX);
MMXi = MMX[ind];
annotation (defaultComponentName="massFraction",
Icon(coordinateSystem(preserveAspectRatio=true,
extent={{-100,-100},{100,100}}), graphics={Text(extent={{82,122},{0,92}},textString="Xi"),Line(points={{0,100},{0,70}}, color={0,0,127}),Line(points={{-100,0},{-70,0}}, color={0,128,255}),Line(points={{70,0},{100,0}}, color={0,128,255})}),
Documentation(
info="<html>
<p>This component monitors the mass fraction of the passing fluid. The sensor is ideal, i.e., it does not influence the fluid.
</p> </html>",
revisions="<html>
<ul><li>2011-12-14: Stefan Wischhusen: Initial Release.</li><li>2018-01-04: Stefan Wischhusen: Corrected failure in accessing the named substance.</li></ul></html>"));
end MolarFractionsTwoPort;
MolarFractionsTwoPort molarFraction(redeclare package Medium =
Modelica.Media.IdealGases.MixtureGases.SimpleNaturalGas,
substanceName = "Methane");
equation
connect(volume.ports[1], pipe.port_a) annotation(
Line(points = {{-49, -32}, {-49, -52}, {-30, -52}}, color = {0, 127, 255}));
connect(pipe.port_b, molarFraction.port_a) annotation(
Line(points = {{-10, -52}, {2, -52}, {2, -24}, {4, -24}}, color = {0, 127, 255}));
connect(molarFraction.port_b, pipe2.port_a) annotation(
Line(points = {{24, -24}, {36, -24}}, color = {0, 127, 255}));
connect(pipe2.port_b, boundary.ports[1]) annotation(
Line(points = {{56, -24}, {70, -24}}, color = {0, 127, 255}));
annotation(uses(Modelica(version = "4.0.0")));
end Test25;