Question-and-Answer Resource for the Building Energy Modeling Community
Get started with the Help page
Ask Your Question

Revision history [back]

Creation of a Water-to-Water Heat Pump in OpenStudio

Hello all!

I am trying to add a water-to-water heat pump using a measure to OpenStudio files. I have not been able to find one that exists already, so I somewhat started from scratch.

The file deletes the current HVAC system, then adds a cooling heat pump, heating heat pump, hot water loop, chilled water loop, a source loop, and then connects fan coils to the zones.

I kept receiving errors about missing control names when reaching the EnergyPlus part of the simulation - "* Severe * <root>[PlantEquipmentOperationSchemes][Source Loop Operation Schemes] - Missing required property 'control_scheme_1_name'." So I tried to fix that and then I got errors on how I was defining loops in OpenStudio.

I've attached my code here. Any idea on what I might be doing wrong? I'm running OpenStudio 3.9, the OS 1.9.0rc-1 application, and EnergyPlus 24-2.

class AddReversibleWWHPSystem < OpenStudio::Measure::ModelMeasure

def name return 'Add Reversible WWHP System and Remove Existing HVAC' end

def description return 'Removes existing HVAC and adds a reversible water-to-water heat pump system with hot, chilled, and shared source loops.' end

def modeler_description return 'Installs heating and cooling WWHPs on separate loops sharing a source loop and serves all zones with four-pipe fan coils.' end

def arguments(model) return OpenStudio::Measure::OSArgumentVector.new end

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

runner.registerInitialCondition("Model has #{model.getThermalZones.size} thermal zones.")

# Remove existing HVAC
model.getAirLoopHVACs.each(&:remove)
model.getPlantLoops.each(&:remove)
model.getZoneHVACComponents.each(&:remove)
runner.registerInfo('Removed all existing HVAC systems.')

# -------------------------------
# Hot Water Loop
# -------------------------------
heating_loop = OpenStudio::Model::PlantLoop.new(model)
heating_loop.setName('Hot Water Loop')
hw_inlet = heating_loop.supplyInletNode
hw_outlet = heating_loop.supplyOutletNode

hw_sched = OpenStudio::Model::ScheduleRuleset.new(model)
hw_sched.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 60.0)
hw_spm = OpenStudio::Model::SetpointManagerScheduled.new(model, hw_sched)
hw_spm.addToNode(hw_outlet)

# -------------------------------
# Chilled Water Loop
# -------------------------------
cooling_loop = OpenStudio::Model::PlantLoop.new(model)
cooling_loop.setName('Chilled Water Loop')
cw_inlet = cooling_loop.supplyInletNode
cw_outlet = cooling_loop.supplyOutletNode

cw_sched = OpenStudio::Model::ScheduleRuleset.new(model)
cw_sched.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 7.0)
cw_spm = OpenStudio::Model::SetpointManagerScheduled.new(model, cw_sched)
cw_spm.addToNode(cw_outlet)

# -------------------------------
# Source Loop
# -------------------------------
source_loop = OpenStudio::Model::PlantLoop.new(model)
source_loop.setName('Source Loop')
src_inlet = source_loop.supplyInletNode
src_outlet = source_loop.supplyOutletNode

src_sched = OpenStudio::Model::ScheduleRuleset.new(model)
src_sched.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 15.0)
src_spm = OpenStudio::Model::SetpointManagerScheduled.new(model, src_sched)
src_spm.addToNode(src_outlet)

# -------------------------------
# Heat Pumps
# -------------------------------
heating_hp = OpenStudio::Model::HeatPumpWaterToWaterEquationFitHeating.new(model)
heating_hp.setName('WWHP Heating')
heating_hp.addToNode(hw_inlet)

cooling_hp = OpenStudio::Model::HeatPumpWaterToWaterEquationFitCooling.new(model)
cooling_hp.setName('WWHP Cooling')
cooling_hp.addToNode(cw_inlet)

# -------------------------------
# Add to Loops
# -------------------------------
source_loop.addDemandBranchForComponent(heating_hp)
source_loop.addDemandBranchForComponent(cooling_hp)

heating_loop.addSupplyBranchForComponent(heating_hp)
cooling_loop.addSupplyBranchForComponent(cooling_hp)

# -------------------------------
# Fan Coil Units in Zones
# -------------------------------
model.getThermalZones.each do |zone|
  sched = model.alwaysOnDiscreteSchedule
  fan = OpenStudio::Model::FanOnOff.new(model, sched)
  cc = OpenStudio::Model::CoilCoolingWater.new(model)
  hc = OpenStudio::Model::CoilHeatingWater.new(model)

  fcu = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model, sched, fan, cc, hc)
  fcu.setName("Fan Coil - #{zone.name}")
  fcu.addToThermalZone(zone)

  cooling_loop.addDemandBranchForComponent(cc)
  heating_loop.addDemandBranchForComponent(hc)
end

runner.registerInfo('WWHPs, loops, and fan coils configured.')

# -------------------------------
# Dummy Source Loop Operation Scheme
# -------------------------------
src_op_scheme = OpenStudio::Model::PlantEquipmentOperationHeatingLoad.new(model)
src_op_scheme.setName('Source Loop Operation Scheme')
src_op_scheme.addEquipment(1.0E10, heating_hp)
src_op_scheme.addEquipment(1.0E10, cooling_hp)
source_loop.setPlantEquipmentOperationHeatingLoad(src_op_scheme)

# -------------------------------
# IDF Injection for E+ Compatibility
# -------------------------------
idf_text = <<~IDF
  PlantEquipmentOperation:HeatingLoad,
    WWHP Heating Operation,
    0.0,100000.0,
    WWHP Heating Equip List;

  PlantEquipmentList,
    WWHP Heating Equip List,
    HeatPump:WaterToWater:EquationFit:Heating,
    WWHP Heating;

  PlantEquipmentOperation:CoolingLoad,
    WWHP Cooling Operation,
    0.0,100000.0,
    WWHP Cooling Equip List;

  PlantEquipmentList,
    WWHP Cooling Equip List,
    HeatPump:WaterToWater:EquationFit:Cooling,
    WWHP Cooling;
IDF

idf_text.split("\n\n").each do |block|
  idf_obj = OpenStudio::IdfObject.load(block)
  if idf_obj.is_initialized
    model.addObject(idf_obj.get)
    runner.registerInfo("Injected IDF block:\n#{block}")
  else
    runner.registerWarning("Could not parse IDF block:\n#{block}")
  end
end

runner.registerFinalCondition("WWHP system added and all operation schemes completed.")
return true

end end

AddReversibleWWHPSystem.new.registerWithApplication

Creation of a Water-to-Water Heat Pump in OpenStudio

Hello all!

I am trying to add a water-to-water heat pump using a measure to OpenStudio files. I have not been able to find one that exists already, so I somewhat started from scratch.

The file deletes the current HVAC system, then adds a cooling heat pump, heating heat pump, hot water loop, chilled water loop, a source loop, and then connects fan coils to the zones.

I kept receiving errors about missing control names when reaching the EnergyPlus part of the simulation - "* Severe * <root>[PlantEquipmentOperationSchemes][Source Loop Operation Schemes] - Missing required property 'control_scheme_1_name'." So I tried to fix that and then I got errors on how I was defining loops in OpenStudio.

I've attached my code here. Any idea on what I might be doing wrong? I'm running OpenStudio 3.9, the OS 1.9.0rc-1 application, and EnergyPlus 24-2.

class AddReversibleWWHPSystem < OpenStudio::Measure::ModelMeasure

def name return 'Add Reversible WWHP System and Remove Existing HVAC' end

def description return 'Removes existing HVAC and adds a reversible water-to-water heat pump system with hot, chilled, and shared source loops.' end

def modeler_description return 'Installs heating and cooling WWHPs on separate loops sharing a source loop and serves all zones with four-pipe fan coils.' end

def arguments(model) return OpenStudio::Measure::OSArgumentVector.new end

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

runner.registerInitialCondition("Model has #{model.getThermalZones.size} thermal zones.")

# Remove existing HVAC
model.getAirLoopHVACs.each(&:remove)
model.getPlantLoops.each(&:remove)
model.getZoneHVACComponents.each(&:remove)
runner.registerInfo('Removed all existing HVAC systems.')

# -------------------------------
# Hot Water Loop
# -------------------------------
heating_loop = OpenStudio::Model::PlantLoop.new(model)
heating_loop.setName('Hot Water Loop')
hw_inlet = heating_loop.supplyInletNode
hw_outlet = heating_loop.supplyOutletNode

hw_sched = OpenStudio::Model::ScheduleRuleset.new(model)
hw_sched.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 60.0)
hw_spm = OpenStudio::Model::SetpointManagerScheduled.new(model, hw_sched)
hw_spm.addToNode(hw_outlet)

# -------------------------------
# Chilled Water Loop
# -------------------------------
cooling_loop = OpenStudio::Model::PlantLoop.new(model)
cooling_loop.setName('Chilled Water Loop')
cw_inlet = cooling_loop.supplyInletNode
cw_outlet = cooling_loop.supplyOutletNode

cw_sched = OpenStudio::Model::ScheduleRuleset.new(model)
cw_sched.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 7.0)
cw_spm = OpenStudio::Model::SetpointManagerScheduled.new(model, cw_sched)
cw_spm.addToNode(cw_outlet)

# -------------------------------
# Source Loop
# -------------------------------
source_loop = OpenStudio::Model::PlantLoop.new(model)
source_loop.setName('Source Loop')
src_inlet = source_loop.supplyInletNode
src_outlet = source_loop.supplyOutletNode

src_sched = OpenStudio::Model::ScheduleRuleset.new(model)
src_sched.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 15.0)
src_spm = OpenStudio::Model::SetpointManagerScheduled.new(model, src_sched)
src_spm.addToNode(src_outlet)

# -------------------------------
# Heat Pumps
# -------------------------------
heating_hp = OpenStudio::Model::HeatPumpWaterToWaterEquationFitHeating.new(model)
heating_hp.setName('WWHP Heating')
heating_hp.addToNode(hw_inlet)

cooling_hp = OpenStudio::Model::HeatPumpWaterToWaterEquationFitCooling.new(model)
cooling_hp.setName('WWHP Cooling')
cooling_hp.addToNode(cw_inlet)

# -------------------------------
# Add to Loops
# -------------------------------
source_loop.addDemandBranchForComponent(heating_hp)
source_loop.addDemandBranchForComponent(cooling_hp)

heating_loop.addSupplyBranchForComponent(heating_hp)
cooling_loop.addSupplyBranchForComponent(cooling_hp)

# -------------------------------
# Fan Coil Units in Zones
# -------------------------------
model.getThermalZones.each do |zone|
  sched = model.alwaysOnDiscreteSchedule
  fan = OpenStudio::Model::FanOnOff.new(model, sched)
  cc = OpenStudio::Model::CoilCoolingWater.new(model)
  hc = OpenStudio::Model::CoilHeatingWater.new(model)

  fcu = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model, sched, fan, cc, hc)
  fcu.setName("Fan Coil - #{zone.name}")
  fcu.addToThermalZone(zone)

  cooling_loop.addDemandBranchForComponent(cc)
  heating_loop.addDemandBranchForComponent(hc)
end

runner.registerInfo('WWHPs, loops, and fan coils configured.')

# -------------------------------
# Dummy Source Loop Operation Scheme
# -------------------------------
src_op_scheme = OpenStudio::Model::PlantEquipmentOperationHeatingLoad.new(model)
src_op_scheme.setName('Source Loop Operation Scheme')
src_op_scheme.addEquipment(1.0E10, heating_hp)
src_op_scheme.addEquipment(1.0E10, cooling_hp)
source_loop.setPlantEquipmentOperationHeatingLoad(src_op_scheme)

# -------------------------------
# IDF Injection for E+ Compatibility
# -------------------------------
idf_text = <<~IDF
  PlantEquipmentOperation:HeatingLoad,
    WWHP Heating Operation,
    0.0,100000.0,
    WWHP Heating Equip List;

  PlantEquipmentList,
    WWHP Heating Equip List,
    HeatPump:WaterToWater:EquationFit:Heating,
    WWHP Heating;

  PlantEquipmentOperation:CoolingLoad,
    WWHP Cooling Operation,
    0.0,100000.0,
    WWHP Cooling Equip List;

  PlantEquipmentList,
    WWHP Cooling Equip List,
    HeatPump:WaterToWater:EquationFit:Cooling,
    WWHP Cooling;
IDF

idf_text.split("\n\n").each do |block|
  idf_obj = OpenStudio::IdfObject.load(block)
  if idf_obj.is_initialized
    model.addObject(idf_obj.get)
    runner.registerInfo("Injected IDF block:\n#{block}")
  else
    runner.registerWarning("Could not parse IDF block:\n#{block}")
  end
end

runner.registerFinalCondition("WWHP system added and all operation schemes completed.")
return true

end end

AddReversibleWWHPSystem.new.registerWithApplication