Skip to content

Allow UEF input for Heat Pump Water Heaters #537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions doc/src/records/dhwheater-doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ activated via DHWSYS wsCalcMode=PRERUN. See RACM Appendix B.

**whUEF=*float***

Water heater Uniform Energy Factor efficiency rating, used when whType=INSTANTANEOUSUEF.
Water heater Uniform Energy Factor efficiency rating. Required when whType=INSTANTANEOUSUEF. When specified with
whType=SMALLSTORAGE and whHeatSrc=ASHPX, the compressor performance will be adjusted to match the value input here.

<%= member_table(
units: "",
Expand Down Expand Up @@ -357,10 +358,14 @@ Air source heat pump type, valid only if whHeatSrc=ASHPX. These choices are supp
"Scalable_MP","multipass scalable type for autosized standard design"
"AquaThermAire","Villara AquaThermAire HPWH"
"GenericUEF217","65-gal tank meeting Federal standard minimum requirement"
"AWHSTier4Generic40","Any 40-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier4Generic50","Any 50-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier4Generic65","Any 65-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier4Generic80","Any 80-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier3Generic40","A 40-gal tank meeting Advanced Water Heating Specification Tier-3 minimum requirements"
"AWHSTier3Generic50","A 50-gal tank meeting Advanced Water Heating Specification Tier-3 minimum requirements"
"AWHSTier3Generic65","A 65-gal tank meeting Advanced Water Heating Specification Tier-3 minimum requirements"
"AWHSTier3Generic80","A 80-gal tank meeting Advanced Water Heating Specification Tier-3 minimum requirements"
"AWHSTier4Generic40","A 40-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier4Generic50","A 50-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier4Generic65","A 65-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"AWHSTier4Generic80","A 80-gal tank meeting Advanced Water Heating Specification Tier-4 minimum requirements"
"BradfordWhiteAeroThermRE2H50","Bradford White 50-gal AeroTherm2023"
"BradfordWhiteAeroThermRE2H65","Bradford White 65-gal AeroTherm2023"
"BradfordWhiteAeroThermRE2H80","Bradford White 80-gal AeroTherm2023"
Expand Down
2 changes: 1 addition & 1 deletion src/curvemap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ RC PERFORMANCEMAP::pm_SetupBtwxt( // input -> Btwxt conversion
return rc;

// message handling linkage
auto MX = std::make_shared< CourierMsgHandlerRec>( pParent);
auto MX = std::make_shared< CSERecordCourier>( pParent);

// transfer grid value to Btwxt axes
std::vector<Btwxt::GridAxis> gridAxes;
Expand Down
45 changes: 15 additions & 30 deletions src/dhwcalc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

#include "cnguts.h"
#include "exman.h"
#include "hvac.h" // CourierMsgHandler, CourierMsgHandlerBase, MessageLevel
#include "hvac.h" // Courier

#include "HPWH.hh" // decls/defns for Ecotope heat pump water heater model

Expand Down Expand Up @@ -2917,33 +2917,6 @@ RC HPWHLINK::Validate(int /*options*/)
} // HPWHLINK::Validate
#endif
//-----------------------------------------------------------------------------
/*static*/ void HPWHLINK::hw_HPWHMessageCallback(
const std::string message,
void* contextPtr)
{
((HPWHLINK*)contextPtr)->hw_HPWHReceiveMessage(message);
} // HPWHLINK::hw_HPWHMessageCallback
//-----------------------------------------------------------------------------
void HPWHLINK::hw_HPWHReceiveMessage(const std::string message)
{
// forward to owner
hw_pOwner->ReceiveRuntimeMessage( message.c_str());

} // HPWHLINK::hw_HPWHReceiveMessage
//-----------------------------------------------------------------------------
static void HPWHLINK_Callback( // message dispatcher
void *pContext, // pointer to specific RSYS
MSGTY msgTy, // message type: bsxmsgERROR etc
const char *msg) // message text
{
HPWHLINK *pHPWHLINK = reinterpret_cast<HPWHLINK *>(pContext);

record *pParent = pHPWHLINK->hw_pOwner;
const char *msgx = strtprintf("HPWHLINK: %s", msg);
pParent->ReceiveMessage(msgTy, msgx);

} // HPWHLINK_Callback
//-----------------------------------------------------------------------------
RC HPWHLINK::hw_Init( // 1st initialization
record* pOwner) // owner object (DHWHEATER, DHWSOLARSYS, ...)
{
Expand All @@ -2962,8 +2935,8 @@ RC HPWHLINK::hw_Init( // 1st initialization

hw_fMixUse = hw_fMixRL = 1.f;

auto MX = std::make_shared<CourierMsgHandler>(HPWHLINK_Callback, this);
hw_pHPWH = new HPWH(MX);
auto MX = std::make_shared<CSERecordCourier>(pOwner);
hw_pHPWH = new HPWH(MX, pOwner->Name());

hw_pHPWH->setMinutesPerStep(Top.tp_tickDurMin); // minutesPerStep

Expand Down Expand Up @@ -4679,6 +4652,18 @@ RC DHWHEATER::wh_HPWHInit() // initialize HPWH model
}
}

if (IsSet(DHWHEATER_UEF))
{
// Temporarily turn off warnings (to avoid excessive messages during iteration)
auto courier = dynamic_cast<CSERecordCourier*>(wh_HPWH.hw_pHPWH->get_courier().get());
courier->set_message_level(MSGTY::msgtyERROR);

// Adjust COP coefficients to match UEF
wh_HPWH.hw_pHPWH->makeGenericUEF(wh_UEF);

courier->restore_message_level();
}

// at this point, HPWH has known size and default UA
// (later capacity scaling does not alter size)
// if additional UA or insulR is provided, adjust UA
Expand Down
15 changes: 1 addition & 14 deletions src/hvac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,19 +400,6 @@ void CHDHW::hvt_Clear() // clear all non-static members

} // CHDHW::hvt_Clear
//-----------------------------------------------------------------------------
static void CHDHW_RGICallback( // btwxt message dispatcher
void* pContext, // pointer to specific RSYS
MSGTY msgTy, // message type: bsxmsgERROR etc
const char* msg) // message text
{
CHDHW* pCHDHW = reinterpret_cast<CHDHW*>(pContext);

record* pParent = pCHDHW->hvt_pParent;
const char* msgx = strtprintf("CHDHW: %s", msg);
pParent->ReceiveMessage(msgTy, msgx);

} // CHDHW_RGICallBack
//-----------------------------------------------------------------------------
RC CHDHW::hvt_Init( // one-time init
float operatingSFP) // full speed operating specific fan power, W/cfm
// returns RCOK iff success
Expand Down Expand Up @@ -446,7 +433,7 @@ RC CHDHW::hvt_Init( // one-time init
}

// message handler
auto cmhCHDHW = std::make_shared< CourierMsgHandler>(CHDHW_RGICallback, this);
auto cmhCHDHW = std::make_shared< CSERecordCourier>(hvt_pParent);

// 1D grid: capacity
GridAxis netCapAxis(netCaps,
Expand Down
81 changes: 37 additions & 44 deletions src/hvac.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,65 +31,58 @@ double FanVariableSpeedPowerFract(double flowFract, MOTTYCH motTy, bool bDucted)
#include <courier/courier.h>

// abstract base class
class CourierMsgHandlerBase : public Courier::Courier
class CSEBaseCourier : public Courier::Courier
{
public:
void receive_error(const std::string& crMsg) override { forward_message(MSGTY::msgtyERROR, crMsg); }
void receive_warning(const std::string& crMsg) override { forward_message(MSGTY::msgtyWARNING, crMsg); }
void receive_info(const std::string& crMsg) override { forward_message( MSGTY::msgtyINFO, crMsg); }
void receive_debug(const std::string& crMsg) override { forward_message(MSGTY::msgtyDEBUG, crMsg); }

void receive_warning(const std::string& crMsg) override {
if (message_level >= MSGTY::msgtyWARNING)
{
forward_message(MSGTY::msgtyWARNING, crMsg);
}
}
void receive_info(const std::string& crMsg) override {
if (message_level >= MSGTY::msgtyINFO) {
forward_message(MSGTY::msgtyINFO, crMsg);
}
}
void receive_debug(const std::string& crMsg) override {
if (message_level >= MSGTY::msgtyDEBUG) {
forward_message(MSGTY::msgtyDEBUG, crMsg);
}
}
void set_message_level(MSGTY message_level_in) {
// sets message level and preserves previous message level
// that can be restored with `restore_message_level()`.
stored_message_level = message_level;
message_level = message_level_in;
}
void restore_message_level() {
message_level = stored_message_level;
}

private:
MSGTY message_level{MSGTY::msgtyINFO};
virtual void forward_message(MSGTY msgty, const std::string& crMsg) = 0;
MSGTY stored_message_level{MSGTY::msgtyINFO};

}; // class CourierMsgHandlerBase
//-----------------------------------------------------------------------------
class CourierMsgHandler : public CourierMsgHandlerBase
{
public:
using MsgCallbackFunc = void(void* pContext, MSGTY msgty, const char* msg);
}; // class CSEBaseCourier

CourierMsgHandler(MsgCallbackFunc* pMsgCallbackFunc,void* context)
: cmh_pMsgCallbackFunc(pMsgCallbackFunc), cmh_context( context)
{ }

private:
virtual void forward_message(MSGTY msgty, const std::string& crMsg) override
{
const char* msg = crMsg.c_str();
if (cmh_pMsgCallbackFunc)
(*cmh_pMsgCallbackFunc)(cmh_context, msgty, msg);
else
err(PABT, "nullptr cmh_pMsgCallbackFunc '%s'", msg);

}

private:
void* cmh_context; // caller context
MsgCallbackFunc* cmh_pMsgCallbackFunc; // pointer to callback function

}; // class CourierMsgHandler
//-----------------------------------------------------------------------------
class CourierMsgHandlerRec : public CourierMsgHandlerBase
class CSERecordCourier : public CSEBaseCourier
// route message to initiating record-based object
{
public:
CourierMsgHandlerRec(class record* pRec)
: cmh_pRec(pRec)
CSERecordCourier(class record* record_pointer)
: record_pointer(record_pointer)
{}

private:
virtual void forward_message(MSGTY msgty, const std::string& crMsg) override
{
const char* msg = crMsg.c_str();
if (cmh_pRec)
cmh_pRec->ReceiveMessage(msgty, msg);
else
err(PABT, "nullptr cmh_pRec '%s'", msg);
class record* record_pointer; // pointer to record
void forward_message(MSGTY msgty, const std::string& crMsg) {
record_pointer->ReceiveMessage(msgty, crMsg.c_str());
}
class record* cmh_pRec; // pointer to record

}; // class CourierRecordHandlerRec
}; // class CSERecordCourier
//=============================================================================

///////////////////////////////////////////////////////////////////////////////
Expand Down
Loading