├── 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 | 149 |

150 | The component volume fluidVolume is an input that needs to be set in the extending class to complete the model. 151 |

152 |

153 | Further source terms must be defined by an extending class for fluid flow across the segment boundary: 154 |

155 | 161 | ")); 162 | end PartialLumpedVolume; 163 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/PartialTwoPort.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | partial model PartialTwoPort "Partial component with two ports" 3 | import Modelica.Constants; 4 | outer LibRAS.System system "System wide properties"; 5 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component" annotation(choicesAllMatching = true); 6 | parameter Boolean allowFlowReversal = system.allowFlowReversal "= true to allow flow reversal, false restricts to design direction (port_a -> port_b)" annotation(Dialog(tab = "Assumptions"), Evaluate = true); 7 | WasteFluidPort_a port_a(redeclare package Medium = Medium, m_flow(min = if allowFlowReversal then -Constants.inf else 0)) "Fluid connector a (positive design flow direction is from port_a to port_b)" annotation(Placement(transformation(extent = {{-110, -10}, {-90, 10}}))); 8 | WasteFluidPort_b port_b(redeclare package Medium = Medium, m_flow(max = if allowFlowReversal then +Constants.inf else 0)) "Fluid connector b (positive design flow direction is from port_a to port_b)" annotation(Placement(transformation(extent = {{110, -10}, {90, 10}}), iconTransformation(extent = {{110, -10}, {90, 10}}))); 9 | // Model structure, e.g., used for visualization 10 | protected 11 | parameter Boolean port_a_exposesState = false "= true if port_a exposes the state of a fluid volume"; 12 | parameter Boolean port_b_exposesState = false "= true if port_b.p exposes the state of a fluid volume"; 13 | parameter Boolean showDesignFlowDirection = true "= false to hide the arrow in the model icon"; 14 | annotation(Documentation(info = " 15 |

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 |

20 |

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 |

25 | "), Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Polygon(points = {{20, -70}, {60, -85}, {20, -100}, {20, -70}}, lineColor = {0, 128, 255}, fillColor = {0, 128, 255}, fillPattern = FillPattern.Solid, visible = showDesignFlowDirection), Polygon(points = {{20, -75}, {50, -85}, {20, -95}, {20, -75}}, lineColor = {255, 255, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid, visible = allowFlowReversal), Line(points = {{55, -85}, {-60, -85}}, color = {0, 128, 255}, visible = showDesignFlowDirection), Text(extent = {{-149, -114}, {151, -154}}, lineColor = {0, 0, 255}, textString = "%name"), Ellipse(extent = {{-110, 26}, {-90, -24}}, lineColor = {0, 0, 0}, fillColor = {0, 0, 0}, fillPattern = FillPattern.Solid, visible = port_a_exposesState), Ellipse(extent = {{90, 25}, {110, -25}}, lineColor = {0, 0, 0}, fillColor = {0, 0, 0}, fillPattern = FillPattern.Solid, visible = port_b_exposesState)})); 26 | end PartialTwoPort; 27 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/PartialTwoPortTransport.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | 3 | partial model PartialTwoPortTransport 4 | "Partial element transporting fluid between two ports without storage of mass or energy" 5 | 6 | extends PartialTwoPort( 7 | final port_a_exposesState=false, 8 | final port_b_exposesState=false); 9 | 10 | // Advanced 11 | // Note: value of dp_start shall be refined by derived model, basing on local dp_nominal 12 | parameter Medium.AbsolutePressure dp_start(min=-Modelica.Constants.inf) = 0.01*system.p_start 13 | "Guess value of dp = port_a.p - port_b.p" 14 | annotation(Dialog(tab = "Advanced")); 15 | parameter Medium.MassFlowRate m_flow_start = system.m_flow_start 16 | "Guess value of m_flow = port_a.m_flow" 17 | annotation(Dialog(tab = "Advanced")); 18 | // Note: value of m_flow_small shall be refined by derived model, basing on local m_flow_nominal 19 | parameter Medium.MassFlowRate m_flow_small = if system.use_eps_Re then system.eps_m_flow*system.m_flow_nominal else system.m_flow_small 20 | "Small mass flow rate for regularization of zero flow" 21 | annotation(Dialog(tab = "Advanced")); 22 | 23 | // Diagnostics 24 | parameter Boolean show_T = true 25 | "= true, if temperatures at port_a and port_b are computed" 26 | annotation(Dialog(tab="Advanced",group="Diagnostics")); 27 | parameter Boolean show_V_flow = true 28 | "= true, if volume flow rate at inflowing port is computed" 29 | annotation(Dialog(tab="Advanced",group="Diagnostics")); 30 | 31 | // Variables 32 | Medium.MassFlowRate m_flow( 33 | min=if allowFlowReversal then -Modelica.Constants.inf else 0, 34 | start = m_flow_start) "Mass flow rate in design flow direction"; 35 | Modelica.SIunits.Pressure dp(start=dp_start) 36 | "Pressure difference between port_a and port_b (= port_a.p - port_b.p)"; 37 | 38 | Modelica.SIunits.VolumeFlowRate V_flow= 39 | m_flow/Modelica.Fluid.Utilities.regStep(m_flow, 40 | Medium.density(state_a), 41 | Medium.density(state_b), 42 | m_flow_small) if show_V_flow 43 | "Volume flow rate at inflowing port (positive when flow from port_a to port_b)"; 44 | 45 | Medium.Temperature port_a_T= 46 | Modelica.Fluid.Utilities.regStep(port_a.m_flow, 47 | Medium.temperature(state_a), 48 | Medium.temperature(Medium.setState_phX(port_a.p, port_a.h_outflow, port_a.Xi_outflow)), 49 | m_flow_small) if show_T 50 | "Temperature close to port_a, if show_T = true"; 51 | Medium.Temperature port_b_T= 52 | Modelica.Fluid.Utilities.regStep(port_b.m_flow, 53 | Medium.temperature(state_b), 54 | Medium.temperature(Medium.setState_phX(port_b.p, port_b.h_outflow, port_b.Xi_outflow)), 55 | m_flow_small) if show_T 56 | "Temperature close to port_b, if show_T = true"; 57 | protected 58 | Medium.ThermodynamicState state_a "state for medium inflowing through port_a"; 59 | Medium.ThermodynamicState state_b "state for medium inflowing through port_b"; 60 | equation 61 | // medium states 62 | state_a = Medium.setState_phX(port_a.p, inStream(port_a.h_outflow), inStream(port_a.Xi_outflow)); 63 | state_b = Medium.setState_phX(port_b.p, inStream(port_b.h_outflow), inStream(port_b.Xi_outflow)); 64 | 65 | // Pressure drop in design flow direction 66 | dp = port_a.p - port_b.p; 67 | 68 | // Design direction of mass flow rate 69 | m_flow = port_a.m_flow; 70 | assert(m_flow > -m_flow_small or allowFlowReversal, "Reverting flow occurs even though allowFlowReversal is false"); 71 | 72 | // Mass balance (no storage) 73 | port_a.m_flow + port_b.m_flow = 0; 74 | 75 | // Transport of substances 76 | port_a.Xi_outflow = inStream(port_b.Xi_outflow); 77 | port_b.Xi_outflow = inStream(port_a.Xi_outflow); 78 | 79 | port_a.C_outflow = inStream(port_b.C_outflow); 80 | port_b.C_outflow = inStream(port_a.C_outflow); 81 | 82 | port_a.C_S_outflow = inStream(port_b.C_S_outflow); 83 | port_b.C_S_outflow = inStream(port_a.C_S_outflow); 84 | port_a.C_X_outflow = inStream(port_b.C_X_outflow); 85 | port_b.C_X_outflow = inStream(port_a.C_X_outflow); 86 | 87 | annotation ( 88 | Documentation(info=" 89 |

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.

93 |

94 | Three equations need to be added by an extending class using this component: 95 |

96 | 101 |

102 | Moreover appropriate values shall be assigned to the following parameters: 103 |

104 | 108 | ")); 109 | end PartialTwoPortTransport; -------------------------------------------------------------------------------- /LibRAS/Interfaces/VesselWasteFluidPorts_a.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector VesselWasteFluidPorts_a "Fluid connector with filled, large icon to be used for horizontally aligned vectors of FluidPorts (vector dimensions must be added after dragging)" 3 | extends WasteFluidPort; 4 | annotation(defaultComponentName = "ports_b", Diagram(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Text(extent = {{-75, 130}, {75, 100}}, textString = "%name"), Rectangle(extent = {{-25, 100}, {25, -100}}, lineColor = {0, 0, 0}), Ellipse(extent = {{-22, 100}, {-10, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-6, 100}, {6, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{10, 100}, {22, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid)}), Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Rectangle(extent = {{-50, 200}, {50, -200}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-44, 200}, {-20, -200}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-12, 200}, {12, -200}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{20, 200}, {44, -200}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid)})); 5 | end VesselWasteFluidPorts_a; 6 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/VesselWasteFluidPorts_b.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector VesselWasteFluidPorts_b "Fluid connector with outlined, large icon to be used for horizontally aligned vectors of FluidPorts (vector dimensions must be added after dragging)" 3 | extends WasteFluidPort; 4 | annotation(defaultComponentName = "ports_b", Diagram(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Text(extent = {{-75, 130}, {75, 100}}, textString = "%name"), Rectangle(extent = {{-25, 100}, {25, -100}}, lineColor = {0, 0, 0}), Ellipse(extent = {{-22, 100}, {-10, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-20, -69}, {-12, 69}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-6, 100}, {6, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{10, 100}, {22, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-4, -69}, {4, 69}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{12, -69}, {20, 69}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid)}), Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Rectangle(extent = {{-50, 200}, {50, -200}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-44, 200}, {-20, -200}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-12, 200}, {12, -200}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{20, 200}, {44, -200}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-39, -118.5}, {-25, 113}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-7, -118.5}, {7, 113}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{25, -117.5}, {39, 114}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid)})); 5 | end VesselWasteFluidPorts_b; 6 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/WasteFluidPort.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector WasteFluidPort 3 | extends Modelica.Fluid.Interfaces.FluidPort; 4 | stream Medium.ExtraProperty C_S_outflow[Medium.nC_S] 5 | "Properties c_i/m close to the connection point if m_flow < 0"; 6 | stream Medium.ExtraProperty C_X_outflow[Medium.nC_X] 7 | "Properties c_i/m close to the connection point if m_flow < 0"; 8 | end WasteFluidPort; 9 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/WasteFluidPort_a.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector WasteFluidPort_a "Generic fluid connector at design inlet" 3 | extends WasteFluidPort; 4 | annotation (defaultComponentName="port_a", 5 | Diagram(coordinateSystem(preserveAspectRatio=false, extent={{-100, 6 | -100},{100,100}}), graphics={Ellipse( 7 | extent={{-40,40},{40,-40}}, 8 | lineColor={0,0,0}, 9 | fillColor={0,127,255}, 10 | fillPattern=FillPattern.Solid), Text(extent={{-150,110},{150,50}}, 11 | textString="%name")}), 12 | Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{ 13 | 100,100}}), graphics={Ellipse( 14 | extent={{-100,100},{100,-100}}, 15 | lineColor={0,127,255}, 16 | fillColor={0,127,255}, 17 | fillPattern=FillPattern.Solid), Ellipse( 18 | extent={{-100,100},{100,-100}}, 19 | lineColor={0,0,0}, 20 | fillColor={0,127,255}, 21 | fillPattern=FillPattern.Solid)})); 22 | end WasteFluidPort_a; 23 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/WasteFluidPort_b.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector WasteFluidPort_b "Generic fluid connector at design outlet" 3 | extends WasteFluidPort; 4 | annotation(defaultComponentName = "port_b", Diagram(coordinateSystem(preserveAspectRatio = false, extent = {{-100, -100}, {100, 100}}), graphics = {Ellipse(extent = {{-40, 40}, {40, -40}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-30, 30}, {30, -30}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Text(extent = {{-150, 110}, {150, 50}}, textString = "%name")}), Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-100, -100}, {100, 100}}), graphics = {Ellipse(extent = {{-100, 100}, {100, -100}}, lineColor = {0, 127, 255}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-100, 100}, {100, -100}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-80, 80}, {80, -80}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid)})); 5 | end WasteFluidPort_b; 6 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/WasteFluidPorts_a.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector WasteFluidPorts_a "Fluid connector with filled, large icon to be used for vectors of FluidPorts (vector dimensions must be added after dragging)" 3 | extends WasteFluidPort; 4 | annotation(defaultComponentName = "ports_a", Diagram(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Text(extent = {{-75, 130}, {75, 100}}, textString = "%name"), Rectangle(extent = {{25, -100}, {-25, 100}}, lineColor = {0, 127, 255}), Ellipse(extent = {{-25, 90}, {25, 40}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-25, 25}, {25, -25}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-25, -40}, {25, -90}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid)}), Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Rectangle(extent = {{50, -200}, {-50, 200}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-50, 180}, {50, 80}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-50, 50}, {50, -50}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-50, -80}, {50, -180}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid)})); 5 | end WasteFluidPorts_a; 6 | 7 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/WasteFluidPorts_b.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Interfaces; 2 | connector WasteFluidPorts_b "Fluid connector with outlined, large icon to be used for vectors of FluidPorts (vector dimensions must be added after dragging)" 3 | extends WasteFluidPort; 4 | annotation(defaultComponentName = "ports_b", Diagram(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Text(extent = {{-75, 130}, {75, 100}}, textString = "%name"), Rectangle(extent = {{-25, 100}, {25, -100}}, lineColor = {0, 0, 0}), Ellipse(extent = {{-25, 90}, {25, 40}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-25, 25}, {25, -25}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-25, -40}, {25, -90}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-15, -50}, {15, -80}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-15, 15}, {15, -15}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-15, 50}, {15, 80}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid)}), Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-50, -200}, {50, 200}}, initialScale = 0.2), graphics = {Rectangle(extent = {{-50, 200}, {50, -200}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-50, 180}, {50, 80}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-50, 50}, {50, -50}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-50, -80}, {50, -180}}, lineColor = {0, 0, 0}, fillColor = {0, 127, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-30, 30}, {30, -30}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-30, 100}, {30, 160}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid), Ellipse(extent = {{-30, -100}, {30, -160}}, lineColor = {0, 127, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid)})); 5 | end WasteFluidPorts_b; 6 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Interfaces 3 | extends Modelica.Icons.InterfacesPackage; 4 | end Interfaces; 5 | -------------------------------------------------------------------------------- /LibRAS/Interfaces/package.order: -------------------------------------------------------------------------------- 1 | WasteFluidPorts_b 2 | WasteFluidPorts_a 3 | WasteFluidPort_b 4 | WasteFluidPort_a 5 | WasteFluidPort 6 | VesselWasteFluidPorts_b 7 | VesselWasteFluidPorts_a 8 | PartialTwoPort 9 | PartialLumpedVolume 10 | PartialTwoPortTransport 11 | -------------------------------------------------------------------------------- /LibRAS/Machines/AdditionController_S.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Machines; 2 | 3 | model AdditionController_S 4 | extends Modelica.Blocks.Icons.Block; 5 | outer LibRAS.System system "System wide properties"; 6 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component" annotation( 7 | choicesAllMatching = true); 8 | parameter Integer index annotation( 9 | Evaluate = true, 10 | Dialog(tab = "General", group = "Basic")); 11 | // Oxygen control 12 | parameter Real K "Proportional gain of PI controller" annotation( 13 | Evaluate = true, 14 | Dialog(tab = "General", group = "Controller")); 15 | parameter Real Ti = 21000 "Integral time for PI controller" annotation( 16 | Evaluate = true, 17 | Dialog(tab = "General", group = "Controller")); 18 | parameter Real max_u = Modelica.Constants.inf "Output upper bound" annotation( 19 | Evaluate = true, 20 | Dialog(tab = "General", group = "Controller")); 21 | parameter Real min_u = Modelica.Constants.eps "Output lower bound" annotation( 22 | Evaluate = true, 23 | Dialog(tab = "General", group = "Controller")); 24 | LibRAS.Sensors.SoluteSensor sensor(substanceIndex = index, redeclare package Medium = Medium) annotation( 25 | Placement(visible = true, transformation(origin = {6, 2}, extent = {{6, 6}, {-6, -6}}, rotation = 90))); 26 | Modelica.Blocks.Interfaces.RealInput setpoint annotation( 27 | Placement(visible = true, transformation(origin = {36, -18}, extent = {{-8, -8}, {8, 8}}, rotation = 180), iconTransformation(origin = {0, 120}, extent = {{-20, -20}, {20, 20}}, rotation = -90))); 28 | Modelica.Blocks.Continuous.LimPID PID(Ti(displayUnit = "s") = Ti, controllerType = Modelica.Blocks.Types.SimpleController.PI, k = K, yMax = max_u, yMin = min_u) annotation( 29 | Placement(visible = true, transformation(origin = {6, -18}, extent = {{6, 6}, {-6, -6}}, rotation = 0))); 30 | LibRAS.Interfaces.WasteFluidPort_a port_a(redeclare package Medium = Medium) "Fluid port for sensing solute concentration" annotation( 31 | Placement(visible = true, transformation(origin = {-22, 2}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {0, -100}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 32 | Modelica.Blocks.Interfaces.RealInput u[Medium.nC_S] "Connector of Real input signals vector" annotation( 33 | Placement(visible = true, transformation(origin = {-19, -31}, extent = {{-5, -5}, {5, 5}}, rotation = 180), iconTransformation(extent = {{-140, -20}, {-100, 20}}, rotation = 0))); 34 | Modelica.Blocks.Interfaces.RealOutput y[Medium.nC_S] "Connector of Real output signals" annotation( 35 | Placement(visible = true, transformation(origin = {-70, -20}, extent = {{-10, -10}, {10, 10}}, rotation = 180), iconTransformation(origin = {120, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0))); 36 | equation 37 | connect(port_a, sensor.port) annotation( 38 | Line(points = {{-22, 2}, {0, 2}, {0, 2}, {0, 2}})); 39 | connect(setpoint, PID.u_s) annotation( 40 | Line(points = {{36, -18}, {14, -18}}, color = {0, 0, 127})); 41 | connect(sensor.C, PID.u_m) annotation( 42 | Line(points = {{6, -4}, {6, -4}, {6, -10}, {6, -10}}, color = {0, 0, 127})); 43 | for i in 1:Medium.nC_S loop 44 | y[i] = u[i] + (if i == index then PID.y else 0); 45 | end for; 46 | annotation( 47 | Icon(coordinateSystem(initialScale = 0.1), graphics = {Line(points = {{-80, 78}, {-80, -90}}, color = {192, 192, 192}), Polygon(lineColor = {192, 192, 192}, fillColor = {192, 192, 192}, fillPattern = FillPattern.Solid, points = {{-80, 90}, {-88, 68}, {-72, 68}, {-80, 90}}), Line(points = {{-90, -80}, {82, -80}}, color = {192, 192, 192}), Polygon(lineColor = {192, 192, 192}, fillColor = {192, 192, 192}, fillPattern = FillPattern.Solid, points = {{90, -80}, {68, -72}, {68, -88}, {90, -80}}), Line(origin = {-1.939, -1.816}, 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}}, color = {0, 0, 127}, smooth = Smooth.Bezier)})); 48 | end AdditionController_S; -------------------------------------------------------------------------------- /LibRAS/Machines/ControlledPump.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Machines; 2 | 3 | model ControlledPump 4 | "Centrifugal pump with ideally controlled mass flow rate" 5 | import SI = Modelica.SIunits; 6 | import Modelica.SIunits.Conversions.NonSIunits.AngularVelocity_rpm; 7 | extends LibRAS.Machines.PartialPump( 8 | N_nominal=1500, 9 | N(start=N_nominal), 10 | redeclare replaceable function flowCharacteristic = 11 | Modelica.Fluid.Machines.BaseClasses.PumpCharacteristics.quadraticFlow 12 | ( V_flow_nominal={0, V_flow_op, 1.5*V_flow_op}, 13 | head_nominal={2*head_op, head_op, 0})); 14 | 15 | // nominal values 16 | parameter Medium.AbsolutePressure p_a_nominal 17 | "Nominal inlet pressure for predefined pump characteristics"; 18 | parameter Medium.AbsolutePressure p_b_nominal 19 | "Nominal outlet pressure, fixed if not control_m_flow and not use_p_set"; 20 | parameter Medium.MassFlowRate m_flow_nominal 21 | "Nominal mass flow rate, fixed if control_m_flow and not use_m_flow_set"; 22 | 23 | // what to control 24 | parameter Boolean control_m_flow = true 25 | "= false to control outlet pressure port_b.p instead of m_flow" 26 | annotation(Evaluate = true); 27 | parameter Boolean use_m_flow_set = false 28 | "= true to use input signal m_flow_set instead of m_flow_nominal" 29 | annotation (Dialog(enable = control_m_flow)); 30 | parameter Boolean use_p_set = false 31 | "= true to use input signal p_set instead of p_b_nominal" 32 | annotation (Dialog(enable = not control_m_flow)); 33 | 34 | // exemplary characteristics 35 | final parameter SI.VolumeFlowRate V_flow_op = m_flow_nominal/rho_nominal 36 | "operational volume flow rate according to nominal values"; 37 | final parameter SI.Height head_op = (p_b_nominal-p_a_nominal)/(rho_nominal*g) 38 | "operational pump head according to nominal values"; 39 | 40 | Modelica.Blocks.Interfaces.RealInput m_flow_set if use_m_flow_set 41 | "Prescribed mass flow rate" 42 | annotation (Placement(transformation( 43 | extent={{-20,-20},{20,20}}, 44 | rotation=-90, 45 | origin={-50,82}))); 46 | Modelica.Blocks.Interfaces.RealInput p_set if use_p_set 47 | "Prescribed outlet pressure" 48 | annotation (Placement(transformation( 49 | extent={{-20,-20},{20,20}}, 50 | rotation=-90, 51 | origin={50,82}))); 52 | 53 | protected 54 | Modelica.Blocks.Interfaces.RealInput m_flow_set_internal 55 | "Needed to connect to conditional connector"; 56 | Modelica.Blocks.Interfaces.RealInput p_set_internal 57 | "Needed to connect to conditional connector"; 58 | equation 59 | // Ideal control 60 | if control_m_flow then 61 | m_flow = m_flow_set_internal; 62 | else 63 | dp_pump = p_set_internal - port_a.p; 64 | end if; 65 | 66 | // Internal connector value when use_m_flow_set = false 67 | if not use_m_flow_set then 68 | m_flow_set_internal = m_flow_nominal; 69 | end if; 70 | if not use_p_set then 71 | p_set_internal = p_b_nominal; 72 | end if; 73 | connect(m_flow_set, m_flow_set_internal); 74 | connect(p_set, p_set_internal); 75 | 76 | annotation (defaultComponentName="pump", 77 | Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100,-100},{100, 78 | 100}}), graphics={Text( 79 | visible=use_p_set, 80 | extent={{82,108},{176,92}}, 81 | textString="p_set"), Text( 82 | visible=use_m_flow_set, 83 | extent={{-20,108},{170,92}}, 84 | textString="m_flow_set")})); 85 | end ControlledPump; 86 | -------------------------------------------------------------------------------- /LibRAS/Machines/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Machines 3 | extends Modelica.Icons.Package; 4 | annotation(Icon( 5 | coordinateSystem(preserveAspectRatio=true, 6 | extent={{-100.0,-100.0},{100.0,100.0}}, 7 | initialScale=0.1), 8 | graphics={ 9 | Line(visible=true, 10 | origin={-2.0,46.0}, 11 | points={{-83.0,-66.0},{-63.0,-66.0}}), 12 | Line(visible=true, 13 | origin={29.0,48.0}, 14 | points={{36.0,-68.0},{56.0,-68.0}}), 15 | Line(visible=true, 16 | origin={-2.0,49.0}, 17 | points={{-83.0,-29.0},{-63.0,-29.0}}), 18 | Line(visible=true, 19 | origin={29.0,52.0}, 20 | points={{36.0,-32.0},{56.0,-32.0}}), 21 | Line(visible=true, 22 | origin={-2.0,49.0}, 23 | points={{-73.0,-9.0},{-73.0,-29.0}}), 24 | Line(visible=true, 25 | origin={29.0,52.0}, 26 | points={{46.0,-12.0},{46.0,-32.0}}), 27 | Line(visible=true, 28 | origin={-0.0,-47.5}, 29 | points={{-75.0,27.5},{-75.0,-27.5},{75.0,-27.5},{75.0,27.5}}), 30 | Rectangle(visible=true, 31 | origin={13.5135,76.9841}, 32 | lineColor={64,64,64}, 33 | fillColor={255,255,255}, 34 | fillPattern=FillPattern.HorizontalCylinder, 35 | extent={{-63.5135,-126.9841},{36.4865,-26.9841}}, 36 | radius=10.0), 37 | Rectangle(visible=true, 38 | origin={13.5135,76.9841}, 39 | lineColor={64,64,64}, 40 | fillPattern=FillPattern.None, 41 | extent={{-63.5135,-126.9841},{36.4865,-26.9841}}, 42 | radius=10.0), 43 | Rectangle(visible=true, 44 | origin={-3.0,73.0769}, 45 | lineColor={64,64,64}, 46 | fillColor={192,192,192}, 47 | fillPattern=FillPattern.HorizontalCylinder, 48 | extent={{-87.0,-83.0769},{-47.0,-63.0769}}), 49 | Rectangle(visible=true, 50 | origin={22.3077,70.0}, 51 | lineColor={64,64,64}, 52 | fillColor={192,192,192}, 53 | fillPattern=FillPattern.HorizontalCylinder, 54 | extent={{27.6923,-80.0},{67.6923,-60.0}})})); 55 | end Machines; 56 | -------------------------------------------------------------------------------- /LibRAS/Machines/package.order: -------------------------------------------------------------------------------- 1 | PartialPump 2 | ControlledPump 3 | AdditionController_S 4 | -------------------------------------------------------------------------------- /LibRAS/Media/WasteWater.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Media; 2 | package WasteWater 3 | extends Modelica.Media.Water.ConstantPropertyLiquidWater( 4 | mediumName="WasteWater" 5 | ); 6 | 7 | constant String solublesNames[:]={String(i) for i in Types.Species.S} 8 | "Names of the soluble species. Auto-generates from the list in Types"; 9 | final constant Integer nC_S=size(solublesNames, 1) annotation (Evaluate=true); 10 | constant Real C_S_nominal[nC_S](min=fill(-1e-6, nC_S)) = 1.0e-3* 11 | ones(nC_S) "Default for the nominal values for the solubles"; 12 | 13 | constant String particulatesNames[:]={String(i) for i in Types.Species.X} 14 | "Names of the particulate species. Auto-generates from the list in Types"; 15 | final constant Integer nC_X=size(particulatesNames, 1) annotation (Evaluate=true); 16 | constant Real C_X_nominal[nC_X](min=fill(-1e-6, nC_X)) = 1.0e-3* 17 | ones(nC_X) "Default for the nominal values for the particulates"; 18 | end WasteWater; 19 | -------------------------------------------------------------------------------- /LibRAS/Media/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Media 3 | extends Modelica.Media; 4 | end Media; 5 | -------------------------------------------------------------------------------- /LibRAS/Media/package.order: -------------------------------------------------------------------------------- 1 | WasteWater 2 | -------------------------------------------------------------------------------- /LibRAS/Pipes/IdealParticleFilter.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Pipes; 2 | model IdealParticleFilter "Particle filter without pressure drop" 3 | extends LibRAS.Interfaces.PartialTwoPort; 4 | parameter Real filterFactor(min = 0.0) = 0.9 "Particulates removal factor" annotation(Dialog(tab = "General")); 5 | parameter Real TSS_ratio (unit="1", min=0) = 0.75 "gTSS/gCOD" annotation(Dialog(tab = "General")); 6 | parameter Medium.MassFlowRate m_flow_nominal = system.m_flow_nominal "Nominal value of m_flow = port_a.m_flow" annotation(Dialog(tab = "Advanced")); 7 | parameter Medium.MassFlowRate m_flow_small(min = 0) = if system.use_eps_Re then system.eps_m_flow * m_flow_nominal else system.m_flow_small "Regularization for bi-directional flow in the region |m_flow| < m_flow_small (m_flow_small > 0 required)" annotation(Dialog(tab = "Advanced")); 8 | Modelica.Blocks.Interfaces.RealOutput TSS (unit="kg/s", displayUnit="kg/d") annotation( 9 | Placement(visible = true, transformation(origin = {-32, -74}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {0, -54}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); 10 | Medium.MassFlowRate [Medium.nC_X] m_X_removed (each displayUnit="g/d"); 11 | equation 12 | // mass balance 13 | 0 = port_a.m_flow + port_b.m_flow; 14 | // momentum equation (no pressure loss) 15 | port_a.p = port_b.p; 16 | // isenthalpic state transformation (no storage and no loss of energy) 17 | port_a.h_outflow = inStream(port_b.h_outflow); 18 | port_b.h_outflow = inStream(port_a.h_outflow); 19 | port_a.Xi_outflow = inStream(port_b.Xi_outflow); 20 | port_b.Xi_outflow = inStream(port_a.Xi_outflow); 21 | port_a.C_outflow = inStream(port_b.C_outflow); 22 | port_b.C_outflow = inStream(port_a.C_outflow); 23 | port_a.C_S_outflow = inStream(port_b.C_S_outflow); 24 | port_b.C_S_outflow = inStream(port_a.C_S_outflow); 25 | port_a.C_X_outflow = inStream(port_b.C_X_outflow) * (1 - filterFactor); 26 | port_b.C_X_outflow = inStream(port_a.C_X_outflow) * (1 - filterFactor); 27 | 28 | if port_a.m_flow > 0 then 29 | m_X_removed = -inStream(port_a.C_X_outflow)*port_a.m_flow/scalar(Medium.density_phX(port_a.p, port_a.h_outflow, {1}))*filterFactor; 30 | TSS = sum((if i == Integer(Types.Species.X.ND) then 0 else m_X_removed[i]) for i in 1:Medium.nC_X)*TSS_ratio; 31 | else 32 | m_X_removed = -inStream(port_b.C_X_outflow)*port_b.m_flow/scalar(Medium.density_phX(port_b.p, port_b.h_outflow, {1}))*filterFactor; 33 | TSS = sum((if i == Integer(Types.Species.X.ND) then 0 else m_X_removed[i]) for i in 1:Medium.nC_X)*TSS_ratio; 34 | end if; 35 | annotation(defaultComponentName = "filter", Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{-100, 40}, {100, -40}}, fillPattern = FillPattern.Solid, fillColor = {95, 95, 95}, pattern = LinePattern.None), Rectangle(extent = {{-100, 44}, {100, -44}}, lineColor = {0, 0, 0}, fillPattern = FillPattern.HorizontalCylinder, fillColor = {0, 127, 255})})); 36 | end IdealParticleFilter; -------------------------------------------------------------------------------- /LibRAS/Pipes/OxygenAdder.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Pipes; 2 | 3 | model OxygenAdder "Addition of oxygen to (small) stirred volume" 4 | extends LibRAS.Interfaces.PartialTwoPort; 5 | parameter Modelica.SIunits.Volume V = 1e-3 "Internal volume" annotation(Dialog(tab = "General")); 6 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model within the device" annotation(choicesAllMatching = true); 7 | Tanks.CST cst(V=V, KLa = 0, nPorts = 2, use_portsData = false, redeclare package Medium = Medium, use_m_S_in = true) annotation(Placement(visible = true, transformation(origin = {0, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 8 | Modelica.Blocks.Interfaces.RealInput O2_in (quantity="MassFlowRate", unit="kg/s", displayUnit="g/s") annotation(Placement(visible = true, transformation(origin = {-5, 63}, extent = {{-11, -11}, {11, 11}}, rotation = -90), iconTransformation(origin = {0, 102}, extent = {{-20, -20}, {20, 20}}, rotation = -90))); 9 | Modelica.Blocks.Sources.Constant const(k = 0) annotation(Placement(visible = true, transformation(origin = {-76, 46}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 10 | Modelica.Blocks.Routing.Replicator replicator1(nout = Medium.nC_S-1) annotation(Placement(visible = true, transformation(origin = {-42, 46}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 11 | parameter Integer ind = Integer(Types.Species.S.O); 12 | equation 13 | connect(const.y, replicator1.u) annotation(Line(points = {{-64, 46}, {-54, 46}, {-54, 46}, {-54, 46}}, color = {0, 0, 127})); 14 | connect(O2_in, cst.m_S_in[ind]) annotation(Line(points = {{-4, 64}, {-4, 64}, {-4, 20}, {-4, 20}}, color = {0, 0, 127})); 15 | for i in 1:ind-1 loop 16 | connect(replicator1.y[i], cst.m_S_in[i]) annotation(Line(points = {{-30, 46}, {-4, 46}, {-4, 20}, {-4, 20}}, color = {0, 0, 127})); 17 | end for; 18 | for i in ind+1:Medium.nC_S loop 19 | connect(replicator1.y[i-1], cst.m_S_in[i]) annotation(Line(points = {{-30, 46}, {-4, 46}, {-4, 20}, {-4, 20}}, color = {0, 0, 127})); 20 | end for; 21 | connect(port_a, cst.ports[1]); 22 | connect(port_b, cst.ports[2]); 23 | annotation(defaultComponentName = "O2addition", uses(Modelica(version = "3.2.1")), Icon(coordinateSystem( 24 | initialScale = 0.1), graphics={Rectangle(fillColor = {0, 127, 255}, fillPattern = FillPattern.HorizontalCylinder, extent = {{-100, 44}, {100, -44}}), Text(lineColor = {0, 0, 255}, extent = {{-150, -89}, {150, -129}}, textString = "%name", fontName = "DejaVu Sans Mono"), Rectangle(fillColor = {85, 170, 127}, fillPattern = FillPattern.VerticalCylinder, extent = {{-44, 100}, {44, 44}})})); 25 | end OxygenAdder; -------------------------------------------------------------------------------- /LibRAS/Pipes/PartialStraightPipe.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Pipes; 2 | partial model PartialStraightPipe "Base class for straight pipe models" 3 | extends LibRAS.Interfaces.PartialTwoPort; 4 | import SI = Modelica.SIunits; 5 | // Geometry 6 | // Note: define nParallel as Real to support inverse calculations 7 | parameter Real nParallel(min = 1) = 1 "Number of identical parallel pipes" annotation(Dialog(group = "Geometry")); 8 | parameter SI.Length length "Length" annotation(Dialog(tab = "General", group = "Geometry")); 9 | parameter Boolean isCircular = true "= true if cross sectional area is circular" annotation(Evaluate, Dialog(tab = "General", group = "Geometry")); 10 | parameter SI.Diameter diameter "Diameter of circular pipe" annotation(Dialog(group = "Geometry", enable = isCircular)); 11 | parameter SI.Area crossArea = Modelica.Constants.pi * diameter * diameter / 4 "Inner cross section area" annotation(Dialog(tab = "General", group = "Geometry", enable = not isCircular)); 12 | parameter SI.Length perimeter = Modelica.Constants.pi * diameter "Inner perimeter" annotation(Dialog(tab = "General", group = "Geometry", enable = not isCircular)); 13 | parameter SI.Height roughness = 2.5e-5 "Average height of surface asperities (default: smooth steel pipe)" annotation(Dialog(group = "Geometry")); 14 | final parameter SI.Volume V = crossArea * length * nParallel "volume size"; 15 | // Static head 16 | parameter SI.Length height_ab = 0 "Height(port_b) - Height(port_a)" annotation(Dialog(group = "Static head")); 17 | // Pressure loss 18 | replaceable model FlowModel = Modelica.Fluid.Pipes.BaseClasses.FlowModels.DetailedPipeFlow constrainedby Modelica.Fluid.Pipes.BaseClasses.FlowModels.PartialStaggeredFlowModel "Wall friction, gravity, momentum flow" annotation(Dialog(group = "Pressure loss"), choicesAllMatching = true); 19 | equation 20 | assert(length >= height_ab, "Parameter length must be greater or equal height_ab."); 21 | annotation(defaultComponentName = "pipe", Icon(coordinateSystem(preserveAspectRatio = false, extent = {{-100, -100}, {100, 100}}), graphics = {Rectangle(extent = {{-100, 40}, {100, -40}}, fillPattern = FillPattern.Solid, fillColor = {95, 95, 95}, pattern = LinePattern.None), Rectangle(extent = {{-100, 44}, {100, -44}}, lineColor = {0, 0, 0}, fillPattern = FillPattern.HorizontalCylinder, fillColor = {0, 127, 255})}), Documentation(info = " 22 |

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 | 40 |

Numerical Issues

41 |

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 |

18 | ")); 19 | end PartialAbsoluteSensor; 20 | -------------------------------------------------------------------------------- /LibRAS/Sensors/PartialFlowSensor.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sensors; 2 | partial model PartialFlowSensor "Partial component to model sensors that measure flow properties" 3 | extends LibRAS.Interfaces.PartialTwoPort; 4 | parameter Medium.MassFlowRate m_flow_nominal = system.m_flow_nominal "Nominal value of m_flow = port_a.m_flow" annotation(Dialog(tab = "Advanced")); 5 | parameter Medium.MassFlowRate m_flow_small(min = 0) = if system.use_eps_Re then system.eps_m_flow * m_flow_nominal else system.m_flow_small "Regularization for bi-directional flow in the region |m_flow| < m_flow_small (m_flow_small > 0 required)" annotation(Dialog(tab = "Advanced")); 6 | equation 7 | // mass balance 8 | 0 = port_a.m_flow + port_b.m_flow; 9 | // momentum equation (no pressure loss) 10 | port_a.p = port_b.p; 11 | // isenthalpic state transformation (no storage and no loss of energy) 12 | port_a.h_outflow = inStream(port_b.h_outflow); 13 | port_b.h_outflow = inStream(port_a.h_outflow); 14 | port_a.Xi_outflow = inStream(port_b.Xi_outflow); 15 | port_b.Xi_outflow = inStream(port_a.Xi_outflow); 16 | port_a.C_outflow = inStream(port_b.C_outflow); 17 | port_b.C_outflow = inStream(port_a.C_outflow); 18 | port_a.C_S_outflow = inStream(port_b.C_S_outflow); 19 | port_b.C_S_outflow = inStream(port_a.C_S_outflow); 20 | port_a.C_X_outflow = inStream(port_b.C_X_outflow); 21 | port_b.C_X_outflow = inStream(port_a.C_X_outflow); 22 | annotation(Documentation(info = " 23 |

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 |

30 | ")); 31 | end PartialFlowSensor; 32 | -------------------------------------------------------------------------------- /LibRAS/Sensors/SoluteSensor.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sensors; 2 | model SoluteSensor 3 | extends LibRAS.Sensors.PartialAbsoluteSensor; 4 | extends Modelica.Icons.RotationalSensor; 5 | parameter String substanceName = "O" "Name of sensed substance (Not used)"; 6 | parameter Integer substanceIndex = Integer(LibRAS.Types.Species.S.O) "Species to sense"; 7 | 8 | Modelica.Blocks.Interfaces.RealOutput C (quantity="MassConcentration", unit="kg/m3", displayUnit="g/m3") "Trace substance in port medium" 9 | annotation (Placement(transformation(extent={{100,-10},{120,10}}))); 10 | 11 | protected 12 | parameter Integer ind(fixed=false) "Index of species in vector of auxiliary substances"; 13 | Medium.ExtraProperty CVec[Medium.nC_S](quantity=Medium.solublesNames) "Trace substances vector, needed because indexed argument for the operator inStream is not supported"; 14 | 15 | initial algorithm 16 | ind:= -1; 17 | for i in 1:Medium.nC_S loop 18 | if ( Modelica.Utilities.Strings.isEqual(Medium.solublesNames[i], substanceName)) then 19 | ind := i; 20 | end if; 21 | end for; 22 | assert(ind > 0, "Substance S.'" + substanceName + "' is not present in medium '" 23 | + Medium.mediumName + "'.\n" 24 | + "Check sensor parameter and medium model."); 25 | equation 26 | CVec = inStream(port.C_S_outflow); 27 | C = CVec[substanceIndex]; 28 | annotation (Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,100}}), graphics={ 29 | Line(points={{0,-70},{0,-100}}, color={0,0,127}), 30 | Text( 31 | extent={{-150,80},{150,120}}, 32 | textString="%name", 33 | lineColor={0,0,255}), 34 | Text( 35 | extent={{160,-30},{60,-60}}, 36 | lineColor={0,0,0}, 37 | textString="C"), 38 | Line(points={{70,0},{100,0}}, color={0,0,127})})); 39 | end SoluteSensor; 40 | -------------------------------------------------------------------------------- /LibRAS/Sensors/StateSensor.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sensors; 2 | model StateSensor 3 | extends LibRAS.Sensors.PartialFlowSensor; 4 | extends Modelica.Icons.RotationalSensor; 5 | import LibRAS.Types.Species.S; 6 | import LibRAS.Types.Species.X; 7 | // Modelica.Blocks.Interfaces.RealOutput states[31] "States at port"; 8 | parameter Integer stateNumber_S[S] = {1, 2, 8, 9, 10, 11, 13, 14}; 9 | parameter Integer stateNumber_S_film[S] = {16, 17, 23, 24, 25, 26, 28, 29}; 10 | parameter Integer stateNumber_X[X] = {3, 4, 5, 6, 7, 12}; 11 | parameter Integer stateNumber_X_film[X] = {18, 19, 20, 21, 22, 27}; 12 | parameter Integer stateNumber_L = 31; 13 | parameter Integer blankStates[:] = {15, 30}; 14 | 15 | Modelica.Blocks.Interfaces.RealOutput C_S[Medium.nC_S] annotation(Placement(visible = true, transformation(origin = {-86, 54}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-32, 102}, extent = {{-10, -10}, {10, 10}}, rotation = 90))); 16 | Modelica.Blocks.Interfaces.RealOutput C_X[Medium.nC_X] annotation(Placement(visible = true, transformation(origin = {-76, 64}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {32, 102}, extent = {{-10, -10}, {10, 10}}, rotation = 90))); 17 | equation 18 | C_S = if port_a.m_flow > 0 then inStream(port_a.C_S_outflow) else inStream(port_b.C_S_outflow); 19 | C_X = if port_a.m_flow > 0 then inStream(port_a.C_X_outflow) else inStream(port_b.C_X_outflow); 20 | /* for i in S loop 21 | states[stateNumber_S[i]] = C_S[Integer(i)]; 22 | states[stateNumber_S_film[i]] = C_S_film[Integer(i)]; 23 | end for; 24 | for i in X loop 25 | states[stateNumber_X[i]] = C_X[Integer(i)]; 26 | states[stateNumber_X_film[i]] = C_X_film[Integer(i)]; 27 | end for; 28 | states[stateNumber_L] = 0; 29 | for i in 4 loop 30 | states[blankStates[i]] = 0; 31 | end for;*/ 32 | annotation(Icon(coordinateSystem(preserveAspectRatio = false, initialScale = 0.1), graphics = {Line(points = {{70, 0}, {100, 0}}, color = {0, 128, 255}), Text(extent = {{162, 120}, {2, 90}}, textString = "states", fontName = "DejaVu Sans Mono"), Line(points = {{-100, 0}, {-70, 0}}, color = {0, 128, 255}), Line(origin = {-32, -8}, points = {{0, 100}, {0, 70}}, color = {0, 0, 127}), Line(origin = {32, -8}, points = {{0, 100}, {0, 70}}, color = {0, 0, 127})}), uses(Modelica(version = "3.2.1"))); 33 | end StateSensor; 34 | -------------------------------------------------------------------------------- /LibRAS/Sensors/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | 3 | package Sensors 4 | extends Modelica.Icons.SensorsPackage; 5 | // End package 6 | end Sensors; 7 | -------------------------------------------------------------------------------- /LibRAS/Sensors/package.order: -------------------------------------------------------------------------------- 1 | StateSensor 2 | SoluteSensor 3 | PartialFlowSensor 4 | PartialAbsoluteSensor 5 | MassFlowRate 6 | -------------------------------------------------------------------------------- /LibRAS/Sources/Boundary_pT.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sources; 2 | model Boundary_pT "Boundary with prescribed pressure, temperature, composition and trace substances" 3 | import Modelica.Media.Interfaces.Choices.IndependentVariables; 4 | extends Sources.PartialSource; 5 | parameter Boolean use_p_in = false "Get the pressure 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.AbsolutePressure p = Medium.p_default "Fixed value of pressure" annotation(Evaluate = true, Dialog(enable = not use_p_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) = fill(0, Medium.nC_S) "Fixed values of trace 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) = fill(0, Medium.nC_X) "Fixed values of trace substances" annotation(Evaluate = true, Dialog(enable = not use_C_S_in and Medium.nC_S > 0)); 17 | Modelica.Blocks.Interfaces.RealInput p_in if use_p_in "Prescribed boundary pressure" annotation(Placement(visible = true, transformation(extent = {{-120, 60}, {-80, 100}}, rotation = 0), iconTransformation(extent = {{-120, 60}, {-80, 100}}, rotation = 0))); 18 | Modelica.Blocks.Interfaces.RealInput T_in if use_T_in "Prescribed boundary temperature" annotation(Placement(transformation(extent = {{-140, 20}, {-100, 60}}))); 19 | Modelica.Blocks.Interfaces.RealInput X_in[Medium.nX] if use_X_in "Prescribed boundary composition" annotation(Placement(visible = true, transformation(extent = {{-140, -60}, {-100, -20}}, rotation = 0), iconTransformation(extent = {{-140, -60}, {-100, -20}}, rotation = 0))); 20 | Modelica.Blocks.Interfaces.RealInput C_in[Medium.nC] if use_C_in "Prescribed boundary trace substances" annotation(Placement(visible = true, transformation(extent = {{-120, -100}, {-80, -60}}, rotation = 0), iconTransformation(extent = {{-120, -100}, {-80, -60}}, rotation = 0))); 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 = {-60, 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 = {60, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90))); 23 | protected 24 | Modelica.Blocks.Interfaces.RealInput p_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 | connect(C_in, C_in_internal) annotation(Line); 32 | connect(p_in, p_in_internal) annotation(Line); 33 | connect(C_X_in, C_X_in_internal) annotation(Line); 34 | connect(C_S_in, C_S_in_internal) annotation(Line); 35 | Modelica.Fluid.Utilities.checkBoundary(Medium.mediumName, Medium.substanceNames, Medium.singleState, true, X_in_internal, "Boundary_pT"); 36 | connect(T_in, T_in_internal); 37 | connect(X_in, X_in_internal); 38 | if not use_p_in then 39 | p_in_internal = p; 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 | medium.p = p_in_internal; 57 | if Medium.ThermoStates == IndependentVariables.ph or Medium.ThermoStates == IndependentVariables.phX then 58 | medium.h = Medium.specificEnthalpy(Medium.setState_pTX(p_in_internal, T_in_internal, X_in_internal)); 59 | else 60 | medium.T = T_in_internal; 61 | end if; 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 = false, extent = {{-100, -100}, {100, 100}}), graphics = {Ellipse(extent = {{-100, 100}, {100, -100}}, lineColor = {0, 0, 0}, fillPattern = FillPattern.Sphere, fillColor = {0, 127, 255}), Text(extent = {{-150, 120}, {150, 160}}, textString = "%name", lineColor = {0, 0, 255}), Line(visible = use_p_in, points = {{-100, 80}, {-58, 80}}, color = {0, 0, 255}), Line(visible = use_T_in, points = {{-100, 40}, {-92, 40}}, color = {0, 0, 255}), Line(visible = use_X_in, points = {{-100, -40}, {-92, -40}}, color = {0, 0, 255}), Line(visible = use_C_in, points = {{-100, -80}, {-60, -80}}, color = {0, 0, 255}), Text(visible = use_p_in, extent = {{-152, 134}, {-68, 94}}, lineColor = {0, 0, 0}, textString = "p"), Text(visible = use_X_in, extent = {{-164, 4}, {-62, -36}}, lineColor = {0, 0, 0}, textString = "X"), Text(visible = use_C_in, extent = {{-164, -90}, {-62, -130}}, lineColor = {0, 0, 0}, textString = "C"), Text(visible = use_T_in, extent = {{-162, 34}, {-60, -6}}, lineColor = {0, 0, 0}, textString = "T")}), Documentation(info = " 67 |

68 | Defines prescribed values for boundary conditions: 69 |

70 | 75 |

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.

77 |

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 | 75 |

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.

77 |

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 | 37 | ")); 38 | end PartialFlowSource; 39 | -------------------------------------------------------------------------------- /LibRAS/Sources/PartialSource.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sources; 2 | partial model PartialSource "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 | LibRAS.Interfaces.WasteFluidPorts_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, 40}, {110, -40}}))); 9 | protected 10 | parameter Types.PortFlowDirection flowDirection = Types.PortFlowDirection.Bidirectional "Allowed flow direction" annotation(Evaluate = true, Dialog(tab = "Advanced")); 11 | equation 12 | // Only one connection allowed to a port to avoid unwanted ideal mixing 13 | for i in 1:nPorts loop 14 | assert(cardinality(ports[i]) <= 1, " 15 | each ports[i] of boundary shall at most be connected to one component. 16 | If two or more connections are present, ideal mixing takes 17 | place with these connections, which is usually not the intention 18 | of the modeller. Increase nPorts to add an additional port. 19 | "); 20 | ports[i].p = medium.p; 21 | ports[i].h_outflow = medium.h; 22 | ports[i].Xi_outflow = medium.Xi; 23 | end for; 24 | annotation(defaultComponentName = "boundary", Documentation(info = " 25 |

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 | 36 | ")); 37 | end PartialSource; 38 | -------------------------------------------------------------------------------- /LibRAS/Sources/WaterExchange.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Sources; 2 | 3 | model WaterExchange "Water make-up and let-down." 4 | extends LibRAS.Interfaces.PartialTwoPort; 5 | // parameter Real makeupRate (min=0) = 0.1 "Water exchange factor" annotation(Dialog(tab = "General")); 6 | parameter Modelica.SIunits.Temperature T_makeup = system.T_ambient "Make-up water temperature" annotation(Dialog(tab = "General")); 7 | replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model within the source" annotation(choicesAllMatching = true); 8 | LibRAS.Sources.MassFlowSource_T source(redeclare package Medium = Medium, T = T_makeup, nPorts = 1, use_m_flow_in = true) annotation(Placement(visible = true, transformation(origin = {-8, 30}, extent = {{10, -10}, {-10, 10}}, rotation = 90))); 9 | LibRAS.Pipes.Tee tee(redeclare package Medium = Medium) annotation(Placement(visible = true, transformation(origin = {-8, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 10 | LibRAS.Sources.MassFlowSource_T drain(redeclare package Medium = Medium, T = T_makeup, nPorts = 1, use_m_flow_in = true) annotation(Placement(visible = true, transformation(origin = {-62, 30}, extent = {{-10, -10}, {10, 10}}, rotation = -90))); 11 | LibRAS.Sensors.MassFlowRate massFlowRate1(redeclare package Medium = Medium) annotation(Placement(visible = true, transformation(origin = {44, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 12 | Modelica.Blocks.Math.Gain gain1(k = -1) annotation(Placement(visible = true, transformation(origin = {-38, 80}, extent = {{6, -6}, {-6, 6}}, rotation = 0))); 13 | Modelica.Blocks.Interfaces.RealInput makeupRate (min=0, max=1) "Water exchange factor" annotation( 14 | Placement(visible = true, transformation(origin = {0, 100}, extent = {{12, -12}, {-12, 12}}, rotation = 90), iconTransformation(origin = {0, -120}, extent = {{-20, -20}, {20, 20}}, rotation = 90))); 15 | Modelica.Blocks.Math.Product product1 annotation( 16 | Placement(visible = true, transformation(origin = {-54, 54}, extent = {{6, -6}, {-6, 6}}, rotation = 90))); 17 | Modelica.Blocks.Math.Product product2 annotation( 18 | Placement(visible = true, transformation(origin = {-16, 54}, extent = {{-6, -6}, {6, 6}}, rotation = -90))); 19 | Medium.MassFlowRate [Medium.nC_S] m_S_removed (each displayUnit="g/d"); 20 | Medium.MassFlowRate [Medium.nC_X] m_X_removed (each displayUnit="g/d"); 21 | Modelica.Blocks.Interfaces.RealOutput V_flow_exchanged(final quantity = "VolumeFlowRate", final unit = "m3/s") "Volume flow rate of exchanged water" annotation( 22 | Placement(visible = true, transformation(origin = {0, -100}, extent = {{10, -10}, {-10, 10}}, rotation = 90), iconTransformation(origin = {-1.77636e-15, 119}, extent = {{19, -19}, {-19, 19}}, rotation = -90))); 23 | protected 24 | Medium.Density d "Density of the passing fluid"; 25 | 26 | equation 27 | connect(source.ports[1], tee.port_3) annotation( 28 | Line(points = {{-8, 20}, {-8, 20}, {-8, 10}, {-8, 10}}, thickness = 0.5)); 29 | connect(massFlowRate1.m_flow, product2.u1) annotation( 30 | Line(points = {{44, 12}, {44, 12}, {44, 66}, {-12, 66}, {-12, 62}, {-12, 62}}, color = {0, 0, 127})); 31 | connect(massFlowRate1.m_flow, product1.u2) annotation( 32 | Line(points = {{44, 12}, {44, 12}, {44, 66}, {-50, 66}, {-50, 62}, {-50, 62}}, color = {0, 0, 127})); 33 | connect(makeupRate, product2.u2) annotation( 34 | Line(points = {{0, 100}, {0, 100}, {0, 68}, {-20, 68}, {-20, 62}, {-20, 62}}, color = {0, 0, 127})); 35 | connect(product2.y, source.m_flow_in) annotation( 36 | Line(points = {{-16, 48}, {-16, 48}, {-16, 40}, {-16, 40}}, color = {0, 0, 127})); 37 | connect(makeupRate, gain1.u) annotation( 38 | Line(points = {{0, 100}, {0, 100}, {0, 80}, {-30, 80}, {-30, 80}, {-30, 80}}, color = {0, 0, 127})); 39 | connect(gain1.y, product1.u1) annotation( 40 | Line(points = {{-44, 80}, {-58, 80}, {-58, 62}, {-58, 62}}, color = {0, 0, 127})); 41 | connect(product1.y, drain.m_flow_in) annotation( 42 | Line(points = {{-54, 48}, {-54, 48}, {-54, 40}, {-54, 40}}, color = {0, 0, 127})); 43 | connect(port_a, drain.ports[1]) annotation( 44 | Line(points = {{-100, 0}, {-62, 0}, {-62, 20}})); 45 | connect(port_a, tee.port_1) annotation( 46 | Line(points = {{-100, 0}, {-18, 0}})); 47 | connect(massFlowRate1.port_a, tee.port_2) annotation( 48 | Line(points = {{34, 0}, {2, 0}}, color = {0, 127, 255})); 49 | connect(massFlowRate1.port_b, port_b) annotation( 50 | Line(points = {{54, 0}, {100, 0}}, color = {0, 127, 255})); 51 | if port_a.m_flow > 0 then 52 | m_S_removed = -(inStream(port_a.C_S_outflow)*port_a.m_flow/scalar(Medium.density_phX(port_a.p, port_a.h_outflow, {1})) + port_b.C_S_outflow*port_b.m_flow/scalar(Medium.density_phX(port_b.p, port_b.h_outflow, {1}))); 53 | m_X_removed = -(inStream(port_a.C_X_outflow)*port_a.m_flow/scalar(Medium.density_phX(port_a.p, port_a.h_outflow, {1})) + port_b.C_X_outflow*port_b.m_flow/scalar(Medium.density_phX(port_b.p, port_b.h_outflow, {1}))); 54 | else 55 | m_S_removed = -(inStream(port_b.C_S_outflow)*port_b.m_flow/scalar(Medium.density_phX(port_b.p, port_b.h_outflow, {1})) + port_a.C_S_outflow*port_a.m_flow/scalar(Medium.density_phX(port_a.p, port_a.h_outflow, {1}))); 56 | m_X_removed = -(inStream(port_b.C_X_outflow)*port_b.m_flow/scalar(Medium.density_phX(port_b.p, port_b.h_outflow, {1})) + port_a.C_X_outflow*port_a.m_flow/scalar(Medium.density_phX(port_a.p, port_a.h_outflow, {1}))); 57 | end if; 58 | 59 | d = Medium.density(Medium.setState_phX(source.ports[1].p, source.ports[1].h_outflow, source.ports[1].Xi_outflow)); 60 | V_flow_exchanged = -source.ports[1].m_flow / d; 61 | annotation(defaultComponentName = "exchange", uses(Modelica(version = "3.2.1")), 62 | Icon(graphics = {Rectangle(origin = {-40, -1}, fillPattern = FillPattern.Solid, extent = {{-2, -70}, {2, 70}}), Rectangle(origin = {-33, 62}, rotation = -45, fillPattern = FillPattern.Solid, extent = {{-11, -2}, {11, 2}}), Rectangle(origin = {-47, 62}, rotation = 45, fillPattern = FillPattern.Solid, extent = {{-11, -2}, {11, 2}}), Rectangle(origin = {33, -60}, rotation = -45, fillPattern = FillPattern.Solid, extent = {{-11, -2}, {11, 2}}), Rectangle(origin = {47, -60}, rotation = 45, fillPattern = FillPattern.Solid, extent = {{-11, -2}, {11, 2}}), Rectangle(origin = {40, 3}, fillPattern = FillPattern.Solid, extent = {{-2, -70}, {2, 70}}), Rectangle(origin = {0, -1}, extent = {{-100, 101}, {100, -99}})})); 63 | end WaterExchange; -------------------------------------------------------------------------------- /LibRAS/Sources/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Sources 3 | extends Modelica.Icons.SourcesPackage; 4 | end Sources; 5 | -------------------------------------------------------------------------------- /LibRAS/Sources/package.order: -------------------------------------------------------------------------------- 1 | PartialSource 2 | PartialFlowSource 3 | MassFlowSource_T 4 | Boundary_pT 5 | WaterExchange 6 | -------------------------------------------------------------------------------- /LibRAS/System.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | model System 3 | extends Modelica.Fluid.System; 4 | import U = LibRAS.Units; 5 | import Modelica.Constants.eps; 6 | 7 | parameter Modelica.SIunits.PartialPressure pCO2 = 320 "Atmospheric CO2 partial pressure" annotation(Dialog(tab="General", group="Environment")); 8 | 9 | parameter U.GrowthRate[2] mu_H = {3.00, 6.00} "Heterotrophs - Growth constant" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 10 | parameter Real[2] K_S = {10.0, 10.0} "Heterotrophs - Organic substrate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 11 | parameter Real[2] K_OH = {0.20, 0.20} "Heterotrophs - Dissolved oxygen" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 12 | parameter Real[2] K_NO = {0.50, 0.50} "Heterotrophs - Nitrate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 13 | parameter Real[2] b_H = {0.20, 0.40} "Heterotrophs - Mortality rate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 14 | parameter Real[2] mu_A = {0.29, 0.76} "Autotrophs - Growth constant" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 15 | parameter Real[2] mu_AOB={0.29, 0.76} "AOB - Growth constant" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 16 | parameter Real[2] mu_NOB={0.58, 1.04} "NOB - Growth constant" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 17 | parameter Real[2] K_NH = {1.00, 1.00} "Autotrophs - Ammonia" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 18 | parameter Real[2] K_OA = {0.50, 0.50} "Autotrophs - Dissolved oxygen" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 19 | parameter Real[2] b_A = {0.05, 0.15} "Autotrophs - Mortality rate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 20 | parameter Real[2] b_AOB= {0.05, 0.15} "AOB - Mortality rate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); // Guessed 21 | parameter Real[2] b_NOB= {0.05, 0.15} "NOB - Mortality rate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); // Guessed 22 | parameter Real[2] nu_g = {1.00, 1.00} "Correction factor for anoxic growth" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 23 | parameter Real[2] nu_NO2={0.80, 0.80} "Anoxic growth nitrite correction" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 24 | parameter Real[2] nu_NO3={0.80, 0.80} "Anoxic growth nitrate correction" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 25 | parameter Real[2] k_a = {0.05, 0.05} "Ammonification rate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 26 | parameter Real[2] k_h = {2.00, 3.00} "Hydrolysis rate" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 27 | parameter Real[2] K_X = {0.30, 0.10} "Heterotrophs - Hydrolysis" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 28 | parameter Real[2] nu_h = {1.30, 1.30} "Correction factor for hydrolysis" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 29 | parameter Real[2] Y_H = {0.67, 0.67} "Heterotrophs - Yield factor" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 30 | parameter Real[2] Y_A = {0.24, 0.24} "Autotrophs - Yield factor" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 31 | parameter Real[2] Y_AOB= {0.21, 0.21} "AOB - Yield factor" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 32 | parameter Real[2] Y_NOB= {0.03, 0.03} "NOB - Yield factor" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 33 | parameter Real[2] f_p = {0.08, 0.08} "Biomass particulate content" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 34 | parameter Real[2] i_XB = {0.08, 0.08} "Biomass nitrogen (S) content" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 35 | parameter Real[2] i_XP = {0.06, 0.06} "Biomass nitrogen (X) content" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 36 | parameter Real[2] K_Alk= {0.10, 0.10} "Autotrophs - Alkalinity" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); 37 | 38 | parameter Real[2] K_NHH = {0.01, 0.01} "Heterotrophs - Ammonia" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); // This is the 0.01 found in the report 39 | parameter Real[2] K_NHI = {5.00, 5.00} "Ammonia inhibition of NOB growth" annotation(Dialog(tab="Biofilm", group="Growth and conversion (at 10 and 20 degC)")); // Iacopozzi et al 2007 40 | 41 | parameter Real K_x (unit="m/s") = 2.0 /(24*3600) "Solute transport coefficient" annotation(Dialog(tab="Biofilm", group="Physical")); 42 | parameter Real K_a (unit="m/s") = 10 /(24*3600) "Attachment coefficent" annotation(Dialog(tab="Biofilm", group="Physical")); 43 | parameter Real K_dA (unit="1/(m.s)") = 30e3 /(24*3600) "Detachment coefficient in nitrifying biofilm" annotation(Dialog(tab="Biofilm", group="Physical")); 44 | parameter Real K_dH (unit="1/(m.s)") = 100e3 /(24*3600) "Detachment coefficient in heterotrophic biofilm" annotation(Dialog(tab="Biofilm", group="Physical")); 45 | parameter Real rho_x (unit="kg/m3") = 50 "Biofilm thinness" annotation(Dialog(tab="Biofilm", group="Physical")); 46 | parameter Real eps_A = 0.5 "Porosity in nitrifying biofilm" annotation(Dialog(tab="Biofilm", group="Physical")); 47 | parameter Real eps_H = 0.8 "Porosity in heterotrophic biofilm" annotation(Dialog(tab="Biofilm", group="Physical")); 48 | parameter Real As = 500 "Carrier specific surface" annotation(Dialog(tab="Biofilm", group="Physical")); 49 | 50 | parameter Real C_S_start[10](each unit = "kg/m3", each displayUnit = "g/m3") = {eps, eps, eps, eps, eps, eps, 2e-3, eps, eps, eps} "Start value of bulk S in CSBRs" annotation(Dialog(tab="Initialization", group="Concentrations")); 51 | parameter Real C_S_film_start[10](each unit = "kg/m3", each displayUnit = "g/m3") = {eps, eps, eps, eps, eps, eps, 2e-3, eps, eps, eps} "Start value of film S in CSBRs" annotation(Dialog(tab="Initialization", group="Concentrations")); 52 | parameter Real C_X_start[7](each unit = "kg/m3", each displayUnit = "g/m3") = {eps, 1e-3, 1e-3, eps, eps, eps, eps} "Start value of bulk X in CSBRs" annotation(Dialog(tab="Initialization", group="Concentrations")); 53 | parameter Real C_X_film_start[7](each unit = "kg/m3", each displayUnit = "g/m3") = {3.0, 0.5, 5.0, 0.5, 0.5, 1.0, 0.1} "Start value of film X in CSBRs" annotation(Dialog(tab="Initialization", group="Concentrations")); 54 | parameter Real C_X_film_start_nitri[7](each unit = "kg/m3", each displayUnit = "g/m3") = (1 - eps_A) / (1 - eps_H) * C_X_film_start "Start value of film X in nitrifying CSBRs" annotation(Dialog(tab="Initialization", group="Concentrations")); 55 | 56 | end System; 57 | -------------------------------------------------------------------------------- /LibRAS/Tanks/CSBR.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Tanks; 2 | model CSBR "Volume of fixed size, closed to the ambient, with inlet/outlet ports" 3 | 4 | import Modelica.Constants.pi; 5 | import SI = Modelica.SIunits; 6 | 7 | // Mass and energy balance, ports 8 | extends Tanks.PartialTank; 9 | extends Tanks.PartialLumpedVessel(final fluidVolume = V, vesselArea = pi * (3 / 4 * V) ^ (2 / 3), heatTransfer(surfaceAreas = {4 * pi * (3 / 4 * V / pi) ^ (2 / 3)})); 10 | extends Tanks.PartialCSBR; 11 | equation 12 | 13 | end CSBR; 14 | -------------------------------------------------------------------------------- /LibRAS/Tanks/CST.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Tanks; 2 | model CST "Ideally stirred spherical volume with inlet/outlet ports and addition of species. Any reactions are neglected." 3 | 4 | import Modelica.Constants.pi; 5 | import SI = Modelica.SIunits; 6 | import LibRAS.Types.Species.S; 7 | import LibRAS.Types.Species.X; 8 | // Mass and energy balance, ports 9 | extends Tanks.PartialTank; 10 | extends Tanks.PartialLumpedVessel(fluidVolume = V, vesselArea = pi * (3 / 4 * V) ^ (2 / 3), heatTransfer(surfaceAreas = {4 * pi * (3 / 4 * V / pi) ^ (2 / 3)})); 11 | extends Tanks.PartialCST; 12 | equation 13 | Vf = fluidVolume; // There might be fish in here, but there are at least no carriers or biofilm 14 | // Mass balances 15 | if traceDynamics <> Modelica.Fluid.Types.Dynamics.SteadyState then 16 | der(mC_S_scaled) = mbC_S_flow./Medium.C_S_nominal + Vf*reactionRate_S./Medium.C_S_nominal + J_gas./Medium.C_S_nominal; 17 | der(mC_X_scaled) = mbC_X_flow./Medium.C_X_nominal + Vf*reactionRate_X./Medium.C_X_nominal; 18 | end if; 19 | 20 | // Reaction rates 21 | for i in S loop 22 | reactionRate_S[Integer(i)] = 0; 23 | end for; 24 | for i in X loop 25 | reactionRate_X[Integer(i)] = 0; 26 | end for; 27 | 28 | 29 | end CST; 30 | -------------------------------------------------------------------------------- /LibRAS/Tanks/FishTank.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Tanks; 2 | 3 | model FishTank 4 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 5 | import SI = Modelica.SIunits; 6 | import LibRAS.Types.Species.S; 7 | import LibRAS.Culture.*; 8 | import Modelica.SIunits.Conversions.from_day; 9 | import Modelica.SIunits.Conversions.from_hour; 10 | import Modelica.SIunits.Conversions.from_minute; 11 | // DESIGN VARIABLES 12 | // parameter SI.Volume V = 9 "Fish tank volume" annotation(Evaluate=true, Dialog(tab="General", group="Design")); 13 | parameter Integer nTanks = 9 annotation( 14 | Dialog(tab = "General", group = "Design")); 15 | parameter SI.Volume[nTanks] tankVolumes = fill(1, nTanks) "Vector of fish basin volumes" annotation( 16 | Dialog(tab = "General", group = "Design"), 17 | HideResult = true); 18 | // FEED AND FISH DATA 19 | parameter Feed.FeedData feed = Feed.DefaultFeed() "FeedData record" annotation( 20 | choicesAllMatching = true, 21 | Dialog(tab = "General", group = "Culture")); 22 | parameter Fish.FishData fish = Fish.RainbowTrout() "FishData record" annotation( 23 | choicesAllMatching = true, 24 | Dialog(tab = "General", group = "Culture")); 25 | // parameter Waste.WasteData waste = Waste.WasteData(fish=fish, feed=feed, loss=loss) "WasteData record" annotation(choicesAllMatching=true, Dialog(tab="General", group="Culture")); 26 | parameter Integer gradingTime(unit = "d", min = 0) = 30 "Time between gradings in days" annotation( 27 | Dialog(tab = "General", group = "Culture")); 28 | parameter SI.Density fishDensity(displayUnit = "kg/m3") = 70 "Maximum fish density in kg/m3" annotation( 29 | Dialog(tab = "General", group = "Culture")); 30 | // GROWTH AND FEEDING 31 | parameter SI.Temp_C T = 15 "Farming temperature" annotation( 32 | Dialog(tab = "General", group = "Culture")); 33 | parameter SI.Time[:] feedingTimes = from_hour({6, 18}) "Feeding times in seconds after beginning of each day (00:00)" annotation( 34 | Dialog(tab = "General", group = "Culture")); 35 | parameter SI.Time feedingDuration = from_minute(15) "Length of feeding period in seconds" annotation( 36 | Dialog(tab = "General", group = "Culture")); 37 | parameter Real FCR = 1.1 "kg feed/kg fish growth" annotation( 38 | Dialog(tab = "General", group = "Culture")); 39 | parameter Real loss = 0.1 "Food loss factor" annotation( 40 | Dialog(tab = "General", group = "Culture")); 41 | // Oxygen control 42 | parameter Real oxygenControl_Q "Throughflow setpoint for controller tuning" annotation( 43 | Evaluate = true, 44 | Dialog(tab = "General", group = "Oxygen control")); 45 | parameter Real oxygenControl_K = 10 * 0.1 * oxygenControl_Q / ((Utilities.oxygenSaturation(SI.Conversions.from_degC(T)) - 8e-3) * sum(tankVolumes)) "Proportional gain of oxygen PI controller" annotation( 46 | Evaluate = true, 47 | Dialog(tab = "General", group = "Oxygen control")); 48 | parameter Real oxygenControl_Ti = oxygenControl_K * sum(tankVolumes) ^ 2 * (Utilities.oxygenSaturation(SI.Conversions.from_degC(T)) - 8e-3) / oxygenControl_Q ^ 2 "Integral time for oyxgen PI controller" annotation( 49 | Evaluate = true, 50 | Dialog(tab = "General", group = "Oxygen control")); 51 | parameter Real oxygenControl_maxKLa = 0.20 "Maximum KLa value in 1/s" annotation( 52 | Evaluate = true, 53 | Dialog(tab = "General", group = "Oxygen control")); 54 | parameter Real oxygenControl_minKLa = 0 "Minimum KLa value in 1/s" annotation( 55 | Evaluate = true, 56 | Dialog(tab = "General", group = "Oxygen control")); 57 | outer LibRAS.System system annotation( 58 | Placement(visible = true, transformation(extent = {{20, 60}, {40, 80}}, rotation = 0))); 59 | parameter Integer nPorts = 0 "Number of ports" annotation( 60 | Evaluate = true, 61 | Dialog(connectorSizing = true, tab = "General", group = "Ports")); 62 | LibRAS.Interfaces.VesselWasteFluidPorts_b ports[nPorts](redeclare each package Medium = Medium) "Fluid inlets and outlets" annotation( 63 | Placement(visible = true, transformation(origin = {48, 42}, extent = {{-40, -10}, {40, 10}}, rotation = -90), iconTransformation(extent = {{-40, -110}, {40, -90}}, rotation = 0))); 64 | LibRAS.Tanks.CST fishtank(redeclare package Medium = Medium, V = sum(tankVolumes), fluidVolume = sum(culture.Vw), energyDynamics = Modelica.Fluid.Types.Dynamics.SteadyState, massDynamics = Modelica.Fluid.Types.Dynamics.FixedInitial, nPorts = nPorts, use_KLa_in = true, use_m_S_in = true, use_m_X_in = true, use_portsData = false, use_HeatTransfer = true) annotation( 65 | Placement(visible = true, transformation(origin = {0, 44}, extent = {{-10, 10}, {10, -10}}, rotation = -90))); 66 | Modelica.Blocks.Continuous.LimPID oxygenPI(Ti(displayUnit = "s") = oxygenControl_Ti, controllerType = Modelica.Blocks.Types.SimpleController.PI, k = oxygenControl_K, limitsAtInit = false, yMax = oxygenControl_maxKLa, yMin = oxygenControl_minKLa) annotation( 67 | Placement(visible = true, transformation(origin = {-24, 20}, extent = {{-6, -6}, {6, 6}}, rotation = 0))); 68 | Modelica.Blocks.Interfaces.RealInput oxygenSetpoint annotation( 69 | Placement(visible = true, transformation(origin = {-100, 20}, extent = {{-8, -8}, {8, 8}}, rotation = 0), iconTransformation(origin = {-120, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0))); 70 | replaceable LibRAS.Culture.SSCulture_V culture(nTanks = nTanks, tankVolumes = tankVolumes, fish = fish, feed = feed, gradingTime = gradingTime, fishDensity = fishDensity, feedingTimes = feedingTimes, feedingDuration = feedingDuration, T = T, FCR = FCR, loss = loss) annotation( 71 | Placement(visible = true, transformation(origin = {-50, 44}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 72 | output SI.Mass mFish "Total mass of fish"; 73 | output SI.Mass meanBW "Mean fish body weight"; 74 | output SI.Density avgDensity = mFish/sum(tankVolumes); 75 | Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature1(T = SI.Conversions.from_degC(T)) annotation( 76 | Placement(visible = true, transformation(origin = {-20, 80}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); 77 | 78 | equation 79 | connect(fixedTemperature1.port, fishtank.heatPort) annotation( 80 | Line(points = {{-10, 80}, {0, 80}, {0, 54}, {0, 54}}, color = {191, 0, 0})); 81 | mFish = sum(culture.m_fish); 82 | meanBW = mFish / sum(culture.n); 83 | connect(fishtank.ports, ports) annotation( 84 | Line(points = {{10, 44}, {48, 44}, {48, 42}}, color = {0, 127, 255})); 85 | connect(oxygenSetpoint, oxygenPI.u_s) annotation( 86 | Line(points = {{-100, 20}, {-32, 20}}, color = {0, 0, 127})); 87 | connect(oxygenPI.y, fishtank.KLa_in) annotation( 88 | Line(points = {{-18, 20}, {0, 20}, {0, 34}, {0, 34}}, color = {0, 0, 127})); 89 | connect(fishtank.C_S[Integer(S.O)], oxygenPI.u_m); 90 | connect(culture.m_X_output, fishtank.m_X_in) annotation( 91 | Line(points = {{-38, 40}, {-10, 40}, {-10, 40}, {-10, 40}}, color = {0, 0, 127})); 92 | connect(culture.m_S_output, fishtank.m_S_in) annotation( 93 | Line(points = {{-38, 48}, {-12, 48}, {-12, 48}, {-10, 48}}, color = {0, 0, 127})); 94 | annotation( 95 | Icon(coordinateSystem(initialScale = 0.2), graphics = {Rectangle(lineColor = {255, 255, 255}, fillColor = {255, 255, 255}, fillPattern = FillPattern.VerticalCylinder, extent = {{-100, -100}, {100, 100}}), Rectangle(fillColor = {85, 170, 255}, fillPattern = FillPattern.VerticalCylinder, extent = {{-100, -100}, {100, 0}}), Text(lineColor = {0, 0, 255}, extent = {{-94, 90}, {95, 60}}, textString = "%name"), Line(points = {{-100, 100}, {-100, -100}, {100, -100}, {100, 100}})}), 96 | experiment(StartTime = 0, StopTime = 2.592e+06, Tolerance = 0.0001, Interval = 3600)); 97 | end FishTank; -------------------------------------------------------------------------------- /LibRAS/Tanks/OpenCSBR.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Tanks; 2 | model OpenCSBR 3 | import SI = Modelica.SIunits; 4 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 5 | 6 | extends OpenTank; 7 | extends Tanks.PartialCSBR; 8 | 9 | Medium.MassFlowRate[nTopPorts, Medium.nC_S] mC_S_flow_top "Dissolved substance mass flow rates from the top ports into the tank"; 10 | Medium.MassFlowRate[nPorts, Medium.nC_S] port_b_mC_S_flow_bottom "Dissolved substance mass flow rates from the bottom ports into the tank"; 11 | 12 | equation 13 | for i in 1:nPorts loop 14 | port_b_mC_S_flow_bottom[i, :] = ports[i].m_flow * actualStream(ports[i].C_S_outflow); 15 | end for; 16 | 17 | for i in 1:nTopPorts loop 18 | // It is assumed that fluid flows only from one of the top ports in to the tank and never vice versa 19 | mC_S_flow_top[i, :] = topPorts[i].m_flow * actualStream(topPorts[i].C_S_outflow); 20 | topPorts[i].C_S_outflow = C_S_start; 21 | end for; 22 | 23 | for i in 1:Medium.nC_S loop 24 | mbC_S_flow[i] = sum(mC_S_flow_top[:, i]) + sum(port_b_mC_S_flow_bottom[:, i]); 25 | end for; 26 | end OpenCSBR; 27 | -------------------------------------------------------------------------------- /LibRAS/Tanks/PartialCST.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Tanks; 2 | partial model PartialCST "Reactionless stirred tank with optional input of species and bubbling of air" 3 | import SI = Modelica.SIunits; 4 | import LibRAS.Types.Species.S; 5 | import LibRAS.Types.Species.X; 6 | import to_degC = Modelica.SIunits.Conversions.to_degC; 7 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 8 | parameter Medium.ExtraProperty C_S_start[Medium.nC_S](quantity = Medium.solublesNames, each unit = "kg/m3", each displayUnit = "g/m3") = system.C_S_start "Start value of solubles" annotation(Dialog(tab = "Initialization", enable = Medium.nC_S > 0)); 9 | parameter Medium.ExtraProperty C_X_start[Medium.nC_X](quantity = Medium.particulatesNames, each unit = "kg/m3", each displayUnit = "g/m3") = system.C_X_start "Start value of particulates" annotation(Dialog(tab = "Initialization", enable = Medium.nC_X > 0)); 10 | parameter Real KLa(unit = "1/s") = 500 / (24 * 3600) "Gas exchange rate" annotation(Dialog(tab = "General", group = "CSBR")); 11 | parameter Real KLa_ratio(min=0) = 0.9 "KLa_CO2/KLa_O2 ratio" annotation(Dialog(tab = "General", group = "CSBR")); 12 | 13 | SI.Mass[Medium.nC_S] mC_S (each min=-1e-5) "Masses of dissolved substances in the fluid"; 14 | Real[Medium.nC_S] C_S(each unit = "kg/m3", each displayUnit = "g/m3", each min = -1e-5) "Dissolved substance mixture content"; 15 | Medium.ExtraPropertyFlowRate[Medium.nC_S] mbC_S_flow(each unit = "kg/s", each displayUnit = "g/s") "Dissolved substance mass flows across boundaries"; 16 | Medium.ExtraPropertyFlowRate[nPorts, Medium.nC_S] ports_mC_S_flow(each unit = "kg/s", each displayUnit = "g/s") annotation(HideResult = true); 17 | Medium.ExtraPropertyFlowRate[Medium.nC_S] sum_ports_mC_S_flow(each unit = "kg/s", each displayUnit = "g/d") "Dissolved substance mass flows through ports" annotation(HideResult = true); 18 | Medium.ExtraPropertyFlowRate[Medium.nC_S] J_gas(each unit = "kg/s", each displayUnit = "g/s") "Gas diffusion rate"; 19 | SI.Mass[Medium.nC_X] mC_X (each min=-1e-5) "Masses of particulate substances in the fluid"; 20 | Real[Medium.nC_X] C_X(each unit = "kg/m3", each displayUnit = "g/m3", each min=-1e-5) "Particulate substance mixture content"; 21 | Medium.ExtraPropertyFlowRate[Medium.nC_X] mbC_X_flow(each unit = "kg/s", each displayUnit = "g/s") "Particulate substance mass flows across boundaries"; 22 | Medium.ExtraPropertyFlowRate[nPorts, Medium.nC_X] ports_mC_X_flow(each unit = "kg/s", each displayUnit = "g/s") annotation(HideResult = true); 23 | Medium.ExtraPropertyFlowRate[Medium.nC_X] sum_ports_mC_X_flow(each unit = "kg/s", each displayUnit = "g/s") "Particulate substance mass flows through ports" annotation(HideResult = true); 24 | 25 | output Medium.ExtraProperty C_S_sat_O2(each unit = "kg/m3", each displayUnit = "g/m3") = (14.53 - 0.411 * to_degC(medium.T) + 9.6e-3 * to_degC(medium.T) ^ 2 - 1.2e-4 * to_degC(medium.T) ^ 3) / 1000; 26 | 27 | SI.Volume Vf; 28 | 29 | parameter Boolean use_KLa_in = false "Get KLa from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 30 | parameter Boolean use_m_S_in = false "Get added S from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 31 | parameter Boolean use_m_X_in = false "Get added X from the input connector" annotation(Evaluate = true, HideResult = true, choices(checkBox = true)); 32 | 33 | Modelica.Blocks.Interfaces.RealInput KLa_in if use_KLa_in "Prescribed oxygenation rate" annotation(Placement(visible = true,transformation(origin = {-60, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {100, 0},extent = {{-20, -20}, {20, 20}}, rotation = 180))); 34 | Modelica.Blocks.Interfaces.RealInput m_S_in[Medium.nC_S] if use_m_S_in "Addition rate of soluble species (kg/s)" annotation(Placement(visible = true, transformation(origin = {40, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {-40, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90))); 35 | Modelica.Blocks.Interfaces.RealInput m_X_in[Medium.nC_X] if use_m_S_in "Addition rate of particulate species (kg/s)"annotation(Placement(visible = true, transformation(origin = {80, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90), iconTransformation(origin = {40, 100}, extent = {{-20, -20}, {20, 20}}, rotation = -90))); 36 | 37 | protected 38 | Modelica.Blocks.Interfaces.RealInput KLa_in_internal "Needed to connect to conditional connector"; 39 | Modelica.Blocks.Interfaces.RealInput m_S_in_internal[Medium.nC_S]; 40 | Modelica.Blocks.Interfaces.RealInput m_X_in_internal[Medium.nC_X]; 41 | Real[Medium.nC_S] mC_S_scaled(each min = -1e-5) "Scaled masses of dissolved substances in the fluid"; 42 | Real[Medium.nC_S] reactionRate_S "Reaction rates (unscaled) of dissolved substances in the fluid"; 43 | Real[Medium.nC_X] mC_X_scaled(each min = -1e-5) "Scaled masses of particulate substances in the fluid"; 44 | Real[Medium.nC_X] reactionRate_X "Reaction rates (unscaled) of particulate substances in the fluid"; 45 | 46 | equation 47 | mC_S = (m/medium.d)*C_S*Vf/V; 48 | mC_S_scaled = mC_S ./ Medium.C_S_nominal; 49 | mC_X = (m/medium.d)*C_X*Vf/V; 50 | mC_X_scaled = mC_X ./ Medium.C_X_nominal; 51 | for i in 1:nPorts loop 52 | ports[i].C_S_outflow = C_S; 53 | ports_mC_S_flow[i, :] = ports[i].m_flow / portInDensities[i] * actualStream(ports[i].C_S_outflow) "Dissolved substance mass flow"; 54 | ports[i].C_X_outflow = C_X; 55 | ports_mC_X_flow[i, :] = ports[i].m_flow / portInDensities[i] * actualStream(ports[i].C_X_outflow) "Particulate mass flow"; 56 | end for; 57 | for i in 1:Medium.nC_S loop 58 | sum_ports_mC_S_flow[i] = sum(ports_mC_S_flow[:, i]); 59 | end for; 60 | for i in 1:Medium.nC_X loop 61 | sum_ports_mC_X_flow[i] = sum(ports_mC_X_flow[:, i]); 62 | end for; 63 | for i in S loop 64 | if i == S.O then 65 | J_gas[Integer(i)] = Vf * KLa_in_internal * ((14.53 - 0.411 * to_degC(medium.T) + 9.6e-3 * to_degC(medium.T) ^ 2 - 1.2e-4 * to_degC(medium.T) ^ 3) / 1000 - C_S[Integer(i)]); 66 | elseif i == S.CO2 then 67 | J_gas[Integer(i)] = Vf * KLa_in_internal * KLa_ratio * (44*Modelica.SIunits.Conversions.to_bar(system.pCO2)*(75.14-2.605*to_degC(medium.T)+0.038*to_degC(medium.T)^2) / 1000 - C_S[Integer(i)]); // Remember the ugly /1000 here 68 | else 69 | J_gas[Integer(i)] = 0; 70 | end if; 71 | end for; 72 | // Mass balances 73 | if traceDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then 74 | // These are probably all wrong 75 | zeros(Medium.nC_S) = mbC_S_flow + reactionRate_S; 76 | zeros(Medium.nC_X) = mbC_X_flow + reactionRate_X; 77 | end if; 78 | 79 | connect(KLa_in, KLa_in_internal); 80 | if not use_KLa_in then 81 | KLa_in_internal = KLa; 82 | end if; 83 | connect(m_S_in, m_S_in_internal); 84 | if not use_m_S_in then 85 | m_S_in_internal = fill(0, Medium.nC_S); 86 | end if; 87 | connect(m_X_in, m_X_in_internal); 88 | if not use_m_X_in then 89 | m_X_in_internal = fill(0, Medium.nC_X); 90 | end if; 91 | 92 | initial equation 93 | if traceDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then 94 | mC_S_scaled = m / medium.d * C_S_start[1:Medium.nC_S] * (Vf/V) ./ Medium.C_S_nominal; 95 | mC_X_scaled = m / medium.d * C_X_start[1:Medium.nC_X] * (Vf/V) ./ Medium.C_X_nominal; 96 | elseif traceDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then 97 | der(mC_S_scaled) = zeros(Medium.nC_S); 98 | der(mC_X_scaled) = zeros(Medium.nC_X); 99 | end if; 100 | end PartialCST; 101 | -------------------------------------------------------------------------------- /LibRAS/Tanks/PartialTank.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Tanks; 2 | partial model PartialTank "Volume of fixed size, closed to the ambient, with inlet/outlet ports" 3 | 4 | import Modelica.Constants.pi; 5 | import SI = Modelica.SIunits; 6 | 7 | // Mass and energy balance, ports 8 | // extends Tanks.PartialLumpedVessel(final fluidVolume = V, vesselArea = pi * (3 / 4 * V) ^ (2 / 3), heatTransfer(surfaceAreas = {4 * pi * (3 / 4 * V / pi) ^ (2 / 3)})); 9 | replaceable package Medium = LibRAS.Media.WasteWater "Medium in the component"; 10 | parameter SI.Volume V "Volume"; 11 | equation 12 | Wb_flow = 0; 13 | for i in 1:nPorts loop 14 | vessel_ps_static[i] = medium.p; 15 | end for; 16 | 17 | mbC_S_flow = sum_ports_mC_S_flow + m_S_in_internal; 18 | mbC_X_flow = sum_ports_mC_X_flow + m_X_in_internal; 19 | 20 | annotation(defaultComponentName = "volume", Icon(coordinateSystem(preserveAspectRatio = true, extent = {{-100, -100}, {100, 100}}), graphics = {Ellipse(extent = {{-100, 100}, {100, -100}}, lineColor = {0, 0, 0}, fillPattern = FillPattern.Sphere, fillColor = {170, 213, 255}), Text(extent = {{-150, 12}, {150, -18}}, lineColor = {0, 0, 0}, textString = "V=%V")})); 21 | end PartialTank; 22 | -------------------------------------------------------------------------------- /LibRAS/Tanks/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Tanks 3 | extends Modelica.Icons.VariantsPackage; 4 | end Tanks; 5 | -------------------------------------------------------------------------------- /LibRAS/Tanks/package.order: -------------------------------------------------------------------------------- 1 | CST 2 | CSBR 3 | OpenTank 4 | OpenCSBR 5 | PartialTank 6 | PartialCST 7 | PartialCSBR 8 | PartialLumpedVessel 9 | FishTank 10 | -------------------------------------------------------------------------------- /LibRAS/Types/ProcessData.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Types; 2 | 3 | package ProcessData 4 | record ProcessMatrix "ASM conversion matrix" 5 | parameter Real[2] _mu_H; 6 | parameter Real[2] _K_S; 7 | parameter Real[2] _K_OH; 8 | parameter Real[2] _K_NO; 9 | parameter Real[2] _b_H; 10 | parameter Real[2] _mu_A; 11 | parameter Real[2] _mu_AOB; 12 | parameter Real[2] _mu_NOB; 13 | parameter Real[2] _K_NH; 14 | parameter Real[2] _K_OA; 15 | parameter Real[2] _b_A; 16 | parameter Real[2] _b_AOB; 17 | parameter Real[2] _b_NOB; 18 | parameter Real[2] _nu_g; 19 | parameter Real[2] _nu_NO2; 20 | parameter Real[2] _nu_NO3; 21 | parameter Real[2] _k_a; 22 | parameter Real[2] _k_h; 23 | parameter Real[2] _K_X; 24 | parameter Real[2] _nu_h; 25 | parameter Real[2] _Y_H; 26 | parameter Real[2] _Y_A; 27 | parameter Real[2] _Y_AOB; 28 | parameter Real[2] _Y_NOB; 29 | parameter Real[2] _f_p; 30 | parameter Real[2] _i_XB; 31 | parameter Real[2] _i_XP; 32 | parameter Real[2] _K_Alk; 33 | parameter Real[2] _K_NHH; 34 | parameter Real[2] _K_NHI; 35 | constant Modelica.SIunits.Conversions.NonSIunits.Temperature_degC T0[2] = {10.0, 20.0}; 36 | parameter Modelica.SIunits.Conversions.NonSIunits.Temperature_degC T = 15 "Operating temperature"; 37 | 38 | // Correlate biofilm parameters to temperature. Adapt units from "standard ASM" to SI. 39 | parameter Real mu_H = _mu_H [2]*((_mu_H [2]/_mu_H [1])^0.1)^(T-T0[2]) /(24*3600); 40 | parameter Real K_S = _K_S [2]*((_K_S [2]/_K_S [1])^0.1)^(T-T0[2]) * 1e-3; 41 | parameter Real K_OH = _K_OH [2]*((_K_OH [2]/_K_OH [1])^0.1)^(T-T0[2]) * 1e-3; 42 | parameter Real K_NO = _K_NO [2]*((_K_NO [2]/_K_NO [1])^0.1)^(T-T0[2]) * 1e-3; 43 | parameter Real b_H = _b_H [2]*((_b_H [2]/_b_H [1])^0.1)^(T-T0[2]) /(24*3600); 44 | parameter Real mu_A = _mu_A [2]*((_mu_A [2]/_mu_A [1])^0.1)^(T-T0[2]) /(24*3600); 45 | parameter Real mu_AOB= _mu_AOB[2]*((_mu_AOB[2]/_mu_AOB[1])^0.1)^(T-T0[2]) /(24*3600); 46 | parameter Real mu_NOB= _mu_NOB[2]*((_mu_NOB[2]/_mu_NOB[1])^0.1)^(T-T0[2]) /(24*3600); 47 | parameter Real K_NH = _K_NH [2]*((_K_NH [2]/_K_NH [1])^0.1)^(T-T0[2]) * 1e-3; 48 | parameter Real K_OA = _K_OA [2]*((_K_OA [2]/_K_OA [1])^0.1)^(T-T0[2]) * 1e-3; 49 | parameter Real b_A = _b_A [2]*((_b_A [2]/_b_A [1])^0.1)^(T-T0[2]) /(24*3600); 50 | parameter Real b_AOB = _b_AOB[2]*((_b_AOB[2]/_b_AOB[1])^0.1)^(T-T0[2]) /(24*3600); 51 | parameter Real b_NOB = _b_NOB[2]*((_b_NOB[2]/_b_NOB[1])^0.1)^(T-T0[2]) /(24*3600); 52 | parameter Real nu_g = _nu_g [2]*((_nu_g [2]/_nu_g [1])^0.1)^(T-T0[2]); 53 | parameter Real nu_NO2= _nu_NO2[2]*((_nu_NO2[2]/_nu_NO2[1])^0.1)^(T-T0[2]); 54 | parameter Real nu_NO3= _nu_NO3[2]*((_nu_NO3[2]/_nu_NO3[1])^0.1)^(T-T0[2]); 55 | parameter Real k_a = _k_a [2]*((_k_a [2]/_k_a [1])^0.1)^(T-T0[2]) /(24*3600) * 1000; 56 | parameter Real k_h = _k_h [2]*((_k_h [2]/_k_h [1])^0.1)^(T-T0[2]) /(24*3600); 57 | parameter Real K_X = _K_X [2]*((_K_X [2]/_K_X [1])^0.1)^(T-T0[2]); 58 | parameter Real nu_h = _nu_h [2]*((_nu_h [2]/_nu_h [1])^0.1)^(T-T0[2]); 59 | parameter Real Y_H = _Y_H [2]*((_Y_H [2]/_Y_H [1])^0.1)^(T-T0[2]); 60 | parameter Real Y_A = _Y_A [2]*((_Y_A [2]/_Y_A [1])^0.1)^(T-T0[2]); 61 | parameter Real Y_AOB = _Y_AOB[2]*((_Y_AOB[2]/_Y_AOB[1])^0.1)^(T-T0[2]); 62 | parameter Real Y_NOB = _Y_NOB[2]*((_Y_NOB[2]/_Y_NOB[1])^0.1)^(T-T0[2]); 63 | parameter Real f_p = _f_p [2]*((_f_p [2]/_f_p [1])^0.1)^(T-T0[2]); 64 | parameter Real i_XB = _i_XB [2]*((_i_XB [2]/_i_XB [1])^0.1)^(T-T0[2]); 65 | parameter Real i_XP = _i_XP [2]*((_i_XP [2]/_i_XP [1])^0.1)^(T-T0[2]); 66 | parameter Real K_Alk = _K_Alk[2]*((_K_Alk[2]/_K_Alk[1])^0.1)^(T-T0[2]) * 1e-3; 67 | parameter Real K_NHH = _K_NHH[2]*((_K_NHH[2]/_K_NHH[1])^0.1)^(T-T0[2]) * 1e-3; // Heterotrophs ammonia monod constant 68 | parameter Real K_NHI = _K_NHI[2]*((_K_NHI[2]/_K_NHI[1])^0.1)^(T-T0[2]) * 1e-3; 69 | 70 | /* R 71 | 1 Aerobic growth of heterotrophs 72 | 2 Anoxic growth of heterotrophs on NO2 73 | 3 Anoxic growth of heterotrophs on NO3 74 | 4 Aerobic growth of AOB 75 | 5 Aerobic growth of NOB 76 | 6 Decay of heterotrophs 77 | 7 Decay of AOB 78 | 8 Decay of NOB 79 | 9 Ammonification of SND 80 | 10 Hydrolysis of XS 81 | 11 Hydrolysis of XND 82 | */ 83 | 84 | parameter Real SoluteReactions[Species.S, :] = { // Make sure we keep the order defined in Types.Species.S 85 | // r1 DN-NO2 DN-NO3 N-AOB N-NOB r6 r7 r8 r9 r10 r11 86 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // I 87 | { -1/Y_H, -1/Y_H, -1/Y_H, 0, 0, 0, 0, 0, 0, 1, 0}, // S 88 | { 1-(1/Y_H), 0, 0, (Y_AOB-3.43)/Y_AOB, (Y_NOB-1.14)/Y_NOB, 0, 0, 0, 0, 0, 0}, // O 89 | { 0, -(1-Y_H)/(1.72*Y_H), 0, 1/Y_AOB, -1/Y_NOB, 0, 0, 0, 0, 0, 0}, // NO2 90 | { 0, 0, -(1-Y_H)/(2.86*Y_H), 0, 1/Y_NOB, 0, 0, 0, 0, 0, 0}, // NO3 91 | { -i_XB, -i_XB, -i_XB, -i_XB - 1/Y_AOB, -i_XB, 0, 0, 0, 1, 0, 0}, // NH 92 | { 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1}, // ND 93 | { -i_XB/14, (1-Y_H)/(14*1.72*Y_H)-i_XB/14, (1-Y_H)/(14*2.86*Y_H)-i_XB/14, -i_XB/14-1/(7*Y_AOB), -i_XB/14, 0, 0, 0, 1/14, 0, 0}, // Alk 94 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // CO2 95 | { 0, (1-Y_H)/(1.72*Y_H), (1-Y_H)/(2.86*Y_H), 0, 0, 0, 0, 0, 0, 0, 0} // N2 96 | }; 97 | 98 | parameter Real ParticulateReactions[Species.X, :] = { // Make sure we keep the order defined in Types.Species.X 99 | // r1 DN-NO2 DN-NO3 N-AOB N-NOB r6 r7 r8 r9 r10 r11 100 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // I 101 | { 0, 0, 0, 0, 0, 1-f_p, 1-f_p, 1-f_p, 0, -1, 0}, // S 102 | { 1, 1, 1, 0, 0, -1, 0, 0, 0, 0, 0}, // BH 103 | { 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0}, // AOB 104 | { 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0}, // NOB 105 | { 0, 0, 0, 0, 0, f_p, f_p, f_p, 0, 0, 0}, // p 106 | { 0, 0, 0, 0, 0, i_XB-f_p*i_XP, i_XB-f_p*i_XP, i_XB-f_p*i_XP, 0, 0, -1} // ND 107 | }; 108 | 109 | end ProcessMatrix; 110 | end ProcessData; 111 | -------------------------------------------------------------------------------- /LibRAS/Types/Species.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Types; 2 | 3 | package Species 4 | type S = enumeration(I "Soluble inert material", S "Soluble easily biodegradable organics", O "Dissolved oxygen", NO2 "Nitrite", NO3 "Nitrate", NH "Ammonium", ND "Soluble organic nitrogen", Alk "Alkalinity", CO2 "Carbon dioxide", N2 "Nitrogen gas"); 5 | type X = enumeration(I "Particulate inert material", S "Slowly biodegradable organics", BH "Heterotrophic biomass", AOB "Autotrophic AOB biomass", NOB "Autotrophic NOB biomass", p "Particulate decay products", ND "Particulate organic nitrogen"); 6 | end Species; 7 | -------------------------------------------------------------------------------- /LibRAS/Types/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | package Types 3 | extends Modelica.Icons.TypesPackage; 4 | end Types; 5 | -------------------------------------------------------------------------------- /LibRAS/Types/package.order: -------------------------------------------------------------------------------- 1 | Species 2 | ProcessData 3 | -------------------------------------------------------------------------------- /LibRAS/Units/GrowthRate.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Units; 2 | 3 | type GrowthRate = Real (final quantity="TimeAging", final unit="1/d") "Growth rate of biomass" annotation(absoluteValue=true); -------------------------------------------------------------------------------- /LibRAS/Units/MassConcentration.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Units; 2 | type MassConcentration = Real (final quantity="MassConcentration", final unit="g/m3"); -------------------------------------------------------------------------------- /LibRAS/Units/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | 3 | package Units 4 | extends Modelica.Icons.Package; 5 | annotation (Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100, 6 | -100},{100,100}}), graphics={ 7 | Line( 8 | points={{-66,78},{-66,-40}}, 9 | color={64,64,64}, 10 | smooth=Smooth.None), 11 | Ellipse( 12 | extent={{12,36},{68,-38}}, 13 | lineColor={64,64,64}, 14 | fillColor={175,175,175}, 15 | fillPattern=FillPattern.Solid), 16 | Rectangle( 17 | extent={{-74,78},{-66,-40}}, 18 | lineColor={64,64,64}, 19 | fillColor={175,175,175}, 20 | fillPattern=FillPattern.Solid), 21 | Polygon( 22 | points={{-66,-4},{-66,6},{-16,56},{-16,46},{-66,-4}}, 23 | lineColor={64,64,64}, 24 | smooth=Smooth.None, 25 | fillColor={175,175,175}, 26 | fillPattern=FillPattern.Solid), 27 | Polygon( 28 | points={{-46,16},{-40,22},{-2,-40},{-10,-40},{-46,16}}, 29 | lineColor={64,64,64}, 30 | smooth=Smooth.None, 31 | fillColor={175,175,175}, 32 | fillPattern=FillPattern.Solid), 33 | Ellipse( 34 | extent={{22,26},{58,-28}}, 35 | lineColor={64,64,64}, 36 | fillColor={255,255,255}, 37 | fillPattern=FillPattern.Solid), 38 | Polygon( 39 | points={{68,2},{68,-46},{64,-60},{58,-68},{48,-72},{18,-72},{18,-64}, 40 | {46,-64},{54,-60},{58,-54},{60,-46},{60,-26},{64,-20},{68,-6},{68, 41 | 2}}, 42 | lineColor={64,64,64}, 43 | smooth=Smooth.Bezier, 44 | fillColor={175,175,175}, 45 | fillPattern=FillPattern.Solid)})); 46 | end Units; -------------------------------------------------------------------------------- /LibRAS/Units/package.order: -------------------------------------------------------------------------------- 1 | GrowthRate 2 | MassConcentration 3 | -------------------------------------------------------------------------------- /LibRAS/Utilities/logisticInterpolation.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Utilities; 2 | 3 | function logisticInterpolation 4 | input Real lower; 5 | input Real upper; 6 | input Real x; 7 | input Real k; 8 | input Real x0; 9 | output Real y; 10 | protected 11 | Real logistic; 12 | algorithm 13 | logistic := exp(-k*(x-x0)); 14 | y := (upper + lower*logistic) / (1 + logistic); 15 | end logisticInterpolation; -------------------------------------------------------------------------------- /LibRAS/Utilities/oxygenSaturation.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Utilities; 2 | 3 | function oxygenSaturation 4 | import to_degC = Modelica.SIunits.Conversions.to_degC; 5 | input Modelica.SIunits.Temperature T; 6 | output Modelica.SIunits.MassConcentration C_O2_sat; 7 | algorithm 8 | C_O2_sat := ((14.53 - 0.411 * to_degC(T) + 9.6e-3 * to_degC(T) ^ 2 - 1.2e-4 * to_degC(T) ^ 3) / 1000); 9 | end oxygenSaturation; -------------------------------------------------------------------------------- /LibRAS/Utilities/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | 3 | package Utilities 4 | extends Modelica.Icons.Package; 5 | annotation ( 6 | Icon(coordinateSystem(extent={{-100.0,-100.0},{100.0,100.0}}), graphics={ 7 | Polygon( 8 | origin={1.3835,-4.1418}, 9 | rotation=45.0, 10 | fillColor={64,64,64}, 11 | pattern=LinePattern.None, 12 | fillPattern=FillPattern.Solid, 13 | points={{-15.0,93.333},{-15.0,68.333},{0.0,58.333},{15.0,68.333},{15.0,93.333},{20.0,93.333},{25.0,83.333},{25.0,58.333},{10.0,43.333},{10.0,-41.667},{25.0,-56.667},{25.0,-76.667},{10.0,-91.667},{0.0,-91.667},{0.0,-81.667},{5.0,-81.667},{15.0,-71.667},{15.0,-61.667},{5.0,-51.667},{-5.0,-51.667},{-15.0,-61.667},{-15.0,-71.667},{-5.0,-81.667},{0.0,-81.667},{0.0,-91.667},{-10.0,-91.667},{-25.0,-76.667},{-25.0,-56.667},{-10.0,-41.667},{-10.0,43.333},{-25.0,58.333},{-25.0,83.333},{-20.0,93.333}}), 14 | Polygon( 15 | origin={10.1018,5.218}, 16 | rotation=-45.0, 17 | fillColor={255,255,255}, 18 | fillPattern=FillPattern.Solid, 19 | points={{-15.0,87.273},{15.0,87.273},{20.0,82.273},{20.0,27.273},{10.0,17.273},{10.0,7.273},{20.0,2.273},{20.0,-2.727},{5.0,-2.727},{5.0,-77.727},{10.0,-87.727},{5.0,-112.727},{-5.0,-112.727},{-10.0,-87.727},{-5.0,-77.727},{-5.0,-2.727},{-20.0,-2.727},{-20.0,2.273},{-10.0,7.273},{-10.0,17.273},{-20.0,27.273},{-20.0,82.273}})}) 20 | ); 21 | end Utilities; -------------------------------------------------------------------------------- /LibRAS/Utilities/package.order: -------------------------------------------------------------------------------- 1 | oxygenSaturation 2 | logisticInterpolation 3 | -------------------------------------------------------------------------------- /LibRAS/Valves/ValveLinear.mo: -------------------------------------------------------------------------------- 1 | within LibRAS.Valves; 2 | 3 | model ValveLinear "Valve for water/steam flows with linear pressure drop" 4 | extends LibRAS.Interfaces.PartialTwoPortTransport; 5 | parameter Modelica.SIunits.AbsolutePressure dp_nominal 6 | "Nominal pressure drop at full opening" 7 | annotation(Dialog(group="Nominal operating point")); 8 | parameter Medium.MassFlowRate m_flow_nominal 9 | "Nominal mass flowrate at full opening"; 10 | final parameter Modelica.Fluid.Types.HydraulicConductance k = m_flow_nominal/dp_nominal 11 | "Hydraulic conductance at full opening"; 12 | Modelica.Blocks.Interfaces.RealInput opening(min=0,max=1) 13 | "=1: completely open, =0: completely closed" 14 | annotation (Placement(transformation( 15 | origin={0,90}, 16 | extent={{-20,-20},{20,20}}, 17 | rotation=270), iconTransformation( 18 | extent={{-20,-20},{20,20}}, 19 | rotation=270, 20 | origin={0,80}))); 21 | 22 | equation 23 | m_flow = opening*k*dp; 24 | 25 | // Isenthalpic state transformation (no storage and no loss of energy) 26 | port_a.h_outflow = inStream(port_b.h_outflow); 27 | port_b.h_outflow = inStream(port_a.h_outflow); 28 | 29 | annotation ( 30 | Icon(coordinateSystem( 31 | preserveAspectRatio=true, 32 | extent={{-100,-100},{100,100}}), graphics={ 33 | Line(points={{0,50},{0,0}}), 34 | Rectangle( 35 | extent={{-20,60},{20,50}}, 36 | lineColor={0,0,0}, 37 | fillColor={0,0,0}, 38 | fillPattern=FillPattern.Solid), 39 | Polygon( 40 | points={{-100,50},{100,-50},{100,50},{0,0},{-100,-50},{-100,50}}, 41 | fillColor={255,255,255}, 42 | fillPattern=FillPattern.Solid), 43 | Polygon( 44 | points=DynamicSelect({{-100,0},{100,-0},{100,0},{0,0},{-100,-0},{-100, 45 | 0}}, {{-100,50*opening},{-100,50*opening},{100,-50*opening},{ 46 | 100,50*opening},{0,0},{-100,-50*opening},{-100,50*opening}}), 47 | fillColor={0,255,0}, 48 | lineColor={255,255,255}, 49 | fillPattern=FillPattern.Solid), 50 | Polygon(points={{-100,50},{100,-50},{100,50},{0,0},{-100,-50},{-100, 51 | 50}}, lineColor={0,0,0})}), 52 | Documentation(info=" 53 |

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.

55 |

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 | 64 | ")); 65 | end ValveLinear; 66 | -------------------------------------------------------------------------------- /LibRAS/Valves/package.mo: -------------------------------------------------------------------------------- 1 | within LibRAS; 2 | 3 | package Valves 4 | extends Modelica.Icons.VariantsPackage; 5 | //End valves 6 | end Valves; 7 | -------------------------------------------------------------------------------- /LibRAS/Valves/package.order: -------------------------------------------------------------------------------- 1 | ValveLinear 2 | -------------------------------------------------------------------------------- /LibRAS/package.mo: -------------------------------------------------------------------------------- 1 | within; 2 | package LibRAS 3 | extends Modelica.Icons.Library; 4 | annotation(uses(Modelica(version="3.2.2"))); 5 | end LibRAS; 6 | -------------------------------------------------------------------------------- /LibRAS/package.order: -------------------------------------------------------------------------------- 1 | Valves 2 | Utilities 3 | Units 4 | Types 5 | Tanks 6 | System 7 | Sources 8 | Sensors 9 | Pipes 10 | Media 11 | Machines 12 | Interfaces 13 | Examples 14 | Culture 15 | Blocks 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![#LibRAS](https://user-images.githubusercontent.com/8419918/31118741-bbecef3e-a82e-11e7-9c76-c6729b9389be.png) 2 | 3 | ## The Recirculating Aquaculture Simulator, second edition. 4 | 5 | This is a project within the Automatic Control group at Chalmers University of Technology. It is now supposed to be fully functioning, but still has a long way to go. 6 | 7 | ### Requirements 8 | 9 | + [OpenModelica](https://openmodelica.org/), version 1.12 (1.13 is broken) 10 | + Dependencies 11 | 12 | Tested and developed on Lubuntu. 13 | 14 | ### Author 15 | Simon Pedersen\ 16 | Automatic Control group, Systems and Control division\ 17 | Department of Electrical Engineering\ 18 | Chalmers University of Technology\ 19 | [pesimon@chalmers.se](mailto:pesimon@chalmers.se) 20 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FishSim/LibRAS/fca9de50a484a2213f3ca1b39e275c237c471688/logo.png --------------------------------------------------------------------------------