I’ve been working with @jdelaneybbb on this model and we traced the root cause, verified it with three single-variable test runs on a 104-unit residential slice (~85k sf) of the same building, and found a practical workaround. Posting the full picture since this affects anyone using the HeatPump_WaterHeater SHW template at central-plant scale.
Root cause
The HPWH template’s DX compressor is left at a residential-scale default capacity and never scales to the load.
In honeybee_openstudio/shw.py (shw_system_to_openstudio), the HeatPump_WaterHeater branch creates the DX coil and sets its COP — but never sets its rated capacity:
python
heat_pump = OSCoilWaterHeatingAirToWaterHeatPump(os_model)
heat_pump.setName('SHW HPWH DX Coil')
heat_pump.setRatedCOP(shw.heater_efficiency) # COP is set
# rated heating capacity: never touched → OpenStudio SDK constructor default
The OpenStudio SDK constructor for CoilWaterHeatingAirToWaterHeatPump hardcodes setRatedHeatingCapacity(4000.0), and the exported IDF confirms it:
Coil:WaterHeating:AirToWaterHeatPump:Pumped,
SHW HPWH DX Coil,
4000, !- Rated Heating Capacity {W}
3.5, !- Rated COP {W/W}
Meanwhile the tank’s backup element is set to 1,000,000 W at Heater Thermal Efficiency 1.0. From our Run 1 results, the annual thermal DHW demand for this slice averages roughly 50 kW — so a 4 kW compressor running flat-out covers less than 8% of demand, and the electric resistance element quietly does the rest.
That’s why changing the COP — even to 15 — barely moves the result: it only applies to that small slice of the load. Your original numbers are consistent with this: gas @ 0.8 → ~20 EUI implies ~16 EUI of thermal demand, and the HPWH result of ~15 is what a COP ≈ 1.0 electricity bill looks like (blended effective COP of ~8% at 3.5 + ~92% at 1.0 ≈ 1.05).
Test runs (single-variable, same model)
| Run |
Change |
Water Systems (kBtu/ft²) |
Effective COP |
| 1 |
Stock HPWH template, COP 3.5 |
17.6 |
≈ 1.0 |
| 2 |
Coil capacity 4 kW → ~200 kW via IDF edit |
14.8 |
≈ 1.1 |
| 3 |
Tank Heater Thermal Efficiency 1.0 → 3.5 via IDF edit |
5.2 |
= demand ÷ 3.5 ✓ |
Run 2 is worth understanding: fixing the capacity alone doesn’t fix it. Honeybee requires ambient_condition to be a Room for the HeatPump type, and OpenStudio’s addToThermalZone then forces the wrapper’s Inlet Air Configuration to ZoneAirOnly — the evaporator breathes one zone’s air. At COP 3.5, a ~200 kW coil extracts ~143 kW of heat from that zone, which quickly crashes the zone below the wrapper’s Minimum Inlet Air Temperature for Compressor Operation (10°C, set by the OpenStudio constructor). The compressor locks out and the model falls back to resistance. A central-scale air-source HPWH can’t live in a mechanical room, and E+ faithfully models that.
The workaround we’re using: IDF edit via HB Run IDF
(a) Simplest and verified (Run 3): model the system as Electric_WaterHeater, export the IDF, and edit the WaterHeater:Mixed object’s Heater Thermal Efficiency from 1.0 to your seasonal COP (e.g., 3.5), then re-run through HB Run IDF. Alternatively, leave the model untouched and divide the Water Systems result by your COP in post-processing — same arithmetic.
Note on why the IDF edit is needed: honeybee-energy’s validation asserts heater_efficiency <= 1 for non-HeatPump equipment types, so a COP can’t be entered upstream. EnergyPlus itself accepts it — the IDD field for WaterHeater:Mixed Heater Thermal Efficiency has \minimum> 0.0 and no maximum. Run 3 returns exactly demand ÷ 3.5, as expected.
This effectively models a constant-COP plant, which is a reasonable first approximation for a central heat pump or ground-source system in an early-stage comparison.
(b) For a dynamic COP (closer to a real central air-source or ground-source plant): keep the HPWH object, set a realistic coil capacity, and change the WaterHeater:HeatPump:PumpedCondenser Inlet Air Configuration from ZoneAirOnly to Schedule with a constant-temperature inlet schedule (~15–20°C) — effectively an infinite constant-temperature source, and a reasonable ground-source approximation. Be aware this is a multi-field edit: with Schedule you must also supply an Inlet Air Humidity Schedule (fraction RH) alongside the temperature schedule, blank the Inlet Air Zone Name, replace the zone exhaust/inlet air nodes with plain unique node names, and resolve the Compressor Location (and zone equipment list) accordingly — otherwise E+ will throw severe errors.
Suggested library fixes
-
Size the DX coil’s Rated Heating Capacity from total_flow, which is already passed into shw_system_to_openstudio for tank sizing. (This field is not autosizable in EnergyPlus — only the evaporator air flow and condenser water flow are — so the capacity would need to be computed, e.g., sized to recover the tank volume over a target period at the loop ΔT.)
-
Consider a source-temperature input for central plants, so the evaporator isn’t necessarily tied to a single zone’s air. @chris