Define current operation mode. The CHP plant switches between six possible operating modes depending on the current mode,
control signals and plant boundary conditions. The regular transition between them
is as follows:

within Buildings.Fluid.CHPs.BaseClasses;
model Controller "Define current operation mode"
  extends Modelica.Blocks.Icons.Block;

  replaceable parameter Buildings.Fluid.CHPs.Data.Generic per
    "Performance data"
  parameter Modelica.Units.SI.Time waitTime=60
    "Wait time before transition from pump-on mode fires"

  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput avaSig
    "True when the plant is available"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput runSig
    "True when plant should run"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput mWat_flow(
    final unit="kg/s")
    "Cooling water mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TEng(
    final unit="K",
    final displayUnit="degC") if not per.warmUpByTimeDelay
    "Engine temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEleNet(final unit="W")
 if not per.warmUpByTimeDelay
    "Net power output"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEle(final unit="W")
 if not per.warmUpByTimeDelay
    "Power demand"

  Buildings.Fluid.CHPs.BaseClasses.Interfaces.ModeTypeOutput opeMod
    "Type of operating mode"
  Modelica.StateGraph.Step staBy(nOut=2, nIn=1)
    "Standby step"
  Modelica.StateGraph.Step pumOn(nOut=2, nIn=1)
    "Pump on step"
  Modelica.StateGraph.StepWithSignal warUp(nIn=2, nOut=2)
    "Warm-up step"
  Modelica.StateGraph.StepWithSignal cooDow(nIn=2, nOut=2)
    "Cool-down step"

protected
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant minWatFlo(
    final k=per.mWatMin_flow) "Minimum water mass flow rate"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant con(
    final k=0.001) "Constant value"
  Buildings.Controls.OBC.CDL.Reals.Max max
    "Maximum between minimum flow rate and 0.001"
  Buildings.Controls.OBC.CDL.Reals.Subtract sub1
    "Mass flow rate difference between actual and minimum value"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hys(
    final uLow=0.01*per.mWatMin_flow - 1e-6,
    final uHigh=0.015*per.mWatMin_flow)
    "Check if actual mass flow rate is larger than the minimum value"
  Buildings.Controls.OBC.CDL.Logical.And goSig
    "Check if water flow rate is higher than the minimum when runSig = true"
  Buildings.Controls.OBC.CDL.Logical.Not notRunSig "Plant is not running"
  Buildings.Controls.OBC.CDL.Logical.Not notAvaSig "Plant is not available"
  Buildings.Controls.OBC.CDL.Logical.And and1
    "Warm-up mode is done and the plant could run"
  Modelica.StateGraph.TransitionWithSignal transition3 "Plant should be off"
  Modelica.StateGraph.TransitionWithSignal transition5 "Plant should be off"
  Modelica.StateGraph.TransitionWithSignal transition9 "Run in cool-down mode"
  Modelica.StateGraph.TransitionWithSignal transition7 "Run in warm-up mode"
  Buildings.Controls.OBC.CDL.Logical.And and3
    "Plant could run and cool-down mode is optional"
  Modelica.StateGraph.TransitionWithSignal transition10 "Plant should be off"
  Buildings.Controls.OBC.CDL.Logical.And and4
    "Plant could run and cool-down mode is optional"
  Buildings.Fluid.CHPs.BaseClasses.AssertWaterFlow assWatMas(
    final mWatMin_flow=per.mWatMin_flow)
    "Assert if water flow rate is outside boundaries"
  Buildings.Fluid.CHPs.BaseClasses.Types.Mode actMod "Mode indicator";
  Modelica.StateGraph.TransitionWithSignal transition1
    "Plant becomes available"
  Modelica.StateGraph.TransitionWithSignal transition2 "Run plant"
  Modelica.StateGraph.Step nor(final nIn=1, final nOut=1)
    "Plant is in normal mode"
  Modelica.StateGraph.TransitionWithSignal transition8 "Run in cool-down mode"
  Buildings.Fluid.CHPs.BaseClasses.WarmUpLeaving warUpCtr(
    final timeDelayStart=per.timeDelayStart,
    final TEngNom=per.TEngNom,
    final PEleMax=per.PEleMax,
    final warmUpByTimeDelay=per.warmUpByTimeDelay)
    "Warm-up control sequence"
  Modelica.StateGraph.TransitionWithSignal transition6 "Run in normal mode"
  Modelica.StateGraph.InitialStep plaOff(final nIn=3, nOut=1)
    "Plant is off"
  Modelica.StateGraph.TransitionWithSignal transition4(
    final enableTimer=true,
    final waitTime=waitTime) "Run in warm-up mode"
  Buildings.Controls.OBC.CDL.Logical.Sources.Constant optCooDow(
    final k=per.coolDownOptional) "Check if cool-down mode is optional"
  Buildings.Controls.OBC.CDL.Logical.Timer timer(
    final t=per.timeDelayCool)
    "Check if the time of  plant in cool-down mode has been longer than the specified delay time"
  Buildings.Controls.OBC.CDL.Logical.Not noGo "Plant should not run"

equation
  if plaOff.active then
    actMod = CHPs.BaseClasses.Types.Mode.Off;
  elseif staBy.active then
    actMod = CHPs.BaseClasses.Types.Mode.StandBy;
  elseif pumOn.active then
    actMod = CHPs.BaseClasses.Types.Mode.PumpOn;
  elseif warUp.active then
    actMod = CHPs.BaseClasses.Types.Mode.WarmUp;
  elseif nor.active then
    actMod = CHPs.BaseClasses.Types.Mode.Normal;
  else
    actMod = CHPs.BaseClasses.Types.Mode.CoolDown;
  end if;
  opeMod = actMod;
  connect(nor.outPort[1], transition8.inPort)
  connect(TEng, warUpCtr.TEng)
  connect(plaOff.outPort[1], transition1.inPort)
  connect(transition6.outPort, nor.inPort[1])
  connect(assWatMas.mWat_flow, mWat_flow)
  connect(minWatFlo.y, max.u1)
  connect(con.y, max.u2)
  connect(sub1.y, hys.u)
  connect(hys.y, goSig.u1)
  connect(runSig, goSig.u2)
  connect(runSig, notRunSig.u)
  connect(runSig, assWatMas.runSig)
  connect(avaSig, transition1.condition)
  connect(runSig, transition2.condition)
  connect(avaSig, notAvaSig.u)
  connect(goSig.y, transition4.condition)
  connect(and1.y, transition6.condition)
  connect(notAvaSig.y, transition3.condition)
  connect(and3.y, transition7.condition)
  connect(and4.y, transition10.condition)
  connect(transition3.outPort, plaOff.inPort[1])
  connect(transition5.outPort, plaOff.inPort[2])
  connect(transition10.outPort, plaOff.inPort[3])
  connect(goSig.y, and3.u2)
  connect(optCooDow.y, and3.u1)
  connect(transition1.outPort, staBy.inPort[1])
  connect(staBy.outPort[1], transition2.inPort)
  connect(staBy.outPort[2], transition3.inPort)
  connect(transition2.outPort, pumOn.inPort[1])
  connect(pumOn.outPort[1], transition4.inPort)
  connect(pumOn.outPort[2], transition5.inPort)
  connect(warUp.outPort[1], transition6.inPort)
  connect(warUp.outPort[2], transition9.inPort)
  connect(warUp.active, warUpCtr.actWarUp)
  connect(warUpCtr.y, and1.u2)
  connect(goSig.y, and1.u1)
  connect(transition4.outPort, warUp.inPort[1])
  connect(transition7.outPort, warUp.inPort[2])
  connect(transition8.outPort, cooDow.inPort[1])
  connect(transition9.outPort, cooDow.inPort[2])
  connect(cooDow.outPort[1], transition10.inPort)
  connect(cooDow.outPort[2], transition7.inPort)
  connect(cooDow.active, timer.u)
  connect(PEleNet, warUpCtr.PEleNet)
  connect(PEle, warUpCtr.PEle)
  connect(notRunSig.y, transition5.condition)
  connect(goSig.y, noGo.u)
  connect(noGo.y, transition8.condition)
  connect(noGo.y, and4.u2)
  connect(noGo.y, transition9.condition)
  connect(timer.passed, and4.u1)
  connect(max.y, sub1.u2)
  connect(mWat_flow, sub1.u1)
end Controller;


Efficiency curve described by a fifth order polynomial,
  function of three input variables. The block computes the efficiency as a polynomial function of three input variables.
The polynomial has the form

within Buildings.Fluid.CHPs.BaseClasses;
block EfficiencyCurve "Efficiency curve described by a fifth order polynomial,
  function of three input variables"
  extends Modelica.Blocks.Icons.Block;

  parameter Real a[27] "Polynomial coefficients";

  Buildings.Controls.OBC.CDL.Interfaces.RealInput PNet(
    final unit="W") "Electric power"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput mWat_flow(
    final unit="kg/s") "Water mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TWatIn(
    final unit="K") "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput eta(
    final unit="1") "Efficiency"

protected
  Real y(unit="1") "Efficiency";
  constant Real etaSma=0.01 "Value for eta if y is zero";

equation
  y = Buildings.Fluid.CHPs.BaseClasses.Functions.polynomialtrivariate(
    a=a,
    x1=PNet,
    x2=mWat_flow,
    x3=TWatIn - 273.15)
    "Efficiency calculated as a function of power, water flow rate and water inlet temperature";
  eta = Buildings.Utilities.Math.Functions.smoothMax(
    x1=y,
    x2=etaSma,
    deltaX=etaSma/2)
    "Corrected efficiency, ensuring that efficiency is not zero";
end EfficiencyCurve;


Energy conversion control volume. The model defines energy conversion that occurs during the normal mode and warm-up mode.
The model

Buildings.Fluid.CHPs.BaseClasses.EnergyConversionWarmUp
is used only for the
warm-up mode dependent on the engine temperature (case of Stirling engines).
The model

Buildings.Fluid.CHPs.BaseClasses.EnergyConversionNormal
is used for all other cases,
i.e. the normal mode, and the warm-up mode based on a time delay (case of internal
combustion engines).

within Buildings.Fluid.CHPs.BaseClasses;
model EnergyConversion "Energy conversion control volume"
  extends Modelica.Blocks.Icons.Block;

  replaceable parameter Buildings.Fluid.CHPs.Data.Generic per
    "Performance data"

  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEle(
    final unit="W") "Power demand"
  Buildings.Fluid.CHPs.BaseClasses.Interfaces.ModeTypeInput opeMod
    "Operation mode"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput mWat_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Water mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TWatIn(
    final unit="K",
    displayUnit="degC")
    "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TRoo(
    final unit="K",
    displayUnit="degC") "Room temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TEng(
    final unit="K",
    displayUnit="degC") if not per.warmUpByTimeDelay
    "Engine temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput PEleNet(
    final unit="W") "Net power output"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mFue_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Fuel mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mAir_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Air mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput QGen_flow(final unit="W")
    "Heat generation rate"

protected
  Buildings.Fluid.CHPs.BaseClasses.AssertFuelFlow assFue(
    final dmFueMax_flow=per.dmFueMax_flow) if per.use_fuelRateLimit
    "Assert if fuel flow rate is outside boundaries"
  Buildings.Fluid.CHPs.BaseClasses.EnergyConversionNormal opeModBas(final per=
        per) "Typical energy conversion mode"
  Buildings.Fluid.CHPs.BaseClasses.EnergyConversionWarmUp opeModWarUpEngTem(
      final per=per) if not per.warmUpByTimeDelay "Warm-up by engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant const(final k=0)
    "Zero constant"
  Buildings.Controls.OBC.CDL.Reals.Switch switch
    "Switch to zero power output if not in (normal or warm-up mode)"
  Modelica.Blocks.Sources.BooleanExpression booExp(
    final y=opeMod ==CHPs.BaseClasses.Types.Mode.WarmUp or
            opeMod ==CHPs.BaseClasses.Types.Mode.Normal)
    "True if active mode is (warm-up or normal)"
  Buildings.Controls.OBC.CDL.Reals.Switch switch2
    "Switch between warm-up and normal value"
  Buildings.Controls.OBC.CDL.Reals.Switch switch3
    "Switch between warm-up and normal value"
  Buildings.Controls.OBC.CDL.Reals.Switch switch4
    "Switch between warm-up and normal value"
  Buildings.Controls.OBC.CDL.Reals.Switch switch5
    "Switch between warm-up and normal value"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant PEleTimeDel(final k=0)
               if per.warmUpByTimeDelay
    "Zero power output in case of warm-up by time delay"
  Buildings.Controls.OBC.CDL.Logical.Sources.Constant delWarUp(
    final k=per.warmUpByTimeDelay) "Warm-up by time delay"
  Modelica.Blocks.Sources.BooleanExpression wamUpMod(
    final y=opeMod == CHPs.BaseClasses.Types.Mode.WarmUp)
    "Check whether warm-up mode is active"
  Buildings.Controls.OBC.CDL.Logical.Not engTemWarUp
    "Warm-up based on engine temperature"
  Buildings.Controls.OBC.CDL.Logical.And and1
    "True if warm-up mode and warm-up based on engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dumTimDel(
    final k=0) if per.warmUpByTimeDelay
    "Set dummy value in case of warm-up by time delay"

equation
  connect(opeModBas.mWat_flow, mWat_flow)
  connect(opeModBas.TWatIn, TWatIn)
  connect(opeModWarUpEngTem.TEng, TEng)
  connect(opeModWarUpEngTem.TWatIn, TWatIn)
  connect(const.y, switch.u3)
  connect(PEle, switch.u1)
  connect(booExp.y, switch.u2)
  connect(switch.y, opeModBas.PEle)
  connect(opeModWarUpEngTem.PEleNet, switch2.u1)
  connect(switch2.y, PEleNet)
  connect(switch3.y, mFue_flow)
  connect(switch4.y, mAir_flow)
  connect(switch5.y, QGen_flow)
  connect(opeModBas.mFue_flow, switch3.u3)
  connect(opeModWarUpEngTem.mFue_flow, switch3.u1)
  connect(opeModBas.mAir_flow, switch4.u3)
  connect(opeModWarUpEngTem.mAir_flow, switch4.u1)
  connect(opeModWarUpEngTem.QGen_flow, switch5.u1)
  connect(opeModBas.QGen_flow, switch5.u3)
  connect(mWat_flow, opeModWarUpEngTem.mWat_flow)
  connect(switch3.y, assFue.mFue_flow)
  connect(PEleTimeDel.y, switch2.u1)
  connect(switch.y, switch2.u3)
  connect(TRoo, opeModWarUpEngTem.TRoo)
  connect(delWarUp.y, engTemWarUp.u)
  connect(engTemWarUp.y, and1.u2)
  connect(wamUpMod.y, and1.u1)
  connect(and1.y, switch3.u2)
  connect(and1.y, switch4.u2)
  connect(and1.y, switch5.u2)
  connect(wamUpMod.y, switch2.u2)
  connect(dumTimDel.y, switch3.u1)
  connect(dumTimDel.y, switch4.u1)
  connect(dumTimDel.y, switch5.u1)
end EnergyConversion;


Energy conversion for typical CHP operation either in normal mode or warm-up mode based on time delay. The model defines energy conversion for a typical CHP operation that includes the
normal mode and warm-up mode based on the time delay (CHPs with internal combustion engines).
Energy conversion from fuel to the electric power and heat is modeled using
system's part-load electrical and thermal efficiencies, based on the empirical
data from the manufacturer.
The curves are described by a fifth order polynomial, a function of the electric
power, water flow rate and water inlet temperature.
The air flow rate is modeled using a second order polynomial, a function of
the fuel flow rate.

within Buildings.Fluid.CHPs.BaseClasses;
model EnergyConversionNormal
  "Energy conversion for typical CHP operation either in normal mode or warm-up mode based on time delay"
  extends Modelica.Blocks.Icons.Block;

  replaceable parameter Buildings.Fluid.CHPs.Data.Generic per
    "Performance data"

  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEle(
    final unit="W") "Electric power"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput mWat_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Water mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TWatIn(
    final unit="K",
    displayUnit="degC") "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mFue_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Fuel mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mAir_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Air mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput QGen_flow(final unit="W")
    "Heat generation rate within the engine"

protected
  Buildings.Fluid.CHPs.BaseClasses.EfficiencyCurve etaE(
    final a=per.coeEtaE)
    "Part load electrical efficiency"
  Buildings.Fluid.CHPs.BaseClasses.EfficiencyCurve etaQ(
    final a=per.coeEtaQ)
    "Part load thermal efficiency"
  Buildings.Controls.OBC.CDL.Reals.Divide groHea
    "Gross heat input into the system"
  Buildings.Controls.OBC.CDL.Reals.Multiply heaGen
    "Heat generation within the engine"
  Buildings.Utilities.Math.Polynomial masFloAir(final a=per.coeMasAir)
    "Air mass flow rate computation"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter masFloFue(
    final k=1/per.LHVFue) "Fuel mass flow rate computation"
equation
  connect(groHea.u1, PEle)
  connect(heaGen.y, QGen_flow)
  connect(masFloAir.y, mAir_flow)
  connect(etaE.TWatIn, TWatIn)
  connect(etaE.PNet, PEle)
  connect(etaQ.PNet, PEle)
  connect(mWat_flow, etaE.mWat_flow)
  connect(etaQ.mWat_flow, mWat_flow)
  connect(etaQ.TWatIn, TWatIn)
  connect(etaE.eta, groHea.u2)
  connect(etaQ.eta, heaGen.u2)
  connect(groHea.y, heaGen.u1)
  connect(masFloFue.y, mFue_flow)
  connect(groHea.y, masFloFue.u)
  connect(masFloFue.y, masFloAir.u)
end EnergyConversionNormal;


Energy conversion during warm-up mode based on engine temperature. The model defines energy conversion for the warm-up mode that is dependent on the
engine temperature (e.g. CHPs with Stirling engines).
The engine fuel flow rate is a function of the fuel flow rate at the maximum power
output, as well as the difference between the nominal engine temperature and the
actual engine temperature.
Energy conversion from fuel to the electric power and heat is modeled using
system's part-load electrical and thermal efficiencies, based on the empirical
data from the manufacturer.
The curves are described by a fifth order polynomial, a function of the electric
power, water flow rate and water inlet temperature.
The air flow rate is modeled using a second order polynomial, a function of
the fuel flow rate.

within Buildings.Fluid.CHPs.BaseClasses;
model EnergyConversionWarmUp
  "Energy conversion during warm-up mode based on engine temperature"
  extends Modelica.Blocks.Icons.Block;

  replaceable parameter Buildings.Fluid.CHPs.Data.Generic per
    "Performance data"

  Buildings.Controls.OBC.CDL.Interfaces.RealInput mWat_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Water mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TWatIn(
    final unit="K",
    displayUnit="degC") "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TRoo(
    final unit="K",
    displayUnit="degC") "Room temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TEng(
    final unit="K",
    displayUnit="degC") "Engine temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput PEleNet(
    final unit="W") "Electric power generation"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mFue_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Fuel mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mAir_flow(
    final unit="kg/s",
    final quantity="MassFlowRate") "Air mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput QGen_flow(final unit="W")
    "Heat generation rate within the engine"

protected
  Buildings.Utilities.Math.SmoothMax smoothMax(
    final deltaX=0.5)
    "Prevent negative value if room temperature exceeds engine nominal temperature"
  Buildings.Utilities.Math.SmoothMax smoothMax2(
    final deltaX=0.5)
    "Prevent zero in denominator"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis temChe(
    final uLow=4.8,
    final uHigh=5.2)
    "Check if room temperature is not close to the nominal engine temperature by less than 5 Kelvin"
  Buildings.Fluid.CHPs.BaseClasses.EfficiencyCurve etaE(
    final a=per.coeEtaE) "Part load electrical efficiency"
  Buildings.Controls.OBC.CDL.Reals.Divide QGroMax
    "Gross heat input into the system"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant PEleMax(
    final k=per.PEleMax) "Maximum power"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter maxFueFlo(
    final k=per.rFue) "Maximum fuel mass flow rate"
  Buildings.Controls.OBC.CDL.Reals.Min fueFlo "Fuel flow"
  Buildings.Controls.OBC.CDL.Reals.Divide division
    "First input divided by second input"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TEngNom(
    y(final unit="K", displayUnit="degC"),
    final k=per.TEngNom) "Nominal engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Subtract sub1
    "Difference between nominal engine temperature and room temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant min1(final k=1)
    "Prevent negative value if room temperature exceeds engine nominal temperature"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter gain(final k=per.kF)
    "Gain by factor of warm-up fuel coefficient"
  Buildings.Controls.OBC.CDL.Reals.Add add2 "Add up two inputs"
  Buildings.Controls.OBC.CDL.Reals.Multiply unlFueFloWarUp
    "Unlimited fuel mass flow rate during warm-up"
  Buildings.Fluid.CHPs.BaseClasses.EfficiencyCurve etaQ(
    final a=per.coeEtaQ) "Part load thermal efficiency"
  Buildings.Controls.OBC.CDL.Reals.Multiply heaGen
    "Heat generation within the engine"
  Buildings.Controls.OBC.CDL.Reals.Divide division1
    "First input divided by second input"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant powCoe(
    final k=per.kP) "Warm-up power coefficient"
  Buildings.Controls.OBC.CDL.Reals.Multiply PEleNet1
    "Generated electrical power"
  Buildings.Utilities.Math.Polynomial masFloAir(final a=per.coeMasAir)
    "Air mass flow rate"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant min2(final k=1)
    "Prevent zero  in denominator"
  Buildings.Controls.OBC.CDL.Utilities.Assert assMes(
    final message="Room temperature is too close to the nominal engine temperature, simulation should be aborted")
    "Assert function for checking room temperature"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter masFloFue(final k=1
        /per.LHVFue) "Fuel mass flow rate computation"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter heaGro(final k=per.LHVFue)
    "Gross heat input into the system"
  Buildings.Controls.OBC.CDL.Reals.Subtract sub2
    "Difference between room temperature and engine temperature"

equation
  connect(PEleMax.y, etaE.PNet)
  connect(QGroMax.u1,PEleMax. y)
  connect(etaE.TWatIn, TWatIn)
  connect(etaE.mWat_flow, mWat_flow)
  connect(etaE.eta, QGroMax.u2)
  connect(TEngNom.y, sub1.u1)
  connect(sub1.u2, TRoo)
  connect(division.y, gain.u)
  connect(gain.y, add2.u1)
  connect(add2.y, unlFueFloWarUp.u2)
  connect(etaQ.PNet,PEleMax. y)
  connect(etaQ.mWat_flow, mWat_flow)
  connect(etaQ.TWatIn, TWatIn)
  connect(etaQ.eta, heaGen.u1)
  connect(maxFueFlo.y, fueFlo.u1)
  connect(unlFueFloWarUp.y, fueFlo.u2)
  connect(division1.u1,powCoe. y)
  connect(division1.u2, division.y)
  connect(PEleNet1.u1, PEleMax.y)
  connect(PEleNet1.y, PEleNet)
  connect(heaGen.y, QGen_flow)
  connect(fueFlo.y, mFue_flow)
  connect(masFloAir.y, mAir_flow)
  connect(min1.y, smoothMax.u2)
  connect(sub1.y, smoothMax.u1)
  connect(smoothMax.y, division.u1)
  connect(min2.y, smoothMax2.u2)
  connect(smoothMax2.y, division.u2)
  connect(temChe.u, sub1.y)
  connect(division1.y, PEleNet1.u2)
  connect(temChe.y, assMes.u)
  connect(min2.y, add2.u2)
  connect(fueFlo.y,masFloAir. u)
  connect(QGroMax.y, masFloFue.u)
  connect(masFloFue.y, maxFueFlo.u)
  connect(masFloFue.y, unlFueFloWarUp.u1)
  connect(heaGro.y, heaGen.u2)
  connect(fueFlo.y, heaGro.u)
  connect(TRoo, sub2.u2)
  connect(TEng, sub2.u1)
  connect(sub2.y, smoothMax2.u1)
end EnergyConversionWarmUp;


Heat exchange within the engine control volume. The model defines the dynamic behavior of the CHP thermal mass (i.e. engine block,
encapsulated working fluid, and internal heat exchange equipment) using a single,
engine control volume.
The thermal energy stored within this volume is quantified using an aggregate
thermal capacitance capEng and an equivalent average engine temperature
TEng.
The heat transfer between the engine and the cooling water control volume is
quantified using the overall thermal conductance UAHex,
while the heat loss to the surroundings is quantified using the overall thermal
conductance UALos.

within Buildings.Fluid.CHPs.BaseClasses;
model EngineTemperature "Heat exchange within the engine control volume"
  extends Modelica.Blocks.Icons.Block;

  parameter Modelica.Units.SI.ThermalConductance UAHex
    "Thermal conductance between the engine and cooling water";
  parameter Modelica.Units.SI.ThermalConductance UALos
    "Thermal conductance between the engine and surroundings";
  parameter Modelica.Units.SI.HeatCapacity capEng
    "Thermal capacitance of the engine control volume";
  parameter Modelica.Units.SI.Temperature TEngIni "Initial engine temperature";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a TRoo
    "Heat port for room temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput QGen_flow(final unit="W")
    "Heat generation rate within the engine"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a TWat
    "Heat port for water volume temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput TEng(
    final unit="K",
    displayUnit="degC") "Engine temperature"

protected
  constant Modelica.Units.SI.Density rhoWat=1000 "Water density";
  constant Modelica.Units.SI.SpecificHeatCapacity cWat=4180
    "Water specific heat";
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theConHX(
    final G=UAHex)
    "Thermal conductance between engine and cooling water volume"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capTheEng(
    final C=capEng, T(fixed=true, start=TEngIni))
    "Thermal capacitance of the engine control volume"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor conTheLos(
    final G=UALos)
    "Thermal conductance between the engine and surroundings"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow QGen1
    "Heat generation within the engine"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor engTem
    "Engine temperature"
equation
  connect(capTheEng.port, theConHX.port_b)
  connect(QGen_flow, QGen1.Q_flow)
  connect(conTheLos.port_b, capTheEng.port)
  connect(engTem.port, capTheEng.port)
  connect(conTheLos.port_a, TRoo)
  connect(theConHX.port_a, TWat)
  connect(QGen1.port, capTheEng.port)
  connect(engTem.T, TEng)
end EngineTemperature;


Constraints for electric power. The model checks if the electric power and power rate are within the boundaries
specified by the manufacturer.
The constraints are applied and a warning message is sent if the electric power
is outside the boundaries.

within Buildings.Fluid.CHPs.BaseClasses;
model FilterPower "Constraints for electric power"
  extends Modelica.Blocks.Icons.Block;

  parameter Modelica.Units.SI.Power PEleMax "Maximum power output";
  parameter Modelica.Units.SI.Power PEleMin "Minimum power output";
  parameter Boolean use_powerRateLimit
    "If true, the rate at which net power output can change is limited";
  parameter Real dPEleMax(final unit="W/s")
    "Maximum rate at which net power output can change";

  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEleDem(final unit="W")
    "Electric power demand"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput PEle(final unit="W")
    "Electric power demand after applied constraints"
  Buildings.Fluid.CHPs.BaseClasses.AssertPower assPow(
    final PEleMax=PEleMax,
    final PEleMin=PEleMin,
    final use_powerRateLimit=use_powerRateLimit,
    final dPEleMax=dPEleMax)
    "Assert if electric power is outside boundaries"

protected
  Modelica.Blocks.Nonlinear.VariableLimiter PLim "Power limiter"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant PMax(
    final k=PEleMax) "Maximum power"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant PMin(
    final k=PEleMin) "Minimum power"
  Buildings.Controls.OBC.CDL.Reals.LimitSlewRate dPLim(
    final raisingSlewRate(unit="W/s") = dPEleMax,
    final fallingSlewRate(unit="W/s") = -dPEleMax,
    final Td=1) "Power rate limiter"
  Buildings.Controls.OBC.CDL.Reals.Switch switch
  Buildings.Controls.OBC.CDL.Logical.Sources.Constant limDp(
    final k=use_powerRateLimit)
    "Check if dP is limited"

equation
  connect(PMax.y, PLim.limit1)
  connect(PMin.y, PLim.limit2)
  connect(PLim.u, PEleDem)
  connect(switch.y, PEle)
  connect(PLim.y, dPLim.u)
  connect(PLim.y, switch.u3)
  connect(assPow.PEleDem, PEleDem)
  connect(limDp.y, switch.u2)

  connect(dPLim.y, switch.u1)
end FilterPower;


within Buildings.Fluid.CHPs;
package BaseClasses "Package with base classes for CHP models"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


Power consumption during stand-by and cool-down modes. The model calculates the power consumption during the stand-by and cool-down modes of operation.

within Buildings.Fluid.CHPs.BaseClasses;
model PowerConsumption
  "Power consumption during stand-by and cool-down modes"
  extends Modelica.Blocks.Icons.Block;

  parameter Modelica.Units.SI.Power PStaBy "Standby electric power";
  parameter Modelica.Units.SI.Power PCooDow "Cooldown electric power";

  Buildings.Fluid.CHPs.BaseClasses.Interfaces.ModeTypeInput opeMod
    "Operation mode"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput PCon(
    final unit="W")
    "Power consumption during stand-by and cool-down modes"

protected
  Modelica.Blocks.Sources.BooleanExpression staBy(
    final y=opeMod == Buildings.Fluid.CHPs.BaseClasses.Types.Mode.StandBy)
    "Check if the operation mode is StandBy"
  Modelica.Blocks.Sources.BooleanExpression cooDow(
    final y=opeMod == Buildings.Fluid.CHPs.BaseClasses.Types.Mode.StandBy or
            opeMod == Buildings.Fluid.CHPs.BaseClasses.Types.Mode.CoolDown)
    "Check if stand-by mode or cool-down mode"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant staByCon(
    final k=PStaBy)
    "Stand-by mode power consumption "
  Buildings.Controls.OBC.CDL.Reals.Switch switch
    "Power consumption during stand-by or cool-down mode"
  Buildings.Controls.OBC.CDL.Reals.Switch switch1 "Cool-down power consumption"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant const(final k=0)
    "Zero power consumption"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant cooDowCon(
    final k=PCooDow)
    "Cool-down mode power consumption "

equation
  connect(staByCon.y, switch.u1)
  connect(switch1.y, PCon)
  connect(staBy.y, switch.u2)
  connect(cooDow.y, switch1.u2)
  connect(const.y, switch1.u3)
  connect(cooDowCon.y, switch.u3)
  connect(switch.y, switch1.u1)
end PowerConsumption;


Model evaluating the condition for transitioning from warm-up to normal mode. The model computes a boolean variable which is true when warm-up
is over.
CHP will transition from the warm-up mode to the normal mode

within Buildings.Fluid.CHPs.BaseClasses;
model WarmUpLeaving
  "Model evaluating the condition for transitioning from warm-up to normal mode"
  extends Modelica.Blocks.Icons.Block;
  parameter Modelica.Units.SI.Time timeDelayStart
    "Time delay between activation and power generation";
  parameter Modelica.Units.SI.Temperature TEngNom
    "Nominal engine operating temperature";
  parameter Modelica.Units.SI.Power PEleMax=0 "Maximum power output"
  parameter Boolean warmUpByTimeDelay
    "If true, the plant will be in warm-up mode depending on the delay time, otherwise depending on engine temperature "
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TEng(
    final unit="K",
    displayUnit="degC") if not warmUpByTimeDelay
    "Engine temperature"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y "Transition signal"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput actWarUp
    "Warm-up state active signal"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEle(final unit="W")
 if not warmUpByTimeDelay
    "Power demand"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEleNet(final unit="W")
 if not warmUpByTimeDelay
    "Net power output"

protected
  Buildings.Controls.OBC.CDL.Logical.Timer timer(
    final t=timeDelayStart) if warmUpByTimeDelay
    "Check the time since the warm-up mode is activated"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hysTem(
    final uLow=-0.5,
    final uHigh=0) if not warmUpByTimeDelay
    "Check if actual engine temperature is higher than the nominal value"
  Buildings.Controls.OBC.CDL.Reals.Subtract sub
 if not warmUpByTimeDelay
    "Difference between actual engine temperature and the nominal value"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant temEngNom(
    y(final unit="K", displayUnit="degC"),
    final k=TEngNom)
    "Nominal engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Subtract sub1
 if not warmUpByTimeDelay
    "Difference between actual power output and demand"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hysPow(
    final uLow=-0.01*PEleMax - 1e-6,
    final uHigh=0) if not warmUpByTimeDelay
    "Check if actual power output is higher than demand"
  Buildings.Controls.OBC.CDL.Logical.Or or2 if not warmUpByTimeDelay "OR evaluation"
  Buildings.Controls.OBC.CDL.Logical.Pre pre if not warmUpByTimeDelay
    "Infinitesimal time delay to break algebraic loop related to power output computation"

equation
  connect(sub.y, hysTem.u)
  connect(TEng, sub.u1)
  connect(temEngNom.y, sub.u2)
  connect(actWarUp, timer.u)
  connect(sub1.y, hysPow.u)
  connect(PEle, sub1.u2)
  connect(hysTem.y, or2.u1)
  connect(hysPow.y, or2.u2)
  connect(or2.y, pre.u)
  connect(pre.y, y)
  connect(PEleNet, sub1.u1)
  connect(timer.passed, y)
end WarmUpLeaving;


Internal controller for water flow rate. The model calculates the water mass flow rate that is determined by the internal controller.
In CHPs that use this type of internal control the cooling water mass flow rate is
controlled to optimize engine performance and heat recovery.
In the main model of the CHP unit

Buildings.Fluid.CHPs.ThermalElectricalFollowing,
this optimum water mass flow rate is specified as the set point signal for the external pump controller.

within Buildings.Fluid.CHPs.BaseClasses;
model WaterFlowControl "Internal controller for water flow rate"
  extends Modelica.Blocks.Icons.Block;

  replaceable parameter Buildings.Fluid.CHPs.Data.Generic per
    "Performance data"

  Buildings.Fluid.CHPs.BaseClasses.Interfaces.ModeTypeInput opeMod  "Operation mode"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput PEle(
    final unit="W") "Electric power"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TWatIn(
    final unit="K", displayUnit="degC") "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput mWatSet_flow(
    final unit="kg/s", final quantity="MassFlowRate")
    "Water mass flow rate set point"
protected
  Modelica.Blocks.Sources.BooleanExpression offStaBy(
    final y=opeMod == Buildings.Fluid.CHPs.BaseClasses.Types.Mode.Off or
            opeMod == Buildings.Fluid.CHPs.BaseClasses.Types.Mode.StandBy)
    "Check if off mode or stand-by mode"
  Buildings.Utilities.Math.Biquadratic mWatIntCon(
    final a=per.coeMasWat) "Internal control of water flow rate "
  Buildings.Controls.OBC.CDL.Reals.Switch watFloSet "Water flow setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant const(final k=0)
    "Zero flow rate"
  Buildings.Controls.OBC.UnitConversions.To_degC to_degC
equation
  connect(mWatIntCon.y, watFloSet.u3)
  connect(watFloSet.y, mWatSet_flow)
  connect(const.y, watFloSet.u1)
  connect(offStaBy.y, watFloSet.u2)

  connect(PEle, mWatIntCon.u1)
  connect(TWatIn, to_degC.u)
  connect(mWatIntCon.u2, to_degC.y)
end WaterFlowControl;


info

within Buildings.Fluid.CHPs.BaseClasses;
package Functions
  extends Modelica.Icons.VariantsPackage;
end Functions;


info

within Buildings.Fluid.CHPs.BaseClasses;
package Interfaces
  extends Modelica.Icons.InterfacesPackage;
end Interfaces;


Library of CHP operation modes

within Buildings.Fluid.CHPs.BaseClasses;
package Types "Library of CHP operation modes"
  extends Modelica.Icons.TypesPackage;
end Types;


Validate model AssertFuelFlow. This example validates

Buildings.Fluid.CHPs.BaseClasses.AssertFuelFlow
for sending a warning message if the fuel mass flow rate is outside boundaries.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model AssertFuelFlow "Validate model AssertFuelFlow"
  extends Modelica.Icons.Example;
  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.AssertFuelFlow assFue(
    final dmFueMax_flow=per.dmFueMax_flow)
    "Assert if fuel mass flow rate is outside boundaries"

  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable mFue_flow(
    final table=[0,0; 300,1; 600,3.5; 900,0],
    final smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Fuel mass flow rate"

equation
  connect(mFue_flow.y[1], assFue.mFue_flow)
end AssertFuelFlow;


Validate model AssertPower. This example validates

Buildings.Fluid.CHPs.BaseClasses.AssertPower
for sending a warning message if the electric power is outside boundaries.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model AssertPower "Validate model AssertPower"
  extends Modelica.Icons.Example;
  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.AssertPower assPow(
    PEleMax=per.PEleMax,
    PEleMin=per.PEleMin,
    use_powerRateLimit=per.use_powerRateLimit,
    dPEleMax=per.dPEleMax)
    "Assert if electric power is outside boundaries"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable PEleDem(
    table=[0,0; 300,500; 600,2000; 900,3001;
           1200,0; 1500,6000; 2000,6000],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Electricity demand"

equation
  connect(PEleDem.y[1], assPow.PEleDem)
end AssertPower;


Validate model AssertWaterFlow. This example validates

Buildings.Fluid.CHPs.BaseClasses.AssertWaterFlow
for sending a warning message if the water mass flow is outside boundaries.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model AssertWaterFlow "Validate model AssertWaterFlow"
  extends Modelica.Icons.Example;
  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.AssertWaterFlow assWatMas(mWatMin_flow=per.mWatMin_flow)
    "Assert if water mass flow is outside boundaries"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable mWat_flow(
    table=[0,0; 300,0.05; 360,0.5;
           600,0.05; 900,0.05],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Water flow rate"
  Modelica.Blocks.Sources.BooleanTable runSig(table={300,1000})
    "Flag is true when electricity/heat demand larger than zero"

equation
  connect(mWat_flow.y[1], assWatMas.mWat_flow)
  connect(runSig.y, assWatMas.runSig)
end AssertWaterFlow;


Validate model AssertWaterTemperature. This example validates

Buildings.Fluid.CHPs.BaseClasses.AssertWaterTemperature
for sending a warning message if the water temperature is outside boundaries.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model AssertWaterTemperature "Validate model AssertWaterTemperature"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.AssertWaterTemperature assWatTem(TWatMax=per.TWatMax)
             "Assert if water temperature is outside boundaries"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TWat(
    table=[0,273.15 + 20; 300,273.15 + 80;
           600,273.15 + 100; 900,273.15 + 100],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Water temperature"

equation
  connect(TWat.y[1], assWatTem.TWat)
end AssertWaterTemperature;


Validate model Controller. This example validates

Buildings.Fluid.CHPs.BaseClasses.Controller
for switching between six operating modes.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model Controller "Validate model Controller"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData3 per
    "CHP performance data"

  Modelica.Blocks.Sources.BooleanTable runSig(
    startValue=false,
    table={900,960,1200,2200})
    "Plant run signal"
  Buildings.Fluid.CHPs.BaseClasses.Controller con(final per=per)
    "Operation mode controller"
  Modelica.Blocks.Sources.BooleanTable avaSig(
    final startValue=false,
    table={300,600,900}) "Plant availability signal"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable mWat_flow(
    table=[0,0; 900,0.4; 1320,0; 1500,0.4;
           1900,0; 1960,0.4; 2200,0; 3000,0],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Water flow rate"

protected
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot

equation
  connect(avaSig.y, con.avaSig)
  connect(runSig.y, con.runSig)
  connect(mWat_flow.y[1], con.mWat_flow)
end Controller;


Validate model CoolDown. This example validates the transitions to and from the cool-down operating mode.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model CoolDown "Validate model CoolDown"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.Types.Mode actMod=
    if norm.active then
      Buildings.Fluid.CHPs.BaseClasses.Types.Mode.Normal
    elseif cooDow.active then
      Buildings.Fluid.CHPs.BaseClasses.Types.Mode.CoolDown
    else
      Buildings.Fluid.CHPs.BaseClasses.Types.Mode.Off
  "Mode indicator";
  Modelica.Blocks.Sources.BooleanTable runSig(
    final startValue=true,
    table={300,600,660,690}) "Plant run signal"

  Buildings.Controls.OBC.CDL.Logical.Not notRun "Plant does not run"
  Buildings.Controls.OBC.CDL.Logical.And and1 "Plant run and cool-down is optional"
  Buildings.Controls.OBC.CDL.Logical.Sources.Constant cooDowOpt(
    final k=per.coolDownOptional)
    "Cool-down is optional"

  Modelica.StateGraph.StepWithSignal cooDow(nIn=2, nOut=2) "Cool-down mode"
protected
  Modelica.StateGraph.InitialStep norm(final nIn=2, final nOut=2)
    "Normal operation mode"
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
  Modelica.StateGraph.TransitionWithSignal transition2 "Cool-down to off mode"
  Modelica.StateGraph.TransitionWithSignal transition1
    "Normal to cool-down mode"
  Modelica.StateGraph.TransitionWithSignal transition4
    "Cool-down to normal mode"
  Modelica.StateGraph.Step off(final nIn=1, final nOut=1) "Off mode"
  Modelica.StateGraph.TransitionWithSignal transition3 "Off to normal mode"
  Modelica.StateGraph.Transition transition5(
    final condition=false,
    final waitTime=0)

protected
  Buildings.Controls.OBC.CDL.Logical.Timer timer(
    final t=per.timeDelayCool)
    "Time of the plant in cool-down mode"

equation
  connect(transition4.outPort, norm.inPort[1])
  connect(transition1.inPort, norm.outPort[1])
  connect(off.inPort[1], transition2.outPort)
  connect(off.outPort[1], transition3.inPort)
  connect(transition3.outPort, norm.inPort[2])
  connect(transition5.inPort, norm.outPort[2])
  connect(runSig.y, notRun.u)
  connect(notRun.y, transition1.condition)
  connect(cooDowOpt.y, and1.u1)
  connect(runSig.y, and1.u2)
  connect(runSig.y, transition3.condition)
  connect(and1.y, transition4.condition)
  connect(cooDow.outPort[1], transition2.inPort)
  connect(cooDow.outPort[2], transition4.inPort)
  connect(transition1.outPort, cooDow.inPort[1])
  connect(transition5.outPort, cooDow.inPort[2])
  connect(cooDow.active, timer.u)
  connect(timer.passed, transition2.condition)
end CoolDown;


Validate model EnergyConversion. This example validates

Buildings.Fluid.CHPs.BaseClasses.EnergyConversion
for defining the energy conversion control volume.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model EnergyConversion "Validate model EnergyConversion"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData2 per
    "CHP performance data"
  parameter Data.ValidationData2 per1(warmUpByTimeDelay=true)
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.EnergyConversion eneConEngTem(
    final per=per)
    "Energy conversion volume: warm-up by engine temperature"
  Buildings.Fluid.CHPs.BaseClasses.EnergyConversion eneConTimDel(
    final per=per1)
    "Energy conversion volume: warm-up by time delay"

  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable mWat_flow(
    table=[0,0; 300,0.4; 2700,0; 3000,0],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Cooling water flow rate"
  Buildings.Fluid.CHPs.BaseClasses.Controller con(final per=per) "Operation mode"
  Modelica.Blocks.Sources.BooleanTable runSig(startValue=false, table={300,2700})
    "Plant run signal"
  Modelica.Blocks.Sources.BooleanTable avaSig(startValue=true, table={3500})
    "Plant availability signal"
  Buildings.Controls.OBC.CDL.Reals.Sources.Ramp TEng(
    final height=90,
    final duration=600,
    final offset=273.15 + 15,
    final startTime=360) "Engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TWatIn(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15) "Cooling water inlet temperature"
  Modelica.Blocks.Sources.TimeTable PEle(
    table=[0,0; 299,0; 300,2500; 2699,2500;2700,0; 3000,0])
    "Electric power demand"
  Controls.OBC.CDL.Reals.Sources.Constant TRoo(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15)
    "Room temperature"
protected
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot

equation
  connect(con.opeMod, eneConEngTem.opeMod)
  connect(avaSig.y, con.avaSig)
  connect(runSig.y, con.runSig)
  connect(TEng.y, con.TEng)
  connect(TEng.y, eneConEngTem.TEng)
  connect(TWatIn.y, eneConEngTem.TWatIn)
  connect(PEle.y, eneConEngTem.PEle)
  connect(mWat_flow.y[1], eneConEngTem.mWat_flow)
  connect(mWat_flow.y[1], con.mWat_flow)
  connect(TWatIn.y, eneConTimDel.TWatIn)
  connect(mWat_flow.y[1], eneConTimDel.mWat_flow)
  connect(PEle.y, eneConTimDel.PEle)
  connect(con.opeMod, eneConTimDel.opeMod)
  connect(PEle.y, con.PEle)
  connect(eneConEngTem.PEleNet, con.PEleNet)
  connect(TRoo.y, eneConEngTem.TRoo)
  connect(TRoo.y, eneConTimDel.TRoo)
end EnergyConversion;


Validate model EnergyConversionNormal. This example validates

Buildings.Fluid.CHPs.BaseClasses.EnergyConversionNormal
for defining energy conversion for a typical CHP operation.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model EnergyConversionNormal "Validate model EnergyConversionNormal"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.EnergyConversionNormal opeModBas(
    final per=per) "Energy conversion for a typical CHP operation"
  Buildings.Controls.OBC.CDL.Reals.Sources.Ramp PEle(
    final height=5000,
    final duration=360,
    final offset=0,
    final startTime=600) "Electric power"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant mWat_flow(
    final k=0.05) "Water mass flow rate"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TWatIn(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15)
    "Water inlet temperature"

equation
  connect(TWatIn.y, opeModBas.TWatIn)
  connect(mWat_flow.y, opeModBas.mWat_flow)
  connect(PEle.y, opeModBas.PEle)
end EnergyConversionNormal;


Validate model EnergyConversionWarmUp. This example validates

Buildings.Fluid.CHPs.BaseClasses.EnergyConversionWarmUp
for defining energy conversion during the warm-up mode dependent on the engine temperature.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model EnergyConversionWarmUp "Validate model EnergyConversionWarmUp"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData2 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.EnergyConversionWarmUp opeModWarUpEngTem(
    final per=per) "Energy conversion during warm-up by engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Ramp TEng(
    y(final unit="K", displayUnit="degC"),
    final height=90,
    final duration=360,
    final offset=273.15 + 15,
    final startTime=600) "Engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant mWat_flow(
    final k=0.05)
    "Water mass flow rate"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TWatIn(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15)
    "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TRoo(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15)
    "Room temperature"

equation
  connect(TWatIn.y, opeModWarUpEngTem.TWatIn)
  connect(mWat_flow.y, opeModWarUpEngTem.mWat_flow)
  connect(TRoo.y, opeModWarUpEngTem.TRoo)
  connect(TEng.y, opeModWarUpEngTem.TEng)
end EnergyConversionWarmUp;


Validate model EngineTemperature. This example validates

Buildings.Fluid.CHPs.BaseClasses.EngineTemperature
for defining the heat exchange within the engine control volume.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model EngineTemperature "Validate model EngineTemperature"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Fluid.CHPs.BaseClasses.EngineTemperature eng(
    final UAHex=per.UAHex,
    final UALos=per.UALos,
    final capEng=per.capEng,
    final TEngIni=273.15 + 20) "Heat exchange within the engine control volume"
  Buildings.Controls.OBC.CDL.Reals.Sources.Ramp QGen(
    final height=5000,
    final duration=360,
    final offset=0,
    final startTime=600) "Heat generation within the engine"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TRoo(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15)
    "Room temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TWat(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 60)
    "Water temperature"

protected
  Buildings.HeatTransfer.Sources.PrescribedTemperature rooTem
    "Room temperature"
  Buildings.HeatTransfer.Sources.PrescribedTemperature cooWatTem
    "Entering cooling water temperature"

equation
  connect(QGen.y, eng.QGen_flow)
  connect(rooTem.port, eng.TRoo)
  connect(rooTem.T, TRoo.y)
  connect(cooWatTem.T, TWat.y)
  connect(cooWatTem.port, eng.TWat)
end EngineTemperature;


Validate model FilterPower. This example validates

Buildings.Fluid.CHPs.BaseClasses.FilerPower
for applying constraints and sending a warning message if the electric power
is outside boundaries. The constraints include minimum and maximum electric power and
the maximum rate of change in power output.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model FilterPower "Validate model FilterPower"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable PEleDem(
    table=[0,0; 300,500; 600,2000; 900,3000;
           1200,0; 1500,6000; 1800,6000],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Electricity demand"
  Buildings.Fluid.CHPs.BaseClasses.FilterPower filPow(
    final PEleMax=per.PEleMax,
    final PEleMin=per.PEleMin,
    final dPEleMax=per.dPEleMax,
    final use_powerRateLimit=per.use_powerRateLimit)
    "Constraints for electric power"

equation
  connect(PEleDem.y[1], filPow.PEleDem)
end FilterPower;


Validation of the baseclasses

within Buildings.Fluid.CHPs.BaseClasses;
package Validation "Validation of the baseclasses"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Validate model PowerConsumption. This example validates

Buildings.Fluid.CHPs.BaseClasses.PowerConsumption
for calculating the power consumption during the stand-by and cool-down modes of
operation.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model PowerConsumption "Validate model PowerConsumption"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable mWat_flow(
    table=[0,0; 300,0.4; 2700,0; 3000,0],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Water mass flow rate"
  Buildings.Fluid.CHPs.BaseClasses.Controller con(final per=per)
  Modelica.Blocks.Sources.BooleanTable runSig(
    final startValue=false,
    final table={300,2700})
    "Plant run signal"
  Modelica.Blocks.Sources.BooleanTable avaSig(
    final startValue=true,
    final table={3500})
    "Plant availability signal"
  Buildings.Fluid.CHPs.BaseClasses.PowerConsumption powCon(
    final PStaBy=per.PStaBy,
    final PCooDow=per.PCooDow)
    "Internal controller for water mass flow rate"

protected
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot

equation
  connect(avaSig.y, con.avaSig)
  connect(runSig.y, con.runSig)
  connect(con.opeMod,powCon. opeMod)
  connect(mWat_flow.y[1], con.mWat_flow)
end PowerConsumption;


Validate model StandBy. This example validates the transitions to and from the stand-by operating mode.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model StandBy "Validate model StandBy"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"

  Buildings.Controls.OBC.CDL.Reals.Sources.Constant mWat_flow(
    final k=0.5) "Water mass flow rate"
  Buildings.Fluid.CHPs.BaseClasses.Types.Mode actMod "Mode indicator";
  Modelica.StateGraph.TransitionWithSignal transition1 "Off to standby mode"
  Modelica.StateGraph.TransitionWithSignal transition2
    "Standby to pump on mode"

  Modelica.StateGraph.Step staBy(nOut=2, nIn=1)
                                         "Plant is in standby mode"
  Modelica.StateGraph.Step pumOn(nIn=1, nOut=1)
                                 "Plant pump is on"
protected
  Modelica.Blocks.Sources.BooleanTable avaSig(table={300,600,900,1260})
    "Plant availability signal"
  Buildings.Controls.OBC.CDL.Logical.Not notAva "Plant not available"
  Modelica.Blocks.Sources.BooleanTable runSig(table={1200,1260})
    "Plant run signal"
  Modelica.StateGraph.InitialStep off(nIn=2, nOut=1)
                                             "Off mode"
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
  Modelica.StateGraph.TransitionWithSignal transition3(
    final enableTimer=true,
    final waitTime=60) "Pump on to off mode"
  Modelica.StateGraph.TransitionWithSignal transition4 "Standby to off mode"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant minWatFlo(
    final k=per.mWatMin_flow)
    "Minimum water mass flow rate"
  Buildings.Controls.OBC.CDL.Reals.Greater cheMinFlo
    "Check if water mass flow rate is higher than the minimum"

equation
  if off.active then
    actMod = CHPs.BaseClasses.Types.Mode.Off;
  elseif staBy.active then
    actMod = CHPs.BaseClasses.Types.Mode.StandBy;
  else
    actMod = CHPs.BaseClasses.Types.Mode.PumpOn;
  end if;
  connect(transition1.inPort, off.outPort[1])
  connect(transition4.outPort, off.inPort[1])
  connect(transition3.outPort, off.inPort[2])
  connect(avaSig.y, transition1.condition)
  connect(avaSig.y, notAva.u)
  connect(notAva.y, transition4.condition)
  connect(runSig.y, transition2.condition)
  connect(mWat_flow.y, cheMinFlo.u1)
  connect(minWatFlo.y, cheMinFlo.u2)
  connect(cheMinFlo.y, transition3.condition)
  connect(pumOn.outPort[1], transition3.inPort)
  connect(transition2.outPort, pumOn.inPort[1])
  connect(staBy.outPort[1], transition2.inPort)
  connect(transition1.outPort, staBy.inPort[1])
  connect(staBy.outPort[2], transition4.inPort)
end StandBy;


Validate model WarmUp if warm-up by engine temperature. This example validates

Buildings.Fluid.CHPs.BaseClasses.WarmUpLeaving.
The example is for the warm-up mode dependent on the engine temperature
(e.g. CHPs with Stirling engines).

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model WarmUpTemperature
  "Validate model WarmUp if warm-up by engine temperature"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData2 per
    "CHP performance data"

  Buildings.Controls.OBC.CDL.Reals.Sources.Ramp TEng(
    final height=200,
    final duration=360,
    final offset=273.15,
    final startTime=0) "Engine temperature"
  Modelica.Blocks.Sources.BooleanTable runSig(
    final startValue=true,
    final table={600})
    "Plant run signal"
  Buildings.Fluid.CHPs.BaseClasses.Types.Mode actMod "Mode indicator";
  Modelica.StateGraph.TransitionWithSignal transition1 "Off to warm-up mode"
  Modelica.StateGraph.TransitionWithSignal transition2 "Warm-up to normal mode"
  Modelica.StateGraph.TransitionWithSignal transition3 "Normal to off mode"

  Modelica.StateGraph.StepWithSignal warUp(nIn=2, nOut=2)
    "Plant is in warm-up mode"
  Controls.OBC.CDL.Reals.Sources.Constant PEle[2](k={1,2})
    "Power output and demand"
protected
  Modelica.StateGraph.InitialStep off(final nIn=2, final nOut=2) "Off mode"
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
  Modelica.StateGraph.TransitionWithSignal transition4 "Warm-up to off mode"
  Modelica.StateGraph.Step nor(nIn=1, nOut=1) "Normal mode"
  Modelica.StateGraph.Transition transition5(
    final condition=false,
    final waitTime=0)
  Buildings.Controls.OBC.CDL.Logical.Not notRun "Plant not run"
  WarmUpLeaving warUpCtr(
    final timeDelayStart=per.timeDelayStart,
    final TEngNom=per.TEngNom,
    final warmUpByTimeDelay=per.warmUpByTimeDelay) "Warm-up control sequence"
equation
  if off.active then
    actMod = CHPs.BaseClasses.Types.Mode.Off;
  elseif warUp.active then
    actMod = CHPs.BaseClasses.Types.Mode.WarmUp;
  else
    actMod = CHPs.BaseClasses.Types.Mode.Normal;
  end if;
  connect(transition4.outPort, off.inPort[1])
  connect(transition1.inPort, off.outPort[1])
  connect(nor.inPort[1], transition2.outPort)
  connect(nor.outPort[1], transition3.inPort)
  connect(transition3.outPort, off.inPort[2])
  connect(transition5.inPort, off.outPort[2])
  connect(runSig.y, notRun.u)
  connect(runSig.y, transition1.condition)
  connect(notRun.y, transition3.condition)
  connect(notRun.y, transition4.condition)
  connect(warUp.active, warUpCtr.actWarUp)
  connect(transition1.outPort, warUp.inPort[1])
  connect(warUp.outPort[1], transition2.inPort)
  connect(transition5.outPort, warUp.inPort[2])
  connect(warUp.outPort[2], transition4.inPort)
  connect(TEng.y, warUpCtr.TEng)
  connect(warUpCtr.y, transition2.condition)
  connect(PEle[1].y, warUpCtr.PEleNet)
  connect(PEle[2].y, warUpCtr.PEle)
end WarmUpTemperature;


Validate model WarmUp if warm-up by time delay. This example validates

Buildings.Fluid.CHPs.BaseClasses.WarmUpLeaving.
The example is for the warm-up mode with the static time delay
(e.g. CHPs with internal combustion engines).

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model WarmUpTimeDelay
  "Validate model WarmUp if warm-up by time delay"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData1 per
    "CHP performance data"
  Modelica.Blocks.Sources.BooleanTable runSig(final table={300,330,600})
  "Plant run signal"
  Buildings.Fluid.CHPs.BaseClasses.Types.Mode actMod "Mode indicator";
  Modelica.StateGraph.TransitionWithSignal transition1 "Off to warm-up mode"
  Modelica.StateGraph.TransitionWithSignal transition2 "Warm-up to normal mode"
  Modelica.StateGraph.TransitionWithSignal transition3 "Normal to off mode"
  Modelica.StateGraph.StepWithSignal warUp(nIn=2, nOut=2)
    "Plant is in warm-up mode"
protected
  Modelica.StateGraph.InitialStep off(nIn=2, nOut=2) "Off mode"
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
  Modelica.StateGraph.TransitionWithSignal transition4 "Warm-up to off mode"
  Modelica.StateGraph.Step nor(nIn=1, nOut=1) "Normal mode"
  Modelica.StateGraph.Transition transition5(
    final condition=false,
    final waitTime=0)
  Buildings.Controls.OBC.CDL.Logical.Not notRun "Plant not run"
  WarmUpLeaving warUpCtr(
    final timeDelayStart=per.timeDelayStart,
    final TEngNom=per.TEngNom,
    final PEleMax=per.PEleMax,
    final warmUpByTimeDelay=per.warmUpByTimeDelay) "Warm-up control sequence"
equation
  if off.active then
    actMod = CHPs.BaseClasses.Types.Mode.Off;
  elseif warUp.active then
    actMod = CHPs.BaseClasses.Types.Mode.WarmUp;
  else
    actMod = CHPs.BaseClasses.Types.Mode.Normal;
  end if;
  connect(transition4.outPort, off.inPort[1])
  connect(transition1.inPort, off.outPort[1])
  connect(nor.inPort[1], transition2.outPort)
  connect(nor.outPort[1], transition3.inPort)
  connect(transition3.outPort, off.inPort[2])
  connect(transition5.inPort, off.outPort[2])
  connect(runSig.y, transition1.condition)
  connect(runSig.y, notRun.u)
  connect(notRun.y, transition3.condition)
  connect(notRun.y, transition4.condition)
  connect(warUpCtr.y, transition2.condition)
  connect(warUp.active, warUpCtr.actWarUp)
  connect(transition1.outPort, warUp.inPort[1])
  connect(warUp.outPort[1], transition2.inPort)
  connect(warUp.outPort[2], transition4.inPort)
  connect(transition5.outPort, warUp.inPort[2])
end WarmUpTimeDelay;


Validate model WaterFlowControl. This example validates

Buildings.Fluid.CHPs.BaseClasses.WaterFlowControl
for calculating the optimum cooling water flow rate based on internal contol.

within Buildings.Fluid.CHPs.BaseClasses.Validation;
model WaterFlowControl "Validate model WaterFlowControl"
  extends Modelica.Icons.Example;

  parameter Buildings.Fluid.CHPs.Data.ValidationData2 per
    "CHP performance data"
  Buildings.Fluid.CHPs.BaseClasses.WaterFlowControl conWat(final per=per)
    "Internal controller for water mass flow rate"
protected
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable mWat_flow(
    table=[0,0; 300,0.4; 2700,0; 3000,0],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Water mass flow rate"
  Buildings.Fluid.CHPs.BaseClasses.Controller con(final per=per)
    "Operation mode controller"
  Modelica.Blocks.Sources.BooleanTable runSig(
    final startValue=false,
    final table={300,2700})
    "Plant run signal"
  Modelica.Blocks.Sources.BooleanTable avaSig(
    final startValue=true,
    final table={3500})
    "Plant availability signal"
  Buildings.Controls.OBC.CDL.Reals.Sources.Ramp TEng(
    y(final unit="K", displayUnit="degC"),
    final height=90,
    final duration=300,
    final offset=273.15 + 15,
    final startTime=360) "Engine temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TWatIn(
    y(final unit="K", displayUnit="degC"),
    final k=273.15 + 15)
    "Water inlet temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable PEle(
    final table=[0,0; 300,2500; 2700,0; 3000,0],
    final smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments)
    "Electric power demand"
equation
  connect(avaSig.y, con.avaSig)
  connect(runSig.y, con.runSig)
  connect(TEng.y, con.TEng)
  connect(con.opeMod, conWat.opeMod)
  connect(TWatIn.y, conWat.TWatIn)
  connect(mWat_flow.y[1], con.mWat_flow)
  connect(PEle.y[1], conWat.PEle)
  connect(PEle.y[1], con.PEle)
  connect(PEle.y[1], con.PEleNet)
end WaterFlowControl;


Generic data for CHP models. This is the base record for CHP models.

within Buildings.Fluid.CHPs.Data;
record Generic "Generic data for CHP models"
  extends Modelica.Icons.Record;
  parameter Real[27] coeEtaQ
    "Vector of coefficients used to calculate thermal efficiency of the engine.
    The independent variable x1 is the steady-state power output,
    x2 is the cooling water mass flow rate, x3 is the cooling water inlet temperature.
    From index 1 to 27, coefficients correspond to the following terms:
    constant, x1^2, x1, x2^2, x2, x3^2, x3,
    x1^2*x2^2, x1*x2, x1*x2^2, x1^2*x2,
    x1^2*x3^2, x1*x3, x1*x3^2, x1^2*x3,
    x2^2*x3^2, x2*x3, x2*x3^2, x2^2*x3,
    x1^2*x2^2*x3^2, x1^2*x2^2*x3, x1^2*x2*x3^2, x1*x2^2*x3^2,
    x1^2*x2*x3, x1*x2^2*x3,  x1*x2*x3^2, x1*x2*x3";
  parameter Real[27] coeEtaE
    "Vector of coefficients used to calculate electrical conversion efficiency
    of the engine. The independent variables and mapping of the coefficients
    to the polynomial terms are the same as for the thermal efficiency";
  parameter Boolean compute_coolingWaterFlowRate=true
    "If true, then an empirical correlation is used to calculate
    cooling water mass flow rate based on internal control";
  parameter Real[6] coeMasWat
    "Vector of coefficients used to calculate cooling water mass flow rate
    in case coolingWaterControl is true.
    The independent variable x1 is the steady-state power output,
    x2 is the cooling water mass flow rate.
    From index 1 to 6, coefficients correspond to the following terms:
    constant, x1, x1^2, x2, x2^2, x1*x2";
  parameter Real[3] coeMasAir
    "Vector of coefficients used to calculate air mass flow rate.
    The independent variable x1 is the fuel mass flow rate.
    From index 1 to 3, coefficients correspond to the following terms:
    constant, x1, x1^2";
  parameter Modelica.Units.SI.ThermalConductance UAHex
    "Thermal conductance between the engine and cooling water";
  parameter Modelica.Units.SI.ThermalConductance UALos
    "Thermal conductance between the engine and surroundings";
  parameter Modelica.Units.SI.HeatCapacity capEng
    "Thermal capacitance of the engine control volume";
  parameter Modelica.Units.SI.HeatCapacity capHeaRec
    "Thermal capacitance of heat recovery portion";
  parameter Boolean warmUpByTimeDelay=true
    "If true, the plant will be in warm-up mode depending on the delay time,
    otherwise depending on engine temperature ";
  parameter Modelica.Units.SI.Time timeDelayStart=60
    "Time delay between activation and power generation";
  parameter Modelica.Units.SI.Temperature TEngNom=273.15 + 100
    "Nominal engine operating temperature";
  parameter Boolean coolDownOptional=false
    "If true, cooldown is optional. The model will complete cooldown before
    switching to standby, but if reactivated during cooldown period, it
    will immediately switch into warm-up mode";
  parameter Modelica.Units.SI.Time timeDelayCool=0 "Cooldown period";
  parameter Modelica.Units.SI.Power PEleMax "Maximum power output";
  parameter Modelica.Units.SI.Power PEleMin=0 "Minimum power output";
  parameter Modelica.Units.SI.MassFlowRate mWatMin_flow=0
    "Minimum cooling water mass flow rate";
  parameter Modelica.Units.SI.Temperature TWatMax=373.15
    "Maximum cooling water temperature";
  parameter Boolean use_powerRateLimit=false
    "If true, the rate at which net power output can change is limited";
  parameter Boolean use_fuelRateLimit=false
    "If true, the rate at which fuel mass flow rate can change is limited";
  parameter Real dPEleMax(final unit="W/s")
    "Maximum rate at which net power output can change";
  parameter Real dmFueMax_flow(final unit="kg/s2")
    "Maximum rate at which fuel mass flow rate can change";
  parameter Modelica.Units.SI.Power PStaBy "Standby electric power";
  parameter Modelica.Units.SI.Power PCooDow "Cooldown electric power";
  parameter Real LHVFue(final unit="J/kg") = 47.614e6
    "Lower heating value of fuel";
  parameter Real kF(final unit="1") = 1
    "Warm-up fuel coefficient";
  parameter Real kP(final unit="1") = 1
    "Warm-up power coefficient";
  parameter Real rFue(final unit="1") = 10
    "Warm-up maximum fuel flow ratio";
end Generic;


Performance data for CHP models

within Buildings.Fluid.CHPs;
package Data "Performance data for CHP models"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Organic Rankine cycle as a bottoming cycle. Model of an organic Rankine cycle (ORC) as a bottoming cycle.

within Buildings.Fluid.CHPs.OrganicRankine;
model Cycle "Organic Rankine cycle as a bottoming cycle"

  extends Buildings.Fluid.Interfaces.FourPortHeatMassExchanger(
    final m1_flow_nominal = mHot_flow_nominal,
    final dp1_nominal = dpHot_nominal,
    final m2_flow_nominal = mCol_flow_nominal,
    final dp2_nominal = dpCol_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T1_start = max(pro.T)*2/3 + min(pro.T)*1/3,
    T2_start = max(pro.T)*1/10 + min(pro.T)*9/10,
    redeclare final Buildings.Fluid.MixingVolumes.MixingVolume vol2(
      V=m2_flow_nominal*tau2/rho2_nominal,
      nPorts=2,
      final prescribedHeatFlowRate=true),
    final vol1(
      final prescribedHeatFlowRate=true));

  replaceable parameter Buildings.Fluid.CHPs.OrganicRankine.Data.Generic pro
    constrainedby Buildings.Fluid.CHPs.OrganicRankine.Data.Generic
    "Property records of the working fluid"
  parameter Modelica.Units.SI.MassFlowRate mHot_flow_nominal
    "Nominal mass flow rate of the evaporator fluid"
  parameter Modelica.Units.SI.PressureDifference dpHot_nominal(
    displayUnit="Pa")
    "Nominal pressure drop of the hot fluid in evaporator"
  parameter Modelica.Units.SI.TemperatureDifference dTPinEva_set(
    final min = 0) = 5
    "Set evaporator pinch point temperature difference"
  parameter Boolean useEvaporatingPressure = false
    "Set true to specify working fluid evaporating pressure instead of temperature"
  parameter Modelica.Units.SI.ThermodynamicTemperature TWorEva =
    max(pro.T)*2/3 + min(pro.T)*1/3
    "Evaporating temperature of the working fluid"
  parameter Modelica.Units.SI.Pressure pWorEva(displayUnit="kPa") =
    max(pro.p)*2/3 + min(pro.p)*1/3
    "Evaporating pressure of the working fluid"
  parameter Modelica.Units.SI.MassFlowRate mCol_flow_nominal
    "Nominal mass flow rate of the condenser fluid"
  parameter Modelica.Units.SI.PressureDifference dpCol_nominal(
    displayUnit="Pa")
    "Nominal pressure drop of the cold fluid in condenser"
  parameter Modelica.Units.SI.TemperatureDifference dTPinCon(
    final min = 0) = 10
    "Condenser pinch point temperature difference"
  parameter Boolean useLowCondenserPressureWarning = true
    "If true, issues warning if pCon < 101325 Pa"
  parameter Modelica.Units.SI.MassFlowRate mWor_flow_max(
    final min = 0)
    "Upper bound of working fluid flow rate"
  parameter Modelica.Units.SI.MassFlowRate mWor_flow_min(
    final min = 0)
    "Lower bound of working fluid flow rate"
  parameter Modelica.Units.SI.MassFlowRate mWor_flow_hysteresis
    = mWor_flow_min + (mWor_flow_max - mWor_flow_min) * 0.1
    "Hysteresis for turning off the cycle when flow too low"
  parameter Modelica.Units.SI.Efficiency etaExp
    "Expander efficiency"
  parameter Modelica.Units.SI.Efficiency etaPum
    "Pump efficiency"

  Modelica.Blocks.Interfaces.BooleanInput ena
    "Enable cycle; set false to force working fluid flow to zero"

  Modelica.Blocks.Interfaces.RealOutput PExp(
    final quantity="Power",
    final unit="W") "Expander power generation"
  Modelica.Blocks.Interfaces.RealOutput QEva_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Evaporator heat flow rate into the cycle"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Condenser heat flow rate out of the cycle"
  Modelica.Blocks.Interfaces.BooleanOutput on_actual
    "Actual on off status of the cycle"
  Modelica.Blocks.Interfaces.RealOutput PPum(
    final quantity="Power",
    final unit="W")
    "Electrical power consumption of the pump"

  Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.FixedEvaporating cyc(
    final pro=pro,
    final mWor_flow_max=mWor_flow_max,
    final mWor_flow_min=mWor_flow_min,
    final mWor_flow_hysteresis=mWor_flow_hysteresis,
    final TWorEva =
            if useEvaporatingPressure
            then Buildings.Utilities.Math.Functions.smoothInterpolation(
                   x = pWorEva,
                   xSup = pro.p,
                   ySup = pro.T)
            else TWorEva,
    final dTPinEva_set=dTPinEva_set,
    final dTPinCon=dTPinCon,
    final cpHot=Medium1.specificHeatCapacityCp(sta1_nominal),
    final cpCol=Medium2.specificHeatCapacityCp(sta2_nominal),
    final etaExp=etaExp,
    final etaPum=etaPum,
    final useLowCondenserPressureWarning=useLowCondenserPressureWarning)
    "Thermodynamic computations of the organic Rankine cycle"

protected
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow preHeaFloEva
    "Prescribed heat flow rate"
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow preHeaFloCon
    "Prescribed heat flow rate"
  Modelica.Blocks.Sources.RealExpression expTHotIn(y=Medium1.temperature(
        state=Medium1.setState_phX(
          p=port_a1.p,
          h=inStream(port_a1.h_outflow),
          X=inStream(port_a1.Xi_outflow))))
    "Expression for evaporator hot fluid incoming temperature"
  Modelica.Blocks.Sources.RealExpression expMHot_flow(y=m1_flow)
    "Expression for evaporator hot fluid flow rate"
  Modelica.Blocks.Sources.RealExpression expTColIn(y=Medium2.temperature(
        state=Medium2.setState_phX(
          p=port_a2.p,
          h=inStream(port_a2.h_outflow),
          X=inStream(port_a2.Xi_outflow))))
    "Expression for condenser cold fluid incoming temperature"
  Modelica.Blocks.Sources.RealExpression expMCol_flow(y=m2_flow)
    "Expression for condenser cold fluid flow rate"
equation
  connect(preHeaFloEva.port, vol1.heatPort)
  connect(preHeaFloCon.port, vol2.heatPort)
  connect(expTHotIn.y, cyc.THotIn)
  connect(expMHot_flow.y, cyc.mHot_flow)
  connect(expTColIn.y, cyc.TColIn)
  connect(expMCol_flow.y, cyc.mCol_flow)
  connect(cyc.PExp,PExp)
  connect(ena, cyc.ena)
  connect(cyc.QEva_flow, QEva_flow)
  connect(cyc.QCon_flow, QCon_flow)
  connect(cyc.on_actual, on_actual)
  connect(cyc.PPum, PPum)
  connect(cyc.QEva_flow, preHeaFloEva.Q_flow)
  connect(cyc.QCon_flow, preHeaFloCon.Q_flow)
end Cycle;


Package for the rankine cycle

within Buildings.Fluid.CHPs;
package OrganicRankine "Package for the rankine cycle"
  extends Modelica.Icons.VariantsPackage;
end OrganicRankine;


Thermodynamic computations of the ORC with fixed evaporating temperature.

within Buildings.Fluid.CHPs.OrganicRankine.BaseClasses;
model FixedEvaporating
  "Thermodynamic computations of the ORC with fixed evaporating temperature"
  extends Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.InterpolateStates(
    TEva=TWorEva,
    TCon=TWorCon);

  parameter Modelica.Units.SI.TemperatureDifference dTPinEva_set(
    final min = 0)
    "Set evaporator pinch point temperature difference"
  parameter Modelica.Units.SI.SpecificHeatCapacity cpHot
    "Constant specific heat capacity"
  parameter Modelica.Units.SI.ThermodynamicTemperature TWorEva
    "Working fluid evaporator temperature"
  parameter Modelica.Units.SI.TemperatureDifference dTPinCon
    "Pinch point temperature difference of condenser"
  parameter Modelica.Units.SI.SpecificHeatCapacity cpCol
    "Constant specific heat capacity"
  parameter Boolean useLowCondenserPressureWarning = true
    "If true, issues warning if pCon < 101325 Pa";
  parameter Modelica.Units.SI.MassFlowRate mWor_flow_max(
    final min = 0)
    "Upper bound of working fluid flow rate"
  parameter Modelica.Units.SI.MassFlowRate mWor_flow_min(
    final min = 0)
    "Lower bound of working fluid flow rate"
  parameter Modelica.Units.SI.MassFlowRate mWor_flow_hysteresis(
    final min = 0)
    "Hysteresis for turning off the cycle when flow too low"

  Modelica.Blocks.Interfaces.RealInput THotIn(
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit="degC") "Incoming temperature of hot fluid in evaporator"
  Modelica.Blocks.Interfaces.RealInput mHot_flow(
    final quantity="MassFlowRate",
    final unit="kg/s") "Evaporator hot fluid flow rate"
  Modelica.Blocks.Interfaces.RealInput TColIn(
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit="degC") "Incoming temperature of cold fluid in condenser"
  Modelica.Blocks.Interfaces.RealInput mCol_flow(
    final quantity="MassFlowRate",
    final unit="kg/s") "Condenser cold fluid flow rate"
  Modelica.Blocks.Interfaces.BooleanInput ena
    "Enable cycle; set false to force working fluid flow to zero"

  Modelica.Blocks.Interfaces.RealOutput QEva_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Evaporator heat flow rate into the cycle"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Condenser heat flow rate out of the cycle"
  Modelica.Blocks.Interfaces.RealOutput PExp(
    final quantity="Power",
    final unit="W") "Expander power generation"
  Modelica.Blocks.Interfaces.RealOutput PPum(
    final quantity="Power",
    final unit="W")
    "Electrical power consumption of the pump"
  Modelica.Blocks.Interfaces.BooleanOutput on_actual = ena and hys.y
    "Actual on off status of the cycle"

  Modelica.Units.SI.ThermodynamicTemperature THotOut(
    start = TWorEva + dTPinEva_set)
    "Outgoing temperature of the evaporator hot fluid";
  Modelica.Units.SI.ThermodynamicTemperature THotPin(
    start = TWorEva + dTPinEva_set)
    "Hot fluid temperature at pinch point";
  Modelica.Units.SI.TemperatureDifference dTPinEva(start = dTPinEva_set)
    "Pinch point temperature difference of evaporator";
  Modelica.Units.SI.ThermodynamicTemperature TWorCon
    "Working fluid condensing temperature";
  Modelica.Units.SI.ThermodynamicTemperature TColOut
    "Fluid temperature out of the condenser";
  Modelica.Units.SI.ThermodynamicTemperature TColPin(
    start = 300)
    "Cold fluid temperature at pinch point";
  Modelica.Units.SI.MassFlowRate mWor_flow
    "Mass flow rate of the working fluid"
  Modelica.Blocks.Logical.Hysteresis hys(
    uLow = mWor_flow_min,
    uHigh = mWor_flow_min + mWor_flow_hysteresis,
    u = mWor_flow_internal)
    "Hysteresis for turning off cycle when working fluid flow too low";

protected
  Modelica.Units.SI.MassFlowRate mWor_flow_internal(
    start = (mWor_flow_max + mWor_flow_min) / 2)
    "Working fluid flow rate, intermediate variable";
  Modelica.Units.SI.ThermodynamicTemperature THotPin_internal
    "Hot fluid temperature at pinch point, intermedaite variable";
  Modelica.Units.SI.ThermodynamicTemperature THotOut_internal
    "Hot fluid outgoing temperature, intermediate variable";
  Modelica.Units.SI.HeatFlowRate QEva_flow_internal
    "Evaporator heat flow rate, intermediate variable";

equation

  assert(not (TWorCon > TWorEva - 1 and ena),
"*** In " + getInstanceName() +
": Working fluid condensing temperature is too high and close to evaporating temperature.
This is likely caused by the flow rate of cold fluid in the condenser being too low
when the ORC is on.");

  assert(not (pCon < 101325 - 1 and ena and useLowCondenserPressureWarning),
"*** In " + getInstanceName() +
": Working fluid condensing pressure is lower than 101325 Pa.
If this is intended, set useLowCondenserPressureWarning = false to turn off this warning.",
level=AssertionLevel.warning);

  if ena then
    // Evaporator
    QEva_flow = mHot_flow * cpHot * (THotOut - THotIn);
    QEva_flow = mWor_flow * (hPumOut - hExpInl);
    (THotPin - THotOut) * (hExpInl - hPumOut)
    = (hPinEva - hPumOut) * (THotIn - THotOut);
    // Condenser
    QCon_flow = mCol_flow * cpCol * (TColOut - TColIn);
    QCon_flow = mWor_flow * (hExpOut - hPumInl);
    (TColPin - TColIn) * (hExpOut - hPumInl)
    = (hPinCon - hPumInl) * (TColOut - TColIn);
  else
    // Evaporator
    QEva_flow = 0;
    THotOut = THotIn;
    THotPin = THotOut;
    // Condenser
    QCon_flow = 0;
    TColOut = TColIn;
    TColPin = TColIn;
  end if;
  dTPinEva = THotPin - TWorEva; // Evaporator
  dTPinCon = TWorCon - TColPin; // Condenser

  // Evaporator internal computation
  QEva_flow_internal = mHot_flow * cpHot * (THotOut_internal - THotIn);
  QEva_flow_internal = mWor_flow_internal * (hPumOut - hExpInl);
  (THotPin_internal - THotOut_internal) * (hExpInl - hPumOut)
  = (hPinEva - hPumOut) * (THotIn - THotOut_internal);
  dTPinEva_set = THotPin_internal - TWorEva;

  // Other components
  PExp = mWor_flow * (hExpOut - hExpInl);
  PPum = mWor_flow * (hPumOut - hPumInl);
  mWor_flow =
    if on_actual
    then
      Buildings.Utilities.Math.Functions.smoothMin(
      x1=mWor_flow_internal,
      x2=mWor_flow_max,
      deltaX=mWor_flow_min*1E-2)
    else 0;
end FixedEvaporating;


Interpolate states of a working fluid.

within Buildings.Fluid.CHPs.OrganicRankine.BaseClasses;
model InterpolateStates "Interpolate states of a working fluid"

  // Input properties
  replaceable parameter Buildings.Fluid.CHPs.OrganicRankine.Data.Generic pro
    "Property records of the working fluid"
  parameter Modelica.Units.SI.SpecificEnthalpy h_small =
    (max(pro.hSatVap) - min(pro.hSatLiq)) * 1E-4
    "A small value for specific enthalpy regularisation"

  input Modelica.Units.SI.ThermodynamicTemperature TEva
    "Evaporating temperature";
  input Modelica.Units.SI.ThermodynamicTemperature TCon
    "Condensing temperature";
  input Modelica.Units.SI.Efficiency etaExp
    "Expander efficiency";
  input Modelica.Units.SI.Efficiency etaPum
    "Pump efficiency";

  // Once-interpolated properties
  Modelica.Units.SI.AbsolutePressure pEva(
    displayUnit = "kPa") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TEva,
      xd = pro.T,
      yd = pro.p,
      d = pDer_T)
    "Evaporating pressure";
  Modelica.Units.SI.AbsolutePressure pCon(
    displayUnit = "kPa") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.p,
      d = pDer_T)
    "Condensing pressure";
  Modelica.Units.SI.Density rhoLiq =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.rhoLiq,
      d = rhoLiqDer_T)
    "Saturated liquid density";
  Modelica.Units.SI.SpecificEntropy sPumInl =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.sSatLiq,
      d = sSatLiqDer_T)
    "Specific entropy at pump inlet";
  Modelica.Units.SI.SpecificEnthalpy hPumInl(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.hSatLiq,
      d = hSatLiqDer_T)
    "Specific enthalpy at pump inlet";
  Modelica.Units.SI.SpecificEnthalpy hPinEva(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TEva,
      xd = pro.T,
      yd = pro.hSatLiq,
      d = hSatLiqDer_T)
    "Specific enthalpy at evaporator-side pinch point";
  Modelica.Units.SI.SpecificEnthalpy hPinCon(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.hSatVap,
      d = hSatVapDer_T)
    "Specific enthalpy at condenser-side pinch point";
  Modelica.Units.SI.SpecificEntropy sSatVapCon(
    start = max(pro.sSatVap) * 0.1 + min(pro.sSatVap) * 0.9) =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.sSatVap,
      d = sSatVapDer_T)
    "Specific entropy of saturated vapor at the condenser";
  Modelica.Units.SI.SpecificEntropy sRefCon =
    Buildings.Utilities.Math.Functions.interpolate(
      u = pCon,
      xd = pro.p,
      yd = pro.sRef,
      d = sRefDer_p)
    "Specific entropy on reference line at condensing pressure";
  Modelica.Units.SI.SpecificEntropy sSatVapEva =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TEva,
      xd = pro.T,
      yd = pro.sSatVap,
      d = sSatVapDer_T)
    "Specific entropy of saturated vapor at evaporating temperature";
  Modelica.Units.SI.SpecificEnthalpy hSatVapEva(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TEva,
      xd = pro.T,
      yd = pro.hSatVap,
      d = hSatVapDer_T)
    "Specific enthalpy of saturated vapor at evaporating temperature";
  Modelica.Units.SI.SpecificEntropy sRefEva =
    Buildings.Utilities.Math.Functions.interpolate(
      u = pEva,
      xd = pro.p,
      yd = pro.sRef,
      d = sRefDer_p)
    "Specific entropy on reference line at evaporating pressure";
  Modelica.Units.SI.SpecificEnthalpy hSatVapCon(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = TCon,
      xd = pro.T,
      yd = pro.hSatVap,
      d = hSatVapDer_T)
    "Specific enthalpy of saturated vapor at the condensing temperature";
  Modelica.Units.SI.SpecificEnthalpy hRefCon(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = pCon,
      xd = pro.p,
      yd = pro.hRef,
      d = hRefDer_p)
    "Specific enthalpy on reference line at condensing pressure";
  Modelica.Units.SI.SpecificEnthalpy hRefEva(
    displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.interpolate(
      u = pEva,
      xd = pro.p,
      yd = pro.hRef,
      d = hRefDer_p)
    "Specific enthalpy on reference line at evaporating pressure";

  // Computed properties not interpolated
  Modelica.Units.SI.SpecificEnthalpy hPumOut(displayUnit = "kJ/kg") =
    hPumInl + wPum
    "Specific enthalpy at pump outlet";
  Modelica.Units.SI.SpecificEnthalpy hExpInl(displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.regStep(
      x = h_reg,
      y1 = hSatVapEva,
      y2 = hExpInlWet,
      x_small = h_small)
    "Specific enthalpy at expander inlet";
  Modelica.Units.SI.SpecificEnthalpy hExpOut(displayUnit = "kJ/kg") =
    Buildings.Utilities.Math.Functions.regStep(
      x = h_reg,
      y1 = hExpOutDry,
      y2 = hSatVapCon,
      x_small = h_small)
    "Specific enthalpy at expander inlet";
  Modelica.Units.SI.TemperatureDifference dTSup = max(0, dTSupWet)
    "Superheating temperature differential";

  // Energy transfer
  Modelica.Units.SI.SpecificEnergy qEva = hPumOut - hExpInl
    "Evaporator specific energy transfer into the cycle";
  Modelica.Units.SI.SpecificEnergy qCon = hExpOut - hPumInl
    "Condenser specific energy transfer out of the cycle";
  Modelica.Units.SI.SpecificEnergy wExp = hExpOut - hExpInl
    "Expander specific work";
  Modelica.Units.SI.SpecificEnergy wPum = (pEva - pCon) / (rhoLiq * etaPum)
    "Pump specific work";
  Modelica.Units.SI.Efficiency etaThe(min=0) =
    (wExp + wPum) / qEva "Thermal efficiency";

protected
  Modelica.Units.SI.SpecificEnthalpy h_reg = hExpOutDry - hSatVapCon
    "For regularisation; if > 0, dry cycle";

  // Intermediate property computation
  // 1. Dry cycle
  //      expander inlet = saturated vapor at evaporating pressure (known),
  //      expander outlet = superheated vapor at condensing pressure (solved).
  Modelica.Units.SI.SpecificEntropy sExpOutDryIse = sSatVapEva
    "Specific entropy at isentropic expander outlet, assuming dry cycle";
  Modelica.Units.SI.SpecificEnthalpy hExpOutDryIse(
    displayUnit = "kJ/kg") =
    if sExpOutDryIse > sSatVapCon
    then (hRefCon - hSatVapCon) * (sExpOutDryIse - sSatVapCon)
         / (sRefCon - sSatVapCon) + hSatVapCon
    else (hSatVapCon - hPumInl) * (sExpOutDryIse - sPumInl)
         / (sSatVapCon - sPumInl) + hPumInl
    "Specific enthalpy at expander outlet, assuming dry cycle";
  Modelica.Units.SI.SpecificEnthalpy hExpOutDry(
    displayUnit = "kJ/kg") =
    hSatVapEva - (hSatVapEva - hExpOutDryIse) * etaExp
    "Specific enthalpy at expander outlet, assuming dry cycle";
  // 2. Wet cycle
  //      expander inlet = superheated vapor at evaporating pressure (solved),
  //      expander outlet = saturated vapor at condensing pressure (known).
  Modelica.Units.SI.SpecificEntropy sExpInlWetIse = sSatVapCon
    "Specific entropy at isentropic expander inlet, assuming wet cycle";
  Modelica.Units.SI.SpecificEnthalpy hExpInlWetIse(
    displayUnit = "kJ/kg") =
    (hRefEva - hSatVapEva) * (sExpInlWetIse - sSatVapEva)
    / (sRefEva - sSatVapEva) + hSatVapEva
    "Specific enthalpy at expander inlet, assuming wet cycle";
  Modelica.Units.SI.SpecificEnthalpy hExpInlWet(
    displayUnit = "kJ/kg") =
    (hExpInlWetIse - hSatVapCon) * etaExp + hSatVapCon
    "Specific enthalpy at expander inlet, assuming wet cycle";
  Modelica.Units.SI.TemperatureDifference dTSupWet(
    displayUnit = "K") =
    (hExpInlWet - hSatVapEva) * pro.dTRef / (hRefEva - hSatVapEva)
    "Superheating temperature differential, assuming wet cycle";

  // Derivatives
  final parameter Real pDer_T[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.T,
      y = pro.p,
      ensureMonotonicity = true)
  "Derivative of saturation pressure vs. saturation temperature for cubic spline";
  final parameter Real rhoLiqDer_T[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.T,
      y = pro.rhoLiq,
      ensureMonotonicity = true)
  "Derivative of saturated liquid density vs. temperature for cubic spline";
  final parameter Real sSatLiqDer_T[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.T,
      y = pro.sSatLiq,
      ensureMonotonicity = true)
  "Derivative of saturated liquid entropy vs. temperature for cubic spline";
  final parameter Real sSatVapDer_T[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.T,
      y = pro.sSatVap,
      ensureMonotonicity = false)
  "Derivative of saturated vapor entropy vs. temperature for cubic spline";
  final parameter Real sRefDer_p[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.p,
      y = pro.sRef,
      ensureMonotonicity = false)
  "Derivative of reference entropy vs. pressure for cubic spline";
  final parameter Real hSatLiqDer_T[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.T,
      y = pro.hSatLiq,
      ensureMonotonicity = true)
  "Derivative of saturated liquid enthalpy vs. temperature for cubic spline";
  final parameter Real hSatVapDer_T[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.T,
      y = pro.hSatVap,
      ensureMonotonicity = false)
  "Derivative of saturated vapor enthalpy vs. temperature for cubic spline";
  final parameter Real hRefDer_p[pro.n]=
    Buildings.Utilities.Math.Functions.splineDerivatives(
      x = pro.p,
      y = pro.hRef,
      ensureMonotonicity = false)
  "Derivative of reference enthaly vs. pressure for cubic spline";
end InterpolateStates;


Package with base classes

within Buildings.Fluid.CHPs.OrganicRankine;
package BaseClasses "Package with base classes"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


Organic Rankine cycle with a dry working fluid. This model validates the basic use of

Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.InterpolateStates.

within Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.Validation;
model DryFluid
  "Organic Rankine cycle with a dry working fluid"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.ThermodynamicTemperature TEva = 450
    "Evaporating temperature";
  parameter Modelica.Units.SI.ThermodynamicTemperature TCon = 310
    "Condensing temperature";
  parameter Modelica.Units.SI.Efficiency etaExp = 0.85
    "Expander efficiency";
  parameter Modelica.Units.SI.Efficiency etaPum = 0.7
    "Pump efficiency";

  Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.InterpolateStates cyc(
    final pro = pro,
    final TEva = TEva,
    final TCon = TCon,
    final etaExp = etaExp,
    final etaPum = etaPum) "Interpolate working fluid states in the cycle"
  replaceable parameter
            Buildings.Fluid.CHPs.OrganicRankine.Data.WorkingFluids.Toluene pro
    constrainedby Buildings.Fluid.CHPs.OrganicRankine.Data.Generic
    "Property record of the working fluid"
end DryFluid;


Package for validation models

within Buildings.Fluid.CHPs.OrganicRankine.BaseClasses;
package Validation "Package for validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Organic Rankine cycle with a wet working fluid. This model is largely the same as

Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.Validation.DryFluid,
except that it validates the handling of wet working fluids.
As a result, a superheating temperature is computed that ensures
the expansion does not land under the dome.
How this superheating temperature is found is explained in the documentation of

Buildings.Fluid.CHPs.OrganicRankine.Cycle.

within Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.Validation;
model WetFluid
  "Organic Rankine cycle with a wet working fluid"
  extends
    Buildings.Fluid.CHPs.OrganicRankine.BaseClasses.Validation.DryFluid(
      TEva = 350,
      redeclare parameter
        Buildings.Fluid.CHPs.OrganicRankine.Data.WorkingFluids.R134a pro);
end WetFluid;


Package with working fluid data

within Buildings.Fluid.CHPs.OrganicRankine;
package Data "Package with working fluid data"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Package with working fluid data

within Buildings.Fluid.CHPs.OrganicRankine.Data;
package WorkingFluids "Package with working fluid data"
  extends Modelica.Icons.Package;
end WorkingFluids;


within Buildings.Fluid.CHPs.OrganicRankine.Examples;
model ORCHotWater "ORC that outputs hot water at a fixed temperature"
  extends Modelica.Icons.Example;

  package MediumHot = Buildings.Media.Air "Evaporator hot fluid";
  package MediumCol = Buildings.Media.Water "Condenser cold fluid";

  parameter Modelica.Units.SI.MassFlowRate mHot_flow_nominal = 1
    "Nominal mass flow rate of evaporator hot fluid";
  parameter Modelica.Units.SI.MassFlowRate mCol_flow_nominal = 1.35
    "Nominal mass flow rate of condenser cold fluid";
  parameter Modelica.Units.SI.PressureDifference dpCon_nominal(
    displayUnit = "Pa") = 10000
    "Nominal pressure drop across the ORC condenser";
  parameter Modelica.Units.SI.PressureDifference dpValCol_nominal(
    displayUnit = "Pa") = 10000
    "Nominal pressure difference used for valves in the cold fluid loop";
  parameter Modelica.Units.SI.ThermodynamicTemperature TCol_start = 35 + 273.15
    "Start value for cold fluid temperature";

  Buildings.Fluid.CHPs.OrganicRankine.Cycle orc(
    redeclare final package Medium1 = MediumHot,
    redeclare final package Medium2 = MediumCol,
    T2_start=TCol_start,
    redeclare Buildings.Fluid.CHPs.OrganicRankine.Data.WorkingFluids.R123 pro,
    final mHot_flow_nominal=mHot_flow_nominal,
    dTPinEva_set=5,
    TWorEva=373.15,
    pWorEva(displayUnit="bar"),
    final mCol_flow_nominal=mCol_flow_nominal,
    dpHot_nominal=0,
    dpCol_nominal=0,
    dTPinCon=5,
    mWor_flow_max=0.5,
    mWor_flow_min=0.1,
    mWor_flow_hysteresis=0.05,
    etaExp=0.8,
    etaPum=0.6) "Organic Rankine cycle"

  Modelica.Units.SI.Efficiency etaThe =orc.PExp  / max(orc.QEva_flow,1)
    "Thermal efficiency of the ORC";

  Buildings.Fluid.Sources.MassFlowSource_T souHot(
    redeclare final package Medium = MediumHot,
    m_flow=mHot_flow_nominal,
    T=423.15,
    nPorts=1) "Evaporator hot fluid source"
  Buildings.Fluid.Sources.Boundary_pT sinHot(
    redeclare final package Medium = MediumHot,
    nPorts=1) "Evaporator hot fluid sink"
  Buildings.Controls.Continuous.LimPID conPI(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=0.25,
    Ti=30,
    Ni=0.2,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=0,
    reverseActing=false) "PI controller"
  Modelica.Blocks.Sources.Constant TWatOut_set(k=55 + 273.15)
    "Set point of hot water output"
  Buildings.Fluid.Sources.Boundary_pT colBou(
    redeclare final package Medium = MediumCol,
    use_T_in=true,
    nPorts=2) "Cold fluid boundary conditions"
  Buildings.Fluid.Sensors.TemperatureTwoPort senTWatSup(
    redeclare final package Medium = MediumCol,
    m_flow_nominal=mCol_flow_nominal,
    T_start=TCol_start)
    "Water supply temperature"
  Buildings.Fluid.Sensors.TemperatureTwoPort senTWatRet(
    redeclare final package Medium = MediumCol,
    m_flow_nominal=mCol_flow_nominal,
    T_start=TCol_start)
    "Water return temperature"
  Modelica.Blocks.Sources.TimeTable TWatRet(
    y(final unit="K", displayUnit="degC"),
    table=[0,35; 3,35; 6,45; 9,45],
    timeScale=100,
    offset=273.15) "Water return temperature values"
  Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear val(
    redeclare final package Medium = MediumCol,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=TCol_start,
    from_dp=false,
    use_strokeTime=false,
    final m_flow_nominal=mCol_flow_nominal,
    final dpValve_nominal=dpValCol_nominal,
    final dpFixed_nominal=fill(dpCon_nominal, 2)) "Control valve"
  Buildings.Fluid.Movers.Preconfigured.FlowControlled_m_flow pum(
    redeclare final package Medium = MediumCol,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    T_start=TCol_start,
    addPowerToMedium=false,
    m_flow_nominal=mCol_flow_nominal,
    dp_nominal=dpCon_nominal + dpValCol_nominal,
    m_flow_start=0) "Cooling water pump"
  Buildings.Fluid.FixedResistances.Junction spl(
    redeclare final package Medium = MediumCol,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    final dp_nominal=fill(0,3),
    final m_flow_nominal=mCol_flow_nominal .* {1,-1,-1},
    final from_dp=false,
    T_start=TCol_start) "Flow splitter"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea(
    realTrue = mCol_flow_nominal)
    "Constant speed primary pump control signal"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hys(
    uLow=mCol_flow_nominal/3,
    uHigh=mCol_flow_nominal/2) "Hysteresis"
  Modelica.Blocks.Logical.And and1
  Buildings.Fluid.Sensors.TemperatureTwoPort senTColOut(
    redeclare final package Medium = MediumCol,
    m_flow_nominal=mCol_flow_nominal,
    T_start=TCol_start)
  Buildings.Fluid.Sensors.MassFlowRate senMasFlo(
    redeclare final package Medium = MediumCol)
    "Mass flow rate sensor for the ORC condenser cold fluid"
  Modelica.Blocks.Sources.BooleanTable booTab(table={100}, startValue=false)
    "Boolean table with initial false"
equation
  connect(orc.port_b1, sinHot.ports[1])
  connect(souHot.ports[1], orc.port_a1)
  connect(TWatOut_set.y, conPI.u_s)
  connect(colBou.ports[1], senTWatSup.port_b)
  connect(senTWatRet.port_a, colBou.ports[2])
  connect(TWatRet.y, colBou.T_in)
  connect(senTWatRet.port_b, val.port_1)
  connect(spl.port_3, val.port_3)
  connect(spl.port_2, senTWatSup.port_a)
  connect(conPI.y, val.y)
  connect(and1.y, orc.ena)
  connect(orc.port_b2,senTColOut. port_a)
  connect(senTColOut.port_b, pum.port_a)
  connect(senTColOut.T, conPI.u_m)
  connect(val.port_2, orc.port_a2)
  connect(pum.m_flow_in, booToRea.y)
  connect(spl.port_1, senMasFlo.port_b)
  connect(senMasFlo.port_a, pum.port_b)
  connect(booTab.y, booToRea.u)
  connect(booTab.y, and1.u2)
  connect(hys.y, and1.u1)
  connect(senMasFlo.m_flow, hys.u)
end ORCHotWater;


Package containing example models

within Buildings.Fluid.CHPs.OrganicRankine;
package Examples "Package containing example models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Collection of validation models

within Buildings.Fluid.CHPs.OrganicRankine;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;