This is an interface that implements the functions to compute the head, power draw
and efficiency of fans and pumps.
The nominal hydraulic characteristic (total pressure rise versus volume flow rate)
is given by a set of data points
using the data record per
, which is an instance of
Buildings.Fluid.Movers.Data.Generic.
A cubic hermite spline with linear extrapolation is used to compute
the performance at other operating points.
The model computes the power and efficiency items in the list below.
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.
In order to prevent the model from producing negative mover power
when either the flow rate or pressure rise is forced to be negative,
the flow work Ẇflo is constrained to be non-negative.
The regularisation starts around 0.01% of the characteristic maximum power
Ẇmax = V̇max Δpmax.
See discussions and an example of this situation in
IBPSA, #1621.
model FlowMachineInterface
extends Modelica.Blocks.Icons.Block;
import cha =
Buildings.Fluid.Movers.BaseClasses.Characteristics;
constant Boolean homotopyInitialization = true ;
parameter Buildings.Fluid.Movers.Data.Generic per
;
parameter Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable preVar=
Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed ;
parameter Boolean computePowerUsingSimilarityLaws
;
final parameter Modelica.Units.SI.VolumeFlowRate V_flow_nominal=per.pressure.V_flow[
nOri] ;
parameter Modelica.Units.SI.Density rho_default
;
final parameter Boolean haveVMax = (
abs(per.pressure.dp[nOri]) < Modelica.Constants.eps)
;
final parameter Modelica.Units.SI.VolumeFlowRate V_flow_max=
if per.V_flow_max>Modelica.Constants.eps
then per.V_flow_max
else
V_flow_nominal
;
parameter Integer nOri(min=1) ;
Modelica.Blocks.Interfaces.RealInput y_in(
final unit="1")
if preSpe
;
Modelica.Blocks.Interfaces.RealOutput y_out(
final unit="1") ;
Modelica.Blocks.Interfaces.RealInput m_flow(
final quantity="MassFlowRate",
final unit="kg/s") ;
Modelica.Blocks.Interfaces.RealInput rho(
final quantity="Density",
final unit="kg/m3",
min=0.0) ;
Modelica.Blocks.Interfaces.RealOutput V_flow(
quantity="VolumeFlowRate",
final unit="m3/s") ;
Modelica.Blocks.Interfaces.RealInput dp_in(
quantity="PressureDifference",
final unit="Pa")
if prePre ;
Modelica.Blocks.Interfaces.RealOutput dp(
quantity="Pressure",
final unit="Pa")
if not prePre ;
Modelica.Blocks.Interfaces.RealOutput WFlo(
quantity="Power",
final unit="W") ;
Modelica.Blocks.Interfaces.RealOutput WHyd(
quantity="Power",
final unit="W") ;
Modelica.Blocks.Interfaces.RealOutput PEle(
quantity="Power",
final unit="W") ;
Modelica.Blocks.Interfaces.RealOutput eta(
final quantity="Efficiency",
final unit="1",
start = 0.49) ;
Modelica.Blocks.Interfaces.RealOutput etaHyd(
final quantity="Efficiency",
final unit="1") ;
Modelica.Blocks.Interfaces.RealOutput etaMot(
final quantity="Efficiency",
final unit="1") ;
Modelica.Blocks.Interfaces.RealOutput r_N(unit="1")
;
Real r_V(start=1, unit="1") ;
protected
final parameter Boolean preSpe=
preVar == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed
;
final parameter Boolean prePre=
preVar == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.PressureDifference
or
preVar == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.FlowRate
;
final parameter Real etaDer[
size(per.efficiency.V_flow,1)]=
if not per.etaHydMet==Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate
then zeros(
size(per.efficiency.V_flow,1))
elseif (
size(per.efficiency.V_flow, 1) == 1)
then {0}
else
Buildings.Utilities.Math.Functions.splineDerivatives(
x=per.efficiency.V_flow,
y=per.efficiency.eta,
ensureMonotonicity=
Buildings.Utilities.Math.Functions.isMonotonic(
x=per.efficiency.eta,
strict=false))
;
final parameter Real motDer[
size(per.motorEfficiency.V_flow, 1)]=
if not per.etaMotMet==Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate
then zeros(
size(per.motorEfficiency.V_flow,1))
elseif (
size(per.motorEfficiency.V_flow, 1) == 1)
then {0}
else
Buildings.Utilities.Math.Functions.splineDerivatives(
x=per.motorEfficiency.V_flow,
y=per.motorEfficiency.eta,
ensureMonotonicity=
Buildings.Utilities.Math.Functions.isMonotonic(
x=per.motorEfficiency.eta,
strict=false))
;
final parameter Real motDer_yMot[
size(per.motorEfficiency_yMot.y,1)]=
if not per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
then zeros(
size(per.motorEfficiency_yMot.y,1))
elseif (
size(per.motorEfficiency_yMot.y,1) == 1)
then {0}
else
Buildings.Utilities.Math.Functions.splineDerivatives(
x=per.motorEfficiency_yMot.y,
y=per.motorEfficiency_yMot.eta,
ensureMonotonicity=
Buildings.Utilities.Math.Functions.isMonotonic(
x=per.motorEfficiency_yMot.eta,
strict=false))
;
final parameter Real motDer_yMot_generic[9]=
if per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve
or (per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided
and per.haveWMot_nominal)
then Buildings.Utilities.Math.Functions.splineDerivatives(
x=per.motorEfficiency_yMot_generic.y,
y=per.motorEfficiency_yMot_generic.eta,
ensureMonotonicity=true)
else zeros(9)
;
final parameter Modelica.Units.SI.PressureDifference dpMax(
displayUnit="Pa")=
per.dpMax ;
parameter Real delta = 0.05
;
parameter Real kRes(min=0, unit="kg/(s.m4)") = dpMax/V_flow_max*delta^2/10
;
parameter Integer curve=
if (haveVMax
and haveDPMax)
or (nOri == 2)
then 1
elseif haveVMax
or haveDPMax
then 2
else 3
;
final parameter Buildings.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))
;
parameter Buildings.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))
;
parameter Buildings.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}))
;
parameter Real preDer1[nOri](
each fixed=false)
;
parameter Real preDer2[nOri+1](
each fixed=false)
;
parameter Real preDer3[nOri+2](
each fixed=false)
;
parameter Real powDer[
size(per.power.V_flow,1)]=
if per.etaHydMet==
Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate
then
Buildings.Utilities.Math.Functions.splineDerivatives(
x=per.power.V_flow,
y=per.power.P,
ensureMonotonicity=
Buildings.Utilities.Math.Functions.isMonotonic(x=per.power.P,
strict=false))
else
zeros(
size(per.power.V_flow,1))
;
final parameter Buildings.Fluid.Movers.BaseClasses.Euler.powerWithDerivative powEu_internal=
if (curve == 1)
then
Buildings.Fluid.Movers.BaseClasses.Euler.power(peak=per.peak,pressure=pCur1)
elseif (curve == 2)
then
Buildings.Fluid.Movers.BaseClasses.Euler.power(peak=per.peak,pressure=pCur2)
else
Buildings.Fluid.Movers.BaseClasses.Euler.power(peak=per.peak,pressure=pCur3)
;
final parameter Buildings.Fluid.Movers.BaseClasses.Characteristics.powerParameters
powEu(
V_flow = powEu_internal.V_flow,
P = powEu_internal.P)
;
final parameter Real powEuDer[:] = powEu_internal.d
;
parameter Boolean haveMinimumDecrease=
if nOri<2
then false
else
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}) ;
parameter Boolean haveDPMax = (
abs(per.pressure.V_flow[1]) < Modelica.Constants.eps)
;
Modelica.Blocks.Interfaces.RealOutput dp_internal
;
Modelica.Units.SI.Efficiency eta_internal
;
Modelica.Units.SI.Power P_internal
;
parameter Real deltaP = 1E-4 * V_flow_max * dpMax
;
Real yMot(
final min=0,
final start=0.833)=
if per.haveWMot_nominal
then WHyd/per.WMot_nominal
else 1
;
function getPerformanceDataAsString
input Buildings.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressure
;
input Real derivative[:](unit="kg/(s.m4)") ;
input Integer minimumLength = 6 ;
input Integer significantDigits = 6 ;
output String str ;
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[:] ;
input String varName ;
input Integer minimumLength = 6 ;
input Integer significantDigits = 6 ;
output String str ;
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
assert(nOri > 1, "Must have at least two data points for pressure.V_flow.");
assert(
Buildings.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(nOri>=2,
"When the maximum flow is not specified,
at least two points are needed for the power curve.");
if nOri>=2
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's performance curve must be decreasing.
Received
" +
getArrayAsString(per.pressure.dp, "dp"));
end if;
end if;
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 satisfy 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's speed is reduced.
In this situation, the solver will fail due to non-convergence and
the simulation stops.");
end if;
if curve == 1
then
preDer1=
Buildings.Utilities.Math.Functions.splineDerivatives(x=pCur1.V_flow,
y=pCur1.dp);
preDer2=
zeros(nOri + 1);
preDer3=
zeros(nOri + 2);
elseif curve == 2
then
preDer1=
zeros(nOri);
preDer2=
Buildings.Utilities.Math.Functions.splineDerivatives(x=pCur2.V_flow,
y=pCur2.dp);
preDer3=
zeros(nOri + 2);
else
preDer1=
zeros(nOri);
preDer2=
zeros(nOri + 1);
preDer3=
Buildings.Utilities.Math.Functions.splineDerivatives(x=pCur3.V_flow,
y=pCur3.dp);
end if;
assert(
not ((per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
or per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve)
and not per.haveWMot_nominal),
"In " +
getInstanceName() + ": etaMotMet is set to
.Efficiency_MotorPartLoadRatio or .GenericCurve which requires
the motor's rated power, but per.WMot_nominal is not assigned or
cannot be estimated because no power curve is provided.");
assert(
max(per.power.P)<1E-6
or per.WMot_nominal>
max(per.power.P)*0.99,
"In " +
getInstanceName() + ": The rated motor power provided in
per.WMot_nominal is smaller than the maximum power provided in per.power.
Use a larger value for per.WMot_nominal or leave it blank to allow the
model to assume a default value.");
assert(homotopyInitialization, "In " +
getInstanceName() +
": The constant homotopyInitialization has been modified from its default
value. This constant will be removed in future releases.",
level = AssertionLevel.warning);
equation
connect(dp_internal,dp);
connect(dp_internal,dp_in);
connect(r_N, y_in);
y_out=r_N;
V_flow = m_flow / rho;
r_V = V_flow/V_flow_max;
if (computePowerUsingSimilarityLaws == false)
and preVar <> Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed
then
r_N=1;
else
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;
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;
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 if;
end if;
WFlo =
Buildings.Utilities.Math.Functions.smoothMax(
x1=dp_internal*V_flow,
x2=0,
deltaX=deltaP/2);
if per.powerOrEfficiencyIsHydraulic
then
eta = etaHyd * etaMot;
else
etaHyd =
Buildings.Utilities.Math.Functions.smoothMin(
x1=eta/etaMot, x2=1, deltaX=1E-3);
end if;
if per.powerOrEfficiencyIsHydraulic
then
P_internal=WHyd;
eta_internal=etaHyd;
PEle = WFlo /
Buildings.Utilities.Math.Functions.smoothMax(
x1=eta, x2=1E-2, deltaX=1E-3);
else
P_internal=PEle;
eta_internal=eta;
WHyd = WFlo /
Buildings.Utilities.Math.Functions.smoothMax(
x1=etaMot, x2=1E-2, deltaX=1E-3);
end if;
if per.etaHydMet==
Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate
then
if homotopyInitialization
then
P_internal =
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
P_internal = (rho/rho_default)*
cha.power(per=per.power, V_flow=V_flow, r_N=r_N, d=powDer, delta=delta);
end if;
eta_internal = WFlo/
Buildings.Utilities.Math.Functions.smoothMax(
x1=P_internal, x2=deltaP, deltaX=deltaP/2);
elseif per.etaHydMet==
Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber
then
if homotopyInitialization
then
P_internal =
homotopy(actual=
cha.power(per=powEu, V_flow=V_flow, r_N=r_N, d=powEuDer, delta=delta),
simplified=V_flow/V_flow_nominal*
cha.power(per=powEu, V_flow=V_flow_nominal, r_N=1, d=powEuDer, delta=delta));
else
P_internal = (rho/rho_default)*
cha.power(per=powEu, V_flow=V_flow, r_N=r_N, d=powEuDer, delta=delta);
end if;
eta_internal = WFlo /
Buildings.Utilities.Math.Functions.smoothMax(
x1=P_internal, x2=deltaP, deltaX=deltaP/2);
elseif per.etaHydMet == Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate
then
if homotopyInitialization
then
eta_internal =
homotopy(actual=
cha.efficiency(per=per.efficiency, V_flow=V_flow, d=etaDer, r_N=r_N, delta=delta),
simplified=
cha.efficiency(per=per.efficiency, V_flow=V_flow_max, d=etaDer, r_N=r_N, delta=delta));
else
eta_internal =
cha.efficiency(per=per.efficiency, V_flow=V_flow, d=etaDer, r_N=r_N, delta=delta);
end if;
if per.powerOrEfficiencyIsHydraulic
then
P_internal=WFlo/
Buildings.Utilities.Math.Functions.smoothMax(
x1=eta_internal, x2=1E-2, deltaX=1E-3);
else
P_internal=WHyd/
Buildings.Utilities.Math.Functions.smoothMax(
x1=eta_internal, x2=1E-2, deltaX=1E-3);
end if;
else
if per.powerOrEfficiencyIsHydraulic
then
eta_internal=0.7;
P_internal=WFlo/eta_internal;
else
eta_internal=0.49;
P_internal=WHyd/eta_internal;
end if;
end if;
if per.etaMotMet == Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate
then
if homotopyInitialization
then
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
etaMot =
cha.efficiency(per=per.motorEfficiency, V_flow=V_flow, d=motDer, r_N=r_N, delta=delta);
end if;
elseif per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
then
if homotopyInitialization
then
etaMot =
homotopy(actual=
cha.efficiency_yMot(
per=per.motorEfficiency_yMot,
y=yMot,
d=motDer_yMot), simplified=
cha.efficiency_yMot(
per=per.motorEfficiency_yMot,
y=1,
d=motDer_yMot));
else
etaMot =
cha.efficiency_yMot(
per=per.motorEfficiency_yMot,
y=yMot,
d=motDer_yMot);
end if;
elseif per.etaMotMet==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve
then
if homotopyInitialization
then
etaMot =
homotopy(actual=
cha.efficiency_yMot(
per=per.motorEfficiency_yMot_generic,
y=yMot,
d=motDer_yMot_generic), simplified=
cha.efficiency_yMot(
per=per.motorEfficiency_yMot_generic,
y=1,
d=motDer_yMot_generic));
else
etaMot =
cha.efficiency_yMot(
per=per.motorEfficiency_yMot_generic,
y=yMot,
d=motDer_yMot_generic);
end if;
else
etaMot = 0.7;
end if;
end FlowMachineInterface;
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.
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.
partial model PartialFlowMachine
extends Buildings.Fluid.Interfaces.LumpedVolumeDeclarations(
final massDynamics=energyDynamics,
final mSenFac=1);
extends Buildings.Fluid.Interfaces.PartialTwoPort(
port_a(
p(start=Medium.p_default),
h_outflow(start=h_outflow_start)),
port_b(
p(start=p_start),
h_outflow(start=h_outflow_start)));
replaceable parameter Buildings.Fluid.Movers.Data.Generic per
constrainedby Buildings.Fluid.Movers.Data.Generic
;
parameter Buildings.Fluid.Types.InputType inputType = Buildings.Fluid.Types.InputType.Continuous
;
parameter Real constInput = 0 ;
parameter Real stageInputs[:]
;
parameter Boolean computePowerUsingSimilarityLaws
;
parameter Boolean addPowerToMedium=true
;
parameter Boolean nominalValuesDefineDefaultPressureCurve = false
;
parameter Modelica.Units.SI.Time tau=1
;
parameter Boolean use_inputFilter=true
;
parameter Modelica.Units.SI.Time riseTime=30
;
parameter Modelica.Blocks.Types.Init init=Modelica.Blocks.Types.Init.InitialOutput
;
Modelica.Blocks.Interfaces.IntegerInput stage
if inputType == Buildings.Fluid.Types.InputType.Stages
;
Modelica.Blocks.Interfaces.RealOutput y_actual(
final unit="1")
;
Modelica.Blocks.Interfaces.RealOutput P(
quantity="Power",
final unit="W") ;
Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
;
Modelica.Units.SI.VolumeFlowRate VMachine_flow(start=_VMachine_flow) = eff.V_flow
;
Modelica.Units.SI.PressureDifference dpMachine(displayUnit="Pa") = -preSou.dp
;
Real eta(unit="1",
final quantity="Efficiency") = eff.eta ;
Real etaHyd(unit="1",
final quantity="Efficiency") = eff.etaHyd ;
Real etaMot(unit="1",
final quantity="Efficiency") = eff.etaMot ;
parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0) = 1E-4*
abs(
_m_flow_nominal) ;
parameter Boolean show_T = false
;
Modelica.Units.SI.MassFlowRate m_flow(start=_m_flow_start) = port_a.m_flow
;
Modelica.Units.SI.PressureDifference dp(
start=_dp_start,
displayUnit="Pa") = port_a.p - port_b.p
;
Medium.ThermodynamicState sta_a=
if allowFlowReversal
then
Medium.setState_phX(port_a.p,
noEvent(
actualStream(port_a.h_outflow)),
noEvent(
actualStream(port_a.Xi_outflow)))
else
Medium.setState_phX(port_a.p,
noEvent(
inStream(port_a.h_outflow)),
noEvent(
inStream(port_a.Xi_outflow)))
if show_T ;
Medium.ThermodynamicState sta_b=
if allowFlowReversal
then
Medium.setState_phX(port_b.p,
noEvent(
actualStream(port_b.h_outflow)),
noEvent(
actualStream(port_b.Xi_outflow)))
else
Medium.setState_phX(port_b.p,
noEvent(port_b.h_outflow),
noEvent(port_b.Xi_outflow))
if show_T ;
protected
parameter Modelica.Units.SI.MassFlowRate _m_flow_nominal=
max(eff.per.pressure.V_flow)*rho_default
;
final parameter Modelica.Units.SI.MassFlowRate _m_flow_start=0
;
final parameter Modelica.Units.SI.PressureDifference _dp_start(displayUnit=
"Pa") = 0
;
final parameter Modelica.Units.SI.VolumeFlowRate _VMachine_flow=0
;
parameter Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable preVar ;
final parameter Boolean speedIsInput=
(preVar == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed)
;
final parameter Integer nOri =
size(per.pressure.V_flow, 1)
;
final parameter Boolean haveVMax = eff.haveVMax
;
final parameter Modelica.Units.SI.VolumeFlowRate V_flow_max=eff.V_flow_max;
final parameter Modelica.Units.SI.Density rho_default=
Medium.density_pTX(
p=Medium.p_default,
T=Medium.T_default,
X=Medium.X_default) ;
final parameter Medium.ThermodynamicState sta_start=
Medium.setState_pTX(
T=T_start,
p=p_start,
X=X_start) ;
final parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_start=
Medium.specificEnthalpy(sta_start) ;
final parameter Modelica.Units.SI.Frequency fCut=5/(2*Modelica.Constants.pi*
riseTime) ;
Modelica.Blocks.Sources.Constant[
size(stageInputs, 1)] stageValues(
final k=stageInputs)
if inputType == Buildings.Fluid.Types.InputType.Stages ;
Modelica.Blocks.Sources.Constant setConst(
final k=constInput)
if inputType == Buildings.Fluid.Types.InputType.Constant
;
Extractor extractor(
final nin=
size(stageInputs,1))
if inputType == Buildings.Fluid.Types.InputType.Stages ;
Modelica.Blocks.Routing.RealPassThrough inputSwitch
;
Buildings.Fluid.Delays.DelayFirstOrder vol(
redeclare final package Medium =
Medium,
final tau=tau,
final energyDynamics=energyDynamics,
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) ;
Buildings.Fluid.BaseClasses.ActuatorFilter filter(
final n=2,
final f=fCut,
final normalized=true,
final initType=init)
if use_inputFilter
;
Buildings.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 == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.FlowRate))
;
Buildings.Fluid.Movers.BaseClasses.PowerInterface heaDis(
final motorCooledByFluid=per.motorCooledByFluid,
final delta_V_flow=1E-3*V_flow_max)
if addPowerToMedium ;
Modelica.Blocks.Math.Add PToMed(
final k1=1,
final k2=1)
if addPowerToMedium ;
Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow prePow(
final alpha=0)
if addPowerToMedium
;
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))))
;
Buildings.Fluid.Sensors.MassFlowRate senMasFlo(
redeclare final package Medium =
Medium) ;
Buildings.Fluid.Sensors.RelativePressure senRelPre(
redeclare final package Medium =
Medium) ;
Buildings.Fluid.Movers.BaseClasses.FlowMachineInterface eff(
per(
final powerOrEfficiencyIsHydraulic = per.powerOrEfficiencyIsHydraulic,
final efficiency = per.efficiency,
final motorEfficiency = per.motorEfficiency,
final motorEfficiency_yMot = per.motorEfficiency_yMot,
final motorCooledByFluid = per.motorCooledByFluid,
final speed_nominal = 0,
final constantSpeed = 0,
final speeds = {0},
final power = per.power,
final peak = per.peak),
final nOri = nOri,
final rho_default=rho_default,
final computePowerUsingSimilarityLaws=computePowerUsingSimilarityLaws,
r_V(start=_m_flow_nominal/rho_default),
final preVar=preVar) ;
protected
block Extractor
extends Modelica.Blocks.Interfaces.MISO;
Modelica.Blocks.Interfaces.IntegerInput index ;
equation
y =
sum({
if index == i
then u[i]
else 0
for i
in 1:nin});
end Extractor;
initial algorithm
assert(nominalValuesDefineDefaultPressureCurve
or
per.havePressureCurve
or
(preVar == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed),
"*** Warning in " +
getInstanceName() +
": Mover is flow or pressure controlled and uses default pressure curve.
This leads to an approximate power consumption.
Set nominalValuesDefineDefaultPressureCurve=true to suppress this warning.",
level=AssertionLevel.warning);
assert(nominalValuesDefineDefaultPressureCurve
or
(per.havePressureCurve
or
(preVar == Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed))
or
per.etaHydMet<>
Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate,
"*** Warning in " +
getInstanceName() +
": Mover is flow or pressure controlled, uses default pressure curve and
has per.etaHydMet=.Power_VolumeFlowRate.
As this can cause wrong power consumption, the model overrides this setting by using per.etaHydMet=.NotProvided.
Set nominalValuesDefineDefaultPressureCurve=true to suppress this warning.",
level=AssertionLevel.warning);
assert(per.havePressureCurve
or
not (per.etaHydMet ==
Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate
or per.etaHydMet ==
Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber),
"*** Warning in " +
getInstanceName() +
": Mover has per.etaHydMet=.Power_VolumeFlowRate or per.etaHydMet=.EulerNumber.
This requires per.pressure to be provided.
Because it is not, the model overrides this setting by using per.etaHydMet=.NotProvided.
Also consider using models under Movers.Preconfigured which autopopulate a pressure curve.",
level=AssertionLevel.warning);
assert(per.havePressureCurve
or per.haveWMot_nominal
or
not (per.etaMotMet ==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
or per.etaMotMet ==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
"*** Warning in " +
getInstanceName() +
": Mover has per.etaMotMet=.Efficiency_MotorPartLoadRatio or per.etaMotMet=.GenericCurve.
This requires per.WMot_nominal or per.pressure to be provided. Because neither is provided,
the model overrides this setting and by using per.etaMotMet=.NotProvided.
Also consider using models under Movers.Preconfigured which autopopulate a pressure curve.",
level=AssertionLevel.warning);
assert(per.powerOrEfficiencyIsHydraulic
or
not (per.etaMotMet ==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
or per.etaMotMet ==
Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
"*** Warning in " +
getInstanceName() +
": Mover has per.etaMotMet=.Efficiency_MotorPartLoadRatio or per.etaMotMet=.GenericCurve
and provides information for total electric power instead of hydraulic power.
This forms an algebraic loop. If simulation fails to converge,
see the \"Motor efficiency\" section in the users guide for how to correct it.",
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.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.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);
connect(eff.WHyd, heaDis.WHyd);
connect(eff.PEle, P);
end PartialFlowMachine;