From 8a1c2511a0e8facdb4b9db5186aebd7941a582bd Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Mon, 8 Jan 2024 13:04:15 -0700 Subject: [PATCH] Eval first-hour rating. --- src/HPWH.cc | 203 +++++++++++++++++++++++++++++++++++++++----- src/HPWH.hh | 7 +- test/testCalcUEF.cc | 77 ++++++++++++----- 3 files changed, 240 insertions(+), 47 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index a1dd2a81..730bcc16 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -5400,30 +5400,186 @@ int HPWH::HPWHinit_file(string configFile) } #endif -HPWH::Usage HPWH::findUsageFromFirstHourRating() { return Usage::Medium; } - -HPWH::Usage HPWH::findUsageFromMaximumGPM_Rating() +bool HPWH::findUsageFromFirstHourRating(HPWH::Usage& usage, const double setpointT_C /* = 51.7 */) { - // Assume flow rate unlimited for heat-exchange models - if (hasHeatExchanger) + double flowRate_Lper_min = GAL_TO_L(3.); + if (tankVolume_L < GAL_TO_L(20.)) + flowRate_Lper_min = GAL_TO_L(1.5); + + constexpr double inletT_C = 14.4; // p. 40433 + constexpr double ambientT_C = 19.7; // p. 40435 + constexpr double externalT_C = 19.7; + constexpr DRMODES drMode = DR_ALLOW; + if (!isSetpointFixed()) + { + if (setSetpoint(setpointT_C, UNITS_C) == HPWH_ABORT) + { + return false; + } + } + + double maxT_C = getTankTemp_C(); + double prevMinT_C = getTankTemp_C(); + double tolT_C = dF_TO_dC(0.); // tolerance + + double drawVolume_L = 0.; + double lastPassDrawVolume_L = 0.; + int testTime_min = 0; + int drawOnTime = 0; + int prevDrawOffTime_min = 0; + bool heatersAreOn = false; + bool isDrawing = false; + bool testIsRunning = true; + int testStage = 1; + bool lastDrawSucceeded = false; + while (testIsRunning) + { + heatersAreOn = false; + for (auto& heatSource : heatSources) + { + heatersAreOn |= heatSource.isEngaged(); + } + double tankT_C = getTankTemp_C(); + + switch (testStage) + { + case 1: + { + if (!heatersAreOn) + { + isDrawing = true; + drawOnTime = testTime_min; + testStage = 2; + } + break; + } + + case 2: + { + if (heatersAreOn) + { + isDrawing = false; + prevDrawOffTime_min = testTime_min; + maxT_C = tankT_C; + testStage = 3; + } + break; + } + + case 3: + { + if (isDrawing) + { + double targetT_C = maxT_C - dF_TO_dC(15.) - tolT_C; + if (outletTemp_C < targetT_C) // outletT has dropped by 15 degF below max T + { + prevMinT_C = outletTemp_C; + maxT_C = tankT_C; // initialize for next pass + isDrawing = false; + prevDrawOffTime_min = testTime_min; + } + } + else + { + if (tankT_C > maxT_C) // has not reached max T + { + maxT_C = tankT_C + tolT_C; + if (testTime_min >= 60) // start a final draw + { + isDrawing = true; + drawOnTime = testTime_min; + testStage = 4; + } + } + else { + if (testTime_min >= 60) + { + testIsRunning = false; + } + else + { + isDrawing = true; + drawOnTime = testTime_min; + } + } + } + break; + } + + case 4: + { + if (testTime_min > drawOnTime) + { + if (outletTemp_C > prevMinT_C - tolT_C) + { + lastDrawSucceeded = true; + } + } + if (outletTemp_C <= prevMinT_C + tolT_C) + { + testIsRunning = false; + } + } + } + + // limit draw-volume increment to tank volume + double incrementalDrawVolume_L = isDrawing ? flowRate_Lper_min * (1.) : 0.; + if (incrementalDrawVolume_L > tankVolume_L) + { + incrementalDrawVolume_L = tankVolume_L; + } + + if (runOneStep(inletT_C, // inlet water temperature (C) + incrementalDrawVolume_L, // draw volume (L) + ambientT_C, // ambient Temp (C) + externalT_C, // external Temp (C) + drMode, // DDR Status + 0., // inlet-2 volume (L) + inletT_C, // inlet-2 Temp (C) + NULL) // no extra heat + == HPWH_ABORT) + { + return false; + } + + // std::cout << preTime_min << ": " << getTankTemp_C() << ", " << (heatersAreOn? "On": + // "Off") << "\n"; + if (testStage == 4) + { + lastPassDrawVolume_L += incrementalDrawVolume_L; + } + else + { + drawVolume_L += incrementalDrawVolume_L; + } + + ++testTime_min; + } + + if (lastDrawSucceeded) { - return Usage::High; + drawVolume_L += lastPassDrawVolume_L; } - // Assume max. flow rate = tankVolume / (1 min) - else if (tankVolume_L < L_TO_GAL(1.7)) + // + if (drawVolume_L < GAL_TO_L(18.)) { - return Usage::VerySmall; + usage = Usage::VerySmall; } - else if (tankVolume_L < L_TO_GAL(2.8)) + else if (drawVolume_L < GAL_TO_L(51.)) { - return Usage::Low; + usage = Usage::Low; } - else if (tankVolume_L < L_TO_GAL(4.)) + else if (drawVolume_L < GAL_TO_L(75.)) { - return Usage::Medium; + usage = Usage::Medium; } - return Usage::High; + else + { + usage = Usage::High; + } + + return true; } //----------------------------------------------------------------------------- @@ -5433,7 +5589,9 @@ HPWH::Usage HPWH::findUsageFromMaximumGPM_Rating() /// @param[out] UEF Result of calculation /// @return true (success), false (failure). //----------------------------------------------------------------------------- -bool HPWH::runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary) +bool HPWH::runDailyTest(const Usage usage, + DailyTestSummary& dailyTestSummary, + const double setpointT_C /* = 51.7 */) { // select the draw pattern based on usage DrawPattern* drawPattern = nullptr; @@ -5465,13 +5623,11 @@ bool HPWH::runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary) return false; } - const double inletT_C = 14.4; // p. 40433 - const double ambientT_C = 19.7; // p. 40435 - const double externalT_C = 19.7; - const DRMODES drMode = DR_ALLOW; + constexpr double inletT_C = 14.4; // p. 40433 + constexpr double ambientT_C = 19.7; // p. 40435 + constexpr double externalT_C = 19.7; + constexpr DRMODES drMode = DR_ALLOW; - /* - constexpr double setpointT_C = 51.7; // if (!isSetpointFixed()) { if (setSetpoint(setpointT_C, UNITS_C) == HPWH_ABORT) @@ -5479,7 +5635,6 @@ bool HPWH::runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary) return false; } } - */ int preTime_min = 0; bool heatersAreOn = false; @@ -5523,6 +5678,7 @@ bool HPWH::runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary) bool isFirstRecoveryPeriod = true; double firstRecoveryUsedEnergy_kJ = 0.; double recoveryHeatingEnergy_kJ = 0.; + bool hasHeated = false; int runTime_min = 0; for (auto& draw : *drawPattern) @@ -5602,7 +5758,8 @@ bool HPWH::runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary) { heatersAreOn |= heatSource.isEngaged(); } - if (!heatersAreOn) + hasHeated |= heatersAreOn; + if (hasHeated && (!heatersAreOn)) { isFirstRecoveryPeriod = false; diff --git a/src/HPWH.hh b/src/HPWH.hh index 8fd10a73..2a67abc2 100644 --- a/src/HPWH.hh +++ b/src/HPWH.hh @@ -957,10 +957,7 @@ class HPWH }; /// Determine usage using the first-hour rating method - Usage findUsageFromFirstHourRating(); - - /// Determine usage using the maximum GPM rating method - Usage findUsageFromMaximumGPM_Rating(); + bool findUsageFromFirstHourRating(HPWH::Usage &usage,const double setpointT_C = 51.7); struct DailyTestSummary { @@ -976,7 +973,7 @@ class HPWH }; /// run 24-hr draw pattern - bool runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary); + bool runDailyTest(const Usage usage, DailyTestSummary& dailyTestSummary, const double setpointT_C = 51.7); struct Draw { diff --git a/test/testCalcUEF.cc b/test/testCalcUEF.cc index 19af2353..9a77da6f 100644 --- a/test/testCalcUEF.cc +++ b/test/testCalcUEF.cc @@ -7,7 +7,9 @@ #include /* Evaluate UEF based on simulations using standard profiles */ -static bool testCalcMetrics(const std::string& sModelName, HPWH::DailyTestSummary& dailyTestSummary) +static bool testCalcMetrics(const std::string& sModelName, + HPWH::Usage& usage, + HPWH::DailyTestSummary& dailyTestSummary) { HPWH hpwh; @@ -17,11 +19,16 @@ static bool testCalcMetrics(const std::string& sModelName, HPWH::DailyTestSummar return false; } - return hpwh.runDailyTest(hpwh.findUsageFromMaximumGPM_Rating(), dailyTestSummary); + if (!hpwh.findUsageFromFirstHourRating(usage)) + return false; + + return hpwh.runDailyTest(usage, dailyTestSummary); } int main(int argc, char* argv[]) { + HPWH::Usage usage; + HPWH::DailyTestSummary dailyTestSummary; bool validNumArgs = false; bool runUnitTests = false; @@ -48,13 +55,13 @@ int main(int argc, char* argv[]) if (runUnitTests) { - HPWH::DailyTestSummary dailyTestSummary; + ASSERTTRUE(testCalcMetrics("AquaThermAire", usage, dailyTestSummary)); + ASSERTTRUE(usage == HPWH::Usage::High); + ASSERTTRUE(cmpd(dailyTestSummary.UEF, 3.2797)); - ASSERTTRUE(testCalcMetrics("AquaThermAire", dailyTestSummary)); - ASSERTTRUE(cmpd(dailyTestSummary.UEF, 2.8258)); - - ASSERTTRUE(testCalcMetrics("AOSmithHPTS50", dailyTestSummary)); - ASSERTTRUE(cmpd(dailyTestSummary.UEF, 4.8021)); + ASSERTTRUE(testCalcMetrics("AOSmithHPTS50", usage, dailyTestSummary)); + ASSERTTRUE(usage == HPWH::Usage::Medium); + ASSERTTRUE(cmpd(dailyTestSummary.UEF, 4.8246)); return 0; } @@ -104,21 +111,53 @@ int main(int argc, char* argv[]) std::cout << "Spec type: " << sPresetOrFile << "\n"; std::cout << "Model name: " << sModelName << "\n"; - HPWH::DailyTestSummary dailyTestSummary; - if (hpwh.runDailyTest(hpwh.findUsageFromMaximumGPM_Rating(), dailyTestSummary)) + if (hpwh.findUsageFromFirstHourRating(usage)) { - std::cout << "\tRecovery Efficiency: " << dailyTestSummary.recoveryEfficiency << "\n"; - std::cout << "\tAdjusted Daily Water Heating Energy Consumption (kJ): " - << dailyTestSummary.adjustedDailyWaterHeatingEnergyConsumption_kJ << "\n"; - std::cout << "\tUEF: " << dailyTestSummary.UEF << "\n"; - std::cout << "\tAnnual Electrical Energy Consumption (kJ): " - << dailyTestSummary.annualElectricalEnergyConsumption_kJ << "\n"; - std::cout << "\tAnnual Energy Consumption (kJ): " - << dailyTestSummary.annualEnergyConsumption_kJ << "\n"; + std::string sUsage = ""; + switch (usage) + { + case HPWH::Usage::VerySmall: + { + sUsage = "Very Small"; + break; + } + case HPWH::Usage::Low: + { + sUsage = "Low"; + break; + } + case HPWH::Usage::Medium: + { + sUsage = "Medium"; + break; + } + case HPWH::Usage::High: + { + sUsage = "High"; + break; + } + } + std::cout << "\tUsage: " << sUsage << "\n"; + + if (hpwh.runDailyTest(usage, dailyTestSummary)) + { + std::cout << "\tRecovery Efficiency: " << dailyTestSummary.recoveryEfficiency << "\n"; + std::cout << "\tUEF: " << dailyTestSummary.UEF << "\n"; + std::cout << "\tAdjusted Daily Water Heating Energy Consumption (kWh): " + << KJ_TO_KWH(dailyTestSummary.adjustedDailyWaterHeatingEnergyConsumption_kJ) << "\n"; + std::cout << "\tAnnual Electrical Energy Consumption (kWh): " + << KJ_TO_KWH(dailyTestSummary.annualElectricalEnergyConsumption_kJ) << "\n"; + std::cout << "\tAnnual Energy Consumption (kWh): " + << KJ_TO_KWH(dailyTestSummary.annualEnergyConsumption_kJ) << "\n"; + } + else + { + std::cout << "Unable to determine efficiency metrics.\n"; + } } else { - std::cout << "Unable to determine efficiency metrics.\n"; + std::cout << "Unable to determine first-hour rating.\n"; } return 0;