# ERV unit supply mass flow rate greater than Supply FanOnOff mass flow rate

Hello,

I used Energy Management System to control supply and exhaust FanOnOff (which is used in an ERV unit) air mass flow rate using actuator "Fan" with control type "Fan Air Mass Flow Rate". The EMS worked well but the supply and exhaust flow rate of ERV (Energy Recovery Ventilator) unit were not modified with the values of supply and exhaust FanOnOff air mass flow rate. Therefore, I received a value of zone mechanical ventilation mass flow rate which is different than the FanOnOff mass flow rate but is the same as ERV supply and exhaust flow rate predefined at the beginning of the simulation.

Does anybody have an idea for this? Did I make some mistakes here?

Thank you very much

Please find below my EMS snippet:

def arguments(model)
args = OpenStudio::Measure::OSArgumentVector.new

occupation_erv_fan_position = OpenStudio::Measure::OSArgument.makeDoubleArgument('occupation_erv_fan_position', true)
occupation_erv_fan_position.setDisplayName('Position of fan mass flow rate control during occupation period')
occupation_erv_fan_position.setDefaultValue(2)
args << occupation_erv_fan_position

absent_erv_fan_position = OpenStudio::Measure::OSArgument.makeDoubleArgument('absent_erv_fan_position', true)
absent_erv_fan_position.setDisplayName('Position of fan mass flow rate control during absent period')
absent_erv_fan_position.setDefaultValue(1)
args << absent_erv_fan_position

night_erv_fan_position = OpenStudio::Measure::OSArgument.makeDoubleArgument('night_erv_fan_position', true)
night_erv_fan_position.setDisplayName('Position of fan mass flow rate control during night time')
night_erv_fan_position.setDefaultValue(1)
args << night_erv_fan_position

return args
end

def run(model, runner, user_arguments)
super(model, runner, user_arguments)

# use the built-in error checking
if !runner.validateUserArguments(arguments(model), user_arguments)
return false
end

# assign the user inputs to variables
occupation_erv_fan_position = runner.getDoubleArgumentValue('occupation_erv_fan_position', user_arguments).to_f
absent_erv_fan_position = runner.getDoubleArgumentValue('absent_erv_fan_position', user_arguments).to_f
night_erv_fan_position = runner.getDoubleArgumentValue('night_erv_fan_position', user_arguments).to_f

# get thermal zones
thermal_zones = model.getThermalZones

thermal_zones.each do |thermal_zone|

zone_eqpt = thermal_zone.equipment # vector

zone_eqpt.each do |eqpt| # for each equipment

# Zone HVAC Energy Recovery Ventilator
if eqpt.to_ZoneHVACEnergyRecoveryVentilator.is_initialized

# get Energy Recovery Ventilator Unit
erv_unit = eqpt.to_ZoneHVACEnergyRecoveryVentilator.get
erv_supply_mass_flow_rate = TPEEHVACHelper.erv_supply_flow_rate(thermal_zone).to_f*1.204   # supply air flow rate is in m3/s and assuming that air density is of 1.204 kg/m3
erv_exhaust_mass_flow_rate = TPEEHVACHelper.erv_supply_flow_rate(thermal_zone).to_f*1.204 # supply air flow rate is in m3/s and assuming that air density is of 1.204 kg/m3

erv_supplyAirFan = erv_unit.supplyAirFan.to_FanOnOff.get
erv_exhaustAirFan = erv_unit.exhaustAirFan.to_FanOnOff.get

# Create EMS Actuator Objects
erv_supplyAirFan_ems_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(erv_supplyAirFan,"Fan","Fan Air Mass Flow Rate")
erv_supplyAirFan_ems_actuator.setName("erv_supplyAirFan_ems_actuator_#{thermal_zone.name.to_s}")
runner.registerInfo("EMS Actuator object named '#{erv_supplyAirFan_ems_actuator.name.to_s}' representing the mass flow rate of erv supply air fan named #{erv_supplyAirFan.name.to_s} added to the model.")

erv_exhaustAirFan_ems_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(erv_exhaustAirFan,"Fan","Fan Air Mass Flow Rate")
erv_exhaustAirFan_ems_actuator.setName("erv_exhaustAirFan_ems_actuator_#{thermal_zone.name.to_s}")
runner.registerInfo("EMS Actuator object named '#{erv_exhaustAirFan_ems_actuator.name.to_s}' representing the mass flow rate of erv supply air fan named #{erv_exhaustAirFan.name.to_s} added to the model.")

# Create new EnergyManagementSystem:Program object
erv_fan_mass_flow_rate_ems_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
erv_fan_mass_flow_rate_ems_prg.setName("erv_fan_mass_flow_rate_ems_prg_#{thermal_zone.name.to_s}")
erv_fan_mass_flow_rate_ems_prg.addLine("IF ((Hour >= 0) && (Hour <= 6)) || (Hour >= 22)") # night time
erv_fan_mass_flow_rate_ems_prg.addLine("SET #{erv_supplyAirFan_ems_actuator.name} = #{erv_supply_mass_flow_rate}*#{night_erv_fan_position}/3.0")
erv_fan_mass_flow_rate_ems_prg.addLine("SET #{erv_exhaustAirFan_ems_actuator.name} = #{erv_exhaust_mass_flow_rate}*#{night_erv_fan_position}/3.0")
#erv_fan_mass_flow_rate_ems_prg ...
edit retag close merge delete

## Comments

IIUC, you see the EMS program doing its thing, but it's overriden by something else later? Can you show your EMS snippet so we can look at the calling point etc?

( 2019-10-04 02:56:46 -0500 )edit

Hi Julien, thank you very much for your comment. Please see my updated question.

( 2019-10-04 03:20:58 -0500 )edit

BeginTimestepBeforePredictor is most likely a wrong calling point as it should be overriden by the normal operation of the program (95% sure...). AfterPredictorAfterHVACManager looks like a better guess (not saying it's right).

Please read the documentation in the EMS Application guide (eg: https://bigladdersoftware.com/epx/doc...)

( 2019-10-04 16:18:30 -0500 )edit

Hi Julien, thanks a lot for your suggestion. I tried to modified ems calling point with AfterPredictorAfterHVACManager but it does not work either (on my updated question you'll see the difference between zone mechanical ventilation and controlled (by ems) fan onoff mass flow rate) when there is no ems these values are the same. By the way, thanks again for your comment, I'll try to dig more on EMS Application Guide to better understand how to chose a correct calling point. Long

( 2019-10-07 02:14:24 -0500 )edit

Below is what I received as answer from http://energyplus.helpserve.com for this question

"This type of EMS override can be tricky. The ERV code wasn't written to allow the fan to override the mass flow rates. It assumes the unit is at full flow whenever it is on.

The only direct control on the ERV with EMS is to actuate the ERV Availability Schedule to turn the ERV on or off. If you use a short time step, say 1 minute, then you could get the desired mass flow rate by determining how many minutes it should be on each hour. ..."

( 2019-11-25 06:23:03 -0500 )edit