├── LICENSE ├── LibRAS ├── Blocks │ ├── AddToIndex.mo │ ├── CEWA.mo │ ├── ESC.mo │ ├── Gain_fromPerDay.mo │ ├── SISO2.mo │ ├── VectorAdd.mo │ ├── VectorGain.mo │ ├── package.mo │ └── package.order ├── Culture │ ├── Feed.mo │ ├── Fish.mo │ ├── PartialCulture.mo │ ├── SSCulture_V.mo │ ├── SSCulture_V_noEvent.mo │ ├── Waste.mo │ ├── package.mo │ ├── package.order │ └── simpleCulture.mo ├── Examples │ ├── Bypass.mo │ ├── Inline.mo │ ├── ReactorTest.mo │ ├── Recycle.mo │ ├── TinyRAS.mo │ ├── package.mo │ └── package.order ├── Interfaces │ ├── PartialLumpedVolume.mo │ ├── PartialTwoPort.mo │ ├── PartialTwoPortTransport.mo │ ├── VesselWasteFluidPorts_a.mo │ ├── VesselWasteFluidPorts_b.mo │ ├── WasteFluidPort.mo │ ├── WasteFluidPort_a.mo │ ├── WasteFluidPort_b.mo │ ├── WasteFluidPorts_a.mo │ ├── WasteFluidPorts_b.mo │ ├── package.mo │ └── package.order ├── Machines │ ├── AdditionController_S.mo │ ├── ControlledPump.mo │ ├── PartialPump.mo │ ├── package.mo │ └── package.order ├── Media │ ├── WasteWater.mo │ ├── package.mo │ └── package.order ├── Pipes │ ├── IdealParticleFilter.mo │ ├── OxygenAdder.mo │ ├── PartialStraightPipe.mo │ ├── StaticPipe.mo │ ├── Tee.mo │ ├── package.mo │ └── package.order ├── Sensors │ ├── MassFlowRate.mo │ ├── PartialAbsoluteSensor.mo │ ├── PartialFlowSensor.mo │ ├── SoluteSensor.mo │ ├── StateSensor.mo │ ├── package.mo │ └── package.order ├── Sources │ ├── Boundary_pT.mo │ ├── MassFlowSource_T.mo │ ├── PartialFlowSource.mo │ ├── PartialSource.mo │ ├── WaterExchange.mo │ ├── package.mo │ └── package.order ├── System.mo ├── Tanks │ ├── CSBR.mo │ ├── CST.mo │ ├── FishTank.mo │ ├── OpenCSBR.mo │ ├── OpenTank.mo │ ├── PartialCSBR.mo │ ├── PartialCST.mo │ ├── PartialLumpedVessel.mo │ ├── PartialTank.mo │ ├── package.mo │ └── package.order ├── Types │ ├── ProcessData.mo │ ├── Species.mo │ ├── package.mo │ └── package.order ├── Units │ ├── GrowthRate.mo │ ├── MassConcentration.mo │ ├── package.mo │ └── package.order ├── Utilities │ ├── logisticInterpolation.mo │ ├── oxygenSaturation.mo │ ├── package.mo │ └── package.order ├── Valves │ ├── ValveLinear.mo │ ├── package.mo │ └── package.order ├── package.mo └── package.order ├── README.md └── logo.png /LibRAS/Blocks/AddToIndex.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | block AddToIndex "Add one scalar input to specified index of other (vector) input" 3 | extends Modelica.Blocks.Icons.Block; 4 | 5 | parameter Integer n=1 "Dimension of input and output vectors."; 6 | parameter Integer i=1 "Index to which u2 is added"; 7 | 8 | Modelica.Blocks.Interfaces.RealInput u1[n] "Connector of Real input signals vector" annotation (Placement( 9 | visible = true, transformation(extent = {{-140, 40}, {-100, 80}}, rotation = 0))); 10 | Modelica.Blocks.Interfaces.RealInput u2 "Connector of Real scalar input signal" annotation (Placement( 11 | transformation(extent={{-140,-80},{-100,-40}}, rotation=0))); 12 | Modelica.Blocks.Interfaces.RealOutput y[n] "Connector of Real output signals" annotation (Placement( 13 | transformation(extent={{100,-10},{120,10}}, rotation=0))); 14 | 15 | Real u2_matrix [n,n]; 16 | equation 17 | u2_matrix = identity(n) * u2; 18 | y = u1 + u2_matrix[i,:]; 19 | 20 | 21 | annotation (Icon(coordinateSystem(initialScale = 0.1), graphics = {Text(lineColor = {0, 0, 255}, extent = {{-150, 110}, {150, 150}}, textString = "%name", fontName = "DejaVu Sans Mono"), Line(points = {{-100, 60}, {-74, 24}, {-44, 24}}, color = {0, 0, 127}), Line(points = {{-100, -60}, {-74, -28}, {-42, -28}}, color = {0, 0, 127}), Ellipse(lineColor = {0, 0, 127}, extent = {{-50, -50}, {50, 50}}, endAngle = 360), Line(points = {{50, 0}, {100, 0}}, color = {0, 0, 127}), Text(extent = {{-38, -34}, {38, 34}}, textString = "+", fontName = "DejaVu Sans Mono")}), Diagram(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{-100, -100}, {100, 100}}, lineColor = {0, 0, 127}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Line(points = {{50, 0}, {100, 0}}, color = {0, 0, 255}), Line(points = {{-100, 60}, {-74, 24}, {-44, 24}}, color = {0, 0, 127}), Line(points = {{-100, -60}, {-74, -28}, {-42, -28}}, color = {0, 0, 127}), Ellipse(extent = {{-50, 50}, {50, -50}}, lineColor = {0, 0, 127}), Line(points = {{50, 0}, {100, 0}}, color = {0, 0, 127}), Text(extent = {{-36, 38}, {40, -30}}, lineColor = {0, 0, 0}, textString = "+"), Text(extent = {{-100, 52}, {5, 92}}, lineColor = {0, 0, 0}, textString = "k1"), Text(extent = {{-100, -52}, {5, -92}}, lineColor = {0, 0, 0}, textString = "k2")})); 22 | end AddToIndex; 23 | -------------------------------------------------------------------------------- /LibRAS/Blocks/CEWA.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | 3 | block CEWA 4 | extends Modelica.Blocks.Interfaces.SISO; 5 | parameter Real T1 = 86400 "Control input filter time constant"; 6 | parameter Real T2 = 6 * 86400; 7 | Modelica.Blocks.Interfaces.RealInput v "Connector of Real input signal" annotation( 8 | Placement(visible = true, transformation(extent = {{-140, -20}, {-100, 20}}, rotation = 0), iconTransformation(origin = {0, -120},extent = {{-20, -20}, {20, 20}}, rotation = 90))); 9 | Real x; 10 | Real q; 11 | initial equation 12 | x = u; 13 | equation 14 | T1 * der(x) = (q - 1) * x + (1 - q) * u; 15 | T2 * der(q) = (-q) + v; 16 | connect(y, x); 17 | end CEWA; -------------------------------------------------------------------------------- /LibRAS/Blocks/ESC.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | 3 | block ESC 4 | extends Modelica.Blocks.Interfaces.SISO; 5 | parameter Real preamp = 1 "Preamplifier gain"; 6 | parameter Real k = 1 "Integrator Gain"; 7 | parameter Modelica.SIunits.Frequency f = 1 "Perturbation frequency"; 8 | parameter Real a = 1 "Perturbation amplitude"; 9 | parameter Real Tf = 1 "Highpass time constant"; 10 | parameter Real m = 0 "Base level of output"; 11 | parameter Modelica.SIunits.Time startTime = 1 "Activate when time >= startTime"; 12 | 13 | Modelica.Blocks.Sources.Sine sine1(amplitude = a, freqHz = f, startTime = startTime) annotation( 14 | Placement(visible = true, transformation(origin = {-58, -36}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 15 | Modelica.Blocks.Continuous.Integrator integrator1(k = k) annotation( 16 | Placement(visible = true, transformation(origin = {8, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 17 | Modelica.Blocks.Math.Product product1 annotation( 18 | Placement(visible = true, transformation(origin = {-24, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 19 | Modelica.Blocks.Continuous.TransferFunction highpass(a = {Tf,1}, b = {preamp, 0}) annotation( 20 | Placement(visible = true, transformation(origin = {-58, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 21 | Modelica.Blocks.Sources.Constant bias(k = m) annotation( 22 | Placement(visible = true, transformation(origin = {8, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 23 | Modelica.Blocks.Math.Add3 add31 annotation( 24 | Placement(visible = true, transformation(origin = {40, -30}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 25 | equation 26 | connect(bias.y, add31.u1) annotation( 27 | Line(points = {{20, 2}, {28, 2}, {28, -22}, {28, -22}}, color = {0, 0, 127})); 28 | connect(integrator1.y, add31.u2) annotation( 29 | Line(points = {{20, -30}, {28, -30}, {28, -30}, {28, -30}}, color = {0, 0, 127})); 30 | connect(add31.y, y) annotation( 31 | Line(points = {{52, -30}, {82, -30}, {82, 0}, {110, 0}, {110, 0}}, color = {0, 0, 127})); 32 | connect(sine1.y, add31.u3) annotation( 33 | Line(points = {{-46, -36}, {-46, -46}, {28, -46}, {28, -38}}, color = {0, 0, 127})); 34 | connect(sine1.y, product1.u2) annotation( 35 | Line(points = {{-46, -36}, {-38, -36}, {-38, -36}, {-36, -36}}, color = {0, 0, 127})); 36 | connect(product1.y, integrator1.u) annotation( 37 | Line(points = {{-12, -30}, {-6, -30}, {-6, -30}, {-4, -30}}, color = {0, 0, 127})); 38 | connect(highpass.y, product1.u1) annotation( 39 | Line(points = {{-46, 0}, {-42, 0}, {-42, -24}, {-36, -24}}, color = {0, 0, 127})); 40 | connect(highpass.u, u) annotation( 41 | Line(points = {{-70, 0}, {-114, 0}, {-114, 0}, {-120, 0}}, color = {0, 0, 127})); 42 | annotation(Icon( 43 | coordinateSystem(preserveAspectRatio=true, 44 | extent={{-100.0,-100.0},{100.0,100.0}}), 45 | graphics={ 46 | Line(points={{-80.0,78.0},{-80.0,-90.0}}, 47 | color={192,192,192}), 48 | Polygon(lineColor={192,192,192}, 49 | fillColor={192,192,192}, 50 | fillPattern=FillPattern.Solid, 51 | points={{-80.0,90.0},{-88.0,68.0},{-72.0,68.0},{-80.0,90.0}}), 52 | Line(points={{-90.0,-80.0},{82.0,-80.0}}, 53 | color={192,192,192}), 54 | Polygon(lineColor={192,192,192}, 55 | fillColor={192,192,192}, 56 | fillPattern=FillPattern.Solid, 57 | points={{90.0,-80.0},{68.0,-72.0},{68.0,-88.0},{90.0,-80.0}}), 58 | Line(origin = {-1.939,-1.816}, 59 | points = {{81.939,36.056},{65.362,36.056},{14.39,-26.199},{-29.966,113.485},{-65.374,-61.217},{-78.061,-78.184}}, 60 | color = {0,0,127}, 61 | smooth = Smooth.Bezier), 62 | Text(lineColor={192,192,192}, 63 | extent={{0.0,-70.0},{60.0,-10.0}}, 64 | textString="ESC")})); 65 | end ESC; -------------------------------------------------------------------------------- /LibRAS/Blocks/Gain_fromPerDay.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | 3 | model Gain_fromPerDay "Gain block which converts from unit/day to unit/second through division by a factor 86400" 4 | extends Blocks.VectorGain(final k=1/(24*3600)); 5 | end Gain_fromPerDay; -------------------------------------------------------------------------------- /LibRAS/Blocks/SISO2.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | 3 | partial block SISO2 4 | "1 Single Input / 2 Single Output continuous control block" 5 | extends Modelica.Blocks.Icons.Block; 6 | 7 | Modelica.Blocks.Interfaces.RealInput u "Connector of Real input signal" annotation (Placement( 8 | transformation(extent={{-140,-20},{-100,20}}))); 9 | Modelica.Blocks.Interfaces.RealOutput y1 "Connector of Real output signal" annotation (Placement( 10 | transformation(extent={{100,40},{140,80}}))); 11 | Modelica.Blocks.Interfaces.RealOutput y2 "Connector of Real output signal" annotation (Placement( 12 | transformation(extent={{100,-80},{140,-40}}))); 13 | end SISO2; -------------------------------------------------------------------------------- /LibRAS/Blocks/VectorAdd.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | block VectorAdd "Output the sum of the two inputs" 3 | extends Modelica.Blocks.Interfaces.MI2MO; 4 | parameter Real k1 = +1 "Gain of upper input"; 5 | parameter Real k2 = +1 "Gain of lower input"; 6 | equation 7 | y = k1 * u1 + k2 * u2; 8 | annotation(Documentation(info = " 9 |
10 | This blocks computes output y as sum of the 11 | two input signals u1 and u2: 12 |
13 |14 | y = k1*u1 + k2*u2; 15 |16 |
17 | Example: 18 |
19 |20 | parameter: k1= +2, k2= -3 21 | 22 | results in the following equations: 23 | 24 | y = 2 * u1 - 3 * u2 25 |26 | 27 | "), Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}, initialScale = 0.1), graphics = {Text(lineColor = {0, 0, 255}, extent = {{-150, 110}, {150, 150}}, textString = "%name"), Line(points = {{-100, 60}, {-74, 24}, {-44, 24}}, color = {0, 0, 127}), Line(points = {{-100, -60}, {-74, -28}, {-42, -28}}, color = {0, 0, 127}), Ellipse(lineColor = {0, 0, 127}, extent = {{-50, -50}, {50, 50}}), Line(points = {{50, 0}, {100, 0}}, color = {0, 0, 127}), Text(extent = {{-38, -34}, {38, 34}}, textString = "+"), Text(extent = {{-100, 52}, {5, 92}}, textString = "%k1"), Text(extent = {{-100, -92}, {5, -52}}, textString = "%k2")}), Diagram(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{-100, -100}, {100, 100}}, lineColor = {0, 0, 127}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Line(points = {{50, 0}, {100, 0}}, color = {0, 0, 255}), Line(points = {{-100, 60}, {-74, 24}, {-44, 24}}, color = {0, 0, 127}), Line(points = {{-100, -60}, {-74, -28}, {-42, -28}}, color = {0, 0, 127}), Ellipse(extent = {{-50, 50}, {50, -50}}, lineColor = {0, 0, 127}), Line(points = {{50, 0}, {100, 0}}, color = {0, 0, 127}), Text(extent = {{-36, 38}, {40, -30}}, lineColor = {0, 0, 0}, textString = "+"), Text(extent = {{-100, 52}, {5, 92}}, lineColor = {0, 0, 0}, textString = "k1"), Text(extent = {{-100, -52}, {5, -92}}, lineColor = {0, 0, 0}, textString = "k2")})); 28 | end VectorAdd; 29 | -------------------------------------------------------------------------------- /LibRAS/Blocks/VectorGain.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Blocks; 2 | block VectorGain "Output the product of a gain value with the input signal" 3 | parameter Real k(start = 1, unit = "1") "Gain value multiplied with input signal"; 4 | parameter Integer n=1 "Dimension of input and output vectors."; 5 | public 6 | Modelica.Blocks.Interfaces.RealInput u[n] "Input signal connector" annotation(Placement(transformation(extent = {{-140, -20}, {-100, 20}}, rotation = 0))); 7 | Modelica.Blocks.Interfaces.RealOutput y[n] "Output signal connector" annotation(Placement(transformation(extent = {{100, -10}, {120, 10}}, rotation = 0))); 8 | equation 9 | y = k * u; 10 | annotation(Documentation(info = " 11 |
12 | This block computes output y as 13 | product of gain k with the 14 | input u: 15 |
16 |17 | y = k * u; 18 |19 | 20 | "), Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Polygon(points = {{-100, -100}, {-100, 100}, {100, 0}, {-100, -100}}, lineColor = {0, 0, 127}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Text(extent = {{-150, -140}, {150, -100}}, lineColor = {0, 0, 0}, textString = "k=%k"), Text(extent = {{-150, 140}, {150, 100}}, textString = "%name", lineColor = {0, 0, 255})}), Diagram(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Polygon(points = {{-100, -100}, {-100, 100}, {100, 0}, {-100, -100}}, lineColor = {0, 0, 127}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Text(extent = {{-76, 38}, {0, -34}}, textString = "k", lineColor = {0, 0, 255})})); 21 | end VectorGain; 22 | -------------------------------------------------------------------------------- /LibRAS/Blocks/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Blocks 3 | extends Modelica.Icons.Package; 4 | annotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}, initialScale=0.1), graphics={ 5 | Rectangle( 6 | origin={0.0,35.1488}, 7 | fillColor={255,255,255}, 8 | extent={{-30.0,-20.1488},{30.0,20.1488}}), 9 | Rectangle( 10 | origin={0.0,-34.8512}, 11 | fillColor={255,255,255}, 12 | extent={{-30.0,-20.1488},{30.0,20.1488}}), 13 | Line( 14 | origin={-51.25,0.0}, 15 | points={{21.25,-35.0},{-13.75,-35.0},{-13.75,35.0},{6.25,35.0}}), 16 | Polygon( 17 | origin={-40.0,35.0}, 18 | pattern=LinePattern.None, 19 | fillPattern=FillPattern.Solid, 20 | points={{10.0,0.0},{-5.0,5.0},{-5.0,-5.0}}), 21 | Line( 22 | origin={51.25,0.0}, 23 | points={{-21.25,35.0},{13.75,35.0},{13.75,-35.0},{-6.25,-35.0}}), 24 | Polygon( 25 | origin={40.0,-35.0}, 26 | pattern=LinePattern.None, 27 | fillPattern=FillPattern.Solid, 28 | points={{-10.0,0.0},{5.0,5.0},{5.0,-5.0}})})); 29 | end Blocks; 30 | -------------------------------------------------------------------------------- /LibRAS/Blocks/package.order: -------------------------------------------------------------------------------- 1 | Gain_fromPerDay 2 | VectorAdd 3 | VectorGain 4 | AddToIndex 5 | ESC 6 | SISO2 7 | CEWA 8 | -------------------------------------------------------------------------------- /LibRAS/Culture/Feed.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | package Feed 4 | record FeedData 5 | parameter Real FCR (min=0) "Feed conversion ratio"; 6 | 7 | parameter Real protein (min=0) "Protein weight fraction"; 8 | parameter Real carbohydrate (min=0) "Carbohydrate weight fraction"; 9 | parameter Real fat (min=0) "Fat weight fraction"; 10 | parameter Real ash (min=0) "Ash weight fraction"; 11 | parameter Real water (min=0) "Water weight fraction"; 12 | 13 | parameter Real N = 0.16*protein "Nitrogen"; 14 | parameter Real P = 0.20*ash "Phosphorus"; 15 | parameter Real COD = (0.528*protein+0.4*carbohydrate+0.78*fat)*(32/12)-inert; 16 | parameter Real inert (min=0) "Inert"; 17 | end FeedData; 18 | 19 | record DefaultFeed = FeedData( 20 | FCR = 0.9, 21 | protein = 0.44, 22 | carbohydrate = 0.14, 23 | fat = 0.24, 24 | ash = 0.08, 25 | water = 0.10, 26 | inert = 0.03 27 | ); 28 | end Feed; 29 | -------------------------------------------------------------------------------- /LibRAS/Culture/Fish.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | package Fish 4 | record FishData 5 | parameter Real TGC (min=0) "Temperature growth coefficient"; 6 | parameter Modelica.SIunits.MassFlowRate O2rate (min=0) "Respiration rate in kgO2/kg fish/s"; 7 | parameter Modelica.SIunits.MassFlowRate CO2rate (min=0) = O2rate*44/32 "CO2 production rate in gCO2/kg fish/s"; 8 | parameter Real mortality (min=0) "Mortality rate, percent dead per production cycle"; 9 | parameter Real[2] T1 (each unit="h"); 10 | parameter Real[2] T2 (each unit="h"); 11 | parameter Real[2] Td (each unit="h"); 12 | parameter Real IBW (min=0, unit="kg", displayUnit="g"); 13 | 14 | parameter Real protein (min=0) "Protein weight fraction"; 15 | parameter Real carbohydrate (min=0) "Carbohydrate weight fraction"; 16 | parameter Real fat (min=0) "Fat weight fraction"; 17 | parameter Real ash (min=0) "Ash weight fraction"; 18 | parameter Real water (min=0) "Water weight fraction"; 19 | 20 | parameter Real N = 0.16*protein "Nitrogen"; 21 | parameter Real P = 0.20*ash "Phosphorus"; 22 | parameter Real COD = (0.528*protein+0.4*carbohydrate+0.78*fat)*(32/12)-inert; 23 | parameter Real inert (min=0) "Inert"; 24 | 25 | parameter Modelica.SIunits.Density bodyDensity = 1e3 "Fish body density"; 26 | end FishData; 27 | 28 | record RainbowTrout = FishData( 29 | TGC = 1.9375e-3, 30 | O2rate = 5.29e-8,//4.051*1e-6/60, 31 | mortality = 0.02, 32 | T1 = {0.1, 3}, 33 | T2 = {0.1, 6}, 34 | Td = {0.3, 5}, 35 | IBW = 10e-3, 36 | protein = 0.1736, 37 | carbohydrate = 0.0024, 38 | fat = 0.0196, 39 | ash = 0.0244, 40 | water = 0.78, 41 | inert = 0.01 42 | ); 43 | 44 | 45 | record AtlanticSalmon = FishData( 46 | TGC = 2.7e-3, // Thorarensen & Farrell 2011 47 | //O2rate = 3.59e-8, // Berg et al., 1993 48 | O2rate = 3.4e-8, // Berg et al., 1993 49 | mortality = 0.02, 50 | T1 = {0.1, 3}, 51 | T2 = {0.1, 6}, 52 | Td = {0.3, 5}, 53 | IBW = 100e-3, 54 | protein = 0.1736, 55 | carbohydrate = 0.0024, 56 | fat = 0.0196, 57 | ash = 0.0244, 58 | water = 0.78, 59 | inert = 0.01 60 | ); 61 | 62 | 63 | 64 | 65 | end Fish; 66 | -------------------------------------------------------------------------------- /LibRAS/Culture/PartialCulture.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | partial model PartialCulture 4 | import SI = Modelica.SIunits; 5 | import Modelica.SIunits.Conversions.from_day; 6 | import Modelica.SIunits.Conversions.from_hour; 7 | import Modelica.SIunits.Conversions.from_minute; 8 | import LibRAS.Types.Species.S; 9 | import LibRAS.Types.Species.X; 10 | 11 | // FEED AND FISH DATA 12 | parameter Feed.FeedData feed = Feed.DefaultFeed() "FeedData record" annotation(choicesAllMatching=true); 13 | parameter Fish.FishData fish = Fish.RainbowTrout()"FishData record" annotation(choicesAllMatching=true); 14 | parameter Waste.WasteData waste = Waste.WasteData(fish=fish, feed=feed, loss=loss) "WasteData record" annotation(choicesAllMatching=true); 15 | 16 | // DESIGN VARIABLES 17 | parameter Integer nTanks = 9; 18 | parameter SI.Volume[nTanks] tankVolumes = fill(1, nTanks) "Vector of fish basin volumes"; 19 | 20 | // GROWTH AND FEEDING 21 | parameter SI.Temp_C T = 15 "Farming temperature"; 22 | 23 | // OUTPUTS 24 | output SI.Volume[nTanks] Vw "Tank water volume (tank volume - fish displacement) in m3"; 25 | Modelica.Blocks.Interfaces.RealOutput[size(m_S_tot, 1)] m_S_output (each unit="kg/s", each displayUnit="g/d") "Connector for soluble species output signal" annotation(Placement(visible = true, transformation(extent = {{-100, 20}, {-60, 60}}, rotation = 0), iconTransformation(extent = {{100, 20}, {140, 60}}, rotation = 0))); 26 | Modelica.Blocks.Interfaces.RealOutput[size(m_X_tot, 1)] m_X_output (each unit="kg/s", each displayUnit="g/d") "Connector for soluble species output signal" annotation(Placement(visible = true, transformation(extent = {{-100, -60}, {-60, -20}}, rotation = 0), iconTransformation(extent = {{100, -60}, {140, -20}}, rotation = 0))); 27 | 28 | 29 | protected 30 | SI.MassFlowRate [S, nTanks] m_S "Produced waste, soluble species"; 31 | SI.MassFlowRate [X, nTanks] m_X "Produced waste, particulate species"; 32 | SI.MassFlowRate [S] m_S_tot "Sum of produced soluble waste"; 33 | SI.MassFlowRate [X] m_X_tot "Sum of produced particulate waste"; 34 | 35 | equation 36 | 37 | for i in S loop 38 | m_S_tot[i] = sum(m_S[i, :]); 39 | connect(m_S_tot[i], m_S_output[Integer(i)]); 40 | end for; 41 | for i in X loop 42 | m_X_tot[i] = sum(m_X[i]); 43 | connect(m_X_tot[i], m_X_output[Integer(i)]); 44 | end for; 45 | 46 | annotation(experiment(StartTime = 0, StopTime = 2592000, Tolerance = 0.0001, Interval = 180), defaultComponentName = "ssculture_V"); 47 | end PartialCulture; -------------------------------------------------------------------------------- /LibRAS/Culture/SSCulture_V.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | model SSCulture_V "Steady-state fish culture specified by tank volumes and maximum stocking density" 4 | extends Culture.PartialCulture; 5 | 6 | import SI = Modelica.SIunits; 7 | import Modelica.SIunits.Conversions.from_day; 8 | import Modelica.SIunits.Conversions.from_hour; 9 | import Modelica.SIunits.Conversions.from_minute; 10 | import LibRAS.Types.Species.S; 11 | import LibRAS.Types.Species.X; 12 | 13 | // DESIGN VARIABLES 14 | parameter Integer gradingTime (min=0) = 30 "Time between gradings in days"; 15 | parameter SI.Density fishDensity = 70 "Maximum fish density in kg/m3"; 16 | parameter Boolean fastCalculation = false "If true, assume constant number of fish"; 17 | 18 | // GROWTH AND FEEDING 19 | parameter SI.Time[:] feedingTimes = from_hour({6, 18}) "Feeding times in seconds after beginning of each day (00:00)"; 20 | parameter SI.Time feedingDuration = from_minute(15) "Length of feeding period in seconds"; 21 | parameter Real FCR = 1.1 "kg feed/kg fish growth"; 22 | parameter Real loss = 0.1 "Food loss factor"; 23 | parameter Real TGC = fish.TGC/from_day(1); 24 | 25 | parameter Real F_nominal = 1e-6 "Scaling value for feed to ease numerics"; 26 | 27 | // CALCULATED PARAMETERS & INITIAL VALUES 28 | final parameter Real [nTanks] n0 = {fishDensity*tankVolumes[nTanks]*(1-fish.mortality)^((i-1)/nTanks-1) / (BW0[nTanks+1]) for i in 1:nTanks} "Number of stocked fish after grading" annotation(HideResult = true); // Back-calculation of n(0) from slaughter weight & tank volume 29 | final parameter SI.Mass [nTanks+1] BW0 = {(fish.TGC*T*i*gradingTime+(fish.IBW*1000)^(1/3))^3 / 1000 for i in 0:nTanks} "Body weight after grading in kg" annotation(HideResult = true); // Using fish.TGC here which is in kg/d! 30 | final parameter SI.Mass [nTanks] delta_m0 = {fishDensity*tankVolumes[nTanks]*(1-fish.mortality)^((i-1)/nTanks-1) * (BW0[i] - (((BW0[i]*1000)^(1/3)+TGC*T*(-86400))^3 / 1000))/BW0[nTanks+1] for i in 1:nTanks} annotation(HideResult = true); 31 | // Linear interpolation from small fish to big fish! 32 | final parameter Real[nTanks] T1 = from_hour({fish.T1[1] + (fish.T1[2]-fish.T1[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time constant 1 (s)"; 33 | final parameter Real[nTanks] T2 = from_hour({fish.T2[1] + (fish.T2[2]-fish.T2[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time constant 2 (s)"; 34 | final parameter Real[nTanks] Td = from_hour({fish.Td[1] + (fish.Td[2]-fish.Td[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time delay (s)"; 35 | final parameter Real death_k = -log(1-fish.mortality)/(nTanks*from_day(gradingTime)) "First order rate constant (1/s) corresponding to fish mortality"; 36 | 37 | // STATE & AUX VARIABLES 38 | SI.Mass [nTanks+1] BW "Fish body mass, kg/fish"; 39 | Real [nTanks+1] BWG "Fish body mass growth rate, kg/fish/s"; 40 | Real [nTanks] n "Number of fish (per tank)"; 41 | Real [nTanks] F (each start=0.0) "Added feed mass in kg"; 42 | Real feedingPulse (start=0.0) "Feeding pulse is HIGH when fish are being fed."; 43 | Real [nTanks] F_digested (each start=0.0, each fixed=true) "Digested feed mass in kg"; 44 | Real [nTanks] feedSignal (each start=0.0, each fixed=true) "Digested feed signal. Nonzero means intestine is processing something."; 45 | Real [nTanks] intestineState1 (each start=0.0, each fixed=true) "Internal state of the intestine wrt feedSignal."; 46 | Real [nTanks] intestineState2 (each start=0.0, each fixed=true) "Internal state of the intestine wrt F."; 47 | Real [nTanks] m_fish "Total fish mass (per tank) in kg"; 48 | Real [nTanks] dm_fish "Total fish mass growth (per tank) in kg"; 49 | 50 | // Sampled variables 51 | Clock clk = Clock(86400); 52 | SI.Time [size(feedingTimes, 1)] ft (each start=0) "Time of next feeding"; 53 | SI.Mass [nTanks] sampled_m (start = {fishDensity*tankVolumes[nTanks] *BW0[i]/BW0[i+1] * (1-fish.mortality)^((i-1)/nTanks-1) for i in 1:nTanks}) "Sampled fish mass" annotation(HideResult = true); 54 | Real [nTanks] delta_m "Delta fish mass"; 55 | SI.Time lastGrading (start=0) "Time of last grading"; 56 | 57 | protected 58 | Real [nTanks] F_scaled (each start=0.0) "Scaled added feed mass in kg"; 59 | Real [nTanks] F_digested_scaled (each start=0.0) "Scaled digested feed mass in kg"; 60 | Real [nTanks] feedSignal_scaled (each start=0.0) "Scaled digested feed signal"; 61 | 62 | initial equation 63 | n = n0; 64 | 65 | equation 66 | Vw = tankVolumes - n0 .* BW0[1:nTanks] / fish.bodyDensity; 67 | 68 | // ONCE PER DAY STARTING AT t=0 69 | when shiftSample(clk, 1, 86400) then 70 | ft = time.+feedingTimes; 71 | sampled_m = sample(m_fish); 72 | delta_m = sampled_m-previous(sampled_m); 73 | end when; 74 | 75 | // Once per grading period starting at t=0 76 | when Clock(86400*gradingTime) then 77 | lastGrading = sample(time); 78 | end when; 79 | 80 | // After each grading, reinit the number of fish in each tank to starting value. Keep this expression matched with the initial equation section! 81 | if fastCalculation then 82 | der(n) = fill(0, nTanks); 83 | else 84 | when sample(gradingTime*86400, gradingTime*86400) then // from_day doesn't work here 85 | reinit(n, n0); 86 | end when; 87 | der(n) = -death_k*n; 88 | end if; 89 | 90 | 91 | // Growth & death 92 | BW = {((BW0[i]*1000)^(1/3)+TGC*T*(time-hold(lastGrading)))^3 / 1000 for i in 1:nTanks+1}; 93 | BWG = {3*TGC*T*((BW0[i]*1000)^(1/3)+TGC*T*(time-hold(lastGrading)))^2 / 1000 for i in 1:nTanks+1}; 94 | 95 | m_fish = BW[1:nTanks] .* n; 96 | dm_fish = (BWG[1:nTanks]-death_k*BW[1:nTanks]) .* n; 97 | 98 | // Feed Signal 99 | // Active (high) for feedingDuration s after each feedingTimes event, which triggers ever 24 h. 100 | // Otherwise inactive (0). 101 | // Signal area over 24 h is 1/F_nominal. 102 | feedingPulse = if ( sum({if (t < time) and (time < t + feedingDuration) then 1 else 0 for t in hold(ft)}) > 0 ) then 1/(feedingDuration*size(feedingTimes, 1)*F_nominal) else 0; 103 | // Feed an amount proportional to how much the fish have grown the past 24 hours. 104 | F_scaled = {feedingPulse * FCR * (if hold(delta_m[i]) > delta_m0[i] then hold(delta_m[i]) else delta_m0[i]) for i in 1:nTanks} "Scaled added feed mass"; 105 | F = F_scaled*F_nominal; 106 | 107 | // Intestine calculations 108 | T1 .* der(intestineState1) = {delay(feedingPulse, Td[i]) for i in 1:nTanks} - intestineState1; // feedingPulse is already scaled 109 | T1 .* der(intestineState2) = {delay(F_scaled[i], Td[i]) for i in 1:nTanks} - intestineState2; 110 | T2 .* der(feedSignal_scaled) = intestineState1 - feedSignal_scaled; 111 | T2 .* der(F_digested_scaled) = intestineState2 - F_digested_scaled; 112 | feedSignal = feedSignal_scaled * F_nominal * 86400; 113 | F_digested = F_digested_scaled * F_nominal; 114 | 115 | for i in S loop 116 | // Conditional matrix product: leave out feedSignal for O2 and CO2, because respiration is not coupled to eating 117 | m_S[i] = waste.S_waste[i, :] * (if i == Types.Species.S.O or i == Types.Species.S.CO2 then {F, F_digested, dm_fish, m_fish} else {F, F_digested, dm_fish.*feedSignal, m_fish.*feedSignal}); 118 | end for; 119 | for i in X loop 120 | m_X[i] = waste.X_waste[i, :] * {F, F_digested, dm_fish.*feedSignal, m_fish.*feedSignal}; 121 | end for; 122 | 123 | annotation(experiment(StartTime = 0, StopTime = 2592000, Tolerance = 0.0001, Interval = 180), defaultComponentName = "ssculture_V"); 124 | end SSCulture_V; -------------------------------------------------------------------------------- /LibRAS/Culture/SSCulture_V_noEvent.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | model SSCulture_V_noEvent 4 | extends Culture.PartialCulture; 5 | 6 | import SI = Modelica.SIunits; 7 | import Modelica.SIunits.Conversions.from_day; 8 | import Modelica.SIunits.Conversions.from_hour; 9 | import Modelica.SIunits.Conversions.from_minute; 10 | import LibRAS.Types.Species.S; 11 | import LibRAS.Types.Species.X; 12 | 13 | // DESIGN VARIABLES 14 | parameter Integer gradingTime (unit="d", min=0) = 30 "Time between gradings in days"; 15 | parameter SI.Density fishDensity = 70 "Maximum fish density in kg/m3"; 16 | parameter Real samplingTime (quantity="time", unit="s", min = 0) = 3600 "Sampling time for fish calculation"; 17 | parameter Boolean fastCalculation = false "If true, assume constant number of fish"; 18 | 19 | // GROWTH AND FEEDING 20 | parameter SI.Time[:] feedingTimes = from_hour({6, 18}) "Feeding times in seconds after beginning of each day (00:00)"; 21 | parameter SI.Time feedingDuration = from_minute(15) "Length of feeding period in seconds"; 22 | parameter Real FCR = 1.1 "kg feed/kg fish growth"; 23 | parameter Real loss = 0.1 "Food loss factor"; 24 | parameter Real TGC = fish.TGC/from_day(1); 25 | 26 | parameter Real F_nominal = 1e-6 "Scaling value for feed to ease numerics"; 27 | 28 | // CALCULATED PARAMETERS & INITIAL VALUES 29 | final parameter Real [nTanks] n0 = {fishDensity*tankVolumes[nTanks]*(1-fish.mortality)^((i-1)/nTanks-1) / (BW0[nTanks+1]) for i in 1:nTanks} "Number of stocked fish after grading" annotation(HideResult = true); // Back-calculation of n(0) from slaughter weight & tank volume 30 | final parameter SI.Mass [nTanks+1] BW0 = {(fish.TGC*T*i*gradingTime+(fish.IBW*1000)^(1/3))^3 / 1000 for i in 0:nTanks} "Body weight after grading in kg" annotation(HideResult = true); // Using fish.TGC here which is in kg/d! 31 | final parameter SI.Mass [nTanks] delta_m0 = {fishDensity*tankVolumes[nTanks]*(1-fish.mortality)^((i-1)/nTanks-1) * (BW0[i] - (((BW0[i]*1000)^(1/3)+TGC*T*(-86400))^3 / 1000))/BW0[nTanks+1] for i in 1:nTanks} annotation(HideResult = true); 32 | // Linear interpolation from small fish to big fish! 33 | final parameter Real[nTanks] T1 = from_hour({fish.T1[1] + (fish.T1[2]-fish.T1[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time constant 1 (s)"; 34 | final parameter Real[nTanks] T2 = from_hour({fish.T2[1] + (fish.T2[2]-fish.T2[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time constant 2 (s)"; 35 | final parameter Real[nTanks] Td = from_hour({fish.Td[1] + (fish.Td[2]-fish.Td[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time delay (s)"; 36 | final parameter Real death_k = -log(1-fish.mortality)/(nTanks*from_day(gradingTime)) "First order rate constant (1/s) corresponding to fish mortality"; 37 | 38 | // STATE & AUX VARIABLES 39 | parameter Real [:] samplePoints = {samplingTime*i for i in 0:integer(gradingTime * 86400 / samplingTime)} "Time points where calculation is performed"; 40 | //parameter SI.Mass [nTanks+1, :] _BW = {((BW0[i]*1000)^(1/3).+TGC*T*(samplePoints)).^3 / 1000 for i in 1:nTanks+1} "Fish body mass, kg/fish" annotation(HideResult = true); 41 | //parameter Real [:, nTanks+1] _BWG "Fish body mass growth rate, kg/fish/s"; 42 | //parameter Real [:, nTanks ] _n "Number of fish (per tank)"; 43 | 44 | SI.Mass [nTanks+1] BW "Fish body mass, kg/fish"; 45 | Real [nTanks+1] BWG "Fish body mass growth rate, kg/fish/s"; 46 | Real [nTanks] n "Number of fish (per tank)"; 47 | Real [nTanks] F (each start=0.0) "Added feed mass in kg"; 48 | Real feedingPulse (start=0.0) "Feeding pulse is HIGH when fish are being fed."; 49 | Real [nTanks] F_digested (each start=0.0, each fixed=true) "Digested feed mass in kg"; 50 | Real [nTanks] feedSignal (each start=0.0, each fixed=true) "Digested feed signal. Nonzero means intestine is processing something."; 51 | Real [nTanks] intestineState1 (each start=0.0, each fixed=true) "Internal state of the intestine wrt feedSignal."; 52 | Real [nTanks] intestineState2 (each start=0.0, each fixed=true) "Internal state of the intestine wrt F."; 53 | Real [nTanks] m_fish "Total fish mass (per tank) in kg"; 54 | Real [nTanks] dm_fish "Total fish mass growth (per tank) in kg"; 55 | Real [nTanks] delta_m "Delta fish mass over two subsequent days"; 56 | 57 | SI.Time timeSinceLastGrading (start=0) "Time since last grading"; 58 | 59 | // Sampled variables 60 | //Clock clk = Clock(86400); 61 | //SI.Mass [nTanks] sampled_m (start = {fishDensity*tankVolumes[nTanks] *BW0[i]/BW0[i+1] * (1-fish.mortality)^((i-1)/nTanks-1) for i in 1:nTanks}) "Sampled fish mass" annotation(HideResult = true); 62 | //SI.Time lastGrading (start=0) "Time since last grading"; 63 | 64 | protected 65 | Real [nTanks] F_scaled (each start=0.0) "Scaled added feed mass in kg"; 66 | Real [nTanks] F_digested_scaled (each start=0.0) "Scaled digested feed mass in kg"; 67 | Real [nTanks] feedSignal_scaled (each start=0.0) "Scaled digested feed signal"; 68 | 69 | initial equation 70 | n = n0; 71 | 72 | equation 73 | Vw = tankVolumes - n0 .* BW0[1:nTanks] / fish.bodyDensity; 74 | timeSinceLastGrading = mod(time, 86400*gradingTime); 75 | 76 | // ONCE PER DAY STARTING AT t=0 77 | /*when shiftSample(clk, 1, 86400) then 78 | sampled_m = sample(m_fish); 79 | //delta_m = sampled_m-previous(sampled_m); 80 | end when;*/ 81 | 82 | // Once per grading period starting at t=0 83 | /*when Clock(86400*gradingTime) then 84 | lastGrading = sample(time); 85 | end when;*/ 86 | 87 | // After each grading, reinit the number of fish in each tank to starting value. Keep this expression matched with the initial equation section! 88 | if fastCalculation then 89 | der(n) = fill(0, nTanks); 90 | else 91 | when sample(gradingTime*86400, gradingTime*86400) then // from_day doesn't work here 92 | reinit(n, n0); 93 | end when; 94 | der(n) = -death_k*n; 95 | end if; 96 | 97 | // Growth & death 98 | BW = {((BW0[i]*1000)^(1/3)+TGC*T*(timeSinceLastGrading))^3 / 1000 for i in 1:nTanks+1}; 99 | BWG = {3*TGC*T*((BW0[i]*1000)^(1/3)+TGC*T*(timeSinceLastGrading))^2 / 1000 for i in 1:nTanks+1}; 100 | m_fish = BW[1:nTanks] .* n; 101 | delta_m = m_fish - {n[i] * ((BW0[i]*1000)^(1/3)+TGC*T*(timeSinceLastGrading - 86400))^3 / 1000 for i in 1:nTanks}; 102 | dm_fish = (BWG[1:nTanks]-death_k*BW[1:nTanks]) .* n; 103 | 104 | // Feed Signal 105 | // Active (high) for feedingDuration s after each feedingTimes event, which triggers ever 24 h. 106 | // Otherwise inactive (0). 107 | // Signal area over 24 h is 1/F_nominal. 108 | feedingPulse = if ( sum({if (mod(time, 86400) > T) and (mod(time, 86400) < T + feedingDuration) then 1 else 0 for T in feedingTimes}) > 0 ) then 1/(feedingDuration*size(feedingTimes, 1)*F_nominal) else 0; 109 | // Feed an amount proportional to how much the fish have grown the past 24 hours. 110 | F_scaled = {feedingPulse * FCR * (if timeSinceLastGrading > 0 then delta_m[i] else delta_m0[i]) for i in 1:nTanks} "Scaled added feed mass"; 111 | F = F_scaled*F_nominal; 112 | 113 | // Intestine calculations 114 | T1 .* der(intestineState1) = {delay(feedingPulse, Td[i]) for i in 1:nTanks} - intestineState1; // feedingPulse is already scaled 115 | T1 .* der(intestineState2) = {delay(F_scaled[i], Td[i]) for i in 1:nTanks} - intestineState2; 116 | T2 .* der(feedSignal_scaled) = intestineState1 - feedSignal_scaled; 117 | T2 .* der(F_digested_scaled) = intestineState2 - F_digested_scaled; 118 | feedSignal = feedSignal_scaled * F_nominal * 86400; 119 | F_digested = F_digested_scaled * F_nominal; 120 | 121 | for i in S loop 122 | // Conditional matrix product: leave out feedSignal for O2 and CO2, because respiration is not coupled to eating 123 | m_S[i] = waste.S_waste[i, :] * (if i == Types.Species.S.O or i == Types.Species.S.CO2 then {F, F_digested, dm_fish, m_fish} else {F, F_digested, dm_fish.*feedSignal, m_fish.*feedSignal}); 124 | end for; 125 | for i in X loop 126 | m_X[i] = waste.X_waste[i, :] * {F, F_digested, dm_fish.*feedSignal, m_fish.*feedSignal}; 127 | end for; 128 | 129 | annotation(experiment(StartTime = 0, StopTime = 2592000, Tolerance = 0.0001, Interval = 180), defaultComponentName = "ssculture_V"); 130 | end SSCulture_V_noEvent; -------------------------------------------------------------------------------- /LibRAS/Culture/Waste.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | package Waste 4 | record WasteData 5 | // Returns data for the production of the modelled compounds. 6 | // The order of the rows in Waste.Prod are given by ASM1, i.e. 7 | // 8 | // S_I, S_S, X_I, X_S, X_BH, X_BA, X_P, 9 | // S_O, S_NO, S_NH, S_ND, X_ND, S_ALK 10 | // 11 | // in units corresponding to ASM1 (see Henze et al., 1987). 12 | // The last three rows added are S_CO2, c_Phosphorus (all kinds) 13 | // and TSS (production not calculated here - set to 0) 14 | // 15 | // Column 1: Food lost in water (per kg feed/day) 16 | // Column 2: Steady state excretion from fish (per kg feed/day) 17 | // Column 3: Correction for fish growth (per kg fish/day) 18 | // Column 4: Correction for respiration (per kg fish) 19 | //------------------------------------------------------------- 20 | // 21 | // Fraction of food not consumed by fish 22 | // Waste.Loss = 0.1; 23 | // 24 | // Waste production rate matrix (Note TSS production not calculated here) 25 | // Coefficients for COD and N in columns 3 and 4 should agree with column 2! 26 | // The sum of each component (COD,N,P) in columns 1-3 should add up to 27 | // 1.0 times the corresponding content (for example Food.COD) 28 | 29 | Feed.FeedData feed = Feed.DefaultFeed() "FeedData record"; 30 | Fish.FishData fish "FishData record"; 31 | 32 | parameter Real loss = 0.1 "Feed loss factor"; 33 | 34 | Real[Types.Species.S, 4] S_waste = { 35 | // Food spillage Excrement Fish growth Respiration 36 | {0.5*feed.inert, 0.5*feed.inert, -0.5*fish.inert, 0}, // I 37 | {0.3*feed.COD, 0.30*feed.COD, -0.30*fish.COD, -0.30*fish.O2rate}, // S 38 | {0, 0, 0, -fish.O2rate}, // O 39 | {0, 0, 0, 0}, // NO2 40 | {0, 0, 0, 0}, // NO3 41 | {0, 0.7*feed.N, -0.7*fish.N, 0}, // NH 42 | {0.5*feed.N, 0.15*feed.N, -0.15*fish.N, 0}, // ND 43 | {0, 0, 0, 0}, // Alk 44 | {0, 0, 0, fish.CO2rate}, // CO2 45 | {0, 0, 0, 0} // N2 46 | }*diagonal({loss, 1-loss, 1, 1}) "Waste production of S species"; 47 | 48 | Real[Types.Species.X, 4] X_waste = { 49 | // Food spillage Excrement Fish growth Respiration 50 | {0.5*feed.inert, 0.5*feed.inert, -0.5*fish.inert, 0}, // I 51 | {0.7*feed.COD, 0.30*feed.COD, -0.30*fish.COD, -0.30*fish.O2rate}, // S 52 | {0, 0.3*feed.COD, -0.3*fish.COD, -0.3*fish.O2rate}, // BH??? 53 | {0, 0, 0, 0}, // AOB 54 | {0, 0, 0, 0}, // NOB 55 | {0, 0.1*feed.COD, -0.1*fish.COD, -0.1*fish.O2rate}, // p 56 | {0.5*feed.N, 0.15*feed.N, -0.15*fish.N, 0} // ND 57 | }*diagonal({loss, 1-loss, 1, 1}) "Waste production of X species"; 58 | 59 | end WasteData; 60 | end Waste; 61 | -------------------------------------------------------------------------------- /LibRAS/Culture/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | 3 | package Culture 4 | 5 | end Culture; -------------------------------------------------------------------------------- /LibRAS/Culture/package.order: -------------------------------------------------------------------------------- 1 | Waste 2 | SSCulture_V 3 | Fish 4 | Feed 5 | SSCulture_V_noEvent 6 | PartialCulture 7 | simpleCulture 8 | -------------------------------------------------------------------------------- /LibRAS/Culture/simpleCulture.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Culture; 2 | 3 | model simpleCulture 4 | extends Culture.PartialCulture(fish=Culture.Fish.AtlanticSalmon(), nTanks=100, tankVolumes=0.25 * cat(1, {0.125 for i in 1:25}, {0.25 for i in 26:50}, {0.50 for i in 51:75}, {1 for i in 76:100})); 5 | 6 | import SI = Modelica.SIunits; 7 | import Modelica.SIunits.Conversions.from_day; 8 | import Modelica.SIunits.Conversions.from_hour; 9 | import Modelica.SIunits.Conversions.from_minute; 10 | import LibRAS.Types.Species.S; 11 | import LibRAS.Types.Species.X; 12 | 13 | // DESIGN VARIABLES 14 | parameter Integer gradingTime (min=0) = 3 "Time between gradings in days"; 15 | parameter SI.Density fishDensity = 55 "Maximum fish density in kg/m3"; 16 | parameter SI.Time[:] feedingTimes = {0} "Feeding times in seconds after beginning of each day (00:00) (NOT USED)"; 17 | parameter SI.Time feedingDuration = 86400 "Length of feeding period in seconds (NOT USED)"; 18 | 19 | // GROWTH AND FEEDING 20 | parameter Real FCR = 0.9 "kg feed/kg fish growth"; 21 | parameter Real loss = 0.1 "Food loss factor"; 22 | parameter Real TGC = fish.TGC/from_day(1); 23 | 24 | parameter Real F_nominal = 1e-6 "Scaling value for feed to ease numerics"; 25 | 26 | // CALCULATED PARAMETERS & INITIAL VALUES 27 | final parameter Real [nTanks] n0 = {fishDensity*tankVolumes[nTanks]*(1-fish.mortality)^((i-1)/nTanks-1) / (BW0[nTanks+1]) for i in 1:nTanks} "Number of stocked fish after grading" annotation(HideResult = false); // Back-calculation of n(0) from slaughter weight & tank volume 28 | final parameter SI.Mass [nTanks+1] BW0 = {(fish.TGC*T*i*gradingTime+(fish.IBW*1000)^(1/3))^3 / 1000 for i in 0:nTanks} "Body weight after grading in kg" annotation(HideResult = false); // Using fish.TGC here which is in kg/d! 29 | // Linear interpolation from small fish to big fish! 30 | final parameter Real[nTanks] T1 = from_hour({fish.T1[1] + (fish.T1[2]-fish.T1[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time constant 1 (s)"; 31 | final parameter Real[nTanks] T2 = from_hour({fish.T2[1] + (fish.T2[2]-fish.T2[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time constant 2 (s)"; 32 | final parameter Real[nTanks] Td = from_hour({fish.Td[1] + (fish.Td[2]-fish.Td[1]) * (i/(nTanks-1)) for i in 0:nTanks-1}) "Digestion time delay (s)"; 33 | final parameter Real death_k = -log(1-fish.mortality)/(nTanks*from_day(gradingTime)) "First order rate constant (1/s) corresponding to fish mortality"; 34 | 35 | // STATE & AUX VARIABLES 36 | // parameter Real [:] samplePoints = {samplingTime*i for i in 0:integer(gradingTime * 86400 / samplingTime)} "Time points where calculation is performed"; 37 | 38 | SI.Mass [nTanks+1] BW "Fish body mass, kg/fish"; 39 | Real [nTanks+1] BWG "Fish body mass growth rate, kg/fish/s"; 40 | Real [nTanks] n "Number of fish (per tank)"; 41 | Real [nTanks] F (each start=0.0) "Added feed mass in kg"; 42 | Real feedingPulse (start=0.0) "Feeding pulse is HIGH when fish are being fed."; 43 | Real [nTanks] F_digested (each start=0.0, each fixed=true) "Digested feed mass in kg"; 44 | Real [nTanks] feedSignal (each start=0.0, each fixed=true) "Digested feed signal. Nonzero means intestine is processing something."; 45 | Real [nTanks] intestineState1 (each start=0.0, each fixed=true) "Internal state of the intestine wrt feedSignal."; 46 | Real [nTanks] intestineState2 (each start=0.0, each fixed=true) "Internal state of the intestine wrt F."; 47 | Real [nTanks] m_fish "Total fish mass (per tank) in kg"; 48 | Real [nTanks] dm_fish "Total fish mass growth (per tank) in kg"; 49 | Real [nTanks] delta_m "Delta fish mass over two subsequent days"; 50 | 51 | output SI.MassFlowRate fishProduction (displayUnit = "kg/d"); 52 | output SI.MassFlowRate F_avg "Feeding rate daily average"; 53 | 54 | protected 55 | Real [nTanks] F_scaled (each start=0.0) "Scaled added feed mass in kg"; 56 | Real [nTanks] F_digested_scaled (each start=0.0) "Scaled digested feed mass in kg"; 57 | Real [nTanks] feedSignal_scaled (each start=0.0) "Scaled digested feed signal"; 58 | 59 | algorithm 60 | // Growth & death 61 | BW := {((BW0[i]*1000)^(1/3)+TGC*T*(86400*gradingTime))^3 / 1000 for i in 1:nTanks+1}; 62 | BWG := {3*TGC*T*((BW0[i]*1000)^(1/3)+TGC*T*86400*gradingTime)^2 / 1000 for i in 1:nTanks+1}; 63 | n := n0 * exp(-death_k*86400*gradingTime); 64 | m_fish := BW[1:nTanks] .* n; 65 | delta_m := (m_fish - (n0 .* BW0[1:nTanks]))/gradingTime; 66 | dm_fish := (BWG[1:nTanks]-death_k*BW[1:nTanks]) .* n; 67 | 68 | fishProduction := (sum(m_fish) - sum(BW0[1:nTanks] .* n0)) / gradingTime / 86400; 69 | 70 | // Feed Signal 71 | // Active (high) for feedingDuration s after each feedingTimes event, which triggers ever 24 h. 72 | // Otherwise inactive (0). 73 | // Signal area over 24 h is 1/F_nominal. 74 | feedingPulse := 1/(86400*F_nominal); 75 | // Feed an amount proportional to how much the fish have grown the past 24 hours. 76 | F_scaled := {feedingPulse * FCR * delta_m[i] for i in 1:nTanks} "Scaled added feed mass"; 77 | F := F_scaled*F_nominal; 78 | F_avg := sum(F); 79 | 80 | equation 81 | Vw = tankVolumes - n0 .* BW0[1:nTanks] / fish.bodyDensity; 82 | // Intestine calculations 83 | T1 .* der(intestineState1) = {delay(feedingPulse, Td[i]) for i in 1:nTanks} - intestineState1; // feedingPulse is already scaled 84 | T1 .* der(intestineState2) = {delay(F_scaled[i], Td[i]) for i in 1:nTanks} - intestineState2; 85 | T2 .* der(feedSignal_scaled) = intestineState1 - feedSignal_scaled; 86 | T2 .* der(F_digested_scaled) = intestineState2 - F_digested_scaled; 87 | feedSignal = feedSignal_scaled * F_nominal * 86400; 88 | F_digested = F_digested_scaled * F_nominal; 89 | 90 | for i in S loop 91 | // Conditional matrix product: leave out feedSignal for O2 and CO2, because respiration is not coupled to eating 92 | m_S[i] = waste.S_waste[i, :] * (if i == Types.Species.S.O or i == Types.Species.S.CO2 then {F, F_digested, dm_fish, m_fish} else {F, F_digested, dm_fish.*feedSignal, m_fish.*feedSignal}); 93 | end for; 94 | for i in X loop 95 | m_X[i] = waste.X_waste[i, :] * {F, F_digested, dm_fish.*feedSignal, m_fish.*feedSignal}; 96 | end for; 97 | 98 | annotation(experiment(StartTime = 0, StopTime = 2.592e+06, Tolerance = 0.0001, Interval = 3600)); 99 | 100 | end simpleCulture; -------------------------------------------------------------------------------- /LibRAS/Examples/Bypass.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Examples; 2 | 3 | model Bypass 4 | extends Modelica.Icons.Example; 5 | import Modelica.SIunits.Conversions.from_bar; 6 | type MassFlowRate = Modelica.SIunits.MassFlowRate(displayUnit = "g/d"); 7 | import LibRAS.Types.Species.S; 8 | import LibRAS.Types.Species.X; 9 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 10 | parameter Modelica.SIunits.Volume V_system = aerob1.V + aerob2.V + nitri1.V + nitri2.V + nitri3.V + degas1.V + sum(fishTank1.tankVolumes) "System volume - used to set controller gains"; 11 | parameter Modelica.SIunits.Volume[:] tankSizes = 0.25 * cat(1, {0.125 for i in 1:25}, {0.25 for i in 26:50}, {0.50 for i in 51:75}, {1 for i in 76:100}) annotation( 12 | HideResult = true); 13 | parameter Real dailyExchange = 0.100; 14 | inner LibRAS.System system(T_ambient = 288.15, T_start = 288.15) annotation( 15 | Placement(visible = true, transformation(extent = {{138, 44}, {158, 64}}, rotation = 0))); 16 | LibRAS.Machines.ControlledPump pump(redeclare package Medium = Medium, m_flow_nominal = 7.5, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5) annotation( 17 | Placement(visible = true, transformation(origin = {60, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 18 | LibRAS.Sources.WaterExchange exchange(redeclare package Medium = Medium) annotation( 19 | Placement(visible = true, transformation(origin = {88, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 20 | LibRAS.Pipes.Tee tee1(redeclare package Medium = Medium) annotation( 21 | Placement(visible = true, transformation(origin = {120, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 22 | LibRAS.Sources.Boundary_pT boundary1(nPorts = 1, redeclare package Medium = Medium) annotation( 23 | Placement(visible = true, transformation(origin = {148, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 24 | LibRAS.Pipes.StaticPipe pipe(diameter = 0.2, height_ab = 5, length = 10, redeclare package Medium = Medium) annotation( 25 | Placement(visible = true, transformation(origin = {30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 26 | LibRAS.Tanks.CSBR aerob1(redeclare package Medium = Medium, KLa = 500 / 86400, V = 1.75, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 27 | Placement(visible = true, transformation(origin = {10, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 28 | LibRAS.Tanks.CSBR aerob2(redeclare package Medium = Medium, KLa = 500 / 86400, V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 29 | Placement(visible = true, transformation(origin = {40, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 30 | LibRAS.Pipes.IdealParticleFilter filter(filterFactor = 0.9, redeclare package Medium = Medium) annotation( 31 | Placement(visible = true, transformation(origin = {-18, 12}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); 32 | LibRAS.Tanks.CSBR nitri1(redeclare package Medium = Medium, C_X_film_start(displayUnit = "kg/m3"), V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_m_S_in = true, use_portsData = false) annotation( 33 | Placement(visible = true, transformation(origin = {70, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 34 | LibRAS.Tanks.CSBR nitri2(redeclare package Medium = Medium, C_X_film_start(displayUnit = "kg/m3"), V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_portsData = false) annotation( 35 | Placement(visible = true, transformation(origin = {95, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 36 | LibRAS.Tanks.CSBR nitri3(redeclare package Medium = Medium, C_X_film_start(displayUnit = "kg/m3"), V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, nitrifying = true, use_portsData = false) annotation( 37 | Placement(visible = true, transformation(origin = {120, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 38 | Modelica.Blocks.Sources.Constant alkSetpoint(k = 2e-3) annotation( 39 | Placement(visible = true, transformation(origin = {155, -79}, extent = {{5, -5}, {-5, 5}}, rotation = 0))); 40 | LibRAS.Tanks.FishTank fishTank1(replaceable LibRAS.Culture.simpleCulture culture, FCR = 0.9, feedingDuration = 86400, feedingTimes = {0}, fishDensity = 55, gradingTime = 3, nPorts = 2, nTanks = 100, oxygenControl_Q = pump.m_flow_nominal * 1e-3, oxygenSetpoint = 9e-3, tankVolumes = tankSizes) annotation( 41 | Placement(visible = true, transformation(origin = {-18, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 42 | LibRAS.Machines.AdditionController_S alkControl(redeclare package Medium = Medium, K = 8 / 86400 * V_system, index = Integer(Types.Species.S.Alk), u = fill(0, Medium.nC_S)) annotation( 43 | Placement(visible = true, transformation(origin = {136, -64}, extent = {{6, 6}, {-6, -6}}, rotation = 0))); 44 | LibRAS.Tanks.CST degas1(redeclare package Medium = Medium, V = 0.7, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, use_portsData = false) annotation( 45 | Placement(visible = true, transformation(origin = {104, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 46 | LibRAS.Machines.ControlledPump pump1(redeclare package Medium = Medium, m_flow_nominal = 4, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5) annotation( 47 | Placement(visible = true, transformation(origin = {37, -9}, extent = {{-9, -9}, {9, 9}}, rotation = 0))); 48 | equation 49 | connect(pump1.port_a, filter.port_b) annotation( 50 | Line(points = {{28, -11}, {-18, -11}, {-18, 2}}, color = {0, 127, 255})); 51 | connect(pump1.port_b, degas1.ports[3]) annotation( 52 | Line(points = {{46, -11}, {102, -11}, {102, 10}, {104, 10}}, color = {0, 127, 255})); 53 | connect(filter.port_b, aerob1.ports[1]) annotation( 54 | Line(points = {{-18, 2}, {-18, 2}, {-18, -30}, {10, -30}, {10, -30}}, color = {0, 127, 255})); 55 | connect(fishTank1.ports[2], filter.port_a) annotation( 56 | Line(points = {{-18, 30}, {-18, 22}}, color = {0, 127, 255})); 57 | connect(degas1.ports[2], tee1.port_3) annotation( 58 | Line(points = {{104, 10}, {120, 10}, {120, 72}})); 59 | connect(nitri3.ports[2], degas1.ports[1]) annotation( 60 | Line(points = {{120, -30}, {120, -10}, {104, -10}, {104, 10}}, color = {0, 127, 255})); 61 | connect(nitri2.ports[2], nitri3.ports[1]) annotation( 62 | Line(points = {{96, -30}, {118, -30}, {118, -30}, {120, -30}}, color = {0, 127, 255})); 63 | connect(nitri1.ports[2], nitri2.ports[1]) annotation( 64 | Line(points = {{70, -30}, {92, -30}, {92, -30}, {96, -30}}, color = {0, 127, 255})); 65 | connect(alkControl.y, nitri1.m_S_in) annotation( 66 | Line(points = {{129, -64}, {66, -64}, {66, -50}}, color = {0, 0, 127})); 67 | connect(aerob2.ports[2], nitri1.ports[1]) annotation( 68 | Line(points = {{40, -30}, {70, -30}}, color = {0, 127, 255})); 69 | connect(aerob1.ports[2], aerob2.ports[1]) annotation( 70 | Line(points = {{10, -30}, {40, -30}}, color = {0, 127, 255})); 71 | connect(pipe.port_b, fishTank1.ports[1]) annotation( 72 | Line(points = {{20, 82}, {-4, 82}, {-4, 26}, {-18, 26}, {-18, 30}}, color = {0, 127, 255})); 73 | exchange.makeupRate = dailyExchange * V_system * 1000 / 86400 / pump.m_flow_nominal; 74 | connect(nitri3.ports[3], alkControl.port_a) annotation( 75 | Line(points = {{120, -30}, {122, -30}, {122, -24}, {136, -24}, {136, -58}}, color = {0, 127, 255})); 76 | connect(alkSetpoint.y, alkControl.setpoint) annotation( 77 | Line(points = {{149.5, -79}, {136, -79}, {136, -71}}, color = {0, 0, 127})); 78 | connect(pump.port_b, pipe.port_a) annotation( 79 | Line(points = {{50, 82}, {40, 82}}, color = {0, 127, 255})); 80 | connect(exchange.port_b, pump.port_a) annotation( 81 | Line(points = {{78, 82}, {70, 82}}, color = {0, 127, 255})); 82 | connect(tee1.port_2, exchange.port_a) annotation( 83 | Line(points = {{110, 82}, {98, 82}}, color = {0, 127, 255})); 84 | connect(boundary1.ports[1], tee1.port_1) annotation( 85 | Line(points = {{138, 82}, {130, 82}}, color = {0, 127, 255})); 86 | annotation( 87 | experiment(StopTime = 1.0368e+07, StartTime = 0, Tolerance = 0.0001, Interval = 3600), 88 | uses(Modelica(version = "3.2.2")), 89 | Placement(visible = true, transformation(origin = {-56, 58}, extent = {{-14, -14}, {14, 14}}, rotation = 0)), 90 | Diagram(coordinateSystem(extent = {{-200, -100}, {200, 100}}, initialScale = 0.1)), 91 | Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}})), 92 | version = "", 93 | __OpenModelica_commandLineOptions = ""); 94 | end Bypass; -------------------------------------------------------------------------------- /LibRAS/Examples/Inline.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Examples; 2 | 3 | model Inline 4 | extends Modelica.Icons.Example; 5 | import Modelica.SIunits.Conversions.from_bar; 6 | type MassFlowRate = Modelica.SIunits.MassFlowRate(displayUnit = "g/d"); 7 | import LibRAS.Types.Species.S; 8 | import LibRAS.Types.Species.X; 9 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 10 | parameter Modelica.SIunits.Volume V_system = aerob1.V + aerob2.V + nitri1.V + nitri2.V + nitri3.V + degas1.V + sum(fishTank1.tankVolumes) "System volume - used to set controller gains"; 11 | parameter Modelica.SIunits.Volume[:] tankSizes = 0.25 * cat(1, {0.125 for i in 1:25}, {0.25 for i in 26:50}, {0.50 for i in 51:75}, {1 for i in 76:100}) annotation( 12 | HideResult = true); 13 | parameter Real dailyExchange = 0.100; 14 | inner LibRAS.System system(T_ambient = 288.15, T_start = 288.15) annotation( 15 | Placement(visible = true, transformation(extent = {{138, 44}, {158, 64}}, rotation = 0))); 16 | LibRAS.Machines.ControlledPump pump(redeclare package Medium = Medium, m_flow_nominal = 7.5, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5) annotation( 17 | Placement(visible = true, transformation(origin = {60, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 18 | LibRAS.Sources.WaterExchange exchange(redeclare package Medium = Medium) annotation( 19 | Placement(visible = true, transformation(origin = {88, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 20 | LibRAS.Pipes.Tee tee1(redeclare package Medium = Medium) annotation( 21 | Placement(visible = true, transformation(origin = {120, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 22 | LibRAS.Sources.Boundary_pT boundary1(nPorts = 1, redeclare package Medium = Medium) annotation( 23 | Placement(visible = true, transformation(origin = {148, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 24 | LibRAS.Pipes.StaticPipe pipe(diameter = 0.2, height_ab = 5, length = 10, redeclare package Medium = Medium) annotation( 25 | Placement(visible = true, transformation(origin = {30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 26 | LibRAS.Tanks.CSBR aerob1(redeclare package Medium = Medium, KLa = 500 / 86400, V = 1.75, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 27 | Placement(visible = true, transformation(origin = {10, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 28 | LibRAS.Tanks.CSBR aerob2(redeclare package Medium = Medium, KLa = 500 / 86400, V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 29 | Placement(visible = true, transformation(origin = {40, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 30 | LibRAS.Pipes.IdealParticleFilter filter(filterFactor = 0.9, redeclare package Medium = Medium) annotation( 31 | Placement(visible = true, transformation(origin = {-4, -12}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 32 | LibRAS.Tanks.CSBR nitri1(redeclare package Medium = Medium, C_X_film_start(displayUnit = "kg/m3"), V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_m_S_in = true, use_portsData = false) annotation( 33 | Placement(visible = true, transformation(origin = {70, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 34 | LibRAS.Tanks.CSBR nitri2(redeclare package Medium = Medium, C_X_film_start(displayUnit = "kg/m3"), V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_portsData = false) annotation( 35 | Placement(visible = true, transformation(origin = {95, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 36 | LibRAS.Tanks.CSBR nitri3(redeclare package Medium = Medium, C_X_film_start(displayUnit = "kg/m3"), V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, nitrifying = true, use_portsData = false) annotation( 37 | Placement(visible = true, transformation(origin = {120, -40}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 38 | Modelica.Blocks.Sources.Constant alkSetpoint(k = 2e-3) annotation( 39 | Placement(visible = true, transformation(origin = {155, -79}, extent = {{5, -5}, {-5, 5}}, rotation = 0))); 40 | LibRAS.Tanks.FishTank fishTank1(replaceable LibRAS.Culture.simpleCulture culture, FCR = 0.9, feedingDuration = 86400, feedingTimes = {0}, fishDensity = 55, gradingTime = 3, nPorts = 2, nTanks = 100, oxygenControl_Q = pump.m_flow_nominal * 1e-3, oxygenSetpoint = 9e-3, tankVolumes = tankSizes) annotation( 41 | Placement(visible = true, transformation(origin = {-18, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 42 | LibRAS.Machines.AdditionController_S alkControl(redeclare package Medium = Medium, K = 8 / 86400 * V_system, index = Integer(Types.Species.S.Alk), u = fill(0, Medium.nC_S)) annotation( 43 | Placement(visible = true, transformation(origin = {136, -64}, extent = {{6, 6}, {-6, -6}}, rotation = 0))); 44 | LibRAS.Tanks.CST degas1(redeclare package Medium = Medium, V = 0.7, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 45 | Placement(visible = true, transformation(origin = {104, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 46 | equation 47 | connect(degas1.ports[2], tee1.port_3) annotation( 48 | Line(points = {{104, 10}, {120, 10}, {120, 72}})); 49 | connect(nitri3.ports[2], degas1.ports[1]) annotation( 50 | Line(points = {{120, -30}, {120, -10}, {104, -10}, {104, 10}}, color = {0, 127, 255})); 51 | connect(nitri2.ports[2], nitri3.ports[1]) annotation( 52 | Line(points = {{96, -30}, {118, -30}, {118, -30}, {120, -30}}, color = {0, 127, 255})); 53 | connect(nitri1.ports[2], nitri2.ports[1]) annotation( 54 | Line(points = {{70, -30}, {92, -30}, {92, -30}, {96, -30}}, color = {0, 127, 255})); 55 | connect(alkControl.y, nitri1.m_S_in) annotation( 56 | Line(points = {{129, -64}, {66, -64}, {66, -50}}, color = {0, 0, 127})); 57 | connect(aerob2.ports[2], nitri1.ports[1]) annotation( 58 | Line(points = {{40, -30}, {70, -30}}, color = {0, 127, 255})); 59 | connect(aerob1.ports[2], aerob2.ports[1]) annotation( 60 | Line(points = {{10, -30}, {40, -30}}, color = {0, 127, 255})); 61 | connect(filter.port_b, aerob1.ports[1]) annotation( 62 | Line(points = {{6, -12}, {10, -12}, {10, -30}}, color = {0, 127, 255})); 63 | connect(fishTank1.ports[2], filter.port_a) annotation( 64 | Line(points = {{-18, 30}, {-18, -12}, {-14, -12}}, color = {0, 127, 255})); 65 | connect(pipe.port_b, fishTank1.ports[1]) annotation( 66 | Line(points = {{20, 82}, {-4, 82}, {-4, 26}, {-18, 26}, {-18, 30}}, color = {0, 127, 255})); 67 | exchange.makeupRate = dailyExchange * V_system * 1000 / 86400 / pump.m_flow_nominal; 68 | connect(nitri3.ports[3], alkControl.port_a) annotation( 69 | Line(points = {{120, -30}, {122, -30}, {122, -24}, {136, -24}, {136, -58}}, color = {0, 127, 255})); 70 | connect(alkSetpoint.y, alkControl.setpoint) annotation( 71 | Line(points = {{149.5, -79}, {136, -79}, {136, -71}}, color = {0, 0, 127})); 72 | connect(pump.port_b, pipe.port_a) annotation( 73 | Line(points = {{50, 82}, {40, 82}}, color = {0, 127, 255})); 74 | connect(exchange.port_b, pump.port_a) annotation( 75 | Line(points = {{78, 82}, {70, 82}}, color = {0, 127, 255})); 76 | connect(tee1.port_2, exchange.port_a) annotation( 77 | Line(points = {{110, 82}, {98, 82}}, color = {0, 127, 255})); 78 | connect(boundary1.ports[1], tee1.port_1) annotation( 79 | Line(points = {{138, 82}, {130, 82}}, color = {0, 127, 255})); 80 | annotation( 81 | experiment(StopTime = 1.0368e+07, StartTime = 0, Tolerance = 0.0001, Interval = 3600), 82 | uses(Modelica(version = "3.2.2")), 83 | Placement(visible = true, transformation(origin = {-56, 58}, extent = {{-14, -14}, {14, 14}}, rotation = 0)), 84 | Diagram(coordinateSystem(extent = {{-200, -100}, {200, 100}}, initialScale = 0.1)), 85 | Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}})), 86 | version = "", 87 | __OpenModelica_commandLineOptions = ""); 88 | end Inline; -------------------------------------------------------------------------------- /LibRAS/Examples/ReactorTest.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Examples; 2 | 3 | model ReactorTest 4 | extends Modelica.Icons.Example; 5 | import Modelica.SIunits.Conversions.from_bar; 6 | type MassFlowRate = Modelica.SIunits.MassFlowRate(displayUnit = "g/d"); 7 | import LibRAS.Types.Species.S; 8 | import LibRAS.Types.Species.X; 9 | 10 | parameter Modelica.SIunits.Volume V_aerob = 5.0 "Treatment tank volume"; 11 | parameter Real C_NH = 5.0; 12 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 13 | inner LibRAS.System system(T_ambient = Modelica.SIunits.Conversions.from_degC(20)) annotation( 14 | Placement(visible = true, transformation(extent = {{138, 44}, {158, 64}}, rotation = 0))); 15 | LibRAS.Machines.ControlledPump pump(redeclare package Medium = Medium, m_flow_nominal = 10, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5) annotation( 16 | Placement(visible = true, transformation(origin = {60, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 17 | LibRAS.Sources.WaterExchange exchange(redeclare package Medium = Medium, source.C_S = {0.1, 0.1, 8, 0.01, 10, C_NH, 0.1, 2, 0.1, 0.1}*1e-3, source.C_X = {0.1, 0.1, 0.1, 0.01, 0.01, 1, 0.1}*1e-3) annotation( 18 | Placement(visible = true, transformation(origin = {88, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 19 | LibRAS.Pipes.Tee tee1(redeclare package Medium = Medium) annotation( 20 | Placement(visible = true, transformation(origin = {120, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 21 | LibRAS.Sources.Boundary_pT boundary1(nPorts = 1, redeclare package Medium = Medium) annotation( 22 | Placement(visible = true, transformation(origin = {148, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 23 | LibRAS.Pipes.StaticPipe pipe(diameter = 0.2, height_ab = 5, length = 10, redeclare package Medium = Medium) annotation( 24 | Placement(visible = true, transformation(origin = {30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 25 | LibRAS.Tanks.CSBR aerob1(KLa = 500 / 86400, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, use_portsData = false, redeclare package Medium = Medium, V = V_aerob, nPorts = 2) annotation( 26 | Placement(visible = true, transformation(origin = {50, 20}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 27 | LibRAS.Tanks.CST bufferTank(redeclare package Medium = Medium, V = 1, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, use_portsData = false) annotation( 28 | Placement(visible = true, transformation(origin = {20, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 29 | equation 30 | connect(bufferTank.ports[2], aerob1.ports[1]) annotation( 31 | Line(points = {{20, 30}, {48, 30}, {48, 30}, {50, 30}}, color = {0, 127, 255}, thickness = 0.5)); 32 | connect(aerob1.ports[2], tee1.port_3) annotation( 33 | Line(points = {{50, 30}, {120, 30}, {120, 72}}, color = {0, 127, 255}, thickness = 0.5)); 34 | connect(pipe.port_b, bufferTank.ports[1]) annotation( 35 | Line(points = {{20, 82}, {0, 82}, {0, 30}, {20, 30}, {20, 30}}, color = {0, 127, 255})); 36 | exchange.makeupRate = 1; 37 | connect(pump.port_b, pipe.port_a) annotation( 38 | Line(points = {{50, 82}, {40, 82}}, color = {0, 127, 255})); 39 | connect(exchange.port_b, pump.port_a) annotation( 40 | Line(points = {{78, 82}, {70, 82}}, color = {0, 127, 255})); 41 | connect(tee1.port_2, exchange.port_a) annotation( 42 | Line(points = {{110, 82}, {98, 82}}, color = {0, 127, 255})); 43 | connect(boundary1.ports[1], tee1.port_1) annotation( 44 | Line(points = {{138, 82}, {130, 82}}, color = {0, 127, 255})); 45 | annotation( 46 | experiment(StopTime = 86400, StartTime = 0, Tolerance = 0.0001, Interval = 3600), 47 | uses(Modelica(version = "3.2.2")), 48 | Placement(visible = true, transformation(origin = {-56, 58}, extent = {{-14, -14}, {14, 14}}, rotation = 0)), 49 | Diagram(coordinateSystem(extent = {{-200, -100}, {200, 100}}, initialScale = 0.1)), 50 | Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}})), 51 | version = "", 52 | __OpenModelica_commandLineOptions = ""); 53 | end ReactorTest; 54 | -------------------------------------------------------------------------------- /LibRAS/Examples/Recycle.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Examples; 2 | 3 | model Recycle 4 | extends Modelica.Icons.Example; 5 | import Modelica.SIunits.Conversions.from_bar; 6 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 7 | parameter Modelica.SIunits.Volume V_system = aerob1.V + aerob2.V + nitri1.V + nitri2.V + nitri3.V + degas1.V + sum(fishTank1.tankVolumes) "System volume"; 8 | parameter Modelica.SIunits.Volume[:] tankSizes = 0.25 * cat(1, {0.125 for i in 1:25}, {0.25 for i in 26:50}, {0.50 for i in 51:75}, {1 for i in 76:100}) annotation( 9 | HideResult = true); 10 | parameter Real dailyExchange = 0.10; 11 | inner LibRAS.System system(T_ambient = 288.15, T_start = 288.15) annotation( 12 | Placement(visible = true, transformation(extent = {{138, 44}, {158, 64}}, rotation = 0))); 13 | LibRAS.Machines.ControlledPump pump(redeclare package Medium = Medium, m_flow_nominal = 7.5, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5) annotation( 14 | Placement(visible = true, transformation(origin = {60, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 15 | LibRAS.Sources.WaterExchange exchange(redeclare package Medium = Medium) annotation( 16 | Placement(visible = true, transformation(origin = {88, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 17 | LibRAS.Pipes.Tee tee1(redeclare package Medium = Medium) annotation( 18 | Placement(visible = true, transformation(origin = {120, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 19 | LibRAS.Sources.Boundary_pT boundary1(nPorts = 1, redeclare package Medium = Medium) annotation( 20 | Placement(visible = true, transformation(origin = {148, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 21 | LibRAS.Pipes.StaticPipe pipe(diameter = 0.2, height_ab = 5, length = 10, redeclare package Medium = Medium) annotation( 22 | Placement(visible = true, transformation(origin = {30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 23 | LibRAS.Tanks.CSBR aerob1(redeclare package Medium = Medium, V = 1.75, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 24 | Placement(visible = true, transformation(origin = {90, -56}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 25 | LibRAS.Tanks.CSBR aerob2(redeclare package Medium = Medium, V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, use_portsData = false) annotation( 26 | Placement(visible = true, transformation(origin = {64, -56}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 27 | LibRAS.Pipes.IdealParticleFilter filter(filterFactor = 0.9, redeclare package Medium = Medium) annotation( 28 | Placement(visible = true, transformation(origin = {54, -4}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 29 | LibRAS.Tanks.CSBR nitri1(redeclare package Medium = Medium, C_X_film_start(each displayUnit = "kg/m3") = (1 - system.eps_A) / (1 - system.eps_H) * system.C_X_film_start, V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_portsData = false, use_m_S_in = true) annotation( 30 | Placement(visible = true, transformation(origin = {36, -56}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 31 | LibRAS.Tanks.CSBR nitri2(redeclare package Medium = Medium, C_X_film_start(each displayUnit = "kg/m3") = (1 - system.eps_A) / (1 - system.eps_H) * system.C_X_film_start, V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_portsData = false) annotation( 32 | Placement(visible = true, transformation(origin = {11, -56}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 33 | LibRAS.Tanks.CSBR nitri3(redeclare package Medium = Medium, C_X_film_start(each displayUnit = "kg/m3") = (1 - system.eps_A) / (1 - system.eps_H) * system.C_X_film_start, V = aerob1.V, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, nitrifying = true, use_portsData = false) annotation( 34 | Placement(visible = true, transformation(origin = {-14, -56}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 35 | Modelica.Blocks.Sources.Constant alkSetpoint(k = 2e-3) annotation( 36 | Placement(visible = true, transformation(origin = {13, -13}, extent = {{5, -5}, {-5, 5}}, rotation = 0))); 37 | LibRAS.Machines.AdditionController_S alkControl(index = Integer(Types.Species.S.Alk), K = 8 / 86400 * V_system, redeclare package Medium = Medium, u = fill(0, Medium.nC_S)) annotation( 38 | Placement(visible = true, transformation(origin = {0, -32}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); 39 | LibRAS.Tanks.FishTank fishTank1(feedingDuration = 86400, feedingTimes = {0}, nPorts = 3, oxygenSetpoint = 9e-3, oxygenControl_Q = pump.m_flow_nominal * 1e-3, nTanks = 100, tankVolumes = tankSizes, fishDensity = 55, fish = Culture.Fish.AtlanticSalmon(), FCR = 0.9, gradingTime = 3, replaceable LibRAS.Culture.simpleCulture culture) annotation( 40 | Placement(visible = true, transformation(origin = {-50, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 41 | LibRAS.Tanks.CST degas1(redeclare package Medium = Medium, V = 0.7, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, use_portsData = false) annotation( 42 | Placement(visible = true, transformation(origin = {90, 6}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 43 | LibRAS.Pipes.Tee tee2(redeclare package Medium = Medium) annotation( 44 | Placement(visible = true, transformation(origin = {-50, -4}, extent = {{10, 10}, {-10, -10}}, rotation = 90))); 45 | LibRAS.Machines.ControlledPump recyclePump(redeclare package Medium = Medium, m_flow_nominal = pump.m_flow_nominal * recycleRatio.k, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5, use_m_flow_set = true) annotation( 46 | Placement(visible = true, transformation(origin = {-36, -46}, extent = {{8, -8}, {-8, 8}}, rotation = 0))); 47 | LibRAS.Sensors.MassFlowRate massFlowRate1(redeclare package Medium = Medium) annotation( 48 | Placement(visible = true, transformation(origin = {110, -4}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 49 | Modelica.Blocks.Math.Gain recycleRatio(k = 0.3) annotation( 50 | Placement(visible = true, transformation(origin = {-22, -22}, extent = {{6, -6}, {-6, 6}}, rotation = 0))); 51 | equation 52 | connect(nitri3.ports[2], recyclePump.port_a) annotation( 53 | Line(points = {{-16, -46}, {-28, -46}, {-28, -46}, {-28, -46}}, color = {0, 127, 255})); 54 | connect(nitri3.ports[3], alkControl.port_a) annotation( 55 | Line(points = {{-16, -46}, {-16, -46}, {-16, -42}, {0, -42}, {0, -38}, {0, -38}}, color = {0, 127, 255})); 56 | connect(nitri2.ports[2], nitri3.ports[1]) annotation( 57 | Line(points = {{11, -46}, {-16, -46}}, color = {0, 127, 255})); 58 | connect(nitri1.ports[2], nitri2.ports[1]) annotation( 59 | Line(points = {{36, -46}, {9, -46}}, color = {0, 127, 255})); 60 | connect(alkControl.y, nitri1.m_S_in) annotation( 61 | Line(points = {{8, -32}, {24, -32}, {24, -72}, {30, -72}, {30, -66}}, color = {0, 0, 127})); 62 | connect(aerob2.ports[2], nitri1.ports[1]) annotation( 63 | Line(points = {{64, -46}, {34, -46}}, color = {0, 127, 255})); 64 | connect(massFlowRate1.port_b, tee1.port_3) annotation( 65 | Line(points = {{120, -6}, {120, 72}}, color = {0, 127, 255})); 66 | connect(degas1.ports[3], massFlowRate1.port_a) annotation( 67 | Line(points = {{90, -4}, {95, -4}, {95, -6}, {100, -6}}, color = {0, 127, 255})); 68 | connect(massFlowRate1.m_flow, recycleRatio.u) annotation( 69 | Line(points = {{110, -17}, {110, -22}, {-15, -22}}, color = {0, 0, 127})); 70 | connect(alkSetpoint.y, alkControl.setpoint) annotation( 71 | Line(points = {{7.5, -13}, {0, -13}, {0, -24}}, color = {0, 0, 127})); 72 | connect(recycleRatio.y, recyclePump.m_flow_set) annotation( 73 | Line(points = {{-29, -22}, {-32, -22}, {-32, -39}}, color = {0, 0, 127})); 74 | connect(aerob1.ports[2], aerob2.ports[1]) annotation( 75 | Line(points = {{90, -46}, {66, -46}, {66, -46}, {64, -46}}, color = {0, 127, 255})); 76 | connect(recyclePump.port_b, tee2.port_2) annotation( 77 | Line(points = {{-44, -46}, {-50, -46}, {-50, -14}}, color = {0, 127, 255})); 78 | connect(tee2.port_3, filter.port_a) annotation( 79 | Line(points = {{-40, -4}, {44, -4}, {44, -4}, {44, -4}}, color = {0, 127, 255})); 80 | connect(fishTank1.ports[2], tee2.port_1) annotation( 81 | Line(points = {{-50, 30}, {-50, 6}}, color = {0, 127, 255})); 82 | connect(filter.port_b, degas1.ports[1]) annotation( 83 | Line(points = {{64, -4}, {90, -4}}, color = {0, 127, 255})); 84 | connect(degas1.ports[2], aerob1.ports[1]) annotation( 85 | Line(points = {{90, -4}, {90, -46}}, color = {0, 127, 255})); 86 | connect(pipe.port_b, fishTank1.ports[1]) annotation( 87 | Line(points = {{20, 82}, {-4, 82}, {-4, 26}, {-50, 26}, {-50, 30}}, color = {0, 127, 255})); 88 | exchange.makeupRate = dailyExchange * V_system * 1000 / 86400 / pump.m_flow_nominal; 89 | connect(pump.port_b, pipe.port_a) annotation( 90 | Line(points = {{50, 82}, {40, 82}}, color = {0, 127, 255})); 91 | connect(exchange.port_b, pump.port_a) annotation( 92 | Line(points = {{78, 82}, {70, 82}}, color = {0, 127, 255})); 93 | connect(tee1.port_2, exchange.port_a) annotation( 94 | Line(points = {{110, 82}, {98, 82}}, color = {0, 127, 255})); 95 | connect(boundary1.ports[1], tee1.port_1) annotation( 96 | Line(points = {{138, 82}, {130, 82}}, color = {0, 127, 255})); 97 | annotation( 98 | experiment(StopTime = 1.0368e+07, StartTime = 0, Tolerance = 0.0001, Interval = 3600), 99 | uses(Modelica(version = "3.2.2")), 100 | Placement(visible = true, transformation(origin = {-56, 58}, extent = {{-14, -14}, {14, 14}}, rotation = 0)), 101 | Diagram(coordinateSystem(extent = {{-200, -100}, {200, 100}}, initialScale = 0.1)), 102 | Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}})), 103 | version = "", 104 | __OpenModelica_commandLineOptions = ""); 105 | end Recycle; -------------------------------------------------------------------------------- /LibRAS/Examples/TinyRAS.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Examples; 2 | 3 | model TinyRAS 4 | extends Modelica.Icons.Example; 5 | import convert = Modelica.SIunits.Conversions; 6 | import Modelica.SIunits.Conversions.from_bar; 7 | type MassFlowRate = Modelica.SIunits.MassFlowRate(displayUnit = "g/d"); 8 | import LibRAS.Types.Species.S; 9 | import LibRAS.Types.Species.X; 10 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 11 | parameter Modelica.SIunits.Time HRT (displayUnit = "min") = 1200 "Fish tank HRT in seconds"; 12 | parameter Modelica.SIunits.Volume V_system = MBBR1.V + MBBR2.V + sum(fishTank1.tankVolumes) "System volume - used to set controller gains and calculate water exchange"; 13 | parameter Real dailyExchange = 0.100; 14 | inner LibRAS.System system(T_ambient = 288.15, T_start = 288.15) annotation( 15 | Placement(visible = true, transformation(extent = {{138, 44}, {158, 64}}, rotation = 0))); 16 | LibRAS.Machines.ControlledPump pump(redeclare package Medium = Medium, m_flow_nominal = 1000*sum(fishTank1.tankVolumes)/HRT, p_a_nominal = system.p_ambient, p_b_nominal = system.p_ambient + 0.5e5) annotation( 17 | Placement(visible = true, transformation(origin = {60, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 18 | LibRAS.Sources.WaterExchange exchange(redeclare package Medium = Medium) annotation( 19 | Placement(visible = true, transformation(origin = {88, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 20 | LibRAS.Pipes.Tee tee1(redeclare package Medium = Medium) annotation( 21 | Placement(visible = true, transformation(origin = {120, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 22 | LibRAS.Sources.Boundary_pT boundary1(nPorts = 1, redeclare package Medium = Medium) annotation( 23 | Placement(visible = true, transformation(origin = {148, 82}, extent = {{10, -10}, {-10, 10}}, rotation = 0))); 24 | LibRAS.Pipes.StaticPipe pipe(diameter = 0.2, height_ab = 5, length = 10, redeclare package Medium = Medium) annotation( 25 | Placement(visible = true, transformation(origin = {30, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 180))); 26 | LibRAS.Pipes.IdealParticleFilter filter(filterFactor = 0.9, redeclare package Medium = Medium) annotation( 27 | Placement(visible = true, transformation(origin = {-4, 52}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 28 | LibRAS.Tanks.CSBR MBBR2(redeclare package Medium = Medium, C_X_film_start(each displayUnit = "kg/m3"), V = 20, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 3, nitrifying = true, use_m_S_in = true, use_portsData = false) annotation( 29 | Placement(visible = true, transformation(origin = {118, 36}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 30 | Modelica.Blocks.Sources.Constant alkSetpoint(k = 2e-3) annotation( 31 | Placement(visible = true, transformation(origin = {117, -1}, extent = {{5, -5}, {-5, 5}}, rotation = 0))); 32 | LibRAS.Tanks.FishTank fishTank1(replaceable LibRAS.Culture.SSCulture_V culture, FCR = 0.9, feedingDuration = 86400, feedingTimes = {0}, fishDensity = 55, gradingTime = 30, nPorts = 2, nTanks = 9, oxygenControl_Q = pump.m_flow_nominal * 1e-3, oxygenSetpoint = 9e-3, tankVolumes = {10 for i in 1:fishTank1.nTanks}) annotation( 33 | Placement(visible = true, transformation(origin = {-18, 82}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 34 | LibRAS.Machines.AdditionController_S alkControl(redeclare package Medium = Medium, K = 8 / 86400 * V_system, index = Integer(Types.Species.S.Alk), u = fill(0, Medium.nC_S)) annotation( 35 | Placement(visible = true, transformation(origin = {102, 12}, extent = {{-6, 6}, {6, -6}}, rotation = 0))); 36 | LibRAS.Tanks.CSBR MBBR1(C_X_film_start(each displayUnit = "kg/m3"), V = 20, energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = 2, nitrifying = true, use_m_S_in = false, use_portsData = false) annotation( 37 | Placement(visible = true, transformation(origin = {54, 42}, extent = {{-10, 10}, {10, -10}}, rotation = 0))); 38 | equation 39 | connect(MBBR1.ports[2], MBBR2.ports[1]) annotation( 40 | Line(points = {{54, 52}, {118, 52}, {118, 46}, {118, 46}}, color = {0, 127, 255}, thickness = 0.5)); 41 | connect(filter.port_b, MBBR1.ports[1]) annotation( 42 | Line(points = {{6, 52}, {52, 52}, {52, 52}, {54, 52}})); 43 | connect(tee1.port_3, MBBR2.ports[2]) annotation( 44 | Line(points = {{120, 72}, {120, 72}, {120, 46}, {118, 46}}, color = {0, 127, 255})); 45 | connect(MBBR2.ports[3], alkControl.port_a) annotation( 46 | Line(points = {{120, 46}, {104, 46}, {104, 18}, {104, 18}}, color = {0, 127, 255}, thickness = 0.5)); 47 | connect(alkControl.y, MBBR2.m_S_in) annotation( 48 | Line(points = {{109.2, 12}, {111.15, 12}, {111.15, 12}, {115.1, 12}, {115.1, 12}, {117, 12}, {117, 19}, {115, 19}, {115, 26}}, color = {0, 0, 127}, thickness = 0.5)); 49 | connect(alkSetpoint.y, alkControl.setpoint) annotation( 50 | Line(points = {{111.5, -1}, {102, -1}, {102, 5}}, color = {0, 0, 127})); 51 | connect(fishTank1.ports[2], filter.port_a) annotation( 52 | Line(points = {{-18, 72}, {-18, 54}, {-14, 54}}, color = {0, 127, 255})); 53 | connect(pipe.port_b, fishTank1.ports[1]) annotation( 54 | Line(points = {{20, 82}, {2, 82}, {2, 68}, {-16, 68}, {-16, 72}, {-18, 72}}, color = {0, 127, 255})); 55 | exchange.makeupRate = dailyExchange * V_system * 1000 / 86400 / pump.m_flow_nominal; 56 | connect(pump.port_b, pipe.port_a) annotation( 57 | Line(points = {{50, 82}, {40, 82}}, color = {0, 127, 255})); 58 | connect(exchange.port_b, pump.port_a) annotation( 59 | Line(points = {{78, 82}, {70, 82}}, color = {0, 127, 255})); 60 | connect(tee1.port_2, exchange.port_a) annotation( 61 | Line(points = {{110, 82}, {98, 82}}, color = {0, 127, 255})); 62 | connect(boundary1.ports[1], tee1.port_1) annotation( 63 | Line(points = {{138, 82}, {130, 82}}, color = {0, 127, 255})); 64 | annotation( 65 | experiment(StopTime = 1.0368e+07, StartTime = 0, Tolerance = 0.0001, Interval = 3600), 66 | uses(Modelica(version = "3.2.2")), 67 | Placement(visible = true, transformation(origin = {-56, 58}, extent = {{-14, -14}, {14, 14}}, rotation = 0)), 68 | Diagram(coordinateSystem(extent = {{-100, -50}, {200, 100}})), 69 | Icon(coordinateSystem(extent = {{-100, -50}, {200, 100}})), 70 | version = "", 71 | __OpenModelica_commandLineOptions = ""); 72 | end TinyRAS; -------------------------------------------------------------------------------- /LibRAS/Examples/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Examples 3 | extends Modelica.Icons.ExamplesPackage; 4 | end Examples; 5 | -------------------------------------------------------------------------------- /LibRAS/Examples/package.order: -------------------------------------------------------------------------------- 1 | Bypass 2 | Inline 3 | Recycle 4 | ReactorTest 5 | TinyRAS 6 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/PartialLumpedVolume.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | partial model PartialLumpedVolume "Lumped volume with mass and energy balance" 3 | import SI = Modelica.SIunits; 4 | import Modelica.Fluid.Types; 5 | import Modelica.Fluid.Types.Dynamics; 6 | import Modelica.Media.Interfaces.Choices.IndependentVariables; 7 | outer LibRAS.System system "System properties"; 8 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component" annotation(choicesAllMatching = true); 9 | // Inputs provided to the volume model 10 | input SI.Volume fluidVolume "Volume"; 11 | // Assumptions 12 | parameter Types.Dynamics energyDynamics = system.energyDynamics "Formulation of energy balance" annotation(Evaluate = true, Dialog(tab = "Assumptions", group = "Dynamics")); 13 | parameter Types.Dynamics massDynamics = system.massDynamics "Formulation of mass balance" annotation(Evaluate = true, Dialog(tab = "Assumptions", group = "Dynamics")); 14 | final parameter Types.Dynamics substanceDynamics = massDynamics "Formulation of substance balance" annotation(Evaluate = true, Dialog(tab = "Assumptions", group = "Dynamics")); 15 | final parameter Types.Dynamics traceDynamics = massDynamics "Formulation of trace substance balance" annotation(Evaluate = true, Dialog(tab = "Assumptions", group = "Dynamics")); 16 | // Initialization 17 | parameter Medium.AbsolutePressure p_start = system.p_start "Start value of pressure" annotation(Dialog(tab = "Initialization")); 18 | parameter Boolean use_T_start = true "= true, use T_start, otherwise h_start" annotation(Dialog(tab = "Initialization"), Evaluate = true); 19 | parameter Medium.Temperature T_start = if use_T_start then system.T_start else Medium.temperature_phX(p_start, h_start, X_start) "Start value of temperature" annotation(Dialog(tab = "Initialization", enable = use_T_start)); 20 | parameter Medium.SpecificEnthalpy h_start = if use_T_start then Medium.specificEnthalpy_pTX(p_start, T_start, X_start) else Medium.h_default "Start value of specific enthalpy" annotation(Dialog(tab = "Initialization", enable = not use_T_start)); 21 | parameter Medium.MassFraction X_start[Medium.nX] = Medium.X_default "Start value of mass fractions m_i/m" annotation(Dialog(tab = "Initialization", enable = Medium.nXi > 0)); 22 | parameter Medium.ExtraProperty C_start[Medium.nC](quantity = Medium.extraPropertiesNames) = fill(0, Medium.nC) "Start value of trace substances" annotation(Dialog(tab = "Initialization", enable = Medium.nC > 0)); 23 | parameter Medium.ExtraProperty C_S_start[Medium.nC_S](quantity = Medium.solublesNames) = fill(0, Medium.nC_S) "Start value of solubles substances" annotation(Dialog(tab = "Initialization", enable = Medium.nC_S > 0)); 24 | parameter Medium.ExtraProperty C_X_start[Medium.nC_X](quantity = Medium.particulatesNames) = fill(0, Medium.nC_X) "Start value of particulate substances" annotation(Dialog(tab = "Initialization", enable = Medium.nC_X > 0)); 25 | Medium.BaseProperties medium(preferredMediumStates = true, p(start = p_start), h(start = h_start), T(start = T_start), Xi(start = X_start[1:Medium.nXi])); 26 | SI.Energy U "Internal energy of fluid"; 27 | SI.Mass m "Mass of fluid"; 28 | SI.Mass[Medium.nXi] mXi "Masses of independent components in the fluid"; 29 | SI.Mass[Medium.nC] mC "Masses of trace substances in the fluid"; 30 | // C need to be added here because unlike for Xi, which has medium.Xi, 31 | // there is no variable medium.C 32 | SI.Mass[Medium.nC_S] mC_S "Masses of trace substances in the fluid"; 33 | SI.Mass[Medium.nC_X] mC_X "Masses of trace substances in the fluid"; 34 | Medium.ExtraProperty C[Medium.nC] "Trace substance mixture content"; 35 | Medium.ExtraProperty C_S[Medium.nC_S] "Trace substance mixture content"; 36 | Medium.ExtraProperty C_X[Medium.nC_X] "Trace substance mixture content"; 37 | // variables that need to be defined by an extending class 38 | SI.MassFlowRate mb_flow "Mass flows across boundaries"; 39 | SI.MassFlowRate[Medium.nXi] mbXi_flow "Substance mass flows across boundaries"; 40 | Medium.ExtraPropertyFlowRate[Medium.nC] mbC_flow "Trace substance mass flows across boundaries"; 41 | Medium.ExtraPropertyFlowRate[Medium.nC_S] mbC_S_flow "Trace substance mass flows across boundaries"; 42 | Medium.ExtraPropertyFlowRate[Medium.nC_X] mbC_X_flow "Trace substance mass flows across boundaries"; 43 | SI.EnthalpyFlowRate Hb_flow "Enthalpy flow across boundaries or energy source/sink"; 44 | SI.HeatFlowRate Qb_flow "Heat flow across boundaries or energy source/sink"; 45 | SI.Power Wb_flow "Work flow across boundaries or source term"; 46 | protected 47 | parameter Boolean initialize_p = not Medium.singleState "= true to set up initial equations for pressure"; 48 | Real[Medium.nC] mC_scaled(min = fill(Modelica.Constants.eps, Medium.nC)) "Scaled masses of trace substances in the fluid"; 49 | Real[Medium.nC_S] mC_S_scaled(min = fill(Modelica.Constants.eps, Medium.nC_S)) "Scaled masses of trace substances in the fluid"; 50 | Real[Medium.nC_X] mC_X_scaled(min = fill(Modelica.Constants.eps, Medium.nC_X)) "Scaled masses of trace substances in the fluid"; 51 | equation 52 | assert(not (energyDynamics <> Dynamics.SteadyState and massDynamics == Dynamics.SteadyState) or Medium.singleState, "Bad combination of dynamics options and Medium not conserving mass if fluidVolume is fixed."); 53 | // Total quantities 54 | m = fluidVolume * medium.d; 55 | mXi = m * medium.Xi; 56 | U = m * medium.u; 57 | mC = m * C; 58 | mC_S = (m/medium.d) * C_S; 59 | mC_X = (m/medium.d) * C_X; 60 | // Energy and mass balances 61 | if energyDynamics == Dynamics.SteadyState then 62 | 0 = Hb_flow + Qb_flow + Wb_flow; 63 | else 64 | der(U) = Hb_flow + Qb_flow + Wb_flow; 65 | end if; 66 | if massDynamics == Dynamics.SteadyState then 67 | 0 = mb_flow; 68 | else 69 | der(m) = mb_flow; 70 | end if; 71 | if substanceDynamics == Dynamics.SteadyState then 72 | zeros(Medium.nXi) = mbXi_flow; 73 | else 74 | der(mXi) = mbXi_flow; 75 | end if; 76 | if traceDynamics == Dynamics.SteadyState then 77 | zeros(Medium.nC) = mbC_flow; 78 | zeros(Medium.nC_S) = mbC_S_flow; 79 | zeros(Medium.nC_X) = mbC_X_flow; 80 | else 81 | der(mC_scaled) = mbC_flow ./ Medium.C_nominal; 82 | der(mC_S_scaled) = mbC_S_flow ./ Medium.C_S_nominal; 83 | der(mC_X_scaled) = mbC_X_flow ./ Medium.C_X_nominal; 84 | end if; 85 | mC = mC_scaled .* Medium.C_nominal; 86 | mC_S = mC_S_scaled .* Medium.C_S_nominal; 87 | mC_X = mC_X_scaled .* Medium.C_X_nominal; 88 | initial equation 89 | // initialization of balances 90 | if energyDynamics == Dynamics.FixedInitial then 91 | /* 92 | if use_T_start then 93 | medium.T = T_start; 94 | else 95 | medium.h = h_start; 96 | end if; 97 | */ 98 | if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then 99 | medium.h = h_start; 100 | else 101 | medium.T = T_start; 102 | end if; 103 | elseif energyDynamics == Dynamics.SteadyStateInitial then 104 | /* 105 | if use_T_start then 106 | der(medium.T) = 0; 107 | else 108 | der(medium.h) = 0; 109 | end if; 110 | */ 111 | if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then 112 | der(medium.h) = 0; 113 | else 114 | der(medium.T) = 0; 115 | end if; 116 | end if; 117 | if massDynamics == Dynamics.FixedInitial then 118 | if initialize_p then 119 | medium.p = p_start; 120 | end if; 121 | elseif massDynamics == Dynamics.SteadyStateInitial then 122 | if initialize_p then 123 | der(medium.p) = 0; 124 | end if; 125 | end if; 126 | if substanceDynamics == Dynamics.FixedInitial then 127 | medium.Xi = X_start[1:Medium.nXi]; 128 | elseif substanceDynamics == Dynamics.SteadyStateInitial then 129 | der(medium.Xi) = zeros(Medium.nXi); 130 | end if; 131 | if traceDynamics == Dynamics.FixedInitial then 132 | mC_scaled = m * C_start[1:Medium.nC] ./ Medium.C_nominal; 133 | mC_S_scaled = (m/medium.d) * C_S_start[1:Medium.nC_S] ./ Medium.C_S_nominal; 134 | mC_X_scaled = (m/medium.d) * C_X_start[1:Medium.nC_X] ./ Medium.C_X_nominal; 135 | elseif traceDynamics == Dynamics.SteadyStateInitial then 136 | der(mC_scaled) = zeros(Medium.nC); 137 | der(mC_S_scaled) = zeros(Medium.nC_S); 138 | der(mC_X_scaled) = zeros(Medium.nC_X); 139 | end if; 140 | annotation(Documentation(info = " 141 |
142 | Interface and base class for an ideally mixed fluid volume with the ability to store mass and energy. 143 | The following boundary flow and source terms are part of the energy balance and must be specified in an extending class: 144 |
145 |Qb_flow
, e.g., convective or latent heat flow rate across segment boundary, andWb_flow
, work term, e.g., p*der(fluidVolume) if the volume is not constant.
150 | The component volume fluidVolume
is an input that needs to be set in the extending class to complete the model.
151 |
153 | Further source terms must be defined by an extending class for fluid flow across the segment boundary: 154 |
155 |Hb_flow
, enthalpy flow,mb_flow
, mass flow,mbXi_flow
, substance mass flow, andmbC_flow
, trace substance mass flow.
16 | This partial model defines an interface for components with two ports.
17 | The treatment of the design flow direction and of flow reversal are predefined based on the parameter allowFlowReversal
.
18 | The component may transport fluid and may have internal storage for a given fluid Medium
.
19 |
21 | An extending model providing direct access to internal storage of mass or energy through port_a or port_b
22 | should redefine the protected parameters port_a_exposesState
and port_b_exposesState
appropriately.
23 | This will be visualized at the port icons, in order to improve the understanding of fluid model diagrams.
24 |
90 | This component transports fluid between its two ports, without storing mass or energy.
91 | Energy may be exchanged with the environment though, e.g., in the form of work.
92 | PartialTwoPortTransport
is intended as base class for devices like orifices, valves and simple fluid machines.
94 | Three equations need to be added by an extending class using this component: 95 |
96 |dp
and the mass flow rate m_flow
,port_b.h_outflow
for flow in design direction, andport_a.h_outflow
for flow in reverse direction.102 | Moreover appropriate values shall be assigned to the following parameters: 103 |
104 |dp_start
for a guess of the pressure dropm_flow_small
for regularization of zero flow.23 | Base class for one dimensional flow models. It specializes a PartialTwoPort with a parameter interface and icon graphics. 24 |
25 | ")); 26 | end PartialStraightPipe; 27 | -------------------------------------------------------------------------------- /LibRAS/Pipes/StaticPipe.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Pipes; 2 | model StaticPipe "Basic pipe flow model without storage of mass or energy" 3 | // extending PartialStraightPipe 4 | extends LibRAS.Pipes.PartialStraightPipe; 5 | import Modelica.Fluid.Types; 6 | // Initialization 7 | parameter Medium.AbsolutePressure p_a_start = system.p_start "Start value of pressure at port a" annotation(Dialog(tab = "Initialization")); 8 | parameter Medium.AbsolutePressure p_b_start = p_a_start "Start value of pressure at port b" annotation(Dialog(tab = "Initialization")); 9 | parameter Medium.MassFlowRate m_flow_start = system.m_flow_start "Start value for mass flow rate" annotation(Evaluate = true, Dialog(tab = "Initialization")); 10 | FlowModel flowModel(redeclare final package Medium = Medium, final n = 2, states = {Medium.setState_phX(port_a.p, inStream(port_a.h_outflow), inStream(port_a.Xi_outflow)), Medium.setState_phX(port_b.p, inStream(port_b.h_outflow), inStream(port_b.Xi_outflow))}, vs = {port_a.m_flow / Medium.density(flowModel.states[1]) / flowModel.crossAreas[1], -port_b.m_flow / Medium.density(flowModel.states[2]) / flowModel.crossAreas[2]} / nParallel, final momentumDynamics = Types.Dynamics.SteadyState, final allowFlowReversal = allowFlowReversal, final p_a_start = p_a_start, final p_b_start = p_b_start, final m_flow_start = m_flow_start, final nParallel = nParallel, final pathLengths = {length}, final crossAreas = {crossArea, crossArea}, final dimensions = {4 * crossArea / perimeter, 4 * crossArea / perimeter}, final roughnesses = {roughness, roughness}, final dheights = {height_ab}, final g = system.g) "Flow model" annotation(Placement(transformation(extent = {{-38, -18}, {38, 18}}))); 11 | equation 12 | // Mass balance 13 | port_a.m_flow = flowModel.m_flows[1]; 14 | 0 = port_a.m_flow + port_b.m_flow; 15 | port_a.Xi_outflow = inStream(port_b.Xi_outflow); 16 | port_b.Xi_outflow = inStream(port_a.Xi_outflow); 17 | port_a.C_outflow = inStream(port_b.C_outflow); 18 | port_b.C_outflow = inStream(port_a.C_outflow); 19 | port_a.C_S_outflow = inStream(port_b.C_S_outflow); 20 | port_b.C_S_outflow = inStream(port_a.C_S_outflow); 21 | port_a.C_X_outflow = inStream(port_b.C_X_outflow); 22 | port_b.C_X_outflow = inStream(port_a.C_X_outflow); 23 | // Energy balance, considering change of potential energy 24 | // Wb_flow = v*A*dpdx + v*F_fric 25 | // = m_flow/d/A * (A*dpdx + A*pressureLoss.dp_fg - F_grav) 26 | // = m_flow/d/A * (-A*g*height_ab*d) 27 | // = -m_flow*g*height_ab 28 | port_b.h_outflow = inStream(port_a.h_outflow) - system.g * height_ab; 29 | port_a.h_outflow = inStream(port_b.h_outflow) + system.g * height_ab; 30 | annotation(defaultComponentName = "pipe", Documentation(info = " 31 |Model of a straight pipe with constant cross section and with steady-state mass, momentum and energy balances, i.e., the model does not store mass or energy. 32 | There exist two thermodynamic states, one at each fluid port. The momentum balance is formulated for the two states, taking into account 33 | momentum flows, friction and gravity. The same result can be obtained by using DynamicPipe with 34 | steady-state dynamic settings. The intended use is to provide simple connections of vessels or other devices with storage, as it is done in: 35 |
36 |42 | With the stream connectors the thermodynamic states on the ports are generally defined by models with storage or by sources placed upstream and downstream of the static pipe. 43 | Other non storage components in the flow path may yield to state transformation. Note that this generally leads to nonlinear equation systems if multiple static pipes, 44 | or other flow models without storage, are directly connected. 45 |
46 | ")); 47 | end StaticPipe; 48 | -------------------------------------------------------------------------------- /LibRAS/Pipes/Tee.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Pipes; 2 | model Tee 3 | "Splitting/joining component with static balances for an infinitesimal control volume" 4 | import Modelica.Fluid.Types; 5 | import Modelica.Fluid.Types.PortFlowDirection; 6 | 7 | replaceable package Medium=Modelica.Media.Interfaces.PartialMedium 8 | "Medium in the component" 9 | annotation (choicesAllMatching=true); 10 | 11 | LibRAS.Interfaces.WasteFluidPort_a port_1(redeclare package Medium = 12 | Medium, m_flow(min=if (portFlowDirection_1 == PortFlowDirection.Entering) then 13 | 0.0 else -Modelica.Constants.inf, max=if (portFlowDirection_1 14 | == PortFlowDirection.Leaving) then 0.0 else Modelica.Constants.inf)) 15 | annotation (Placement(transformation(extent={{-110,-10},{-90,10}}))); 16 | LibRAS.Interfaces.WasteFluidPort_b port_2(redeclare package Medium = 17 | Medium, m_flow(min=if (portFlowDirection_2 == PortFlowDirection.Entering) then 18 | 0.0 else -Modelica.Constants.inf, max=if (portFlowDirection_2 19 | == PortFlowDirection.Leaving) then 0.0 else Modelica.Constants.inf)) 20 | annotation (Placement(transformation(extent={{90,-10},{110,10}}))); 21 | LibRAS.Interfaces.WasteFluidPort_a port_3( 22 | redeclare package Medium=Medium, 23 | m_flow(min=if (portFlowDirection_3==PortFlowDirection.Entering) then 0.0 else -Modelica.Constants.inf, 24 | max=if (portFlowDirection_3==PortFlowDirection.Leaving) then 0.0 else Modelica.Constants.inf)) 25 | annotation (Placement(transformation(extent={{-10,90},{10,110}}))); 26 | 27 | protected 28 | parameter PortFlowDirection portFlowDirection_1=PortFlowDirection.Bidirectional 29 | "Flow direction for port_1" 30 | annotation(Dialog(tab="Advanced")); 31 | parameter PortFlowDirection portFlowDirection_2=PortFlowDirection.Bidirectional 32 | "Flow direction for port_2" 33 | annotation(Dialog(tab="Advanced")); 34 | parameter PortFlowDirection portFlowDirection_3=PortFlowDirection.Bidirectional 35 | "Flow direction for port_3" 36 | annotation(Dialog(tab="Advanced")); 37 | equation 38 | connect(port_1, port_2) annotation (Line( 39 | points={{-100,0},{100,0}}, 40 | color={0,127,255})); 41 | connect(port_1, port_3) annotation (Line( 42 | points={{-100,0},{0,0},{0,100}}, 43 | color={0,127,255})); 44 | annotation(Icon(coordinateSystem( 45 | preserveAspectRatio=true, 46 | extent={{-100,-100},{100,100}}), graphics={ 47 | Rectangle( 48 | extent={{-100,44},{100,-44}}, 49 | lineColor={0,0,0}, 50 | fillPattern=FillPattern.HorizontalCylinder, 51 | fillColor={0,127,255}), 52 | Text( 53 | extent={{-150,-89},{150,-129}}, 54 | lineColor={0,0,255}, 55 | textString="%name"), 56 | Rectangle( 57 | extent={{-44,100},{44,44}}, 58 | lineColor={0,0,0}, 59 | fillPattern=FillPattern.VerticalCylinder, 60 | fillColor={0,127,255}), 61 | Rectangle( 62 | extent={{-22,82},{21,-4}}, 63 | fillPattern=FillPattern.Solid, 64 | fillColor={0,128,255}, 65 | pattern=LinePattern.None, 66 | lineColor={0,0,0})})); 67 | 68 | end Tee; 69 | -------------------------------------------------------------------------------- /LibRAS/Pipes/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | 3 | package Pipes 4 | extends Modelica.Icons.VariantsPackage; 5 | //End pipes 6 | end Pipes; 7 | -------------------------------------------------------------------------------- /LibRAS/Pipes/package.order: -------------------------------------------------------------------------------- 1 | StaticPipe 2 | Tee 3 | IdealParticleFilter 4 | OxygenAdder 5 | PartialStraightPipe 6 | -------------------------------------------------------------------------------- /LibRAS/Sensors/MassFlowRate.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sensors; 2 | model MassFlowRate "Ideal sensor for mass flow rate" 3 | extends Sensors.PartialFlowSensor; 4 | extends Modelica.Icons.RotationalSensor; 5 | Modelica.Blocks.Interfaces.RealOutput m_flow(quantity = "MassFlowRate", final unit = "kg/s") "Mass flow rate from port_a to port_b" annotation(Placement(transformation(origin = {0, 110}, extent = {{10, -10}, {-10, 10}}, rotation = 270))); 6 | equation 7 | m_flow = port_a.m_flow; 8 | annotation(Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-100, -100}, {100, 100}}), graphics = {Line(points = {{70, 0}, {100, 0}}, color = {0, 128, 255}), Text(extent = {{162, 120}, {2, 90}}, lineColor = {0, 0, 0}, textString = "m_flow"), Line(points = {{0, 100}, {0, 70}}, color = {0, 0, 127}), Line(points = {{-100, 0}, {-70, 0}}, color = {0, 128, 255})}), Documentation(info = " 9 |10 | This component monitors the mass flow rate flowing from port_a to port_b. 11 | The sensor is ideal, i.e., it does not influence the fluid. 12 |
13 | ")); 14 | end MassFlowRate; 15 | -------------------------------------------------------------------------------- /LibRAS/Sensors/PartialAbsoluteSensor.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sensors; 2 | partial model PartialAbsoluteSensor "Partial component to model a sensor that measures a potential variable" 3 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the sensor" annotation(choicesAllMatching = true); 4 | LibRAS.Interfaces.WasteFluidPort_a port(redeclare package Medium = Medium, m_flow(min = 0)) annotation(Placement(transformation(origin = {0, -100}, extent = {{-10, -10}, {10, 10}}, rotation = 90))); 5 | equation 6 | port.m_flow = 0; 7 | port.h_outflow = Medium.h_default; 8 | port.Xi_outflow = Medium.X_default[1:Medium.nXi]; 9 | port.C_outflow = zeros(Medium.nC); 10 | port.C_S_outflow = zeros(Medium.nC_S); 11 | port.C_X_outflow = zeros(Medium.nC_X); 12 | annotation(Documentation(info = " 13 |
14 | Partial component to model an absolute sensor. Can be used for pressure sensor models.
15 | Use for other properties such as temperature or density is discouraged, because the enthalpy at the connector can have different meanings, depending on the connection topology. Use PartialFlowSensor
instead.
16 | as signal.
17 |
24 | Partial component to model a sensor that measures any intensive properties
25 | of a flow, e.g., to get temperature or density in the flow
26 | between fluid connectors.
27 | The model includes zero-volume balance equations. Sensor models inheriting from
28 | this partial class should add a medium instance to calculate the measured property.
29 |
68 | Defines prescribed values for boundary conditions: 69 |
70 |If use_p_in
is false (default option), the p
parameter
76 | is used as boundary pressure, and the p_in
input connector is disabled; if use_p_in
is true, then the p
parameter is ignored, and the value provided by the input connector is used instead.
The same thing goes for the temperature, composition and trace substances.
78 |79 | Note, that boundary temperature, 80 | mass fractions and trace substances have only an effect if the mass flow 81 | is from the boundary into the port. If mass is flowing from 82 | the port into the boundary, the boundary definitions, 83 | with exception of boundary pressure, do not have an effect. 84 |
85 | ")); 86 | end Boundary_pT; -------------------------------------------------------------------------------- /LibRAS/Sources/MassFlowSource_T.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sources; 2 | model MassFlowSource_T "Ideal flow source that produces a prescribed mass flow with prescribed temperature, mass fraction and trace substances" 3 | import Modelica.Media.Interfaces.Choices.IndependentVariables; 4 | extends LibRAS.Sources.PartialFlowSource; 5 | parameter Boolean use_m_flow_in = false "Get the mass flow rate from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 6 | parameter Boolean use_T_in = false "Get the temperature from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 7 | parameter Boolean use_X_in = false "Get the composition from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 8 | parameter Boolean use_C_in = false "Get the trace substances from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 9 | parameter Boolean use_C_S_in = false "Get the trace substances from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 10 | parameter Boolean use_C_X_in = false "Get the trace substances from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 11 | parameter Medium.MassFlowRate m_flow (displayUnit="kg/s") = 0 "Fixed mass flow rate going out of the fluid port" annotation(Evaluate = true, Dialog(enable = not use_m_flow_in)); 12 | parameter Medium.Temperature T = Medium.T_default "Fixed value of temperature" annotation(Evaluate = true, Dialog(enable = not use_T_in)); 13 | parameter Medium.MassFraction X[Medium.nX] = Medium.X_default "Fixed value of composition" annotation(Evaluate = true, Dialog(enable = not use_X_in and Medium.nXi > 0)); 14 | parameter Medium.ExtraProperty C[Medium.nC](quantity = Medium.extraPropertiesNames) = fill(0, Medium.nC) "Fixed values of trace substances" annotation(Evaluate = true, Dialog(enable = not use_C_in and Medium.nC > 0)); 15 | parameter Medium.ExtraProperty C_S[Medium.nC_S](quantity = Medium.solublesNames, each unit="kg/m3", each displayUnit="g/m3") = fill(Modelica.Constants.eps, Medium.nC_S) "Fixed values of dissolved substances" annotation(Evaluate = true, Dialog(enable = not use_C_S_in and Medium.nC_S > 0)); 16 | parameter Medium.ExtraProperty C_X[Medium.nC_X](quantity = Medium.particulatesNames, each unit="kg/m3", each displayUnit="g/m3") = fill(Modelica.Constants.eps, Medium.nC_X) "Fixed values of particulate substances" annotation(Evaluate = true, Dialog(enable = not use_C_X_in and Medium.nC_X > 0)); 17 | Modelica.Blocks.Interfaces.RealInput m_flow_in if use_m_flow_in "Prescribed mass flow rate" annotation(Placement(transformation(extent = {{-120, 60}, {-80, 100}}), iconTransformation(extent = {{-120, 60}, {-80, 100}}))); 18 | Modelica.Blocks.Interfaces.RealInput T_in if use_T_in "Prescribed fluid temperature" annotation(Placement(transformation(extent = {{-140, 20}, {-100, 60}}), iconTransformation(extent = {{-140, 20}, {-100, 60}}))); 19 | Modelica.Blocks.Interfaces.RealInput X_in[Medium.nX] if use_X_in "Prescribed fluid composition" annotation(Placement(transformation(extent = {{-140, -60}, {-100, -20}}))); 20 | Modelica.Blocks.Interfaces.RealInput C_in[Medium.nC] if use_C_in "Prescribed boundary trace substances" annotation(Placement(transformation(extent = {{-120, -100}, {-80, -60}}))); 21 | Modelica.Blocks.Interfaces.RealInput C_S_in[Medium.nC_S] if use_C_S_in "Prescribed boundary trace substances" annotation(Placement(visible = true,transformation(origin = {-60, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {20, -100},extent = {{-20, -20}, {20, 20}}, rotation = 90))); 22 | Modelica.Blocks.Interfaces.RealInput C_X_in[Medium.nC_X] if use_C_X_in "Prescribed boundary trace substances" annotation(Placement(visible = true,transformation(origin = {60, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {-40, -100},extent = {{-20, -20}, {20, 20}}, rotation = 90))); 23 | protected 24 | Modelica.Blocks.Interfaces.RealInput m_flow_in_internal "Needed to connect to conditional connector"; 25 | Modelica.Blocks.Interfaces.RealInput T_in_internal "Needed to connect to conditional connector"; 26 | Modelica.Blocks.Interfaces.RealInput X_in_internal[Medium.nX] "Needed to connect to conditional connector"; 27 | Modelica.Blocks.Interfaces.RealInput C_in_internal[Medium.nC] "Needed to connect to conditional connector"; 28 | Modelica.Blocks.Interfaces.RealInput C_S_in_internal[Medium.nC_S] "Needed to connect to conditional connector"; 29 | Modelica.Blocks.Interfaces.RealInput C_X_in_internal[Medium.nC_X] "Needed to connect to conditional connector"; 30 | equation 31 | Modelica.Fluid.Utilities.checkBoundary(Medium.mediumName, Medium.substanceNames, Medium.singleState, true, X_in_internal, "MassFlowSource_T"); 32 | connect(m_flow_in, m_flow_in_internal); 33 | connect(T_in, T_in_internal); 34 | connect(X_in, X_in_internal); 35 | connect(C_in, C_in_internal); 36 | connect(C_S_in, C_S_in_internal); 37 | connect(C_X_in, C_X_in_internal); 38 | if not use_m_flow_in then 39 | m_flow_in_internal = m_flow; 40 | end if; 41 | if not use_T_in then 42 | T_in_internal = T; 43 | end if; 44 | if not use_X_in then 45 | X_in_internal = X; 46 | end if; 47 | if not use_C_in then 48 | C_in_internal = C; 49 | end if; 50 | if not use_C_S_in then 51 | C_S_in_internal = C_S; 52 | end if; 53 | if not use_C_X_in then 54 | C_X_in_internal = C_X; 55 | end if; 56 | if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then 57 | medium.h = Medium.specificEnthalpy(Medium.setState_pTX(medium.p, T_in_internal, X_in_internal)); 58 | else 59 | medium.T = T_in_internal; 60 | end if; 61 | sum(ports.m_flow) = -m_flow_in_internal; 62 | medium.Xi = X_in_internal[1:Medium.nXi]; 63 | ports.C_outflow = fill(C_in_internal, nPorts); 64 | ports.C_S_outflow = fill(C_S_in_internal, nPorts); 65 | ports.C_X_outflow = fill(C_X_in_internal, nPorts); 66 | annotation(defaultComponentName = "boundary", Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{35, 45}, {100, -45}}, lineColor = {0, 0, 0}, fillPattern = FillPattern.HorizontalCylinder, fillColor = {0, 127, 255}), Ellipse(extent = {{-100, 80}, {60, -80}}, lineColor = {0, 0, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Polygon(points = {{-60, 70}, {60, 0}, {-60, -68}, {-60, 70}}, lineColor = {0, 0, 255}, fillColor = {0, 0, 255}, fillPattern = FillPattern.Solid), Text(extent = {{-54, 32}, {16, -30}}, lineColor = {255, 0, 0}, textString = "m"), Text(extent = {{-150, 130}, {150, 170}}, textString = "%name", lineColor = {0, 0, 255}), Ellipse(extent = {{-26, 30}, {-18, 22}}, lineColor = {255, 0, 0}, fillColor = {255, 0, 0}, fillPattern = FillPattern.Solid), Text(visible = use_m_flow_in, extent = {{-185, 132}, {-45, 100}}, lineColor = {0, 0, 0}, textString = "m_flow"), Text(visible = use_T_in, extent = {{-111, 71}, {-71, 37}}, lineColor = {0, 0, 0}, textString = "T"), Text(visible = use_X_in, extent = {{-153, -44}, {-33, -72}}, lineColor = {0, 0, 0}, textString = "X"), Text(visible = use_C_in, extent = {{-155, -98}, {-35, -126}}, lineColor = {0, 0, 0}, textString = "C")}), Documentation(info = " 67 |68 | Models an ideal flow source, with prescribed values of flow rate, temperature, composition and trace substances: 69 |
70 |If use_m_flow_in
is false (default option), the m_flow
parameter
76 | is used as boundary pressure, and the m_flow_in
input connector is disabled; if use_m_flow_in
is true, then the m_flow
parameter is ignored, and the value provided by the input connector is used instead.
The same thing goes for the temperature and composition
78 |79 | Note, that boundary temperature, 80 | mass fractions and trace substances have only an effect if the mass flow 81 | is from the boundary into the port. If mass is flowing from 82 | the port into the boundary, the boundary definitions, 83 | with exception of boundary flow rate, do not have an effect. 84 |
85 | ")); 86 | end MassFlowSource_T; 87 | -------------------------------------------------------------------------------- /LibRAS/Sources/PartialFlowSource.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sources; 2 | partial model PartialFlowSource "Partial component source with one fluid connector" 3 | import Modelica.Constants; 4 | import Modelica.Fluid.Types; 5 | parameter Integer nPorts = 0 "Number of ports" annotation(Dialog(connectorSizing = true)); 6 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model within the source" annotation(choicesAllMatching = true); 7 | Medium.BaseProperties medium "Medium in the source"; 8 | Interfaces.WasteFluidPort_b ports[nPorts](redeclare each package Medium = Medium, m_flow(each max = if flowDirection == Types.PortFlowDirection.Leaving then 0 else +Constants.inf, each min = if flowDirection == Types.PortFlowDirection.Entering then 0 else -Constants.inf)) annotation(Placement(transformation(extent = {{90, 10}, {110, -10}}))); 9 | protected 10 | parameter Types.PortFlowDirection flowDirection = Types.PortFlowDirection.Bidirectional "Allowed flow direction" annotation(Evaluate = true, Dialog(tab = "Advanced")); 11 | equation 12 | assert(abs(sum(abs(ports.m_flow)) - max(abs(ports.m_flow))) <= Modelica.Constants.small, "FlowSource only supports one connection with flow"); 13 | // Only one connection allowed to a port to avoid unwanted ideal mixing 14 | for i in 1:nPorts loop 15 | assert(cardinality(ports[i]) <= 1, " 16 | each ports[i] of boundary shall at most be connected to one component. 17 | If two or more connections are present, ideal mixing takes 18 | place with these connections, which is usually not the intention 19 | of the modeller. Increase nPorts to add an additional port. 20 | "); 21 | ports[i].p = medium.p; 22 | ports[i].h_outflow = medium.h; 23 | ports[i].Xi_outflow = medium.Xi; 24 | end for; 25 | annotation(defaultComponentName = "boundary", Documentation(info = " 26 |27 | Partial component to model the volume interface of a source 28 | component, such as a mass flow source. The essential 29 | features are: 30 |
31 |26 | Partial component to model the volume interface of a source 27 | component, such as a mass flow source. The essential 28 | features are: 29 |
30 |This very simple model provides a pressure drop which is proportional to the flowrate and to the opening
input, without computing any fluid property. It can be used for testing purposes, when
54 | a simple model of a variable pressure loss is needed.
A medium model must be nevertheless be specified, so that the fluid ports can be connected to other components using the same medium model.
56 |The model is adiabatic (no heat losses to the ambient) and neglects changes in kinetic energy from the inlet to the outlet.
57 | ", 58 | revisions=" 59 |