CHW and/or HW minimum flow bypass valve controller.

within Buildings.Templates.Plants.Controls.MinimumFlow;
block ControllerDualMode
  "CHW and/or HW minimum flow bypass valve controller"
  parameter Boolean have_heaWat
    "Set to true for plants that provide HW"
  parameter Boolean have_chiWat
    "Set to true for plants that provide CHW"
  parameter Boolean have_pumChiWatPri(
    start=not have_heaWat)
    "Set to true for plants with separate primary CHW pumps"
  parameter Boolean have_valInlIso
    "Set to true to enable control loop based on inlet isolation valve command"
  parameter Boolean have_valOutIso
    "Set to true to enable control loop based on outlet isolation valve command"
  parameter Integer nEqu=0
    "Number of plant equipment"
  parameter Integer nEnaHeaWat=0
    "Number of enable signals for HW loop – Valve command or pump status"
  parameter Integer nEnaChiWat=nEnaChiWat
    "Number of enable signals for CHW loop – Valve command or pump status"
  parameter Real VHeaWat_flow_nominal[nEqu](
    start=fill(0, nEqu),
    unit=fill("m3/s", nEqu))
    "Design HW flow rate – Each unit"
  parameter Real VHeaWat_flow_min[nEqu](
    start=fill(0, nEqu),
    unit=fill("m3/s", nEqu))
    "Minimum HW flow rate – Each unit"
  parameter Real VChiWat_flow_nominal[nEqu](
    start=fill(0, nEqu),
    unit=fill("m3/s", nEqu))
    "Design CHW flow rate – Each unit"
  parameter Real VChiWat_flow_min[nEqu](
    start=fill(0, nEqu),
    unit=fill("m3/s", nEqu))
    "Minimum CHW flow rate – Each unit"
  parameter Real k(
    min=0)=1
    "Gain of controller";
  parameter Modelica.Units.SI.Time Ti(
    min=Buildings.Controls.OBC.CDL.Constants.small)=0.5
    "Time constant of integrator block";
  Buildings.Controls.OBC.CDL.Interfaces.RealInput VChiWatPri_flow(
    final unit="m3/s")
    if have_chiWat
    "Primary CHW volume flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput VHeaWatPri_flow(
    final unit="m3/s")
    if have_heaWat
    "Primary HW volume flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1PumHeaWatPri_actual[nEnaHeaWat]
    if have_heaWat and not
                          (have_valInlIso or have_valOutIso)
    "Primary HW pump status"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValHeaWatInlIso[nEnaHeaWat]
    if have_heaWat and have_valInlIso
    "Equipment inlet HW isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValHeaWatOutIso[nEnaHeaWat]
    if have_heaWat and have_valOutIso
    "Equipment outlet HW isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput yValHeaWatMinByp(
    final unit="1")
    if have_heaWat
    "HW minimum flow bypass valve command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Equ[nEqu]
    "Equipment enable command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput VHeaWatPriSet_flow(
    final unit="m3/s")
    if have_heaWat
    "Primary HW flow setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1HeaEqu[nEqu]
    if have_heaWat and have_chiWat
    "Equipment heating/cooling mode command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1PumChiWatPri_actual[nEnaChiWat]
    if have_chiWat and have_pumChiWatPri and not have_valInlIso and not have_valOutIso
    "Primary CHW pump status"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValChiWatInlIso[nEnaChiWat]
    if have_chiWat and have_valInlIso
    "Equipment inlet CHW isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValChiWatOutIso[nEnaChiWat]
    if have_chiWat and have_valOutIso
    "Equipment uutlet CHW isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput yValChiWatMinByp(
    final unit="1")
    if have_chiWat
    "CHW minimum flow bypass valve command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput VChiWatPriSet_flow(
    final unit="m3/s")
    if have_chiWat
    "Primary CHW flow setpoint"
  Controller ctlFloMinHeaWat(
    final have_valInlIso=have_valInlIso,
    final have_valOutIso=have_valOutIso,
    final V_flow_nominal=VHeaWat_flow_nominal,
    final V_flow_min=VHeaWat_flow_min,
    final k=k,
    final Ti=Ti,
    final nEqu=nEqu,
    final nEna=nEnaHeaWat)
    if have_heaWat
    "HW minimum flow control"
  Controller ctlFloMinChiWat(
    final have_valInlIso=have_valInlIso,
    final have_valOutIso=have_valOutIso,
    final V_flow_nominal=VChiWat_flow_nominal,
    final V_flow_min=VChiWat_flow_min,
    final k=k,
    final Ti=Ti,
    final nEqu=nEqu,
    final nEna=nEnaChiWat)
    if have_chiWat
    "CHW minimum flow control"
  Utilities.PlaceholderLogical phHeaCoo[nEqu](
    each final have_inp=have_heaWat and have_chiWat,
    each final have_inpPh=false,
    each final u_internal=have_heaWat)
    "Placeholder signal for single mode applications"
  Buildings.Controls.OBC.CDL.Logical.Not u1Coo[nEqu]
    "True if cooling mode"
  Buildings.Controls.OBC.CDL.Logical.And onAndHea[nEqu]
    if have_heaWat
    "True if commanded on in heating mode"
  Buildings.Controls.OBC.CDL.Logical.And onAndCoo[nEqu]
    if have_chiWat
    "True if commanded on in cooling mode"
  Utilities.PlaceholderLogical phPumChiWatPri[nEnaChiWat](
    each final have_inp=have_heaWat and have_chiWat and not have_pumChiWatPri,
    each final have_inpPh=true)
    if have_chiWat and not
                          (have_valInlIso or have_valOutIso)
    "Use CHW pump signal in case the plant does not have common CHW and HW dedicated pumps"
  Buildings.Controls.OBC.CDL.Logical.And pumOnAndCoo[nEqu]
    if have_heaWat and have_chiWat and not have_pumChiWatPri and not
                                                                    (have_valInlIso
      or have_valOutIso)
    "True if pump proven on and equipment in cooling mode – Case with common CHW and HW dedicated pumps"
  Buildings.Controls.OBC.CDL.Logical.And pumOnAndHea[nEqu]
    if have_heaWat and have_chiWat and not have_pumChiWatPri and not
                                                                    (have_valInlIso
      or have_valOutIso)
    "True if pump proven on and equipment in heating mode – Case with common CHW and HW dedicated pumps"
  Utilities.PlaceholderLogical phPumHeaWatPri[nEnaHeaWat](
    each final have_inp=have_heaWat and have_chiWat and not have_pumChiWatPri,
    each final have_inpPh=true)
    if have_heaWat and not
                          (have_valInlIso or have_valOutIso)
    "Use HW pump signal in case the plant does not have common CHW and HW dedicated pumps"
equation
  connect(ctlFloMinHeaWat.y, yValHeaWatMinByp)
  connect(ctlFloMinHeaWat.VPriSet_flow, VHeaWatPriSet_flow)
  connect(ctlFloMinChiWat.y, yValChiWatMinByp)
  connect(ctlFloMinChiWat.VPriSet_flow, VChiWatPriSet_flow)
  connect(u1HeaEqu, phHeaCoo.u)
  connect(phHeaCoo.y, u1Coo.u)
  connect(u1Equ, onAndHea.u1)
  connect(onAndHea.y, ctlFloMinHeaWat.u1Equ)
  connect(onAndCoo.y, ctlFloMinChiWat.u1Equ)
  connect(u1ValHeaWatInlIso, ctlFloMinHeaWat.u1ValInlIso)
  connect(u1ValHeaWatOutIso, ctlFloMinHeaWat.u1ValOutIso)
  connect(VChiWatPri_flow, ctlFloMinChiWat.VPri_flow)
  connect(u1ValChiWatInlIso, ctlFloMinChiWat.u1ValInlIso)
  connect(u1ValChiWatOutIso, ctlFloMinChiWat.u1ValOutIso)
  connect(VHeaWatPri_flow, ctlFloMinHeaWat.VPri_flow)
  connect(u1Equ, onAndCoo.u1)
  connect(u1Coo.y, onAndCoo.u2)
  connect(phPumChiWatPri.y, ctlFloMinChiWat.u1PumPri_actual)
  connect(u1Coo.y, pumOnAndCoo.u1)
  connect(u1PumHeaWatPri_actual, pumOnAndCoo.u2)
  connect(phHeaCoo.y, onAndHea.u2)
  connect(u1PumHeaWatPri_actual, pumOnAndHea.u2)
  connect(phHeaCoo.y, pumOnAndHea.u1)
  connect(phPumHeaWatPri.y, ctlFloMinHeaWat.u1PumPri_actual)
  connect(pumOnAndHea.y, phPumHeaWatPri.u)
  connect(u1PumHeaWatPri_actual, phPumHeaWatPri.uPh)
  connect(pumOnAndCoo.y, phPumChiWatPri.u)
  connect(u1PumChiWatPri_actual, phPumChiWatPri.uPh)
end ControllerDualMode;


within Buildings.Templates.Plants.Controls;
package MinimumFlow
  "Package of sequences for minimum flow bypass valve"
end MinimumFlow;


Minimum flow setpoint calculation. The minimum flow setpoint is determined as follows, to provide minimum flow
through all operating units.

within Buildings.Templates.Plants.Controls.MinimumFlow;
block Setpoint
  "Minimum flow setpoint calculation"
  parameter Integer nEqu(
    final min=1)=0
    "Number of plant equipment"
  parameter Real V_flow_nominal[:](
    each final unit="m3/s")
    "Design flow rate – Each unit";
  parameter Real V_flow_min[:](
    each final unit="m3/s")
    "Minimum flow rate – Each unit";
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1[nEqu]
    "Equipment enable command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput VPriSet_flow(
    final unit="m3/s")
    "Primary flow setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant floMin[nEqu](
    final k=V_flow_min)
    "Minimum flow rate"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea[nEqu](
    each final realTrue=1,
    each final realFalse=0)
    "Convert to real signal"
  Buildings.Controls.OBC.CDL.Reals.Multiply floMinEna[nEqu]
    "Minimum flow for enabled equipment"
  Buildings.Controls.OBC.CDL.Reals.Divide ratFloMinEna[nEqu]
    "Minimum flow ratio for enabled equipment"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant floDes[nEqu](
    final k=V_flow_nominal)
    "Design flow rate"
  Buildings.Controls.OBC.CDL.Reals.MultiMax maxRatFloMinEna(
    nin=nEqu)
    "Maximum of minimum flow ratio for enabled equipment"
  Buildings.Controls.OBC.CDL.Reals.Multiply floDesEqu[nEqu]
    "Design flow for enabled equipment"
  Buildings.Controls.OBC.CDL.Reals.MultiSum sumFloDesEna(
    nin=nEqu)
    "Sum of design flow for enabled equipment"
  Buildings.Controls.OBC.CDL.Reals.Multiply floMinSet
    "Minimum flow setpoint"
equation
  connect(u1, booToRea.u)
  connect(booToRea.y, floMinEna.u2)
  connect(floMin.y, floMinEna.u1)
  connect(floDes.y, ratFloMinEna.u2)
  connect(floMinEna.y, ratFloMinEna.u1)
  connect(ratFloMinEna.y, maxRatFloMinEna.u)
  connect(booToRea.y, floDesEqu.u1)
  connect(floDes.y, floDesEqu.u2)
  connect(maxRatFloMinEna.y, floMinSet.u1)
  connect(floMinSet.y, VPriSet_flow)
  connect(floDesEqu.y, sumFloDesEna.u)
  connect(sumFloDesEna.y, floMinSet.u2)
end Setpoint;


Validation model for the minimum flow bypass valve controller. This model validates

Buildings.Templates.Plants.Controls.MinimumFlow.Controller
in two configurations: one where the bypass valve controller is enabled
with the isolation valve command (component ctlFloMinVal), and
another where it is enabled with the primary pump status (component ctlFloMinPum).

within Buildings.Templates.Plants.Controls.MinimumFlow.Validation;
model Controller
  "Validation model for the minimum flow bypass valve controller"
  Buildings.Templates.Plants.Controls.MinimumFlow.Controller ctlFloMinPum(
    have_valInlIso=false,
    have_valOutIso=false,
    V_flow_nominal={0.02, 0.05},
    V_flow_min={0.01, 0.03},
    nEna=2,
    nEqu=2)
    "Valve controller enabled by primary pump status"
  Buildings.Templates.Plants.Controls.MinimumFlow.Controller ctlFloMinVal(
    have_valInlIso=true,
    have_valOutIso=false,
    V_flow_nominal={0.02, 0.05},
    V_flow_min={0.01, 0.03},
    nEna=2,
    nEqu=2)
    "Valve controller enabled by isolation valve command"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1EquValPum(
    table=[
      0, 0, 0;
      1, 1, 0;
      2, 1, 1;
      3, 0, 1;
      4, 0, 0],
    timeScale=200,
    period=900)
    "Source signal for equipment or valve command or pump status"
  Buildings.Controls.OBC.CDL.Reals.Sources.Sin dVPri_flow(
    amplitude=sum(ctlFloMinVal.V_flow_min) / 2,
    freqHz=3 / 1000)
    "Source signal for primary flow rate variation around setpoint"
  Buildings.Controls.OBC.CDL.Reals.Add VPri_flow1
    "Primary flow rate"
  Buildings.Controls.OBC.CDL.Reals.Add VPri_flow
    "Primary flow rate"
equation
  connect(u1EquValPum.y[1:2], ctlFloMinVal.u1ValInlIso[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPum.u1PumPri_actual[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinVal.u1Equ[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPum.u1Equ[1:2])
  connect(dVPri_flow.y, VPri_flow1.u2)
  connect(VPri_flow1.y, ctlFloMinPum.VPri_flow)
  connect(ctlFloMinPum.VPriSet_flow, VPri_flow1.u1)
  connect(ctlFloMinVal.VPriSet_flow, VPri_flow.u1)
  connect(dVPri_flow.y, VPri_flow.u2)
  connect(VPri_flow.y, ctlFloMinVal.VPri_flow)
end Controller;


Validation model for the dual mode minimum flow bypass valve controller. This model validates

Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode
for cooling-only (components *Coo), heating-only (components *Hea)
and heating and cooling applications (components *HeaCoo),
and for two plant configurations: one where the bypass valve controller is enabled
with the isolation valve command (components ctlFloMinVal*), and
another where it is enabled with the primary pump status (components ctlFloMinPum*).

within Buildings.Templates.Plants.Controls.MinimumFlow.Validation;
model ControllerDualMode
  "Validation model for the dual mode minimum flow bypass valve controller"
  Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode ctlFloMinPumCoo(
    have_heaWat=false,
    have_chiWat=true,
    have_valInlIso=false,
    have_valOutIso=false,
    VChiWat_flow_nominal={0.02, 0.05},
    VChiWat_flow_min={0.01, 0.03},
    nEqu=2,
    nEnaChiWat=2)
    "Valve controller enabled by primary pump status – Cooling-only application"
  Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode ctlFloMinValCoo(
    have_heaWat=false,
    have_chiWat=true,
    have_valInlIso=true,
    have_valOutIso=false,
    VChiWat_flow_nominal={0.02, 0.05},
    VChiWat_flow_min={0.01, 0.03},
    nEqu=2,
    nEnaChiWat=2)
    "Valve controller enabled by isolation valve command – Cooling-only application"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1EquValPum(
    table=[
      0, 0, 0;
      1, 1, 0;
      2, 1, 1;
      3, 0, 1;
      4, 0, 0],
    timeScale=200,
    period=900)
    "Source signal for equipment or valve command or pump status"
  Buildings.Controls.OBC.CDL.Reals.Sources.Sin dVPri_flow(
    amplitude=sum(ctlFloMinValCoo.VChiWat_flow_min) / 2,
    freqHz=3 / 1000)
    "Source signal for primary flow rate variation around setpoint"
  Buildings.Controls.OBC.CDL.Reals.Add VPriCoo_flow1
    "Primary flow rate"
  Buildings.Controls.OBC.CDL.Reals.Add VPriCoo_flow
    "Primary flow rate"
  Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode ctlFloMinPumHea(
    have_heaWat=true,
    have_chiWat=false,
    have_valInlIso=false,
    have_valOutIso=false,
    VHeaWat_flow_nominal={0.02, 0.05},
    VHeaWat_flow_min={0.01, 0.03},
    nEqu=2,
    nEnaChiWat=2,
    nEnaHeaWat=2)
    "Valve controller enabled by primary pump status – Heating-only application"
  Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode ctlFloMinValHea(
    have_heaWat=true,
    have_chiWat=false,
    have_valInlIso=true,
    have_valOutIso=false,
    VHeaWat_flow_nominal={0.02, 0.05},
    VHeaWat_flow_min={0.01, 0.03},
    nEqu=2,
    nEnaChiWat=2,
    nEnaHeaWat=2)
    "Valve controller enabled by isolation valve command – Heating-only application"
  Buildings.Controls.OBC.CDL.Reals.Add VPriHea_flow1
    "Primary flow rate"
  Buildings.Controls.OBC.CDL.Reals.Add VPriHea_flow
    "Primary flow rate"
  Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode ctlFloMinPumHeaCoo(
    have_heaWat=true,
    have_chiWat=true,
    have_pumChiWatPri=false,
    have_valInlIso=false,
    have_valOutIso=false,
    VHeaWat_flow_nominal={0.02, 0.05},
    VHeaWat_flow_min={0.01, 0.03},
    VChiWat_flow_nominal={0.02, 0.05},
    VChiWat_flow_min={0.01, 0.03},
    nEqu=2,
    nEnaChiWat=2,
    nEnaHeaWat=2)
    "Valve controller enabled by primary pump status – Heating and cooling application"
  Buildings.Templates.Plants.Controls.MinimumFlow.ControllerDualMode ctlFloMinValHeaCoo(
    have_heaWat=true,
    have_chiWat=true,
    have_pumChiWatPri=false,
    have_valInlIso=true,
    have_valOutIso=false,
    VHeaWat_flow_nominal={0.02, 0.05},
    VHeaWat_flow_min={0.01, 0.03},
    VChiWat_flow_nominal={0.02, 0.05},
    VChiWat_flow_min={0.01, 0.03},
    nEqu=2,
    nEnaChiWat=2,
    nEnaHeaWat=2)
    "Valve controller enabled by isolation valve command – Heating and cooling application"
  Buildings.Controls.OBC.CDL.Reals.Add VPriHeaCoo_flow1
    "Primary flow rate"
  Buildings.Controls.OBC.CDL.Reals.Add VPriHeaCoo_flow
    "Primary flow rate"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1HeaEqu(
    table=[
      0, 0, 0;
      1.5, 1, 0;
      2.5, 0, 1],
    timeScale=200,
    period=900)
    "Source signal for equipment heating/cooling mode command or pump status"
  Buildings.Controls.OBC.CDL.Logical.And u1ValChiWat[2]
    "Source signal for CHW isolation valves"
  Buildings.Controls.OBC.CDL.Logical.And u1ValHeaWat[2]
    "Source signal for HW isolation valves"
  Buildings.Controls.OBC.CDL.Logical.Not u1CooEqu[2]
    "True if equipment is commanded in cooling mode"
equation
  connect(u1EquValPum.y[1:2], ctlFloMinValCoo.u1Equ[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPumCoo.u1Equ[1:2])
  connect(dVPri_flow.y, VPriCoo_flow1.u2)
  connect(dVPri_flow.y, VPriCoo_flow.u2)
  connect(ctlFloMinPumCoo.VChiWatPriSet_flow, VPriCoo_flow1.u1)
  connect(ctlFloMinValCoo.VChiWatPriSet_flow, VPriCoo_flow.u1)
  connect(VPriCoo_flow.y, ctlFloMinValCoo.VChiWatPri_flow)
  connect(VPriCoo_flow1.y, ctlFloMinPumCoo.VChiWatPri_flow)
  connect(u1EquValPum.y[1:2], ctlFloMinValCoo.u1ValChiWatInlIso[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPumCoo.u1PumChiWatPri_actual[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinValHea.u1Equ[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPumHea.u1Equ[1:2])
  connect(dVPri_flow.y, VPriHea_flow.u2)
  connect(dVPri_flow.y, VPriHea_flow1.u2)
  connect(ctlFloMinPumHea.VHeaWatPriSet_flow, VPriHea_flow1.u1)
  connect(ctlFloMinValHea.VHeaWatPriSet_flow, VPriHea_flow.u1)
  connect(VPriHea_flow1.y, ctlFloMinPumHea.VHeaWatPri_flow)
  connect(VPriHea_flow.y, ctlFloMinValHea.VHeaWatPri_flow)
  connect(u1EquValPum.y[1:2], ctlFloMinValHea.u1ValHeaWatInlIso[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPumHea.u1PumHeaWatPri_actual[1:2])
  connect(u1EquValPum.y[1:2], ctlFloMinPumHeaCoo.u1Equ[1:2])
  connect(ctlFloMinPumHeaCoo.VHeaWatPriSet_flow, VPriHeaCoo_flow1.u1)
  connect(ctlFloMinValHeaCoo.VHeaWatPriSet_flow, VPriHeaCoo_flow.u1)
  connect(VPriHeaCoo_flow1.y, ctlFloMinPumHeaCoo.VHeaWatPri_flow)
  connect(VPriHeaCoo_flow.y, ctlFloMinValHeaCoo.VHeaWatPri_flow)
  connect(u1EquValPum.y[1:2], ctlFloMinValHeaCoo.u1Equ)
  connect(u1EquValPum.y[1:2], ctlFloMinPumHeaCoo.u1PumHeaWatPri_actual[1:2])
  connect(dVPri_flow.y, VPriHeaCoo_flow.u2)
  connect(dVPri_flow.y, VPriHeaCoo_flow1.u2)
  connect(VPriHeaCoo_flow.y, ctlFloMinValHeaCoo.VChiWatPri_flow)
  connect(VPriHeaCoo_flow1.y, ctlFloMinPumHeaCoo.VChiWatPri_flow)
  connect(u1HeaEqu.y[1:2], ctlFloMinValHeaCoo.u1HeaEqu[1:2])
  connect(u1HeaEqu.y[1:2], ctlFloMinPumHeaCoo.u1HeaEqu[1:2])
  connect(u1EquValPum.y[1:2], u1ValChiWat[1:2].u1)
  connect(u1EquValPum.y[1:2], u1ValHeaWat[1:2].u1)
  connect(u1HeaEqu.y[1:2], u1ValHeaWat[1:2].u2)
  connect(u1HeaEqu.y[1:2], u1CooEqu.u)
  connect(u1CooEqu.y, u1ValChiWat.u2)
  connect(u1ValChiWat.y, ctlFloMinValHeaCoo.u1ValChiWatInlIso)
  connect(u1ValHeaWat.y, ctlFloMinValHeaCoo.u1ValHeaWatInlIso)
end ControllerDualMode;


Collection of validation models

within Buildings.Templates.Plants.Controls.MinimumFlow;
package Validation
  "Collection of validation models"
end Validation;


Validation model for the minimum flow setpoint calculation. This model validates

Buildings.Templates.Plants.Controls.MinimumFlow.Setpoint
in a configuration with two equipment with different minimum flow ratios.

within Buildings.Templates.Plants.Controls.MinimumFlow.Validation;
model Setpoint
  "Validation model for the minimum flow setpoint calculation"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1Equ(
    table=[
      0, 0, 0;
      1, 1, 0;
      2, 1, 1;
      3, 0, 1;
      4, 0, 0],
    timeScale=200,
    period=900)
    "Source signal for equipment enable command"
  Buildings.Templates.Plants.Controls.MinimumFlow.Setpoint setFloMin(
    V_flow_nominal={0.02, 0.05},
    V_flow_min={0.01, 0.03},
    nEqu=2)
    "Minimum flow setpoint calculation"
equation
  connect(u1Equ.y[1:2], setFloMin.u1[1:2])
end Setpoint;


within Buildings.Templates.Plants.Controls;
package Pumps "Package of sequences for primary and secondary pumps"
end Pumps;


Differential pressure control for variable speed pumps. For plants where a remote DP sensor(s) is hardwired to the pump controller
(have_senDpRemHar=true):

within Buildings.Templates.Plants.Controls.Pumps.Generic;
block ControlDifferentialPressure
  "Differential pressure control for variable speed pumps"
  parameter Boolean have_senDpRemWir
    "Set to true for remote differential pressure sensor(s) hardwired to controller"
  parameter Integer nPum(
    min=1)
    "Number of pumps that operate at design conditions"
  parameter Integer nSenDpRem(
    min=1)
    "Number of remote loop differential pressure sensors used for pump speed control"
  parameter Real y_min(
    max=1,
    min=0,
    unit="1")=0.1
    "Minimum pump speed";
  final parameter Real y_max(
    final unit="1",
    final min=0,
    final max=2)=1
    "Maximum pump speed";
  parameter Real k(
    min=100 * Buildings.Controls.OBC.CDL.Constants.eps)=1
    "Gain of controller"
  parameter Real Ti(
    min=100 * Buildings.Controls.OBC.CDL.Constants.eps,
    unit="s")=60
    "Time constant of integrator block"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput y1_actual[nPum]
    "Pump status"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpRemSet[nSenDpRem](
    each final unit="Pa")
    if have_senDpRemWir
    "Remote differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpLoc(
    final unit="Pa")
    if not have_senDpRemWir
    "Loop differential pressure local to the plant"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpRem[nSenDpRem](
    each final unit="Pa")
    if have_senDpRemWir
    "Remote loop differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpLocSet[nSenDpRem](
    each final unit="Pa") if not have_senDpRemWir
    "Local differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput y(
    final unit="1")
    "Pump speed command"
  Buildings.Controls.OBC.CDL.Logical.MultiOr anyOn(
    final nin=nPum)
    "Return true when any pump is proven on"
  Buildings.Controls.OBC.CDL.Routing.BooleanScalarReplicator repEna(
    final nout=nSenDpRem)
    "Replicate"
  Buildings.Controls.OBC.CDL.Reals.MultiMax maxSet(
    nin=nSenDpRem)
    if not have_senDpRemWir
    "Maximum DP setpoint"
  Utilities.PIDWithEnable ctlDpRem[nSenDpRem](
    each final k=k,
    each final Ti=Ti,
    each r=dpSca,
    each final yMin=y_min,
    each final yMax=y_max,
    each final y_reset=y_min,
    each final y_neutral=0)
    if have_senDpRemWir
    "Remote differential pressure control"
  Utilities.PIDWithEnable ctlDpLoc(
    final k=k,
    final Ti=Ti,
    final r=dpSca,
    final yMin=y_min,
    final yMax=y_max,
    final y_reset=y_min,
    final y_neutral=0)
    if not have_senDpRemWir
    "Local differential pressure control"
  Buildings.Controls.OBC.CDL.Reals.MultiMax maxY(
    nin=nSenDpRem)
    if have_senDpRemWir
    "Maximum control loop output"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dpLocSetMax(final unit="Pa")
    if not have_senDpRemWir "Maximum local differential pressure setpoint"
protected
  parameter Real dpSca(
    final unit="Pa",
    final min=0)=1E4
    "Differential pressure used as a scaling factor for PI control";
equation
  connect(y1_actual, anyOn.u)
  connect(anyOn.y, repEna.u)
  connect(dpLocSet, maxSet.u)
  connect(dpRemSet, ctlDpRem.u_s)
  connect(maxSet.y, ctlDpLoc.u_s)
  connect(dpRem, ctlDpRem.u_m)
  connect(repEna.y, ctlDpRem.uEna)
  connect(dpLoc, ctlDpLoc.u_m)
  connect(anyOn.y, ctlDpLoc.uEna)
  connect(ctlDpRem.y, maxY.u)
  connect(ctlDpLoc.y, y)
  connect(maxY.y, y)
  connect(maxSet.y, dpLocSetMax)
end ControlDifferentialPressure;


<html>
<p>
This package contains control sequences for primary or secondary pumps.
</p>
</html>

within Buildings.Templates.Plants.Controls.Pumps;
package Generic
end Generic;


Local differential pressure reset. For plants where the remote DP sensor(s) is not hardwired to the plant controller, but
a local DP sensor is hardwired to the plant controller,
remote DP is maintained at setpoint by a reverse acting PI loop <i>running in the controller
to which the remote sensor is wired</i>.

within Buildings.Templates.Plants.Controls.Pumps.Generic;
block ResetLocalDifferentialPressure
  "Local differential pressure reset"
  parameter Real dpLocSet_min(
    start=0,
    final unit="Pa",
    final min=0)=5 * 6895
    "Minimum loop differential pressure setpoint local to the plant";
  parameter Real dpLocSet_max(
    start=1E5,
    final unit="Pa",
    final min=0)
    "Maximum loop differential pressure setpoint local to the plant";
  parameter Real k(
    final min=100 * Buildings.Controls.OBC.CDL.Constants.eps)=1
    "Gain of controller"
  parameter Real Ti(
    final unit="s",
    final min=100 * Buildings.Controls.OBC.CDL.Constants.eps)=60
    "Time constant of integrator block"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpRemSet(
    final unit="Pa")
    "Remote loop differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpRem(
    final unit="Pa")
    "Remote loop differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dpLocSet(
    final unit="Pa")
    "Local differential pressure setpoint "
  Buildings.Controls.OBC.CDL.Reals.PID ctlDpRem(
    final k=k,
    final Ti=Ti,
    final r=dpLocSet_max)
    "Remote loop differential pressure controller"
  Buildings.Controls.OBC.CDL.Reals.Line dpLocRes
    "Local loop differential pressure reset"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant zer(
    final k=0)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant one(
    final k=1)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dpLocSetMin(
    final k=dpLocSet_min)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dpLocSetMax(
    final k=dpLocSet_max)
    "Constant"
equation
  connect(dpRem, ctlDpRem.u_m)
  connect(zer.y, dpLocRes.x1)
  connect(dpLocSetMin.y, dpLocRes.f1)
  connect(one.y, dpLocRes.x2)
  connect(dpLocSetMax.y, dpLocRes.f2)
  connect(dpRemSet, ctlDpRem.u_s)
  connect(ctlDpRem.y, dpLocRes.u)
  connect(dpLocRes.y, dpLocSet)
end ResetLocalDifferentialPressure;


Generic staging logic for headered pumps.

within Buildings.Templates.Plants.Controls.Pumps.Generic;
block StagingHeadered "Generic staging logic for headered pumps"
  parameter Boolean is_pri(start=true)
    "Set to true for primary pumps, false for secondary pumps"
  parameter Boolean is_hdr(start=false)
    "Set to true for headered pumps, false for dedicated pumps"
  parameter Boolean is_ctlDp(start=false)
    "Set to true for variable speed pumps using ∆p pump speed control"
  parameter Boolean have_valInlIso(start=false)
    "Set to true if the system as inlet isolation valves"
  parameter Boolean have_valOutIso(start=false)
    "Set to true if the system as outlet isolation valves"
  parameter Integer nEqu
    "Number of equipment"
  parameter Integer nPum
    "Number of pumps"
  parameter Integer nSenDp(final min=1, start=1)
    "Number of hardwired ∆p sensors"
  parameter Real V_flow_nominal(
    min=1E-6,
    start=1E-6,
    unit="m3/s")
    "Design flow rate"
  parameter Real dtRun(
    min=0,
    start=600,
    unit="s")=600
    "Runtime before triggering stage change command based on efficiency condition"
  parameter Real dtRunFaiSaf(
    final min=0,
    final unit="s",
    start=300)=300
    "Runtime before triggering stage change command based on failsafe condition"
  parameter Real dtRunFaiSafLowY(
    min=0,
    start=600,
    unit="s")=dtRun
    "Runtime before triggering stage change command based on low pump speed failsafe condition"
  parameter Real dVOffUp(
    max=1,
    min=0,
    start=0.03,
    unit="1")=0.03
    "Stage up flow point offset"
  parameter Real dVOffDow(
    max=1,
    min=0,
    start=0.03,
    unit="1")=dVOffUp
    "Stage down flow point offset"
  parameter Real dpOff(
    final min=0,
    final unit="Pa",
    start=1E4)=1E4
    "Stage change ∆p point offset (>0)"
  parameter Real yUp(
    final min=0,
    final unit="1",
    start=0.99)=0.99
    "Stage up pump speed point"
  parameter Real yDow(
    final min=0,
    final unit="1",
    start=0.4)=0.4
    "Stage down pump speed point"
  final parameter Real staPum[nPum, nPum](
    each unit="1",
    each min=0,
    each max=1)={fill(i / nPum, nPum) for i in 1:nPum}
    "Pump staging matrix"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Pum[nEqu]
    if is_pri and (not is_hdr or is_hdr and not is_ctlDp)
    "Pump command from equipment enable logic"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Pum_actual[nPum]
    "Pump status – Hardware point"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y1[nPum]
    "Pump command – Hardware point"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y1_actual[nEqu]
    "Pump status to equipment enable logic"
  Utilities.StageIndex nPumHdrDp(
    final have_inpAva=false,
    final nSta=nPum)
    if is_hdr and is_ctlDp
    "Compute number of pumps to be staged on – Headered pumps using ∆p control"
  Generic.StagingHeaderedDeltaP staHdrDp(
    final nSenDp=nSenDp,
    final V_flow_nominal=V_flow_nominal,
    final dVOffDow=dVOffDow,
    final dVOffUp=dVOffUp,
    final dpOff=dpOff,
    final dtRun=dtRun,
    final dtRunFaiSaf=dtRunFaiSaf,
    final dtRunFaiSafLowY=dtRunFaiSafLowY,
    final nPum=nPum,
    final yDow=yDow,
    final yUp=yUp)
    if is_hdr and is_ctlDp
    "Stage headered variable speed pumps using ∆p control"
  StagingRotation.SortRuntime sorRunTimHdr(
    nin=nPum)
    if is_hdr
    "Sort by increasing staging runtime"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToInteger booToInt[nEqu](
    each final integerTrue=1,
    each final integerFalse=0)
    if is_pri and is_hdr and not is_ctlDp
    "Convert to integer"
  Buildings.Controls.OBC.CDL.Integers.MultiSum nPumHdrPriNotDp0(
    nin=nEqu)
    if is_pri and is_hdr and not is_ctlDp
    "Compute number of pumps to be staged on – Headered primary pumps not using ∆p control"
  Buildings.Controls.OBC.CDL.Logical.Sources.Constant u1Ava[nPum](
    each final k=true)
    if is_hdr
    "Pump available signal – Block does not handle faulted equipment yet"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput V_flow(
    final unit="m3/s")
    if is_hdr and is_ctlDp
    "Flow rate"
  Buildings.Controls.OBC.CDL.Routing.BooleanExtractSignal sigPumPriDed(final nin
      =nEqu, final nout=nPum)
    if is_pri and not is_hdr
    "Extract dedicated primary pump command signal assuming nEqu=nPum"
  Buildings.Controls.OBC.CDL.Routing.BooleanExtractSignal y1Ded_actual(
    nin=nPum,
    nout=nEqu)
    if not is_hdr
    "Extract dedicated pump status assuming nEqu=nPum"
  Buildings.Controls.OBC.CDL.Routing.BooleanExtractor y1LeaHdr_actual(
    final nin=nPum)
    if is_hdr
    "Lead headered pump status"
  Buildings.Controls.OBC.CDL.Routing.BooleanScalarReplicator booScaRep(
    final nout=nEqu)
    if is_hdr
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValInlIso[nEqu]
    if is_pri and is_hdr and have_valInlIso
    "Equipment inlet isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValOutIso[nEqu]
    if is_pri and is_hdr and have_valOutIso
    "Equipment outlet isolation valve command"
  Primary.EnableLeadHeadered enaLeaHdrPri(
    final typCon=Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel,
    final typValIso=Buildings.Templates.Plants.Controls.Types.Actuator.TwoPosition,
    final nValIso=2 * nEqu)
    if is_pri and is_hdr
    "Enable/disable lead primary headered pump"
  Utilities.PlaceholderLogical phValInlIso[nEqu](each final have_inp=
        have_valInlIso, each final have_inpPh=true) if is_pri and is_hdr
    "Placeholder value if signal is not available"
  Utilities.PlaceholderLogical phValOutIso[nEqu](each final have_inp=
        have_valOutIso, each final have_inpPh=true) if is_pri and is_hdr
    "Placeholder value if signal is not available"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Pla
    if not is_pri and is_hdr
    "Plant enable signal"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToInteger booToInt1(
    final integerTrue=1,
    final integerFalse=0)
    if is_pri and is_hdr and not is_ctlDp
    "Convert lead pump enable signal to integer"
  Buildings.Controls.OBC.CDL.Integers.Multiply nPumHdrPriNotDp
    if is_pri and is_hdr and not is_ctlDp
    "Reset number of enabled pumps to zero if lead pump disabled"
  StagingRotation.EquipmentEnable enaHdr(
    final staEqu=staPum)
    if is_hdr
    "Enable headered pumps"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput y(final unit="1")
    if is_hdr and is_ctlDp
    "Pump speed command"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dp[nSenDp](each final unit=
        "Pa") if is_hdr and is_ctlDp "Loop differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpSet[nSenDp](each final unit
      ="Pa") if is_hdr and is_ctlDp "Loop differential pressure setpoint"
equation
  connect(u1Pum_actual, staHdrDp.u1_actual)
  connect(staHdrDp.y1Up, nPumHdrDp.u1Up)
  connect(staHdrDp.y1Dow, nPumHdrDp.u1Dow)
  connect(u1Pum, booToInt.u)
  connect(booToInt.y, nPumHdrPriNotDp0.u)
  connect(u1Ava.y, sorRunTimHdr.u1Ava)
  connect(V_flow, staHdrDp.V_flow)
  connect(u1Pum, sigPumPriDed.u)
  connect(u1Pum_actual, y1Ded_actual.u)
  connect(y1Ded_actual.y, y1_actual)
  connect(u1Pum_actual, y1LeaHdr_actual.u)
  connect(y1LeaHdr_actual.y, booScaRep.u)
  connect(booScaRep.y, y1_actual)
  connect(u1ValInlIso, phValInlIso.u)
  connect(enaLeaHdrPri.y1, nPumHdrDp.u1Lea)
  connect(phValInlIso.y, enaLeaHdrPri.u1ValIso[1:nEqu])
  connect(phValOutIso.y, enaLeaHdrPri.u1ValIso[nEqu + 1:2*nEqu])
  connect(u1ValOutIso, phValOutIso.u)
  connect(u1ValOutIso, phValInlIso.uPh)
  connect(u1ValInlIso, phValOutIso.uPh)
  connect(u1Pla, nPumHdrDp.u1Lea)
  connect(enaLeaHdrPri.y1, booToInt1.u)
  connect(nPumHdrPriNotDp0.y, nPumHdrPriNotDp.u1)
  connect(booToInt1.y, nPumHdrPriNotDp.u2)
  connect(sorRunTimHdr.yIdx[1], y1LeaHdr_actual.index)
  connect(u1Pum_actual, sorRunTimHdr.u1Run)
  connect(enaHdr.y1, y1)
  connect(nPumHdrDp.y, enaHdr.uSta)
  connect(nPumHdrPriNotDp.y, enaHdr.uSta)
  connect(u1Ava.y, enaHdr.u1Ava)
  connect(sorRunTimHdr.yIdx, enaHdr.uIdxAltSor)
  connect(sigPumPriDed.y, y1)
  connect(dpSet, staHdrDp.dpSet)
  connect(dp, staHdrDp.dp)
  connect(y, staHdrDp.y)
end StagingHeadered;


Staging logic for headered variable speed pumps using ∆p pump speed control. Pumps are staged as a function of the ratio ratV_flow
of current volume flow rate V_flow to design volume
flow rate V_flow_nominal,
the number of operating pumps nPum_actual
and the number of pumps that operate at design conditions
nPum.
Pumps are assumed to be equally sized.

FR = V_flow / V_flow_nominal

within Buildings.Templates.Plants.Controls.Pumps.Generic;
block StagingHeaderedDeltaP
  "Staging logic for headered variable speed pumps using ∆p pump speed control"
  parameter Integer nPum(final min=1)
    "Number of pumps that operate at design conditions"
  parameter Integer nSenDp(final min=1)
    "Number of hardwired ∆p sensors"
  parameter Real V_flow_nominal(min=1E-6, unit="m3/s")
    "Design flow rate";
  parameter Real dtRun(
    min=0,
    unit="s")=10*60
    "Runtime before triggering stage change command based on efficiency condition";
  parameter Real dtRunFaiSaf(
    min=0,
    unit="s")=5*60
    "Runtime before triggering stage change command based on failsafe condition";
  parameter Real dtRunFaiSafLowY(
    min=0,
    unit="s")=dtRun
    "Runtime before triggering stage change command based on low pump speed failsafe condition";
  parameter Real dVOffUp(
    max=1,
    min=0,
    unit="1")=0.03
    "Stage up flow point offset (>0)";
  parameter Real dVOffDow(
    max=1,
    min=0,
    unit="1")=dVOffUp
    "Stage down flow point offset (>0)";
  parameter Real dpOff(
    min=0,
    unit="Pa")=1E4
    "Stage change ∆p point offset (>0)";
  parameter Real yUp(
    min=0,
    unit="1")=0.99
    "Stage up pump speed point";
  parameter Real yDow(
    min=0,
    unit="1")=0.4
    "Stage down pump speed point";
  Buildings.Controls.OBC.CDL.Interfaces.RealInput V_flow(
    final unit="m3/s") "Flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y1Up
    "Stage up command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y1Dow
    "Stage down command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1_actual[nPum]
    "Pump status"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter norV(
    final k=1 / V_flow_nominal)
    "Normalize to design value"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter norN(
    final k=1 / nPum)
    "Normalize to design value"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea[nPum]
    "Convert to real value"
  Buildings.Controls.OBC.CDL.Reals.Greater higV
    "Compare to stage up flow point"
  Buildings.Controls.OBC.CDL.Reals.Less lowV "Compare to stage down flow point"
  Buildings.Controls.OBC.CDL.Reals.AddParameter poiDow(
    p=- 1 / nPum - dVOffDow)
    "Calculate stage down flow point"
  Buildings.Controls.OBC.CDL.Reals.AddParameter poiUp(
    p=- dVOffUp)
    "Calculate stage up flow point"
  Buildings.Templates.Plants.Controls.Utilities.TimerWithReset timHigV(final t=
        dtRun)
    "Return true if stage up condition is true for specified duration"
  Buildings.Templates.Plants.Controls.Utilities.TimerWithReset timLowV(final t=
        dtRun)
    "Return true if stage down condition is true for specified duration"
  Buildings.Controls.OBC.CDL.Logical.Change cha[nPum]
    "Return true when pump status changes"
  Buildings.Controls.OBC.CDL.Reals.MultiSum nOpe(
    nin=nPum)
    "Return number of operating pumps"
  Buildings.Controls.OBC.CDL.Logical.MultiOr anyCha(
    nin=nPum)
    "Return true when any pump status changes"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput y(final unit="1")
    "Pump speed command"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dp[nSenDp](each final unit="Pa")
    "Loop differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpSet[nSenDp](each final unit
      ="Pa") "Loop differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Reals.GreaterThreshold higY(final t=yUp)
    "True if pump speed command exceeds high limit"
  Utilities.TimerWithReset timHigY(final t=dtRunFaiSaf)
                                                     "Timer"
  Buildings.Controls.OBC.CDL.Reals.Subtract delDpSet[nSenDp]
    "Difference between dp measurement and setpoint"
  Buildings.Controls.OBC.CDL.Reals.LessThreshold lowDp[nSenDp](each final t=-
        dpOff)
    "True if dp < setpoint - dpOff"
  Utilities.TimerWithReset timLowDp[nSenDp](each final t=dtRunFaiSaf)
                                                                 "Timer"
  Buildings.Controls.OBC.CDL.Logical.And higYAndLowDp
    "Return true when pump status changes"
  Buildings.Controls.OBC.CDL.Logical.Or effOrFailSafUp
    "True if efficiency OR failsafe condition met"
  Buildings.Controls.OBC.CDL.Reals.LessThreshold lowY(final t=yDow)
    "True if pump speed command is less than low limit"
  Utilities.TimerWithReset timLowY(final t=dtRunFaiSafLowY)
                                                     "Timer"
  Utilities.TimerWithReset timHigDp[nSenDp](each final t=dtRunFaiSaf)
                                                                 "Timer"
  Buildings.Controls.OBC.CDL.Reals.GreaterThreshold higDp[nSenDp](each final t=
        -dpOff)
               "True if dp > setpoint - dpOff"
  Buildings.Controls.OBC.CDL.Logical.Latch lckEff
    "Lock efficiency condition true signal until failsafe condition true"
  Buildings.Controls.OBC.CDL.Logical.Latch lckFaiSaf
    "Lock failsafe condition true signal until efficiency condition true"
  Buildings.Controls.OBC.CDL.Logical.Pre preEff
    "True if lag pump staged on based on efficiency condition"
  Buildings.Controls.OBC.CDL.Logical.Pre preFaiSaf
    "True if lag pump staged on based on failsafe condition"
  Buildings.Controls.OBC.CDL.Logical.And staDowAndPreEff
    "Stage down efficiency condition met and lag pump staged on based on efficiency condition"
  Buildings.Controls.OBC.CDL.Logical.MultiAnd staDowAndPreFaiSaf(nin=3)
    "Stage down failsafe condition met and lag pump staged on based on failsafe condition"
  Buildings.Controls.OBC.CDL.Logical.Or effOrFailSafDow
    "True if efficiency OR failsafe condition met"
  Buildings.Controls.OBC.CDL.Logical.Edge edgUp
    "Trigger stage up command when the conditions are met"
  Buildings.Controls.OBC.CDL.Logical.Edge edgDow
    "Trigger stage down command when the conditions are met"
  Buildings.Controls.OBC.CDL.Logical.MultiAnd allLowDp(nin=nSenDp)
    "True if condition met for all sensors"
  Buildings.Controls.OBC.CDL.Logical.MultiAnd allHigDp(nin=nSenDp)
    "True if condition met for all sensors"
  Buildings.Controls.OBC.CDL.Routing.BooleanScalarReplicator booScaRep(final
      nout=nSenDp) "Replicate signal"
equation
  connect(norN.y, poiDow.u)
  connect(norN.y, poiUp.u)
  connect(norV.y, higV.u1)
  connect(poiUp.y, higV.u2)
  connect(poiDow.y, lowV.u2)
  connect(norV.y, lowV.u1)
  connect(lowV.y, timLowV.u)
  connect(V_flow, norV.u)
  connect(higV.y, timHigV.u)
  connect(booToRea.y, nOpe.u)
  connect(u1_actual, booToRea.u)
  connect(u1_actual, cha.u)
  connect(nOpe.y, norN.u)
  connect(cha.y, anyCha.u)
  connect(anyCha.y, timHigV.reset)
  connect(anyCha.y, timLowV.reset)
  connect(higY.y, timHigY.u)
  connect(y, higY.u)
  connect(anyCha.y, timHigY.reset)
  connect(dp, delDpSet.u1)
  connect(dpSet, delDpSet.u2)
  connect(delDpSet.y, lowDp.u)
  connect(lowDp.y, timLowDp.u)
  connect(timHigY.passed, higYAndLowDp.u1)
  connect(timHigV.passed, effOrFailSafUp.u1)
  connect(higYAndLowDp.y, effOrFailSafUp.u2)
  connect(y, lowY.u)
  connect(lowY.y, timLowY.u)
  connect(anyCha.y, timLowY.reset)
  connect(higDp.y, timHigDp.u)
  connect(delDpSet.y, higDp.u)
  connect(higYAndLowDp.y, lckEff.clr)
  connect(timHigV.passed, lckEff.u)
  connect(higYAndLowDp.y, lckFaiSaf.u)
  connect(timHigV.passed, lckFaiSaf.clr)
  connect(lckFaiSaf.y, preFaiSaf.u)
  connect(lckEff.y, preEff.u)
  connect(preEff.y, staDowAndPreEff.u2)
  connect(timLowV.passed, staDowAndPreEff.u1)
  connect(timLowY.passed, staDowAndPreFaiSaf.u[1])
  connect(preFaiSaf.y, staDowAndPreFaiSaf.u[2])
  connect(staDowAndPreEff.y, effOrFailSafDow.u1)
  connect(staDowAndPreFaiSaf.y, effOrFailSafDow.u2)
  connect(effOrFailSafUp.y, edgUp.u)
  connect(edgUp.y, y1Up)
  connect(effOrFailSafDow.y, edgDow.u)
  connect(edgDow.y, y1Dow)
  connect(timLowDp.passed, allLowDp.u)
  connect(timHigDp.passed, allHigDp.u)
  connect(allLowDp.y, higYAndLowDp.u2)
  connect(allHigDp.y, staDowAndPreFaiSaf.u[3])
  connect(anyCha.y, booScaRep.u)
  connect(booScaRep.y, timLowDp.reset)
  connect(booScaRep.y, timHigDp.reset)
end StagingHeaderedDeltaP;


Validation model for the differential pressure control of variable speed pumps. This model validates

Buildings.Templates.Plants.Controls.Pumps.Generic.ControlDifferentialPressure
in a configuration with two pumps and two remote DP sensors that either are
hardwired to the controller (component ctlDpRem)
or are not hardwired to the controller, which uses a local DP sensor instead
(component ctlDpLoc).

within Buildings.Templates.Plants.Controls.Pumps.Generic.Validation;
model ControlDifferentialPressure
  "Validation model for the differential pressure control of variable speed pumps"
  parameter Integer nPum=4
    "Number of primary pumps that operate at design conditions";
  parameter Real VPri_flow_nominal=0.1
    "Design primary flow rate";
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable ratDp(
    table=[
      0, 0.1, 0.5;
      1, 1, 0.5;
      1.5, 1, 0.2;
      2, 0.1, 0.1],
    timeScale=3600)
    "Differential pressure ratio to design value"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable y1Pum(
    table=[
      0, 0, 0;
      2000, 1, 0;
      6000, 0, 1;
      8000, 0, 0],
    period=8400)
    "Pump status"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter dpRemSet[2](
    k={3E4, 2E4})
    "Differential pressure setpoint"
  Buildings.Templates.Plants.Controls.Pumps.Generic.ControlDifferentialPressure ctlDpRem(
    have_senDpRemWir=true,
    nPum=2,
    nSenDpRem=2)
    "Differential pressure control with remote sensors hardwired to the plant controller"
  Buildings.Controls.OBC.CDL.Reals.MultiMax mulMax(
    nin=2)
    "Maximum value"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter dpLoc(
    final k=5)
    "Differential pressure local to the plant"
  Buildings.Templates.Plants.Controls.Pumps.Generic.ControlDifferentialPressure ctlDpLoc(
    have_senDpRemWir=false,
    nPum=2,
    nSenDpRem=2)
    "Differential pressure control without remote sensors hardwired to the plant controller"
  Buildings.Controls.OBC.CDL.Reals.Sources.Sin sin[2](
    amplitude=0.1 * dpRemSet.k,
    freqHz={2 / 8000, 4 / 8000},
    each phase=3.1415926535898)
    "Source signal used to generate measurement values"
  Buildings.Controls.OBC.CDL.Reals.Add dpRem[2]
    "Differential pressure at remote location"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter dpLocSet[2](
    each final k=5)
    "Local differential pressure setpoint"
equation
  connect(y1Pum.y, ctlDpRem.y1_actual)
  connect(mulMax.y, dpLoc.u)
  connect(y1Pum.y, ctlDpLoc.y1_actual)
  connect(dpLoc.y, ctlDpLoc.dpLoc)
  connect(ratDp.y, dpRemSet.u)
  connect(sin.y, dpRem.u2)
  connect(dpRemSet.y, dpRem.u1)
  connect(dpRemSet.y, ctlDpRem.dpRemSet)
  connect(dpRem.y, ctlDpRem.dpRem)
  connect(dpRem.y, mulMax.u)
  connect(dpRemSet.y, dpLocSet.u)
  connect(dpLocSet.y, ctlDpLoc.dpLocSet)
end ControlDifferentialPressure;


Collection of validation models

within Buildings.Templates.Plants.Controls.Pumps.Generic;
package Validation "Collection of validation models"
end Validation;


Validation model for the local differential pressure reset. This model validates

Buildings.Templates.Plants.Controls.Pumps.Generic.ResetLocalDifferentialPressure.

within Buildings.Templates.Plants.Controls.Pumps.Generic.Validation;
model ResetLocalDifferentialPressure
  "Validation model for the local differential pressure reset"
  parameter Integer nPum=4
    "Number of primary pumps that operate at design conditions";
  parameter Real VPri_flow_nominal=0.1
    "Design primary flow rate";
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable ratDp(
    table=[
      0, 0.1;
      1, 1;
      1.5, 1;
      2, 0.1],
    timeScale=3600)
    "Differential pressure ratio to design value"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter dpRemSet(
    k=3E4)
    "Differential pressure setpoint"
  Buildings.Templates.Plants.Controls.Pumps.Generic.ResetLocalDifferentialPressure resDpLoc(
    dpLocSet_max=1E5,
    Ti=10)
    "Local differential pressure reset"
  Buildings.Controls.OBC.CDL.Reals.Sources.Sin sin(
    amplitude=0.1 * dpRemSet.k,
    freqHz=4 / 8000,
    phase=3.1415926535898)
    "Source signal used to generate measurement values"
  Buildings.Controls.OBC.CDL.Reals.Add dpRem
    "Differential pressure at remote location"
equation
  connect(sin.y, dpRem.u2)
  connect(dpRemSet.y, resDpLoc.dpRemSet)
  connect(dpRem.y, resDpLoc.dpRem)
  connect(dpRemSet.y, dpRem.u1)
  connect(ratDp.y[1], dpRemSet.u)
end ResetLocalDifferentialPressure;


Validation model for staging of headered pumps. This model validates

Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeadered
with three plant equipment and three pumps and for the following configurations.

within Buildings.Templates.Plants.Controls.Pumps.Generic.Validation;
model StagingHeadered
  "Validation model for staging of headered pumps"
  parameter Integer nEqu=3
    "Number of plant equipment";
  parameter Integer nPum=nEqu
    "Number of pumps that operate at design conditions";
  parameter Real V_flow_nominal=0.1
    "Design flow rate";
  Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeadered staPumPriDp(
    is_pri=true,
    is_hdr=true,
    is_ctlDp=true,
    have_valInlIso=true,
    have_valOutIso=true,
    final nEqu=nEqu,
    final nPum=nPum,
    nSenDp=1,
    final V_flow_nominal=V_flow_nominal)
    "Pump staging – Headered primary pumps with ∆p control"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable ratFlo(table=[0,0; 1,1; 1.5,
        1; 2,0], timeScale=3600) "Flow ratio to design value"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter VPri_flow(final k=
        V_flow_nominal)
    "Flow rate"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1(
    table=[0,0,0,0; 0.1,1,0,0; 0.5,1,1,0; 1,1,1,1; 1.5,1,1,1; 2,0,1,1; 2.5,0,0,1;
        3,0,0,0],
    timeScale=3600,
    period=10800)
    "Command signal – Plant, equipment or isolation valve depending on tested configuration"
  Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeadered staPumSecDp(
    is_pri=false,
    is_hdr=true,
    is_ctlDp=true,
    have_valInlIso=true,
    have_valOutIso=true,
    final nEqu=nEqu,
    final nPum=nPum,
    nSenDp=1,
    final V_flow_nominal=V_flow_nominal)
    "Pump staging – Headered secondary pumps with ∆p control"
  Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeadered staPumPriNoDp(
    is_pri=true,
    is_hdr=true,
    is_ctlDp=false,
    have_valInlIso=true,
    have_valOutIso=false,
    final nEqu=nEqu,
    final nPum=nPum,
    final V_flow_nominal=V_flow_nominal)
    "Pump staging – Headered primary pumps without ∆p control"
  Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeadered staPumPriDed(
    is_pri=true,
    is_hdr=false,
    is_ctlDp=false,
    have_valInlIso=true,
    have_valOutIso=false,
    final nEqu=nEqu,
    final nPum=nPum,
    final V_flow_nominal=V_flow_nominal)
    "Pump staging – Dedicated primary pumps"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dpSet(k=Buildings.Templates.Data.Defaults.dpChiWatLocSet_max)
    "Loop differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant yPum(k=0.6)
    "Pump speed command"
  Components.Controls.StatusEmulator y1Pum_actual[nPum] "Pump Status"
equation
  connect(ratFlo.y[1],VPri_flow. u)
  connect(VPri_flow.y, staPumPriDp.V_flow)
  connect(u1.y, staPumPriDp.u1ValInlIso)
  connect(u1.y, staPumPriDp.u1ValOutIso)
  connect(u1.y[1], staPumSecDp.u1Pla)
  connect(VPri_flow.y, staPumSecDp.V_flow)
  connect(u1.y, staPumPriNoDp.u1ValInlIso)
  connect(u1.y, staPumPriNoDp.u1Pum)
  connect(u1.y, staPumPriDed.u1Pum)
  connect(yPum.y, staPumPriDp.y)
  connect(yPum.y, staPumSecDp.y)
  connect(dpSet.y, staPumPriDp.dp[1])
  connect(dpSet.y, staPumPriDp.dpSet[1])
  connect(dpSet.y, staPumSecDp.dp[1])
  connect(dpSet.y, staPumSecDp.dpSet[1])
  connect(staPumPriDp.y1, y1Pum_actual.y1)
  connect(y1Pum_actual.y1_actual, staPumPriDed.u1Pum_actual)
  connect(y1Pum_actual.y1_actual, staPumPriNoDp.u1Pum_actual)
  connect(y1Pum_actual.y1_actual, staPumPriDp.u1Pum_actual)
  connect(y1Pum_actual.y1_actual, staPumSecDp.u1Pum_actual)
end StagingHeadered;


Validation model for staging of headered variable speed pumps using ∆p pump speed control. This model validates

Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeaderedDeltaP
in a configuration with four pumps.

within Buildings.Templates.Plants.Controls.Pumps.Generic.Validation;
model StagingHeaderedDeltaP
  "Validation model for staging of headered variable speed pumps using ∆p pump speed control"
  parameter Integer nPum=4
    "Number of pumps that operate at design conditions";
  parameter Real V_flow_nominal=0.1
    "Design flow rate";
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable ratFlo(
    table=[
      0, 0;
      1, 1;
      1.5, 1;
      2, 0],
    timeScale=3600) "Flow ratio to design value"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable enaLea(
    table=[
      0, 1;
      8000, 0],
    period=8400)
    "Lead pump enable signal"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter VPri_flow(
    final k=V_flow_nominal)
    "Flow rate"
  Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeaderedDeltaP staPum(
    final nPum=nPum,
    nSenDp=1,
    final V_flow_nominal=V_flow_nominal)
    "Pump staging triggered by efficiency conditions only"
  Utilities.StageIndex idxSta(
    have_inpAva=false,
    final nSta=nPum)
    "Calculate stage index = number of enabled pumps"
  Buildings.Controls.OBC.CDL.Integers.GreaterEqualThreshold y1[nPum](
    final t={i for i in 1:nPum})
    "Pump command"
  Buildings.Controls.OBC.CDL.Routing.IntegerScalarReplicator rep(
    nout=nPum)
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dpSet(k=Buildings.Templates.Data.Defaults.dpChiWatLocSet_max)
    "Loop differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant yPum(k=0.6)
    "Pump speed command"
  Buildings.Templates.Plants.Controls.Pumps.Generic.StagingHeaderedDeltaP staPumFaiSaf(
    final nPum=nPum,
    nSenDp=1,
    final V_flow_nominal=V_flow_nominal,
    dVOffUp=0) "Pump staging triggered by failsafe conditions only"
  Utilities.StageIndex idxSta1(have_inpAva=false, final nSta=nPum)
    "Calculate stage index = number of enabled pumps"
  Buildings.Controls.OBC.CDL.Integers.GreaterEqualThreshold y2[nPum](final t={i
        for i in 1:nPum})
    "Pump command"
  Buildings.Controls.OBC.CDL.Routing.IntegerScalarReplicator rep1(nout=nPum)
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea[nPum]
    "Convert Boolean signal to real value"
  Buildings.Controls.OBC.CDL.Reals.MultiSum mulSum(k=fill(1/nPum*V_flow_nominal*
        0.99, nPum), nin=nPum)
    "Compute nPum_actual / nPum * V_flow_nominal * 0.99"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable dp(
    table=[0,-2*staPumFaiSaf.dpOff; 1,-2*staPumFaiSaf.dpOff; 1.5,0; 2,0],
    offset={dpSet.k},
    timeScale=3600) "Loop differential pressure"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable yPum1(table=[0,1; 1,1; 1.5,
        0.3; 2,0.3], timeScale=3600) "Pump speed command"
  Components.Controls.StatusEmulator y1Pum_actual[nPum] "Pump Status"
  Components.Controls.StatusEmulator y1Pum_actual1[nPum] "Pump Status"
equation
  connect(ratFlo.y[1], VPri_flow.u)
  connect(VPri_flow.y, staPum.V_flow)
  connect(staPum.y1Up, idxSta.u1Up)
  connect(staPum.y1Dow, idxSta.u1Dow)
  connect(rep.y, y1.u)
  connect(enaLea.y[1], idxSta.u1Lea)
  connect(idxSta.y, rep.u)
  connect(dpSet.y, staPum.dpSet[1])
  connect(dpSet.y, staPum.dp[1])
  connect(yPum.y, staPum.y)
  connect(staPumFaiSaf.y1Up, idxSta1.u1Up)
  connect(rep1.y, y2.u)
  connect(enaLea.y[1], idxSta1.u1Lea)
  connect(idxSta1.y, rep1.u)
  connect(dp.y[1], staPumFaiSaf.dp[1])
  connect(dpSet.y, staPumFaiSaf.dpSet[1])
  connect(yPum1.y[1], staPumFaiSaf.y)
  connect(staPumFaiSaf.y1Dow, idxSta1.u1Dow)
  connect(mulSum.y, staPumFaiSaf.V_flow)
  connect(y1.y, y1Pum_actual.y1)
  connect(y1Pum_actual.y1_actual, staPum.u1_actual)
  connect(y2.y, y1Pum_actual1.y1)
  connect(y1Pum_actual1.y1_actual, staPumFaiSaf.u1_actual)
  connect(y1Pum_actual1.y1_actual, booToRea.u)
  connect(booToRea.y, mulSum.u)
end StagingHeaderedDeltaP;


Pump disable for plants with dedicated primary pumps. The pump is disabled when the associated plant equipment is disabled and:

within Buildings.Templates.Plants.Controls.Pumps.Primary;
block DisableDedicated
  "Pump disable for plants with dedicated primary pumps"
  parameter Boolean have_reqFlo=false
    "Set to true if plant equipment provides flow request point via network interface"
  parameter Real dtOff(
    min=0,
    unit="s")=if not have_reqFlo then 3 * 60 else 10 * 60
    "Runtime with lead equipment proven off before disabling";
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ReqFlo
    if have_reqFlo
    "Flow request from equipment"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1
    "Enable signal from system enable logic"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Equ
    "Equipment enable signal"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Equ_actual
    "Equipment status"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y1
    "Lead pump enable signal"
  Buildings.Controls.OBC.CDL.Logical.Latch lat
    "Clear enable signal if disable conditions are met"
  Buildings.Controls.OBC.CDL.Logical.Not dis
    "Return true if lead equipment is disabled"
  Buildings.Controls.OBC.CDL.Logical.Not off
    "Return true if lead equipment is proven off"
  Buildings.Controls.OBC.CDL.Logical.Sources.Constant fal(
    final k=false)
    if not have_reqFlo
    "Placeholder constant"
  Buildings.Controls.OBC.CDL.Logical.Or offOrNotReq
    "Return true if lead equipment proven off OR not requesting flow"
  Buildings.Controls.OBC.CDL.Logical.Timer timOff(
    final t=dtOff)
    "Return true if lead equipment is proven off for specified duration"
  Buildings.Controls.OBC.CDL.Logical.And disAndOffOrNotReq
    "Return true if lead equipment disbaled AND (proven off OR not requesting flow)"
  Buildings.Controls.OBC.CDL.Logical.Edge edg
    "Trigger true signal when disable conditions turn true"
  Buildings.Controls.OBC.CDL.Logical.Not noReq
    if have_reqFlo
    "Return true if no flow request"
  Utilities.Initialization ini(
    final yIni=false)
    "Force false clear signal at initial time"
equation
  connect(lat.y, y1)
  connect(u1, lat.u)
  connect(u1Equ, dis.u)
  connect(u1Equ_actual, off.u)
  connect(fal.y, offOrNotReq.u2)
  connect(off.y, timOff.u)
  connect(timOff.passed, offOrNotReq.u1)
  connect(u1ReqFlo, noReq.u)
  connect(noReq.y, offOrNotReq.u2)
  connect(dis.y, disAndOffOrNotReq.u1)
  connect(offOrNotReq.y, disAndOffOrNotReq.u2)
  connect(disAndOffOrNotReq.y, edg.u)
  connect(edg.y, ini.u)
  connect(ini.y, lat.clr)
end DisableDedicated;


Lead primary pump enable/disable for plants with headered primary pumps.

within Buildings.Templates.Plants.Controls.Pumps.Primary;
block EnableLeadHeadered
  "Lead primary pump enable/disable for plants with headered primary pumps"
  parameter Buildings.Templates.Plants.Controls.Types.EquipmentConnection typCon
    "Type of connection between equipment and primary loop"
  parameter Buildings.Templates.Plants.Controls.Types.Actuator typValIso=
    Buildings.Templates.Plants.Controls.Types.Actuator.TwoPosition
    "Type of isolation valve"
  parameter Integer nValIso(
    final min=1)
    "Number of isolation valves"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1ValIso[nValIso]
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.TwoPosition
    "Isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput uValIso[nValIso]
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.Modulating
    "Isolation valve command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y1
    "Lead pump enable signal"
  Buildings.Controls.OBC.CDL.Logical.MultiOr anyOpePar(
    nin=nValIso)
    if typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel
    "Return true if any valve is commanded open - Parallel piped equipment"
  Buildings.Controls.OBC.CDL.Logical.MultiAnd allCloPar(
    nin=nValIso)
    if typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel
    "Return true if all valves are commanded closed - Parallel piped equipment"
  Buildings.Controls.OBC.CDL.Logical.Not cloParMod[nValIso]
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.Modulating
      and typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel
    "Return true if valve is commanded closed"
  Buildings.Controls.OBC.CDL.Reals.GreaterThreshold opeParMod[nValIso](
    each final t=0)
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.Modulating
      and typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel
    "Return true if valve commanded > 0 % open"
  Buildings.Controls.OBC.CDL.Logical.Latch lat
    "Clear enable signal if disable conditions are met"
  Buildings.Controls.OBC.CDL.Logical.MultiOr anyCloSer(
    nin=nValIso)
    if typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Series
    "Return true if any valve is commanded closed - Series piped equipment"
  Buildings.Controls.OBC.CDL.Logical.MultiAnd allOpeSer(
    nin=nValIso)
    if typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Series
    "Return true if all valves are commanded open - Series piped equipment"
  Buildings.Controls.OBC.CDL.Reals.LessThreshold cloSerMod[nValIso](
    each final t=0.99)
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.Modulating
      and typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Series
    "Return true if valve commanded < 99 % open"
  Buildings.Controls.OBC.CDL.Logical.Not opeSerMod[nValIso]
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.Modulating
      and typCon == Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Series
    "Return true if valve is commanded open"
  Buildings.Controls.OBC.CDL.Logical.Not cloTwo[nValIso]
    if typValIso == Buildings.Templates.Plants.Controls.Types.Actuator.TwoPosition
    "Return true if valve is commanded closed"
equation
  connect(uValIso, opeParMod.u)
  connect(opeParMod.y, cloParMod.u)
  connect(lat.y, y1)
  connect(anyOpePar.y, lat.u)
  connect(allCloPar.y, lat.clr)
  connect(allOpeSer.y, lat.clr)
  connect(anyCloSer.y, lat.u)
  connect(uValIso, cloSerMod.u)
  connect(cloSerMod.y, opeSerMod.u)
  connect(u1ValIso, cloTwo.u)
  connect(cloTwo.y, anyCloSer.u)
  connect(u1ValIso, anyOpePar.u)
  connect(cloTwo.y, allCloPar.u)
  connect(u1ValIso, allOpeSer.u)
  connect(cloParMod.y, allCloPar.u)
  connect(cloSerMod.y, anyCloSer.u)
  connect(opeSerMod.y, allOpeSer.u)
  connect(opeParMod.y, anyOpePar.u)
end EnableLeadHeadered;


Sequences for primary pumps

within Buildings.Templates.Plants.Controls.Pumps;
package Primary "Sequences for primary pumps"
end Primary;


Variable speed primary pumps.

within Buildings.Templates.Plants.Controls.Pumps.Primary;
block VariableSpeed
  "Variable speed primary pumps"
  parameter Boolean have_heaWat
    "Set to true for plants that provide HW"
  parameter Boolean have_chiWat
    "Set to true for plants that provide CHW"
  parameter Boolean have_pumPriCtlDp
    "Set to true for primary variable speed pumps using ∆p pump speed control"
  final parameter Boolean have_pumHeaWatPri=have_heaWat
    "Set to true for plants with primary HW pumps"
  parameter Boolean have_pumChiWatPriDed(
    start=false)
    "Set to true for plants with separate dedicated primary CHW pumps"
  final parameter Boolean have_pumChiWatPri=have_chiWat and (have_pumPriHdr or have_pumChiWatPriDed)
    "Set to true for plants with separate primary CHW pumps"
  parameter Boolean have_pumPriHdr
    "Set to true for headered primary pumps, false for dedicated pumps"
  parameter Integer nEqu(
    start=0)
    "Number of equipment"
  parameter Integer nPumHeaWatPri
    "Number of primary HW pumps"
  parameter Integer nPumChiWatPri(
    start=if have_pumChiWatPri then nEqu else 0)
    "Number of primary CHW pumps"
  parameter Real yPumHeaWatPriSet(
    max=2,
    min=0,
    start=1,
    unit="1")
    "Primary pump speed providing design heat pump flow in heating mode"
  parameter Real yPumChiWatPriSet(
    max=2,
    min=0,
    start=1,
    unit="1")
    "Primary pump speed providing design heat pump flow in cooling mode"
  parameter Boolean have_senDpHeaWatRemWir(
    start=false)
    "Set to true for remote HW differential pressure sensor(s) hardwired to controller"
  parameter Integer nSenDpHeaWatRem(
    start=0)
    "Number of remote HW differential pressure sensors used for pump speed control"
  parameter Real yPumHeaWatPri_min(
    max=1,
    min=0,
    start=0.1,
    unit="1")=0.1
    "Minimum primary HW pump speed"
  parameter Real kCtlDpHeaWat(
    min=100 * Buildings.Controls.OBC.CDL.Constants.eps,
    start=1)=1
    "Gain of controller for HW loop ∆p control"
  parameter Real TiCtlDpHeaWat(
    min=100 * Buildings.Controls.OBC.CDL.Constants.eps,
    start=60,
    unit="s")=60
    "Time constant of integrator block for HW loop ∆p control"
  parameter Boolean have_senDpChiWatRemWir(
    start=false)
    "Set to true for remote CHW differential pressure sensor(s) hardwired to controller"
  parameter Integer nSenDpChiWatRem(
    start=0)
    "Number of remote CHW differential pressure sensors used for pump speed control"
  parameter Real yPumChiWatPri_min(
    max=1,
    min=0,
    start=0.1,
    unit="1")=0.1
    "Minimum primary CHW pump speed"
  parameter Real kCtlDpChiWat(
    min=100 * Buildings.Controls.OBC.CDL.Constants.eps,
    start=1)=1
    "Gain of controller for CHW loop ∆p control"
  parameter Real TiCtlDpChiWat(
    min=100 * Buildings.Controls.OBC.CDL.Constants.eps,
    start=60,
    unit="s")=60
    "Time constant of integrator block for CHW loop ∆p control"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1PumHeaWatPri[nPumHeaWatPri]
    if have_pumHeaWatPri
    "Primary HW pump start command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput yPumHeaWatPriHdr
    if have_pumHeaWatPri and have_pumPriHdr
    "Headered primary HW pump speed command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1PumChiWatPri[nPumChiWatPri]
    if have_pumChiWatPri
    "Primary CHW pump start command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput yPumChiWatPriHdr
    if have_pumChiWatPri and have_pumPriHdr
    "Headered primary CHW pump speed command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput yPumHeaWatPriDed[nPumHeaWatPri]
    if have_pumHeaWatPri and not have_pumPriHdr
    "Dedicated primary HW pump speed command"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput yPumChiWatPriDed[nPumChiWatPri]
    if have_pumChiWatPri and not have_pumPriHdr
    "Dedicated primary CHW pump speed command"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Hea[nEqu]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Heating/cooling mode command"
  Buildings.Controls.OBC.CDL.Reals.Switch setPumChiWatPriDed[nPumChiWatPri]
    if have_pumChiWatPri and not have_pumPriHdr
    "Set prescribed speed when pump is enabled"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant zer(
    final k=0)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Switch setPumHeaWatPriDed[nPumHeaWatPri]
    if have_pumHeaWatPri and not have_pumPriHdr
    "Set prescribed speed when pump is enabled"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep(
    nout=nPumHeaWatPri)
    if have_pumHeaWatPri
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep1(
    nout=nPumChiWatPri)
    if have_pumChiWatPri
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep2(
    nout=nPumChiWatPri)
    if have_pumChiWatPri
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Reals.Switch setPumChiWatPriHdr
    if have_pumChiWatPri and have_pumPriHdr
    "Set prescribed speed when pump is enabled"
  Buildings.Controls.OBC.CDL.Reals.Switch setPumHeaWatPriHdr
    if have_pumHeaWatPri and have_pumPriHdr
    "Set prescribed speed when pump is enabled"
  Buildings.Controls.OBC.CDL.Logical.MultiOr anyPumChiWatPri(
    nin=nPumChiWatPri)
    if have_pumChiWatPri
    "Return true if any pump is enabled"
  Buildings.Controls.OBC.CDL.Logical.MultiOr anyPumHeaWatPri(
    nin=nPumHeaWatPri)
    if have_pumHeaWatPri
    "Return true if any pump is enabled"
  Buildings.Controls.OBC.CDL.Reals.Switch selSpeHea[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri and not have_pumPriHdr
    "Select prescribed pump speed depending on heating/cooling mode – Case with common CHW and HW dedicated pumps"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep3(
    nout=nPumHeaWatPri)
    if have_heaWat
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep4(
    nout=nPumHeaWatPri)
    if have_heaWat and have_chiWat and not have_pumChiWatPri and not have_pumPriHdr
    "Replicate signal"
  Utilities.PlaceholderReal ph[nPumHeaWatPri](
    each final have_inp=have_heaWat and have_chiWat and not have_pumChiWatPri
      and not have_pumPriHdr,
    each final have_inpPh=true)
    if have_pumHeaWatPri and not have_pumPriHdr
    "Always use HW pump speed in case of separate dedicated CHW pumps "
  Generic.ControlDifferentialPressure ctlDpHeaWat(
    final have_senDpRemWir=have_senDpHeaWatRemWir,
    final k=kCtlDpHeaWat,
    final nPum=nPumHeaWatPri,
    final nSenDpRem=nSenDpHeaWatRem,
    final Ti=TiCtlDpHeaWat,
    final y_min=yPumHeaWatPri_min)
    if have_heaWat and have_pumPriCtlDp
    "HW loop ∆p control"
  Utilities.PlaceholderReal phSpePumHeaWatPri(
    final have_inp=have_pumPriCtlDp,
    final have_inpPh=false,
    final u_internal=yPumHeaWatPriSet)
    if have_heaWat
    "Replace with fixed speed"
  Generic.ControlDifferentialPressure ctlDpChiWat(
    final have_senDpRemWir=have_senDpChiWatRemWir,
    final k=kCtlDpChiWat,
    final nPum=if have_pumChiWatPri then nPumChiWatPri else nPumHeaWatPri,
    final nSenDpRem=nSenDpChiWatRem,
    final Ti=TiCtlDpChiWat,
    final y_min=if have_pumChiWatPri then yPumChiWatPri_min else yPumHeaWatPri_min)
    if have_chiWat and have_pumPriCtlDp
    "CHW loop ∆p control"
  Utilities.PlaceholderReal phSpePumChiWatPri(
    final have_inp=have_pumPriCtlDp,
    final have_inpPh=false,
    final u_internal=yPumChiWatPriSet)
    if have_chiWat
    "Replace with fixed speed"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1PumHeaWatPri_actual[nPumHeaWatPri]
    if have_pumHeaWatPri and have_pumPriCtlDp
    "Primary HW pump status"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1PumChiWatPri_actual[nPumChiWatPri]
    if have_pumChiWatPri and have_pumPriCtlDp
    "Primary CHW pump status"
  Buildings.Controls.OBC.CDL.Logical.Not u1Coo[nEqu]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Return true if cooling mode command"
  Buildings.Controls.OBC.CDL.Logical.And u1CooAndOn[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri and have_pumPriCtlDp
    "Return true if cooling mode command and pump proven on"
  Utilities.PlaceholderLogical phPumChiWatPriSta[if have_pumChiWatPri then nPumChiWatPri
    else nPumHeaWatPri](
    each final have_inp=have_pumChiWatPri,
    each final have_inpPh=true)
    if have_chiWat and have_pumPriCtlDp
    "Replace with common dedicated pump signal"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpChiWatLoc(
    final unit="Pa")
    if have_chiWat and have_pumPriCtlDp and not have_senDpChiWatRemWir
    "Local CHW differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpChiWatLocSet[nSenDpChiWatRem](
    each final unit="Pa")
    if have_chiWat and have_pumPriCtlDp and not have_senDpChiWatRemWir
    "Local CHW differential pressure setpoint output from each of the remote loops"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpChiWatRem[nSenDpChiWatRem](
    each final unit="Pa")
    if have_chiWat and have_pumPriCtlDp and have_senDpChiWatRemWir
    "Remote CHW differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpHeaWatLoc(
    final unit="Pa")
    if have_heaWat and have_pumPriCtlDp and not have_senDpHeaWatRemWir
    "Local HW differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpHeaWatLocSet[nSenDpHeaWatRem](
    each final unit="Pa")
    if have_heaWat and have_pumPriCtlDp and not have_senDpHeaWatRemWir
    "Local HW differential pressure setpoint output from each of the remote loops"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpHeaWatRem[nSenDpHeaWatRem](
    each final unit="Pa")
    if have_heaWat and have_pumPriCtlDp and have_senDpHeaWatRemWir
    "Remote HW differential pressure"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpHeaWatRemSet[nSenDpHeaWatRem](
    each final unit="Pa")
    if have_heaWat and have_pumPriCtlDp and have_senDpHeaWatRemWir
    "Remote HW differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpChiWatRemSet[nSenDpChiWatRem](
    each final unit="Pa")
    if have_chiWat and have_pumPriCtlDp and have_senDpChiWatRemWir
    "Remote CHW differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dpHeaWatLocSetMax(
    final unit="Pa")
    if have_heaWat and have_pumPriCtlDp and not have_senDpHeaWatRemWir
    "Maximum HW local differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dpChiWatLocSetMax(
    final unit="Pa")
    if have_chiWat and have_pumPriCtlDp and not have_senDpChiWatRemWir
    "Maximum CHW local differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Logical.Latch latCoo[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Latch signal until pump is re-enabled with equipment commanded to alternative mode"
  Buildings.Controls.OBC.CDL.Logical.Edge edgU1PumHeaWatPri[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Return true exactly when the pump is enabled"
  Buildings.Controls.OBC.CDL.Logical.And edgU1Coo[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Return true if equipment commanded in cooling mode when the pump is enabled"
  Buildings.Controls.OBC.CDL.Logical.And edgU1AndHea[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Return true if equipment commanded in heating mode when the pump is enabled"
  Buildings.Controls.OBC.CDL.Logical.Latch latHea[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri
    "Latch signal until pump is re-enabled with equipment commanded to alternative mode"
  Buildings.Controls.OBC.CDL.Logical.And u1HeaAndOn[nPumHeaWatPri]
    if have_heaWat and have_chiWat and not have_pumChiWatPri and have_pumPriCtlDp
    "Return true if heating mode command and pump proven on"
  Utilities.PlaceholderLogical phPumHeaWatPriSta[nPumHeaWatPri](
    each final have_inp=have_chiWat and have_pumChiWatPri or not have_chiWat,
    each final have_inpPh=true)
    if have_heaWat and have_pumPriCtlDp
    "Replace with common dedicated pump signal"
equation
  connect(u1PumChiWatPri, setPumChiWatPriDed.u2)
  connect(setPumChiWatPriDed.y, yPumChiWatPriDed)
  connect(setPumHeaWatPriDed.y, yPumHeaWatPriDed)
  connect(zer.y, rep.u)
  connect(zer.y, rep2.u)
  connect(setPumHeaWatPriHdr.y, yPumHeaWatPriHdr)
  connect(setPumChiWatPriHdr.y, yPumChiWatPriHdr)
  connect(rep2.y, setPumChiWatPriDed.u3)
  connect(rep1.y, setPumChiWatPriDed.u1)
  connect(zer.y, setPumChiWatPriHdr.u3)
  connect(rep.y, setPumHeaWatPriDed.u3)
  connect(zer.y, setPumHeaWatPriHdr.u3)
  connect(u1PumChiWatPri, anyPumChiWatPri.u)
  connect(u1PumHeaWatPri, anyPumHeaWatPri.u)
  connect(anyPumHeaWatPri.y, setPumHeaWatPriHdr.u2)
  connect(anyPumChiWatPri.y, setPumChiWatPriHdr.u2)
  connect(u1PumHeaWatPri, setPumHeaWatPriDed.u2)
  connect(rep3.y, selSpeHea.u1)
  connect(ph.y, setPumHeaWatPriDed.u1)
  connect(selSpeHea.y, ph.u)
  connect(u1Hea, u1Coo.u)
  connect(u1PumChiWatPri_actual, phPumChiWatPriSta.u)
  connect(phPumChiWatPriSta.y, ctlDpChiWat.y1_actual)
  connect(dpHeaWatRemSet, ctlDpHeaWat.dpRemSet)
  connect(dpHeaWatRem, ctlDpHeaWat.dpRem)
  connect(dpHeaWatLocSet, ctlDpHeaWat.dpLocSet)
  connect(dpHeaWatLoc, ctlDpHeaWat.dpLoc)
  connect(dpChiWatRemSet, ctlDpChiWat.dpRemSet)
  connect(dpChiWatRem, ctlDpChiWat.dpRem)
  connect(dpChiWatLocSet, ctlDpChiWat.dpLocSet)
  connect(dpChiWatLoc, ctlDpChiWat.dpLoc)
  connect(ctlDpHeaWat.dpLocSetMax, dpHeaWatLocSetMax)
  connect(ctlDpChiWat.dpLocSetMax, dpChiWatLocSetMax)
  connect(rep3.y, ph.uPh)
  connect(rep4.y, selSpeHea.u3)
  connect(u1PumHeaWatPri, edgU1PumHeaWatPri.u)
  connect(u1Hea, edgU1AndHea.u2)
  connect(u1PumHeaWatPri_actual, u1HeaAndOn.u1)
  connect(edgU1AndHea.y, latHea.u)
  connect(phSpePumChiWatPri.y, setPumChiWatPriHdr.u1)
  connect(ctlDpHeaWat.y, phSpePumHeaWatPri.u)
  connect(ctlDpChiWat.y, phSpePumChiWatPri.u)
  connect(phSpePumHeaWatPri.y, setPumHeaWatPriHdr.u1)
  connect(phSpePumHeaWatPri.y, rep3.u)
  connect(phSpePumChiWatPri.y, rep4.u)
  connect(u1CooAndOn.y, phPumChiWatPriSta.uPh)
  connect(phPumHeaWatPriSta.y, ctlDpHeaWat.y1_actual)
  connect(u1HeaAndOn.y, phPumHeaWatPriSta.uPh)
  connect(u1PumHeaWatPri_actual, phPumHeaWatPriSta.u)
  connect(latCoo.y, u1CooAndOn.u2)
  connect(latHea.y, selSpeHea.u2)
  connect(latHea.y, u1HeaAndOn.u2)
  connect(phSpePumChiWatPri.y, rep1.u)
  connect(u1PumHeaWatPri_actual, u1CooAndOn.u1)
  connect(u1Coo.y, edgU1Coo.u2)
  connect(edgU1PumHeaWatPri.y, edgU1Coo.u1)
  connect(edgU1PumHeaWatPri.y, edgU1AndHea.u1)
  connect(edgU1Coo.y, latCoo.u)
  connect(edgU1AndHea.y, latCoo.clr)
  connect(edgU1Coo.y, latHea.clr)
end VariableSpeed;


Validation model for dedicated primary pump disable logic. This model validates

Buildings.Templates.Plants.Controls.Pumps.Primary.DisableDedicated
in a configuration with a flow request software point or not.

within Buildings.Templates.Plants.Controls.Pumps.Primary.Validation;
model DisableDedicated
  "Validation model for dedicated primary pump disable logic"
  Buildings.Controls.OBC.CDL.Logical.Sources.Pulse y1(
    period=60 * 30)
    "Lead pump enable"
  Buildings.Templates.Plants.Controls.Pumps.Primary.DisableDedicated enaDed
    "Command dedicated pumps – Without flow request"
  Buildings.Templates.Plants.Controls.Pumps.Primary.DisableDedicated enaDedReq(
    have_reqFlo=true)
    "Command dedicated pumps – With flow request"
  Buildings.Controls.OBC.CDL.Logical.Sources.Pulse y1ReqEquLea(
    period=y1EquLea.period + 4 * 60,
    shift=y1EquLea.shift)
    "Lead equipment flow request"
  Buildings.Controls.OBC.CDL.Logical.Sources.Pulse y1EquLea(
    period=y1.period,
    shift=60)
    "Lead equipment enable"
  Buildings.Controls.OBC.CDL.Logical.Sources.Pulse y1EquLea_actual(
    period=y1EquLea.period,
    shift=y1EquLea.shift + 60)
    "Lead equipment status"
equation
  connect(y1.y, enaDed.u1)
  connect(y1.y, enaDedReq.u1)
  connect(y1ReqEquLea.y, enaDedReq.u1ReqFlo)
  connect(y1EquLea.y, enaDed.u1Equ)
  connect(y1EquLea.y, enaDedReq.u1Equ)
  connect(y1EquLea_actual.y, enaDedReq.u1Equ_actual)
  connect(y1EquLea_actual.y, enaDed.u1Equ_actual)
end DisableDedicated;


Validation model for the enabling logic of headered primary pumps. This model validates

Buildings.Templates.Plants.Controls.Pumps.Primary.EnableLeadHeadered
in a configuration with two production units, either parallel piped
or series piped, with either two-position or modulating isolation valves.

within Buildings.Templates.Plants.Controls.Pumps.Primary.Validation;
model EnableLeadHeadered
  "Validation model for the enabling logic of headered primary pumps"
  Buildings.Controls.OBC.CDL.Logical.Sources.Pulse u1ValIso[2](
    period=60 * {20, 30})
    "Isolation valve command"
  Buildings.Templates.Plants.Controls.Pumps.Primary.EnableLeadHeadered enaSerTwo(
    typCon=Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Series,
    typValIso=Buildings.Templates.Plants.Controls.Types.Actuator.TwoPosition,
    nValIso=2)
    "Enable lead pump - Series piped equipment with two-position isolation valves"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea[2]
    "Convert to real"
  Buildings.Controls.OBC.CDL.Reals.LimitSlewRate uValIso[2](
    each raisingSlewRate=1 / 200)
    "Compute valve command"
  Buildings.Templates.Plants.Controls.Pumps.Primary.EnableLeadHeadered enaSerMod(
    typCon=Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Series,
    typValIso=Buildings.Templates.Plants.Controls.Types.Actuator.Modulating,
    nValIso=2)
    "Enable lead pump - Series piped equipment with modulating isolation valves"
  Buildings.Templates.Plants.Controls.Pumps.Primary.EnableLeadHeadered enaParTwo(
    typCon=Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel,
    typValIso=Buildings.Templates.Plants.Controls.Types.Actuator.TwoPosition,
    nValIso=2)
    "Enable lead pump - Parallel piped equipment with two-position isolation valves"
  Buildings.Templates.Plants.Controls.Pumps.Primary.EnableLeadHeadered enaParMod(
    typCon=Buildings.Templates.Plants.Controls.Types.EquipmentConnection.Parallel,
    typValIso=Buildings.Templates.Plants.Controls.Types.Actuator.Modulating,
    nValIso=2)
    "Enable lead pump - Parallel piped equipment with modulating isolation valves"
equation
  connect(u1ValIso.y, booToRea.u)
  connect(booToRea.y, uValIso.u)
  connect(u1ValIso.y, enaSerTwo.u1ValIso)
  connect(uValIso.y, enaSerMod.uValIso)
  connect(u1ValIso.y, enaParTwo.u1ValIso)
  connect(uValIso.y, enaParMod.uValIso)
end EnableLeadHeadered;


Collection of validation models

within Buildings.Templates.Plants.Controls.Pumps.Primary;
package Validation "Collection of validation models"
end Validation;


Validation model for the control logic of variable speed primary pumps. This model validates

Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed
with two plant equipment and two primary pumps and for the following configurations.

within Buildings.Templates.Plants.Controls.Pumps.Primary.Validation;
model VariableSpeed
  "Validation model for the control logic of variable speed primary pumps"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriHdrHea(
    have_heaWat=true,
    have_chiWat=false,
    have_pumPriCtlDp=false,
    have_pumPriHdr=true,
    nPumHeaWatPri=2,
    yPumHeaWatPriSet=0.8)
    "Headered primary pumps without Δp control – Heating-only plant"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1(
    table=[
      0, 0, 0;
      0.1, 1, 0;
      0.5, 1, 1;
      1, 1, 1;
      1.5, 1, 1;
      2, 0, 1;
      2.5, 0, 0;
      3, 0, 0],
    timeScale=800,
    period=3500)
    "Command signal – Plant, equipment or isolation valve depending on tested configuration"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriHdr(
    have_heaWat=true,
    have_chiWat=true,
    have_pumPriCtlDp=false,
    have_pumPriHdr=true,
    nEqu=2,
    nPumHeaWatPri=2,
    nPumChiWatPri=2,
    yPumHeaWatPriSet=0.8,
    yPumChiWatPriSet=0.9)
    "Headered primary pumps without Δp control – Heating and cooling plant"
  Buildings.Controls.OBC.CDL.Logical.Not u1Coo[2]
    "Opposite signal to generate cooling system commands"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriDedCom(
    have_heaWat=true,
    have_chiWat=true,
    have_pumPriCtlDp=false,
    have_pumChiWatPriDed=false,
    have_pumPriHdr=false,
    nEqu=2,
    nPumHeaWatPri=2,
    yPumHeaWatPriSet=0.8,
    yPumChiWatPriSet=0.9)
    "Common dedicated primary pumps without Δp control – Heating and cooling plant"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriDedSep(
    have_heaWat=true,
    have_chiWat=true,
    have_pumPriCtlDp=false,
    have_pumChiWatPriDed=true,
    have_pumPriHdr=false,
    nEqu=2,
    nPumHeaWatPri=2,
    nPumChiWatPri=2,
    yPumHeaWatPriSet=0.8,
    yPumChiWatPriSet=0.9)
    "Separate dedicated primary pumps without Δp control – Heating and cooling plant"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriHdrHeaDp(
    have_heaWat=true,
    have_chiWat=false,
    have_pumPriCtlDp=true,
    have_pumPriHdr=true,
    nPumHeaWatPri=2,
    yPumHeaWatPriSet=0.8,
    have_senDpHeaWatRemWir=false,
    nSenDpHeaWatRem=2)
    "Headered primary pumps with Δp control – Heating-only plant"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable ratDp(
    table=[
      0, 0.1, 0.5;
      1, 1, 0.5;
      1.5, 1, 0.2;
      2, 0.1, 0.1],
    timeScale=3600)
    "Differential pressure ratio to design value"
  Buildings.Controls.OBC.CDL.Reals.MultiplyByParameter dpSet[2](
    k={3E4, 2E4})
    "Differential pressure setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Sin sin[2](
    amplitude=0.1 * dpSet.k,
    freqHz={2 / 8000, 4 / 8000},
    each phase=3.1415926535898)
    "Source signal used to generate measurement values"
  Buildings.Controls.OBC.CDL.Reals.Add dp[2]
    "Differential pressure"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriHdrDp(
    have_heaWat=true,
    have_chiWat=true,
    have_pumPriCtlDp=true,
    have_pumPriHdr=true,
    nEqu=2,
    nPumHeaWatPri=2,
    nPumChiWatPri=2,
    yPumHeaWatPriSet=0.8,
    yPumChiWatPriSet=0.9,
    have_senDpHeaWatRemWir=true,
    nSenDpHeaWatRem=2,
    have_senDpChiWatRemWir=true,
    nSenDpChiWatRem=2)
    "Headered primary pumps with Δp control – Heating and cooling plant"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriDedComDp(
    have_heaWat=true,
    have_chiWat=true,
    have_pumPriCtlDp=true,
    have_pumChiWatPriDed=false,
    have_pumPriHdr=false,
    nEqu=2,
    nPumHeaWatPri=2,
    yPumHeaWatPriSet=0.8,
    yPumChiWatPriSet=0.9,
    have_senDpHeaWatRemWir=false,
    nSenDpHeaWatRem=2,
    have_senDpChiWatRemWir=false,
    nSenDpChiWatRem=2)
    "Common dedicated primary pumps with Δp control – Heating and cooling plant"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable u1Hea(
    table=[0,0,0; 0.1,1,0; 0.5,1,0; 1,0,0; 1.5,0,1; 2,0,1; 2.5,0,0; 3,0,0],
    timeScale=600,
    period=5000)
    "Heating mode command signal"
  Buildings.Templates.Plants.Controls.Pumps.Primary.VariableSpeed ctlPumPriDedSepDp(
    have_heaWat=true,
    have_chiWat=true,
    have_pumPriCtlDp=true,
    have_pumChiWatPriDed=true,
    have_pumPriHdr=false,
    nEqu=2,
    nPumHeaWatPri=2,
    nPumChiWatPri=2,
    yPumHeaWatPriSet=0.8,
    yPumChiWatPriSet=0.9,
    have_senDpHeaWatRemWir=false,
    nSenDpHeaWatRem=2,
    have_senDpChiWatRemWir=false,
    nSenDpChiWatRem=2)
    "Separate dedicated primary pumps with Δp control – Heating and cooling plant"
equation
  connect(u1.y, u1Coo.u)
  connect(u1Coo.y, ctlPumPriHdr.u1PumChiWatPri)
  connect(u1.y, ctlPumPriHdr.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriDedCom.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriDedSep.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriHdrHea.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriHdrHeaDp.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriHdrHeaDp.u1PumHeaWatPri_actual)
  connect(ratDp.y, dpSet.u)
  connect(sin.y, dp.u2)
  connect(dpSet.y, dp.u1)
  connect(dpSet[1:2].y, ctlPumPriHdrHeaDp.dpHeaWatLocSet)
  connect(dp[1].y, ctlPumPriHdrHeaDp.dpHeaWatLoc)
  connect(u1.y, ctlPumPriHdrDp.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriHdrDp.u1PumHeaWatPri_actual)
  connect(u1Coo.y, ctlPumPriHdrDp.u1PumChiWatPri)
  connect(u1Coo.y, ctlPumPriHdrDp.u1PumChiWatPri_actual)
  connect(dpSet.y, ctlPumPriHdrDp.dpHeaWatRemSet)
  connect(dp.y, ctlPumPriHdrDp.dpHeaWatRem)
  connect(dp.y, ctlPumPriHdrDp.dpChiWatRem)
  connect(dpSet.y, ctlPumPriHdrDp.dpChiWatRemSet)
  connect(u1.y, ctlPumPriDedComDp.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriDedComDp.u1PumHeaWatPri_actual)
  connect(dp[1].y, ctlPumPriDedComDp.dpHeaWatLoc)
  connect(dp[2].y, ctlPumPriDedComDp.dpChiWatLoc)
  connect(dpSet.y, ctlPumPriDedComDp.dpHeaWatLocSet)
  connect(dpSet.y, ctlPumPriDedComDp.dpChiWatLocSet)
  connect(u1Hea.y, ctlPumPriDedComDp.u1Hea)
  connect(dpSet.y, ctlPumPriDedSepDp.dpHeaWatLocSet)
  connect(dpSet.y, ctlPumPriDedSepDp.dpChiWatLocSet)
  connect(dp[2].y, ctlPumPriDedSepDp.dpChiWatLoc)
  connect(dp[1].y, ctlPumPriDedSepDp.dpHeaWatLoc)
  connect(u1.y, ctlPumPriDedSepDp.u1PumHeaWatPri)
  connect(u1.y, ctlPumPriDedSepDp.u1PumHeaWatPri_actual)
  connect(u1Coo.y, ctlPumPriDedSepDp.u1PumChiWatPri)
  connect(u1Coo.y, ctlPumPriDedSepDp.u1PumChiWatPri_actual)
  connect(u1Hea.y, ctlPumPriDedCom.u1Hea)
  connect(u1Coo.y, ctlPumPriDedSep.u1PumChiWatPri)
end VariableSpeed;


Plant reset logic

within Buildings.Templates.Plants.Controls;
package Setpoints "Plant reset logic"
end Setpoints;


Plant reset logic. This logic is used in primary-only and primary-secondary systems
serving differential pressure controlled pumps.

within Buildings.Templates.Plants.Controls.Setpoints;
block PlantReset
  "Plant reset logic"
  parameter Integer nSenDpRem(final min=1)
    "Number of remote loop differential pressure sensors used for pump speed control"
  parameter Real dpSet_max[nSenDpRem](
    each final min=5*6894,
    each unit="Pa")
    "Maximum differential pressure setpoint";
  parameter Real dpSet_min(
    final min=0,
    final unit="Pa")=5*6894
    "Minimum value to which the differential pressure can be reset";
  parameter Real TSup_nominal(
    final min=273.15,
    final unit="K",
    displayUnit="degC")
    "Design supply temperature";
  parameter Real TSupSetLim(
    final min=273.15,
    final unit="K",
    displayUnit="degC")
    "Limit value to which the supply temperature can be reset";
  parameter Real dtHol(
    final min=0,
    final unit="s")=900
    "Minimum hold time during stage change"
  parameter Real resDp_max(
    final max=1,
    final min=0,
    final unit="1")=0.5
    "Upper limit of plant reset interval for differential pressure reset"
  parameter Real resTSup_min(
    final max=1,
    final min=0,
    final unit="1")=resDp_max
    "Lower limit of plant reset interval for supply temperature reset"
  parameter Real res_init(
    final max=1,
    final min=0,
    final unit="1")=1
    "Initial reset value"
  parameter Real res_min(
    final max=1,
    final min=0,
    final unit="1")=0
    "Minimum reset value"
  parameter Real res_max(
    final max=1,
    final min=0,
    final unit="1")=1
    "Maximum reset value"
  parameter Real dtDel(
    final min=100*1E-15,
    final unit="s")=900
    "Delay time before the reset begins"
  parameter Real dtRes(
    final min=1E-3,
    final unit="s")=300
    "Time step"
  parameter Integer nReqResIgn(min=0)=2
    "Number of ignored requests"
  parameter Real tri(
    final max=0,
    final unit="1")=-0.02
    "Trim amount"
  parameter Real rsp(
    final min=0,
    final unit="1")=0.03
    "Respond amount (must have opposite sign of trim amount)"
  parameter Real rsp_max(
    final min=0,
    final unit="1")=0.07
    "Maximum response per time interval (must have same sign as respond amount)"
  Buildings.Controls.OBC.CDL.Interfaces.IntegerInput nReqRes
    "Sum of reset requests of all loads served"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1Ena
    "Plant enable"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput u1StaPro
    "Staging process in progress"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput TSupSet(
    final unit="K",
    displayUnit="degC")
    "Supply temperature setpoint"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dpSet[nSenDpRem](
    each final min=0,
    each final unit="Pa")
    "Differential pressure setpoint"
  Buildings.Controls.OBC.ASHRAE.G36.Generic.TrimAndRespond triRes(
    final have_hol=true,
    final delTim=dtDel,
    final iniSet=res_init,
    final maxRes=rsp_max,
    final maxSet=res_max,
    final minSet=res_min,
    final numIgnReq=nReqResIgn,
    final resAmo=rsp,
    final samplePeriod=dtRes,
    final triAmo=tri,
    dtHol=dtHol)
    "Compute plant reset with trim and respond logic "
  Buildings.Controls.OBC.CDL.Reals.Line resTSup
    "Supply temperature reset"
  Buildings.Controls.OBC.CDL.Reals.Line resDp[nSenDpRem]
    "Differential pressure reset"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant zer(
    final k=0)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant one(
    final k=1)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant resDpMax(
    final k=resDp_max)
    "Constant"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep(
    final nout=nSenDpRem)
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep1(
    final nout=nSenDpRem)
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep2(
    final nout=nSenDpRem)
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dpSetMax[nSenDpRem](
    final k=dpSet_max)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant resTSupMin(
    final k=resTSup_min)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSupSetNom(
    final k=TSup_nominal)
    "Constant"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant dpSetMin(
    final k=dpSet_min)
    "Constant"
  Buildings.Controls.OBC.CDL.Routing.RealScalarReplicator rep3(
    final nout=nSenDpRem)
    "Replicate signal"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSupSetMinMax(final k=
        TSupSetLim) "Constant"
equation
  connect(nReqRes, triRes.numOfReq)
  connect(resTSup.y, TSupSet)
  connect(resDp.y, dpSet)
  connect(one.y, resTSup.x2)
  connect(zer.y, rep.u)
  connect(rep.y, resDp.x1)
  connect(rep1.y, resDp.u)
  connect(rep2.y, resDp.x2)
  connect(dpSetMax.y, resDp.f2)
  connect(TSupSetNom.y, resTSup.f2)
  connect(dpSetMin.y, rep3.u)
  connect(rep3.y, resDp.f1)
  connect(TSupSetMinMax.y, resTSup.f1)
  connect(resTSupMin.y, resTSup.x1)
  connect(resDpMax.y, rep2.u)
  connect(u1Ena, triRes.uDevSta)
  connect(triRes.y, rep1.u)
  connect(u1StaPro, triRes.uHol)
  connect(triRes.y, resTSup.u)
end PlantReset;


Collection of validation models

within Buildings.Templates.Plants.Controls.Setpoints;
package Validation "Collection of validation models"
end Validation;


Plant reset. This model validates

Buildings.Templates.Plants.Controls.Setpoints.PlantReset.

within Buildings.Templates.Plants.Controls.Setpoints.Validation;
model PlantReset
  Buildings.Templates.Plants.Controls.Setpoints.PlantReset res(
    nSenDpRem=2,
    dpSet_max={5E4,8E4},
    TSup_nominal=323.15,
    TSupSetLim=298.15,
    resDp_max=0.75,
    resTSup_min=0.25) "Plant reset"
  Buildings.Controls.OBC.CDL.Conversions.RealToInteger reaToInt
    "Convert real to integer"
  Buildings.Controls.OBC.CDL.Logical.Sources.TimeTable ena(
    table=[
      0, 0;
      2, 1;
      19, 0],
    timeScale=1000,
    final period=20000)
    "Plant enable"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable timTabLin(
    final smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    final table=[
      0, 0;
      150, 0;
      300, 0;
      450, 0;
      600, 0;
      750, 0;
      900, 0;
      1050, 0;
      1200, 4;
      1350, 3;
      1500, 2;
      1650, 1;
      1800, 0],
    timeScale=10)
    "Time table with smoothness method of constant segments"
  Buildings.Controls.OBC.CDL.Logical.Sources.Pulse staPro(
    width=0.1,
    final period=8000,
    shift=2000)
    "Staging process in progress"
equation
  connect(timTabLin.y[1], reaToInt.u)
  connect(reaToInt.y,res.nReqRes)
  connect(staPro.y, res.u1StaPro)
  connect(ena.y[1], res.u1Ena)
end PlantReset;