Skip to content

Conversation

@yujiex
Copy link
Collaborator

@yujiex yujiex commented Mar 20, 2025

Pull request overview

Description of the purpose of this PR

Heat pumps are an essential technology for decarbonizing buildings. EnergyPlus is capable of modeling several types of heat pumps, e.g., for space cooling or heating: air to air, air to water, water to water; and heat pump water heaters. Although there is an existing feature to model air-to-water heat pumps in EnergyPlus, its capability is limited and its implementation is not straightforward compared with other types of HP. Some of the limitations include:

  • Cannot specify parameters like single, multi-speed, or variable speed for capacity control (for example, the Daikin model EWYT-CZ series is inverter controlled, and the EWYQ series is a fixed-speed two-stage model.)
  • Missing details of crankcase heater control and more flexible defrost control
  • The naming of the object is not intuitive - it does not refer to water to water or air to water, making searching for the object difficult
  • Missing the input of an availability schedule (which disables the HP for seasonal consideration)
  • Designed as separate heating and cooling objects while in reality, it should be one piece of equipment

This feature will create a new air-to-water heat pump (AWHP) model that includes heating and cooling components in one single object, capable of multi-speed controls, and includes more equipment details such as fan characteristics, PLR ranges, availability schedules, etc.

About the extended internal data structure arrays

The following entried are added to the internal data structure to hold info about this new AWHP.

Entries Added to
HeatPumpAirToWaterCooling,
HeatPumpAirToWaterHeating,
HeatPumpAirToWater,
“ConnectionObjectType” in DataLoopNodes.hh
“PlantEquipmentType” in Enums.hh
"HeatPump:AirToWater:Cooling",
"HeatPump:AirToWater:Heating",
"HeatPump:AirToWater",
PlantEquipTypeNames in DataPlant.hh
“ConnectionObjectTypeNames” in BranchNodeConnections.cc
"HEATPUMP:AIRTOWATER:COOLING",
"HEATPUMP:AIRTOWATER:HEATING",
"HEATPUMP:AIRTOWATER",
“PlantEquipTypeNamesUC” in DataPlant.hh
“ConnectionObjectTypeNamesUC” in BranchNodeConnections.cc

Regression diffs

798 files have regression diffs
Select a representative one, for example, 5ZoneAirCooled, the diffs happen in

  • audit: the number of SubTable and ColumnTags are different, due to the addition of HTML output table "AWHPs" in the equipment summary and the added entries in the "central plant" table
image
  • tbl diff: it shows up as diff in the summary but the diff file shows 0 diff
image

Pull Request Author

  • Title of PR should be user-synopsis style (clearly understandable in a standalone changelog context)
  • Label the PR with at least one of: Defect, Refactoring, NewFeature, Performance, and/or DoNoPublish
  • Pull requests that impact EnergyPlus code must also include unit tests to cover enhancement or defect repair
  • Author should provide a "walkthrough" of relevant code changes using a GitHub code review comment process
  • If any diffs are expected, author must demonstrate they are justified using plots and descriptions
  • If changes fix a defect, the fix should be demonstrated in plots and descriptions
  • If any defect files are updated to a more recent version, upload new versions here or on DevSupport
  • If IDD requires transition, transition source, rules, ExpandObjects, and IDFs must be updated, and add IDDChange label
  • If structural output changes, add to output rules file and add OutputChange label
  • If adding/removing any LaTeX docs or figures, update that document's CMakeLists file dependencies

Reviewer

  • Perform a Code Review on GitHub
  • If branch is behind develop, merge develop and build locally to check for side effects of the merge
  • If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If feature, test running new feature, try creative ways to break it
  • CI status: all green or justified
  • Check that performance is not impacted (CI Linux results include performance check)
  • Run Unit Test(s) locally
  • Check any new function arguments for performance impacts
  • Verify IDF naming conventions and styles, memos and notes and defaults
  • If new idf included, locally check the err file and other outputs

@yujiex yujiex added NewFeature Includes code to add a new feature to EnergyPlus IDDChange Code changes impact the IDD file (cannot be merged after IO freeze) labels Mar 20, 2025
@yujiex yujiex self-assigned this Mar 20, 2025
@EnergyArchmage
Copy link
Contributor

As I read the description it sounds like you intend one set of plant nodes for cooling loop connection and another set of plant nodes for heating loop connectiong. But the IDD only has one set.

@yujiex
Copy link
Collaborator Author

yujiex commented Mar 21, 2025

As I read the description it sounds like you intend one set of plant nodes for cooling loop connection and another set of plant nodes for heating loop connectiong. But the IDD only has one set.

I was debating on this: whether I should do separate sets of nodes for heating, cooling, and potentially domestic hot water; or resemble the actual system which just have one set of nodes (1 air 1 water). Any suggestions?


Table 2. Input fields of the heat pump objects in EnergyPlus

<table class="table table-bordered table-hover table-condensed">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table is almost impossible to read on GitHub since the columns are so wide and it is so tall it cannot be scrolled. I would suggest either breaking it into multiple tables or make the column width narrower by using shorter titles in each.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me adjust the format and try to make it more readable

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reformatted the table. Hope it's more readable now.

## Proposed Approach ##

This new feature proposes to create a new input object, HeatPump:AirToWater.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is very difficult to compare what is being proposed to what appeared in the table of existing heat pumps because the of difficulties in that table (see previous comment). Perhaps repeat the table with the new object or else include it in the original table. Or perhaps you state that you are including all features from that table (if you are).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will include the proposed one in the comparison table.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposed object is added as another row in the idd comparison table

- time since defrost started, timeSinceStart

Note that ambient air temperature is already an existing EnergyPlus actuator. It could be used in defrost calculation as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please describe the outputs that you are planning to provide. Including timestep output variables, modifications or additions to the tabular outputs, and whether any rated efficiency metrics (such as from AHRI or ISO or other standards organizations) are going to be provided.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a section describing the output

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a section listing the output variables

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. It would be great to add a tabular output file in the Equipment Summary report. Maybe it could include outputting a calculated rating efficiency equivalent to an AHRI or ISO test procedure (assuming there is one).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just added some contents to the "Outputs" section including some info on entries to be added to the Equipment Summary report.

boiler to produce hot water. The following is an example of the current object.
The object specifies capacity and EIR as functions of temperature and part load
ratio, with three performance curves.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this new object intending to replace the use of any existing objects and deprecate those? If not, adding a new object just seems to add some additional confusion on which one should be used. Perhaps you should include in the NFP some user oriented guidance on when each object that is mentioned in the NFP should be used including the proposed new one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new object can model multi-speed/variable-speed/modular products while the existing one can't. So for these cases users would want to use the new object. I will add some documentation on how to choose between them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a section with notes on which model to use to model an air-to-water heat pump.

@yujiex
Copy link
Collaborator Author

yujiex commented Apr 8, 2025

Thanks for the feedbacks from the last technicality meetings. Regarding the few issues brought up.

  • The idd object is modified to have separate water inlet outlet nodes for chilled water loop, hot water loop, and condenser loop.
  • We will try to reuse existing logic/code but we're leaning towards keeping the new object with the heating and cooling component both encapsulated in this object. The new object will manage the coordination of heating and cooling operation internally.
  • As for the performance curve: the 205 chiller performance map option is indeed pretty generic and clean. However manufacturers do not have this kind of data, they seem to provide data by stage (or speed level for variable-speed case). The current plant loop heat pump object performance curves cannot fully represent multi-stage products. The following is a toy example.

Toy example

For simplicity, we'll control theControl PLR to be the same: PLR = 0.4 (load / max capacity = 0.4)
Capacity is a function of both air and water temperature, for easy display, we'll fix the air temperature and only vary the water temperature. The figure plots the low-capacity and high-capacity heating capacity data

capacity_curve_at_70Fair

As the plant loop heat pump only has one EIR curve as a function of air and water temperature, and EIR curve as a function of PLR, we want to find a PLR adjustment function f(.) such that the following holds.

$$ EIR_{high}(T_{air},T_{water}) * f(PLR) = EIR_{low}(T_{air},T_{water}) $$

As PLR is fixed to 0.4, $f(PLR)$ should be a constant, however, from the following EIR data plot. The $EIR_{high}(T_{air},T_{water}) / EIR_{low}(T_{air},T_{water})$ ratio are different for different water temperature value. We cannot find an $f(0.4)$ to make the above equation holds.

EIR_curve_at_70Fair

For heating capacity, the one set of curve is also not enough: Using just one set of curve, the cycling ratio is always 0.4 (for simplicity assuming there's no minPLR limits and cycling ratio and PLR are equal) in this example for all water temperature values shown in the first figure. However, in reality, the second stage compressor will always be off and the first stage compressor will cycle. It's cycling ratio is not always constant, it varies between 0.73 and 0.75.

@yujiex
Copy link
Collaborator Author

yujiex commented Apr 14, 2025

Re @EnergyArchmage question from last time, we have a field "Compressor Multiplier" to model modular heat pumps where several of the same AWHP objects can be used together to provide larger heating/cooling capacity.

We will restrict the system to allow only one Air-to-Water Heat Pump (AWHP) object to serve a single hot or chilled water loop. This means that load splitting between two AWHP objects will not be permitted. The current object cannot model scenarios where two multi-stage AWHPs (AWHP-A with "Compressor Multiplier" = 2) and one variable speed compressor AWHP (AWHP-B with "Compressor Multiplier" = 1) serve the same set of heating or cooling loads, as this would require splitting loads between the two AWHPs, which is not allowed.

However, we do allow configurations where one AWHP serves the first floor and another serves the second floor, with each floor having its own dedicated plant loop.

@rraustad
Copy link
Contributor

rraustad commented Sep 29, 2025

  1. The speeds are now reported discretely for FixedSpeed, I haven't tested VariableSpeed ControlType yet. Do you consider adding additional output variables like Cycling Ratio or Speed Ratio to report the ratio between stages, like UnitarySystem?

Re 2: There is a cycling ratio to capture the case for when the capacity at minimum speed level is larger than the demand. Would we want a speed ratio output for the demand > min-speed-capacity @dareumnam?

@yujiex I think the output (a speed ratio output for the demand > min-speed-capacity ) will definitely be helpful to the modelers, so it’s a good idea to include it as well. @mitchute do you have any other thoughts on this?

SpeedRatio is used for multi- or variable-speed components (e.g., the 10-speed coil model). PartLoadRatio is used for single-speed units with a cycling ratio variable to show when the unit turns on and off in a time step. You can certainly add SpeedRatio but it should be the same as PartLoadRatio so would be redundant at speed 1.

Update: This new model does have a Number of Speeds input so maybe this is a good idea.

@yujiex
Copy link
Collaborator Author

yujiex commented Sep 29, 2025

@dareumnam @rraustad thanks for your input. I will add a speed ratio output.

@github-actions
Copy link

⚠️ Regressions detected on macos-14 for commit 0f601d3

Regression Summary
  • Audit: 798
  • Table Big Diffs: 2

@github-actions
Copy link

⚠️ Regressions detected on ubuntu-24.04 for commit 0f601d3

Regression Summary
  • Audit: 802
  • Table Big Diffs: 2

this->eirFuncTempCurveValue = (1 - interpRatio) * eirModifierFuncTempLow + interpRatio * eirModifierFuncTempHigh;
this->eirFuncPLRModifierValue = (1 - interpRatio) * eirModifierFuncPLRLow + interpRatio * eirModifierFuncPLRHigh;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yujiex do you think you need to pull the powerUsage at lines 702 and 703 into this if/else block? why set speed level and performance curves at the high speed for CompressorControlType::FixedSpeed and interpolated otherwise while the power calculation always interpolates?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the multi-stage fixed-speed case, when the demand is between speed level n and speed level n-1, the heat pump runs p% time at speed n and (1-p)% time at speed n - 1.
for the variable speed case, it interpolates the performance curve to mimic modulating to a intermediate speed to satisfy the demand

I felt the two should theoretically result in the same power consumption? Like if the two speed level has capacity 100W and 200W and that the demand is 150W for example, then in the first case, it will have 50% time running at the lower speed and 50% time running at the higher speed; in the second case, it will interpolate the two speed levels with interpolation factor of 0.5. In both cases, they'll have the same power consumption: average of the power at the two speed level?

// currentLoad will be met and there should? be some adjustment based on outlet water temp limit?
}

void HeatPumpAirToWater::calcLoadSideHeatTransfer(EnergyPlusData &state, Real64 const availableCapacity, Real64 const currentLoad)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be out or scope for this new model but all plant equipment models should be passing back the load met (Real64 &currentLoad) to the plant manager (i.e., returned from the simulate function call to the plant manager) so the plant knows what the remaining load is. If the load is greater than the equipment capacity then the plant will have a remaining load to meet. All the simulate functions should look like this: simulate(state, , , Real64 &curLoad) {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Although in void EIRPlantLoopHeatPump::simulate(), it seems like CurLoad is not updated in the function: in in both setOperatingFlowRatesASHP(..., CurLoad), and doPhysics(state, CurLoad), CurLoad is passed by value.

// calculate power usage from EIR curves
Real64 eirModifierFuncTempLow = 1.0;
Real64 eirModifierFuncPLRLow = 1.0;
if (speedLevel > 0) {
Copy link
Contributor

@rraustad rraustad Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed at the bottom of this function is this->speedLevel += 1; and that this conditional and the curve indexes below use a local variable speedLevel. This local variable is always = the operating speed. So what does the this->speedLevel += 1; at the bottom of the function do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this->speedLevel += 1 is for reporting. In the internal calculation, the speed level is 0-indexed. But to follow the reporting convention, speed level need to start from 1. So there is the shift in this->speedLevel.

@github-actions
Copy link

⚠️ Regressions detected on macos-14 for commit 7af3821

Regression Summary
  • Audit: 798
  • Table Big Diffs: 2

@github-actions
Copy link

⚠️ Regressions detected on ubuntu-24.04 for commit 7af3821

Regression Summary
  • Audit: 802
  • Table Big Diffs: 2

Copy link
Contributor

@rraustad rraustad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All questions were sufficiently answered. @mitchute this looks ready.

@mitchute
Copy link
Collaborator

mitchute commented Oct 1, 2025

I bumped the version numbers up to current. We can merge this after CI wraps up one last time.

@github-actions
Copy link

github-actions bot commented Oct 1, 2025

⚠️ Regressions detected on macos-14 for commit 434addf

Regression Summary
  • Audit: 798
  • Table Big Diffs: 2

@github-actions
Copy link

github-actions bot commented Oct 1, 2025

⚠️ Regressions detected on ubuntu-24.04 for commit 434addf

Regression Summary
  • Audit: 802
  • Table Big Diffs: 2

Copy link
Collaborator

@dareumnam dareumnam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rraustad, thanks so much for your review and input.
I built it locally, ran the newly added unit tests (all passed), and ran the created test files successfully, confirming the results match what Yujie explained. CI checks look good, diffs are explained and verified, comments have been addressed, and all related documentation has been updated. This one looks all set to go. @mitchute

@mitchute
Copy link
Collaborator

mitchute commented Oct 1, 2025

OK, I made another quick review and made one final commit. We'll let CI finish, then merge this.

@github-actions
Copy link

github-actions bot commented Oct 1, 2025

⚠️ Regressions detected on ubuntu-24.04 for commit 7ab7c32

Regression Summary
  • Audit: 802
  • RDD: 3
  • Table Big Diffs: 2

@github-actions
Copy link

github-actions bot commented Oct 1, 2025

⚠️ Regressions detected on macos-14 for commit 7ab7c32

Regression Summary
  • Audit: 798
  • RDD: 3
  • Table Big Diffs: 2

@mitchute
Copy link
Collaborator

mitchute commented Oct 1, 2025

Been waiting most of the day, but I think Decent CI is hung up here. Merging and moving on. Thanks all.

@mitchute mitchute merged commit d4b5202 into develop Oct 1, 2025
10 of 11 checks passed
@mitchute mitchute deleted the NewAWHPobject branch October 1, 2025 21:25
Copy link
Contributor

@jmarrec jmarrec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just have a couple of questions / suggestions. Sorry for dropping in late.

I'm guessing you tested this with real world manufacturer data?

A datasets/ file would be nice, or at least one example file should use proper data.

Comment on lines +434 to +476
Curve:Biquadratic,
CapCurveFuncTemp, !- Name
0.5, !- Coefficient1 Constant
0.0, !- Coefficient2 x
0.0, !- Coefficient3 x**2
0.0, !- Coefficient4 y
0.0, !- Coefficient5 y**2
0.0, !- Coefficient6 x*y
5.0, !- Minimum Value of x
10.0, !- Maximum Value of x
24.0, !- Minimum Value of y
35.0, !- Maximum Value of y
, !- Minimum Curve Output
, !- Maximum Curve Output
Temperature, !- Input Unit Type for X
Temperature, !- Input Unit Type for Y
Dimensionless; !- Output Unit Type

Curve:Biquadratic,
EIRCurveFuncTemp, !- Name
1.0, !- Coefficient1 Constant
0.0, !- Coefficient2 x
0.0, !- Coefficient3 x**2
0.0, !- Coefficient4 y
0.0, !- Coefficient5 y**2
0.0, !- Coefficient6 x*y
5.0, !- Minimum Value of x
10.0, !- Maximum Value of x
24.0, !- Minimum Value of y
35.0, !- Maximum Value of y
, !- Minimum Curve Output
, !- Maximum Curve Output
Temperature, !- Input Unit Type for X
Temperature, !- Input Unit Type for Y
Dimensionless; !- Output Unit Type

Curve:Quadratic,
EIRCurveFuncPLR, !- Name
1.0, !- Coefficient1 Constant
0.0, !- Coefficient2 x
0.0, !- Coefficient3 x**2
0.0, !- Minimum Value of x
1.0; !- Maximum Value of x
Copy link
Contributor

@jmarrec jmarrec Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for jumping late, i just noticed this PR dropping in develop yesterday and I am interested in the feature, as I have had to model AWHP before including some systems like the one mentioned in the PR's original post.

I'm a bit confused by these curves, and they seem really generic to mimic a real world system.

I'm sure the testfiles added are nice to ensure that a simulation runs and for coverage, but it's also an Example File, so does it mimic real world operation?

Comment on lines +472 to +497
EnergyManagementSystem:Actuator,
opModeAct_cooling, !- Name
test_AWHP, !- Actuated Component Unique Name
HeatPump:AirToWater:Cooling, !- Actuated Component Type
Operating Mode; !- Actuated Component Control Type

EnergyManagementSystem:OutputVariable,
Operating Model Output Cooling, !- Name
opModeAct_cooling, !- EMS Variable Name
Averaged, !- Type of Data in Variable
SystemTimeStep, !- Update Frequency
, !- EMS Program or Subroutine Name
; !- Units

EnergyManagementSystem:ProgramCallingManager,
Change_OpMode_EMS_Program_Manager, !- Name
InsideHVACSystemIterationLoop, !- EnergyPlus Model Calling Point
Change_OpMode_EMS_Program; !- Program Name 1

EnergyManagementSystem:Program,
Change_OpMode_EMS_Program, !- Name
IF (Hour <= 12), !- Program Line 2
SET opModeAct_cooling = 0, !- <none>
ELSE, !- <none>
SET opModeAct_cooling = 1, !- <none>
ENDIF; !- <none>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this EMS program here?

Again, this ends up being an Example File.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some example EMS code for controlling the operating mode. Do you feel it's too simplified and better be removed? I can update the example in a followup PR.

Comment on lines +1 to +9
! PlantLoopHeatPump_EIR_AirSource.idf
!
! Basic file description:
! The EIR formulated air-to-water heat pump is demonstrated using a simple collection of plant loops. Building loads
! are represented using Plant Load Profile objects
! Run: 1 design day.
! Building: None.
! System: None.
! Plant: PLANT LOAD PROFILE with District Heating and Cooling as necessary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three testfiles added have exactly the same description. I feel like there should be be some difference here.

And the mention of the special shenanigans like the EMS program would probably be good for the heating/cooling only ones.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I forgot to update the description. I will modify them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this and for all your review! @jmarrec

@yujiex
Copy link
Collaborator Author

yujiex commented Oct 3, 2025

Just have a couple of questions / suggestions. Sorry for dropping in late.

I'm guessing you tested this with real world manufacturer data?

A datasets/ file would be nice, or at least one example file should use proper data.

Yes, a Daikin engineer is using their performance data to test the model. But they don't want to reveal their product data, so the one here uses very generic setting in the curves. I'll try to create another example with more realistic data. I'll create a followup PR to add it in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

IDDChange Code changes impact the IDD file (cannot be merged after IO freeze) NewFeature Includes code to add a new feature to EnergyPlus

Projects

None yet

Development

Successfully merging this pull request may close these issues.