Design of a Multi-Zone VAV System (the Long Way)#


In this notebook a multi-zone VAV system is designed with the package hvac.air_conditioning. The example is taken from the book Heating and Cooling of Buildings by T. Agami Reddy, Jan F. Kreider, Peter S. Curtiss and Ari Rabl (3th Edition, 2017), Chapter 19.

from hvac import Quantity
from hvac.fluids import HumidAir
from hvac.air_conditioning import AirConditioningProcess, AdiabaticMixing, AirStream, Fan
Q_ = Quantity

0. Design Data#

A two-zone building is to be equipped with a VAV system with preheat and reheat under the load conditions noted as follows. Size the supply fan, cooling coil, preheat coil, and reheat coil (or baseboard heating) for this building. Note that these will also apply to a CAV system under peak design conditions.

Space loads

Zone A

Zone B

Sensible peak summer design cooling load

224,844 Btu/hr

103,308 Btu/hr

Latent peak summer design cooling load

56,000 Btu/hr

20,000 Btu/hr

Sensible peak winter design load

-143,000 Btu/hr

49,092 Btu/hr

Zone temperature

75°F

75°F

Notice that interior zone B still requires cooling in winter.

Design parameters

  1. summer design dry-and wet-bulb temperatures: 97°F and 76°F.

  2. winter design temperature: 7°F and air assumed to be totally dry.

  3. pressure drop in supply system at full air-flow: 3.0 inWG.

  4. fan efficiency: 60%.

  5. return air fan air temperature rise: none.

  6. ventilation airflow rate: 2400 ft3/min.

  7. relative air humidity at cooling coil outlet: 85%.

  8. temperature difference between supply air and zone air: 20°F (to ensure proper mixing).

  9. maximum supply air temperature: 105°F (to avoid stratification).

Sketch

scheme

Assumptions

  • Ignore factors not included in the list given earlier, such as duct heat losses and gains.

  • The location is assumed to be at sea level.

  • Rather than use enthalpy balances on the zones, the flows will be designed based on sensible heat loads (this is meant to simplify the problem).

  • Peak loads are coincident; no diversity adjustment is used.

  • During peak heating, latent loads on the space are negligible.

  • During winter, room humidifiers are used to maintain the necessary humidity levels.

  • The supply airflow to the zones cannot be reduced to less than 60 % of the full-load design value by mass to ensure proper mixing of the cold supply air once it enters the space.

COOLING DESIGN DAY#

1. Mass Flow Rate of Ventilation Air#

outdoor_air = HumidAir(Tdb=Q_(97, 'degF'), Twb=Q_(76, 'degF'))
V_vent = Q_(2400, 'ft ** 3 / min')
m_vent = outdoor_air.rho * V_vent

ja.display_list([
    f"ventilation mass flow rate: <b>{m_vent.to('lb / hr'):~P.0f}</b>"
])
  • ventilation mass flow rate: 10029 lb/h

2. Zone Supply Air Flow Rates#

The zone air temperature is 75 °F. To ensure proper mixing with space air the supply air temperature is limited to 20 °F below the zone air temperature. The RH of the air leaving the cooling coil is specified at 85 %. As there is no addition nor extraction of water from the air between the cooling coil and the zones, this is also the RH of the supply air to the zones.

supply_air = HumidAir(Tdb=Q_(75 - 20, 'degF'), RH=Q_(85, 'pct'))

Mass flow rate of supply air to zone A

The required mass flow rate of supply air to zone A is determined to compensate for the sensible heat load of the zone.

zone_A = AirConditioningProcess(
    air_in=supply_air,
    T_ao=Q_(75, 'degF'),
    Q_sen=Q_(224_844, 'Btu / hr')
)
m_supply_zA = zone_A.m_da

ja.display_list([
    f"supply air mass flow rate to zone A: <b>{m_supply_zA.to('lb / min'):~P.3f}</b>"
])
  • supply air mass flow rate to zone A: 769.099 lb/min

Mass flow rate of supply air to zone B

zone_B = AirConditioningProcess(
    air_in=supply_air,
    T_ao=Q_(75, 'degF'),
    Q_sen=Q_(103_308, 'Btu / hr')
)
m_supply_zB = zone_B.m_da

ja.display_list([
    f"supply air mass flow rate to zone B: <b>{m_supply_zB.to('lb / min'):~P.3f}</b>"
])
  • supply air mass flow rate to zone B: 353.374 lb/min

Total mass flow rate of supply air

m_supply = m_supply_zA + m_supply_zB

ja.display_list([
    f"total supply air mass flow rate: <b>{m_supply.to('lb / min'):~P.3f}</b>"
])
  • total supply air mass flow rate: 1122.473 lb/min

3. Supply Fan Power, Fan Heating and Air Condition at Cooling Coil Exit#

Mechanical supply fan power

The fan pressure is already given (in reality this needs to be calculated after the supply air flow rates to the zones have been determined). The fan efficiency is also given.

dP_fan = Q_(3.0, 'inch_H2O_60F')
eta_fan = Q_(60, 'pct')

To determine the fan power the total volume flow rate of supply air to the zones must first be calculated:

V_supply = supply_air.v * m_supply

Fan power then follows from:

W_fan = V_supply * dP_fan / eta_fan

ja.display_list([
    f"supply fan power: <b>{W_fan.to('hp'):~P.0f}</b>"
])
  • supply fan power: 12 hp

Supply air temperature raise due to fan heating

supply_fan = Fan(
    air_out=supply_air,
    fan_efficiency=eta_fan,
    fan_pressure=dP_fan
)
dT_fan = supply_fan.air_out.Tdb - supply_fan.air_in.Tdb

ja.display_list([
    f"fan temperature rise: <b>{dT_fan.to('delta_degF'):~P.0f}</b>"
])
  • fan temperature rise: 2 Δ°F

Air Condition at Cooling Coil Exit

cooled_air = HumidAir(Tdb=supply_air.Tdb - dT_fan, RH=Q_(85, 'pct'))

ja.display_list([
    f"cooled air: <b>{cooled_air.Tdb.to('degF'):~P.0f} TDB, {cooled_air.W.to('lb / lb'):~P.5f} lb<sub>w</sub>/lb<sub>da</sub></b>"
])
  • cooled air: 53 °F TDB, 0.00733 lbw/lbda

4. Return Air Condition#

Zone A

zone_A = AirConditioningProcess(
    air_in=supply_air,
    T_ao=Q_(75, 'degF'),
    m_da=m_supply_zA,
    Q_sen=Q_(224_844, 'Btu / hr'),
    Q_lat=Q_(56_000, 'Btu / hr')
)
return_air_zA = zone_A.air_out

ja.display_list([
    f"return air zone A: <b>{return_air_zA.Tdb.to('degF'):~P.0f} TDB, {return_air_zA.W.to('lb / lb'):~P.5f} lb<sub>w</sub>/lb<sub>da</sub></b>"
])
  • return air zone A: 75 °F TDB, 0.00894 lbw/lbda

Zone B

zone_B = AirConditioningProcess(
    air_in=supply_air,
    T_ao=Q_(75, 'degF'),
    m_da=m_supply_zB,
    Q_sen=Q_(103_308, 'Btu / hr'),
    Q_lat=Q_(20_000, 'Btu / hr')
)
return_air_zB = zone_B.air_out

ja.display_list([
    f"return air zone B: <b>{return_air_zB.Tdb.to('degF'):~P.0f} TDB, {return_air_zB.W.to('lb / lb'):~P.5f} lb<sub>w</sub>/lb<sub>da</sub></b>"
])
  • return air zone B: 75 °F TDB, 0.00869 lbw/lbda

Adiabatic mixing of return air from zones

return_air_mixing = AdiabaticMixing(
    in1=AirStream(state=return_air_zA, m_da=m_supply_zA),
    in2=AirStream(state=return_air_zB, m_da=m_supply_zB),
    out=AirStream(m_da=m_supply)
)
return_air = return_air_mixing.stream_out.state

ja.display_list([
    f"return air from zones: <b>{return_air.Tdb.to('degF'):~P.0f} TDB, {return_air.W.to('lb / lb'):~P.5f} lb<sub>w</sub>/lb<sub>da</sub></b>"
])
  • return air from zones: 75 °F TDB, 0.00886 lbw/lbda

5. Air Condition at Cooling Coil Entrance#

mixing_chamber = AdiabaticMixing(
    in1=AirStream(state=outdoor_air, m_da=m_vent),
    in2=AirStream(state=return_air, m_da=m_supply - m_vent),
    out=AirStream(m_da=m_supply_zA + m_supply_zB)
)
mixed_air = mixing_chamber.stream_out.state

ja.display_list([
    f"mixed air: <b>{mixed_air.Tdb.to('degF'):~P.1f} TDB, {mixed_air.W.to('lb / lb'):~P.5f} lb<sub>w</sub>/lb<sub>da</sub></b>"
])
  • mixed air: 78.3 °F TDB, 0.00971 lbw/lbda

6. Cooling Coil Load#

cooling_coil = AirConditioningProcess(
    air_in=mixed_air,
    air_out=cooled_air,
    m_da=m_supply
)

ja.display_list([
    f"cooling coil load: <b>{cooling_coil.Q.to('Btu / hr'):~P.0f}</b>",
    f"cooling coil ADP: <b>{cooling_coil.ADP.Tdb.to('degF'):~P.0f} TDB, {cooling_coil.ADP.W.to('lb / lb'):~P.5f} lb<sub>w</sub>/lb<sub>da</sub></b>",
    f"cooling coil contact factor: <b>{cooling_coil.beta.to('pct'):~P.0f}</b>"
])
  • cooling coil load: -586874 Btu/h
  • cooling coil ADP: 46 °F TDB, 0.00669 lbw/lbda
  • cooling coil contact factor: 79 %

HEATING DESIGN DAY#

1. Mass Flow Rate of Ventilation Air#

outdoor_air = HumidAir(Tdb=Q_(7, 'degF'), RH=Q_(0, 'pct'))  # dry air
V_vent = Q_(2400, 'ft ** 3 / min')
m_vent = outdoor_air.rho * V_vent

ja.display_list([
    f"ventilation mass flow rate: <b>{m_vent.to('lb / hr'):~P.0f}</b>"
])
  • ventilation mass flow rate: 12250 lb/h

2. Preheat Coil Peak Load#

The peak load on the preheat coil is determined based on the load needed to heat the cold outdoor ventilation air to the nominal coil outlet temperature.

preheat_coil = AirConditioningProcess(
    air_in=outdoor_air,
    T_ao=cooled_air.Tdb,
    m_da=m_vent
)
Q_preheat_peak = preheat_coil.Q_sen

ja.display_list([
    f"preheat coil peak load: <b>{Q_preheat_peak.to('Btu/hr'):~P.0f}</b>"
])
  • preheat coil peak load: 137878 Btu/h

3. Zone Supply Air Flow Rates and Supply Air Temperatures#

Zone A (requires heating)

At first take the maximum allowable supply air temperature of 105 °F to determine mass flow rate.

zone_A = AirConditioningProcess(
    T_ai=Q_(105, 'degF'),
    T_ao=Q_(75, 'degF'),
    Q_sen=Q_(-143_000, 'Btu / hr')
)
m_supply_zA_winter = zone_A.m_da

ja.display_list([
    f"supply air mass flow rate to zone A: <b>{m_supply_zA_winter.to('lb / min'):~P.3f}</b>"
])
  • supply air mass flow rate to zone A: 326.096 lb/min

Recall the stipulation that the supply air mass flow rate cannot be less than 60 % of the peak air flow to ensure proper mixing with zone air.

m_supply_zA_winter = max(m_supply_zA_winter, 0.6 * m_supply_zA)

ja.display_list([
    f"supply air mass flow rate to zone A: <b>{m_supply_zA_winter.to('lb / min'):~P.3f}</b>"
])
  • supply air mass flow rate to zone A: 461.459 lb/min

Supply air temperature:

zone_A = AirConditioningProcess(
    T_ao=Q_(75, 'degF'),
    m_da=m_supply_zA_winter,
    Q_sen=Q_(-143_000, 'Btu / hr')
)
T_supply_zA = zone_A.T_ai

ja.display_list([
    f"supply air temperature for zone A: <b>{T_supply_zA.to('degF'):~P.3f}</b>"
])
  • supply air temperature for zone A: 96.200 °F

Zone B (requires cooling)

At first take the minimum supply air temperature (cooling) to determine mass flow rate.

zone_B = AirConditioningProcess(
    T_ai=supply_air.Tdb,
    T_ao=Q_(75, 'degF'),
    Q_sen=Q_(49_092, 'Btu / hr')
)
m_supply_zB_winter = zone_B.m_da

ja.display_list([
    f"supply air mass flow rate to zone B: <b>{m_supply_zB_winter.to('lb / min'):~P.3f}</b>"
])
  • supply air mass flow rate to zone B: 167.924 lb/min

Next, check again for the condition that mass flow rate cannot be less than 60 % of peak air flow.

m_supply_zB_winter = max(m_supply_zB_winter, 0.6 * m_supply_zB)

ja.display_list([
    f"supply air mass flow rate to zone B: <b>{m_supply_zB_winter.to('lb / min'):~P.3f}</b>"
])
  • supply air mass flow rate to zone B: 212.025 lb/min

Supply air temperature:

zone_B = AirConditioningProcess(
    T_ao=Q_(75, 'degF'),
    m_da=m_supply_zB_winter,
    Q_sen=Q_(49_092, 'Btu / hr')
)
T_supply_zB = zone_B.T_ai

ja.display_list([
    f"supply air temperature for zone B: <b>{T_supply_zB.to('degF'):~P.3f}</b>"
])
  • supply air temperature for zone B: 59.160 °F

Total supply air mass flow rate

m_supply = m_supply_zA_winter + m_supply_zB_winter

ja.display_list([
    f"total supply air mass flow rate: <b>{m_supply.to('lb / min'):~P.3f}</b>"
])
  • total supply air mass flow rate: 673.484 lb/min

Global Supply Air Temperature

The global supply air temperature must be the smallest required supply air temperature of all zones.

supply_air = HumidAir(Tdb=min(T_supply_zA, T_supply_zB), RH=Q_(0, 'pct'))

ja.display_list([
    f"global supply air temperature: <b>{supply_air.Tdb.to('degF'):~P.3f}</b>"
])
  • global supply air temperature: 59.160 °F

4. Return Air Condition#

return_air_mixing = AdiabaticMixing(
    in1=AirStream(state=HumidAir(Tdb=Q_(75, 'degF'), RH=Q_(0, 'pct')), m_da=m_supply_zA_winter),
    in2=AirStream(state=HumidAir(Tdb=Q_(75, 'degF'), RH=Q_(0, 'pct')), m_da=m_supply_zB_winter),
    out=AirStream(m_da=m_supply)
)
return_air = return_air_mixing.stream_out.state

ja.display_list([
    f"return air temperature: <b>{return_air.Tdb.to('degF'):~P.3f}</b>"
])
  • return air temperature: 75.000 °F

5. Adiabatic Mixing of Return Air and Outdoor Ventilation Air#

mixing_chamber = AdiabaticMixing(
    in1=AirStream(state=outdoor_air, m_da=m_vent),
    in2=AirStream(state=return_air, m_da=m_supply - m_vent),
    out=AirStream(m_da=m_supply)
)
mixed_air = mixing_chamber.stream_out.state

ja.display_list([
    f"mixed air temperature: <b>{mixed_air.Tdb.to('degF'):~P.3f}</b>"
])
  • mixed air temperature: 54.392 °F

6. Fan Heating and Air Condition at Cooling Coil Exit#

Supply air temperature raise due to fan heating

supply_fan = Fan(
    air_out=supply_air,
    fan_efficiency=eta_fan,
    fan_pressure=dP_fan
)
dT_fan = supply_air.Tdb - supply_fan.air_in.Tdb

ja.display_list([
    f"fan temperature rise: <b>{dT_fan.to('delta_degF'):~P.0f}</b>"
])
  • fan temperature rise: 2 Δ°F

Air Condition at Cooling Coil Exit

cooled_air = HumidAir(Tdb=supply_air.Tdb - dT_fan, RH=Q_(0, 'pct'))

ja.display_list([
    f"temperature cooled air: <b>{cooled_air.Tdb.to('degF'):~P.1f}</b>"
])
  • temperature cooled air: 57.3 °F

Check if Preheating the Mixed Air is necessary on Heating Design Day

Notice that the cooled air temperature is higher than the mixed air temperature. The mixed air is therefore preheated to the cooled air temperature.

preheat_coil = AirConditioningProcess(
    air_in=mixed_air,
    air_out=cooled_air,
    m_da=m_supply
)
preheated_air = cooled_air

ja.display_list([
    f"preheat coil load at design conditions: {preheat_coil.Q.to('Btu / hr'):~P.0f}"
])
  • preheat coil load at design conditions: 29050 Btu/h

7. Cooling Coil Load#

cooling_coil = AirConditioningProcess(
    T_ai=preheated_air.Tdb,
    T_ao=cooled_air.Tdb,
    m_da=m_supply
)

ja.display_list([
    f"cooling coil load: <b>{cooling_coil.Q_sen.to('Btu / hr'):~P.0f}</b>"
])
  • cooling coil load: 0 Btu/h

8. Reheat Coil Loads#

Zone A

reheat_coil_zA = AirConditioningProcess(
    T_ai=supply_air.Tdb,
    T_ao=T_supply_zA,
    m_da=m_supply_zA_winter
)

ja.display_list([
    f"reheat coil load: <b>{reheat_coil_zA.Q_sen.to('Btu / hr'):~P.0f}</b>"
])
  • reheat coil load: 249846 Btu/h

Zone B

reheat_coil_zB = AirConditioningProcess(
    T_ai=supply_air.Tdb,
    T_ao=T_supply_zB,
    m_da=m_supply_zB_winter
)

ja.display_list([
    f"reheat coil load: <b>{reheat_coil_zB.Q_sen.to('Btu / hr'):~P.0f}</b>"
])
  • reheat coil load: 0 Btu/h

Total peak heat requirement

Q_heat_tot = reheat_coil_zA.Q_sen + reheat_coil_zB.Q_sen + Q_preheat_peak

ja.display_list([
    f"peak heat requirement: <b>{Q_heat_tot.to('Btu / hr'):~P.0f}</b>"
])
  • peak heat requirement: 387724 Btu/h

This heat rate must be increased by pickup loads from night setback, safety factors, and piping losses to size the coils and the boiler.