IEA EBC Annex 60 EBC logo

Annex60.Fluid.Movers.BaseClasses

Package with base classes for Annex60.Fluid.Movers

Information

This package contains base classes that are used to construct the models in Annex60.Fluid.Movers.

Extends from Modelica.Icons.BasesPackage (Icon for packages containing base classes).

Package Content

Name Description
Annex60.Fluid.Movers.BaseClasses.FlowMachineInterface FlowMachineInterface Partial model with performance curves for fans or pumps
Annex60.Fluid.Movers.BaseClasses.IdealSource IdealSource Base class for pressure and mass flow source with optional power input
Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine PartialFlowMachine Partial model to interface fan or pump models with the medium
Annex60.Fluid.Movers.BaseClasses.PowerInterface PowerInterface Partial model to compute power draw and heat dissipation of fans and pumps
Annex60.Fluid.Movers.BaseClasses.Characteristics Characteristics Functions for fan or pump characteristics
Annex60.Fluid.Movers.BaseClasses.Types Types Package with type definitions
Annex60.Fluid.Movers.BaseClasses.Validation Validation Collection of validation models

Annex60.Fluid.Movers.BaseClasses.FlowMachineInterface Annex60.Fluid.Movers.BaseClasses.FlowMachineInterface

Partial model with performance curves for fans or pumps

Annex60.Fluid.Movers.BaseClasses.FlowMachineInterface

Information

This is an interface that implements the functions to compute the head, power draw and efficiency of fans and pumps.

The nominal hydraulic characteristic (volume flow rate versus total pressure) is given by a set of data points using the data record per, which is an instance of Annex60.Fluid.Movers.Data.Generic. A cubic hermite spline with linear extrapolation is used to compute the performance at other operating points.

The fan or pump energy balance can be specified in two alternative ways:

For exceptions to this general rule, check the User's Guide for more information.

Implementation

For numerical reasons, the user-provided data points for volume flow rate versus pressure rise are modified to add a fan internal flow resistance. Because this flow resistance is subtracted during the simulation when computing the fan pressure rise, the model reproduces the exact points that were provided by the user.

Also for numerical reasons, the pressure rise at zero flow rate and the flow rate at zero pressure rise is added to the user-provided data, unless the user already provides these data points. Since Modelica 3.2 does not allow dynamic memory allocation, this implementation required the use of three different arrays for the situation where no additional point is added, where one additional point is added and where two additional points are added. The parameter curve causes the correct data record to be used during the simulation.

Extends from Modelica.Blocks.Interfaces.BlockIcon (This icon will be removed in future Modelica versions, use Modelica.Blocks.Icons.Block instead.).

Parameters

TypeNameDefaultDescription
Genericper Record with performance data
PrescribedVariablepreVarAnnex60.Fluid.Movers.BaseCla...Type of prescribed variable
BooleancomputePowerUsingSimilarityLaws = true, compute power exactly, using similarity laws. Otherwise approximate.
Densityrho_default Fluid density at medium default state [kg/m3]
BooleanhaveVMax Flag, true if user specified data that contain V_flow_max
VolumeFlowRateV_flow_max Maximum volume flow rate, used for smoothing [m3/s]
IntegernOri Number of data points for pressure curve
Advanced
BooleanhomotopyInitializationtrue= true, use homotopy method

Connectors

TypeNameDescription
input RealInputy_inPrescribed mover speed [1]
output RealOutputy_outMover speed (prescribed or computed) [1]
input RealInputm_flowMass flow rate [kg/s]
input RealInputrhoMedium density [kg/m3]
output RealOutputV_flowVolume flow rate [m3/s]
input RealInputdp_inPrescribed pressure increase [Pa]
output RealOutputdpPressure increase (computed or prescribed) [Pa]
output RealOutputWFloFlow work [W]
output RealOutputPEleElectrical power consumed [W]
output RealOutputetaOverall efficiency [1]
output RealOutputetaHydHydraulic efficiency [1]
output RealOutputetaMotMotor efficiency [1]
output RealOutputr_NRatio N_actual/N_nominal [1]

Modelica definition

model FlowMachineInterface "Partial model with performance curves for fans or pumps" extends Modelica.Blocks.Interfaces.BlockIcon; import cha = Annex60.Fluid.Movers.BaseClasses.Characteristics; parameter Annex60.Fluid.Movers.Data.Generic per "Record with performance data"; parameter Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable preVar= Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed "Type of prescribed variable"; parameter Boolean computePowerUsingSimilarityLaws "= true, compute power exactly, using similarity laws. Otherwise approximate."; final parameter Modelica.SIunits.VolumeFlowRate V_flow_nominal= per.pressure.V_flow[nOri] "Nominal volume flow rate, used for homotopy"; parameter Modelica.SIunits.Density rho_default "Fluid density at medium default state"; parameter Boolean haveVMax "Flag, true if user specified data that contain V_flow_max"; parameter Modelica.SIunits.VolumeFlowRate V_flow_max "Maximum volume flow rate, used for smoothing"; parameter Integer nOri(min=1) "Number of data points for pressure curve"; parameter Boolean homotopyInitialization = true "= true, use homotopy method"; // Normalized speed Modelica.Blocks.Interfaces.RealInput y_in(final unit="1") if preSpe "Prescribed mover speed"; Modelica.Blocks.Interfaces.RealOutput y_out( final unit="1") "Mover speed (prescribed or computed)"; Modelica.Blocks.Interfaces.RealInput m_flow( final quantity="MassFlowRate", final unit="kg/s") "Mass flow rate"; Modelica.Blocks.Interfaces.RealInput rho( final quantity="Density", final unit="kg/m3", min=0.0) "Medium density"; Modelica.Blocks.Interfaces.RealOutput V_flow( quantity="VolumeFlowRate", final unit="m3/s") "Volume flow rate"; Modelica.Blocks.Interfaces.RealInput dp_in( quantity="PressureDifference", final unit="Pa") if prePre "Prescribed pressure increase"; Modelica.Blocks.Interfaces.RealOutput dp( quantity="Pressure", final unit="Pa") if not prePre "Pressure increase (computed or prescribed)"; Modelica.Blocks.Interfaces.RealOutput WFlo( quantity="Power", final unit="W") "Flow work"; Modelica.Blocks.Interfaces.RealOutput PEle( quantity="Power", final unit="W") "Electrical power consumed"; Modelica.Blocks.Interfaces.RealOutput eta( final quantity="Efficiency", final unit="1") "Overall efficiency"; Modelica.Blocks.Interfaces.RealOutput etaHyd( final quantity="Efficiency", final unit="1") "Hydraulic efficiency"; Modelica.Blocks.Interfaces.RealOutput etaMot( final quantity="Efficiency", final unit="1") "Motor efficiency"; // "Shaft rotational speed"; Modelica.Blocks.Interfaces.RealOutput r_N(unit="1") "Ratio N_actual/N_nominal"; Real r_V(start=1, unit="1") "Ratio V_flow/V_flow_max"; protected final parameter Boolean preSpe= preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed "True if speed is a prescribed variable of this block"; final parameter Boolean prePre= preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.PressureDifference or preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.FlowRate "True if pressure head is a prescribed variable of this block"; // Derivatives for cubic spline final parameter Real motDer[size(per.motorEfficiency.V_flow, 1)](each fixed=false) "Coefficients for polynomial of motor efficiency vs. volume flow rate"; final parameter Real hydDer[size(per.hydraulicEfficiency.V_flow,1)](each fixed=false) "Coefficients for polynomial of hydraulic efficiency vs. volume flow rate"; parameter Modelica.SIunits.PressureDifference dpMax(displayUnit="Pa")= if haveDPMax then per.pressure.dp[1] else per.pressure.dp[1] - ((per.pressure.dp[2] - per.pressure.dp[1])/( per.pressure.V_flow[2] - per.pressure.V_flow[1]))*per.pressure.V_flow[1] "Maximum head"; parameter Real delta = 0.05 "Small value used to for regularization and to approximate an internal flow resistance of the fan"; parameter Real kRes(min=0, unit="kg/(s.m4)") = dpMax/V_flow_max*delta^2/10 "Coefficient for internal pressure drop of fan or pump"; parameter Integer curve= if (haveVMax and haveDPMax) or (nOri == 2) then 1 elseif haveVMax or haveDPMax then 2 else 3 "Flag, used to pick the right representatio of the fan or pump pressure curve"; final parameter Annex60.Fluid.Movers.BaseClasses.Characteristics.flowParametersInternal pCur1( final n = nOri, final V_flow = if (haveVMax and haveDPMax) or (nOri == 2) then {per.pressure.V_flow[i] for i in 1:nOri} else zeros(nOri), final dp = if (haveVMax and haveDPMax) or (nOri == 2) then {(per.pressure.dp[i] + per.pressure.V_flow[i] * kRes) for i in 1:nOri} else zeros(nOri)) "Volume flow rate vs. total pressure rise with correction for pump resistance added"; parameter Annex60.Fluid.Movers.BaseClasses.Characteristics.flowParametersInternal pCur2( final n = nOri + 1, V_flow = if (haveVMax and haveDPMax) or (nOri == 2) then zeros(nOri + 1) elseif haveVMax then cat(1, {0}, {per.pressure.V_flow[i] for i in 1:nOri}) elseif haveDPMax then cat(1, { per.pressure.V_flow[i] for i in 1:nOri}, {V_flow_max}) else zeros(nOri + 1), dp = if (haveVMax and haveDPMax) or (nOri == 2) then zeros(nOri + 1) elseif haveVMax then cat(1, {dpMax}, {per.pressure.dp[i] + per.pressure.V_flow[i] * kRes for i in 1:nOri}) elseif haveDPMax then cat(1, {per.pressure.dp[i] + per.pressure.V_flow[i] * kRes for i in 1:nOri}, {0}) else zeros(nOri+1)) "Volume flow rate vs. total pressure rise with correction for pump resistance added"; parameter Annex60.Fluid.Movers.BaseClasses.Characteristics.flowParametersInternal pCur3( final n = nOri + 2, V_flow = if (haveVMax and haveDPMax) or (nOri == 2) then zeros(nOri + 2) elseif haveVMax or haveDPMax then zeros(nOri + 2) else cat(1, {0}, {per.pressure.V_flow[i] for i in 1:nOri}, {V_flow_max}), dp = if (haveVMax and haveDPMax) or (nOri == 2) then zeros(nOri + 2) elseif haveVMax or haveDPMax then zeros(nOri + 2) else cat(1, {dpMax}, {per.pressure.dp[i] + per.pressure.V_flow[i] * kRes for i in 1:nOri}, {0})) "Volume flow rate vs. total pressure rise with correction for pump resistance added"; parameter Real preDer1[nOri](each fixed=false) "Derivatives of flow rate vs. pressure at the support points"; parameter Real preDer2[nOri+1](each fixed=false) "Derivatives of flow rate vs. pressure at the support points"; parameter Real preDer3[nOri+2](each fixed=false) "Derivatives of flow rate vs. pressure at the support points"; parameter Real powDer[size(per.power.V_flow,1)]= if per.use_powerCharacteristic then Annex60.Utilities.Math.Functions.splineDerivatives( x=per.power.V_flow, y=per.power.P, ensureMonotonicity=Annex60.Utilities.Math.Functions.isMonotonic(x=per.power.P, strict=false)) else zeros(size(per.power.V_flow,1)) "Coefficients for polynomial of power vs. flow rate"; parameter Boolean haveMinimumDecrease= Modelica.Math.BooleanVectors.allTrue({(per.pressure.dp[i + 1] - per.pressure.dp[i])/(per.pressure.V_flow[i + 1] - per.pressure.V_flow[ i]) < -kRes for i in 1:nOri - 1}) "Flag used for reporting"; parameter Boolean haveDPMax = (abs(per.pressure.V_flow[1]) < Modelica.Constants.eps) "Flag, true if user specified data that contain dpMax"; Modelica.Blocks.Interfaces.RealOutput dp_internal "If dp is prescribed, use dp_in and solve for r_N, otherwise compute dp using r_N"; function getPerformanceDataAsString input Annex60.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressure "Performance data"; input Real derivative[:](unit="kg/(s.m4)") "Derivative"; input Integer minimumLength = 6 "Minimum width of result"; input Integer significantDigits = 6 "Number of significant digits"; output String str "String representation"; algorithm str :=""; for i in 1:size(derivative, 1) loop str :=str + " V_flow[" + String(i) + "]=" + String( pressure.V_flow[i], minimumLength=minimumLength, significantDigits=significantDigits) + "\t" + "dp[" + String(i) + "]=" + String( pressure.dp[i], minimumLength=minimumLength, significantDigits=significantDigits) + "\tResulting derivative dp/dV_flow = " + String( derivative[i], minimumLength=minimumLength, significantDigits=significantDigits) + "\n"; end for; end getPerformanceDataAsString; function getArrayAsString input Real array[:] "Array to be printed"; input String varName "Variable name"; input Integer minimumLength = 6 "Minimum width of result"; input Integer significantDigits = 6 "Number of significant digits"; output String str "String representation"; algorithm str :=""; for i in 1:size(array, 1) loop str :=str + " " + varName + "[" + String(i) + "]=" + String( array[i], minimumLength=minimumLength, significantDigits=significantDigits) + "\n"; end for; end getArrayAsString; initial equation // Check validity of data assert(nOri > 1, "Must have at least two data points for pressure.V_flow."); assert(Annex60.Utilities.Math.Functions.isMonotonic(x=per.pressure.V_flow, strict=true) and per.pressure.V_flow[1] > -Modelica.Constants.eps, "The fan pressure rise must be a strictly decreasing sequence with respect to the volume flow rate, with the first element for the fan pressure raise being non-zero. The following performance data have been entered: " + getArrayAsString(per.pressure.V_flow, "pressure.V_flow")); if not haveVMax then assert((per.pressure.V_flow[nOri]-per.pressure.V_flow[nOri-1]) /((per.pressure.dp[nOri]-per.pressure.dp[nOri-1]))<0, "The last two pressure points for the fan or pump performance curve must be decreasing. You need to set more reasonable parameters. Received " + getArrayAsString(per.pressure.dp, "dp")); end if; // Write warning if the volumetric flow rate versus pressure curve does not satisfy // the minimum decrease condition if (not haveMinimumDecrease) then Modelica.Utilities.Streams.print(" Warning: ======== It is recommended that the volume flow rate versus pressure relation of the fan or pump satisfies the minimum decrease condition (per.pressure.dp[i+1]-per.pressure.dp[i]) d[i] = ------------------------------------------------- < " + String(-kRes) + " (per.pressure.V_flow[i+1]-per.pressure.V_flow[i]) is " + getArrayAsString({(per.pressure.dp[i+1]-per.pressure.dp[i]) /(per.pressure.V_flow[i+1]-per.pressure.V_flow[i]) for i in 1:nOri-1}, "d") + " Otherwise, a solution to the equations may not exist if the fan or pump speed is reduced. In this situation, the solver will fail due to non-convergence and the simulation stops."); end if; // Correction for flow resistance of pump or fan if (haveVMax and haveDPMax) or (nOri == 2) then // ----- Curve 1 // V_flow_max and dpMax are provided by the user, or we only have two data points preDer1= Annex60.Utilities.Math.Functions.splineDerivatives(x=pCur1.V_flow, y=pCur1.dp); preDer2= zeros(nOri + 1); preDer3= zeros(nOri + 2); elseif haveVMax or haveDPMax then // ----- Curve 2 // V_flow_max or dpMax is provided by the user, but not both preDer1= zeros(nOri); preDer2= Annex60.Utilities.Math.Functions.splineDerivatives(x=pCur2.V_flow, y=pCur2.dp); preDer3= zeros(nOri + 2); else // ----- Curve 3 // Neither V_flow_max nor dpMax are provided by the user preDer1= zeros(nOri); preDer2= zeros(nOri + 1); preDer3= Annex60.Utilities.Math.Functions.splineDerivatives(x=pCur3.V_flow, y=pCur3.dp); end if; // Compute derivatives for cubic spline motDer = if per.use_powerCharacteristic then zeros(size(per.motorEfficiency.V_flow, 1)) elseif (size(per.motorEfficiency.V_flow, 1) == 1) then {0} else Annex60.Utilities.Math.Functions.splineDerivatives( x=per.motorEfficiency.V_flow, y=per.motorEfficiency.eta, ensureMonotonicity=Annex60.Utilities.Math.Functions.isMonotonic(x=per.motorEfficiency.eta, strict=false)); hydDer = if per.use_powerCharacteristic then zeros(size(per.hydraulicEfficiency.V_flow, 1)) elseif (size(per.hydraulicEfficiency.V_flow, 1) == 1) then {0} else Annex60.Utilities.Math.Functions.splineDerivatives(x=per.hydraulicEfficiency.V_flow, y=per.hydraulicEfficiency.eta); equation //assign values of dp and r_N, depending on which variable exists and is prescribed connect(dp_internal,dp); connect(dp_internal,dp_in); connect(r_N, y_in); y_out=r_N; V_flow = m_flow/rho; // Hydraulic equations r_V = V_flow/V_flow_max; // If the speed is not prescribed and we do not require exact power computations, we set r_N = 1. // Similarity laws are then not used, meaning the power computation is less accurate. // This however has the advantage that no non-linear algebraic loop is formed and // it allows an implementation when the pressure curve is unknown. if (computePowerUsingSimilarityLaws == false) and preVar <> Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed then r_N=1; else // For the homotopy method, we approximate dp by an equation // that is linear in V_flow, and that goes linearly to 0 as r_N goes to 0. // The three branches below are identical, except that we pass either // pCur1, pCur2 or pCur3, and preDer1, preDer2 or preDer3 if (curve == 1) then if homotopyInitialization then V_flow*kRes + dp_internal = homotopy(actual=cha.pressure( V_flow=V_flow, r_N=r_N, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer1, per=pCur1), simplified=r_N * (cha.pressure( V_flow=V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer1, per=pCur1) +(V_flow-V_flow_nominal) * (cha.pressure( V_flow=(1+delta)*V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer1, per=pCur1) -cha.pressure(V_flow=(1-delta)*V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer1, per=pCur1)) /(2*delta*V_flow_nominal))); else V_flow*kRes + dp_internal= cha.pressure(V_flow=V_flow, r_N=r_N, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer1, per=pCur1); end if; // end of computation for this branch elseif (curve == 2) then if homotopyInitialization then V_flow*kRes + dp_internal = homotopy(actual=cha.pressure( V_flow=V_flow, r_N=r_N, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer2, per=pCur2), simplified=r_N * (cha.pressure( V_flow=V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer2, per=pCur2) +(V_flow-V_flow_nominal) * (cha.pressure( V_flow=(1+delta)*V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer2, per=pCur2) -cha.pressure(V_flow=(1-delta)*V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer2, per=pCur2)) /(2*delta*V_flow_nominal))); else V_flow*kRes + dp_internal= cha.pressure(V_flow=V_flow, r_N=r_N, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer2, per=pCur2); end if; // end of computation for this branch else if homotopyInitialization then V_flow*kRes + dp_internal = homotopy(actual=cha.pressure( V_flow=V_flow, r_N=r_N, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer3, per=pCur3), simplified=r_N * (cha.pressure( V_flow=V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer3, per=pCur3) +(V_flow-V_flow_nominal)* (cha.pressure(V_flow=(1+delta)*V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer3, per=pCur3) -cha.pressure(V_flow=(1-delta)*V_flow_nominal, r_N=1, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer3, per=pCur3)) /(2*delta*V_flow_nominal))); else V_flow*kRes + dp_internal= cha.pressure(V_flow=V_flow, r_N=r_N, dpMax=dpMax, V_flow_max=V_flow_max, d=preDer3, per=pCur3); end if; // end of computation for this branch end if; // end of if/else choosing between exact/simplified power computation end if; // Flow work WFlo = dp_internal*V_flow; // Power consumption if per.use_powerCharacteristic then // For the homotopy, we want P/V_flow to be bounded as V_flow -> 0 to avoid a very high medium // temperature near zero flow. if homotopyInitialization then PEle = homotopy(actual=cha.power(per=per.power, V_flow=V_flow, r_N=r_N, d=powDer, delta=delta), simplified=V_flow/V_flow_nominal* cha.power(per=per.power, V_flow=V_flow_nominal, r_N=1, d=powDer, delta=delta)); else PEle = (rho/rho_default)*cha.power(per=per.power, V_flow=V_flow, r_N=r_N, d=powDer, delta=delta); end if; // To compute the efficiency, we set a lower bound on the electricity consumption. // This is needed because WFlo can be close to zero when P is zero, thereby // causing a division by zero. // Earlier versions of the model computed WFlo = eta * P, but this caused // a division by zero. eta = WFlo / Annex60.Utilities.Math.Functions.smoothMax(x1=PEle, x2=1E-5, deltaX=1E-6); // In this configuration, we only know the total power consumption. // Because nothing is known about etaMot versus etaHyd, we set etaHyd=1. This will // cause etaMot=eta, because eta=etaHyd*etaMot. // Earlier versions used etaMot=sqrt(eta), but as eta->0, this function has // and infinite derivative. etaHyd = 1; etaMot = eta; else if homotopyInitialization then etaHyd = homotopy(actual=cha.efficiency(per=per.hydraulicEfficiency, V_flow=V_flow, d=hydDer, r_N=r_N, delta=delta), simplified=cha.efficiency(per=per.hydraulicEfficiency, V_flow=V_flow_max, d=hydDer, r_N=r_N, delta=delta)); etaMot = homotopy(actual=cha.efficiency(per=per.motorEfficiency, V_flow=V_flow, d=motDer, r_N=r_N, delta=delta), simplified=cha.efficiency(per=per.motorEfficiency, V_flow=V_flow_max, d=motDer, r_N=r_N, delta=delta)); else etaHyd = cha.efficiency(per=per.hydraulicEfficiency, V_flow=V_flow, d=hydDer, r_N=r_N, delta=delta); etaMot = cha.efficiency(per=per.motorEfficiency, V_flow=V_flow, d=motDer, r_N=r_N, delta=delta); end if; // To compute the electrical power, we set a lower bound for eta to avoid // a division by zero. PEle = WFlo / Annex60.Utilities.Math.Functions.smoothMax(x1=eta, x2=1E-5, deltaX=1E-6); eta = etaHyd * etaMot; end if; end FlowMachineInterface;

Annex60.Fluid.Movers.BaseClasses.IdealSource Annex60.Fluid.Movers.BaseClasses.IdealSource

Base class for pressure and mass flow source with optional power input

Annex60.Fluid.Movers.BaseClasses.IdealSource

Information

Model of a fictitious pipe that is used as a base class for a pressure source or to prescribe a mass flow rate.

Note that for fans and pumps with dynamic balance, both the heat and the flow work are added to the volume of air or water. This simplifies the equations compared to adding heat to the volume, and flow work to this model.

Extends from Annex60.Fluid.Interfaces.PartialTwoPortTransport (Partial element transporting fluid between two ports without storage of mass or energy).

Parameters

TypeNameDefaultDescription
replaceable package MediumPartialMediumMedium in the component
Booleancontrol_m_flow = false to control dp instead of m_flow
Assumptions
BooleanallowFlowReversaltrue= false to simplify equations, assuming, but not enforcing, no flow reversal
Advanced
PressureDifferencedp_start0Guess value of dp = port_a.p - port_b.p [Pa]
MassFlowRatem_flow_start0Guess value of m_flow = port_a.m_flow [kg/s]
MassFlowRatem_flow_small Small mass flow rate for regularization of zero flow [kg/s]
Diagnostics
Booleanshow_Tfalse= true, if temperatures at port_a and port_b are computed
Booleanshow_V_flowtrue= true, if volume flow rate at inflowing port is computed

Connectors

TypeNameDescription
FluidPort_aport_aFluid connector a (positive design flow direction is from port_a to port_b)
FluidPort_bport_bFluid connector b (positive design flow direction is from port_a to port_b)
input RealInputm_flow_inPrescribed mass flow rate [kg/s]
input RealInputdp_inPrescribed pressure difference port_a.p-port_b.p [Pa]

Modelica definition

model IdealSource "Base class for pressure and mass flow source with optional power input" extends Annex60.Fluid.Interfaces.PartialTwoPortTransport(show_T=false); // Quantity to control parameter Boolean control_m_flow "= false to control dp instead of m_flow"; Modelica.Blocks.Interfaces.RealInput m_flow_in(unit="kg/s") if control_m_flow "Prescribed mass flow rate"; Modelica.Blocks.Interfaces.RealInput dp_in(unit="Pa") if not control_m_flow "Prescribed pressure difference port_a.p-port_b.p"; protected Modelica.Blocks.Interfaces.RealInput m_flow_internal(unit="kg/s") "Needed to connect to conditional connector"; Modelica.Blocks.Interfaces.RealInput dp_internal(unit="Pa") "Needed to connect to conditional connector"; equation // Ideal control if control_m_flow then m_flow = m_flow_internal; dp_internal = 0; else dp = dp_internal; m_flow_internal = 0; end if; connect(dp_internal, dp_in); connect(m_flow_internal, m_flow_in); // Energy balance (no storage) port_a.h_outflow = inStream(port_b.h_outflow); port_b.h_outflow = inStream(port_a.h_outflow); end IdealSource;

Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine

Partial model to interface fan or pump models with the medium

Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine

Information

This is the base model for fans and pumps. It provides an interface between the equations that compute head and power consumption, and the implementation of the energy and pressure balance of the fluid.

Optionally, the fluid volume is computed using a dynamic balance or a steady-state balance.

The parameter addPowerToMedium determines whether any power is added to the fluid. The default is addPowerToMedium=true, and hence the outlet enthalpy is higher than the inlet enthalpy if the flow device is operating. The setting addPowerToMedium=false is physically incorrect (since the flow work, the flow friction and the fan heat do not increase the enthalpy of the medium), but this setting does in some cases lead to simpler equations and more robust simulation, in particular if the mass flow is equal to zero.

Extends from Annex60.Fluid.Interfaces.LumpedVolumeDeclarations (Declarations for lumped volumes), Annex60.Fluid.Interfaces.PartialTwoPortInterface (Partial model transporting fluid between two ports without storing mass or energy).

Parameters

TypeNameDefaultDescription
replaceable package MediumPartialMediumMedium in the component
Genericperredeclare parameter Annex60....Record with performance data
BooleancomputePowerUsingSimilarityLaws = true, compute power exactly, using similarity laws. Otherwise approximate.
BooleanaddPowerToMediumtrueSet to false to avoid any power (=heat and flow work) being added to medium (may give simpler equations)
BooleannominalValuesDefineDefaultPressureCurvefalseSet to true to avoid warning if m_flow_nominal and dp_nominal are used to construct the default pressure curve
Nominal condition
MassFlowRatem_flow_nominal Nominal mass flow rate [kg/s]
Control
InputTypeinputTypeAnnex60.Fluid.Types.InputTyp...Control input type
RealconstInput0Constant input set point
RealstageInputs[:] Vector of input set points corresponding to stages
Dynamics
Equations
DynamicsenergyDynamicsModelica.Fluid.Types.Dynamic...Type of energy balance: dynamic (3 initialization options) or steady state
DynamicsmassDynamicsenergyDynamicsType of mass balance: dynamic (3 initialization options) or steady state
RealmSenFac1Factor for scaling the sensible thermal mass of the volume
Nominal condition
Timetau1Time constant of fluid volume for nominal flow, used if energy or mass balance is dynamic [s]
Filtered speed
BooleanfilteredSpeedtrue= true, if speed is filtered with a 2nd order CriticalDamping filter
TimeriseTime30Rise time of the filter (time to reach 99.6 % of the speed) [s]
InitinitModelica.Blocks.Types.Init.I...Type of initialization (no init/steady state/initial state/initial output)
Realy_start0Initial value of speed [1]
Initialization
AbsolutePressurep_startMedium.p_defaultStart value of pressure [Pa]
TemperatureT_startMedium.T_defaultStart value of temperature [K]
MassFractionX_start[Medium.nX]Medium.X_defaultStart value of mass fractions m_i/m [kg/kg]
ExtraPropertyC_start[Medium.nC]fill(0, Medium.nC)Start value of trace substances
ExtraPropertyC_nominal[Medium.nC]fill(1E-2, Medium.nC)Nominal value of trace substances. (Set to typical order of magnitude.)
Assumptions
BooleanallowFlowReversaltrue= false to simplify equations, assuming, but not enforcing, no flow reversal
Advanced
MassFlowRatem_flow_small1E-4*abs(m_flow_nominal)Small mass flow rate for regularization of zero flow [kg/s]
Diagnostics
Booleanshow_Tfalse= true, if actual temperature at port is computed

Connectors

TypeNameDescription
input IntegerInputstageStage input signal for the pressure head
output RealOutputy_actualActual normalised pump speed that is used for computations [1]
output RealOutputPElectrical power consumed [W]
HeatPort_aheatPortHeat dissipation to environment

Modelica definition

partial model PartialFlowMachine "Partial model to interface fan or pump models with the medium" extends Annex60.Fluid.Interfaces.LumpedVolumeDeclarations( final mSenFac=1); extends Annex60.Fluid.Interfaces.PartialTwoPortInterface( show_T=false, port_a( h_outflow(start=h_outflow_start)), port_b( h_outflow(start=h_outflow_start), p(start=p_start), final m_flow(max = if allowFlowReversal then +Modelica.Constants.inf else 0))); replaceable parameter Annex60.Fluid.Movers.Data.Generic per constrainedby Annex60.Fluid.Movers.Data.Generic "Record with performance data"; parameter Annex60.Fluid.Types.InputType inputType = Annex60.Fluid.Types.InputType.Continuous "Control input type"; parameter Real constInput = 0 "Constant input set point"; parameter Real stageInputs[:] "Vector of input set points corresponding to stages"; parameter Boolean computePowerUsingSimilarityLaws "= true, compute power exactly, using similarity laws. Otherwise approximate."; parameter Boolean addPowerToMedium=true "Set to false to avoid any power (=heat and flow work) being added to medium (may give simpler equations)"; parameter Boolean nominalValuesDefineDefaultPressureCurve = false "Set to true to avoid warning if m_flow_nominal and dp_nominal are used to construct the default pressure curve"; parameter Modelica.SIunits.Time tau=1 "Time constant of fluid volume for nominal flow, used if energy or mass balance is dynamic"; // Classes used to implement the filtered speed parameter Boolean filteredSpeed=true "= true, if speed is filtered with a 2nd order CriticalDamping filter"; parameter Modelica.SIunits.Time riseTime=30 "Rise time of the filter (time to reach 99.6 % of the speed)"; parameter Modelica.Blocks.Types.Init init=Modelica.Blocks.Types.Init.InitialOutput "Type of initialization (no init/steady state/initial state/initial output)"; parameter Real y_start(min=0, max=1, unit="1")=0 "Initial value of speed"; // Connectors and ports Modelica.Blocks.Interfaces.IntegerInput stage if inputType == Annex60.Fluid.Types.InputType.Stages "Stage input signal for the pressure head"; Modelica.Blocks.Interfaces.RealOutput y_actual( final unit="1") "Actual normalised pump speed that is used for computations"; Modelica.Blocks.Interfaces.RealOutput P( quantity="Power", final unit="W") "Electrical power consumed"; Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort "Heat dissipation to environment"; // Variables Modelica.SIunits.VolumeFlowRate VMachine_flow(start=_VMachine_flow) = eff.V_flow "Volume flow rate"; Modelica.SIunits.PressureDifference dpMachine(displayUnit="Pa")= -preSou.dp "Pressure difference"; Real eta(unit="1", final quantity="Efficiency") = eff.eta "Global efficiency"; Real etaHyd(unit="1", final quantity="Efficiency") = eff.etaHyd "Hydraulic efficiency"; Real etaMot(unit="1", final quantity="Efficiency") = eff.etaMot "Motor efficiency"; // Quantity to control protected final parameter Modelica.SIunits.VolumeFlowRate _VMachine_flow = 0 "Start value for VMachine_flow, used to avoid a warning if not specified"; parameter Types.PrescribedVariable preVar "Type of prescribed variable"; // The parameter speedIsInput is required to conditionally remove the instance gain. // If the conditional removal of this instance where to use the test // preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed, // then OpenModelica fails to translate the model with the message // .../PartialFlowMachine.mo:185:3-189:70:writable] // Error: Variable Types.PrescribedVariable.Speed not found in scope // Annex60.Fluid.Movers.SpeedControlled_y$floMac1. final parameter Boolean speedIsInput= (preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed) "Parameter that is true if speed is the controlled variables"; final parameter Integer nOri = size(per.pressure.V_flow, 1) "Number of data points for pressure curve"; final parameter Boolean haveVMax = (abs(per.pressure.dp[nOri]) < Modelica.Constants.eps) "Flag, true if user specified data that contain V_flow_max"; final parameter Modelica.SIunits.VolumeFlowRate V_flow_max= if per.havePressureCurve then (if haveVMax then per.pressure.V_flow[nOri] else per.pressure.V_flow[nOri] - (per.pressure.V_flow[nOri] - per.pressure.V_flow[ nOri - 1])/((per.pressure.dp[nOri] - per.pressure.dp[nOri - 1]))*per.pressure.dp[nOri]) else m_flow_nominal/rho_default "Maximum volume flow rate, used for smoothing"; final parameter Modelica.SIunits.Density rho_default= Medium.density_pTX( p=Medium.p_default, T=Medium.T_default, X=Medium.X_default) "Default medium density"; final parameter Medium.ThermodynamicState sta_start=Medium.setState_pTX( T=T_start, p=p_start, X=X_start) "Medium state at start values"; final parameter Modelica.SIunits.SpecificEnthalpy h_outflow_start = Medium.specificEnthalpy(sta_start) "Start value for outflowing enthalpy"; Modelica.Blocks.Sources.Constant[size(stageInputs, 1)] stageValues( final k=stageInputs) if inputType == Annex60.Fluid.Types.InputType.Stages "Stage input values"; Modelica.Blocks.Sources.Constant setConst( final k=constInput) if inputType == Annex60.Fluid.Types.InputType.Constant "Constant input set point"; Extractor extractor(final nin=size(stageInputs,1)) if inputType == Annex60.Fluid.Types.InputType.Stages "Stage input extractor"; Modelica.Blocks.Routing.RealPassThrough inputSwitch "Dummy connection for easy connection of input options"; Annex60.Fluid.Delays.DelayFirstOrder vol( redeclare final package Medium = Medium, final tau=tau, final energyDynamics=energyDynamics, final massDynamics=massDynamics, final T_start=T_start, final X_start=X_start, final C_start=C_start, final m_flow_nominal=m_flow_nominal, final m_flow_small=m_flow_small, final p_start=p_start, final prescribedHeatFlowRate=true, final allowFlowReversal=allowFlowReversal, nPorts=2) "Fluid volume for dynamic model"; Modelica.Blocks.Continuous.Filter filter( order=2, f_cut=5/(2*Modelica.Constants.pi*riseTime), final init=init, x(each stateSelect=StateSelect.always), final analogFilter=Modelica.Blocks.Types.AnalogFilter.CriticalDamping, final filterType=Modelica.Blocks.Types.FilterType.LowPass) if filteredSpeed "Second order filter to approximate valve opening time, and to improve numerics"; Modelica.Blocks.Math.Gain gaiSpe(y(final unit="1")) if inputType == Annex60.Fluid.Types.InputType.Continuous and speedIsInput "Gain to normalized speed using speed_nominal or speed_rpm_nominal"; Annex60.Fluid.Movers.BaseClasses.IdealSource preSou( redeclare final package Medium = Medium, final m_flow_small=m_flow_small, final allowFlowReversal=allowFlowReversal, final control_m_flow= (preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.FlowRate)) "Pressure source"; Annex60.Fluid.Movers.BaseClasses.PowerInterface heaDis( final motorCooledByFluid=per.motorCooledByFluid, final delta_V_flow=1E-3*V_flow_max) if addPowerToMedium "Heat dissipation into medium"; Modelica.Blocks.Math.Add PToMed(final k1=1, final k2=1) if addPowerToMedium "Heat and work input into medium"; Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow prePow( final alpha=0) if addPowerToMedium "Prescribed power (=heat and flow work) flow for dynamic model"; Modelica.Blocks.Sources.RealExpression rho_inlet(y= Medium.density( Medium.setState_phX(port_a.p, inStream(port_a.h_outflow), inStream(port_a.Xi_outflow)))) "Density of the inflowing fluid"; Annex60.Fluid.Sensors.MassFlowRate senMasFlo( redeclare final package Medium = Medium) "Mass flow rate sensor"; Sensors.RelativePressure senRelPre( redeclare final package Medium = Medium) "Head of mover"; // Because the speed data are not used by FlowMachineInterface, we set them // to zero. FlowMachineInterface eff( per( final hydraulicEfficiency = per.hydraulicEfficiency, final motorEfficiency = per.motorEfficiency, final motorCooledByFluid = per.motorCooledByFluid, final speed_nominal = 0, final constantSpeed = 0, final speeds = {0}, final power = per.power), final nOri = nOri, final rho_default=rho_default, final computePowerUsingSimilarityLaws=computePowerUsingSimilarityLaws, final haveVMax=haveVMax, final V_flow_max=V_flow_max, r_N(start=y_start), r_V(start=m_flow_nominal/rho_default), final preVar=preVar) "Flow machine"; protected block Extractor "Extract scalar signal out of signal vector dependent on IntegerRealInput index" extends Modelica.Blocks.Interfaces.MISO; Modelica.Blocks.Interfaces.IntegerInput index "Integer input for control input"; equation y = sum({if index == i then u[i] else 0 for i in 1:nin}); end Extractor; initial equation // The control signal is dp or m_flow but the user did not provide a pump curve. // Hence, the speed is computed using default values, which likely are wrong. // Therefore, scaling the power using the speed is inaccurate. assert(nominalValuesDefineDefaultPressureCurve or per.havePressureCurve or (preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed), "*** Warning: You are using a flow or pressure controlled mover with the default pressure curve. This leads to approximate calculations of the electrical power consumption. Add the correct pressure curve in the record per to obtain an accurate computation. Setting nominalValuesDefineDefaultPressureCurve=true will suppress this warning.", level=AssertionLevel.warning); // The control signal is dp or m_flow but the user did not provide a pump curve. // Hence, the speed is computed using default values, which likely are wrong. // In addition, the user wants to use (V_flow, P) to compute the power. // This can lead to using a power that is less than the flow work. We avoid // this by ignoring the setting of per.use_powerCharacteristics. assert(nominalValuesDefineDefaultPressureCurve or (per.havePressureCurve or (preVar == Annex60.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed)) or per.use_powerCharacteristic == false, "*** Warning: You are using a flow or pressure controlled mover with the default pressure curve and you set use_powerCharacteristic = true. Since this can cause wrong power consumption, the model will overwrite this setting and use instead use_powerCharacteristic = false. Since this causes the efficiency curve to be used, make sure that the efficiency curves in the performance record per are correct or add the pressure curve of the mover. Setting nominalValuesDefineDefaultPressureCurve=true will suppress this warning.", level=AssertionLevel.warning); equation connect(prePow.port, vol.heatPort); connect(vol.heatPort, heatPort); connect(preSou.port_b, port_b); connect(stageValues.y, extractor.u); connect(extractor.y, inputSwitch.u); connect(setConst.y, inputSwitch.u); connect(extractor.index, stage); connect(PToMed.y, prePow.Q_flow); connect(PToMed.u1, heaDis.Q_flow); connect(senRelPre.port_b, preSou.port_a); connect(senRelPre.port_a, preSou.port_b); connect(heaDis.etaHyd,eff. etaHyd); connect(heaDis.V_flow,eff. V_flow); connect(eff.PEle, heaDis.PEle); connect(eff.WFlo, heaDis.WFlo); connect(rho_inlet.y,eff. rho); connect(eff.m_flow, senMasFlo.m_flow); connect(eff.PEle, P); connect(eff.WFlo, PToMed.u2); connect(inputSwitch.y, filter.u); connect(senRelPre.p_rel, eff.dp_in); connect(eff.y_out, y_actual); connect(port_a, vol.ports[1]); connect(vol.ports[2], senMasFlo.port_a); connect(senMasFlo.port_b, preSou.port_a); end PartialFlowMachine;

Annex60.Fluid.Movers.BaseClasses.PowerInterface Annex60.Fluid.Movers.BaseClasses.PowerInterface

Partial model to compute power draw and heat dissipation of fans and pumps

Annex60.Fluid.Movers.BaseClasses.PowerInterface

Information

Block that implements the functions to compute the heat dissipation of fans and pumps. It is used by the model Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine.

Extends from Modelica.Blocks.Interfaces.BlockIcon (This icon will be removed in future Modelica versions, use Modelica.Blocks.Icons.Block instead.).

Parameters

TypeNameDefaultDescription
BooleanmotorCooledByFluid Flag, true if the motor is cooled by the fluid stream
VolumeFlowRatedelta_V_flow Factor used for setting heat input into medium to zero at very small flows [m3/s]
Advanced
BooleanhomotopyInitializationtrue= true, use homotopy method

Connectors

TypeNameDescription
input RealInputetaHydHydraulic efficiency [1]
input RealInputV_flowVolume flow rate [m3/s]
input RealInputWFloFlow work [W]
input RealInputPEleElectrical power consumed [W]
output RealOutputQ_flowHeat input from fan or pump to medium [W]

Modelica definition

model PowerInterface "Partial model to compute power draw and heat dissipation of fans and pumps" extends Modelica.Blocks.Interfaces.BlockIcon; parameter Boolean homotopyInitialization = true "= true, use homotopy method"; parameter Boolean motorCooledByFluid "Flag, true if the motor is cooled by the fluid stream"; parameter Modelica.SIunits.VolumeFlowRate delta_V_flow "Factor used for setting heat input into medium to zero at very small flows"; Modelica.Blocks.Interfaces.RealInput etaHyd( final quantity="Efficiency", final unit="1") "Hydraulic efficiency"; Modelica.Blocks.Interfaces.RealInput V_flow( final quantity="VolumeFlowRate", final unit="m3/s") "Volume flow rate"; Modelica.Blocks.Interfaces.RealInput WFlo( final quantity="Power", final unit="W") "Flow work"; Modelica.Blocks.Interfaces.RealInput PEle( final quantity="Power", final unit="W") "Electrical power consumed"; Modelica.Blocks.Interfaces.RealOutput Q_flow( quantity="Power", final unit="W") "Heat input from fan or pump to medium"; Modelica.SIunits.Power WHyd "Hydraulic power input (converted to flow work and heat)"; protected Modelica.SIunits.HeatFlowRate QThe_flow "Heat input from fan or pump to medium"; equation // Hydraulic power (transmitted by shaft), etaHyd = WFlo/WHyd etaHyd * WHyd = WFlo; // Heat input into medium QThe_flow + WFlo = if motorCooledByFluid then PEle else WHyd; // At m_flow = 0, the solver may still obtain positive values for QThe_flow. // The next statement sets the heat input into the medium to zero for very small flow rates. Q_flow = if homotopyInitialization then homotopy(actual=Annex60.Utilities.Math.Functions.regStep( y1=QThe_flow, y2=0, x=noEvent(abs(V_flow))-2*delta_V_flow, x_small=delta_V_flow), simplified=0) else Annex60.Utilities.Math.Functions.regStep( y1=QThe_flow, y2=0, x=noEvent(abs(V_flow))-2*delta_V_flow, x_small=delta_V_flow); end PowerInterface;

Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine.Extractor Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine.Extractor

Extract scalar signal out of signal vector dependent on IntegerRealInput index

Annex60.Fluid.Movers.BaseClasses.PartialFlowMachine.Extractor

Information

Extends from Modelica.Blocks.Interfaces.MISO (Multiple Input Single Output continuous control block).

Parameters

TypeNameDefaultDescription
Integernin1Number of inputs

Connectors

TypeNameDescription
input RealInputu[nin]Connector of Real input signals
output RealOutputyConnector of Real output signal
input IntegerInputindexInteger input for control input

Modelica definition

block Extractor "Extract scalar signal out of signal vector dependent on IntegerRealInput index" extends Modelica.Blocks.Interfaces.MISO; Modelica.Blocks.Interfaces.IntegerInput index "Integer input for control input"; equation y = sum({if index == i then u[i] else 0 for i in 1:nin}); end Extractor;

http://iea-annex60.org