Model that checks the correct implementation of the 2nd order derivative of the flow function. This model validates the implementation of

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp
and its second order derivative

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp_der2.
If the derivative implementation is wrong, the simulation will stop with an error.

within Buildings.Fluid.BaseClasses.FlowModels.Validation;
model BasicFlowFunction_dp_DerivativeCheck2
  "Model that checks the correct implementation of the 2nd order derivative of the flow function"
  extends Modelica.Icons.Example;

  parameter Real k = 0.35 "Flow coefficient";
  parameter Modelica.Units.SI.MassFlowRate m_flow_turbulent=0.36
    "Mass flow rate where transition to turbulent flow occurs";
  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  Modelica.Units.SI.MassFlowRate m_flow_comp "Comparison value for m_flow";
  Real der_m_flow(unit="kg/s2") "1st order derivative of mass flow rate";
  Real der_m_flow_comp(unit="kg/s2")
    "2nd order derivative of comparison value for m_flow";

  Modelica.Units.SI.PressureDifference dp "Pressure drop";
  Modelica.Units.SI.MassFlowRate err_m_flow "Integration error for m_flow";
  Real err_der_m_flow(unit="kg/s2") "Integration error for der_m_flow";
initial equation
  m_flow = m_flow_comp;
  der_m_flow = der_m_flow_comp;
equation
  // Multiply by 1 to avoid a warning due to unit mismatch,
  // and raise to third power so that dp_der2 has positive and negative sign
  // in Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp_der2
  dp = 1*time^3;

  m_flow = Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
    dp=dp,
    k = k,
    m_flow_turbulent=m_flow_turbulent);

  // Equate first and second order derivatives
  der_m_flow      = der(m_flow);
  der_m_flow_comp = der(m_flow_comp);
  der(der_m_flow) = der(der_m_flow_comp);

  // Error control
  err_m_flow = m_flow-m_flow_comp;
  assert(abs(err_m_flow) < 1E-3, "Error in implementation.");
  err_der_m_flow = der_m_flow-der_m_flow_comp;
  assert(abs(err_der_m_flow) < 1E-3, "Error in implementation.");
end BasicFlowFunction_dp_DerivativeCheck2;


Model that checks the correct implementation of the 1st order derivative of the flow function. This model validates the implementation of

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow
and its first order derivative

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow_der.
If the derivative implementation is wrong, the simulation will stop with an error.

within Buildings.Fluid.BaseClasses.FlowModels.Validation;
model BasicFlowFunction_m_flow_DerivativeCheck
  "Model that checks the correct implementation of the 1st order derivative of the flow function"
  extends Modelica.Icons.Example;

  constant Real gain = 0.5 "Gain for computing the mass flow rate";

  parameter Real k = 0.35 "Flow coefficient";
  parameter Modelica.Units.SI.MassFlowRate m_flow_turbulent=0.36
    "Mass flow rate where transition to turbulent flow occurs";
  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  Modelica.Units.SI.PressureDifference dp "Pressure drop";
  Modelica.Units.SI.PressureDifference dp_comp "Comparison value for dp";
  Modelica.Units.SI.PressureDifference err "Integration error";
initial equation
 dp = dp_comp;
equation
  m_flow = time*gain;
  dp = Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow(
    m_flow=m_flow,
    k = k,
    m_flow_turbulent=m_flow_turbulent);
  der(dp) = der(dp_comp);
  err = dp-dp_comp;
  assert(abs(err) < 1E-3, "Error in implementation.");
end BasicFlowFunction_m_flow_DerivativeCheck;


Model that checks the correct implementation of the 2nd order derivative of the flow function. This model validates the implementation of

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow
and its second order derivative

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow_der2.
If the derivative implementation is wrong, the simulation will stop with an error.

within Buildings.Fluid.BaseClasses.FlowModels.Validation;
model BasicFlowFunction_m_flow_DerivativeCheck2
  "Model that checks the correct implementation of the 2nd order derivative of the flow function"
  extends Modelica.Icons.Example;
  Buildings.Utilities.Diagnostics.CheckEquality cheEqu1(
    threShold=1e-3)
    "Block for checking integration error";
  Buildings.Utilities.Diagnostics.CheckEquality cheEqu2(
    threShold=1e-3)
    "Block for checking integration error";
  parameter Real k = 0.35 "Flow coefficient";
  parameter Modelica.Units.SI.MassFlowRate m_flow_turbulent=0.36
    "Mass flow rate where transition to turbulent flow occurs";
  Modelica.Units.SI.PressureDifference dp "Pressure drop";
  Modelica.Units.SI.PressureDifference dp_comp "Comparison value for dp";
  Real der_dp(unit="Pa/s") "1st order derivative of pressure drop";
  Real der_dp_comp(unit="Pa/s")
    "2nd order derivative of comparison value for pressure drop";

  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";

  Modelica.Units.SI.PressureDifference err_dp "Integration error for dp";
  Real err_der_dp(unit="Pa/s") "Integration error for der_dp";
initial equation
  dp = dp_comp;
  der_dp = der_dp_comp;
equation
  // Divide by 8 to avoid a warning due to unit mismatch, and
  // and raise to third power so that m_flow_der2 has positive and negative sign
  // in Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow_der2
  m_flow = time^3/8;

  dp = Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow(
    m_flow=m_flow,
    k = k,
    m_flow_turbulent=m_flow_turbulent);

  // Equate first and second order derivatives
  der_dp      = der(dp);
  der_dp_comp = der(dp_comp);
  der(der_dp) = der(der_dp_comp);

  // Error control
  cheEqu1.u1 = dp;
  cheEqu1.u2 = dp_comp;
  err_dp = cheEqu1.y;
  assert(abs(err_dp) < 1E-3, "Error in implementation.");
  cheEqu2.u1 = der_dp;
  cheEqu2.u2 = der_dp_comp;
  err_der_dp = cheEqu2.y;
  assert(abs(err_der_dp) < 1E-3, "Error in implementation.");
end BasicFlowFunction_m_flow_DerivativeCheck2;


Test model for flow function and its inverse. This model tests the inverse formulation of the flow functions.
The pressure difference dp and dpCalc need to
be equal up to the solver tolerance, except for a small neighborhood
around the origin. In this neighborhood around the origin, the functions

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp
and

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow
are not invertible.

within Buildings.Fluid.BaseClasses.FlowModels.Validation;
model InverseFlowFunctions "Test model for flow function and its inverse"
  extends Modelica.Icons.Example;
  Modelica.Units.SI.MassFlowRate m_flow;
  Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference";
  Modelica.Units.SI.PressureDifference dpCalc(displayUnit="Pa")
    "Pressure difference computed by the flow functions";
  Modelica.Units.SI.Pressure deltaDp(displayUnit="Pa")
    "Pressure difference between input and output to the functions";
  Modelica.Units.SI.Time dTime=2;
 parameter Real k = 0.5;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1 "Nominal flow rate";
equation
  dp = (time-0.5)/dTime * 20;
  m_flow=FlowModels.basicFlowFunction_dp(dp=dp, k=k, m_flow_turbulent=m_flow_nominal*0.3);
  dpCalc=FlowModels.basicFlowFunction_m_flow(m_flow=m_flow, k=k, m_flow_turbulent=m_flow_nominal*0.3);
  deltaDp = dp - dpCalc;
end InverseFlowFunctions;


Test model that inverts basicFlowFunction_dp. This model tests whether the Modelica translator substitutes the
inverse function for

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp.
Specifically, this function declares in its annotation section
that its inverse is provided by

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow.
Translating this model should therefore give no nonlinear equations
after the symbolic manipulation.

within Buildings.Fluid.BaseClasses.FlowModels.Validation;
model InvertingBasicFlowFunction_dp
  "Test model that inverts basicFlowFunction_dp"
  extends Modelica.Icons.Example;

 parameter Real k = 0.5 "Flow coefficient";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1.5
    "Nominal mass flow rate";

  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  Modelica.Units.SI.PressureDifference dp(displayUnit="Pa", start=0)
    "Pressure difference";
equation
  m_flow = 4*(time-0.5);
  m_flow = FlowModels.basicFlowFunction_dp(dp=dp, k=k, m_flow_turbulent=m_flow_nominal*0.3);
end InvertingBasicFlowFunction_dp;


Test model that inverts basicFlowFunction_m_flow. This model tests whether the Modelica translator substitutes the
inverse function for

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow.
Specifically, this function declares in its annotation section
that its inverse is provided by

Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp.
Translating this model should therefore give no nonlinear equations
after the symbolic manipulation.

within Buildings.Fluid.BaseClasses.FlowModels.Validation;
model InvertingBasicFlowFunction_m_flow
  "Test model that inverts basicFlowFunction_m_flow"
  extends Modelica.Icons.Example;

 parameter Real k = 0.5 "Flow coefficient";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.5
    "Nominal mass flow rate";

  Modelica.Units.SI.MassFlowRate m_flow(start=0) "Mass flow rate";
  Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference";
equation
  dp = 4*(time-0.5);
  dp = FlowModels.basicFlowFunction_m_flow(m_flow=m_flow, k=k, m_flow_turbulent=m_flow_nominal*0.3);
end InvertingBasicFlowFunction_m_flow;


Collection of validation models

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


Validation model for the actuator filter. This example validates

Buildings.Fluid.BaseClasses.ActuatorFilter.
The validation is done for different settings of u_nominal and for
different start values of the filter output.

within Buildings.Fluid.BaseClasses.Validation;
model ActuatorFilter "Validation model for the actuator filter"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Time riseTime=10 "Cut-off frequency of filter";
  final parameter Modelica.Units.SI.Frequency fCut=5/(2*Modelica.Constants.pi*
      riseTime) "Cut-off frequency of filter";

  Buildings.Fluid.BaseClasses.ActuatorFilter act_1(f=fCut, initType=Modelica.Blocks.Types.Init.InitialState)
    "Filter with u_nominal not set"
  Buildings.Fluid.BaseClasses.ActuatorFilter act_2(f=fCut, initType=Modelica.Blocks.Types.Init.InitialState)
    "Filter with u_nominal set to 100"
  Modelica.Blocks.Math.Gain gain(k=100) "Gain for input signal"
  Modelica.Blocks.Sources.Step step(startTime=0)
  Buildings.Fluid.BaseClasses.ActuatorFilter act_y_start05(
    f=fCut,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=0.5) "Filter with initial start value for output"
  Buildings.Fluid.BaseClasses.ActuatorFilter act_y_start1(
    f=fCut,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=1) "Filter with initial start value for output"
equation
  connect(act_2.u, gain.y)
  connect(step.y, act_1.u)
  connect(step.y, gain.u)
  connect(act_y_start05.u, step.y)
  connect(act_y_start1.u, step.y)
end ActuatorFilter;


Example use of MassFlowRateMultiplier

within Buildings.Fluid.BaseClasses.Validation;
model MassFlowRateMultiplier "Example use of MassFlowRateMultiplier"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

  Buildings.Fluid.BaseClasses.MassFlowRateMultiplier
    massFlowRateMultiplier(
      redeclare package Medium = Medium,
      k=5)
      "Mass flow rate multiplier"
  Modelica.Blocks.Sources.Ramp ram_m_flow(
    height=10,
    duration=10,
    offset=0) "Mass flow rate ramp"
  Buildings.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1) "Mass flow source"
  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=2)
    "Mass flow sink"
  Buildings.Fluid.BaseClasses.MassFlowRateMultiplier
    massFlowRateMultiplier1(
    redeclare package Medium = Medium,
    use_input=true,
    k=5)
      "Mass flow rate multiplier"
  Modelica.Blocks.Sources.Ramp gaiMasFlo(
    height=4.9,
    duration=10,
    offset=0.1) "Mass flow rate multiplier factor"
  Buildings.Fluid.Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1) "Mass flow source"
equation
  connect(ram_m_flow.y, sou.m_flow_in)
  connect(sou.ports[1], massFlowRateMultiplier.port_a)
  connect(massFlowRateMultiplier.port_b, sin.ports[1])
  connect(sou1.ports[1], massFlowRateMultiplier1.port_a)
  connect(massFlowRateMultiplier1.port_b, sin.ports[2])
  connect(gaiMasFlo.y, massFlowRateMultiplier1.u)
  connect(ram_m_flow.y, sou1.m_flow_in)
end MassFlowRateMultiplier;


Collection of validation models

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


Boiler with efficiency curve described by a polynomial of the control signal and (optional) temperature. This is a model of a boiler whose efficiency is described by a polynomial.
See
Buildings.Fluid.Boilers.UsersGuide for details.

within Buildings.Fluid.Boilers;
model BoilerPolynomial
  "Boiler with efficiency curve described by a polynomial of the control signal and (optional) temperature"
  extends Buildings.Fluid.Boilers.BaseClasses.PartialBoiler(
    eta=if effCur ==Buildings.Fluid.Types.EfficiencyCurves.Constant then
          a[1]
        elseif effCur ==Buildings.Fluid.Types.EfficiencyCurves.Polynomial then
          Buildings.Utilities.Math.Functions.polynomial(a=a, x=y)
        elseif effCur ==Buildings.Fluid.Types.EfficiencyCurves.QuadraticLinear then
          Buildings.Utilities.Math.Functions.quadraticLinear(a=aQuaLin, x1=y, x2=T)
        else
          0,
    final eta_nominal = if effCur ==Buildings.Fluid.Types.EfficiencyCurves.Constant then
          a[1]
        elseif effCur ==Buildings.Fluid.Types.EfficiencyCurves.Polynomial then
          Buildings.Utilities.Math.Functions.polynomial(a=a, x=1)
        elseif effCur ==Buildings.Fluid.Types.EfficiencyCurves.QuadraticLinear then
          Buildings.Utilities.Math.Functions.quadraticLinear(a=aQuaLin, x1=1, x2=T_nominal)
        else
          999);

  parameter Buildings.Fluid.Types.EfficiencyCurves
    effCur=Buildings.Fluid.Types.EfficiencyCurves.Constant
    "Curve used to compute the efficiency";
  parameter Real a[:] = {0.9} "Coefficients for efficiency curve";
  parameter Modelica.Units.SI.Temperature T_nominal=353.15
    "Temperature used to compute nominal efficiency (only used if efficiency curve depends on temperature)"

protected
  parameter Real aQuaLin[6] = if size(a, 1) == 6 then a else fill(0, 6)
  "Auxiliary variable for efficiency curve because quadraticLinear requires exactly 6 elements";

initial equation
  if  effCur == Buildings.Fluid.Types.EfficiencyCurves.QuadraticLinear then
    assert(size(a, 1) == 6,
    "The parameter 'a' must have exactly 6 elements
    when the boiler efficiency curve is set to
    'Buildings.Fluid.Types.EfficiencyCurves.QuadraticLinear'.
    The number of elements currently supplied is " + String(size(a, 1)) + ".");
  end if;
end BoilerPolynomial;


Boiler with efficiency described by a table with control signal and inlet temperature. This is a model of a boiler whose efficiency is described
by a table with control signal and inlet temperature.
See
Buildings.Fluid.Boilers.UsersGuide for details.

within Buildings.Fluid.Boilers;
model BoilerTable
  "Boiler with efficiency described by a table with control signal and inlet temperature"
  extends Buildings.Fluid.Boilers.BaseClasses.PartialBoiler(
    final eta=effTab.y,
    final Q_flow_nominal = per.Q_flow_nominal,
    final eta_nominal= per.eta_nominal,
    final fue=per.fue,
    final UA=per.UA,
    final VWat = per.VWat,
    final mDry = per.mDry,
    final m_flow_nominal = per.m_flow_nominal,
    final dp_nominal = per.dp_nominal);

  parameter Buildings.Fluid.Boilers.Data.Generic per
    "Records of efficiency curves"

  Modelica.Blocks.Tables.CombiTable2Ds effTab(final table=per.effCur, final
      smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Look-up table that represents a set of efficiency curves varying with both the firing rate (control signal) and the inlet water temperature"

  Modelica.Blocks.Sources.RealExpression TIn(
    y=Medium.temperature(state=Medium.setState_phX(
        p=port_a.p, h=inStream(port_a.h_outflow), X=inStream(port_a.Xi_outflow))))
    "Water inlet temperature"

initial equation
  assert(abs(per.effCur[end,1] - 1) < 1E-6,
    "Efficiency curve at full load (y = 1) must be provided.");

equation
  connect(effTab.u1, y)
  connect(TIn.y, effTab.u2)
end BoilerTable;


Package with boiler models

within Buildings.Fluid;
package Boilers "Package with boiler models"
  extends Modelica.Icons.VariantsPackage;
end Boilers;


User's Guide

within Buildings.Fluid.Boilers;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;
end UsersGuide;


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


Boiler base class with efficiency unspecified. This is a base model of a boiler.
The efficiency specified in extended models.
See
Buildings.Fluid.Boilers.UsersGuide for details.

within Buildings.Fluid.Boilers.BaseClasses;
partial model PartialBoiler "Boiler base class with efficiency unspecified"
  extends Interfaces.TwoPortHeatMassExchanger(
    redeclare final Buildings.Fluid.MixingVolumes.MixingVolume vol,
    show_T = true,
    final tau=VWat*rho_default/m_flow_nominal);

  parameter Buildings.Fluid.Data.Fuels.Generic fue "Fuel type"

  // These parameters can be supplied via Data records
  parameter Modelica.Units.SI.Power Q_flow_nominal "Nominal heating power";
  parameter Modelica.Units.SI.ThermalConductance UA=0.05*Q_flow_nominal/30
    "Overall UA value";
  parameter Modelica.Units.SI.Volume VWat=1.5E-6*Q_flow_nominal
    "Water volume of boiler"
  parameter Modelica.Units.SI.Mass mDry=1.5E-3*Q_flow_nominal
    "Mass of boiler that will be lumped to water heat capacity"
  parameter Modelica.Units.SI.Efficiency eta_nominal
    "Boiler efficiency at nominal condition";

  input Modelica.Units.SI.Efficiency eta "Boiler efficiency";
  Modelica.Units.SI.Power QFue_flow=y*Q_flow_nominal/eta_nominal
    "Heat released by fuel";
  Modelica.Units.SI.Power QWat_flow=eta*QFue_flow + UAOve.Q_flow
    "Heat transfer from gas into water";
    // The direction of UAOve.Q_flow is from the ambient to the boiler
    //   and therefore it takes a plus sign here.
  Modelica.Units.SI.MassFlowRate mFue_flow=QFue_flow/fue.h
    "Fuel mass flow rate";
  Modelica.Units.SI.VolumeFlowRate VFue_flow=mFue_flow/fue.d
    "Fuel volume flow rate";

  Modelica.Blocks.Interfaces.RealInput y(min=0, max=1) "Part load ratio"

  Modelica.Blocks.Interfaces.RealOutput T(
    final quantity="ThermodynamicTemperature",
    final unit = "K",
    displayUnit = "degC",
    min=0)
    "Temperature of the fluid"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "Heat port, can be used to connect to ambient"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCapDry(
    C=500*mDry,
    T(start=T_start))
    if not (energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState)
    "Heat capacity of boiler metal"

  Buildings.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow"
  Modelica.Blocks.Sources.RealExpression Q_flow_in(y=QWat_flow)
    "Heat transfer from gas into water"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen
    "Temperature of fluid"

  Modelica.Thermal.HeatTransfer.Components.ThermalConductor UAOve(G=UA)
    "Overall thermal conductance (if heatPort is connected)"

equation
  assert(eta > 0.001, "Efficiency curve is wrong.");

  connect(UAOve.port_b, vol.heatPort)
  connect(UAOve.port_a, heatPort)
  connect(heaCapDry.port, vol.heatPort)
  connect(temSen.T, T)
  connect(preHeaFlo.port, vol.heatPort)
  connect(Q_flow_in.y,preHeaFlo. Q_flow)
  connect(vol.heatPort, temSen.port)
end PartialBoiler;


within Buildings.Fluid.Boilers;
package Data "Package containing performance data for boilers"
extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


within Buildings.Fluid.Boilers.Data;
package Lochinvar "Package containing data for Lochinvar boilers"
  extends Modelica.Icons.Package;
end Lochinvar;


Specifications for Lochinvar Crest FB-4001 boiler. Performance data for boiler model.
See the documentation of

Buildings.Fluid.Boilers.Data.Lochinvar.

within Buildings.Fluid.Boilers.Data.Lochinvar.Crest;
record FBdash4001 "Specifications for Lochinvar Crest FB-4001 boiler"
  extends Buildings.Fluid.Boilers.Data.Lochinvar.Crest.FBdash2501(
    Q_flow_nominal = 1126272.122,
    VWat = 0.760867769,
    mDry = 1725.918968,
    m_flow_nominal = 22.081569,
    dp_nominal = 32579.88);
    // Data of this model are based on 22F of dT instead of 20F.
end FBdash4001;


within Buildings.Fluid.Boilers.Data.Lochinvar;
package Crest "Package with performance data for Lochinvar Crest boilers"
  extends Modelica.Icons.Package;
end Crest;


within Buildings.Fluid.Boilers.Data.Lochinvar;
package FTXL "Package with performance data for Lochinvar FTXL™ Fire Tube boilers"
  extends Modelica.Icons.Package;
end FTXL;


within Buildings.Fluid.Boilers.Data.Lochinvar;
package KnightXL "Package with performance data for Lochinvar Knight™ XL boilers"
  extends Modelica.Icons.Package;
end KnightXL;


within Buildings.Fluid.Boilers.Examples;
model BoilerPolynomial
 "Boilers with efficiency specified by polynomial and with open loop control"
 extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water "Medium model";
  parameter Modelica.Units.SI.Power Q_flow_nominal=3000 "Nominal power";
  parameter Modelica.Units.SI.Temperature dT_nominal=20
    "Nominal temperature difference";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/
      dT_nominal/4200 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=3000
    "Pressure drop at m_flow_nominal";

  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=2,
    p(displayUnit="Pa") = 300000,
    T=333.15) "Sink"
  Buildings.Fluid.Sources.Boundary_pT sou(
    nPorts=2,
    redeclare package Medium = Medium,
    p=300000 + dp_nominal,
    T=303.15)
  Modelica.Blocks.Sources.TimeTable y(table=[0,0; 1800,1; 1800,0; 2400,0; 2400,
        1; 3600,1])
  Buildings.Fluid.Boilers.BoilerPolynomial boi1(
    a={0.9},
    effCur=Buildings.Fluid.Types.EfficiencyCurves.Constant,
    Q_flow_nominal=Q_flow_nominal,
    m_flow_nominal = m_flow_nominal,
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal,
    fue=Buildings.Fluid.Data.Fuels.NaturalGasLowerHeatingValue(),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    from_dp=true,
    T_start=293.15) "Boiler"
  Buildings.HeatTransfer.Sources.FixedTemperature TAmb1(      T=288.15)
    "Ambient temperature in boiler room"
  Buildings.Fluid.Boilers.BoilerPolynomial boi2(
    a={0.9},
    effCur=Buildings.Fluid.Types.EfficiencyCurves.Constant,
    Q_flow_nominal=Q_flow_nominal,
    m_flow_nominal = m_flow_nominal,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    dp_nominal=dp_nominal,
    fue=Buildings.Fluid.Data.Fuels.NaturalGasLowerHeatingValue(),
    from_dp=true,
    T_start=293.15) "Boiler"
  Buildings.HeatTransfer.Sources.FixedTemperature TAmb2(      T=288.15)
    "Ambient temperature in boiler room"
equation
  connect(TAmb1.port, boi1.heatPort)
  connect(TAmb2.port, boi2.heatPort)
  connect(sou.ports[1], boi1.port_a)
  connect(sou.ports[2], boi2.port_a)
  connect(boi2.port_b, sin.ports[2])
  connect(boi1.port_b, sin.ports[1])
  connect(y.y, boi1.y)
  connect(y.y, boi2.y)
end BoilerPolynomial;


within Buildings.Fluid.Boilers.Examples;
model BoilerPolynomialClosedLoop
 "Boilers with efficiency specified by polynomial and with open loop control"
  extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water "Medium model";
  parameter Modelica.Units.SI.Power Q_flow_nominal=20000 "Nominal power";
  parameter Modelica.Units.SI.Temperature dT_nominal=20
    "Nominal temperature difference";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/
      dT_nominal/4200 "Nominal mass flow rate";

  Buildings.Fluid.Boilers.BoilerPolynomial boi(
    a={0.9},
    effCur=Buildings.Fluid.Types.EfficiencyCurves.Constant,
    Q_flow_nominal=Q_flow_nominal,
    redeclare package Medium = Medium,
    dp_nominal=20000,
    m_flow_nominal=m_flow_nominal,
    fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue(),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=293.15) "Boiler"
  Modelica.Blocks.Logical.OnOffController onOffController(bandwidth=20)
  Modelica.Blocks.Math.BooleanToReal booleanToReal
  Actuators.Valves.ThreeWayEqualPercentageLinear val(
    redeclare package Medium = Medium,
    l={0.01,0.01},
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=6000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
  Movers.FlowControlled_m_flow pumLoa(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=2*m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for heating load"
  Movers.FlowControlled_m_flow pumBoi(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for boiler loop"
  Modelica.Blocks.Sources.Constant m_flow_pum(k=m_flow_nominal)
    "Mass flow rate of pump"
  Modelica.Blocks.Sources.Constant TSetBoi(k=273.15 + 70)
    "Temperature setpoint for boiler"
  Controls.Continuous.LimPID conPID(
    Td=1,
    k=0.5,
    Ti=100)
  Modelica.Blocks.Sources.Constant TSetHea(k=273.15 + 40)
    "Temperature setpoint for heating load"
  Buildings.Fluid.FixedResistances.Junction spl(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal*{1,2,1},
    dp_nominal={0,0,200},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Splitter/mixer"
  Buildings.Fluid.FixedResistances.Junction spl1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal*{1,1,1},
    dp_nominal={0,0,100},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Splitter/mixer"
  Buildings.Fluid.FixedResistances.Junction spl2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal*{1,1,1},
    dp_nominal=0*{1,1,1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Splitter/mixer"
  Buildings.Fluid.FixedResistances.Junction spl3(
    redeclare package Medium = Medium,
    dp_nominal=0*{1,1,1},
    m_flow_nominal=m_flow_nominal*{2,1,1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Splitter/mixer"
  Storage.ExpansionVessel exp(redeclare package Medium = Medium, V_start=1)
  Buildings.Fluid.FixedResistances.Junction spl4(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal*{1,1,1},
    dp_nominal=0*{1,1,1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Splitter/mixer"
  MixingVolumes.MixingVolume vol(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=2*m_flow_nominal,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor thermalConductor(G=0.1
        *Q_flow_nominal/(40 - 20))
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TBou(T=293.15)
    "Fixed temperature boundary condition"
  Modelica.Blocks.Math.Gain gain(k=2)
    "Multiply the mass flow rate as this circuit has a smaller temperature difference"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TVol
    "Sensor for volume temperature"
equation
  connect(onOffController.y, booleanToReal.u)
  connect(boi.port_b, pumBoi.port_a)
  connect(boi.T, onOffController.u)
  connect(conPID.y, val.y)
  connect(TSetHea.y, conPID.u_s)
  connect(TSetBoi.y, onOffController.reference)
  connect(val.port_2, spl.port_1)
  connect(spl.port_2, pumLoa.port_a)
  connect(pumBoi.port_b, spl1.port_1)
  connect(spl1.port_2, val.port_1)
  connect(spl2.port_2, boi.port_a)
  connect(boi.port_a, exp.port_a)
  connect(spl3.port_2, spl4.port_1)
  connect(spl4.port_2, spl2.port_1)
  connect(spl4.port_3, val.port_3)
  connect(booleanToReal.y, boi.y)
  connect(m_flow_pum.y, pumBoi.m_flow_in)
  connect(vol.ports[1], spl3.port_1)
  connect(TBou.port, thermalConductor.port_a)
  connect(thermalConductor.port_b, vol.heatPort)
  connect(gain.y, pumLoa.m_flow_in)
  connect(m_flow_pum.y, gain.u)
  connect(TVol.port, vol.heatPort)
  connect(TVol.T, conPID.u_m)
  connect(pumLoa.port_b, vol.ports[2])
  connect(spl.port_3, spl3.port_3)
  connect(spl1.port_3, spl2.port_3)
end BoilerPolynomialClosedLoop;


within Buildings.Fluid.Boilers.Examples;
model BoilerTable
  "Boilers with efficiency described by table"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Medium model";
  parameter Buildings.Fluid.Boilers.Data.Lochinvar.Crest.FBdash2501 per
    "Record containing a table that describes the efficiency curves"

  Buildings.Fluid.Boilers.BoilerTable boi1(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    from_dp=true,
    T_start=293.15,
    per=per) "Boiler with transient computation"
  Buildings.Fluid.Boilers.BoilerTable boi2(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    from_dp=true,
    T_start=293.15,
    per=per) "Boiler with steady-state computation"

  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    p(displayUnit="Pa") = 300000,
    T=sou.T,
    nPorts=2) "Sink"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    p=300000 + per.dp_nominal,
    use_T_in=true,
    nPorts=2) "Source"
  Buildings.HeatTransfer.Sources.FixedTemperature TAmb(T=288.15)
    "Ambient temperature in boiler room"
  Modelica.Blocks.Sources.TimeTable y(table=[0,0; 1800,1; 1800,0; 2400,0; 2400,1;
        6000,1]) "Firing rate"
  Modelica.Blocks.Sources.TimeTable TIn(table=[0,303.15; 3000,303.15; 4200,293.15;
        4800,293.15; 5400,303.15; 6000,303.15])
        "Inlet temperature"

equation
  connect(TAmb.port,boi1. heatPort)
  connect(y.y,boi1. y)
  connect(TIn.y, sou.T_in)
  connect(y.y,boi2. y)
  connect(TAmb.port,boi2. heatPort)
  connect(sou.ports[1],boi1. port_a)
  connect(sou.ports[2],boi2. port_a)
  connect(boi1.port_b, sin.ports[1])
  connect(boi2.port_b, sin.ports[2])
end BoilerTable;


within Buildings.Fluid.Boilers;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


within Buildings.Fluid.Boilers.Validation;
model BoilerTableEfficiencyCurves
  "Boilers with efficiency curves specified by look-up table"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Medium model";
  parameter Buildings.Fluid.Boilers.Data.Lochinvar.Crest.FBdash2501 per
    "Record containing a table that describes the efficiency curves"

  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=3,
    p(displayUnit="Pa") = 300000,
    T=sou.T) "Sink"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    p=300000 + per.dp_nominal,
    use_T_in=true,
    nPorts=3) "Source"
  Buildings.Fluid.Boilers.BoilerTable boi1(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    from_dp=true,
    T_start=293.15,
    per=per) "Boiler 1 set at 5% firing rate"
  Buildings.HeatTransfer.Sources.FixedTemperature
    TAmb1(T=288.15) "Ambient temperature in boiler room"
  Buildings.Fluid.Boilers.BoilerTable boi2(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    from_dp=true,
    T_start=293.15,
    per=per) "Boiler 2 set at 50% firing rate"
  Buildings.HeatTransfer.Sources.FixedTemperature
    TAmb2(T=288.15) "Ambient temperature in boiler room"
  Buildings.Fluid.Boilers.BoilerTable boi3(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    from_dp=true,
    T_start=293.15,
    per=per) "Boiler 3 set at 100% firing rate"
  HeatTransfer.Sources.FixedTemperature TAmb3(T=288.15)
    "Ambient temperature in boiler room"

  Modelica.Blocks.Sources.Constant y1(k=0.05)
    "Setting the firing rate at constant 5%"
  Modelica.Blocks.Sources.Constant y2(k=0.5)
    "Setting the firing rate at constant 50%"
  Modelica.Blocks.Sources.Constant y3(k=1)
    "Setting the firing rate at constant 100%"
  Modelica.Blocks.Sources.Ramp TIn(
    height=per.effCur[1,end]-per.effCur[1,2],
    duration=3600,
    offset=per.effCur[1,2],
    y(final unit="K",
      displayUnit="degC"))
    "Ramps the T_inlet from the first to the last temperature provided by the efficiency curve table"

equation
  connect(TAmb1.port, boi1.heatPort)
  connect(TAmb2.port, boi2.heatPort)
  connect(boi2.port_b, sin.ports[2])
  connect(boi1.port_b, sin.ports[1])
  connect(TAmb3.port, boi3.heatPort)
  connect(boi3.port_b, sin.ports[3])
  connect(boi1.port_a, sou.ports[1])
  connect(boi3.port_a, sou.ports[2])
  connect(boi2.port_a, sou.ports[3])
  connect(y1.y, boi1.y)
  connect(y2.y, boi2.y)
  connect(y3.y, boi3.y)
  connect(TIn.y, sou.T_in)
end BoilerTableEfficiencyCurves;


Collection of validation models

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


Indirect steam heated absorption chiller based on performance curves. Model for an indirect steam heated absorption chiller based on performance curves.
The model uses performance curves similar to the EnergyPlus model Chiller:Absorption:Indirect.

within Buildings.Fluid.Chillers;
model AbsorptionIndirectSteam
  "Indirect steam heated absorption chiller based on performance curves"
    extends Buildings.Fluid.Interfaces.FourPortHeatMassExchanger(
     T1_start = 273.15+25,
     T2_start = 273.15+5,
     m1_flow_nominal= per.mCon_flow_nominal,
     m2_flow_nominal= per.mEva_flow_nominal,
     dp1_nominal = per.dpCon_nominal,
     dp2_nominal = per.dpEva_nominal,
   redeclare final Buildings.Fluid.MixingVolumes.MixingVolume
      vol1(final V=m1_flow_nominal*tau1/rho1_nominal,
           nPorts=2,
           final prescribedHeatFlowRate=true),
      vol2(final V=m2_flow_nominal*tau2/rho2_nominal,
           nPorts=2,
           final prescribedHeatFlowRate=true));

  parameter Buildings.Fluid.Chillers.Data.AbsorptionIndirectSteam.Generic per
   "Performance data"
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_small=-per.QEva_flow_nominal*
      1E-6
    "Small value for heat flow rate or power, used to avoid division by zero"

  Modelica.Blocks.Interfaces.BooleanInput on
    "Set to true to enable the absorption chiller"
  Modelica.Blocks.Interfaces.RealInput TSet(final unit="K", displayUnit="degC")
    "Evaporator setpoint leaving water temperature"
  Modelica.Blocks.Interfaces.RealOutput P(final unit="W")
   "Chiller pump power"
  Modelica.Blocks.Interfaces.RealOutput QGen_flow(final unit="W")
  "Required generator heat flow rate in the form of steam"
  Modelica.Blocks.Interfaces.RealOutput QEva_flow(final unit="W")
  "Evaporator heat flow rate"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(final unit="W")
  "Condenser heat flow rate"

  Real PLR(min=0, final unit="1") = perMod.PLR
   "Part load ratio";
  Real CR(min=0, final unit="1") = perMod.CR
   "Cycling ratio";

protected
  BaseClasses.AbsorptionIndirectSteam perMod(
    final per=per,
    final Q_flow_small=Q_flow_small) "Block that computes the performance"

  Modelica.Blocks.Sources.RealExpression QEva_flow_set(
    final y=Buildings.Utilities.Math.Functions.smoothMin(
        x1=m2_flow*(hEvaSet - inStream(port_a2.h_outflow)),
        x2=-Q_flow_small,
        deltaX=Q_flow_small/10)) "Setpoint heat flow rate of the evaporator"

  Modelica.Units.SI.SpecificEnthalpy hEvaSet=Medium2.specificEnthalpy_pTX(
      p=port_b2.p,
      T=TSet,
      X=cat(
        1,
        port_b2.Xi_outflow,
        {1 - sum(port_b2.Xi_outflow)})) "Chilled water setpoint enthalpy";

  Modelica.Blocks.Sources.RealExpression TConEnt(
        y=Medium1.temperature(
        Medium1.setState_phX(
          p = port_a1.p,
          h = inStream(port_a1.h_outflow))))
   "Condenser entering water temperature"

  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TEvaLvg
    "Leaving evaporator temperature"

  HeatTransfer.Sources.PrescribedHeatFlow preHeaFloCon
    "Prescribed heat flow rate for the condenser"
  HeatTransfer.Sources.PrescribedHeatFlow preHeaFloEva
    "Prescribed heat flow rate for the evaporator"

equation
  connect(on, perMod.on)
  connect(perMod.QCon_flow, preHeaFloCon.Q_flow)
  connect(perMod.QEva_flow, preHeaFloEva.Q_flow)
  connect(preHeaFloEva.port, vol2.heatPort)
  connect(perMod.QEva_flow, QEva_flow)
  connect(TConEnt.y, perMod.TConEnt)
  connect(QEva_flow_set.y, perMod.QEva_flow_set)
  connect(preHeaFloCon.port, vol1.heatPort)
  connect(perMod.QCon_flow, QCon_flow)
  connect(perMod.QGen_flow, QGen_flow)
  connect(TEvaLvg.port, vol2.heatPort)
  connect(TEvaLvg.T, perMod.TEvaLvg)
  connect(perMod.P, P)
end AbsorptionIndirectSteam;


Chiller with prescribed evaporator leaving temperature and performance curve adjusted based on Carnot efficiency. This is a model of a chiller whose coefficient of performance COP changes
with temperatures in the same way as the Carnot efficiency changes.
The control input is the setpoint of the evaporator leaving temperature, which
is met exactly at steady state if the chiller has sufficient capacity.

within Buildings.Fluid.Chillers;
model Carnot_TEva
  "Chiller with prescribed evaporator leaving temperature and performance curve adjusted based on Carnot efficiency"
 extends Buildings.Fluid.Chillers.BaseClasses.PartialCarnot_T(
   final COP_is_for_cooling = true,
   final QCon_flow_nominal = -QEva_flow_nominal*(1 + COP_nominal)/COP_nominal,
   PEle(y=-QEva_flow/COP),
   redeclare HeatExchangers.HeaterCooler_u con(
    final from_dp=from_dp1,
    final dp_nominal=dp1_nominal,
    final linearizeFlowResistance=linearizeFlowResistance1,
    final deltaM=deltaM1,
    final tau=tau1,
    final T_start=T1_start,
    final energyDynamics=energyDynamics,
    final homotopyInitialization=homotopyInitialization,
    final Q_flow_nominal=QCon_flow_nominal),
   redeclare HeatExchangers.SensibleCooler_T eva(
    final from_dp=from_dp2,
    final dp_nominal=dp2_nominal,
    final linearizeFlowResistance=linearizeFlowResistance2,
    final deltaM=deltaM2,
    final QMin_flow=QEva_flow_min,
    final tau=tau2,
    final T_start=T2_start,
    final energyDynamics=energyDynamics,
    final homotopyInitialization=homotopyInitialization));

  parameter Modelica.Units.SI.HeatFlowRate QEva_flow_min(max=0) = -Modelica.Constants.inf
    "Maximum heat flow rate for cooling (negative)";

  Modelica.Blocks.Interfaces.RealInput TSet(unit="K")
    "Evaporator leaving water temperature"

protected
  Modelica.Blocks.Math.Gain yCon(final k=1/QCon_flow_nominal)
    "Normalized condenser heat flow rate"
  Modelica.Blocks.Math.Add QCon_flow_internal(final k1=-1)
    "Heat added to condenser"
initial equation
  assert(QEva_flow_nominal < 0, "Parameter QEva_flow_nominal must be negative.");

equation
  connect(TSet, eva.TSet)
  connect(eva.Q_flow, QEva_flow)
  connect(QCon_flow_internal.y, yCon.u)
  connect(yCon.y, con.u)
  connect(QCon_flow_internal.y, QCon_flow)
  connect(QCon_flow_internal.u1, eva.Q_flow)
  connect(QCon_flow_internal.u2, PEle.y)
end Carnot_TEva;


Chiller with performance curve adjusted based on Carnot efficiency. This is a model of a chiller whose coefficient of performance COP changes
with temperatures in the same way as the Carnot efficiency changes.
The input signal <i>y</i> is the control signal for the compressor.

within Buildings.Fluid.Chillers;
model Carnot_y
  "Chiller with performance curve adjusted based on Carnot efficiency"
  extends Buildings.Fluid.Chillers.BaseClasses.PartialCarnot_y(
    final COP_is_for_cooling = true);
end Carnot_y;


Electric chiller based on the DOE-2.1 model. Model of an electric chiller, based on the DOE-2.1 chiller model and
the EnergyPlus chiller model Chiller:Electric:EIR.

within Buildings.Fluid.Chillers;
model ElectricEIR "Electric chiller based on the DOE-2.1 model"
  extends Buildings.Fluid.Chillers.BaseClasses.PartialElectric(
  final QEva_flow_nominal = per.QEva_flow_nominal,
  final COP_nominal= per.COP_nominal,
  final PLRMax= per.PLRMax,
  final PLRMinUnl= per.PLRMinUnl,
  final PLRMin= per.PLRMin,
  final etaMotor= per.etaMotor,
  final mEva_flow_nominal= per.mEva_flow_nominal,
  final mCon_flow_nominal= per.mCon_flow_nominal,
  final TEvaLvg_nominal= per.TEvaLvg_nominal);

  parameter Buildings.Fluid.Chillers.Data.ElectricEIR.Generic per
    "Performance data"

protected
  final parameter Modelica.Units.NonSI.Temperature_degC TConEnt_nominal_degC=
      Modelica.Units.Conversions.to_degC(per.TConEnt_nominal)
    "Temperature of fluid entering condenser at nominal condition";

  Modelica.Units.NonSI.Temperature_degC TConEnt_degC
    "Temperature of fluid entering condenser";
initial equation
  // Verify correctness of performance curves, and write warning if error is bigger than 10%
  Buildings.Fluid.Chillers.BaseClasses.warnIfPerformanceOutOfBounds(
     Buildings.Utilities.Math.Functions.biquadratic(a=per.capFunT,
     x1=TEvaLvg_nominal_degC, x2=TConEnt_nominal_degC),
     "Capacity as function of temperature ",
     "per.capFunT");
equation
  TConEnt_degC=Modelica.Units.Conversions.to_degC(TConEnt);

  if on then
    // Compute the chiller capacity fraction, using a biquadratic curve.
    // Since the regression for capacity can have negative values (for unreasonable temperatures),
    // we constrain its return value to be non-negative. This prevents the solver to pick the
    // unrealistic solution.
    capFunT = Buildings.Utilities.Math.Functions.smoothMax(
       x1 = 1E-6,
       x2 = Buildings.Utilities.Math.Functions.biquadratic(a=per.capFunT, x1=TEvaLvg_degC, x2=TConEnt_degC),
       deltaX = 1E-7);
/*    assert(capFunT > 0.1, "Error: Received capFunT = " + String(capFunT)  + ".\n"
           + "Coefficient for polynomial seem to be not valid for the encountered temperature range.\n"
           + "Temperatures are TConEnt_degC = " + String(TConEnt_degC) + " degC\n"
           + "                 TEvaLvg_degC = " + String(TEvaLvg_degC) + " degC");
*/
    // Chiller energy input ratio biquadratic curve.
    EIRFunT = Buildings.Utilities.Math.Functions.biquadratic(a=per.EIRFunT, x1=TEvaLvg_degC, x2=TConEnt_degC);
    // Chiller energy input ratio quadratic curve
    EIRFunPLR   = per.EIRFunPLR[1]+per.EIRFunPLR[2]*PLR2+per.EIRFunPLR[3]*PLR2^2;
  else
    capFunT   = 0;
    EIRFunT   = 0;
    EIRFunPLR = 0;
  end if;

ensures that the chiller capacity does not exceed the chiller capacity specified
by the parameter per.PLRMax.

The test

  CR = min(PLR1/per.PRLMin, 1.0);

computes a cycling ratio. This ratio expresses the fraction of time
that a chiller would run if it were to cycle because its load is smaller than the
minimal load at which it can operate.
Note that this model continuously operates even if the part load ratio is below the minimum part load ratio.
Its leaving evaporator and condenser temperature can therefore be considered as an
average temperature between the modes where the compressor is off and on.

The test

  PLR2 = max(per.PLRMinUnl, PLR1);

computes the part load ratio of the compressor.
The assumption is that for a part load ratio below per.PLRMinUnl,
the chiller uses hot gas bypass to reduce the capacity, while the compressor
power draw does not change.

The electric power only contains the power for the compressor, but not any power for pumps or fans.

The model can be parametrized to compute a transient
or steady-state response.
The transient response of the chiller is computed using a first
order differential equation for the evaporator and condenser fluid volumes.
The chiller outlet temperatures are equal to the temperatures of these lumped volumes.

Optionally, the model can be configured to represent heat recovery chillers with
a switchover option by setting the parameter have_switchover to
true.
In that case an additional Boolean input connector coo is used.
The chiller is tracking a chilled water supply temperature setpoint at the
outlet of the evaporator barrel if coo is true.
Otherwise, if coo is false, the chiller is tracking
a hot water supply temperature setpoint at the outlet of the condenser barrel.
See

Buildings.Fluid.Chillers.Examples.ElectricEIR_HeatRecovery
for an example with a chiller operating in heating mode.

References

Hydeman, M. and K.L. Gillespie. 2002. Tools and Techniques to Calibrate Electric Chiller
Component Models. ASHRAE Transactions, AC-02-9-1.

EnergyPlus v22.1.0 Engineering Reference

",
revisions="

March 12, 2015, by Michael Wetter:

Refactored model to make it once continuously differentiable.
This is for issue 373.

Jan. 9, 2011, by Michael Wetter:

Added input signal to switch chiller off.

Sep. 8, 2010, by Michael Wetter:

Revised model and included it in the Buildings library.

October 13, 2008, by Brandon Hencey:

First implementation.

"));
end ElectricEIR;


Electric chiller based on the DOE-2.1 model, but with performance as a function of condenser leaving instead of entering temperature. Model of an electric chiller, based on the model by
Hydeman et al. (2002) that has been developed in the CoolTools project
and that is implemented in EnergyPlus as the model
Chiller:Electric:ReformulatedEIR.
This empirical model is similar to

Buildings.Fluid.Chillers.ElectricEIR.
The difference is that to compute the performance, this model
uses the condenser leaving temperature instead of the entering temperature,
and it uses a bicubic polynomial to compute the part load performance.

within Buildings.Fluid.Chillers;
model ElectricReformulatedEIR
  "Electric chiller based on the DOE-2.1 model, but with performance as a function of condenser leaving instead of entering temperature"
  extends Buildings.Fluid.Chillers.BaseClasses.PartialElectric(
  final QEva_flow_nominal = per.QEva_flow_nominal,
  final COP_nominal= per.COP_nominal,
  final PLRMax= per.PLRMax,
  final PLRMinUnl= per.PLRMinUnl,
  final PLRMin= per.PLRMin,
  final etaMotor= per.etaMotor,
  final mEva_flow_nominal= per.mEva_flow_nominal,
  final mCon_flow_nominal= per.mCon_flow_nominal,
  final TEvaLvg_nominal= per.TEvaLvg_nominal);

  parameter Buildings.Fluid.Chillers.Data.ElectricReformulatedEIR.Generic per
    "Performance data"

protected
  final parameter Modelica.Units.NonSI.Temperature_degC TConLvg_nominal_degC=
      Modelica.Units.Conversions.to_degC(per.TConLvg_nominal)
    "Temperature of fluid leaving condenser at nominal condition";

  Modelica.Units.NonSI.Temperature_degC TConLvg_degC
    "Temperature of fluid leaving condenser";
initial equation
  // Verify correctness of performance curves, and write warning if error is bigger than 10%
  Buildings.Fluid.Chillers.BaseClasses.warnIfPerformanceOutOfBounds(
     Buildings.Utilities.Math.Functions.biquadratic(a=per.capFunT,
     x1=TEvaLvg_nominal_degC, x2=TConLvg_nominal_degC),
     "Capacity as a function of temperature ",
     "per.capFunT");
equation
  TConLvg_degC=Modelica.Units.Conversions.to_degC(TConLvg);

  if on then
    // Compute the chiller capacity fraction, using a biquadratic curve.
    // Since the regression for capacity can have negative values (for unreasonable temperatures),
    // we constrain its return value to be non-negative. This prevents the solver to pick the
    // unrealistic solution.
    capFunT = Buildings.Utilities.Math.Functions.smoothMax(
      x1 = 1E-6,
      x2 = Buildings.Utilities.Math.Functions.biquadratic(a=per.capFunT, x1=TEvaLvg_degC, x2=TConLvg_degC),
      deltaX = 1E-7);
/*    assert(capFunT > 0.1, "Error: Received capFunT = " + String(capFunT)  + ".\n"
           + "Coefficient for polynomial seem to be not valid for the encountered temperature range.\n"
           + "Temperatures are TConLvg_degC = " + String(TConLvg_degC) + " degC\n"
           + "                 TEvaLvg_degC = " + String(TEvaLvg_degC) + " degC");
*/
    // Chiller energy input ratio biquadratic curve.
    EIRFunT = Buildings.Utilities.Math.Functions.biquadratic(a=per.EIRFunT, x1=TEvaLvg_degC, x2=TConLvg_degC);
    // Chiller energy input ratio bicubic curve
    EIRFunPLR   = Buildings.Utilities.Math.Functions.bicubic(a=per.EIRFunPLR, x1=TConLvg_degC, x2=PLR2);
  else
    capFunT   = 0;
    EIRFunT   = 0;
    EIRFunPLR = 0;
  end if;

ensures that the chiller capacity does not exceed the chiller capacity specified
by the parameter per.PLRMax.

The test

  CR = min(PLR1/per.PRLMin, 1.0);

computes a cycling ratio. This ratio expresses the fraction of time
that a chiller would run if it were to cycle because its load is smaller than
the minimal load at which it can operate.
Note that this model continuously operates even if the part load ratio is below the minimum part load ratio.
Its leaving evaporator and condenser temperature can therefore be considered as an
average temperature between the modes where the compressor is off and on.

The test

  PLR2 = max(per.PLRMinUnl, PLR1);

computes the part load ratio of the compressor.
The assumption is that for a part load ratio below per.PLRMinUnl,
the chiller uses hot gas bypass to reduce the capacity, while the compressor
power draw does not change.

The electric power only contains the power for the compressor, but not any power for pumps or fans.

The model can be parametrized to compute a transient
or steady-state response.
The transient response of the chiller is computed using a first
order differential equation for the evaporator and condenser fluid volumes.
The chiller outlet temperatures are equal to the temperatures of these lumped volumes.

Optionally, the model can be configured to represent heat recovery chillers with
a switchover option by setting the parameter have_switchover to
true.
In that case an additional Boolean input connector coo is used.
The chiller is tracking a chilled water supply temperature setpoint at the
outlet of the evaporator barrel if coo is true.
Otherwise, if coo is false, the chiller is tracking
a hot water supply temperature setpoint at the outlet of the condenser barrel.
See

Buildings.Fluid.Chillers.Examples.ElectricEIR_HeatRecovery
for an example with a chiller operating in heating mode.

References

Hydeman, M., N. Webb, P. Sreedharan, and S. Blanc. 2002. Development and Testing of a
Reformulated Regression-Based Electric Chiller Model. ASHRAE Transactions, HI-02-18-2.

Hydeman, M. and K.L. Gillespie. 2002. Tools and Techniques to Calibrate Electric Chiller
Component Models. ASHRAE Transactions, AC-02-9-1.

",
revisions="

March 12, 2015, by Michael Wetter:

Refactored model to make it once continuously differentiable.
This is for issue 373.

Jan. 9, 2011, by Michael Wetter:

Added input signal to switch chiller off.

September 17, 2010, by Michael Wetter:

First implementation.

"));
end ElectricReformulatedEIR;


Package with chiller models

within Buildings.Fluid;
package Chillers "Package with chiller models"
  extends Modelica.Icons.VariantsPackage;
end Chillers;


Absorption indirect chiller performance curve method. Block that computes the performance for the model

Buildings.Fluid.Chillers.AbsorptionIndirectSteam.
See
Buildings.Fluid.Chillers.AbsorptionIndirectSteam
for a description of this model.

within Buildings.Fluid.Chillers.BaseClasses;
block AbsorptionIndirectSteam
  "Absorption indirect chiller performance curve method"
  extends Modelica.Blocks.Icons.Block;

  parameter Buildings.Fluid.Chillers.Data.AbsorptionIndirectSteam.Generic per
    "Performance data"
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_small
    "Small value for heat flow rate or power, used to avoid division by zero"

  Modelica.Blocks.Interfaces.BooleanInput on
    "Set to true to enable the absorption chiller"

  Modelica.Blocks.Interfaces.RealInput TConEnt(
    final unit="K",
    displayUnit="degC")
    "Condenser entering water temperature"
  Modelica.Blocks.Interfaces.RealInput TEvaLvg(
    final unit="K",
    displayUnit="degC")
    "Evaporator leaving water temperature"

  Modelica.Blocks.Interfaces.RealInput QEva_flow_set(final unit="W")
    "Evaporator setpoint heat flow rate"

  Modelica.Blocks.Interfaces.RealOutput QCon_flow(final unit="W")
    "Condenser heat flow rate "

  Modelica.Blocks.Interfaces.RealOutput QEva_flow(final unit="W")
    "Evaporator heat flow rate"

  Modelica.Blocks.Interfaces.RealOutput QGen_flow(final unit="W")
    "Required generator heat flow rate"

  Modelica.Blocks.Interfaces.RealOutput P(final unit="W")
    "Pump power"

  Modelica.Blocks.Interfaces.RealOutput PLR(min=0, final unit="1")
   "Part load ratio"
  Modelica.Blocks.Interfaces.RealOutput CR(min=0, final unit="1")
   "Cycling ratio"

  Modelica.Units.SI.Efficiency genHIR
    "Ratio of the generator heat input to chiller operating capacity";
  Modelica.Units.SI.Efficiency EIRP(min=0)
    "Ratio of the actual absorber pumping power to the nominal pumping power";
  Real capFunEva(min=0)
    "Evaporator capacity factor function of temperature curve";
  Real capFunCon(min=0)
   "Condenser capacity factor function of temperature curve";
  Real genConT(min=0)
   "Heat input modifier based on the generator input temperature";
  Real genEvaT(min=0)
   "Heat input modifier based on the evaporator outlet temperature";

  Modelica.Units.SI.HeatFlowRate QEva_flow_ava
    "Cooling capacity available at the evaporator";
protected
  Modelica.Units.NonSI.Temperature_degC TConEnt_degC
    "Condenser entering water temperature in degC";
  Modelica.Units.NonSI.Temperature_degC TEvaLvg_degC
    "Evaporator leaving water temperature in degC";

initial equation
  assert(per.QEva_flow_nominal < 0,
  "In " + getInstanceName() + ": Parameter QEva_flow_nominal must be smaller than zero.");
  assert(Q_flow_small > 0,
  "In " + getInstanceName() + ": Parameter Q_flow_small must be larger than zero.");

equation
  TConEnt_degC=Modelica.Units.Conversions.to_degC(TConEnt);
  TEvaLvg_degC=Modelica.Units.Conversions.to_degC(TEvaLvg);

  if on then
    capFunEva = Buildings.Utilities.Math.Functions.smoothMax(
           x1 = 1E-6,
           x2 = Buildings.Utilities.Math.Functions.polynomial(x=TEvaLvg_degC, a=per.capFunEva),
       deltaX = Q_flow_small);

    capFunCon = Buildings.Utilities.Math.Functions.smoothMax(
           x1 = 1E-6,
           x2 = Buildings.Utilities.Math.Functions.polynomial(x=TConEnt_degC, a=per.capFunCon),
       deltaX = Q_flow_small);

    QEva_flow_ava = per.QEva_flow_nominal*capFunEva*capFunCon;

    QEva_flow = Buildings.Utilities.Math.Functions.smoothMax(
          x1 = QEva_flow_set,
          x2 = QEva_flow_ava,
          deltaX = Q_flow_small);
    PLR =Buildings.Utilities.Math.Functions.smoothMin(
      x1=QEva_flow_set/(QEva_flow_ava - Q_flow_small),
      x2=per.PLRMax,
      deltaX=per.PLRMax/100);

    genHIR = per.genHIR[1]+ per.genHIR[2]*PLR+per.genHIR[3]*PLR^2+per.genHIR[4]*PLR^3;

    genConT = Buildings.Utilities.Math.Functions.polynomial(x=TConEnt_degC, a=per.genConT);

    genEvaT = Buildings.Utilities.Math.Functions.polynomial(x=TEvaLvg_degC, a=per.genEvaT);

    EIRP = Buildings.Utilities.Math.Functions.polynomial(x=PLR, a=per.EIRP);

    CR = Buildings.Utilities.Math.Functions.smoothMin(
      x1 = PLR/per.PLRMin,
      x2 = 1,
      deltaX = 0.001);

    QGen_flow = -QEva_flow_ava * genHIR * genConT * genEvaT * CR;

    P = EIRP * per.P_nominal * CR;

    QCon_flow = -QEva_flow + QGen_flow + P;
  else
    capFunEva = 0;
    capFunCon = 0;
    QEva_flow_ava = 0;
    QEva_flow = 0;
    PLR = 0;
    genHIR = per.genHIR[1];
    genConT = 1;
    genEvaT = 1;
    EIRP = 0;
    CR = 0;
    QGen_flow = 0;
    P = 0;
    QCon_flow = 0;
  end if;
end AbsorptionIndirectSteam;


= true, use homotopy method

within Buildings.Fluid.Chillers.BaseClasses;
partial model Carnot
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
    m1_flow_nominal = QCon_flow_nominal/cp1_default/dTCon_nominal,
    m2_flow_nominal = QEva_flow_nominal/cp2_default/dTEva_nominal);

  constant Boolean homotopyInitialization = true "= true, use homotopy method"

  parameter Modelica.Units.SI.HeatFlowRate QEva_flow_nominal(max=0)
    "Nominal cooling heat flow rate (QEva_flow_nominal < 0)"
  parameter Modelica.Units.SI.HeatFlowRate QCon_flow_nominal(min=0)
    "Nominal heating flow rate"

  parameter Modelica.Units.SI.TemperatureDifference dTEva_nominal(final max=0)
     = -10 "Temperature difference evaporator outlet-inlet"
  parameter Modelica.Units.SI.TemperatureDifference dTCon_nominal(final min=0)
     = 10 "Temperature difference condenser outlet-inlet"

  // Efficiency
  parameter Boolean use_eta_Carnot_nominal = true
    "Set to true to use Carnot effectiveness etaCarnot_nominal rather than COP_nominal"
  parameter Real etaCarnot_nominal(unit="1") = 0.3
    "Carnot effectiveness (=COP/COP_Carnot) used during simulation if use_eta_Carnot_nominal = true"

  parameter Real COP_nominal(unit="1") = etaCarnot_nominal*TUseAct_nominal/
    (TCon_nominal+TAppCon_nominal - (TEva_nominal-TAppEva_nominal))
    "Coefficient of performance at TEva_nominal and TCon_nominal, used during simulation if use_eta_Carnot_nominal = false"

  parameter Modelica.Units.SI.Temperature TCon_nominal=303.15
    "Condenser temperature used to compute COP_nominal if use_eta_Carnot_nominal=false"
  parameter Modelica.Units.SI.Temperature TEva_nominal=278.15
    "Evaporator temperature used to compute COP_nominal if use_eta_Carnot_nominal=false"

  parameter Real a[:] = {1}
    "Coefficients for efficiency curve (need p(a=a, yPL=1)=1)"

  parameter Modelica.Units.SI.Pressure dp1_nominal(displayUnit="Pa")
    "Pressure difference over condenser"
  parameter Modelica.Units.SI.Pressure dp2_nominal(displayUnit="Pa")
    "Pressure difference over evaporator"

  parameter Modelica.Units.SI.TemperatureDifference TAppCon_nominal(min=0) =
    if cp1_default < 1500 then 5 else 2
    "Temperature difference between refrigerant and working fluid outlet in condenser"

  parameter Modelica.Units.SI.TemperatureDifference TAppEva_nominal(min=0) =
    if cp2_default < 1500 then 5 else 2
    "Temperature difference between refrigerant and working fluid outlet in evaporator"

  parameter Boolean from_dp1=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"
  parameter Boolean from_dp2=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"

  parameter Boolean linearizeFlowResistance1=false
    "= true, use linear relation between m_flow and dp for any flow rate"
  parameter Boolean linearizeFlowResistance2=false
    "= true, use linear relation between m_flow and dp for any flow rate"

  parameter Real deltaM1(final unit="1")=0.1
    "Fraction of nominal flow rate where flow transitions to laminar"
  parameter Real deltaM2(final unit="1")=0.1
    "Fraction of nominal flow rate where flow transitions to laminar"

  parameter Modelica.Units.SI.Time tau1=60
    "Time constant at nominal flow rate (used if energyDynamics1 <> Modelica.Fluid.Types.Dynamics.SteadyState)"
  parameter Modelica.Units.SI.Time tau2=60
    "Time constant at nominal flow rate (used if energyDynamics2 <> Modelica.Fluid.Types.Dynamics.SteadyState)"

  parameter Modelica.Units.SI.Temperature T1_start=Medium1.T_default
    "Initial or guess value of set point"
  parameter Modelica.Units.SI.Temperature T2_start=Medium2.T_default
    "Initial or guess value of set point"

  parameter Modelica.Fluid.Types.Dynamics energyDynamics=
    Modelica.Fluid.Types.Dynamics.SteadyState "Type of energy balance: dynamic (3 initialization options) or steady state"

  Modelica.Blocks.Interfaces.RealOutput QCon_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Actual heating heat flow rate added to fluid 1"

  Modelica.Blocks.Interfaces.RealOutput P(
    final quantity="Power",
    final unit="W") "Electric power consumed by compressor"

  Modelica.Blocks.Interfaces.RealOutput QEva_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Actual cooling heat flow rate removed from fluid 2"

  Real yPL(final unit="1", min=0) = if COP_is_for_cooling
     then QEva_flow/QEva_flow_nominal
     else QCon_flow/QCon_flow_nominal "Part load ratio";

  Real etaPL(final unit = "1")=
    if evaluate_etaPL
      then Buildings.Utilities.Math.Functions.polynomial(a=a, x=yPL)
      else 1
    "Efficiency due to part load (etaPL(yPL=1)=1)";

  Real COP(min=0, final unit="1") = etaCarnot_nominal_internal * COPCar * etaPL
    "Coefficient of performance";

  Real COPCar(min=0) = TUseAct/Buildings.Utilities.Math.Functions.smoothMax(
    x1=1,
    x2=TConAct - TEvaAct,
    deltaX=0.25) "Carnot efficiency";

  Modelica.Units.SI.Temperature TConAct(start=TCon_nominal + TAppCon_nominal)
     = Medium1.temperature(staB1) + QCon_flow/QCon_flow_nominal*TAppCon_nominal
    "Condenser temperature used to compute efficiency, taking into account pinch temperature between fluid and refrigerant";

  Modelica.Units.SI.Temperature TEvaAct(start=TEva_nominal - TAppEva_nominal)
     = Medium2.temperature(staB2) - QEva_flow/QEva_flow_nominal*TAppEva_nominal
    "Evaporator temperature used to compute efficiency, taking into account pinch temperature between fluid and refrigerant";

protected
  constant Boolean COP_is_for_cooling
    "Set to true if the specified COP is for cooling";

  parameter Real etaCarnot_nominal_internal(unit="1")=
    if use_eta_Carnot_nominal
      then etaCarnot_nominal
      else COP_nominal/
           (TUseAct_nominal / (TCon_nominal + TAppCon_nominal - (TEva_nominal - TAppEva_nominal)))
    "Carnot effectiveness (=COP/COP_Carnot) used to compute COP";

  // For Carnot_y, computing etaPL = f(yPL) introduces a nonlinear equation.
  // The parameter below avoids this if a = {1}.
  final parameter Boolean evaluate_etaPL=
    not ((size(a, 1) == 1 and abs(a[1] - 1)  < Modelica.Constants.eps))
    "Flag, true if etaPL should be computed as it depends on yPL"

  final parameter Modelica.Units.SI.Temperature TUseAct_nominal=if
      COP_is_for_cooling then TEva_nominal - TAppEva_nominal else TCon_nominal
       + TAppCon_nominal
    "Nominal evaporator temperature for chiller or condenser temperature for heat pump, taking into account pinch temperature between fluid and refrigerant";
  Modelica.Units.SI.Temperature TUseAct=if COP_is_for_cooling then TEvaAct
       else TConAct
    "Temperature of useful heat (evaporator for chiller, condenser for heat pump), taking into account pinch temperature between fluid and refrigerant";

  final parameter Modelica.Units.SI.SpecificHeatCapacity cp1_default=
      Medium1.specificHeatCapacityCp(Medium1.setState_pTX(
      p=Medium1.p_default,
      T=Medium1.T_default,
      X=Medium1.X_default))
    "Specific heat capacity of medium 1 at default medium state";

  final parameter Modelica.Units.SI.SpecificHeatCapacity cp2_default=
      Medium2.specificHeatCapacityCp(Medium2.setState_pTX(
      p=Medium2.p_default,
      T=Medium2.T_default,
      X=Medium2.X_default))
    "Specific heat capacity of medium 2 at default medium state";

  Medium1.ThermodynamicState staA1 = Medium1.setState_phX(
    port_a1.p,
    inStream(port_a1.h_outflow),
    inStream(port_a1.Xi_outflow)) "Medium properties in port_a1";
  Medium1.ThermodynamicState staB1 = Medium1.setState_phX(
    port_b1.p,
    port_b1.h_outflow,
    port_b1.Xi_outflow) "Medium properties in port_b1";
  Medium2.ThermodynamicState staA2 = Medium2.setState_phX(
    port_a2.p,
    inStream(port_a2.h_outflow),
    inStream(port_a2.Xi_outflow)) "Medium properties in port_a2";
  Medium2.ThermodynamicState staB2 = Medium2.setState_phX(
    port_b2.p,
    port_b2.h_outflow,
    port_b2.Xi_outflow) "Medium properties in port_b2";

  replaceable Interfaces.PartialTwoPortInterface con
    constrainedby Interfaces.PartialTwoPortInterface(
      redeclare final package Medium = Medium1,
      final allowFlowReversal=allowFlowReversal1,
      final m_flow_nominal=m1_flow_nominal,
      final m_flow_small=m1_flow_small,
      final show_T=false) "Condenser"

  replaceable Interfaces.PartialTwoPortInterface eva
    constrainedby Interfaces.PartialTwoPortInterface(
      redeclare final package Medium = Medium2,
      final allowFlowReversal=allowFlowReversal2,
      final m_flow_nominal=m2_flow_nominal,
      final m_flow_small=m2_flow_small,
      final show_T=false) "Evaporator"

initial equation
  assert(dTEva_nominal < 0,
    "Parameter dTEva_nominal must be negative.");
  assert(dTCon_nominal > 0,
    "Parameter dTCon_nominal must be positive.");

  assert(abs(Buildings.Utilities.Math.Functions.polynomial(
         a=a, x=1)-1) < 0.01, "Efficiency curve is wrong. Need etaPL(y=1)=1.");
  assert(etaCarnot_nominal_internal < 1,   "Parameters lead to etaCarnot_nominal > 1. Check parameters.");

  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

equation
  connect(port_a2, eva.port_a)
  connect(eva.port_b, port_b2)
  connect(port_a1, con.port_a)
  connect(con.port_b, port_b1)
end Carnot;


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


Partial model for chiller with performance curve adjusted based on Carnot efficiency. This is a partial model of a chiller whose coefficient of performance (COP) changes
with temperatures in the same way as the Carnot efficiency changes.
This base class is used for the Carnot chiller and Carnot heat pump
that uses the compressor part load ratio as the control signal.

within Buildings.Fluid.Chillers.BaseClasses;
partial model PartialCarnot_T
  "Partial model for chiller with performance curve adjusted based on Carnot efficiency"
  extends Buildings.Fluid.Chillers.BaseClasses.Carnot;

protected
  Modelica.Blocks.Sources.RealExpression PEle "Electrical power consumption"

equation
  connect(PEle.y, P)
end PartialCarnot_T;


Partial chiller model with performance curve adjusted based on Carnot efficiency. This is a partial model of a chiller whose coefficient of performance (COP) changes
with temperatures in the same way as the Carnot efficiency changes.
This base class is used for the Carnot chiller and Carnot heat pump
that uses the leaving fluid temperature as the control signal.

within Buildings.Fluid.Chillers.BaseClasses;
partial model PartialCarnot_y
  "Partial chiller model with performance curve adjusted based on Carnot efficiency"
  extends Carnot(
    final QCon_flow_nominal= P_nominal - QEva_flow_nominal,
    final QEva_flow_nominal = if COP_is_for_cooling
                              then -P_nominal * COP_nominal
                              else -P_nominal * (COP_nominal-1),
    redeclare HeatExchangers.HeaterCooler_u con(
      final from_dp=from_dp1,
      final dp_nominal=dp1_nominal,
      final linearizeFlowResistance=linearizeFlowResistance1,
      final deltaM=deltaM1,
      final tau=tau1,
      final T_start=T1_start,
      final energyDynamics=energyDynamics,
      final homotopyInitialization=homotopyInitialization,
      final Q_flow_nominal=QCon_flow_nominal),
      redeclare HeatExchangers.HeaterCooler_u eva(
      final from_dp=from_dp2,
      final dp_nominal=dp2_nominal,
      final linearizeFlowResistance=linearizeFlowResistance2,
      final deltaM=deltaM2,
      final tau=tau2,
      final T_start=T2_start,
      final energyDynamics=energyDynamics,
      final homotopyInitialization=homotopyInitialization,
      final Q_flow_nominal=QEva_flow_nominal));

  parameter Modelica.Units.SI.Power P_nominal(min=0)
    "Nominal compressor power (at y=1)"

  Modelica.Blocks.Interfaces.RealInput y(min=0, max=1, unit="1")
    "Part load ratio of compressor"

protected
  Modelica.Units.SI.HeatFlowRate QCon_flow_internal(start=QCon_flow_nominal) =
    P - QEva_flow_internal "Condenser heat input";
  Modelica.Units.SI.HeatFlowRate QEva_flow_internal(start=QEva_flow_nominal) =
    if COP_is_for_cooling then -COP*P else (1 - COP)*P "Evaporator heat input";

  Modelica.Blocks.Sources.RealExpression yEva_flow_in(
    y=QEva_flow_internal/QEva_flow_nominal)
    "Normalized evaporator heat flow rate"
  Modelica.Blocks.Sources.RealExpression yCon_flow_in(
    y=QCon_flow_internal/QCon_flow_nominal)
    "Normalized condenser heat flow rate"

  Modelica.Blocks.Math.Gain PEle(final k=P_nominal)
    "Electrical power consumption"
equation

  connect(PEle.y, P)
  connect(PEle.u, y)
  connect(yEva_flow_in.y, eva.u)
  connect(yCon_flow_in.y, con.u)
  connect(con.Q_flow, QCon_flow)
  connect(eva.Q_flow, QEva_flow)
end PartialCarnot_y;


Partial model for electric chiller based on the model in DOE-2, CoolTools and EnergyPlus. Base class for model of an electric chiller, based on the DOE-2.1 chiller model and the
CoolTools chiller model that are implemented in EnergyPlus as the models
Chiller:Electric:EIR and Chiller:Electric:ReformulatedEIR.

within Buildings.Fluid.Chillers.BaseClasses;
partial model PartialElectric
  "Partial model for electric chiller based on the model in DOE-2, CoolTools and EnergyPlus"
  extends Buildings.Fluid.Interfaces.FourPortHeatMassExchanger(
   m1_flow_nominal = mCon_flow_nominal,
   m2_flow_nominal = mEva_flow_nominal,
   T1_start = 273.15+25,
   T2_start = 273.15+5,
   redeclare final Buildings.Fluid.MixingVolumes.MixingVolume vol2(
      V=m2_flow_nominal*tau2/rho2_nominal,
      nPorts=2,
      final prescribedHeatFlowRate=true),
    vol1(
      final prescribedHeatFlowRate=true));

  parameter Boolean have_switchover=false
    "Set to true for heat recovery chiller with built-in switchover"

  Modelica.Blocks.Interfaces.BooleanInput on
    "Set to true to enable compressor, or false to disable compressor"
  Modelica.Blocks.Interfaces.RealInput TSet(unit="K", displayUnit="degC")
    "Set point for leaving chilled water temperature (condenser water if have_switchover=true and coo=false)"
  Modelica.Blocks.Interfaces.RealOutput P(final quantity="Power", unit="W")
    "Electric power consumed by compressor"
  Modelica.Blocks.Interfaces.RealOutput COP_h(final unit="1") if have_switchover
    "Coefficient of performance of heating";

  Modelica.Units.SI.Temperature TEvaEnt "Evaporator entering temperature";
  Modelica.Units.SI.Temperature TEvaLvg "Evaporator leaving temperature";
  Modelica.Units.SI.Temperature TConEnt "Condenser entering temperature";
  Modelica.Units.SI.Temperature TConLvg "Condenser leaving temperature";

  Modelica.Units.SI.Efficiency COP "Coefficient of performance";
  Modelica.Units.SI.HeatFlowRate QCon_flow "Condenser heat input";
  Modelica.Units.SI.HeatFlowRate QEva_flow "Evaporator heat input";

  Real capFunT(min=0, unit="1")
    "Cooling capacity factor function of temperature curve";
  Modelica.Units.SI.Efficiency EIRFunT
    "Power input to cooling capacity ratio function of temperature curve";
  Modelica.Units.SI.Efficiency EIRFunPLR
    "Power input to cooling capacity ratio function of part load ratio";
  Real PLR1(min=0, unit="1", start=0) "Part load ratio";
  Real PLR2(min=0, unit="1") "Part load ratio";
  Real CR(min=0, unit="1") "Cycling ratio";

  Controls.OBC.CDL.Interfaces.BooleanInput coo if have_switchover
    "Switchover signal: true for cooling, false for heating"
  Controls.OBC.CDL.Logical.Sources.Constant tru(
    final k=true) if not have_switchover
    "Constant true signal"
protected
  Controls.OBC.CDL.Interfaces.BooleanInput coo_internal
    "Internal switchover signal: true for cooling, false for heating"

  Modelica.Units.SI.HeatFlowRate QEva_flow_ava(nominal=QEva_flow_nominal, start
      =QEva_flow_nominal) "Cooling capacity available at evaporator";
  Modelica.Units.SI.HeatFlowRate QEva_flow_set(nominal=QEva_flow_nominal, start
      =QEva_flow_nominal)
    "Cooling capacity required to cool to set point temperature";
  Modelica.Units.SI.HeatFlowRate QCon_flow_set(
    nominal=-QEva_flow_nominal * (1 + 1 / COP_nominal * etaMotor),
    start=-QEva_flow_nominal * (1 + 1 / COP_nominal * etaMotor))
    "Heating capacity required to heat up condenser water to setpoint";
  Modelica.Units.SI.SpecificEnthalpy hSet
    "Enthalpy setpoint for leaving chilled water";
  // Performance data
  parameter Modelica.Units.SI.HeatFlowRate QEva_flow_nominal(max=0)
    "Reference capacity (negative number)";
  parameter Modelica.Units.SI.Efficiency COP_nominal
    "Reference coefficient of performance";
  parameter Real PLRMax(min=0, unit="1") "Maximum part load ratio";
  parameter Real PLRMinUnl(min=0, unit="1") "Minimum part unload ratio";
  parameter Real PLRMin(min=0, unit="1") "Minimum part load ratio";
  parameter Modelica.Units.SI.Efficiency etaMotor(max=1)
    "Fraction of compressor motor heat entering refrigerant";
  parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal
    "Nominal mass flow at evaporator";
  parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal
    "Nominal mass flow at condenser";
  parameter Modelica.Units.SI.Temperature TEvaLvg_nominal
    "Temperature of fluid leaving evaporator at nominal condition";
  final parameter Modelica.Units.NonSI.Temperature_degC TEvaLvg_nominal_degC=
      Modelica.Units.Conversions.to_degC(TEvaLvg_nominal)
    "Temperature of fluid leaving evaporator at nominal condition";
  Modelica.Units.NonSI.Temperature_degC TEvaLvg_degC
    "Temperature of fluid leaving evaporator";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_small=QEva_flow_nominal*1E-9
    "Small value for heat flow rate or power, used to avoid division by zero";
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow preHeaFloEva
    "Prescribed heat flow rate"
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow preHeaFloCon
    "Prescribed heat flow rate"
  Modelica.Blocks.Sources.RealExpression QEva_flow_in(y=QEva_flow)
    "Evaporator heat flow rate"
  Modelica.Blocks.Sources.RealExpression QCon_flow_in(y=QCon_flow)
    "Condenser heat flow rate"
  Modelica.Blocks.Sources.RealExpression calCOPHea(
    final y=QCon_flow/(P - Q_flow_small)) if have_switchover
    "Compute heating COP"
initial equation
  assert(QEva_flow_nominal < 0, "Parameter QEva_flow_nominal must be smaller than zero.");
  assert(Q_flow_small < 0, "Parameter Q_flow_small must be smaller than zero.");
  assert(PLRMinUnl >= PLRMin, "Parameter PLRMinUnl must be bigger or equal to PLRMin");
  assert(PLRMax > PLRMinUnl, "Parameter PLRMax must be bigger than PLRMinUnl");
equation
  // Condenser temperatures
  TConEnt = Medium1.temperature(Medium1.setState_phX(port_a1.p,
                                                     inStream(port_a1.h_outflow),
                                                     inStream(port_a1.Xi_outflow)));
  TConLvg = vol1.heatPort.T;
  // Evaporator temperatures
  TEvaEnt = Medium2.temperature(Medium2.setState_phX(port_a2.p, inStream(port_a2.h_outflow)));
  TEvaLvg = vol2.heatPort.T;
  TEvaLvg_degC=Modelica.Units.Conversions.to_degC(TEvaLvg);

  // Enthalpy of temperature setpoint
  hSet =if coo_internal then Medium2.specificEnthalpy_pTX(
    p=port_b2.p,
    T=TSet,
    X=cat(
      1,
      port_b2.Xi_outflow,
      {1 - sum(port_b2.Xi_outflow)})) else Medium1.specificEnthalpy_pTX(
    p=port_b1.p,
    T=TSet,
    X=cat(
      1,
      port_b1.Xi_outflow,
      {1 - sum(port_b1.Xi_outflow)}));

  if on then
    // Available cooling capacity
    QEva_flow_ava = QEva_flow_nominal*capFunT;
    // Cooling capacity required to chill water to setpoint
    QEva_flow_set = Buildings.Utilities.Math.Functions.smoothMin(
      x1=if coo_internal then m2_flow * (hSet - inStream(port_a2.h_outflow))
         else P * etaMotor - QCon_flow_set,
      x2=Q_flow_small,
      deltaX=-Q_flow_small/100);
    // Heating capacity required to heat up condenser water to setpoint
    // (Q_flow_small is a negative number.)
    QCon_flow_set = Buildings.Utilities.Math.Functions.smoothMax(
      x1=if coo_internal then QCon_flow else m1_flow * (hSet - inStream(port_a1.h_outflow)),
      x2=-Q_flow_small,
      deltaX=-Q_flow_small/100);
    // Part load ratio
    PLR1 = Buildings.Utilities.Math.Functions.smoothMin(
      x1 = QEva_flow_set/(QEva_flow_ava+Q_flow_small),
      x2 = PLRMax,
      deltaX=PLRMax/100);
    // PLR2 is the compressor part load ratio. The lower bound PLRMinUnl is
    // since for PLR1<PLRMinUnl, the chiller uses hot gas bypass, under which
    // condition the compressor power is assumed to be the same as if the chiller
    // were to operate at PLRMinUnl
    PLR2 = Buildings.Utilities.Math.Functions.smoothMax(
      x1 = PLRMinUnl,
      x2 = PLR1,
      deltaX = PLRMinUnl/100);

    // Cycling ratio.
    // Due to smoothing, this can be about deltaX/10 above 1.0
    CR = Buildings.Utilities.Math.Functions.smoothMin(
      x1 = PLR1/PLRMin,
      x2 = 1,
      deltaX=0.001);

    // Compressor power.
    P = -QEva_flow_ava/COP_nominal*EIRFunT*EIRFunPLR*CR;
    // Heat flow rates into evaporator and condenser
    // Q_flow_small is a negative number.
    QEva_flow = Buildings.Utilities.Math.Functions.smoothMax(
      x1 = QEva_flow_set,
      x2 = QEva_flow_ava,
      deltaX= -Q_flow_small/10);

  //QEva_flow = max(QEva_flow_set, QEva_flow_ava);
    QCon_flow = -QEva_flow + P*etaMotor;
    // Coefficient of performance
    COP = -QEva_flow/(P-Q_flow_small);
  else
    QEva_flow_ava = 0;
    QEva_flow_set = 0;
    QCon_flow_set = 0;
    PLR1 = 0;
    PLR2 = 0;
    CR   = 0;
    P    = 0;
    QEva_flow = 0;
    QCon_flow = 0;
    COP  = 0;
  end if;
  connect(calCOPHea.y, COP_h);
  connect(QCon_flow_in.y, preHeaFloCon.Q_flow)
  connect(preHeaFloCon.port, vol1.heatPort)
  connect(QEva_flow_in.y, preHeaFloEva.Q_flow)
  connect(preHeaFloEva.port, vol2.heatPort)
  connect(coo, coo_internal)
  connect(tru.y, coo_internal)
end PartialElectric;


within Buildings.Fluid.Chillers.Data;
package AbsorptionIndirectSteam
  extends Modelica.Icons.MaterialPropertiesPackage;

  record Generic "Generic data record for absorption indirect chiller"
    extends Modelica.Icons.Record;

    parameter Modelica.Units.SI.HeatFlowRate QEva_flow_nominal(final max=0)
      "Nominal evaporator cooling capacity (negative number)"
    parameter Modelica.Units.SI.Power P_nominal "Nominal absorber pump power"

    parameter Real PLRMax(min=0)
    "Maximum part load ratio"
    parameter Real PLRMin(min=0)
    "Minimum part load ratio"

    parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal
      "Nominal mass flow rate at condenser"
    parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal
      "Nominal mass flow rate at evaporator"
    parameter Modelica.Units.SI.PressureDifference dpCon_nominal(displayUnit=
          "Pa") = 30000
      "Pressure difference at condenser at nominal mass flow rate"
    parameter Modelica.Units.SI.PressureDifference dpEva_nominal(displayUnit=
          "Pa") = 30000
      "Pressure difference at evaporator at nominal mass flow rate"

    parameter Real capFunEva[4]
    "Cubic coefficients for the evaporator capacity factor as a function of temperature curve"
    parameter Real capFunCon[4]
    "Cubic coefficients for capFunCon for the condenser capacity factor as a function of temperature curve"
    parameter Real genHIR[4]
    "Cubic coefficients for the generator heat input to chiller operating capacity"
    parameter Real genConT[4]
    "Cubic coefficients for heat input modifier based on the generator input temperature"
    parameter Real genEvaT[4]
    "Cubic coefficients for heat input modifier based on the evaporator input temperature"
    parameter Real EIRP[3]
    "Quadratic coefficients for the actual absorber pumping power to the nominal pumping power"
  end Generic;
end AbsorptionIndirectSteam;


Base classes for package Data

within Buildings.Fluid.Chillers.Data;
package BaseClasses "Base classes for package Data"
  extends Modelica.Icons.BasesPackage;

  record Chiller "Base classes for chiller models"
    extends Modelica.Icons.Record;
    parameter Modelica.Units.SI.HeatFlowRate QEva_flow_nominal(max=0)
      "Reference capacity (negative number)"
    parameter Real COP_nominal "Reference coefficient of performance"

    parameter Real PLRMax(min=0) "Maximum part load ratio";
    parameter Real PLRMinUnl(min=0) "Minimum part unload ratio";
    parameter Real PLRMin(min=0) "Minimum part load ratio";

    parameter Real etaMotor(min=0, max=1)
      "Fraction of compressor motor heat entering refrigerant";

    parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal
      "Nominal mass flow at evaporator"
    parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal
      "Nominal mass flow at condenser"
    parameter Modelica.Units.SI.Temperature TEvaLvg_nominal
      "Temperature of fluid leaving evaporator at nominal condition"

    constant Integer nCapFunT "Number of coefficients for capFunT"
    constant Integer nEIRFunT "Number of coefficients for EIRFunT"
    constant Integer nEIRFunPLR "Number of coefficients for EIRFunPLR"
    parameter Real capFunT[nCapFunT] "Biquadratic coefficients for capFunT"
    parameter Real EIRFunT[nEIRFunT] "Biquadratic coefficients for EIRFunT"
    parameter Real EIRFunPLR[nEIRFunPLR] "Coefficients for EIRFunPLR"
    parameter Modelica.Units.SI.Temperature TEvaLvgMin
      "Minimum value for leaving evaporator temperature"
    parameter Modelica.Units.SI.Temperature TEvaLvgMax
      "Maximum value for leaving evaporator temperature"
  end Chiller;
end BaseClasses;