District cooling plant model

within Buildings.DHC.Plants.Cooling;
model ElectricChillerParallel
  "District cooling plant model"
  extends Buildings.DHC.Plants.BaseClasses.PartialPlant(
    have_eleCoo=true,
    have_pum=true,
    have_fan=true,
    have_weaBus=true,
    typ=Buildings.DHC.Types.DistrictSystemType.Cooling);
  // chiller parameters
  final parameter Integer numChi=2
    "Number of chillers"
  replaceable parameter Buildings.Fluid.Chillers.Data.ElectricEIR.Generic perChi
    "Performance data of chiller"
  parameter Modelica.Units.SI.MassFlowRate mCHW_flow_nominal
    "Nominal chilled water mass flow rate"
  parameter Modelica.Units.SI.Pressure dpCHW_nominal
    "Pressure difference at the chilled water side"
  parameter Modelica.Units.SI.HeatFlowRate QChi_nominal(max=0)
    "Nominal cooling capacity of single chiller (negative means cooling)"
  parameter Modelica.Units.SI.MassFlowRate mMin_flow
    "Minimum mass flow rate of single chiller"
  // cooling tower parameters
  parameter Modelica.Units.SI.MassFlowRate mCW_flow_nominal
    "Nominal condenser water mass flow rate"
  parameter Modelica.Units.SI.Pressure dpCW_nominal
    "Pressure difference at the condenser water side"
  parameter Modelica.Units.SI.Temperature TAirInWB_nominal
    "Nominal air wetbulb temperature"
  parameter Modelica.Units.SI.Temperature TCW_nominal
    "Nominal condenser water temperature at tower inlet"
  parameter Modelica.Units.SI.TemperatureDifference dT_nominal
    "Temperature difference between inlet and outlet of the tower"
  parameter Modelica.Units.SI.TemperatureDifference dTApp
    "Approach temperature"
  parameter Modelica.Units.SI.Temperature TMin
    "Minimum allowed water temperature entering chiller"
  parameter Modelica.Units.SI.Power PFan_nominal "Fan power"
  // pump parameters
  replaceable parameter Buildings.Fluid.Movers.Data.Generic perCHWPum
    constrainedby Buildings.Fluid.Movers.Data.Generic
    "Performance data of chilled water pump"
  replaceable parameter Buildings.Fluid.Movers.Data.Generic perCWPum
    constrainedby Buildings.Fluid.Movers.Data.Generic
    "Performance data of condenser water pump"
  parameter Modelica.Units.SI.Pressure dpCHWPumVal_nominal
    "Nominal pressure drop of chilled water pump valve"
  parameter Modelica.Units.SI.Pressure dpCWPumVal_nominal
    "Nominal pressure drop of condenser water pump valve"
  parameter Modelica.Units.SI.Time tau=1
    "Pump time constant at nominal flow (if energyDynamics <> SteadyState)"
  parameter Boolean use_riseTime=false
    "Set to true to continuously change motor speed"
  parameter Boolean use_strokeTime=use_riseTime
    "Set to true to continuously change valve position"
  parameter Modelica.Units.SI.Time riseTime=30
    "Time needed to change motor speed between zero and full speed"
  parameter Modelica.Units.SI.Time strokeTime=riseTime
    "Time needed to open or close valve"
  parameter Modelica.Blocks.Types.Init init=Modelica.Blocks.Types.Init.InitialOutput
    "Type of initialization for pumps (no init/steady state/initial state/initial output)"
  parameter Real[numChi] yCHWP_start=fill(0,numChi)
    "Initial value of CHW pump signals"
  parameter Real[numChi] yCWP_start=fill(0,numChi)
    "Initial value of CW pump signals"
  parameter Modelica.Units.SI.PressureDifference dpCooTowVal_nominal
    "Nominal pressure difference of the cooling tower valve";
  // control settings
  parameter Modelica.Units.SI.Time tWai "Waiting time"
  // dynamics
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=
    Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"
  parameter Modelica.Fluid.Types.Dynamics massDynamics=energyDynamics
    "Type of mass balance: dynamic (3 initialization options) or steady state, must be steady state if energyDynamics is steady state"
  Modelica.Blocks.Interfaces.BooleanInput on
    "On signal of the plant"
  Modelica.Blocks.Interfaces.RealInput TCHWSupSet(
    final unit="K",
    displayUnit="degC")
    "Set point for chilled water supply temperature"
  Buildings.Applications.BaseClasses.Equipment.ElectricChillerParallel mulChiSys(
    final use_strokeTime=use_strokeTime,
    final per=fill(perChi, numChi),
    final m1_flow_nominal=mCW_flow_nominal,
    final m2_flow_nominal=mCHW_flow_nominal,
    final dp1_nominal=dpCW_nominal/2,
    final dp2_nominal=dpCHW_nominal,
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium) "Chillers connected in parallel"
  Buildings.DHC.Plants.Cooling.Subsystems.CoolingTowersWithBypass cooTowWitByp(
    redeclare final package Medium=Medium,
    final num=numChi,
    final m_flow_nominal=mCW_flow_nominal,
    final use_strokeTime=use_strokeTime,
    final dp_nominal=dpCW_nominal/2,
    final dpValve_nominal = dpCooTowVal_nominal,
    final TAirInWB_nominal=TAirInWB_nominal,
    final TWatIn_nominal=TCW_nominal,
    final dT_nominal=dT_nominal,
    final dTApp=dTApp,
    final PFan_nominal=PFan_nominal,
    final TMin=TMin)
    "Cooling towers with bypass valve"
  Buildings.Applications.BaseClasses.Equipment.FlowMachine_y pumCHW(
    redeclare final package Medium = Medium,
    final per=fill(perCHWPum, numChi),
    yValve_start=fill(1, numChi),
    final tau=tau,
    final use_riseTime=use_riseTime,
    final riseTime=riseTime,
    final use_strokeTime=use_strokeTime,
    final strokeTime=strokeTime,
    final init=init,
    final yPump_start=yCHWP_start,
    final energyDynamics=energyDynamics,
    final m_flow_nominal=mCHW_flow_nominal,
    final dpValve_nominal=dpCHWPumVal_nominal,
    final num=numChi) "Chilled water pumps"
  Buildings.Applications.BaseClasses.Equipment.FlowMachine_m pumCW(
    redeclare final package Medium = Medium,
    final per=fill(perCWPum, numChi),
    final tau=tau,
    final use_riseTime=use_riseTime,
    final riseTime=riseTime,
    final use_strokeTime=use_strokeTime,
    final strokeTime=strokeTime,
    final init=init,
    final yPump_start=yCWP_start,
    final energyDynamics=energyDynamics,
    final m_flow_nominal=mCW_flow_nominal,
    final dpValve_nominal=dpCWPumVal_nominal,
    final num=numChi) "Condenser water pumps"
  Buildings.Fluid.Actuators.Valves.TwoWayEqualPercentage valByp(
    redeclare final package Medium=Medium,
    final allowFlowReversal=false,
    final m_flow_nominal=mCHW_flow_nominal,
    final dpValve_nominal=dpCHWPumVal_nominal,
    final use_strokeTime=true,
    strokeTime=60)
    "Chilled water bypass valve"
  Buildings.Fluid.Sensors.TemperatureTwoPort senTCHWSup(
    redeclare final package Medium=Medium,
    final m_flow_nominal=mCHW_flow_nominal)
    "Chilled water supply temperature"
  Buildings.DHC.Plants.Cooling.Controls.ChillerStage chiStaCon(
    final tWai=tWai,
    final QChi_nominal=QChi_nominal)
    "Chiller staging controller"
  Buildings.Fluid.Sensors.TemperatureTwoPort senTCHWRet(
    redeclare final package Medium=Medium,
    final m_flow_nominal=mCHW_flow_nominal)
    "Chilled water return temperature"
  Buildings.Fluid.Sources.Boundary_pT expTanCW(
    redeclare final package Medium=Medium,
    p=300000,
    nPorts=1)
    "Condenser water expansion tank"
  Buildings.Fluid.Sources.Boundary_pT expTanCHW(
    redeclare final package Medium=Medium,
    p=300000,
    nPorts=1)
    "Chilled water expansion tank"
  Buildings.Fluid.Sensors.MassFlowRate senMasFlo(
    redeclare final package Medium=Medium)
    "Chilled water return mass flow"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal chiOn[numChi]
    "Convert chiller on signal from boolean to real"
  Buildings.Controls.OBC.CDL.Reals.MultiSum totPPum(
    nin=4)
    "Total pump power"
  Buildings.Controls.OBC.CDL.Reals.MultiSum totPFan(
    nin=2)
    "Total fan power"
  Buildings.Controls.OBC.CDL.Reals.MultiSum totPCoo(
    nin=2)
    "Total cooling power"
  Buildings.Fluid.FixedResistances.Junction joiCHWRet(
    redeclare final package Medium=Medium,
    final m_flow_nominal=mCHW_flow_nominal .* {1,-1,1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    dp_nominal={0,0,0})
    "Flow joint for the chilled water return side"
  Buildings.Fluid.FixedResistances.Junction splCHWSup(
    redeclare final package Medium=Medium,
    final m_flow_nominal=mCHW_flow_nominal .* {1,-1,-1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    dp_nominal={0,0,0})
    "Flow splitter for the chilled water supply side"
  Buildings.DHC.Plants.Cooling.Controls.ChilledWaterBypass chiBypCon(
    final numChi=numChi,
    final mMin_flow=mMin_flow,
    k=1,
    Ti=120)
    "Chilled water bypass control"
  Fluid.Sensors.MassFlowRate senMasFloCHW(
    redeclare final package Medium = Medium)
    "Total chilled water pump mass flow rate"
protected
  final parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
    T=Medium.T_default,
    p=Medium.p_default,
    X=Medium.X_default)
    "Medium state at default properties";
  final parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(sta_default)
    "Specific heat capacity of the fluid";

initial equation
  assert(energyDynamics <> Modelica.Fluid.Types.Dynamics.SteadyState or
         massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState,
         "In " + getInstanceName() +
         ": energyDynamics is selected as steady state, and therefore massDynamics must also be steady-state.");

equation
  connect(cooTowWitByp.port_b,pumCW.port_a)
  connect(on,chiStaCon.on)
  connect(pumCHW.port_b,mulChiSys.port_a2)
  connect(pumCW.port_b,mulChiSys.port_a1)
  connect(mulChiSys.port_b1,cooTowWitByp.port_a)
  connect(expTanCW.ports[1],pumCW.port_a)
  connect(senTCHWRet.port_b,senMasFlo.port_a)
  connect(chiStaCon.y,mulChiSys.on)
  connect(chiStaCon.y,chiOn.u)
  connect(chiOn.y,pumCW.u)
  connect(chiStaCon.y,cooTowWitByp.on)
  connect(weaBus.TWetBul,cooTowWitByp.TWetBul)
  connect(port_aSerCoo,senTCHWRet.port_a)
  connect(senTCHWSup.port_b,port_bSerCoo)
  connect(TCHWSupSet,mulChiSys.TSet)
  connect(totPPum.y,PPum)
  connect(pumCW.P,totPPum.u[1:2])
  connect(pumCHW.P,totPPum.u[3:4])
  connect(totPFan.y,PFan)
  connect(cooTowWitByp.PFan,totPFan.u[1:2])
  connect(totPCoo.y,PCoo)
  connect(mulChiSys.P,totPCoo.u[1:2])
  connect(splCHWSup.port_3,senTCHWSup.port_a)
  connect(senTCHWRet.T,chiStaCon.TChiWatRet)
  connect(senTCHWSup.T,chiStaCon.TChiWatSup)
  connect(senMasFlo.m_flow,chiStaCon.mFloChiWat)
  connect(chiStaCon.y,chiBypCon.chiOn)
  connect(senMasFlo.port_b, joiCHWRet.port_3)
  connect(valByp.port_b, joiCHWRet.port_1)
  connect(senMasFloCHW.m_flow, chiBypCon.mFloChi)
  connect(valByp.port_a, splCHWSup.port_2)
  connect(joiCHWRet.port_2, pumCHW.port_a)
  connect(expTanCHW.ports[1], pumCHW.port_a)
  connect(mulChiSys.port_b2, senMasFloCHW.port_a)
  connect(senMasFloCHW.port_b, splCHWSup.port_1)
  connect(chiBypCon.y, valByp.y)
  connect(chiOn.y, pumCHW.u)
end ElectricChillerParallel;


within Buildings.DHC.Plants;
package Cooling "Package of models for central cooling production"
  extends Modelica.Icons.VariantsPackage;
end Cooling;


within Buildings.DHC.Plants.Cooling;
model StoragePlant "Model of a storage plant with a chiller and a CHW tank"

  extends Buildings.Fluid.Interfaces.PartialFourPort(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium);

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium package";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=
     mTan_flow_nominal+mChi_flow_nominal
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate mTan_flow_nominal(min=0)
    "Nominal mass flow rate for CHW tank branch"
  parameter Modelica.Units.SI.MassFlowRate mChi_flow_nominal(min=0)
    "Nominal mass flow rate for CHW chiller branch"
  parameter Modelica.Units.SI.PressureDifference dpPum_nominal
    "Nominal pressure difference for secondary pump sizing"
  parameter Modelica.Units.SI.PressureDifference dpVal_nominal
    "Nominal pressure difference for return valve sizing"
  parameter Modelica.Units.SI.Temperature T_CHWS_nominal(
    final displayUnit="degC")=
     7+273.15 "Nominal temperature of CHW supply"
  parameter Modelica.Units.SI.Temperature T_CHWR_nominal(
    final displayUnit="degC")=
     12+273.15
    "Nominal temperature of CHW return"

  Buildings.Fluid.Movers.Preconfigured.FlowControlled_m_flow pumPri(
    redeclare final package Medium = Medium,
    final addPowerToMedium=false,
    final m_flow_nominal=mChi_flow_nominal,
    final dp_nominal=chi2PreDro.dp_nominal) "Primary CHW pump"
  Buildings.Fluid.FixedResistances.PressureDrop chi2PreDro(
    redeclare final package Medium = Medium,
    final m_flow_nominal=mChi_flow_nominal,
    dp_nominal=0.1*dpPum_nominal) "Pressure drop of the chiller loop"
  Buildings.DHC.Plants.Cooling.BaseClasses.TankBranch tanBra(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final mTan_flow_nominal=mTan_flow_nominal,
    final mChi_flow_nominal=mChi_flow_nominal,
    final T_CHWS_nominal=T_CHWS_nominal,
    final T_CHWR_nominal=T_CHWR_nominal,
    VTan=0.8,
    hTan=3,
    dIns=0.3) "Tank branch, tank can be charged remotely"
  Buildings.DHC.Plants.Cooling.BaseClasses.ReversibleConnection
    revCon(redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final mTan_flow_nominal=mTan_flow_nominal,
    final dpPum_nominal=dpPum_nominal,
    final dpVal_nominal=dpVal_nominal,
    final T_start=T_CHWS_nominal)
    "Reversible connection"
  Buildings.DHC.Plants.Cooling.Controls.FlowControl floCon(
    final mChi_flow_nominal=mChi_flow_nominal,
    final mTan_flow_nominal=mTan_flow_nominal)
    "Control block for storage plant flows"
  Buildings.DHC.Plants.Cooling.Controls.TankStatus tanSta(
    TLow=T_CHWS_nominal + 0.5,
    THig=T_CHWR_nominal - 0.5) "Tank status"
  Modelica.Blocks.Interfaces.BooleanInput chiEnaSta
    "Chiller enable status, true if chiller is enabled"
  Modelica.Blocks.Interfaces.IntegerInput com
    "Command: 1 = charge tank, 2 = no command, 3 = discharge from tank"
  Modelica.Blocks.Interfaces.BooleanInput hasLoa
    "Set to true if the district has a load"
  Modelica.Blocks.Interfaces.RealInput yPum(final unit="1")
    "Normalized speed signal for the secondary pump"
  Buildings.Fluid.Sensors.RelativePressure senRelPre(
    redeclare final package Medium = Medium)
    "Relative pressure sensor"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dp(
    final quantity="PressureDifference",
    final unit="Pa",
    final displayUnit="Pa")
    "Pressure drop accross the connection (measured)"
  Modelica.Blocks.Interfaces.BooleanOutput isChaRem
    "Is operated for remote charging"
equation
  connect(tanSta.y, floCon.tanSta)
  connect(floCon.mPriPum_flow, pumPri.m_flow_in)
  connect(floCon.ySecPum,revCon. yPum)
  connect(floCon.yVal,revCon. yVal)
  connect(tanBra.TTan,tanSta. TTan)
  connect(chiEnaSta, floCon.chiEnaSta)
  connect(com, floCon.com)
  connect(hasLoa, floCon.hasLoa)
  connect(yPum, floCon.yPum)
  connect(port_b2, chi2PreDro.port_b)
  connect(tanBra.port_a2, port_a2)
  connect(revCon.port_b, port_b1)
  connect(port_a1, pumPri.port_a)
  connect(pumPri.port_b, tanBra.port_a1)
  connect(tanBra.port_b2, chi2PreDro.port_a)
  connect(tanBra.port_b1, revCon.port_a)
  connect(port_b1, senRelPre.port_a)
  connect(port_a2, senRelPre.port_b)
  connect(senRelPre.p_rel, dp)
  connect(floCon.isChaRem, isChaRem)
end StoragePlant;


Ideal user model

within Buildings.DHC.Plants.Cooling.BaseClasses;
model IdealUser "Ideal user model"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium package";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Nominal mass flow rate";

  parameter Modelica.Units.SI.Temperature T_CHWR_nominal
    "Nominal temperature of CHW return";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    final displayUnit="Pa")
    "Nominal pressure drop when valve is fully open";

  Buildings.Fluid.Actuators.Valves.TwoWayEqualPercentage val(
    redeclare final package Medium = Medium,
    final use_strokeTime=true,
    final dpValve_nominal=dp_nominal/2,
    final init=Modelica.Blocks.Types.Init.InitialState,
    final dpFixed_nominal=dp_nominal/2,
    final m_flow_nominal=m_flow_nominal,
    y_start=0) "User control valve"
  Buildings.Controls.Continuous.LimPID conPI(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=0.5,
    Ti=20,
    final reverseActing=true)  "PI controller"
  Modelica.Blocks.Interfaces.RealInput mPre_flow(
    final quantity = "MassFlowRate",
    final unit = "kg/s")
    "Load in terms of flow rate prescription"
  Modelica.Blocks.Interfaces.RealOutput yVal_actual(
    final unit = "1")
    "Consumer control valve actuator position"
  Buildings.Fluid.Sensors.RelativePressure senRelPre(
    redeclare final package Medium = Medium)
    "Differential pressure sensor"
  Modelica.Blocks.Interfaces.RealOutput dp(
    final quantity="PressureDifference",
    final unit="Pa",
    displayUnit="Pa")
    "Differential pressure from the sensor"
  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    p(start=Medium.p_default),
    redeclare final package Medium = Medium,
    h_outflow(start=Medium.h_default, nominal=Medium.h_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    p(start=Medium.p_default),
    redeclare final package Medium = Medium,
    h_outflow(start=Medium.h_default, nominal=Medium.h_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"
  Buildings.Fluid.Sensors.MassFlowRate senMasFlo(
    redeclare final package Medium = Medium)
    "Mass flow rate sensor"
  Buildings.Fluid.Sources.PropertySource_T proSou(
    redeclare final package Medium = Medium,
    final use_T_in=true) "Ideal temperature source"
  Modelica.Blocks.Sources.Constant TRet(final k=T_CHWR_nominal)
    "Constant CHW return temperature"
equation
  connect(senRelPre.p_rel, dp)
  connect(val.y_actual, yVal_actual)
  connect(senRelPre.port_a, port_a)
  connect(senRelPre.port_b, port_b)
  connect(conPI.y, val.y)
  connect(senMasFlo.port_b, port_b)
  connect(senMasFlo.m_flow, conPI.u_m)
  connect(port_a, val.port_a)
  connect(conPI.u_s, mPre_flow)
  connect(senMasFlo.port_a, proSou.port_b)
  connect(proSou.port_a, val.port_b)
  connect(TRet.y, proSou.T_in)
end IdealUser;


Package containing base classes

within Buildings.DHC.Plants.Cooling;
package BaseClasses "Package containing base classes"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


A pair of junctions in parallel

within Buildings.DHC.Plants.Cooling.BaseClasses;
model ParallelJunctions "A pair of junctions in parallel"

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium package";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Design mass flow rate (used to approximate dynamics"
  parameter Modelica.Units.SI.Time tau=30 "Time constant at nominal flow"
  parameter Modelica.Units.SI.Temperature T1_start=Medium.T_default
    "Start temperature of the volume"
  parameter Modelica.Units.SI.Temperature T2_start=Medium.T_default
    "Start temperature of the volume"

  Modelica.Fluid.Interfaces.FluidPort_a port_a2(
    redeclare final package Medium = Medium,
    p(final displayUnit="Pa")) "Fluid connector"
  Modelica.Fluid.Interfaces.FluidPort_b port_b1(
    redeclare final package Medium = Medium,
    p(final displayUnit="Pa")) "Fluid connector"
  Modelica.Fluid.Interfaces.FluidPort_b port_b2(
    redeclare final package Medium = Medium,
    p(final displayUnit="Pa")) "Fluid connector"
  Modelica.Fluid.Interfaces.FluidPort_a port_a1(
    redeclare final package Medium = Medium,
    p(final displayUnit="Pa")) "Fluid connector"
  Modelica.Fluid.Interfaces.FluidPort_a port_c1(
    redeclare final package Medium = Medium,
    p(final displayUnit="Pa")) "Fluid connector"
  Modelica.Fluid.Interfaces.FluidPort_b port_c2(
    redeclare final package Medium = Medium,
    p(final displayUnit="Pa")) "Fluid connector"
  Buildings.Fluid.FixedResistances.Junction jun1(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    final tau=tau,
    final T_start=T1_start,
    final m_flow_nominal={-m_flow_nominal,-m_flow_nominal,m_flow_nominal},
    final dp_nominal={0,0,0})
    "Junction"
  Buildings.Fluid.FixedResistances.Junction jun2(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    final tau=tau,
    final T_start=T2_start,
    final m_flow_nominal={m_flow_nominal,m_flow_nominal,-m_flow_nominal},
    final dp_nominal={0,0,0})
    "Junction"
equation
  connect(port_a1, jun1.port_1)
  connect(jun1.port_3,port_c1)
  connect(jun1.port_2, port_b1)
  connect(port_b2, jun2.port_1)
  connect(jun2.port_2, port_a2)
  connect(jun2.port_3,port_c2)
end ParallelJunctions;


CHW supply and return pipes in parallel

within Buildings.DHC.Plants.Cooling.BaseClasses;
model ParallelPipes "CHW supply and return pipes in parallel"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final m1_flow_nominal = m_flow_nominal,
    final m2_flow_nominal = m_flow_nominal);

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium package";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    final displayUnit="Pa")
    "Nominal pressure drop"

  Buildings.Fluid.FixedResistances.PressureDrop preDro1(
    redeclare package Medium = Medium1,
    final allowFlowReversal=true,
    final dp_nominal=dp_nominal,
    final m_flow_nominal=m1_flow_nominal) "Flow resistance"
  Buildings.Fluid.FixedResistances.PressureDrop preDro2(
    redeclare package Medium = Medium2,
    final allowFlowReversal=true,
    final dp_nominal=dp_nominal,
    final m_flow_nominal=m2_flow_nominal) "Flow resistance"
equation
  connect(preDro1.port_a, port_a1)
  connect(preDro1.port_b, port_b1)
  connect(preDro2.port_a, port_a2)
  connect(preDro2.port_b, port_b2)
end ParallelPipes;


within Buildings.DHC.Plants.Cooling.BaseClasses;
model ReversibleConnection
  "A connection that supports reversible flow with a pump and a valve"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    final allowFlowReversal=true);

  parameter Modelica.Units.SI.MassFlowRate mTan_flow_nominal(min=0)
    "Nominal mass flow rate for CHW tank branch";
  parameter Modelica.Units.SI.PressureDifference dpPum_nominal
    "Nominal pressure difference for supply pump sizing";
  parameter Modelica.Units.SI.PressureDifference dpVal_nominal
    "Nominal pressure difference for return valve sizing";
  parameter Modelica.Units.SI.ThermodynamicTemperature T_start
    "Start temperature"

  Modelica.Blocks.Interfaces.RealOutput PEle(
    final quantity="Power",
    final unit="W")
    "Estimated power consumption"
  Modelica.Blocks.Interfaces.RealInput yPum(final unit="1")
    "Normalized speed signal for pump"
  Buildings.Fluid.Movers.Preconfigured.SpeedControlled_y pum(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final addPowerToMedium=false,
    final dp_nominal=dpPum_nominal) "Supply pump"
  Buildings.Fluid.FixedResistances.CheckValve cheVal(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final dpValve_nominal=0.1*dpPum_nominal,
    final dpFixed_nominal=0) "Check valve"
  Buildings.Fluid.Actuators.Valves.TwoWayPressureIndependent val(
    redeclare final package Medium = Medium,
    final m_flow_nominal=mTan_flow_nominal,
    final dpValve_nominal=dpVal_nominal,
    y_start=0)
    "Valve that throttles CHW from the supply line to the tank"
  Modelica.Blocks.Interfaces.RealInput yVal(final unit="1")
    "Normalized flow signal for valve"

protected
  Buildings.Fluid.FixedResistances.Junction jun1(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    tau=30,
    T_start=T_start,
    m_flow_nominal={-mTan_flow_nominal,m_flow_nominal,-m_flow_nominal},
    dp_nominal={0,0,0}) "Junction"
  Buildings.Fluid.FixedResistances.Junction jun2(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    tau=30,
    T_start=T_start,
    m_flow_nominal={-m_flow_nominal,-mTan_flow_nominal,m_flow_nominal},
    dp_nominal={0,0,0}) "Junction"

equation
  connect(pum.port_b, jun2.port_1)
  connect(jun2.port_3, port_b)
  connect(val.port_a, jun2.port_2)
  connect(jun1.port_3, port_a)
  connect(jun1.port_1, val.port_b)
  connect(pum.y, yPum)
  connect(yVal, val.y)
  connect(pum.P, PEle)
  connect(jun1.port_2, cheVal.port_a)
  connect(cheVal.port_b, pum.port_a)
end ReversibleConnection;


within Buildings.DHC.Plants.Cooling.BaseClasses;
model TankBranch "Model of the tank branch of a storage plant"

  extends Buildings.Fluid.Interfaces.PartialFourPort(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium);

  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium package";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal(min=0)
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate mTan_flow_nominal(min=0)
    "Nominal mass flow rate for CHW tank branch"
  parameter Modelica.Units.SI.MassFlowRate mChi_flow_nominal(min=0)
    "Nominal mass flow rate for CHW chiller branch"
  parameter Modelica.Units.SI.Temperature T_CHWS_nominal(displayUnit="degC")=
    7+273.15 "Nominal temperature of CHW supply"
  parameter Modelica.Units.SI.Temperature T_CHWR_nominal(displayUnit="degC")=
    12+273.15
    "Nominal temperature of CHW return"

  // Storage tank parameters
  parameter Modelica.Units.SI.Volume VTan "Tank volume"
  parameter Modelica.Units.SI.Length hTan
    "Height of tank (without insulation)"
  parameter Modelica.Units.SI.Length dIns "Thickness of insulation"
  parameter Modelica.Units.SI.ThermalConductivity kIns=0.04
    "Specific heat conductivity of insulation"
  parameter Integer nSeg(min=2) = 5 "Number of volume segments"
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=
    Modelica.Fluid.Types.Dynamics.FixedInitial
    "Formulation of energy balance"
  parameter Medium.AbsolutePressure p_start = Medium.p_default
    "Start value of pressure"
  parameter Medium.Temperature T_start=T_CHWR_nominal
    "Start value of temperature"
  parameter Modelica.Units.SI.Temperature TFlu_start[nSeg]=T_start*ones(nSeg)
    "Initial temperature of the tank segments, with TFlu_start[1] being the top segment"
  parameter Modelica.Units.SI.Time tau=1 "Time constant for mixing"

  Buildings.Fluid.Storage.Stratified tan(
    redeclare final package Medium = Medium,
    final allowFlowReversal=true,
    final VTan=VTan,
    final hTan=hTan,
    final dIns=dIns,
    final kIns=kIns,
    final nSeg=nSeg,
    final energyDynamics=energyDynamics,
    final p_start=p_start,
    final T_start=T_start,
    final TFlu_start=TFlu_start,
    final tau=tau,
    final m_flow_nominal=mTan_flow_nominal,
    show_T=true) "Tank"
  Buildings.Fluid.Sensors.MassFlowRate senFlo(
    redeclare final package Medium = Medium,
    final allowFlowReversal=true) "Flow rate sensor for the tank,"
  Modelica.Blocks.Interfaces.RealOutput mTan_flow(
    final quantity="MassFlowRate",
    final unit="kg/s") "Mass flow rate of the tank"
  Modelica.Blocks.Interfaces.RealOutput Ql_flow(
    final quantity="HeatFlowRate",
    final unit="W")
    "Heat loss of tank (positive if heat flows from tank to ambient)"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorTop
    "Heat port tank top (outside insulation)"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorSid
    "Heat port tank side (outside insulation)"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorBot
    "Heat port tank bottom (outside insulation). Leave unconnected for adiabatic condition"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a[tan.nSeg] heaPorVol
    "Heat port that connects to the control volumes of the tank"
  Modelica.Blocks.Interfaces.RealOutput TTan[2](
    each final unit="K",
    each final quantity="Temperature",
    each displayUnit="degC") "Temperatures at the tank 1: top and 2: bottom"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTemTop
    "Temperature sensor for tank top"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTemBot
    "Temperature sensor for tank bottom"

protected
  Buildings.Fluid.FixedResistances.Junction junSup(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=T_CHWS_nominal,
    tau=30,
    m_flow_nominal={-mChi_flow_nominal,mTan_flow_nominal,m_flow_nominal},
    dp_nominal={0,0,0}) "Junction on the supply side"
  Buildings.Fluid.FixedResistances.Junction junRet(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=T_CHWR_nominal,
    tau=30,
    m_flow_nominal={-m_flow_nominal,mChi_flow_nominal,mTan_flow_nominal},
    dp_nominal={0,0,0}) "Junction on the return side"

equation
  connect(senFlo.m_flow, mTan_flow)
  connect(tan.Ql_flow, Ql_flow)
  connect(tan.heaPorTop, heaPorTop)
  connect(tan.heaPorSid, heaPorSid)
  connect(tan.heaPorBot, heaPorBot)
  connect(heaPorVol, tan.heaPorVol)
  connect(junRet.port_3, senFlo.port_a)
  connect(senFlo.port_b, tan.port_a)
  connect(tan.port_b, junSup.port_3)
  connect(tan.heaPorTop, senTemTop.port)
  connect(tan.heaPorBot, senTemBot.port)
  connect(senTemTop.T, TTan[1])
  connect(senTemBot.T, TTan[2])
  connect(port_b2, junRet.port_2)
  connect(junRet.port_1, port_a2)
  connect(junSup.port_2, port_b1)
  connect(junSup.port_1, port_a1)
end TankBranch;


Test model for the dummy user

within Buildings.DHC.Plants.Cooling.BaseClasses.Validation;
model IdealUser "Test model for the dummy user"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Water "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=500000
    "Nominal pressure difference";
  parameter Modelica.Units.SI.AbsolutePressure p_Pressurisation=300000
    "Pressurisation point";
  parameter Modelica.Units.SI.Temperature T_CHWR_nominal(
    final displayUnit="degC")=12+273.15
    "Nominal temperature of CHW return";
  parameter Modelica.Units.SI.Temperature T_CHWS_nominal(
    final displayUnit="degC")=7+273.15
    "Nominal temperature of CHW supply";
  parameter Boolean allowFlowReversal=false
    "Flow reversal setting";
  parameter Modelica.Units.SI.Power QCooLoa_flow_nominal=5*4200*0.9
    "Nominal cooling load of one consumer";

  Buildings.DHC.Plants.Cooling.BaseClasses.IdealUser ideUse(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    T_CHWR_nominal=T_CHWR_nominal,
    final dp_nominal=dp_nominal) "Ideal user"
  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare final package Medium = Medium,
    final p=p_Pressurisation,
    final T=T_CHWR_nominal,
    nPorts=1) "Sink representing CHW return line"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare final package Medium = Medium,
    final p=p_Pressurisation+dp_nominal,
    final T=T_CHWS_nominal,
    nPorts=1) "Source representing CHW supply line"
  Modelica.Blocks.Sources.TimeTable mPre_flow(table=[0*3600,0; 0.5*3600,0; 0.5*
        3600,m_flow_nominal; 0.75*3600,m_flow_nominal; 0.75*3600,0; 1*3600,0])
    "Prescribed flow rate representing cooling load"
equation
  connect(sou.ports[1],ideUse. port_a)
  connect(ideUse.port_b, sin.ports[1])
  connect(mPre_flow.y, ideUse.mPre_flow)
end IdealUser;


Package containing validation models

within Buildings.DHC.Plants.Cooling.BaseClasses;
package Validation "Package containing validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Controller for chilled water bypass valve. This model implements the chilled water loop bypass valve control logic as
follows:

within Buildings.DHC.Plants.Cooling.Controls;
model ChilledWaterBypass
  "Controller for chilled water bypass valve"
  extends Modelica.Blocks.Icons.Block;
  parameter Integer numChi(
    min=1)
    "Number of chillers";
  parameter Modelica.Units.SI.MassFlowRate mMin_flow
    "Minimum mass flow rate of single chiller";
  parameter Real k(min=0) = 0.06 "Gain of controller";
  parameter Modelica.Units.SI.Time Ti(min=Modelica.Constants.small) = 60
    "Time constant of Integrator block"
  parameter Modelica.Blocks.Types.SimpleController controllerType=
         Modelica.Blocks.Types.SimpleController.PI
    "Type of controller";
  Modelica.Blocks.Interfaces.BooleanInput chiOn[numChi]
    "On signals of the chillers"
  Modelica.Blocks.Interfaces.RealInput mFloChi(final unit="kg/s")
    "Mass flow rate through the chillers"
  Modelica.Blocks.Interfaces.RealOutput y
    "Bypass valve opening ratio"
  Buildings.Controls.OBC.CDL.Reals.PIDWithReset
                                       bypValCon(
    controllerType=controllerType,
    final k=k,
    final Ti=Ti,
    y_reset=0)
    "Chilled water bypass valve controller"
  Modelica.Blocks.Math.BooleanToInteger booToInt[numChi]
    "Boolean signal to integer"
  Buildings.Controls.OBC.CDL.Integers.GreaterThreshold intGreThr
    "Greater than zero"
  Buildings.Controls.OBC.CDL.Integers.MultiSum numChiOn(nin=numChi)
    "Number of chillers on"
  Buildings.Controls.OBC.CDL.Conversions.IntegerToReal intToRea
    "Integer to real"
  Modelica.Blocks.Math.Gain mFloSetSca(k=1/numChi)
    "Normalize mass flowrate setpoint"
  Modelica.Blocks.Math.Gain mFloBypSca(k=1/(numChi*mMin_flow))
    "Normalize the measured mass flowrate"
equation
  connect(chiOn, booToInt.u)
  connect(booToInt.y, numChiOn.u)
  connect(numChiOn.y, intGreThr.u)
  connect(intGreThr.y, bypValCon.trigger)
  connect(numChiOn.y, intToRea.u)
  connect(bypValCon.y, y)
  connect(intToRea.y, mFloSetSca.u)
  connect(mFloSetSca.y, bypValCon.u_s)
  connect(mFloChi, mFloBypSca.u)
  connect(mFloBypSca.y, bypValCon.u_m)
end ChilledWaterBypass;


Controller for two headered variable speed chilled water pumps. This model implements the control logic for variable speed pumps.
The staging of pumps is implemented through an instance of

Buildings.Applications.BaseClasses.Controls.VariableSpeedPumpStage.

within Buildings.DHC.Plants.Cooling.Controls;
model ChilledWaterPumpSpeed
  "Controller for two headered variable speed chilled water pumps"
  extends Modelica.Blocks.Icons.Block;
  parameter Modelica.Units.SI.PressureDifference dpSetPoi(displayUnit="Pa")
    "Pressure difference setpoint";
  parameter Modelica.Units.SI.Time tWai "Waiting time";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Nominal mass flow rate of single chilled water pump";
  parameter Real minSpe(
    final unit="1",
    final min=0,
    final max=1)=0.05
    "Minimum speed ratio required by chilled water pumps";
  parameter Modelica.Units.SI.MassFlowRate criPoiFlo=0.7*m_flow_nominal
    "Critcal point of flowrate for switching pump on or off";
  parameter Modelica.Units.SI.MassFlowRate deaBanFlo=0.1*m_flow_nominal
    "Deadband for critical point of flowrate";
  parameter Real criPoiSpe=0.5
    "Critical point of speed signal for switching on or off";
  parameter Real deaBanSpe=0.3
    "Deadband for critical point of speed signal";
  parameter Modelica.Blocks.Types.SimpleController controllerType=
    Modelica.Blocks.Types.SimpleController.PI
    "Type of pump speed controller"
  parameter Real k(
    final unit="1",
    final min=0)=1
    "Gain of controller"
  parameter Modelica.Units.SI.Time Ti(final min=Modelica.Constants.small) = 60
    "Time constant of Integrator block"
  parameter Modelica.Units.SI.Time Td(final min=0) = 0.1
    "Time constant of Derivative block"
  Modelica.Blocks.Interfaces.RealInput masFloPum(
    final unit="kg/s")
    "Total mass flowrate of chilled water pumps"
  Modelica.Blocks.Interfaces.RealInput dpMea(
    final unit="Pa")
    "Measured pressure difference"
  Modelica.Blocks.Interfaces.RealOutput y[numPum](
    each final unit="1",
    each final min=0,
    each final max=1)
    "Pump speed signal"
  Modelica.Blocks.Math.Product pumSpe[numPum] "Output pump speed"
  Buildings.Applications.BaseClasses.Controls.VariableSpeedPumpStage pumStaCon(
    final tWai=tWai,
    final m_flow_nominal=m_flow_nominal,
    final minSpe=minSpe,
    final criPoiFlo=criPoiFlo,
    final deaBanFlo=deaBanFlo,
    final criPoiSpe=criPoiSpe,
    final deaBanSpe=deaBanSpe)
    "Chilled water pump staging control"
  Buildings.Controls.OBC.CDL.Reals.PIDWithReset conPID(
    final controllerType=controllerType,
    final Ti=Ti,
    final k=k,
    final Td=Td)
    "PID controller of pump speed"
  Modelica.Blocks.Sources.Constant dpSetSca(final k=1)
    "Scaled differential pressure setpoint"
  Modelica.Blocks.Math.Gain gai(k=1/dpSetPoi) "Gain for mesaured dp value"
  Modelica.Blocks.Math.RealToBoolean twoPum(threshold=1.5) "Two pumps are on"
  Modelica.Blocks.Math.Sum totPum(final nin=numPum) "Total number of pumps on"
  Modelica.Blocks.Interfaces.BooleanInput on
    "On signal of the plant"
  Modelica.Blocks.Logical.Or orRes "Or block for controller reset"
protected
  final parameter Integer numPum=2
    "Number of chilled water pumps";
equation
  connect(pumStaCon.masFloPum,masFloPum)
  connect(conPID.y,pumStaCon.speSig)
  connect(pumStaCon.y,pumSpe.u1)
  connect(conPID.y,pumSpe[1].u2)
  connect(conPID.y,pumSpe[2].u2)
  connect(dpSetSca.y,conPID.u_s)
  connect(dpMea, gai.u)
  connect(gai.y, conPID.u_m)
  connect(pumStaCon.y, totPum.u)
  connect(totPum.y, twoPum.u)
  connect(pumSpe.y, y)
  connect(on, pumStaCon.on)
  connect(twoPum.y, orRes.u2)
  connect(on, orRes.u1)
  connect(orRes.y, conPID.trigger)
end ChilledWaterPumpSpeed;


within Buildings.DHC.Plants.Cooling.Controls;
model ChillerStage
  "Chiller staging controller for plants with two chillers of the same size"
  replaceable package Medium=Buildings.Media.Water
    constrainedby Modelica.Media.Interfaces.PartialMedium
    "Service side medium";
  parameter Modelica.Units.SI.Time tWai "Waiting time";
  parameter Modelica.Units.SI.Power QChi_nominal(final max=0)
    "Nominal cooling capacity (negative)";
  parameter Modelica.Units.SI.Power staUpThr(final min=0) = -0.8*QChi_nominal
    "Stage up load threshold(from one to two chillers)";
  parameter Modelica.Units.SI.Power staDowThr(final min=0) = -0.6*QChi_nominal
    "Stage down load threshold(from two to one chiller)";
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
    "State graph root"
  Modelica.Blocks.Interfaces.BooleanInput on
    "Enabling signal of the plant. True: chiller should be enabled"
  Modelica.Blocks.Interfaces.RealInput TChiWatRet
    "Chilled water return temperature"
  Modelica.Blocks.Interfaces.RealInput TChiWatSup
    "Chilled water supply temperature"
  Modelica.Blocks.Interfaces.RealInput mFloChiWat
    "Chilled water mass flow rate"
  Modelica.Blocks.Interfaces.BooleanOutput y[2]
    "On/off signal for the chillers - false: off; true: on"
  Modelica.StateGraph.InitialStep off(nIn=1, nOut=1)
    "No cooling is demanded"
  Modelica.StateGraph.StepWithSignal oneOn(
    nOut=2,
    nIn=2)
    "Status of one chiller on"
  Modelica.StateGraph.StepWithSignal twoOn(nOut=1, nIn=1)
    "Status of two chillers on"
  Modelica.StateGraph.TransitionWithSignal offToOne(
    enableTimer=true,
    waitTime=tWai)
    "Condition of transition from off to one chiller on"
  Modelica.StateGraph.TransitionWithSignal oneToTwo(
    enableTimer=true,
    waitTime=tWai)
    "Condition of transition from one chiller to two chillers"
  Modelica.StateGraph.TransitionWithSignal twoToOne(
    enableTimer=true,
    waitTime=tWai) "Condition of transion from two chillers to one chiller"
  Modelica.StateGraph.TransitionWithSignal oneToOff(
    enableTimer=true,
    waitTime=tWai)
    "Condition of transition from one chiller to off"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis thrOneToTwo(uLow=-staDowThr/
        QChi_nominal, uHigh=-staUpThr/QChi_nominal)
                    "Threshold of turning two chillers on"
  Modelica.Blocks.Logical.Not thrTwoToOne
    "Threshold of turning off the second chiller"
  Modelica.Blocks.Math.Add dT(
    final k1=-1,
    final k2=+1)
    "Temperature difference"
  Modelica.Blocks.Math.Product pro
    "Product"
  Modelica.Blocks.Math.Gain plr(final k=cp_default/QChi_nominal)
    "Specific heat multiplier to calculate heat flow rate"
  Buildings.Controls.OBC.CDL.Logical.Or Or "On signal for either chiller"
  Modelica.Blocks.Logical.Not notOn "on switches to false"
  Modelica.Blocks.Logical.Or TwoToOne
    "Conditions that turn off the second chiller"
protected
  final parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
    T=Medium.T_default,
    p=Medium.p_default,
    X=Medium.X_default)
    "Medium state at default properties";
  final parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(sta_default)
    "Specific heat capacity of the fluid";
equation
  connect(off.outPort[1],offToOne.inPort)
  connect(oneToTwo.outPort,twoOn.inPort[1])
  connect(twoToOne.outPort,oneOn.inPort[2])
  connect(oneOn.outPort[2],oneToOff.inPort)
  connect(oneOn.outPort[1],oneToTwo.inPort)
  connect(oneToTwo.condition,thrOneToTwo.y)
  connect(dT.y,pro.u1)
  connect(plr.u, pro.y)
  connect(plr.y, thrOneToTwo.u)
  connect(dT.u1,TChiWatRet)
  connect(TChiWatSup,dT.u2)
  connect(pro.u2,mFloChiWat)
  connect(oneToTwo.condition, thrTwoToOne.u)
  connect(oneOn.active, Or.u1)
  connect(twoOn.active, Or.u2)
  connect(Or.y, y[1])
  connect(twoOn.active, y[2])
  connect(on, notOn.u)
  connect(twoOn.outPort[1],twoToOne.inPort)
  connect(offToOne.outPort,oneOn.inPort[1])
  connect(oneToOff.outPort,off.inPort[1])
  connect(on, offToOne.condition)
  connect(thrTwoToOne.y, TwoToOne.u2)
  connect(notOn.y, TwoToOne.u1)
  connect(TwoToOne.y, twoToOne.condition)
  connect(notOn.y, oneToOff.condition)
end ChillerStage;


This block controls the flow at the primary and secondary pumps. This block implements a state graph to control the flows of the storage plant.
It receives two tank status Boolean signals indicating that the tank
is charged or empty. These two signals can be both false indicating an
in-between state. The block can receive one of the following commands:

within Buildings.DHC.Plants.Cooling.Controls;
block FlowControl
  "This block controls the flow at the primary and secondary pumps"
  extends Modelica.Blocks.Icons.Block;

  parameter Modelica.Units.SI.MassFlowRate mChi_flow_nominal
    "Nominal mass flow rate of the chiller loop"
  parameter Modelica.Units.SI.MassFlowRate mTan_flow_nominal
    "Nominal mass flow rate of the tank branch"

  Modelica.Blocks.Interfaces.IntegerInput com
    "Command: 1 = charge tank, 2 = no command, 3 = discharge tank"
  Modelica.Blocks.Interfaces.BooleanInput chiEnaSta
    "Chiller enable status, true if chiller is enabled"
  Modelica.Blocks.Interfaces.BooleanInput hasLoa "Set to true if there is a load"
  Modelica.Blocks.Interfaces.RealInput yPum(final unit="1")
    "Normalized speed signal for the secondary pump"

  Modelica.Blocks.Interfaces.RealOutput yVal "Valve normalized mass flow rate"
  Modelica.Blocks.Interfaces.RealOutput mPriPum_flow(final unit="kg/s")
    "Primary pump mass flow rate"
  Modelica.Blocks.Interfaces.RealOutput ySecPum(final unit="1")
    "Secondary pump normalized speed"
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot "State graph root"
  Modelica.StateGraph.InitialStep allOff(nOut=1, nIn=1) "Initial step, all off"

  Modelica.StateGraph.Transition traChaLoc(condition=com == 1 and (not tanSta[2])
         and chiEnaSta)
    "Transition: Charge tank command AND tank not charged AND chiller enabled"
  Modelica.StateGraph.Step steChaLoc(nIn=1, nOut=1) "Step: Local charging"
  Modelica.StateGraph.Transition traRes1(condition=not traChaLoc.condition)
                                       "Transition: Reset to initial step"
  Modelica.StateGraph.Transition traChaRem(condition=com == 1 and (not tanSta[2])
         and not chiEnaSta)
    "Transition: Charge tank command AND tank not charged AND chiller not enabled"
  Modelica.StateGraph.Step steChaRem(nIn=1, nOut=1) "Step: Remote charging"
  Modelica.StateGraph.Transition traRes2(condition=not traChaRem.condition)
                                       "Transition: Reset to initial step"
  Modelica.StateGraph.Transition traProChi(condition=chiEnaSta)
    "Transition: Chiller enabled"
  Modelica.StateGraph.Step steProChi(nIn=2, nOut=2)
    "Step: Chiller produces CHW"
  Modelica.StateGraph.Transition traRes4(condition=not traPro.condition)
    "Transition: Reset to initial step"
  Modelica.StateGraph.Transition traProTan(condition=com == 3 and (not tanSta[1]))
    "Transition: Tank commanded to discharge AND is not empty"
  Modelica.StateGraph.Step steProTan(nIn=2, nOut=2) "Step: Tank produces CHW"
  Modelica.StateGraph.Transition traRes3(condition=not traPro.condition)
    "Transition: Reset to initial step"
  Modelica.Blocks.Sources.BooleanExpression expPriPumFlo(
    y=steChaLoc.active or steProChi.active)
                          "Expression for local charging OR chiller output"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal swiPriPum(realTrue=
        mChi_flow_nominal, realFalse=0) "Switch for primary pump flow"
  Modelica.Blocks.Sources.BooleanExpression expSecPum(y=stePumSecOn.active)
                          "Expression for tank output or chiller output"
  Modelica.Blocks.Sources.BooleanExpression expVal(y=steChaRem.active)
    "Boolean expression for remotely charging the tank"
  Modelica.StateGraph.Alternative alt(nBranches=3)
    "Alternative: Tank charging or plant outputting CHW"

  Buildings.Controls.OBC.CDL.Reals.Switch swiSecPum
    "Switch for secondary pump flow"
  Modelica.Blocks.Sources.Constant zer(final k=0) "Constant zero"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal swiVal(realTrue=
        mTan_flow_nominal, realFalse=0) "Switch for valve flow"
  Modelica.Blocks.Interfaces.BooleanInput tanSta[2]
    "Tank status - 1: is empty; 2: is charged; can be both false"
  Modelica.StateGraph.Step stePumSecOn(nOut=1, nIn=1) "Step: Secondary pump on"
  Modelica.StateGraph.Transition traPro(condition=hasLoa and (traProTan.condition
         or traProChi.condition))
    "Transition: Has load and the plant can produce CHW via either the chiller or the tank"
  Modelica.StateGraph.Parallel parallel(nBranches=2)
    "Parallel states of the components in the primary and secondary loops"
  Modelica.StateGraph.Transition traTanToChi(condition=(not traProTan.condition)
         and traProChi.condition)
    "Transition: Tank no longer available (empty or commanded off) AND chiller available"
  Modelica.StateGraph.Alternative altTanCha1(nBranches=2)
    "Alternative: Tank charging locally or remotely"
  Modelica.StateGraph.Step steRou1(nIn=1, nOut=1) "A step for routing only"
  Modelica.StateGraph.Step steRou2(nIn=1, nOut=1) "A step for routing only"
  Modelica.StateGraph.Transition traRou(final condition=true)
    "A routing transition, always true"
  Modelica.StateGraph.Transition traChiToTan(condition=traProTan.condition)
    "Transition: Production priority handed to tank whenever possible"
  Modelica.Blocks.Interfaces.BooleanOutput isChaRem
    "Is operated for remote charging"
  Modelica.Blocks.Sources.BooleanExpression booleanExpression(y=steChaRem.active)
equation
  connect(traChaLoc.outPort,steChaLoc. inPort[1])
  connect(steChaLoc.outPort[1], traRes1.inPort)
  connect(traChaRem.outPort,steChaRem. inPort[1])
  connect(steChaRem.outPort[1], traRes2.inPort)
  connect(traProChi.outPort,steProChi. inPort[1])
  connect(traProTan.outPort,steProTan. inPort[1])
  connect(expPriPumFlo.y, swiPriPum.u)
  connect(alt.inPort, allOff.outPort[1])
  connect(alt.outPort, allOff.inPort[1])
  connect(mPriPum_flow, swiPriPum.y)
  connect(expSecPum.y, swiSecPum.u2)
  connect(swiSecPum.u1, yPum)
  connect(ySecPum, swiSecPum.y)
  connect(zer.y, swiSecPum.u3)
  connect(swiVal.u, expVal.y)
  connect(swiVal.y, yVal)
  connect(traPro.outPort, parallel.inPort)
  connect(parallel.split[1], stePumSecOn.inPort[1])
  connect(stePumSecOn.outPort[1], parallel.join[1])
  connect(traTanToChi.outPort, steProChi.inPort[2])
  connect(traProTan.inPort, altTanCha1.split[1])
  connect(traRes3.outPort, altTanCha1.join[1])
  connect(traProChi.inPort, altTanCha1.split[2])
  connect(traRes4.outPort, altTanCha1.join[2])
  connect(steRou1.outPort[1], altTanCha1.inPort)
  connect(altTanCha1.outPort,steRou2. inPort[1])
  connect(steRou1.inPort[1], parallel.split[2])
  connect(steRou2.outPort[1], parallel.join[2])
  connect(parallel.outPort, traRou.inPort)
  connect(traChaLoc.inPort, alt.split[1])
  connect(traChaRem.inPort, alt.split[2])
  connect(traRes1.outPort, alt.join[1])
  connect(traRes2.outPort, alt.join[2])
  connect(traPro.inPort, alt.split[3])
  connect(traRou.outPort, alt.join[3])
  connect(booleanExpression.y, isChaRem)
  connect(traChiToTan.outPort, steProTan.inPort[2])
  connect(steProChi.outPort[1], traChiToTan.inPort)
  connect(steProChi.outPort[2], traRes4.inPort)
  connect(steProTan.outPort[1], traTanToChi.inPort)
  connect(steProTan.outPort[2], traRes3.inPort)
end FlowControl;


within Buildings.DHC.Plants.Cooling;
package Controls "Package of control sequences for cooling plants"
  extends Modelica.Icons.VariantsPackage;
end Controls;


Block that includes or excludes storage plant pressure signal for min. This block finds the minimum value from pressure head signals.
The signal from the storage plant is included
only when the plant is in remote charging mode.

within Buildings.DHC.Plants.Cooling.Controls;
block SelectMin
  "Block that includes or excludes storage plant pressure signal for min"
  extends Modelica.Blocks.Icons.Block;
  parameter Integer nin
    "Number of input connections"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpUse[nin]
    "Connector of Real input signals"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput dpStoPla
    "Connector of Real input signals"
  Modelica.Blocks.Interfaces.BooleanInput isChaRem
    "The storage plant is in remote charging mode"
  Modelica.Blocks.Interfaces.RealOutput y
equation
  y = if isChaRem then
        min(min(dpUse),dpStoPla)
      else
        min(dpUse);
end SelectMin;


Returns the tank status from its temperature sensors. This model outputs tank status signals using the temperatures
at the CHW tank top and the tank bottom as input.
The status has two separate boolean signals indicating whether the tank is
charged or empty (of cooling). The two output signals can be both false,
indicating an in-between state, but they can never both be true.

within Buildings.DHC.Plants.Cooling.Controls;
block TankStatus "Returns the tank status from its temperature sensors"

  parameter Modelica.Units.SI.Temperature THig
    "Higher threshold to consider the tank empty";
  parameter Modelica.Units.SI.Temperature TLow
    "Lower threshold to consider the tank full";

  parameter Modelica.Units.SI.TemperatureDifference dTHys(min=0.1) = 0.5
    "Deadband for hysteresis";

  Buildings.Controls.OBC.CDL.Interfaces.RealInput TTan[2](
    each final quantity="Temperature",
    each final unit="K",
    each displayUnit="degC") "Temperatures at the tank 1: top; and 2: bottom"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hysCha(
    final uLow=TLow,
    final uHigh=TLow + dTHys) "Hysteresis, tank charged"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hysEmp(
    final uHigh=THig,
    final uLow=THig - dTHys) "Hysteresis, tank empty"
  Buildings.Controls.OBC.CDL.Logical.Not not1 "Not block"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanOutput y[2]
    "Tank status - y[1]=true is empty; y[2] = true is charged; both false means partially charged"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TTanTopChe(
    final k(final unit="K", displayUnit="degC") = THig) "Set point for top temperatuer of tank"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TTanBotChe(
    final k(final unit="K", displayUnit="degC") = TLow) "Set point for bottom temperature of tank"
  Buildings.Controls.OBC.CDL.Reals.Greater gre
   "Test for temperature set points"
  Buildings.Controls.OBC.CDL.Utilities.Assert assMes(
    message = "THig must be greater than TLow.")
   "Assertion if temperature set points are not correct"
equation
  connect(hysCha.y, not1.u)
  connect(TTan[1],hysCha. u)
  connect(TTan[2],hysEmp. u)
  connect(hysEmp.y, y[1])
  connect(not1.y, y[2])
  connect(TTanTopChe.y, gre.u1)
  connect(TTanBotChe.y, gre.u2)
  connect(gre.y, assMes.u)
end TankStatus;


Example to test the chilled water bypass controller. This model validates the chilled water bypass valve control logic implemented in

Buildings.DHC.Plants.Cooling.Controls.ChilledWaterBypass.

within Buildings.DHC.Plants.Cooling.Controls.Validation;
model ChilledWaterBypass
  "Example to test the chilled water bypass controller"
  extends Modelica.Icons.Example;
  Buildings.DHC.Plants.Cooling.Controls.ChilledWaterBypass chiBypCon(numChi=2,
    mMin_flow=0.03)
    "Chilled water bypass loop control"
  Modelica.Blocks.Sources.BooleanTable onOne(
    table(
      each displayUnit="s")={300,900})
    "On signal of the first chiller"
  Modelica.Blocks.Sources.BooleanTable onTwo(
    table(
      each displayUnit="s")={600,900})
    "On signal of the second chiller"
  Modelica.Blocks.Sources.Sine mFloChi(
    amplitude=0.03,
    f=1/300,
    offset=0.03,
    startTime=300) "Mass flow rate through the chillers"
equation
  connect(mFloChi.y,chiBypCon.mFloChi)
  connect(onTwo.y,chiBypCon.chiOn[2])
  connect(onOne.y,chiBypCon.chiOn[1])
end ChilledWaterBypass;


Example to test the chilled water pump speed controller. This model validates the variable speed pump control logic implemented in

Buildings.DHC.Plants.Cooling.Controls.ChilledWaterPumpSpeed.

within Buildings.DHC.Plants.Cooling.Controls.Validation;
model ChilledWaterPumpSpeed
  "Example to test the chilled water pump speed controller"
  extends Modelica.Icons.Example;
  Buildings.DHC.Plants.Cooling.Controls.ChilledWaterPumpSpeed chiWatPumSpe(
    dpSetPoi=68900,
    tWai=30,
    m_flow_nominal=0.5,
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=1,
    Ti=60,
    Td=0.1)
    "Chilled water pump speed controller"
  Modelica.Blocks.Sources.Pulse mFloTot(
    amplitude=2*chiWatPumSpe.m_flow_nominal,
    period=300,
    startTime=150) "Total chilled water mass flow rate"
  Modelica.Blocks.Sources.Pulse dpMea(
    amplitude=0.4*chiWatPumSpe.dpSetPoi,
    period=150,
    offset=0.8*chiWatPumSpe.dpSetPoi,
    startTime=150) "Measured pressure drop"
  Modelica.Blocks.Sources.BooleanConstant on "Plant on signal"
equation
  connect(mFloTot.y,chiWatPumSpe.masFloPum)
  connect(dpMea.y, chiWatPumSpe.dpMea)
  connect(on.y, chiWatPumSpe.on)
end ChilledWaterPumpSpeed;


Example to test the chiller staging controller. This model validates the chiller staging control logic implemented in

Buildings.DHC.Plants.Cooling.Controls.ChillerStage.

within Buildings.DHC.Plants.Cooling.Controls.Validation;
model ChillerStage
  "Example to test the chiller staging controller"
  extends Modelica.Icons.Example;
  Buildings.DHC.Plants.Cooling.Controls.ChillerStage chiStaCon(
    tWai=30,
    QChi_nominal=-200*3.517*1000)
    "Chiller staging controller"
  Modelica.Blocks.Sources.BooleanTable on(
    table(
      each displayUnit="s")={300,900})
    "On signal of the cooling plant"
  Modelica.Blocks.Sources.Constant TRet(k=18)
    "Return temperature"
  Modelica.Blocks.Sources.Constant TSup(k=8)
    "Supply temperature"
  Modelica.Blocks.Sources.Sine mFlo(
    amplitude=0.5*chiStaCon.QChi_nominal/(-10)/4200,
    f=1/300,
    offset=0.5*chiStaCon.QChi_nominal/(-10)/4200) "Total mass flow rate"
equation
  connect(on.y,chiStaCon.on)
  connect(mFlo.y,chiStaCon.mFloChiWat)
  connect(TRet.y,chiStaCon.TChiWatRet)
  connect(TSup.y,chiStaCon.TChiWatSup)
end ChillerStage;


Collection of validation models

within Buildings.DHC.Plants.Cooling.Controls;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Validation model for tank status controller. This model validates

Buildings.DHC.Plants.Cooling.Controls.TankStatus.
Note that the output signals turn true as soon as their respective temperature
input crosses the threshold, but there is a delay for it to turn back to
false due to the hysteresis.

within Buildings.DHC.Plants.Cooling.Controls.Validation;
model TankStatus "Validation model for tank status controller"
  extends Modelica.Icons.Example;
  Buildings.DHC.Plants.Cooling.Controls.TankStatus tanSta(
    TLow=280.15,
    THig=286.15,
    dTHys=1) "Tank status"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TTanBot(
    table=[0,273.15 + 11; 0.25,273.15 + 13.5; 0.5,273.15 + 12.5; 0.75,273.15 + 13.5; 1,273.15 + 11])
    "Temperature at tank bottom"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TTanTop(
    table=[0,273.15 + 9; 0.25,273.15 + 6.5;
        0.5,273.15 + 7.5; 0.75,273.15 + 6.5; 1,273.15 + 9])
    "Temperature at tank top"
equation
  connect(TTanTop.y[1], tanSta.TTan[1])
  connect(TTanBot.y[1], tanSta.TTan[2])
end TankStatus;


within Buildings.DHC.Plants.Cooling.Examples;
model ElectricChillerParallel
  "Example to test the chiller cooling plant"
  extends Modelica.Icons.Example;
  package Medium=Buildings.Media.Water
    "Medium model for water";
  // chiller and cooling tower
  replaceable parameter Buildings.Fluid.Chillers.Data.ElectricEIR.ElectricEIRChiller_York_YT_1055kW_5_96COP_Vanes perChi
    "Performance data of chiller";
  parameter Modelica.Units.SI.MassFlowRate mCHW_flow_nominal=18.3
    "Nominal chilled water mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate mCW_flow_nominal=34.7
    "Nominal condenser water mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dpCHW_nominal=44.8*1000
    "Nominal chilled water side pressure";
  parameter Modelica.Units.SI.PressureDifference dpCW_nominal=46.2*1000
    "Nominal condenser water side pressure";
  parameter Modelica.Units.SI.Power QChi_nominal=mCHW_flow_nominal*4200*(6.67
       - 18.56) "Nominal cooling capaciaty (Negative means cooling)";
  parameter Modelica.Units.SI.MassFlowRate mMin_flow=mCHW_flow_nominal*0.1
    "Minimum mass flow rate of single chiller";
  parameter Modelica.Units.SI.TemperatureDifference dTApp=3
    "Approach temperature";
  parameter Modelica.Units.SI.Power PFan_nominal=5000 "Fan power";
  // control settings
  parameter Modelica.Units.SI.Temperature TCHWSet=273.15 + 8
    "Chilled water temperature setpoint";
  parameter Modelica.Units.SI.Time tWai=30 "Waiting time";
  // pumps
  parameter Buildings.Fluid.Movers.Data.Generic perCHWPum(
    pressure=Buildings.Fluid.Movers.BaseClasses.Characteristics.flowParameters(
      V_flow=mCHW_flow_nominal/1000*{0.2,0.6,0.8,1.0},
      dp=(dpCHW_nominal+18000+30000)*{1,0.8,0.6,0.2}))
    "Performance data for chilled water pumps";
  parameter Buildings.Fluid.Movers.Data.Generic perCWPum(
    pressure=Buildings.Fluid.Movers.BaseClasses.Characteristics.flowParameters(
      V_flow=mCW_flow_nominal/1000*{0.2,0.6,1.0,1.2},
      dp=(2*dpCW_nominal+60000+6000)*{1,0.8,0.6,0.2}))
    "Performance data for condenser water pumps";
  parameter Modelica.Units.SI.Pressure dpCHWPumVal_nominal=6000
    "Nominal pressure drop of chilled water pump valve";
  parameter Modelica.Units.SI.Pressure dpCWPumVal_nominal=6000
    "Nominal pressure drop of chilled water pump valve";
  parameter Modelica.Units.SI.PressureDifference dpCooTowVal_nominal=6000
    "Nominal pressure difference of the cooling tower valve";
  replaceable Buildings.DHC.Plants.Cooling.ElectricChillerParallel pla(
    perChi=perChi,
    dTApp=dTApp,
    perCHWPum=perCHWPum,
    perCWPum=perCWPum,
    mCHW_flow_nominal=mCHW_flow_nominal,
    dpCHW_nominal=dpCHW_nominal,
    QChi_nominal=QChi_nominal,
    mMin_flow=mMin_flow,
    mCW_flow_nominal=mCW_flow_nominal,
    dpCW_nominal=dpCW_nominal,
    TAirInWB_nominal=298.7,
    TCW_nominal=308.15,
    dT_nominal=5.56,
    TMin=288.15,
    PFan_nominal=PFan_nominal,
    dpCooTowVal_nominal=dpCooTowVal_nominal,
    dpCHWPumVal_nominal=dpCHWPumVal_nominal,
    dpCWPumVal_nominal=dpCWPumVal_nominal,
    tWai=tWai,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "District cooling plant"
  Buildings.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    final computeWetBulbTemperature=true,
    filNam=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data"
  Modelica.Blocks.Sources.BooleanConstant on
    "On signal of the plant"
  Modelica.Blocks.Sources.Constant TCHWSupSet(
    k=TCHWSet)
    "Chilled water supply temperature setpoint"
  Fluid.MixingVolumes.MixingVolume vol(
    nPorts=2,
    redeclare package Medium=Medium,
    m_flow_nominal=pla.numChi*mCHW_flow_nominal,
    V=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volume"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow fixHeaFlo(
    T_ref=293.15)
    "Fixed heat flow rate"
  Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=pla.numChi*mCHW_flow_nominal,
    dp_nominal(displayUnit="kPa") = 60000)
    "Flow resistance"
  Modelica.Blocks.Sources.Sine loaVar(
    amplitude=913865,
    f=1/126900,
    offset=913865,
    startTime(displayUnit="h") = 21600) "Variable demand load"
equation
  connect(fixHeaFlo.port,vol.heatPort)
  connect(vol.ports[1], res.port_a)
  connect(res.port_b, pla.port_aSerCoo)
  connect(on.y,pla.on)
  connect(weaDat.weaBus,pla.weaBus)
  connect(fixHeaFlo.Q_flow,loaVar. y)
  connect(pla.port_bSerCoo, vol.ports[2])
  connect(pla.TCHWSupSet, TCHWSupSet.y)
end ElectricChillerParallel;


within Buildings.DHC.Plants.Cooling;
package Examples "Example models integrating multiple components"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


within Buildings.DHC.Plants.Cooling.Examples;
model StoragePlantDualSource
  "Idealised district system model with two sources and three users"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Water "Medium model for CHW";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate, slightly larger than needed by one user load";
  parameter Modelica.Units.SI.MassFlowRate mTan_flow_nominal=1
    "Nominal mass flow rate for CHW tank branch"
  parameter Modelica.Units.SI.MassFlowRate mChi_flow_nominal=1
    "Nominal mass flow rate for CHW chiller branch"
  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    final displayUnit="Pa")=
     300000
    "Nominal pressure difference";
  parameter Modelica.Units.SI.Temperature T_CHWR_nominal(
    final displayUnit="degC")=
     12+273.15
    "Nominal temperature of CHW return";
  parameter Modelica.Units.SI.Temperature T_CHWS_nominal(
    final displayUnit="degC")=
     7+273.15
    "Nominal temperature of CHW supply";

// First plant: chiller only
  Buildings.Fluid.Sources.PropertySource_T chi1(
    redeclare final package Medium = Medium,
    final use_T_in=true) "Chiller 1 represented by an ideal temperature source"
  Modelica.Blocks.Sources.Constant TSet1(k=T_CHWS_nominal)
    "Constant CHW leaving temperature"
  Buildings.Fluid.Movers.Preconfigured.SpeedControlled_y pumSup1(
    redeclare final package Medium = Medium,
    final addPowerToMedium=false,
    final m_flow_nominal=m_flow_nominal,
    final dp_nominal=dp_nominal) "CHW supply pump for chi1"
  Buildings.Controls.Continuous.LimPID conPI_pumChi1(
    final controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=0.2,
    Ti=10,
    final reverseActing=true) "PI controller"
  Buildings.DHC.Plants.Cooling.Controls.SelectMin selMin_dp(nin=3)
    "Min of pressure heads with the signal from storage plant optionally used"

// Second plant: chiller and tank
  Buildings.DHC.Plants.Cooling.StoragePlant stoPla(redeclare
      final package Medium = Medium,
    mTan_flow_nominal=mTan_flow_nominal,
    mChi_flow_nominal=mChi_flow_nominal,
    dpPum_nominal=dp_nominal,
    dpVal_nominal=0.5*dp_nominal,
    T_CHWS_nominal=T_CHWS_nominal,
    T_CHWR_nominal=T_CHWR_nominal)                  "Storage plant"
  Buildings.Fluid.Sources.Boundary_pT bou(
    p(final displayUnit="Pa") = 101325 + dp_nominal,
    redeclare final package Medium = Medium,
    nPorts=1) "Pressure boundary"
  Buildings.Fluid.Sources.PropertySource_T chi2(
    redeclare final package Medium = Medium,
    final use_T_in=true) "Chiller represented by an ideal temperature source"
  Modelica.Blocks.Sources.Constant TSet2(final k=T_CHWS_nominal)
    "Constant CHW leaving temperature"
  Modelica.Blocks.Math.Gain gaiStoPla(final k=1/stoPla.dpVal_nominal)
    "Gain to normalise dp measurement"

// Users
  Buildings.DHC.Plants.Cooling.BaseClasses.IdealUser ideUse1(
    redeclare final package Medium = Medium,
    final m_flow_nominal=0.6*m_flow_nominal,
    dp_nominal=0.2*dp_nominal,
    final T_CHWR_nominal=T_CHWR_nominal) "Ideal user"
  Buildings.DHC.Plants.Cooling.BaseClasses.IdealUser ideUse2(
    redeclare final package Medium = Medium,
    final m_flow_nominal=0.65*m_flow_nominal,
    dp_nominal=0.2*dp_nominal,
    final T_CHWR_nominal=T_CHWR_nominal) "Ideal user"
  Buildings.DHC.Plants.Cooling.BaseClasses.IdealUser ideUse3(
    redeclare final package Medium = Medium,
    final m_flow_nominal=0.65*m_flow_nominal,
    dp_nominal=0.2*dp_nominal,
    final T_CHWR_nominal=T_CHWR_nominal) "Ideal user"
  Modelica.Blocks.Sources.Constant set_dpUse(final k=1)
    "Normalized consumer differential pressure setpoint"
  Modelica.Blocks.Sources.TimeTable mLoa1_flow(table=[0,0; 1800,0; 1800,ideUse1.m_flow_nominal;
        7000,ideUse1.m_flow_nominal; 7000,0; 9000,0])
    "Cooling load of user 1 represented by flow rate"
  Modelica.Blocks.Sources.TimeTable mLoa2_flow(table=[0,0; 3500,0; 3500,ideUse2.m_flow_nominal;
        6500,ideUse2.m_flow_nominal; 6500,0; 9000,0])
    "Cooling load of user 2 represented by flow rate"
  Modelica.Blocks.Sources.TimeTable mLoa3_flow(table=[0,0; 4500,0; 4500,ideUse3.m_flow_nominal;
        6000,ideUse3.m_flow_nominal; 6000,0; 9000,0])
    "Cooling load of user 3 represented by flow rate"
  Modelica.Blocks.Math.Gain gaiUse1(final k=1/ideUse1.dp_nominal)
    "Gain to normalise dp measurement"
  Modelica.Blocks.Math.Gain gaiUse2(final k=1/ideUse2.dp_nominal)
    "Gain to normalise dp measurement"
  Modelica.Blocks.Math.Gain gaiUse3(final k=1/ideUse3.dp_nominal)
    "Gain to normalise dp measurement"

// District pipe network
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelJunctions
    parJunPla1(
    redeclare final package Medium = Medium,
    T1_start=T_CHWS_nominal,
    T2_start=T_CHWR_nominal,
    m_flow_nominal = 2*m_flow_nominal)
    "Parallel junctions for breaking algebraic loops"
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelJunctions
    parJunUse2(
    redeclare final package Medium = Medium,
    T1_start=T_CHWR_nominal,
    T2_start=T_CHWS_nominal,
    m_flow_nominal = 2*m_flow_nominal)
    "Parallel junctions for breaking algebraic loops"
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelJunctions
    parJunPla2(
    redeclare final package Medium = Medium,
    T1_start=T_CHWS_nominal,
    T2_start=T_CHWR_nominal,
    m_flow_nominal = 2*m_flow_nominal)
    "Parallel junctions for breaking algebraic loops"
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelPipes
    parPipS1U1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=0.15*dp_nominal) "Parallel pipes"
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelPipes
    parPipS1U2(
    redeclare package Medium = Medium,
    m_flow_nominal=2*m_flow_nominal,
    dp_nominal=0.15*dp_nominal) "Parallel pipes"
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelPipes
    parPipS2U2(
    redeclare package Medium = Medium,
    m_flow_nominal=2*m_flow_nominal,
    dp_nominal=0.15*dp_nominal) "Parallel pipes"
  Buildings.DHC.Plants.Cooling.BaseClasses.ParallelPipes
    parPipS2U3(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=0.15*dp_nominal) "Parallel pipes"

  Modelica.Blocks.Routing.Multiplex muxDp(n=3) "Multiplexer block for routing"
  Buildings.Controls.OBC.CDL.Reals.MultiMax mulMax_yVal_actual(nin=3)
    "Position of the most open user control valve"
  Modelica.Blocks.Sources.IntegerTable com(table=[0,2; 200,1; 3000,2; 4000,3;
        6000,2; 7500,1])
    "Command: 1 = charge tank, 2 = no command, 3 = discharge from tank"
  Modelica.Blocks.Sources.BooleanTable chiEnaSta(table={0,6000}, startValue=
        false) "Chiller enable status, true if chiller is enabled"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hys_yVal_actual(uLow=0.05,
      uHigh=0.5) "Hysteresis for user control valve position"

equation
  connect(set_dpUse.y,conPI_pumChi1.u_s)
  connect(mLoa1_flow.y, ideUse1.mPre_flow)
  connect(mLoa2_flow.y, ideUse2.mPre_flow)
  connect(mLoa3_flow.y, ideUse3.mPre_flow)
  connect(ideUse1.dp, gaiUse1.u)
  connect(ideUse2.dp, gaiUse2.u)
  connect(ideUse3.dp, gaiUse3.u)
  connect(parJunUse2.port_c2, ideUse2.port_a)
  connect(ideUse2.port_b,parJunUse2.port_c1)
  connect(parPipS1U1.port_b2, parJunPla1.port_a1)
  connect(parJunPla1.port_b2, parPipS1U1.port_a1)
  connect(parJunPla1.port_b1, parPipS1U2.port_a2)
  connect(parPipS1U2.port_b2, parJunUse2.port_a2)
  connect(parJunUse2.port_b1, parPipS1U2.port_a1)
  connect(parPipS1U2.port_b1, parJunPla1.port_a2)
  connect(parJunUse2.port_b2, parPipS2U2.port_a2)
  connect(parPipS2U2.port_b1, parJunUse2.port_a1)
  connect(parPipS2U2.port_a1, parJunPla2.port_b2)
  connect(parJunPla2.port_a1, parPipS2U2.port_b2)
  connect(parPipS2U3.port_a2, parJunPla2.port_b1)
  connect(parJunPla2.port_a2, parPipS2U3.port_b1)
  connect(gaiUse1.y, muxDp.u[1])
  connect(gaiUse2.y, muxDp.u[2])
  connect(gaiUse3.y, muxDp.u[3])
  connect(ideUse1.yVal_actual, mulMax_yVal_actual.u[1])
  connect(ideUse2.yVal_actual, mulMax_yVal_actual.u[2])
  connect(ideUse3.yVal_actual, mulMax_yVal_actual.u[3])
  connect(com.y, stoPla.com)
  connect(chiEnaSta.y, stoPla.chiEnaSta)
  connect(hys_yVal_actual.y, stoPla.hasLoa)
  connect(pumSup1.port_b, parJunPla1.port_c1)
  connect(conPI_pumChi1.y, pumSup1.y)
  connect(conPI_pumChi1.y, stoPla.yPum)
  connect(pumSup1.port_a, chi1.port_b)
  connect(chi1.port_a, parJunPla1.port_c2)
  connect(TSet1.y, chi1.T_in)
  connect(TSet2.y, chi2.T_in)
  connect(stoPla.port_b1, parJunPla2.port_c1)
  connect(parJunPla2.port_c2, stoPla.port_a2)
  connect(stoPla.port_b2, chi2.port_a)
  connect(chi2.port_b, stoPla.port_a1)
  connect(bou.ports[1], stoPla.port_a2)
  connect(parPipS1U1.port_a2, ideUse1.port_a)
  connect(ideUse1.port_b, parPipS1U1.port_b1)
  connect(parPipS2U3.port_b2, ideUse3.port_a)
  connect(parPipS2U3.port_a1, ideUse3.port_b)
  connect(stoPla.dp, gaiStoPla.u)
  connect(selMin_dp.y, conPI_pumChi1.u_m)
  connect(gaiStoPla.y, selMin_dp.dpStoPla)
  connect(stoPla.isChaRem, selMin_dp.isChaRem)
  connect(muxDp.y, selMin_dp.dpUse[1:3])
  connect(mulMax_yVal_actual.y, hys_yVal_actual.u)
end StoragePlantDualSource;


within Buildings.DHC.Plants.Cooling.Subsystems;
model CoolingTowersParallel
  "Multiple identical cooling towers in parallel connection"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    redeclare replaceable package Medium=Buildings.Media.Water);
  parameter Integer num(
    final min=1)=2
    "Number of cooling towers";
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"
  parameter Modelica.Units.SI.PressureDifference dp_nominal
    "Nominal pressure difference of the tower"
  parameter Modelica.Units.SI.PressureDifference dpValve_nominal
    "Nominal pressure difference of the valve";
  parameter Real ratWatAir_nominal(
    final min=0,
    final unit="1")=0.625
    "Design water-to-air ratio"
  parameter Modelica.Units.SI.Temperature TAirInWB_nominal
    "Nominal outdoor (air inlet) wetbulb temperature"
  parameter Modelica.Units.SI.Temperature TWatIn_nominal
    "Nominal water inlet temperature"
  parameter Modelica.Units.SI.TemperatureDifference dT_nominal
    "Temperature difference between inlet and outlet of the tower"
  parameter Modelica.Units.SI.Power PFan_nominal "Fan power"
  parameter Boolean use_strokeTime=true
    "Set to true to continuously open and close valve"
  parameter Modelica.Units.SI.Time strokeTime=30
    "Time needed to change valve position from 0 to 1"
  Modelica.Blocks.Interfaces.BooleanInput on[num]
    "On signal for cooling towers"
  Modelica.Blocks.Interfaces.RealInput uFanSpe(
    final unit="1")
    "Fan speed control signal"
  Modelica.Blocks.Interfaces.RealInput TWetBul(
    final unit="K",
    displayUnit="degC")
    "Entering air wetbulb temperature"
  Modelica.Blocks.Interfaces.RealOutput PFan[num](
    each final quantity="Power",
    each final unit="W")
    "Electric power consumed by fan"
  Modelica.Blocks.Interfaces.RealOutput TLvg(
    final unit="K",
    displayUnit="degC")
    "Leaving water temperature"
  replaceable Buildings.Fluid.HeatExchangers.CoolingTowers.Merkel cooTow[num](
    each final allowFlowReversal=allowFlowReversal,
    each final m_flow_small=m_flow_small,
    each final ratWatAir_nominal=ratWatAir_nominal,
    each final TAirInWB_nominal=TAirInWB_nominal,
    each final TWatIn_nominal=TWatIn_nominal,
    each final TWatOut_nominal=TWatIn_nominal-dT_nominal,
    each final PFan_nominal=PFan_nominal,
    each final dp_nominal=0)
    constrainedby
    Buildings.Fluid.HeatExchangers.CoolingTowers.BaseClasses.CoolingTowerVariableSpeed(
      redeclare each final package Medium=Medium,
      each final show_T=show_T,
      each final m_flow_nominal=m_flow_nominal,
      each final energyDynamics=energyDynamics)
    "Cooling tower type"
  Buildings.Fluid.Actuators.Valves.TwoWayEqualPercentage val[num](
    redeclare each final package Medium=Medium,
    each final allowFlowReversal=allowFlowReversal,
    each final m_flow_nominal=m_flow_nominal,
    each final dpValve_nominal=dpValve_nominal,
    each final use_strokeTime=use_strokeTime,
    each strokeTime=strokeTime,
    each final dpFixed_nominal=dp_nominal)
    "Cooling tower valves"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea[num]
    "Boolean signal to real signal"
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_small=m_flow_small,
    final T_start=Medium.T_default)

equation
  for i in 1:num loop
    connect(port_a,val[i].port_a)
    connect(val[i].port_b,cooTow[i].port_a)
    connect(uFanSpe,cooTow[i].y)
    connect(TWetBul,cooTow[i].TAir)
    connect(cooTow[i].PFan,PFan[i])
    connect(cooTow[i].port_b, senTem.port_a)
  end for;
  connect(on,booToRea.u)
  connect(senTem.port_b, port_b)
  connect(senTem.T, TLvg)
  connect(booToRea.y, val.y)
end CoolingTowersParallel;


Cooling tower system with bypass valve

within Buildings.DHC.Plants.Cooling.Subsystems;
model CoolingTowersWithBypass
  "Cooling tower system with bypass valve"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    redeclare replaceable package Medium=Buildings.Media.Water);
  parameter Integer num(
    final min=1)=2
    "Number of cooling towers";
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"
  parameter Boolean use_strokeTime=true
    "Set to true to continuously open and close valve"
  parameter Modelica.Units.SI.Time strokeTime=30
    "Time needed to change valve position from 0 to 1"
  parameter Modelica.Units.SI.Pressure dp_nominal
    "Nominal pressure difference of the tower"
  parameter Modelica.Units.SI.PressureDifference dpValve_nominal
    "Nominal pressure difference of the valve";
  parameter Real ratWatAir_nominal(
    final min=0,
    final unit="1")=0.625
    "Design water-to-air ratio"
  parameter Modelica.Units.SI.Temperature TAirInWB_nominal
    "Nominal outdoor (air inlet) wetbulb temperature"
  parameter Modelica.Units.SI.Temperature TWatIn_nominal
    "Nominal water inlet temperature"
  parameter Modelica.Units.SI.TemperatureDifference dT_nominal
    "Temperature difference between inlet and outlet of the tower"
  parameter Modelica.Units.SI.Power PFan_nominal "Fan power"
  parameter Modelica.Units.SI.TemperatureDifference dTApp=3
    "Approach temperature"
  parameter Modelica.Units.SI.Temperature TMin
    "Minimum allowed water temperature entering chiller"
  parameter Modelica.Blocks.Types.SimpleController controllerType=Modelica.Blocks.Types.SimpleController.PI
    "Type of fan speed controller"
  parameter Real k(
    final unit="1",
    final min=0)=1
    "Gain of the tower PID controller"
  parameter Modelica.Units.SI.Time Ti(final min=Modelica.Constants.small) = 60
    "Integrator time constant of the tower PID controller"
  parameter Modelica.Units.SI.Time Td(final min=0) = 0.1
    "Derivative time constant of the tower PID controller"
  Modelica.Blocks.Interfaces.BooleanInput on[num]
    "On signal for cooling towers"
  Modelica.Blocks.Interfaces.RealInput TWetBul(
    final unit="K",
    displayUnit="degC")
    "Entering air wetbulb temperature"
  Modelica.Blocks.Interfaces.RealOutput PFan[num](
    each final quantity="Power",
    each final unit="W")
    "Electric power consumed by fan"
  Modelica.Blocks.Interfaces.RealOutput TLvg(
    final unit="K",
    displayUnit="degC")
    "Leaving water temperature"
  Buildings.DHC.Plants.Cooling.Subsystems.CoolingTowersParallel cooTowSys(
    redeclare final package Medium=Medium,
    final use_strokeTime=false,
    final num=num,
    final show_T=show_T,
    final m_flow_small=m_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal/num,
    final dpValve_nominal=dpValve_nominal,
    final dp_nominal=dp_nominal,
    final ratWatAir_nominal=ratWatAir_nominal,
    final TAirInWB_nominal=TAirInWB_nominal,
    final TWatIn_nominal=TWatIn_nominal,
    final dT_nominal=dT_nominal,
    final PFan_nominal=PFan_nominal,
    final energyDynamics=energyDynamics)
    "Cooling tower system"
  Buildings.Fluid.Actuators.Valves.TwoWayEqualPercentage valByp(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal,
    final show_T=show_T,
    final dpValve_nominal=dpValve_nominal,
    final strokeTime=strokeTime,
    final use_strokeTime=use_strokeTime)
    "Condenser water bypass valve"
  Buildings.Fluid.Sensors.TemperatureTwoPort senTCWSup(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_small=m_flow_nominal,
    final T_start=Medium.T_default)
    "Temperature sensor"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetByp(
    final k=TMin)
    "Bypass loop temperature setpoint"
  Buildings.Controls.OBC.CDL.Reals.PIDWithReset bypValCon(
    u_s(
      final unit="K",
      displayUnit="degC"),
    u_m(
      final unit="K",
      displayUnit="degC"),
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    final k=k,
    final Ti=Ti,
    final y_reset=0)
    "Bypass valve controller"
  Buildings.Controls.OBC.CDL.Reals.PID cooTowSpeCon(
    u_s(
      final unit="K",
      displayUnit="degC"),
    u_m(
      final unit="K",
      displayUnit="degC"),
    final reverseActing=false,
    final controllerType=controllerType,
    final k=k,
    final Ti=Ti)
    "Cooling tower fan speed controller"
  Buildings.Controls.OBC.CDL.Reals.Switch swi
    "Output the input of higher value"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hys(
    uLow=TMin-0.1,
    uHigh=TMin+0.1)
    "Compare if (TWetBul+dTApp) is greater than TMin"
  Buildings.Controls.OBC.CDL.Reals.AddParameter addPar(
    p=dTApp)
    "Add approach temperature on top of wetbulb temperature"
  Buildings.Fluid.FixedResistances.Junction jun(
  redeclare final package Medium=Medium,
    m_flow_nominal=m_flow_nominal .* {1,-1,-1},
    dp_nominal={0,0,0},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Upstream mixing point of cooTowSys and valByp"
protected
  parameter Boolean enableTi=
    controllerType == Modelica.Blocks.Types.SimpleController.PI or
    controllerType == Modelica.Blocks.Types.SimpleController.PID
    "Flag to enable controller parameter Ti";
  parameter Boolean enableTd=
    controllerType == Modelica.Blocks.Types.SimpleController.PD or
    controllerType == Modelica.Blocks.Types.SimpleController.PID
    "Flag to enable controller parameter Td";
equation
  connect(cooTowSys.TWetBul,TWetBul)
  connect(cooTowSys.port_b,senTCWSup.port_a)
  connect(senTCWSup.port_b,port_b)
  connect(TSetByp.y,bypValCon.u_s)
  connect(senTCWSup.T,bypValCon.u_m)
  connect(valByp.port_b,senTCWSup.port_a)
  connect(cooTowSpeCon.y,cooTowSys.uFanSpe)
  connect(cooTowSys.PFan,PFan)
  connect(bypValCon.y,valByp.y)
  connect(senTCWSup.T,cooTowSpeCon.u_m)
  connect(hys.y, swi.u2)
  connect(cooTowSpeCon.u_s,swi.y)
  connect(TSetByp.y,swi.u3)
  connect(TWetBul,addPar.u)
  connect(addPar.y,swi.u1)
  connect(on,cooTowSys.on)
  connect(on[1],bypValCon.trigger)
  connect(port_a, jun.port_1)
  connect(cooTowSys.port_a, jun.port_2)
  connect(valByp.port_a, jun.port_3)
  connect(addPar.y, hys.u)
  connect(senTCWSup.T, TLvg)
end CoolingTowersWithBypass;


within Buildings.DHC.Plants.Cooling;
package Subsystems "Package of models for subsystems of cooling plants"
  extends Modelica.Icons.VariantsPackage;
end Subsystems;


Example model for parallel cooling tower model. This model validates the parallel connected cooling tower subsystem in

Buildings.DHC.Plants.Cooling.Subsystems.CoolingTowersParallel.

within Buildings.DHC.Plants.Cooling.Subsystems.Examples;
model CoolingTowersParallel
  "Example model for parallel cooling tower model"
  extends Modelica.Icons.Example;
  extends
    Buildings.DHC.Plants.Cooling.Subsystems.Examples.BaseClasses.PartialCoolingTowersSubsystem(
    redeclare Buildings.DHC.Plants.Cooling.Subsystems.CoolingTowersParallel tow(
      TAirInWB_nominal=273.15+25.55,
      TWatIn_nominal=273.15+35,
      dT_nominal=5.56,
      dpValve_nominal = 6000,
      dp_nominal = 6000,
      PFan_nominal=4800,
      use_strokeTime=false),
    weaDat(final computeWetBulbTemperature=true));

   Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetLea(
    k=273.15+18)
    "Setpoint for leaving temperature"
  Buildings.Controls.OBC.CDL.Reals.PIDWithReset conFan(
    k=1,
    Ti=60,
    Td=10,
    reverseActing=false,
    u_s(
      unit="K",
      displayUnit="degC"),
    u_m(
      unit="K",
      displayUnit="degC"))
    "Controller for tower fan"
equation
  connect(TSetLea.y, conFan.u_s)
  connect(tow.TLvg, conFan.u_m)
  connect(conFan.y, tow.uFanSpe)
  connect(weaBus.TWetBul, tow.TWetBul)
  connect(hys.y, tow.on[1])
  connect(hys.y, tow.on[2])
  connect(hys.y, conFan.trigger)
end CoolingTowersParallel;


Example model for parallel cooling towers with bypass valve. This model validates the parallel connected cooling tower subsystem in

Buildings.DHC.Plants.Cooling.Subsystems.CoolingTowerWithBypass.

within Buildings.DHC.Plants.Cooling.Subsystems.Examples;
model CoolingTowersWithBypass
  "Example model for parallel cooling towers with bypass valve"
  extends Modelica.Icons.Example;
  extends Buildings.DHC.Plants.Cooling.Subsystems.Examples.BaseClasses.PartialCoolingTowersSubsystem(
    redeclare Buildings.DHC.Plants.Cooling.Subsystems.CoolingTowersWithBypass tow(
      dpValve_nominal = 6000,
      dp_nominal(displayUnit="Pa") = 6000,
      TAirInWB_nominal=273.15 + 25.55,
      TWatIn_nominal=273.15 + 35,
      dT_nominal=5.56,
      PFan_nominal=4800,
      TMin=TMin,
      controllerType=Modelica.Blocks.Types.SimpleController.PI),
    weaDat(final computeWetBulbTemperature=true));
  parameter Modelica.Units.SI.Temperature TMin=273.15 + 10
    "Minimum allowed water temperature entering chiller";
equation
  connect(weaBus.TWetBul, tow.TWetBul)
  connect(hys.y, tow.on[1])
  connect(hys.y, tow.on[2])
end CoolingTowersWithBypass;


within Buildings.DHC.Plants.Cooling.Subsystems;
package Examples "Example models integrating multiple components"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Package with base classes

within Buildings.DHC.Plants.Cooling.Subsystems.Examples;
package BaseClasses "Package with base classes"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


within Buildings.DHC.Plants.Cooling.Subsystems.Examples.BaseClasses;
partial model PartialCoolingTowersSubsystem
  "Partial class for test models of subsystems"

  package Medium_W = Buildings.Media.Water "Medium model for water";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.5
    "Design water flow rate"

  replaceable
    Buildings.Fluid.Interfaces.PartialTwoPortInterface tow
    constrainedby Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    redeclare package Medium = Medium_W,
    m_flow_nominal=m_flow_nominal,
    show_T=true) "Cooling tower"

  Buildings.Fluid.Movers.FlowControlled_m_flow pum(
    redeclare package Medium = Medium_W,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nominalValuesDefineDefaultPressureCurve=true,
    use_riseTime=false) "Pump for condenser water loop"

  Buildings.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(filNam=
    Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))

  Buildings.BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"

  Buildings.Controls.OBC.CDL.Reals.Switch swi "Control switch for chilled water pump"

  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSwi(
    k=273.15 + 22,
    y(unit="K",
      displayUnit="degC"))
    "Switch temperature for switching tower pump on"

  Buildings.Controls.OBC.CDL.Reals.Sources.Constant zer(k=0) "Zero flow rate"

  Buildings.Controls.OBC.CDL.Reals.Sources.Constant m_flow(k=m_flow_nominal)
    "Water flow rate"

  Buildings.Fluid.MixingVolumes.MixingVolume vol(nPorts=3,
    redeclare package Medium = Medium_W,
    m_flow_nominal=m_flow_nominal,
    V=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)

  Buildings.Fluid.Sources.Boundary_pT exp(
    redeclare package Medium = Medium_W,
    nPorts=1) "Expansion vessel"

  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow fixHeaFlo(
    Q_flow=0.5*m_flow_nominal*4200*5) "Fixed heat flow rate"

  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TVol
    "Water temperature"

  Buildings.Fluid.Sensors.TemperatureTwoPort TEnt(
    redeclare package Medium = Medium_W,
    m_flow_nominal=m_flow_nominal)
    "Water entering temperature"

  Buildings.Controls.OBC.CDL.Reals.Hysteresis hys(
    final uLow=-1,
    final uHigh=1)
    "Pump on-off control"

  Buildings.Controls.OBC.CDL.Reals.Subtract sub "Inputs different"

equation
  connect(weaDat.weaBus, weaBus)
  connect(zer.y, swi.u3)
  connect(m_flow.y, swi.u1)
  connect(vol.ports[1], pum.port_a)
  connect(fixHeaFlo.port, vol.heatPort)
  connect(vol.heatPort, TVol.port)
  connect(tow.port_b, vol.ports[2])
  connect(swi.y, pum.m_flow_in)
  connect(exp.ports[1], vol.ports[3])
  connect(pum.port_b, TEnt.port_a)
  connect(TEnt.port_b, tow.port_a)
  connect(sub.y, hys.u)
  connect(TVol.T, sub.u1)
  connect(TSwi.y, sub.u2)
  connect(hys.y, swi.u2)
end PartialCoolingTowersSubsystem;


within Buildings.DHC.Plants;
package Heating "Package of models for central heating production"
  extends Modelica.Icons.VariantsPackage;
end Heating;


Model for sewage heat recovery plant

within Buildings.DHC.Plants.Heating;
model SewageHeatRecovery
  "Model for sewage heat recovery plant"
  extends Buildings.DHC.Plants.BaseClasses.PartialPlant(
    final have_fan=false,
    final have_pum=true,
    final have_eleHea=false,
    final have_eleCoo=false,
    final have_weaBus=false,
    final typ=Buildings.DHC.Types.DistrictSystemType.CombinedGeneration5);

  parameter Modelica.Units.SI.MassFlowRate mSew_flow_nominal
    "Sewage water nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate mDis_flow_nominal
    "District water nominal mass flow rate"
  parameter Modelica.Units.SI.PressureDifference dpSew_nominal
    "Sewage side pressure drop at nominal mass flow rate"
  parameter Modelica.Units.SI.PressureDifference dpDis_nominal
    "District side pressure drop at nominal mass flow rate"
  parameter Modelica.Units.SI.Efficiency epsHex "Heat exchanger effectiveness";
  // IO CONNECTORS
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TSewWat(
    final unit="K",
    displayUnit="degC")
    "Sewage water temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput mPum_flow(
    final unit="kg/s")
    "Pumps mass flow rate"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput dH_flow(final unit="W")
    "Variation of enthalpy flow rate across HX (leaving - entering)"
  // COMPONENTS
  Fluid.HeatExchangers.ConstantEffectiveness hex(
    redeclare final package Medium1=Medium,
    redeclare final package Medium2=Medium,
    final allowFlowReversal1=allowFlowReversal,
    final allowFlowReversal2=allowFlowReversal,
    final m1_flow_nominal=mSew_flow_nominal,
    final m2_flow_nominal=mDis_flow_nominal,
    final dp1_nominal=dpSew_nominal,
    final dp2_nominal=dpDis_nominal,
    final eps=epsHex)
    "Heat exchanger (primary is sewage water side)"
  Buildings.DHC.ETS.BaseClasses.Pump_m_flow pumDis(
    redeclare final package Medium=Medium,
    final m_flow_nominal=mDis_flow_nominal,
    final dp_nominal=dpDis_nominal,
    final allowFlowReversal=allowFlowReversal)
    "District water pump"
  Fluid.Sources.Boundary_pT souSew(
    redeclare final package Medium = Medium,
    final use_T_in=true,
    final nPorts=2)
    "Source of sewage water"
  Buildings.DHC.ETS.BaseClasses.Pump_m_flow pumSew(
    redeclare final package Medium=Medium,
    final m_flow_nominal=mSew_flow_nominal,
    final dp_nominal=dpSew_nominal,
    final allowFlowReversal=allowFlowReversal)
    "Sewage water pump"
  Fluid.Sensors.TemperatureTwoPort senTSewOut(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=mSew_flow_nominal,
    tau=0)
  Buildings.DHC.Networks.BaseClasses.DifferenceEnthalpyFlowRate senDifEntFlo(
    redeclare package Medium1 = Medium,
    final m_flow_nominal=mDis_flow_nominal)
    "Variation of enthalpy flow rate"
 Buildings.Controls.OBC.CDL.Reals.MultiSum sumPPum(nin=2) "Sum pump power"
equation
  connect(senTSewOut.port_b, souSew.ports[1])
  connect(souSew.ports[2], pumSew.port_a)
  connect(souSew.T_in, TSewWat)
  connect(mPum_flow, pumSew.m_flow_in)
  connect(pumSew.port_b, hex.port_a1)
  connect(hex.port_b1, senTSewOut.port_a)
  connect(mPum_flow, pumDis.m_flow_in)
  connect(senDifEntFlo.dH_flow, dH_flow)
  connect(senDifEntFlo.port_b2, hex.port_a2)
  connect(hex.port_b2, senDifEntFlo.port_a1)
  connect(port_aSerAmb, senDifEntFlo.port_a2)
  connect(senDifEntFlo.port_b1, pumDis.port_a)
  connect(pumDis.port_b, port_bSerAmb)
  connect(sumPPum.y, PPum)
  connect(pumSew.P, sumPPum.u[1])
  connect(pumDis.P, sumPPum.u[2])
end SewageHeatRecovery;


Package with models for steam plants

within Buildings.DHC.Plants;
package Steam "Package with models for steam plants"
  extends Modelica.Icons.VariantsPackage;
end Steam;


within Buildings.DHC.Plants.Steam;
model SingleBoiler "A generic steam plant with a single boiler that discharges
  saturated steam"
  extends Buildings.DHC.Plants.BaseClasses.PartialPlant(
    final typ=Buildings.DHC.Types.DistrictSystemType.HeatingGeneration1,
    redeclare replaceable package MediumHea_b=Buildings.Media.Steam,
    final have_fan=false,
    final have_pum=true,
    final nFue=1,
    final fue={fueBoi},
    final have_eleHea=false,
    final have_eleCoo=false,
    final have_weaBus=false);

  parameter Buildings.Fluid.Data.Fuels.Generic fueBoi=
    Buildings.Fluid.Data.Fuels.NaturalGasLowerHeatingValue()
    "Boiler fuel type"

  // Nominal values
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.Power Q_flow_nominal=m_flow_nominal*(
      MediumHea_b.specificEnthalpy(MediumHea_b.setState_pTX(
        p=pSteSet,
        T=TSat,
        X=MediumHea_b.X_default)) -
      Medium.specificEnthalpy(Medium.setState_pTX(
        p=pSteSet,
        T=TSat,
        X=Medium.X_default)))
    "Nominal heating power";

  // Setpoints
  parameter Modelica.Units.SI.AbsolutePressure pSteSet=300000
    "Steam pressure setpoint";
  final parameter Modelica.Units.SI.Temperature TSat=
    MediumHea_b.saturationTemperature(pSteSet)
    "Saturation temperature at pressure setpoint";
  parameter Modelica.Units.SI.AbsolutePressure pTanFW=101325
    "Pressure of feedwater tank";
  parameter Modelica.Units.SI.Volume VBoiWatSet=VBoi/2
    "Setpoint for liquid water volume in the boiler";

  // System sizing
  parameter Modelica.Units.SI.Volume VBoi=3
    "Total drum volume of steam boiler";
  parameter Real boiSca = 1.25 "Boiler heat capacity scaling factor";
  parameter Modelica.Units.SI.Mass mDry = 1.5E-3*Q_flow_nominal
    "Mass of boiler that will be lumped to water heat capacity"
  parameter Buildings.Fluid.Movers.Data.Generic per(
    pressure(
      V_flow=(m_flow_nominal/1000)*{0.4,0.6,0.8,1.0},
      dp=(pSteSet-pTanFW)*{1.34,1.27,1.17,1.0}))
    "Performance data for the feedwater pump";

  // Initial conditions
  parameter Modelica.Units.SI.Volume VTanFW_start=1
    "Setpoint for liquid water volume in the boiler"
  parameter Modelica.Media.Interfaces.Types.AbsolutePressure pBoi_start=pTanFW
    "Start value of boiler pressure"
  parameter Real yPum_start=0.7 "Initial value of pump speed"

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

  // Boiler controller
  parameter Modelica.Blocks.Types.SimpleController controllerTypeBoi=
    Modelica.Blocks.Types.SimpleController.PI "Type of controller"
  parameter Real kBoi(min=0) = 10 "Gain of controller"
  parameter Modelica.Units.SI.Time TiBoi(min=Modelica.Constants.small)=120
    "Time constant of Integrator block"
  parameter Modelica.Units.SI.Time TdBoi(min=0)=10
    "Time constant of Derivative block"
  parameter Real wpBoi(min=0) = 1 "Set-point weight for Proportional block (0..1)"
  parameter Real wdBoi(min=0) = 0 "Set-point weight for Derivative block (0..1)"
  parameter Real NiBoi(min=100*Modelica.Constants.eps) = 0.9
    "Ni*Ti is time constant of anti-windup compensation"
  parameter Real NdBoi(min=100*Modelica.Constants.eps) = 10
    "The higher Nd, the more ideal the derivative block"

  // Feedwater pump controller
  parameter Modelica.Blocks.Types.SimpleController controllerTypePum=
    Modelica.Blocks.Types.SimpleController.PI "Type of controller"
  parameter Real kPum(min=0) = 5 "Gain of controller"
  parameter Modelica.Units.SI.Time TiPum(min=Modelica.Constants.small)=120
    "Time constant of Integrator block"
  parameter Modelica.Units.SI.Time TdPum(min=0)=0.1
    "Time constant of Derivative block"
  parameter Real wpPum(min=0) = 1 "Set-point weight for Proportional block (0..1)"
  parameter Real wdPum(min=0) = 0 "Set-point weight for Derivative block (0..1)"
  parameter Real NiPum(min=100*Modelica.Constants.eps) = 0.9
    "Ni*Ti is time constant of anti-windup compensation"
  parameter Real NdPum(min=100*Modelica.Constants.eps) = 10
    "The higher Nd, the more ideal the derivative block"

  // Pump can have reverse flow at start up
  Buildings.Fluid.Movers.SpeedControlled_y pumFW(
    redeclare final package Medium = Medium,
    final energyDynamics=energyDynamics,
    p_start=pTanFW,
    final allowFlowReversal=allowFlowReversal,
    final per=per,
    final y_start=yPum_start)
    "Feedwater pump"

  // At start up, water leaves port_a, see Buildings.DHC.Examples.Steam.SingleBoiler
  Buildings.DHC.Plants.Steam.BaseClasses.BoilerPolynomial boi(
    redeclare final package MediumSte = MediumHea_b,
    redeclare final package MediumWat = Medium,
    final energyDynamics=energyDynamics,
    final massDynamics=massDynamics,
    final allowFlowReversal=allowFlowReversal,
    final p_start=pTanFW,
    fixed_p_start=true,
    final fue=fueBoi,
    final m_flow_nominal=m_flow_nominal,
    final dp_nominal=3000,
    final Q_flow_nominal=Q_flow_nominal*boiSca,
    final V=VBoi,
    final mDry=mDry)
    "Steam boiler"
  Buildings.Controls.Continuous.LimPID conPum(
    final controllerType=controllerTypePum,
    final k=kPum,
    final Ti=TiPum,
    final Td=TdPum,
    final wp=wpPum,
    final wd=wdPum,
    final Ni=NiPum,
    final Nd=NdPum,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=yPum_start)
    "Pump control"
  Modelica.Blocks.Math.Gain VNor(final k=1/VBoiWatSet)
    "Normalized volume setpoint"
  Buildings.Controls.Continuous.LimPID conBoi(
    final controllerType=controllerTypeBoi,
    final k=kBoi,
    final Ti=TiBoi,
    final Td=TdBoi,
    final wp=wpBoi,
    final wd=wdBoi,
    final Ni=NiBoi,
    final Nd=NdBoi)
    "Boiler control"
  Modelica.Blocks.Math.Gain PNor(final k=1/pSteSet)
    "Normalized pressure setpoint"
  Buildings.Fluid.Sensors.Pressure senPreSte(
    redeclare final package Medium = MediumHea_b)
    "Steam pressure sensor"
  Modelica.Blocks.Sources.Constant uni(final k=1) "Unitary"
  Buildings.Fluid.Storage.ExpansionVessel tanFW(
    redeclare final package Medium = Medium,
    final V_start=VTanFW_start,
    final p_start=pTanFW)
    "Feedwater tank"
  Buildings.Fluid.FixedResistances.CheckValve cheVal(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal,
    dpValve_nominal=6000,
    rhoStd=Medium.density_pTX(
        pSteSet,
        MediumHea_b.saturationTemperature(pSteSet),
        Medium.X_default))
    "Check valve"
equation
  connect(port_aSerHea, pumFW.port_a)
  connect(boi.port_b, port_bSerHea)
  connect(conPum.y, pumFW.y)
  connect(uni.y, conPum.u_s)
  connect(VNor.y, conPum.u_m)
  connect(boi.VLiq, VNor.u)
  connect(senPreSte.port, boi.port_b)
  connect(senPreSte.p, PNor.u)
  connect(PNor.y, conBoi.u_m)
  connect(conBoi.y, boi.y)
  connect(uni.y, conBoi.u_s)
  connect(QFue_flow, boi.QFueFlo)
  connect(PPum, pumFW.P)
  connect(tanFW.port_a, pumFW.port_a)
  connect(pumFW.port_b, cheVal.port_a)
  connect(cheVal.port_b, boi.port_a)
end SingleBoiler;