Controller for economizer. Economizer controller.

within Buildings.Air.Systems.SingleZone.VAV.BaseClasses;
model ControllerEconomizer "Controller for economizer"
  extends Modelica.Blocks.Icons.Block;

  parameter Buildings.Controls.OBC.CDL.Types.SimpleController controllerTypeEco=
    Buildings.Controls.OBC.CDL.Types.SimpleController.PI
    "Type of controller"
  parameter Real kEco(final unit="1/K")=0.1
    "Gain for economizer control signal"
  parameter Real TiEco(
    final unit="s",
    final quantity="Time")=900
    "Time constant of integrator block for economizer control signal"
  parameter Real TdEco(
    final unit="s",
    final quantity="Time")=0.1
    "Time constant of derivative block for economizer control signal"

  Modelica.Blocks.Interfaces.RealInput TMixSet(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Mixed air setpoint temperature"
  Modelica.Blocks.Interfaces.RealInput TMix(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Measured mixed air temperature"
  Modelica.Blocks.Interfaces.RealInput TOut(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Measured outside air temperature"
  Modelica.Blocks.Interfaces.BooleanInput cooSta "Cooling status"
  Modelica.Blocks.Interfaces.RealInput TRet(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Return air temperature"
  Modelica.Blocks.Interfaces.RealInput minOAFra(
    final min = 0,
    final max = 1,
    final unit="1")
    "Minimum outside air fraction"
  Modelica.Blocks.Interfaces.RealOutput yOutAirFra(
    final unit="1")
    "Control signal for outside air fraction"

  Modelica.Blocks.Nonlinear.VariableLimiter Limiter(
    final strict=true)
    "Signal limiter"
  Modelica.Blocks.Sources.Constant const(
    final k=1)
    "Constant output signal with value 1"
  Modelica.Blocks.Logical.Switch switch1 "Switch to select control output"
  Modelica.Blocks.MathBoolean.And and1(
    final nu=3) "Logical and"
  Controls.Continuous.LimPID con(
    final controllerType=controllerTypeEco,
    final k=kEco,
    final Ti=TiEco,
    final Td=TdEco,
    final reverseActing=false,
    final yMax=1,
    final yMin=0)
    "Controller"
  Modelica.Blocks.Math.Feedback feedback "Control error"
  Modelica.Blocks.Math.Feedback feedback1
  Controls.OBC.CDL.Reals.Hysteresis hysChiPla(
    final uLow=0.95,
    final uHigh=0.98)
    "Hysteresis with delay to switch on cooling"
  Modelica.Blocks.Logical.Or or1 "Saturated ecnomizer or no economizer"
  Modelica.Blocks.Interfaces.BooleanOutput yCoiSta "Cooling coil status"
  Modelica.Blocks.Logical.Not not1 "No economizer"

  Controls.OBC.CDL.Reals.Hysteresis hysCooPot(
    final uLow=0,
    final uHigh=0.5)
    "Hysteresis with delay to check for cooling potential of outside air"
  Controls.OBC.CDL.Logical.TrueFalseHold truFalHolCooPot(
    final trueHoldDuration=60*15)
    "True/false hold for cooling potential"
  Controls.OBC.CDL.Logical.TrueFalseHold truFalHolTMix(
    final trueHoldDuration=60*15)
    "True/false hold for mixing temperature"
  Controls.OBC.CDL.Reals.Hysteresis hysTMix(
    final uLow=-0.5,
    final uHigh=0.5)
    "Hysteresis with delay for mixed air temperature"
protected
  Controls.OBC.CDL.Logical.Pre pre1
    "Use left-limit of signal to break algebraic loop"
equation
  connect(Limiter.limit2, minOAFra)
  connect(const.y, Limiter.limit1)
  connect(minOAFra, switch1.u3)
  connect(switch1.y, Limiter.u)
  connect(con.u_s, TMixSet)
  connect(TMix, con.u_m)
  connect(con.y, switch1.u1)
  connect(TOut, feedback.u2)
  connect(feedback.u1, TMix)
  connect(Limiter.y, yOutAirFra)
  connect(feedback1.u1, TRet)
  connect(feedback1.u2,TOut)
  connect(Limiter.y, hysChiPla.u)
  connect(or1.y, yCoiSta)
  connect(not1.y, or1.u2)
  connect(hysChiPla.y, or1.u1)

  connect(feedback.y, hysTMix.u)
  connect(hysTMix.y, truFalHolTMix.u)
  connect(feedback1.y, hysCooPot.u)
  connect(hysCooPot.y, truFalHolCooPot.u)
  connect(truFalHolCooPot.y, and1.u[1])
  connect(truFalHolTMix.y, and1.u[2])
  connect(cooSta, and1.u[3])
  connect(and1.y, pre1.u)
  connect(pre1.y, switch1.u2)
  connect(pre1.y, not1.u)
end ControllerEconomizer;


Controller for heating coil and fan signal. Controller for heating coil and fan speed.

within Buildings.Air.Systems.SingleZone.VAV.BaseClasses;
model ControllerHeatingFan "Controller for heating coil and fan signal"
  extends Modelica.Blocks.Icons.Block;

  parameter Buildings.Controls.OBC.CDL.Types.SimpleController controllerTypeHea=
    Buildings.Controls.OBC.CDL.Types.SimpleController.PI
    "Type of controller"
  parameter Real kHea(final unit="1/K")=0.1
    "Gain for heating coil control signal"
  parameter Real TiHea(
    final unit="s",
    final quantity="Time")=900
    "Time constant of integrator block for heating coil control signal"
  parameter Real TdHea(
    final unit="s",
    final quantity="Time")=0.1
    "Time constant of derivative block for heating coil control signal"
  parameter Buildings.Controls.OBC.CDL.Types.SimpleController controllerTypeFan=
    Buildings.Controls.OBC.CDL.Types.SimpleController.PI
    "Type of controller"
  parameter Real kFan(final unit="1/K")=0.1
    "Gain for fan signal"
  parameter Real TiFan(
    final unit="s",
    final quantity="Time")=900
    "Time constant of integrator block for fan signal"
  parameter Real TdFan(
    final unit="s",
    final quantity="Time")=0.1
    "Time constant of derivative block for fan signal"
  parameter Real minAirFlo(
    final min=0,
    final max=1,
    final unit="1")
    "Minimum airflow fraction of system";

  Modelica.Blocks.Interfaces.RealInput TSetRooCoo(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Zone cooling setpoint"
  Modelica.Blocks.Interfaces.RealInput TRoo(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Zone temperature measurement"
  Modelica.Blocks.Interfaces.RealInput TSetRooHea(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Zone heating setpoint"
  Modelica.Blocks.Interfaces.RealOutput yFan(
    final unit="1")
    "Control signal for fan"
  Modelica.Blocks.Interfaces.RealOutput yHea(
    final unit="1")
    "Control signal for heating coil"

  Controls.Continuous.LimPID conHeaCoi(
    final controllerType=controllerTypeHea,
    final k=kHea,
    final Ti=TiHea,
    final Td=TdHea)
    "Controller for heating coil"
  Controls.Continuous.LimPID conFan(
    final controllerType=controllerTypeFan,
    final k=kFan,
    final Ti=TiFan,
    final Td=TdFan,
    final yMax=1,
    final yMin=minAirFlo,
    final reverseActing=false)
    "Controller for fan"

equation
  connect(TSetRooHea, conHeaCoi.u_s)
  connect(TRoo, conHeaCoi.u_m)
  connect(conHeaCoi.y, yHea)
  connect(conFan.u_s, TSetRooCoo)
  connect(TRoo, conFan.u_m)
  connect(conFan.y, yFan)
end ControllerHeatingFan;


within Buildings.Air.Systems.SingleZone.VAV;
package BaseClasses "Package with base classes for Buildings.Air.Systems.SingleZone.VAV"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


Validate the block ControllerChillerDXHeatingEconomizer. Validation test for the block

Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerChillerDXHeatingEconomizer.

within Buildings.Air.Systems.SingleZone.VAV.BaseClasses.Validation;
model ControllerChillerDXHeatingEconomizer
  "Validate the block ControllerChillerDXHeatingEconomizer"
  extends Modelica.Icons.Example;

  Modelica.Blocks.Sources.Sine mixAirTem(
    amplitude=7.5,
    f=1/86400,
    offset=20 + 273.15) "Mixed air temperature"
  Modelica.Blocks.Sources.Sine retAirTem(
    amplitude=10,
    f=1/86400,
    offset=21 + 273.15) "Return air temperature"
  Modelica.Blocks.Sources.Sine outAirTem(
    f=1/86400,
    amplitude=6,
    offset=18 + 273.15) "Measured outdoor air temperature"

  Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerChillerDXHeatingEconomizer
    con(
    TSupChi_nominal=279.15,
    minAirFlo=0.1,
    minOAFra=0.4,
    TSetSupAir=286.15) "Controller"
  Controls.OBC.CDL.Reals.Sources.TimeTable  TSetRooHea(
    table=[
      0,      15 + 273.15;
      8*3600, 20 + 273.15;
      18*3600,15 + 273.15;
      24*3600,15 + 273.15],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    extrapolation=Buildings.Controls.OBC.CDL.Types.Extrapolation.Periodic)
    "Heating setpoint for room temperature"
  Controls.OBC.CDL.Reals.Sources.TimeTable TSetRooCoo(
    table=[
       0,      30 + 273.15;
       8*3600, 25 + 273.15;
      18*3600, 30 + 273.15;
      24*3600, 30 + 273.15],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    extrapolation=Buildings.Controls.OBC.CDL.Types.Extrapolation.Periodic)
    "Cooling setpoint for room temperature"
  Controls.SetPoints.OccupancySchedule occSch(occupancy=3600*{8,18})
    "Occupancy schedule"
  Modelica.Blocks.Sources.Sine supAirTem(
    amplitude=7,
    f=1/86400,
    offset=13 + 273.15) "Supply air temperature"
equation
  connect(con.TSetRooHea, TSetRooHea.y[1])
  connect(con.TSetRooCoo, TSetRooCoo.y[1])
  connect(con.uOcc, occSch.occupied)
  connect(con.TOut, outAirTem.y)
  connect(con.TRoo, retAirTem.y)
  connect(mixAirTem.y, con.TMix)
  connect(supAirTem.y, con.TSup)
end ControllerChillerDXHeatingEconomizer;


Validate the block ControllerEconomizer. Validation test for the block

Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerEconomizer.

within Buildings.Air.Systems.SingleZone.VAV.BaseClasses.Validation;
model ControllerEconomizer "Validate the block ControllerEconomizer"
  extends Modelica.Icons.Example;

  Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerEconomizer conEco
    "Controller for economizer"
  Modelica.Blocks.Sources.Sine mixAirTem(
    amplitude=7.5,
    f=1/86400,
    offset=20 + 273.15) "Mixed air temperature"
  Modelica.Blocks.Sources.Sine retAirTem(
    amplitude=4,
    f=1/86400,
    offset=21 + 273.15) "Return air temperature"
  Modelica.Blocks.Sources.Sine mixAirTemSet(
    amplitude=5,
    f=1/86400,
    offset=20 + 273.15) "Mixed air temperature setpoint"
  Modelica.Blocks.Sources.Sine minOutAirFra(
    f=1/86400,
    offset=0.2,
    amplitude=0.15) "Minimum outdoor air fraction"
  Modelica.Blocks.Sources.Sine outAirTem(
    f=1/86400,
    amplitude=6,
    offset=18 + 273.15) "Measured outdoor air temperature"
  Modelica.Blocks.Sources.BooleanConstant cooSta(k=true) "Cooling status"

equation
  connect(mixAirTemSet.y, conEco.TMixSet)
  connect(mixAirTem.y, conEco.TMix)
  connect(retAirTem.y, conEco.TRet)
  connect(minOutAirFra.y, conEco.minOAFra)
  connect(outAirTem.y, conEco.TOut)
  connect(cooSta.y, conEco.cooSta)
end ControllerEconomizer;


Validate the block ControllerHeatingFan. Validation test for the block

Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerHeatingFan.

within Buildings.Air.Systems.SingleZone.VAV.BaseClasses.Validation;
model ControllerHeatingFan "Validate the block ControllerHeatingFan"
  extends Modelica.Icons.Example;

  Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerHeatingFan
    conHeaFan(minAirFlo=0.6)
    "Controller for heating and cooling"
  Modelica.Blocks.Sources.Sine zonHeaSet(
    f=1/86400,
    offset=20 + 273.15,
    amplitude=1) "Zone heating setpoint"
  Modelica.Blocks.Sources.Sine zonCooSet(
    f=1/86400,
    amplitude=2,
    offset=25 + 273.15) "Zone cooling setpoint"
  Modelica.Blocks.Sources.Sine zonTem(
    amplitude=4,
    f=1/86400,
    offset=21 + 273.15) "Zone temperature"

equation
  connect(zonHeaSet.y, conHeaFan.TSetRooHea)
  connect(zonCooSet.y, conHeaFan.TSetRooCoo)
  connect(zonTem.y, conHeaFan.TRoo)
end ControllerHeatingFan;


info

within Buildings.Air.Systems.SingleZone.VAV.BaseClasses;
package Validation
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Variable air volume flow system with single themal zone and conventional control. Implementation of
Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses.PartialOpenLoop
with conventional single-maximum control sequence.

within Buildings.Air.Systems.SingleZone.VAV.Examples;
model ChillerDXHeatingEconomizer
  "Variable air volume flow system with single themal zone and conventional control"
  extends Modelica.Icons.Example;
  extends
    Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses.PartialOpenLoop(
    hvac(QCoo_flow_nominal=-10000));
  parameter Modelica.Units.SI.Temperature TSupChi_nominal=279.15
    "Design value for chiller leaving water temperature";

  Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerChillerDXHeatingEconomizer con(
    minAirFlo=0.1,
    minOAFra=0.4,
    controllerTypeHea=Buildings.Controls.OBC.CDL.Types.SimpleController.P,
    controllerTypeCoo=Buildings.Controls.OBC.CDL.Types.SimpleController.P,
    kCoo=1,
    TiCoo=120,
    controllerTypeFan=Buildings.Controls.OBC.CDL.Types.SimpleController.P,
    kFan=4,
    controllerTypeEco=Buildings.Controls.OBC.CDL.Types.SimpleController.P,
    kEco=4,
    kHea=4,
    TSupChi_nominal=TSupChi_nominal,
    TSetSupAir=286.15) "Controller for single zone VAV system"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TSetRooHea(
    table=[
      0,       15 + 273.15;
      8*3600,  20 + 273.15;
      18*3600, 15 + 273.15;
      24*3600, 15 + 273.15],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    extrapolation=Buildings.Controls.OBC.CDL.Types.Extrapolation.Periodic)
    "Heating setpoint for room temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TSetRooCoo(
    table=[
      0,       30 + 273.15;
      8*3600,  25 + 273.15;
      18*3600, 30 + 273.15;
      24*3600, 30 + 273.15],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    extrapolation=Buildings.Controls.OBC.CDL.Types.Extrapolation.Periodic)
    "Cooling setpoint for room temperature"
  Buildings.Controls.SetPoints.OccupancySchedule occSch(occupancy=3600*{8,18})
    "Occupancy schedule"

equation
  connect(TSetRooHea.y[1], con.TSetRooHea)
  connect(TSetRooCoo.y[1], con.TSetRooCoo)
  connect(hvac.uFan, con.yFan)
  connect(hvac.uHea, con.yHea)
  connect(con.yCooCoiVal, hvac.uCooVal)
  connect(hvac.uEco, con.yOutAirFra)
  connect(con.chiOn, hvac.chiOn)
  connect(hvac.TSetChi, con.TSetSupChi)
  connect(hvac.TMix, con.TMix)
  connect(hvac.TSup, con.TSup)
  connect(zon.TRooAir, con.TRoo)
  connect(occSch.occupied, con.uOcc)
  connect(weaBus.TDryBul, con.TOut)
end ChillerDXHeatingEconomizer;


Variable air volume flow system with single themal zone and ASHRAE Guideline 36 sequence control. Implementation of
Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses.PartialOpenLoop
with ASHRAE Guideline 36 control sequence.

within Buildings.Air.Systems.SingleZone.VAV.Examples;
model Guideline36
  "Variable air volume flow system with single themal zone and ASHRAE Guideline 36 sequence control"
  extends Modelica.Icons.Example;
  extends
    Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses.PartialOpenLoop(
    hvac(QCoo_flow_nominal=-10000));
  parameter Modelica.Units.SI.Temperature TSupChi_nominal=279.15
    "Design value for chiller leaving water temperature";

  Buildings.Controls.OBC.ASHRAE.G36.AHUs.SingleZone.VAV.Controller con(
    eneStd=Buildings.Controls.OBC.ASHRAE.G36.Types.EnergyStandard.ASHRAE90_1,
    venStd=Buildings.Controls.OBC.ASHRAE.G36.Types.VentilationStandard.ASHRAE62_1,

    final VAreBreZon_flow=0.0144,
    final VPopBreZon_flow=0.0075,
    ecoHigLimCon=Buildings.Controls.OBC.ASHRAE.G36.Types.ControlEconomizer.FixedDryBulb,

    ashCliZon=Buildings.Controls.OBC.ASHRAE.G36.Types.ASHRAEClimateZone.Zone_6B,

    freSta=Buildings.Controls.OBC.ASHRAE.G36.Types.FreezeStat.No_freeze_stat,
    have_winSen=true,
    have_CO2Sen=false,
    buiPreCon=Buildings.Controls.OBC.ASHRAE.G36.Types.PressureControl.BarometricRelief,

    have_locAdj=false,
    ignDemLim=false,
    kCoo=0.1,
    TiCoo=120,
    TiHea=120,
    TSupDew_max=297.15,
    maxHeaSpe=0.2,
    maxCooSpe=1,
    minSpe=0.1,
    kCooCoi=1,
    VOutMin_flow=0.0144,
    VOutDes_flow=0.025,
    kHea=0.1,
    kMod=4,
    have_occSen=false,
    TSup_max=343.15,
    TSup_min=286.15,
    outDamMinFloMinSpe=0.2304,
    outDamMinFloMaxSpe=0.02304,
    outDamDesFloMinSpe=0.4,
    outDamDesFloMaxSpe=0.04) "VAV controller"

  Buildings.Controls.OBC.CDL.Reals.Hysteresis hysChiPla(
    uLow=-1,
    uHigh=0)
    "Hysteresis with delay to switch on cooling"
  Modelica.Blocks.Math.Feedback errTRooCoo
    "Control error on room temperature for cooling"
  Buildings.Controls.SetPoints.OccupancySchedule occSch(occupancy=3600*{8,18})
    "Occupancy schedule"
  Modelica.Blocks.Sources.BooleanConstant uWin(k=true)  "Window opening signal"
  Buildings.Controls.OBC.CDL.Integers.Sources.Constant demLim(final k=0)
    "Cooling and heating demand imit level"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant cooWarTim(final k=0)
    "Cooldown and warm-up time"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TOccHeaSet(final k=293.15)
    "Occupied heating setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TOccCooSet(final k=298.15)
    "Occupied cooling setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TUnoHeaSet(final k=288.15)
    "Unoccupied heating setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TUnoCooSet(final k=303.15)
    "Unoccupied cooling setpoint"
  Modelica.Blocks.Sources.BooleanConstant freRes(k=true)
    "Freeze protection reset"
protected
  Modelica.Blocks.Sources.Constant TSetSupChiConst(final k=TSupChi_nominal)
    "Set point for chiller temperature"

equation
  connect(con.yHeaCoi, hvac.uHea)
  connect(TSetSupChiConst.y, hvac.TSetChi)
  connect(errTRooCoo.y, hysChiPla.u)
  connect(zon.TRooAir, errTRooCoo.u1)
  connect(hysChiPla.y, hvac.chiOn)
  connect(zon.TRooAir, con.TZon)
  connect(occSch.tNexOcc, con.tNexOcc)
  connect(con.TZonCooSet, errTRooCoo.u2)
  connect(demLim.y, con.uCooDemLimLev)
  connect(demLim.y, con.uHeaDemLimLev)
  connect(cooWarTim.y, con.warUpTim)
  connect(cooWarTim.y, con.cooDowTim)
  connect(con.yCooCoi, hvac.uCooVal)
  connect(TOccHeaSet.y, con.TOccHeaSet)
  connect(TOccCooSet.y, con.TOccCooSet)
  connect(TUnoHeaSet.y, con.TUnoHeaSet)
  connect(TUnoCooSet.y, con.TUnoCooSet)
  connect(weaBus.TDryBul, con.TOut)
  connect(uWin.y, con.u1Win)
  connect(occSch.occupied, con.u1Occ)
  connect(hvac.TSup, con.TAirSup)
  connect(hvac.TMix, con.TAirMix)
  connect(con.ySupFan, hvac.uFan)
  connect(con.yCooCoi, con.uCooCoi_actual)
  connect(con.yHeaCoi, con.uHeaCoi_actual)
  connect(freRes.y, con.u1SofSwiRes)
  connect(con.yOutDam, hvac.uEco)
  connect(con.ySupFan, con.uSupFan_actual)
end Guideline36;


within Buildings.Air.Systems.SingleZone.VAV;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


within Buildings.Air.Systems.SingleZone.VAV.Examples;
package BaseClasses "Package with base classes for Buildings.Air.Systems.SingleZone.VAV.Examples"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


within Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses;
partial model PartialOpenLoop
  "Partial model of a single zone variable air volume flow system and thermal zone"

  package MediumA = Buildings.Media.Air(extraPropertiesNames={"CO2"})
    "Buildings library air media package with CO2";
  package MediumW = Buildings.Media.Water
    "Buildings library air media package";
  parameter Modelica.Units.SI.Temperature TSupChi_nominal=279.15
    "Design value for chiller leaving water temperature";

  Buildings.Air.Systems.SingleZone.VAV.ChillerDXHeatingEconomizer hvac(
    redeclare package MediumA = MediumA,
    redeclare package MediumW = MediumW,
    mAir_flow_nominal=0.75,
    etaHea_nominal=0.99,
    QHea_flow_nominal=7000,
    QCoo_flow_nominal=-7000,
    TSupChi_nominal=TSupChi_nominal)
    "Single zone VAV system"
  Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses.Room zon(
    redeclare package MediumA = MediumA,
      mAir_flow_nominal=0.75)
    "Thermal envelope of single zone"
  Buildings.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
      computeWetBulbTemperature=false,
      filNam=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/DRYCOLD.mos"))
    "Weather data"
  Modelica.Blocks.Continuous.Integrator EFan
  "Total fan energy"
  Modelica.Blocks.Continuous.Integrator EHea
  "Total heating energy"
  Modelica.Blocks.Continuous.Integrator ECoo
  "Total cooling energy"
  Modelica.Blocks.Math.MultiSum EHVAC(nu=4)
  "Total HVAC energy"
  Modelica.Blocks.Continuous.Integrator EPum
  "Total pump energy"
  BoundaryConditions.WeatherData.Bus weaBus
  "Weather bus"

equation
  connect(weaDat.weaBus, weaBus)
  connect(hvac.supplyAir, zon.supplyAir)
  connect(hvac.returnAir, zon.returnAir)
  connect(hvac.weaBus, weaBus)
  connect(zon.weaBus, weaBus)
  connect(hvac.PFan, EFan.u)
  connect(hvac.QHea_flow, EHea.u)
  connect(hvac.PCoo, ECoo.u)
  connect(hvac.PPum, EPum.u)
  connect(EFan.y, EHVAC.u[1])
  connect(EHea.y, EHVAC.u[2])
  connect(ECoo.y, EHVAC.u[3])
  connect(EPum.y, EHVAC.u[4])
end PartialOpenLoop;


within Buildings.Air.Systems.SingleZone.VAV.Examples.BaseClasses;
model Room
  "BESTest Case 600 with fluid ports for air HVAC and internal load"

  replaceable package MediumA = Buildings.Media.Air "Medium model";
  parameter Modelica.Units.SI.MassFlowRate mAir_flow_nominal
    "Design airflow rate of system";
  parameter Modelica.Units.SI.Angle S_=Buildings.Types.Azimuth.S
    "Azimuth for south walls";
  parameter Modelica.Units.SI.Angle E_=Buildings.Types.Azimuth.E
    "Azimuth for east walls";
  parameter Modelica.Units.SI.Angle W_=Buildings.Types.Azimuth.W
    "Azimuth for west walls";
  parameter Modelica.Units.SI.Angle N_=Buildings.Types.Azimuth.N
    "Azimuth for north walls";
  parameter Modelica.Units.SI.Angle C_=Buildings.Types.Tilt.Ceiling
    "Tilt for ceiling";
  parameter Modelica.Units.SI.Angle F_=Buildings.Types.Tilt.Floor
    "Tilt for floor";
  parameter Modelica.Units.SI.Angle Z_=Buildings.Types.Tilt.Wall
    "Tilt for wall";
  parameter Integer nConExtWin = 1 "Number of constructions with a window";
  parameter Integer nConBou = 1
    "Number of surface that are connected to constructions that are modeled inside the room";
  parameter Buildings.HeatTransfer.Data.OpaqueConstructions.Generic matExtWal(
    nLay=3,
    absIR_a=0.9,
    absIR_b=0.9,
    absSol_a=0.6,
    absSol_b=0.6,
    material={
      Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.009,
        k=0.140,
        c=900,
        d=530,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef),
      Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.066,
        k=0.040,
        c=840,
        d=12,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef),
      Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.012,
        k=0.160,
        c=840,
        d=950,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef)})
    "Exterior wall"
  parameter Buildings.HeatTransfer.Data.OpaqueConstructions.Generic matFlo(
    final nLay= 2,
    absIR_a=0.9,
    absIR_b=0.9,
    absSol_a=0.6,
    absSol_b=0.6,
    material={
      Buildings.HeatTransfer.Data.Solids.Generic(
        x=1.003,
        k=0.040,
        c=0,
        d=0,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef),
     Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.025,
        k=0.140,
        c=1200,
        d=650,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef)})
    "Floor"
  parameter Buildings.HeatTransfer.Data.Solids.Generic soil(
    x=2,
    k=1.3,
    c=800,
    d=1500) "Soil properties"
  parameter Buildings.HeatTransfer.Data.OpaqueConstructions.Generic roof(
    nLay=3,
    absIR_a=0.9,
    absIR_b=0.9,
    absSol_a=0.6,
    absSol_b=0.6,
    material={
      Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.019,
        k=0.140,
        c=900,
        d=530,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef),
     Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.1118,
        k=0.040,
        c=840,
        d=12,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef),
     Buildings.HeatTransfer.Data.Solids.Generic(
        x=0.010,
        k=0.160,
        c=840,
        d=950,
        nStaRef=Buildings.ThermalZones.Detailed.Validation.BESTEST.nStaRef)})
    "Roof"
  parameter Buildings.ThermalZones.Detailed.Validation.BESTEST.Data.Win600 window600(
    UFra=3,
    haveExteriorShade=false,
    haveInteriorShade=false) "Window"

  Buildings.HeatTransfer.Conduction.SingleLayer soi(
    A=48,
    material=soil,
    steadyStateInitial=true,
    stateAtSurface_a=false,
    stateAtSurface_b=true,
    T_a_start=283.15,
    T_b_start=283.75)
    "2 m deep soil (per definition on p.4 of ASHRAE 140-2007)"
  Modelica.Fluid.Interfaces.FluidPort_a supplyAir(redeclare final package
      Medium = MediumA)
    "Supply air"
  Modelica.Fluid.Interfaces.FluidPort_b returnAir(redeclare final package
      Medium = MediumA)
    "Return air"
  Buildings.BoundaryConditions.WeatherData.Bus weaBus
    "Weather data bus"
  Modelica.Blocks.Interfaces.RealOutput TRooAir
    "Room air temperature"
  Buildings.ThermalZones.Detailed.MixedAir roo(
    redeclare package Medium = MediumA,
    use_C_flow=true,
    nPorts=5,
    hRoo=2.7,
    nConExtWin=nConExtWin,
    nConBou=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    AFlo=48,
    datConBou(
      layers={matFlo},
      each A=48,
      each til=F_),
    datConExt(
      layers={roof,matExtWal,matExtWal,matExtWal},
      A={48,6*2.7,6*2.7,8*2.7},
      til={C_,Z_,Z_,Z_},
      azi={S_,W_,E_,N_}),
    nConExt=4,
    nConPar=0,
    nSurBou=0,
    datConExtWin(
      layers={matExtWal},
      A={8*2.7},
      glaSys={window600},
      wWin={2*3},
      hWin={2},
      fFra={0.001},
      til={Z_},
      azi={S_}),
    intConMod=Buildings.HeatTransfer.Types.InteriorConvection.Temperature,
    extConMod=Buildings.HeatTransfer.Types.ExteriorConvection.TemperatureWind,
    steadyStateWindow=false)
    "Room model for Case 600"
  Modelica.Blocks.Sources.Constant qConGai_flow(k=192/48)
    "Convective heat gain"
  Modelica.Blocks.Sources.Constant qRadGai_flow(k=288/48)
    "Radiative heat gain"
  Modelica.Blocks.Routing.Multiplex3 mul
    "Multiplex"
  Modelica.Blocks.Sources.Constant qLatGai_flow(k=96/48)
    "Latent heat gain"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TSoi[nConBou](
    each T= 283.15)
    "Boundary condition for construction"
  Fluid.Sources.MassFlowSource_WeatherData sinInf(
    redeclare package Medium = MediumA,
    C=fill(0.0004, 1),
    nPorts=1,
    use_m_flow_in=true)
    "Sink model for air infiltration"
  Modelica.Blocks.Sources.Constant InfiltrationRate(k=48*2.7*0.5/3600)
    "0.41 ACH adjusted for the altitude (0.5 at sea level)"
  Modelica.Blocks.Math.Product product
  Buildings.Fluid.Sensors.Density density(redeclare package Medium = MediumA,
      warnAboutOnePortConnection=false)
    "Air density inside the building"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTZon
    "Zone air temperature sensor"
  Fluid.Sources.MassFlowSource_WeatherData souInf(
    redeclare package Medium = MediumA,
    use_m_flow_in=true,
    C=fill(0.0004, 1),
    nPorts=1)
    "Source model for air infiltration"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable intLoad(table=[0,
        0.1; 8*3600,0.1; 8*3600,1.0; 18*3600,1.0; 18*3600,0.1; 24*3600,0.1])
    "Internal loads"
  Modelica.Blocks.Sources.Constant CO2_flow_per(k=1.023e-5)
    "Latent heat gain"
  Modelica.Blocks.Logical.GreaterThreshold greThr(threshold=0.1)
    "Greater than"
  Modelica.Blocks.Sources.Constant desOcc(k=2)
    "Design number of occupants"
  Modelica.Blocks.Math.Product numOcc
    "Number of occupants in the zone"
  Modelica.Blocks.Math.BooleanToReal booToRea
    "Convert Boolean to Real signal"

protected
  Modelica.Blocks.Math.Product pro1
    "Product for internal gain"
  Modelica.Blocks.Math.Product pro2
    "Product for internal gain"
  Modelica.Blocks.Math.Product pro3
    "Product for internal gain"
  Modelica.Blocks.Math.Gain gaiInf(final k=-1)
    "Gain for infiltration"
  Modelica.Blocks.Math.Product pro4
    "Product for internal gain"

equation
  connect(mul.y, roo.qGai_flow)
  connect(density.port, roo.ports[1])
  connect(density.d, product.u2)
  connect(TSoi[1].port, soi.port_a)
  connect(soi.port_b, roo.surf_conBou[1])
  connect(sinInf.ports[1], roo.ports[2])
  connect(weaBus,sinInf. weaBus)
  connect(weaBus, roo.weaBus)
  connect(senTZon.T, TRooAir)
  connect(senTZon.port, roo.heaPorAir)
  connect(qRadGai_flow.y, pro1.u1)
  connect(qLatGai_flow.y, pro2.u1)
  connect(qConGai_flow.y, pro3.u1)
  connect(intLoad.y[1], pro2.u2)
  connect(pro1.y, mul.u1[1])
  connect(pro3.y, mul.u2[1])
  connect(pro2.y, mul.u3[1])
  connect(souInf.weaBus, weaBus)
  connect(souInf.ports[1], roo.ports[3])
  connect(product.y, gaiInf.u)
  connect(gaiInf.y, souInf.m_flow_in)
  connect(product.y, sinInf.m_flow_in)
  connect(supplyAir, roo.ports[4])
  connect(returnAir, roo.ports[5])
  connect(InfiltrationRate.y, product.u1)
  connect(intLoad.y[1], pro1.u2)
  connect(pro3.u2, intLoad.y[1])
  connect(CO2_flow_per.y, pro4.u2)
  connect(desOcc.y, numOcc.u1)
  connect(greThr.y, booToRea.u)
  connect(booToRea.y, numOcc.u2)
  connect(greThr.u, pro2.u2)
  connect(numOcc.y, pro4.u1)
  connect(pro4.y, roo.C_flow[1])
end Room;


Example model using the block OptimalStart with a conventional controller for a single-zone VAV system in spring. This is an example model on how to use the block

Buildings.Controls.OBC.Utilities.OptimalStart
that integrates with a conventional controller, a single-zone VAV system
and a single-zone floor building. The building, HVAC system and controller model
can be found in the base class

Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUConventional.

within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
model ConventionalSpring
  "Example model using the block OptimalStart with a conventional controller for a single-zone VAV system in spring"
  extends Modelica.Icons.Example;

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    computeWetBulbTemperature=false,
    filNam=Modelica.Utilities.Files.loadResource(
        "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"))
    "Weather data"

  Buildings.Controls.OBC.Utilities.OptimalStart optSta(
    computeHeating=true,
    computeCooling=true,
    uLow=0.1,
    thrOptOn(displayUnit="s"))
    "Optimal start for heating and cooling system "
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TSetRooHea(
    table=[0,15 + 273.15; 8*3600,20 + 273.15; 18*3600,15 + 273.15; 24*3600,15 +
        273.15],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    extrapolation=Buildings.Controls.OBC.CDL.Types.Extrapolation.Periodic,
    y(each unit="K",
      each displayUnit="degC"))
    "Heating setpoint for room temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.TimeTable TSetRooCoo(
    table=[0,30 + 273.15; 8*3600,24 + 273.15; 18*3600,30 + 273.15; 24*3600,30 +
        273.15],
    smoothness=Buildings.Controls.OBC.CDL.Types.Smoothness.ConstantSegments,
    extrapolation=Buildings.Controls.OBC.CDL.Types.Extrapolation.Periodic,
    y(each unit="K",
      each displayUnit="degC"))
    "Cooling setpoint for room temperature"
  Buildings.Controls.SetPoints.OccupancySchedule occSch(occupancy=3600*{8,18})
    "Occupancy schedule"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetHeaOn(k=20 + 273.15)
    "Zone heating setpoint during occupied period"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetCooOn(k=24 + 273.15)
    "Zone cooling setpoint during occupied time"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea(realTrue=-6)
    "Switch to occupied cooling setpoint"
  Buildings.Controls.OBC.CDL.Conversions.BooleanToReal booToRea1(realTrue=5)
    "Switch to occupied heating setpoint"
  Buildings.Controls.OBC.CDL.Reals.Add add3
    "New cooling setpoint schedule for room"
  Buildings.Controls.OBC.CDL.Reals.Add add4
    "New heating setpoint schedule for room"
  Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUConventional zonAHUOpt
    "Model of a single zone with AHU and controller"
  Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUConventional zonAHUCon
    "Model of a single zone with AHU and controller"
  Buildings.Controls.OBC.CDL.Logical.Or or2
    "Occupied or optimal start for preconditioning"

equation
  connect(TSetHeaOn.y, optSta.TSetZonHea)
  connect(TSetRooCoo.y[1], add3.u2)
  connect(occSch.tNexOcc, optSta.tNexOcc)
  connect(booToRea.y, add3.u1)
  connect(TSetCooOn.y, optSta.TSetZonCoo)
  connect(optSta.optOn, booToRea.u)
  connect(add4.y, zonAHUOpt.TSetRooHea)
  connect(add3.y, zonAHUOpt.TSetRooCoo)
  connect(TSetRooHea.y[1], zonAHUCon.TSetRooHea)
  connect(TSetRooCoo.y[1], zonAHUCon.TSetRooCoo)
  connect(zonAHUOpt.TZon, optSta.TZon)
  connect(optSta.optOn, booToRea1.u)
  connect(occSch.occupied, zonAHUCon.uOcc)
  connect(optSta.optOn, or2.u1)
  connect(occSch.occupied, or2.u2)
  connect(or2.y, zonAHUOpt.uOcc)
  connect(add4.u2, TSetRooHea.y[1])
  connect(booToRea1.y, add4.u1)

  connect(weaDat.weaBus, zonAHUOpt.weaBus)
  connect(weaDat.weaBus, zonAHUCon.weaBus)
end ConventionalSpring;


Example model using the block OptimalStart with a conventional controller for a single-zone VAV system in summer. This is an example model on how to use the block

Buildings.Controls.OBC.Utilities.OptimalStart
that integrates with a conventional controller, a single-zone VAV system
and a single-zone floor building. The building, HVAC system and controller model
can be found in the base class

Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUConventional.

within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
model ConventionalSummer
  "Example model using the block OptimalStart with a conventional controller for a single-zone VAV system in summer"
  extends Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.ConventionalSpring;
end ConventionalSummer;


Example model using the block OptimalStart with a conventional controller for a single-zone VAV system in winter. This is an example model on how to use the block

Buildings.Controls.OBC.Utilities.OptimalStart
that integrates with a conventional controller, a single-zone VAV system
and a single-zone floor building. The building, HVAC system and controller model
can be found in the base class

Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUConventional.

within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
model ConventionalWinter
  "Example model using the block OptimalStart with a conventional controller for a single-zone VAV system in winter"
  extends Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.ConventionalSpring;
end ConventionalWinter;


Example model using the block OptimalStart with a Guideline36 controller for a single-zone system in spring. This is an example model on how to use the block

Buildings.Controls.OBC.Utilities.OptimalStart
that integrates with a controller based on Guideline36

Buildings.Controls.OBC.ASHRAE.G36.AHUs.SingleZone.VAV.Controller,
a single-zone VAV system and a single-zone floor building.
The building, HVAC system and controller model
can be found in the base class

Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUG36.

within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
model Guideline36Spring
  "Example model using the block OptimalStart with a Guideline36 controller for a single-zone system in spring"
  extends Modelica.Icons.Example;

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    computeWetBulbTemperature=false,
    filNam=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"))
    "Weather data"

  Buildings.Controls.OBC.Utilities.OptimalStart optStaHea(
    computeHeating=true,
    computeCooling=false,
    uLow=0.1,
    thrOptOn(displayUnit="s"))
    "Optimal start for heating"
  Buildings.Controls.SetPoints.OccupancySchedule occSch(occupancy=3600*{8,18})
    "Occupancy schedule"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetHeaOn(k=20 + 273.15)
    "Zone heating setpoint during occupied period"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetCooOn(k=24 + 273.15)
    "Zone cooling setpoint during occupied time"
  Buildings.Controls.OBC.Utilities.OptimalStart optStaCoo(
    computeHeating=false,
    computeCooling=true,
    uLow=0.1,
    thrOptOn(displayUnit="s"))
    "Optimal start for cooling"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant con(k=0)
    "No optimal start"
  Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUG36 zonAHUG36Opt
    "A single zone building with a VAV system and a Guideline36 controller"
  Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUG36 zonAHUG36Con
    "A single zone building with a VAV system and a Guideline36 controller"

equation
  connect(TSetHeaOn.y, optStaHea.TSetZonHea)
  connect(TSetCooOn.y, optStaCoo.TSetZonCoo)
  connect(occSch.tNexOcc, optStaCoo.tNexOcc)
  connect(optStaCoo.TZon, optStaHea.TZon)
  connect(occSch.tNexOcc, zonAHUG36Opt.tNexOcc)
  connect(occSch.occupied, zonAHUG36Opt.uOcc)
  connect(optStaHea.tOpt, zonAHUG36Opt.warUpTim)
  connect(optStaCoo.tOpt, zonAHUG36Opt.cooDowTim)
  connect(zonAHUG36Opt.TZon, optStaHea.TZon)
  connect(con.y, zonAHUG36Con.warUpTim)
  connect(con.y, zonAHUG36Con.cooDowTim)
  connect(occSch.tNexOcc, zonAHUG36Con.tNexOcc)
  connect(occSch.occupied, zonAHUG36Con.uOcc)
  connect(occSch.tNexOcc, optStaHea.tNexOcc)

  connect(weaDat.weaBus, zonAHUG36Opt.weaBus)
  connect(weaDat.weaBus, zonAHUG36Con.weaBus)
end Guideline36Spring;


Example model using the block OptimalStart with a Guideline36 controller for a single-zone system in summer. This is an example model on how to use the block

Buildings.Controls.OBC.Utilities.OptimalStart
that integrates with a controller based on Guideline36

Buildings.Controls.OBC.ASHRAE.G36.AHUs.SingleZone.VAV.Controller,
a single-zone VAV system and a single-zone floor building.
The building, HVAC system and controller model
can be found in the base class

Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUG36.

within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
model Guideline36Summer
  "Example model using the block OptimalStart with a Guideline36 controller for a single-zone system in summer"
  extends Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.Guideline36Spring;
end Guideline36Summer;


Example model using the block OptimalStart with a Guideline36 controller for a single-zone system in winter. This is an example model on how to use the block

Buildings.Controls.OBC.Utilities.OptimalStart
that integrates with a controller based on Guideline36

Buildings.Controls.OBC.ASHRAE.G36.AHUs.SingleZone.VAV.Controller,
a single-zone VAV system and a single-zone floor building.
The building, HVAC system and controller model
can be found in the base class

Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses.ZoneWithAHUG36.

within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
model Guideline36Winter
  "Example model using the block OptimalStart with a Guideline36 controller for a single-zone system in winter"
  extends Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.Guideline36Spring;
end Guideline36Winter;


within Buildings.Air.Systems.SingleZone.VAV.Examples;
package OptimalStart "Example models of the block OptimalStart"
extends Modelica.Icons.ExamplesPackage;
end OptimalStart;


within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart;
package BaseClasses "Base classes of models in the package OptimalStart"
extends Modelica.Icons.BasesPackage;
end BaseClasses;


within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses;
block ZoneWithAHUConventional
  "A single zone building with a VAV system and a conventional controller"

  package MediumA = Buildings.Media.Air(extraPropertiesNames={"CO2"})
    "Buildings library air media package";
  package MediumW = Buildings.Media.Water
    "Buildings library water media package";
  parameter Modelica.Units.SI.Temperature TSupChi_nominal=279.15
    "Design value for chiller leaving water temperature";
  parameter Modelica.Units.SI.Volume VRoo=4555.7 "Space volume of the floor";
  parameter Modelica.Units.SI.MassFlowRate mAir_flow_nominal=VRoo*4*1.2/3600
    "Design air flow rate";
  parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal=90000
    "Design heating flow rate";
  parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal=-100000
    "Design cooling flow rate";

  Buildings.Controls.OBC.CDL.Interfaces.RealInput TSetRooHea(
    final unit="K",
    displayUnit="degC")
    "Room heating setpoint temperature"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput TSetRooCoo(
    final unit="K",
    displayUnit="degC")
    "Room cooling setpoint temperature"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput uOcc
    "Current occupancy period, true if it is in occupant period"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput TZon(
    final unit="K",
    displayUnit="degC")
    "Zone temperature"

  Buildings.Air.Systems.SingleZone.VAV.ChillerDXHeatingEconomizer hvac(
    redeclare package MediumA = MediumA,
    redeclare package MediumW = MediumW,
    mAir_flow_nominal=mAir_flow_nominal,
    etaHea_nominal=0.99,
    QHea_flow_nominal=QHea_flow_nominal,
    QCoo_flow_nominal=QCoo_flow_nominal,
    TSupChi_nominal=TSupChi_nominal)
    "Single zone VAV system"

  Buildings.Air.Systems.SingleZone.VAV.BaseClasses.ControllerChillerDXHeatingEconomizer
    con(
    minAirFlo=0.1,
    minOAFra=0.4,
    TSupChi_nominal=TSupChi_nominal,
    TSetSupAir=286.15)
    "Controller"
  Buildings.ThermalZones.Detailed.Validation.BaseClasses.SingleZoneFloor
    sinZonFlo(redeclare package Medium = MediumA)
    "Single zone floor building"
  Buildings.BoundaryConditions.WeatherData.Bus weaBus
    "Weather bus"
equation
  connect(con.yCooCoiVal,hvac. uCooVal)
  connect(hvac.uEco,con. yOutAirFra)
  connect(con.chiOn,hvac. chiOn)
  connect(con.yFan,hvac. uFan)
  connect(con.yHea,hvac. uHea)
  connect(hvac.TMix,con. TMix)
  connect(hvac.TSup,con. TSup)
  connect(sinZonFlo.TRooAir,con. TRoo)
  connect(hvac.supplyAir,sinZonFlo. ports[1])
  connect(hvac.returnAir,sinZonFlo. ports[2])
  connect(con.TSetSupChi,hvac. TSetChi)
  connect(TSetRooHea, con.TSetRooHea)
  connect(TSetRooCoo, con.TSetRooCoo)
  connect(sinZonFlo.TRooAir,TZon)
  connect(uOcc, con.uOcc)
  connect(weaBus, sinZonFlo.weaBus)
  connect(weaBus, hvac.weaBus)
  connect(weaBus.TDryBul, con.TOut)
end ZoneWithAHUConventional;


within Buildings.Air.Systems.SingleZone.VAV.Examples.OptimalStart.BaseClasses;
block ZoneWithAHUG36
  "A single zone building with a VAV system and a Guideline36 controller"

  package MediumA = Buildings.Media.Air(extraPropertiesNames={"CO2"})
    "Buildings library air media package";
  package MediumW = Buildings.Media.Water
    "Buildings library water media package";
  parameter Modelica.Units.SI.Temperature TSupChi_nominal=279.15
    "Design value for chiller leaving water temperature";
  parameter Modelica.Units.SI.Volume VRoo=4555.7 "Space volume of the floor";
  parameter Modelica.Units.SI.MassFlowRate mAir_flow_nominal=VRoo*4*1.2/3600
    "Design air flow rate";
  parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal=90000
    "Design heating flow rate";
  parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal=-100000
    "Design cooling flow rate";

  Buildings.Controls.OBC.CDL.Interfaces.RealInput warUpTim(
    final unit="s",
    final quantity="Time")
    "Warm-up time retrieved from optimal warm-up block"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput cooDowTim(
    final unit="s",
    final quantity="Time")
    "Cool-down time retrieved from optimal cool-down block"
  Buildings.Controls.OBC.CDL.Interfaces.RealInput tNexOcc(
    final unit="s",
    final quantity="Time")
    "Next occupancy time"
  Buildings.Controls.OBC.CDL.Interfaces.BooleanInput uOcc
    "Current occupancy period, true if it is in occupant period"
  Buildings.Controls.OBC.CDL.Interfaces.RealOutput TZon(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature") "Zone air temperature"

  Buildings.Controls.OBC.ASHRAE.G36.AHUs.SingleZone.VAV.Controller con(
    eneStd=Buildings.Controls.OBC.ASHRAE.G36.Types.EnergyStandard.ASHRAE90_1,
    venStd=Buildings.Controls.OBC.ASHRAE.G36.Types.VentilationStandard.ASHRAE62_1,

    VAreBreZon_flow=0.4989,
    VPopBreZon_flow=0.2075,
    ecoHigLimCon=Buildings.Controls.OBC.ASHRAE.G36.Types.ControlEconomizer.FixedDryBulb,

    ashCliZon=Buildings.Controls.OBC.ASHRAE.G36.Types.ASHRAEClimateZone.Zone_6B,

    freSta=Buildings.Controls.OBC.ASHRAE.G36.Types.FreezeStat.No_freeze_stat,
    have_winSen=false,
    have_CO2Sen=false,
    buiPreCon=Buildings.Controls.OBC.ASHRAE.G36.Types.PressureControl.BarometricRelief,

    have_locAdj=false,
    TSupDew_max=297.15,
    maxHeaSpe=0.2,
    maxCooSpe=1,
    minSpe=0.1,
    kCoo=0.1,
    kCooCoi=1,
    VOutMin_flow=0.5,
    VOutDes_flow=0.71,
    kHea=0.1,
    kMod=4,
    have_occSen=false,
    TSup_max=343.15,
    TSup_min=286.15,
    uLow=0,
    uHigh=0.5) "VAV controller"
  Buildings.ThermalZones.Detailed.Validation.BaseClasses.SingleZoneFloor sinZonFlo(
    redeclare package Medium = MediumA)
    "Single zone floor"
  Buildings.Air.Systems.SingleZone.VAV.ChillerDXHeatingEconomizer hvac(
    redeclare package MediumA = MediumA,
    redeclare package MediumW = MediumW,
    mAir_flow_nominal=mAir_flow_nominal,
    etaHea_nominal=0.99,
    QHea_flow_nominal=QHea_flow_nominal,
    QCoo_flow_nominal=QCoo_flow_nominal,
    TSupChi_nominal=TSupChi_nominal) "HVAC system"
  Buildings.Controls.OBC.CDL.Reals.Hysteresis hysChiPla1(uLow=-1, uHigh=0)
    "Hysteresis with delay to switch on cooling"
  Modelica.Blocks.Math.Feedback errTRooCoo1
    "Control error on room temperature for cooling"
  Buildings.BoundaryConditions.WeatherData.Bus weaBus
    "Weather bus"
  Buildings.Controls.OBC.CDL.Integers.Sources.Constant demLim(
    final k=0)
    "Cooling and heating demand imit level"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TOccHeaSet(final k=293.15)
    "Occupied heating setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TOccCooSet(final k=297.15)
    "Occupied cooling setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TUnoHeaSet(final k=288.15)
    "Unoccupied heating setpoint"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TUnoCooSet(final k=303.15)
    "Unoccupied cooling setpoint"
  Modelica.Blocks.Sources.BooleanConstant freRes(k=true)
    "Freeze protection reset"
protected
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetSupChiConst(
    final k=TSupChi_nominal)
    "Set point for chiller temperature"

equation
  connect(con.yHeaCoi, hvac.uHea)
  connect(weaBus, sinZonFlo.weaBus)
  connect(con.yCooCoi, hvac.uCooVal)
  connect(hvac.supplyAir, sinZonFlo.ports[1])
  connect(hvac.returnAir, sinZonFlo.ports[2])
  connect(errTRooCoo1.y,hysChiPla1. u)
  connect(hysChiPla1.y, hvac.chiOn)
  connect(con.TZonCooSet, errTRooCoo1.u2)
  connect(TSetSupChiConst.y, hvac.TSetChi)
  connect(sinZonFlo.TRooAir, con.TZon)
  connect(demLim.y, con.uCooDemLimLev)
  connect(demLim.y, con.uHeaDemLimLev)
  connect(sinZonFlo.TRooAir, TZon)
  connect(tNexOcc, con.tNexOcc)
  connect(warUpTim, con.warUpTim)
  connect(cooDowTim, con.cooDowTim)
  connect(hvac.weaBus, weaBus)
  connect(sinZonFlo.TRooAir, errTRooCoo1.u1)
  connect(weaBus.TDryBul, con.TOut)
  connect(uOcc, con.u1Occ)
  connect(TOccHeaSet.y, con.TOccHeaSet)
  connect(TOccCooSet.y, con.TOccCooSet)
  connect(TUnoHeaSet.y, con.TUnoHeaSet)
  connect(TUnoCooSet.y, con.TUnoCooSet)
  connect(hvac.TSup, con.TAirSup)
  connect(hvac.TMix, con.TAirMix)
  connect(con.yOutDam, hvac.uEco)
  connect(con.ySupFan, hvac.uFan)
  connect(freRes.y, con.u1SofSwiRes)
  connect(con.yHeaCoi, con.uHeaCoi_actual)
  connect(con.yCooCoi, con.uCooCoi_actual)
  connect(con.ySupFan, con.uSupFan_actual)
end ZoneWithAHUG36;


within Buildings;
package Airflow "Package to compute airflow and contaminant transport between rooms"
  extends Modelica.Icons.Package;
end Airflow;


Powerlaw with coefficient for mass flow rate. This model describes the one-directional pressure driven air flow through an opening, using the equation

within Buildings.Airflow.Multizone;
model Coefficient_m_flow "Powerlaw with coefficient for mass flow rate"
  extends Buildings.Airflow.Multizone.BaseClasses.PartialOneWayFlowElement(
    m_flow=rho*Buildings.Airflow.Multizone.BaseClasses.powerLawFixedM(
        C=C,
        dp=dp,
        m=m,
        a=a,
        b=b,
        c=c,
        d=d,
        dp_turbulent=dp_turbulent),
    final m_flow_nominal=k*dp_turbulent,
    final m_flow_small=1E-4*abs(m_flow_nominal));
  extends Buildings.Airflow.Multizone.BaseClasses.PowerLawResistanceParameters(
    m = 0.5);
  parameter Real k "Flow coefficient, k = m_flow/ dp^m";
protected
  parameter Real C=k/rho_default "Flow coefficient, C = V_flow/dp^m";
end Coefficient_m_flow;


Power law with coefficient for volume flow rate. This model describes the one-directional pressure driven air flow through an opening, using the equation

within Buildings.Airflow.Multizone;
model Coefficient_V_flow "Power law with coefficient for volume flow rate"
  extends Buildings.Airflow.Multizone.BaseClasses.PartialOneWayFlowElement(
    m_flow = V_flow*rho,
    V_flow = Buildings.Airflow.Multizone.BaseClasses.powerLawFixedM(
      C=C,
      dp=dp,
      m=m,
      a=a,
      b=b,
      c=c,
      d=d,
      dp_turbulent=dp_turbulent),
    final m_flow_nominal=rho_default*C*dp_turbulent,
    final m_flow_small=1E-4*abs(m_flow_nominal));
   extends Buildings.Airflow.Multizone.BaseClasses.PowerLawResistanceParameters(
     m = 0.5);

  parameter Real C "Flow coefficient, C = V_flow/ dp^m";
end Coefficient_V_flow;


Door model using discretization along height coordinate. This model describes the bi-directional air flow through an open door.

within Buildings.Airflow.Multizone;
model DoorDiscretizedOpen
  "Door model using discretization along height coordinate"
  extends Buildings.Airflow.Multizone.BaseClasses.DoorDiscretized;

  parameter Real CD=0.65 "Discharge coefficient"

protected
  constant Real mFixed = 0.5 "Fixed value for flow coefficient";
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  constant Real a = gamma
    "Polynomial coefficient for regularized implementation of flow resistance";
  constant Real b = 1/8*mFixed^2 - 3*gamma - 3/2*mFixed + 35.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
  constant Real c = -1/4*mFixed^2 + 3*gamma + 5/2*mFixed - 21.0/4
    "Polynomial coefficient for regularized implementation of flow resistance";
  constant Real d = 1/8*mFixed^2 - gamma - mFixed + 15.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
equation
  m=mFixed;
  A = wOpe*hOpe;
  CVal = CD*dA*sqrt(2/rho_default);
  // orifice equation
  for i in 1:nCom loop
    dV_flow[i] = Buildings.Airflow.Multizone.BaseClasses.powerLawFixedM(
      C=CVal,
      dp=dpAB[i],
      m=mFixed,
      a=a,
      b=b,
      c=c,
      d=d,
      dp_turbulent=dp_turbulent);
  end for;
end DoorDiscretizedOpen;


within Buildings.Airflow.Multizone;
model DoorDiscretizedOperable
  "Door model using discretization along height coordinate"
  extends Buildings.Airflow.Multizone.BaseClasses.DoorDiscretized;

  parameter Modelica.Units.SI.PressureDifference dpCloRat(
    min=0,
    displayUnit="Pa") = 4 "Pressure drop at rating condition of closed door"

  parameter Real CDCloRat(min=0, max=1)=1
    "Discharge coefficient at rating conditions of closed door"

  parameter Modelica.Units.SI.Area LClo(min=0)
    "Effective leakage area of closed door"

  parameter Real CDOpe=0.65 "Discharge coefficient of open door"
  parameter Real CDClo=0.65 "Discharge coefficient of closed door"

  parameter Real mOpe = 0.5 "Flow exponent for door of open door"
  parameter Real mClo= 0.65 "Flow exponent for crack of closed door"

  Modelica.Blocks.Interfaces.RealInput y(min=0, max=1, unit="1")
    "Opening signal, 0=closed, 1=open"
protected
  parameter Modelica.Units.SI.Area AOpe=wOpe*hOpe "Open aperture area";
  parameter Modelica.Units.SI.Area AClo(fixed=false) "Closed aperture area";

  Real COpe "Open aperture flow coefficient, C = V_flow/ dp^m";
  Real CClo "Closed aperture flow coefficient, C = V_flow/ dp^m";

 Real fraOpe "Fraction of aperture that is open";
initial equation
  AClo=CDClo/CDCloRat * LClo * dpCloRat^(0.5-mClo);
equation
  fraOpe =y;
  CClo = CDClo * AClo/nCom * sqrt(2/rho_default);
  COpe = CDOpe * AOpe/nCom * sqrt(2/rho_default);

  // flow exponent
  m    = fraOpe*mOpe + (1-fraOpe)*mClo;
  // opening area
  A = fraOpe*AOpe + (1-fraOpe)*AClo;
  // friction coefficient for power law
  CVal = fraOpe*COpe + (1-fraOpe)*CClo;

  // orifice equation
  for i in 1:nCom loop
    dV_flow[i] = Buildings.Airflow.Multizone.BaseClasses.powerLaw(C=CVal,
      dp=dpAB[i],
      m=m,
      dp_turbulent=dp_turbulent);
  end for;
end DoorDiscretizedOperable;


Door model for bi-directional air flow between rooms. Model for bi-directional air flow through a large opening such as a door.

within Buildings.Airflow.Multizone;
model DoorOpen
  "Door model for bi-directional air flow between rooms"
  extends Buildings.Airflow.Multizone.BaseClasses.Door(
    final vAB = VAB_flow/AOpe,
    final vBA = VBA_flow/AOpe);

  parameter Real CD=0.65 "Discharge coefficient"

  parameter Real m = 0.5 "Flow coefficient"

protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  constant Real a = gamma
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real b = 1/8*m^2 - 3*gamma - 3/2*m + 35.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real c = -1/4*m^2 + 3*gamma + 5/2*m - 21.0/4
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real d = 1/8*m^2 - gamma - m + 15.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";

  parameter Real CVal=CD*AOpe*sqrt(2/rho_default)
    "Flow coefficient, C = V_flow/ dp^m";
  parameter Real kT = rho_default * CD * AOpe/3 *
    sqrt(Modelica.Constants.g_n /(Medium.T_default*conTP) * hOpe)
    "Constant coefficient for buoyancy driven air flow rate";

  parameter Modelica.Units.SI.MassFlowRate m_flow_turbulent=CVal*rho_default*
      sqrt(dp_turbulent)
    "Mass flow rate where regularization to laminar flow occurs for temperature-driven flow";

equation
  // Air flow rate due to static pressure difference
  VABp_flow = Buildings.Airflow.Multizone.BaseClasses.powerLawFixedM(C=CVal,
      dp=port_a1.p-port_a2.p,
      m=m,
      a=a,
      b=b,
      c=c,
      d=d,
      dp_turbulent=dp_turbulent);
  // Air flow rate due to buoyancy
  // Because powerLawFixedM requires as an input a pressure difference pa-pb,
  // we convert Ta-Tb by multiplying it with rho*R, and we divide
  // above the constant expression by (rho*R)^m on the right hand-side of kT.
  // Note that here, k is for mass flow rate, whereas in powerLawFixedM, it is for volume flow rate.
  // We can use m=0.5 as this is from Bernoulli.
  mABt_flow = Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
      k=kT,
      dp=conTP*(Medium.temperature(state_a1_inflow)-Medium.temperature(state_a2_inflow)),
      m_flow_turbulent=m_flow_turbulent);
end DoorOpen;


Door model for bi-directional air flow between rooms that can be open or closed. Model for bi-directional air flow through a large opening such as a door which can be opened or closed
based on the control input signal <i>y</i>.

within Buildings.Airflow.Multizone;
model DoorOperable
  "Door model for bi-directional air flow between rooms that can be open or closed"
  extends Buildings.Airflow.Multizone.BaseClasses.Door(
    final vAB = VAB_flow/A,
    final vBA = VBA_flow/A);

  parameter Real CDOpe=0.65 "Discharge coefficient of open door"

  parameter Real mOpe = 0.5 "Flow exponent for door of open door"

  parameter Modelica.Units.SI.Area LClo(min=0)
    "Effective leakage area of closed door"

  parameter Real mClo= 0.65 "Flow exponent for crack of closed door"

  parameter Modelica.Units.SI.PressureDifference dpCloRat(
    min=0,
    displayUnit="Pa") = 4 "Pressure drop at rating condition of closed door"

  parameter Real CDCloRat(min=0, max=1)=1
    "Discharge coefficient at rating conditions of closed door"

  Modelica.Blocks.Interfaces.RealInput y(min=0, max=1, unit="1")
    "Opening signal, 0=closed, 1=open"

protected
  parameter Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  parameter Real[2] a = {gamma, gamma}
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real b[2] = {1/8*m^2 - 3*gamma - 3/2*m + 35.0/8 for m in {mOpe, mClo}}
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real c[2] = {-1/4*m^2 + 3*gamma + 5/2*m - 21.0/4 for m in {mOpe, mClo}}
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real d[2] = {1/8*m^2 - gamma - m + 15.0/8 for m in {mOpe, mClo}}
    "Polynomial coefficient for regularized implementation of flow resistance";

  parameter Modelica.Units.SI.Area AClo=LClo*dpCloRat^(0.5 - mClo)
    "Closed area";
  parameter Real CVal[2]=
    {CDOpe*AOpe*sqrt(2/rho_default),
    CDCloRat*AClo*sqrt(2/rho_default)}
    "Flow coefficient, C = V_flow/ dp^m";

  parameter Real kT = rho_default * CDOpe * AOpe/3 *
    sqrt(Modelica.Constants.g_n /(Medium.T_default*conTP) * hOpe)
    "Constant coefficient for buoyancy driven air flow rate";

  parameter Modelica.Units.SI.MassFlowRate m_flow_turbulent=CVal[1]*rho_default
      *sqrt(dp_turbulent)
    "Mass flow rate where regularization to laminar flow occurs for temperature-driven flow";

  Modelica.Units.SI.VolumeFlowRate VABpOpeClo_flow[2](each nominal=0.001)
    "Volume flow rate from A to B if positive due to static pressure difference";

  Modelica.Units.SI.Area A "Current opening area";
equation
  // Air flow rate due to static pressure difference
  VABpOpeClo_flow = Buildings.Airflow.Multizone.BaseClasses.powerLawFixedM(
    C=CVal,
    dp=port_a1.p-port_a2.p,
    m={mOpe, mClo},
    a=a,
    b=b,
    c=c,
    d=d,
    dp_turbulent=dp_turbulent);
  VABp_flow = y*VABpOpeClo_flow[1] + (1-y)*VABpOpeClo_flow[2];
  A = y*AOpe + (1-y)*AClo;
  // Air flow rate due to buoyancy
  // Because powerLawFixedM requires as an input a pressure difference pa-pb,
  // we convert Ta-Tb by multiplying it with rho*R, and we divide
  // above the constant expression by (rho*R)^m on the right hand-side of kT.
  mABt_flow = y*Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
      k=kT,
      dp=conTP*(Medium.temperature(state_a1_inflow)-Medium.temperature(state_a2_inflow)),
      m_flow_turbulent=m_flow_turbulent);
end DoorOperable;


Effective air leakage area. This model describes the one-directional pressure driven
air flow through a crack-like opening, using the equation

within Buildings.Airflow.Multizone;
model EffectiveAirLeakageArea "Effective air leakage area"
  extends Buildings.Airflow.Multizone.Coefficient_V_flow(
    m=0.65,
    final C=L * CDRat * sqrt(2.0/rho_default) * dpRat^(0.5-m));

  parameter Modelica.Units.SI.PressureDifference dpRat(
    min=0,
    displayUnit="Pa") = 4 "Pressure drop"
  parameter Real CDRat(
    min=0,
    max=1) = 1 "Discharge coefficient"

  parameter Modelica.Units.SI.Area L(min=0) "Effective leakage area";

  Modelica.Units.SI.Velocity v(nominal=1) = V_flow/L "Average velocity";
end EffectiveAirLeakageArea;


within Buildings.Airflow.Multizone;
model MediumColumn
  "Vertical shaft with no friction and no storage of heat and mass"

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Modelica.Units.SI.Length h(min=0) = 3 "Height of shaft";
  parameter Buildings.Airflow.Multizone.Types.densitySelection densitySelection
    "Select how to pick density"

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

  Modelica.Units.SI.VolumeFlowRate V_flow
    "Volume flow rate at inflowing port (positive when flow from port_a to port_b)";
  Modelica.Units.SI.MassFlowRate m_flow
    "Mass flow rate from port_a to port_b (m_flow > 0 is design flow direction)";
  Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference between port_a and port_b";
  Modelica.Units.SI.Density rho "Density in medium column";
protected
  Medium.ThermodynamicState sta_a=Medium.setState_phX(
      port_a.p,
      actualStream(port_a.h_outflow),
      actualStream(port_a.Xi_outflow)) "Medium properties in port_a";
  Medium.MassFraction Xi[Medium.nXi] "Mass fraction used to compute density";
initial equation
  // The next assert tests for all allowed values of the enumeration.
  // Testing against densitySelection > 0 gives an error in OpenModelica as enumerations start with 1.
  assert(densitySelection == Buildings.Airflow.Multizone.Types.densitySelection.fromTop
     or densitySelection == Buildings.Airflow.Multizone.Types.densitySelection.fromBottom
     or densitySelection == Buildings.Airflow.Multizone.Types.densitySelection.actual,
    "You need to set the parameter \"densitySelection\" for the model MediumColumn.");
equation
  // Design direction of mass flow rate
  m_flow = port_a.m_flow;

  // Pressure difference between ports
  // Xi is computed first as it is used in two expression, and in one
  // of them only one component is used.
  // We test for Medium.nXi == 0 as Modelica.Media.Air.SimpleAir has no
  // moisture and hence Xi[1] is an illegal statement.
  // We first compute temperature and then invoke a density function that
  // takes temperature as an argument. Simply calling a density function
  // of a medium that takes enthalpy as an argument would be dangerous
  // as different media can have different datum for the enthalpy.
  if (densitySelection == Buildings.Airflow.Multizone.Types.densitySelection.fromTop) then
      Xi = inStream(port_a.Xi_outflow);
      rho = Buildings.Utilities.Psychrometrics.Functions.density_pTX(
        p=Medium.p_default,
        T=Medium.temperature(Medium.setState_phX(port_a.p, inStream(port_a.h_outflow), Xi)),
        X_w=if Medium.nXi == 0 then 0 else Xi[1]);
  elseif (densitySelection == Buildings.Airflow.Multizone.Types.densitySelection.fromBottom) then
      Xi = inStream(port_b.Xi_outflow);
      rho = Buildings.Utilities.Psychrometrics.Functions.density_pTX(
        p=Medium.p_default,
        T=Medium.temperature(Medium.setState_phX(port_b.p, inStream(port_b.h_outflow), Xi)),
        X_w=if Medium.nXi == 0 then 0 else Xi[1]);
   else
      Xi = actualStream(port_a.Xi_outflow);
      rho = Buildings.Utilities.Psychrometrics.Functions.density_pTX(
        p=Medium.p_default,
        T=Medium.temperature(Medium.setState_phX(port_a.p, actualStream(port_a.h_outflow), Xi)),
        X_w=if Medium.nXi == 0 then 0 else Xi[1]);
  end if;

  V_flow = m_flow/Medium.density(sta_a);

  dp = port_a.p - port_b.p;
  dp = -h*rho*Modelica.Constants.g_n;

  // Isenthalpic state transformation (no storage and no loss of energy)
  port_a.h_outflow = inStream(port_b.h_outflow);
  port_b.h_outflow = inStream(port_a.h_outflow);

  // Mass balance (no storage)
  port_a.m_flow + port_b.m_flow = 0;

  // Transport of substances
  port_a.Xi_outflow = inStream(port_b.Xi_outflow);
  port_b.Xi_outflow = inStream(port_a.Xi_outflow);

  port_a.C_outflow = inStream(port_b.C_outflow);
  port_b.C_outflow = inStream(port_a.C_outflow);
end MediumColumn;


within Buildings.Airflow.Multizone;
model MediumColumnDynamic
  "Vertical shaft with no friction and storage of heat and mass"
  extends Buildings.Fluid.Interfaces.LumpedVolumeDeclarations(
    final massDynamics=energyDynamics);

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Modelica.Units.SI.Length h(min=0) = 3 "Height of shaft";

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare final package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare final package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

  parameter Modelica.Units.SI.Volume V "Volume in medium shaft";

  // Heat transfer through boundary
  parameter Boolean use_HeatTransfer = false
    "= true to use the HeatTransfer model"

  // m_flow_nominal is not used by vol, since this component
  // can only be configured as a dynamic model.
  // Therefore we set it to about 10 air changes per hour
  Buildings.Fluid.MixingVolumes.MixingVolume vol(
    final nPorts=2,
    redeclare final package Medium = Medium,
    final m_flow_nominal=V/360,
    final V=V,
    final energyDynamics=energyDynamics,
    final massDynamics=massDynamics,
    final p_start=p_start,
    final T_start=T_start,
    final X_start=X_start,
    final C_start=C_start) "Air volume in the shaft"

  MediumColumn colTop(
    redeclare final package Medium = Medium,
    final densitySelection=Buildings.Airflow.Multizone.Types.densitySelection.fromBottom,
    h=h/2)
    "Medium column that connects to top port"

  MediumColumn colBot(
    redeclare final package Medium = Medium,
    final densitySelection=Buildings.Airflow.Multizone.Types.densitySelection.fromTop,
    h=h/2)
    "Medium colum that connects to bottom port"

  replaceable model HeatTransfer =
      Modelica.Fluid.Vessels.BaseClasses.HeatTransfer.IdealHeatTransfer
    constrainedby
    Modelica.Fluid.Vessels.BaseClasses.HeatTransfer.PartialVesselHeatTransfer
    "Wall heat transfer"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort if use_HeatTransfer
    "Heat port to exchange energy with the fluid volume"

equation
  connect(colBot.port_a, vol.ports[1])
  connect(vol.ports[2], colTop.port_b)
  connect(colTop.port_a, port_a)

  connect(colBot.port_b, port_b)
  connect(heatPort, vol.heatPort)
end MediumColumnDynamic;


Orifice. This model describes the mass flow rate and pressure difference relation
of an orifice in the form

within Buildings.Airflow.Multizone;
model Orifice "Orifice"
  extends Buildings.Airflow.Multizone.Coefficient_V_flow(
    m=0.5,
    final C=CD*A*sqrt(2.0/rho_default));

  parameter Modelica.Units.SI.Area A "Area of orifice"
  parameter Real CD=0.65 "Discharge coefficient"

  Modelica.Units.SI.Velocity v(nominal=1) = V_flow/A "Average velocity";
end Orifice;


within Buildings.Airflow;
package Multizone "Package with models for multizone airflow and contaminant transport"
  extends Modelica.Icons.VariantsPackage;
end Multizone;


Powerlaw with flow coefficient and flow exponent fitted based on 2 datapoints. Model that fits the flow coefficient of the massflow version of the
orifice equation based on 2 datapoints of mass flow rate and pressure difference.

within Buildings.Airflow.Multizone;
model Points_m_flow
  "Powerlaw with flow coefficient and flow exponent fitted based on 2 datapoints"
  extends Buildings.Airflow.Multizone.Coefficient_m_flow(final k=
    mMea_flow_nominal[1]/(dpMea_nominal[1]^m2),
    final m=m2);

  parameter Modelica.Units.SI.PressureDifference dpMea_nominal[2](each displayUnit="Pa")
    "Pressure difference of two test points"
  parameter Modelica.Units.SI.MassFlowRate mMea_flow_nominal[2]
    "Mass flow rate of two test points"
protected
  parameter Real m2 = (log(mMea_flow_nominal[1]) - log(mMea_flow_nominal[2]))/
             (log(dpMea_nominal[1]) -log(dpMea_nominal[2])) "Flow exponent";
end Points_m_flow;


Powerlaw with flow coeffient fitted based on flow exponent and 1 datapoint. Model that fits the flow coefficient of the massflow version of the
orifice equation based on 1 datapoint of mass flow rate and pressure difference, and given flow exponent.

within Buildings.Airflow.Multizone;
model Point_m_flow
  "Powerlaw with flow coeffient fitted based on flow exponent and 1 datapoint"
  extends Buildings.Airflow.Multizone.Coefficient_m_flow(final k=mMea_flow_nominal/
        (dpMea_nominal^m));
  parameter Modelica.Units.SI.PressureDifference dpMea_nominal(displayUnit="Pa")
    "Pressure difference of test point"
  parameter Modelica.Units.SI.MassFlowRate mMea_flow_nominal
    "Mass flow rate of test point"
end Point_m_flow;


Mass flow(y-axis) vs Pressure(x-axis) cubic spline fit model based from table data, with last two points linearly interpolated. This model describes the one-directional pressure driven air flow through an
opening based on user-provided tabular data describing the relation between mass flow rate
and pressure difference over the component.

within Buildings.Airflow.Multizone;
model Table_m_flow
  "Mass flow(y-axis) vs Pressure(x-axis) cubic spline fit model based from table data, with last two points linearly interpolated"
  extends Buildings.Airflow.Multizone.BaseClasses.PartialOneWayFlowElement(
    m_flow = Buildings.Utilities.Math.Functions.interpolate(
      u=dp,
      xd=dpMea_nominal,
      yd=mMea_flow_nominal,
      d=d),
    final m_flow_nominal=max(abs(dpMea_nominal[1]), abs(dpMea_nominal[end])));

  parameter Modelica.Units.SI.PressureDifference dpMea_nominal[:](each displayUnit="Pa")
    "Pressure difference of test points"
  parameter Modelica.Units.SI.MassFlowRate mMea_flow_nominal[:]
    "Mass flow rate of test points"
protected
  parameter Real[size(dpMea_nominal, 1)] d = Buildings.Utilities.Math.Functions.splineDerivatives(
    x=dpMea_nominal,
    y=mMea_flow_nominal,
    ensureMonotonicity=true)
    "Derivatives at the support points";
initial equation
  assert(size(dpMea_nominal, 1) == size(mMea_flow_nominal, 1),
    "Size of parameters are size(dpMea_nominal, 1) = " + String(size(dpMea_nominal, 1)) +
    " and size(mMea_flow_nominal, 1) = " + String(size(mMea_flow_nominal, 1)) + ". They must be equal.");
end Table_m_flow;


Volume flow(y-axis) vs Pressure(x-axis) cubic spline fit model based on table data, with last two points linearly interpolated. This model describes the one-directional pressure driven air flow through an
opening based on user-provided tabular data describing the relation between volume flow rate
and pressure difference over the component.

within Buildings.Airflow.Multizone;
model Table_V_flow
  "Volume flow(y-axis) vs Pressure(x-axis) cubic spline fit model based on table data, with last two points linearly interpolated"
  extends Buildings.Airflow.Multizone.Table_m_flow(
    final mMea_flow_nominal = VMea_flow_nominal*rho_default);

  parameter Modelica.Units.SI.VolumeFlowRate VMea_flow_nominal[:]
    "Volume flow rate of test points"
initial equation
  assert(size(dpMea_nominal, 1) == size(VMea_flow_nominal, 1),
    "Size of parameters are size(dpMea_nominal, 1) = " + String(size(dpMea_nominal, 1)) +
    " and size(VMea_flow_nominal, 1) = " + String(size(VMea_flow_nominal, 1)) + ". They must be equal.");
end Table_V_flow;


User's Guide

within Buildings.Airflow.Multizone;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

end UsersGuide;


Zonal flow with input air change per second. This model computes the air exchange between volumes.

within Buildings.Airflow.Multizone;
model ZonalFlow_ACS "Zonal flow with input air change per second"
  extends Buildings.Airflow.Multizone.BaseClasses.ZonalFlow;

  parameter Boolean useDefaultProperties = false
    "Set to true to use constant density";
  parameter Modelica.Units.SI.Volume V "Volume of room";

  Modelica.Blocks.Interfaces.RealInput ACS
    "Air change per seconds, relative to the smaller of the two volumes"
protected
  Modelica.Units.SI.VolumeFlowRate V_flow
    "Volume flow rate at standard pressure";
  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX(T=Medium.T_default,
         p=Medium.p_default, X=Medium.X_default);
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid volume";

  Medium.ThermodynamicState sta_a1_inflow=
      Medium1.setState_phX(port_a1.p, port_b1.h_outflow, port_b1.Xi_outflow)
    "Medium properties in port_a1";
  Medium.ThermodynamicState sta_a2_inflow=
      Medium1.setState_phX(port_a2.p, port_b2.h_outflow, port_b2.Xi_outflow)
    "Medium properties in port_a2";

equation
  when useDefaultProperties and initial() then
   assert( abs(1-rho_default/((Medium.density(sta_a1_inflow) + Medium.density(sta_a2_inflow))/2))  < 0.2,
    "Wrong density. Densities need to match.");
// The next three lines have been removed as they cause
// a compilation error in OpenModelica.
//    + "\n Medium.density(sta_a1) = " + String(Medium.density(sta_a1_inflow))
//    + "\n Medium.density(sta_a2) = " + String(Medium.density(sta_a2_inflow))
//    + "\n rho_nominal            = " + String(rho_default));
  end when;
  V_flow = V * ACS;
  m_flow / V_flow = if useDefaultProperties then rho_default else (Medium.density(sta_a1_inflow) + Medium.density(sta_a2_inflow))/2;
  // assign variable in base class
  port_a1.m_flow = m_flow;
  port_a2.m_flow = m_flow;
end ZonalFlow_ACS;


within Buildings.Airflow.Multizone;
model ZonalFlow_m_flow "Zonal flow with input air change per second"
  extends Buildings.Airflow.Multizone.BaseClasses.ZonalFlow;
   Modelica.Blocks.Interfaces.RealInput mAB_flow "Mass flow rate from A to B, positive if flow from port_a1 to port_b1"
  Modelica.Blocks.Interfaces.RealInput mBA_flow "Mass flow rate from B to A, positive if flow from port_a2 to port_b2"

equation
  port_a1.m_flow = mAB_flow;
  port_a2.m_flow = mBA_flow;
end ZonalFlow_m_flow;


within Buildings.Airflow.Multizone.BaseClasses;
partial model Door
  "Partial door model for bi-directional flow"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final allowFlowReversal1=true,
    final allowFlowReversal2=true,
    final m1_flow_nominal=10/3600*1.2,
    final m2_flow_nominal=m1_flow_nominal,
    final m1_flow_small=1E-4*abs(m1_flow_nominal),
    final m2_flow_small=1E-4*abs(m2_flow_nominal));

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Modelica.Units.SI.Length wOpe=0.9 "Width of opening"
  parameter Modelica.Units.SI.Length hOpe=2.1 "Height of opening"

  parameter Modelica.Units.SI.PressureDifference dp_turbulent(
    min=0,
    displayUnit="Pa") = 0.01
    "Pressure difference where laminar and turbulent flow relation coincide"

  Modelica.Units.SI.VolumeFlowRate VAB_flow(nominal=0.001)
    "Volume flow rate from A to B if positive";
  Modelica.Units.SI.VolumeFlowRate VBA_flow(nominal=0.001)
    "Volume flow rate from B to A if positive";

  input Modelica.Units.SI.Velocity vAB(nominal=0.01)
    "Average velocity from A to B";
  input Modelica.Units.SI.Velocity vBA(nominal=0.01)
    "Average velocity from B to A";

protected
  final parameter Modelica.Units.SI.Area AOpe=wOpe*hOpe "Open aperture area";

  constant Real conTP = Buildings.Media.Air.dStp*Modelica.Media.IdealGases.Common.SingleGasesData.Air.R_s
    "Conversion factor for converting temperature difference to pressure difference";

  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default);

  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density";

  Modelica.Units.SI.VolumeFlowRate VABp_flow(nominal=0.001)
    "Volume flow rate from A to B if positive due to static pressure difference";
  Modelica.Units.SI.MassFlowRate mABt_flow(nominal=0.001)
    "Mass flow rate from A to B if positive due to buoyancy";

equation
  // Net flow rate
  port_a1.m_flow = (rho_default * VABp_flow/2 + mABt_flow);
  port_b2.m_flow = (rho_default * VABp_flow/2 - mABt_flow);

  // Average velocity (using the whole orifice area)
  VAB_flow = (max(port_a1.m_flow, 0) + max(port_b2.m_flow, 0))/rho_default;
  VBA_flow = (max(port_a2.m_flow, 0) + max(port_b1.m_flow, 0))/rho_default;

  // Energy balance (no storage, no heat loss/gain)
  port_a1.h_outflow = inStream(port_b1.h_outflow);
  port_b1.h_outflow = inStream(port_a1.h_outflow);
  port_a2.h_outflow = inStream(port_b2.h_outflow);
  port_b2.h_outflow = inStream(port_a2.h_outflow);

  // Mass balance (no storage)
  port_a1.m_flow = -port_b1.m_flow;
  port_a2.m_flow = -port_b2.m_flow;

  port_a1.Xi_outflow = inStream(port_b1.Xi_outflow);
  port_b1.Xi_outflow = inStream(port_a1.Xi_outflow);
  port_a2.Xi_outflow = inStream(port_b2.Xi_outflow);
  port_b2.Xi_outflow = inStream(port_a2.Xi_outflow);

  // Transport of trace substances
  port_a1.C_outflow = inStream(port_b1.C_outflow);
  port_b1.C_outflow = inStream(port_a1.C_outflow);
  port_a2.C_outflow = inStream(port_b2.C_outflow);
  port_b2.C_outflow = inStream(port_a2.C_outflow);
end Door;


Door model using discretization along height coordinate. This is a partial model for the bi-directional air flow through a door.

within Buildings.Airflow.Multizone.BaseClasses;
partial model DoorDiscretized
  "Door model using discretization along height coordinate"
  extends Buildings.Airflow.Multizone.BaseClasses.TwoWayFlowElementBuoyancy;

  parameter Integer nCom=10 "Number of compartments for the discretization";

  parameter Modelica.Units.SI.PressureDifference dp_turbulent(
    min=0,
    displayUnit="Pa") = 0.01
    "Pressure difference where laminar and turbulent flow relation coincide. Recommended: 0.01";

  Modelica.Units.SI.PressureDifference dpAB[nCom](each nominal=1)
    "Pressure difference between compartments";
  Modelica.Units.SI.Velocity v[nCom](each nominal=0.01)
    "Velocity in compartment from A to B";
  Modelica.Units.SI.Velocity vTop "Velocity at top of opening from A to B";
  Modelica.Units.SI.Velocity vBot "Velocity at bottom of opening from A to B";

protected
  parameter Modelica.Units.SI.Length dh=hOpe/nCom "Height of each compartment";

  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default);

  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid volume";

  parameter Real hAg[nCom](each unit="m2/s2")=
    {Modelica.Constants.g_n*(hA - (i - 0.5)*dh) for i in 1:nCom}
    "Product g*h_i for each compartment";

  parameter Real hBg[nCom](each unit="m2/s2")=
    {Modelica.Constants.g_n*(hB - (i - 0.5)*dh) for i in 1:nCom}
    "Product g*h_i for each compartment";
  Modelica.Units.SI.AbsolutePressure pA[nCom](each nominal=101325)
    "Pressure in compartments of room A";
  Modelica.Units.SI.AbsolutePressure pB[nCom](each nominal=101325)
    "Pressure in compartments of room B";

  Modelica.Units.SI.VolumeFlowRate dV_flow[nCom]
    "Volume flow rate through compartment from A to B";
  Modelica.Units.SI.VolumeFlowRate dVAB_flow[nCom]
    "Volume flow rate through compartment from A to B if positive";
  Modelica.Units.SI.VolumeFlowRate dVBA_flow[nCom]
    "Volume flow rate through compartment from B to A if positive";
  Modelica.Units.SI.VolumeFlowRate VZerCom_flow=VZer_flow/nCom
    "Small flow rate for regularization";

  Real m(min=0.5, max=1) "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  Real CVal "Flow coefficient for each compartment, C = V_flow/ dp^m";
  Modelica.Units.SI.Area dA "Compartment area";
  Real gaiFlo[nCom] "Gain to sum up the positive flows and set the negative to zero in a differentiable way";
equation
  dA = A/nCom;

  for i in 1:nCom loop
    // pressure drop in each compartment
    pA[i] = port_a1.p + rho_a1_inflow*hAg[i];
    pB[i] = port_a2.p + rho_a2_inflow*hBg[i];
    dpAB[i] = pA[i] - pB[i];
    v[i] = dV_flow[i]/dA;
    // assignment of net volume flows
    gaiFlo[i] = Buildings.Utilities.Math.Functions.smoothHeaviside(x=dV_flow[i], delta=VZerCom_flow);
    dVAB_flow[i] =  dV_flow[i] * gaiFlo[i];
    dVBA_flow[i] = -dV_flow[i] * (1-gaiFlo[i]);
  end for;
  // add positive and negative flows
  VAB_flow = sum(dVAB_flow);
  VBA_flow = sum(dVBA_flow);
  vTop = v[nCom];
  vBot = v[1];
end DoorDiscretized;


Interface that defines parameters for error control. This is an interface that defines parameters used for error control.

within Buildings.Airflow.Multizone.BaseClasses;
model ErrorControl "Interface that defines parameters for error control"
  parameter Boolean forceErrorControlOnFlow = true
    "Flag to force error control on m_flow. Set to true if interested in flow rate"
end ErrorControl;


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


Partial model for flow resistance with one-way flow. This partial model is used to model one way flow-elements.
It holds the conservation equations and should be extended by
definition of one of the following variables:

within Buildings.Airflow.Multizone.BaseClasses;
partial model PartialOneWayFlowElement
  "Partial model for flow resistance with one-way flow"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    final allowFlowReversal=true);
  extends Buildings.Airflow.Multizone.BaseClasses.ErrorControl;

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

  parameter Boolean useDefaultProperties=true
    "Set to false to use density and viscosity based on actual medium state, rather than using default values"
  parameter Modelica.Units.SI.PressureDifference dp_turbulent(min=0, displayUnit="Pa") = 0.1
    "Pressure difference where laminar and turbulent flow relation coincide. Recommended = 0.1"

  Modelica.Units.SI.VolumeFlowRate V_flow = m_flow/rho
    "Volume flow rate through the component";
  Modelica.Units.SI.Density rho "Fluid density at port_a";

protected
  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default)
    "State of the medium at the medium default properties";
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density at the medium default properties";
  parameter Modelica.Units.SI.DynamicViscosity dynVis_default=
    Medium.dynamicViscosity(sta_default)
    "Dynamic viscosity at the medium default properties";

  Medium.ThermodynamicState sta "State of the medium in the component";
  Modelica.Units.SI.DynamicViscosity dynVis "Dynamic viscosity";
  Real mExc(quantity="Mass", final unit="kg")
    "Air mass exchanged (for purpose of error control only)";

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

equation
  if forceErrorControlOnFlow then
    der(mExc) = port_a.m_flow;
  else
    der(mExc) = 0;
  end if;

  if useDefaultProperties then
    sta = sta_default;
    rho = rho_default;
    dynVis = dynVis_default;

  else
    sta = if homotopyInitialization then
        Medium.setState_phX(
          port_a.p,
          homotopy(
            actual=actualStream(port_a.h_outflow),
            simplified=inStream(port_a.h_outflow)),
          homotopy(
            actual=actualStream(port_a.Xi_outflow),
            simplified=inStream(port_a.Xi_outflow)))
      else
        Medium.setState_phX(
          port_a.p,
          actualStream(port_a.h_outflow),
          actualStream(port_a.Xi_outflow));

    rho = Medium.density(sta);
    dynVis = Medium.dynamicViscosity(sta);
  end if;

  // Isenthalpic state transformation (no storage and no loss of energy)
  port_a.h_outflow = inStream(port_b.h_outflow);
  port_b.h_outflow = inStream(port_a.h_outflow);

  // Mass balance (no storage)
  port_a.m_flow + port_b.m_flow = 0;

  // Transport of substances
  port_a.Xi_outflow = inStream(port_b.Xi_outflow);
  port_b.Xi_outflow = inStream(port_a.Xi_outflow);

  port_a.C_outflow = inStream(port_b.C_outflow);
  port_b.C_outflow = inStream(port_a.C_outflow);

  m_flow=port_a.m_flow;
end PartialOneWayFlowElement;


Power law used in orifice equations. This model describes the mass flow rate and pressure difference relation
of an orifice in the form

within Buildings.Airflow.Multizone.BaseClasses;
function powerLaw "Power law used in orifice equations"
  extends Modelica.Icons.Function;

  input Real C "Flow coefficient, C = V_flow/ dp^m";
  input Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference";
  input Real m(min=0.5, max=1)
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  input Modelica.Units.SI.PressureDifference dp_turbulent(
    min=0,
    displayUnit="Pa") = 0.001 "Pressure difference where regularization starts";
  output Modelica.Units.SI.VolumeFlowRate V_flow "Volume flow rate";
protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  Real a
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real b
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real c
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real d
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real pi "Normalized pressure";
  Real pi2 "Square of normalized pressure";
algorithm
 if (dp >= dp_turbulent) then
   V_flow :=C *dp^m;
 elseif (dp <= -dp_turbulent) then
   V_flow :=-C*(-dp)^m;
 else
   a := gamma;
   b := 1/8*m^2 - 3*gamma - 3/2*m + 35.0/8;
   c := -1/4*m^2 + 3*gamma + 5/2*m - 21.0/4;
   d := 1/8*m^2 - gamma - m + 15.0/8;
   pi  := dp/dp_turbulent;
   pi2 := pi*pi;
   V_flow :=C *dp_turbulent^m * pi * ( a + pi2 * ( b + pi2 * ( c + pi2 * d)));
 end if;
end powerLaw;


Power law used in orifice equations when m is constant. This model describes the mass flow rate and pressure difference relation
of an orifice in the form

within Buildings.Airflow.Multizone.BaseClasses;
function powerLawFixedM
  "Power law used in orifice equations when m is constant"
  extends Modelica.Icons.Function;

  input Real C "Flow coefficient, C = V_flow/ dp^m";
  input Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference";
  input Real m(min=0.5, max=1)
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  input Real a "Polynomial coefficient";
  input Real b "Polynomial coefficient";
  input Real c "Polynomial coefficient";
  input Real d "Polynomial coefficient";
  input Modelica.Units.SI.PressureDifference dp_turbulent(min=0) = 0.001
    "Pressure difference where regularization starts";
  output Modelica.Units.SI.VolumeFlowRate V_flow "Volume flow rate";
protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  Real pi "Normalized pressure";
  Real pi2 "Square of normalized pressure";
algorithm
 if (dp >= dp_turbulent) then
   V_flow :=C *dp^m;
 elseif (dp <= -dp_turbulent) then
   V_flow :=-C*(-dp)^m;
 else
   pi  := dp/dp_turbulent;
   pi2 := pi*pi;
   V_flow :=C *dp_turbulent^m * pi * ( a + pi2 * ( b + pi2 * ( c + pi2 * d)));
 end if;
end powerLawFixedM;


Power law resistance parameters. Parameters that are required for the components that implement a power law resistance.

within Buildings.Airflow.Multizone.BaseClasses;
model PowerLawResistanceParameters "Power law resistance parameters"
  parameter Real m(min=0.5, max=1)
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";

  parameter Real a = gamma
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real b = 1/8*m^2 - 3*gamma - 3/2*m + 35.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real c = -1/4*m^2 + 3*gamma + 5/2*m - 21.0/4
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real d = 1/8*m^2 - gamma - m + 15.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
end PowerLawResistanceParameters;


Flow resistance that uses the power law

within Buildings.Airflow.Multizone.BaseClasses;
partial model TwoWayFlowElement "Flow resistance that uses the power law"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final allowFlowReversal1=true,
    final allowFlowReversal2=true,
    final m1_flow_nominal=10/3600*1.2,
    final m2_flow_nominal=m1_flow_nominal,
    final m1_flow_small=1E-4*abs(m1_flow_nominal),
    final m2_flow_small=1E-4*abs(m2_flow_nominal));
  extends Buildings.Airflow.Multizone.BaseClasses.ErrorControl;

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Modelica.Units.SI.Velocity vZer=0.001
    "Minimum velocity to prevent zero flow. Recommended: 0.001";

  Modelica.Units.SI.VolumeFlowRate VAB_flow(nominal=0.001)
    "Volume flow rate from A to B if positive";
  Modelica.Units.SI.VolumeFlowRate VBA_flow(nominal=0.001)
    "Volume flow rate from B to A if positive";
  Modelica.Units.SI.MassFlowRate mAB_flow(nominal=0.001)
    "Mass flow rate from A to B if positive";
  Modelica.Units.SI.MassFlowRate mBA_flow(nominal=0.001)
    "Mass flow rate from B to A if positive";

  Modelica.Units.SI.Velocity vAB(nominal=0.01) "Average velocity from A to B";
  Modelica.Units.SI.Velocity vBA(nominal=0.01) "Average velocity from B to A";

  Modelica.Units.SI.Density rho_a1_inflow
    "Density of air flowing in from port_a1";
  Modelica.Units.SI.Density rho_a2_inflow
    "Density of air flowing in from port_a2";

  Modelica.Units.SI.Area A "Face area";
protected
  Modelica.Units.SI.VolumeFlowRate VZer_flow(fixed=false)
    "Minimum net volume flow rate to prevent zero flow";

  Modelica.Units.SI.Mass mExcAB(start=0, fixed=true)
    "Air mass exchanged (for purpose of error control only)";
  Modelica.Units.SI.Mass mExcBA(start=0, fixed=true)
    "Air mass exchanged (for purpose of error control only)";

  Medium.MassFraction Xi_a1_inflow[Medium1.nXi]
    "Mass fraction of medium that flows in at port a1";
  Medium.MassFraction Xi_a2_inflow[Medium2.nXi]
    "Mass fraction of medium that flows in at port a2";
equation
  // enforcing error control on both direction rather than on the sum only
  // gives higher robustness. The reason may be that for bi-directional flow,
  // (VAB_flow - VBA_flow) may be close to zero.
  if forceErrorControlOnFlow then
    der(mExcAB) = mAB_flow;
    der(mExcBA) = mBA_flow;
  else
    der(mExcAB) = 0;
    der(mExcBA) = 0;
  end if;

  // Compute the density of the inflowing media.
  // Note that Modelica.Media.Air.SimpleAir does not contain moisture,
  // and hence we check for Medium?.nXi == 0.
  // We first compute temperature and then invoke a density function that
  // takes temperature as an argument. Simply calling a density function
  // of a medium that takes enthalpy as an argument would be dangerous
  // as different media can have different datum for the enthalpy.
  Xi_a1_inflow = inStream(port_a1.Xi_outflow);
  rho_a1_inflow = Buildings.Utilities.Psychrometrics.Functions.density_pTX(
        p=port_a1.p,
        T=Medium1.temperature(state_a1_inflow),
        X_w=if Medium1.nXi == 0 then 0 else Xi_a1_inflow[1]);
  Xi_a2_inflow = inStream(port_a2.Xi_outflow);
  rho_a2_inflow = Buildings.Utilities.Psychrometrics.Functions.density_pTX(
        p=port_a2.p,
        T=Medium2.temperature(state_a2_inflow),
        X_w=if Medium2.nXi == 0 then 0 else Xi_a2_inflow[1]);

  VZer_flow = vZer*A;

  mAB_flow = rho_a1_inflow*VAB_flow;
  mBA_flow = rho_a2_inflow*VBA_flow;
  // Average velocity (using the whole orifice area)
  vAB = VAB_flow/A;
  vBA = VBA_flow/A;

  port_a1.m_flow = mAB_flow;
  port_a2.m_flow = mBA_flow;

  // Energy balance (no storage, no heat loss/gain)
  port_a1.h_outflow = inStream(port_b1.h_outflow);
  port_b1.h_outflow = inStream(port_a1.h_outflow);
  port_a2.h_outflow = inStream(port_b2.h_outflow);
  port_b2.h_outflow = inStream(port_a2.h_outflow);

  // Mass balance (no storage)
  port_a1.m_flow = -port_b1.m_flow;
  port_a2.m_flow = -port_b2.m_flow;

  port_a1.Xi_outflow = inStream(port_b1.Xi_outflow);
  port_b1.Xi_outflow = inStream(port_a1.Xi_outflow);
  port_a2.Xi_outflow = inStream(port_b2.Xi_outflow);
  port_b2.Xi_outflow = inStream(port_a2.Xi_outflow);

  // Transport of trace substances
  port_a1.C_outflow = inStream(port_b1.C_outflow);
  port_b1.C_outflow = inStream(port_a1.C_outflow);
  port_a2.C_outflow = inStream(port_b2.C_outflow);
  port_b2.C_outflow = inStream(port_a2.C_outflow);
end TwoWayFlowElement;


Flow resistance that uses the power law. This is a partial model for models that describe the bi-directional
air flow through large openings.

within Buildings.Airflow.Multizone.BaseClasses;
partial model TwoWayFlowElementBuoyancy
  "Flow resistance that uses the power law"
  extends Buildings.Airflow.Multizone.BaseClasses.TwoWayFlowElement;

  parameter Modelica.Units.SI.Length wOpe=0.9 "Width of opening"
  parameter Modelica.Units.SI.Length hOpe=2.1 "Height of opening"

  parameter Modelica.Units.SI.Length hA=2.7/2
    "Height of reference pressure zone A"
  parameter Modelica.Units.SI.Length hB=2.7/2
    "Height of reference pressure zone B"
end TwoWayFlowElementBuoyancy;


Wind pressure coefficient for low-rise buildings. This function computes the wind pressure coefficient for
low-rise buildings with rectangular shape.
The correlation is the data fit from Swami and Chandra (1987),
who fitted a function to various wind pressure coefficients from the literature.
The same correlation is also implemented in CONTAM (Persily and Ivy, 2001).

within Buildings.Airflow.Multizone.BaseClasses;
function windPressureLowRise "Wind pressure coefficient for low-rise buildings"
  extends Modelica.Icons.Function;

  input Real Cp0
    "Wind pressure coefficient for normal wind incidence angle";
  input Modelica.Units.SI.Angle alpha
    "Wind incidence angle (0: normal to wall)";
  input Real G "Natural logarithm of side ratio";
  output Real Cp "Wind pressure coefficient";
protected
  constant Modelica.Units.SI.Angle pi2=2*Modelica.Constants.pi;
  constant Modelica.Units.SI.Angle aRDel=5*Modelica.Constants.pi/180
    "Lower bound where transition occurs";
  constant Modelica.Units.SI.Angle aRDel2=aRDel/2
    "Half-width of transition interval";
  constant Modelica.Units.SI.Angle aRMax=175*Modelica.Constants.pi/180
    "Upper bound where transition occurs";
  Real a180 = Modelica.Math.log(1.248 - 0.703 +
              0.131*Modelica.Math.sin(2*Modelica.Constants.pi*G)^3
              + 0.071*G^2) "Attenuation factor at 180 degree incidence angle";

  Modelica.Units.SI.Angle aR "alpha, restricted to 0...pi";
  Modelica.Units.SI.Angle incAng2 "0.5*wind incidence angle";
  Real sinA2 "=sin(alpha/2)";
  Real cosA2 "=cos(alpha/2)";
  Real a "Attenuation factor";
algorithm
  // Restrict incAng to [0...pi]

  // Change sign to positive
  aR :=if alpha < 0 then -alpha else alpha;
  // Constrain to [0...2*pi]
  if aR > pi2 then
    aR := aR - integer(aR/pi2)*pi2;
  end if;
  // Constrain to [0...pi]
  if aR > Modelica.Constants.pi then
    aR := pi2-aR;
  end if;

  // Evaluate eqn. 2-1 from FSEC-CR-163-86
  incAng2 :=aR/2;
  sinA2 :=Modelica.Math.sin(incAng2);
  cosA2 :=Modelica.Math.cos(incAng2);
  // Implementation of the wind pressure coefficient that is once
  // continuously differentiable for all incidence angles
  if aR < aRDel then
    Cp :=Cp0*Buildings.Utilities.Math.Functions.regStep(
      y1=Modelica.Math.log(1.248 - 0.703*sinA2 - 1.175*Modelica.Math.sin(aR)^2
         + 0.131*Modelica.Math.sin(2*aR*G)^3 + 0.769*cosA2 + 0.071*G^2*sinA2^2
         + 0.717*cosA2^2),
      y2=1,
      x=aR - aRDel2,
      x_small=aRDel2);
  elseif aR > aRMax then
    Cp :=Cp0*Buildings.Utilities.Math.Functions.regStep(
      y1=a180,
      y2=Modelica.Math.log(1.248 - 0.703*sinA2 - 1.175*Modelica.Math.sin(aR)^2
         + 0.131*Modelica.Math.sin(2*aR*G)^3 + 0.769*cosA2 + 0.071*G^2*sinA2^2
         + 0.717*cosA2^2),
      x=aR + aRDel2 - Modelica.Constants.pi,
      x_small=aRDel2);
  else
    Cp :=Cp0*Modelica.Math.log(1.248 - 0.703*sinA2 - 1.175*Modelica.Math.sin(aR)^2 +
      0.131*Modelica.Math.sin(2*aR*G)^3 + 0.769*cosA2 + 0.071*G^2*sinA2^2 + 0.717*cosA2^2);
  end if;
end windPressureLowRise;


Flow across zonal boundaries of a room

within Buildings.Airflow.Multizone.BaseClasses;
partial model ZonalFlow "Flow across zonal boundaries of a room"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final allowFlowReversal1 = false,
    final allowFlowReversal2 = false,
    final m1_flow_nominal = 10/3600*1.2,
    final m2_flow_nominal = m1_flow_nominal,
    final m1_flow_small=1E-4*abs(m1_flow_nominal),
    final m2_flow_small=1E-4*abs(m2_flow_nominal));

   replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
    "Medium in the component"

equation
  // Energy balance (no storage, no heat loss/gain)
  port_a1.h_outflow = inStream(port_b1.h_outflow);
  port_b1.h_outflow = inStream(port_a1.h_outflow);
  port_a2.h_outflow = inStream(port_b2.h_outflow);
  port_b2.h_outflow = inStream(port_a2.h_outflow);

  // Mass balance (no storage)
  port_a1.m_flow = -port_b1.m_flow;
  port_a2.m_flow = -port_b2.m_flow;

  port_a1.Xi_outflow = inStream(port_b1.Xi_outflow);
  port_b1.Xi_outflow = inStream(port_a1.Xi_outflow);
  port_a2.Xi_outflow = inStream(port_b2.Xi_outflow);
  port_b2.Xi_outflow = inStream(port_a2.Xi_outflow);

  // Transport of trace substances
  port_a1.C_outflow = inStream(port_b1.C_outflow);
  port_b1.C_outflow = inStream(port_a1.C_outflow);
  port_a2.C_outflow = inStream(port_b2.C_outflow);
  port_b2.C_outflow = inStream(port_a2.C_outflow);
end ZonalFlow;