From 8150ae35fe0dd4f572038b0fe04410cc2dc75f99 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 21 Jan 2020 13:42:27 -0800 Subject: [PATCH 01/22] ExtraHeat for StorageTank --- src/HPWH.cc | 117 +++++++++++++++++++++++++++++++++++++------- src/HPWH.in.hh | 12 +++-- test/CMakeLists.txt | 14 +++--- test/main.cc | 13 +++-- 4 files changed, 125 insertions(+), 31 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 292b9e3c..57126afc 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -204,7 +204,8 @@ string HPWH::getVersion(){ int HPWH::runOneStep(double inletT_C, double drawVolume_L, double tankAmbientT_C, double heatSourceAmbientT_C, DRMODES DRstatus, double minutesPerStep, - double inletVol2_L, double inletT2_C) { + double inletVol2_L, double inletT2_C, + vector* nodeExtraHeat_W) { //returns 0 on successful completion, HPWH_ABORT on failure //check for errors @@ -379,6 +380,10 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, isHeating = false; } + //If theres extra user defined heat to add -> Add extra heat! + if (nodeExtraHeat_W != NULL) { + addExtraHeat(nodeExtraHeat_W); + } //track the depressed local temperature if (doTempDepression) { @@ -435,7 +440,6 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, } //end runOneStep - int HPWH::runNSteps(int N, double *inletT_C, double *drawVolume_L, double *tankAmbientT_C, double *heatSourceAmbientT_C, DRMODES *DRstatus, double minutesPerStep) { @@ -1553,6 +1557,34 @@ void HPWH::mixTankInversions() { } while ( hasInversion ); } + + +void addExtraHeat(vector* nodeExtraHeat_W){ + if ((*nodeExtraHeat_W).size() > numNodes){ + if (hpwhVerbosity >= VRB_reluctant) { + msg("nodeExtraHeat_KWH has more nodes (%i) than the HPWH tank (%i) \n", (*nodeExtraHeat_KWH).size(), numNodes); + } + return HPWH_ABORT; + } + + double minutesToRun = minutesPerStep; + + for (i = 0; i < numHeatSources; i++){ + if (setOfSources[i].typeOfHeatSource = TYPE_extra) { + + // Set up the extra heat source + setOfSources[i].setupExtraHeat(vector* nodeExtraHeat_W)) + + // add heat + setOfSources[i].addHeat(heatSourceAmbientT_C, minutesToRun); + + if (doInversionMixing) { + mixTankInversions(); + } + break; + } + } +} /////////////////////////////////////////////////////////////////////////////////// @@ -2381,6 +2413,41 @@ void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { typeOfHeatSource = TYPE_resistance; } +////////////////////////////////////////////////////////////////////////////////////////////////////// +void HPWH::HeatSource::setupExtraHeat(vector* nodeExtraHeat_W)) { + + //get sum of vector + double watts = 0.0;\ + for (int i = 0; i < (*nodeExtraHeat_W).size(); i++) { + watts += (*nodeExtraHeat_W)[i]; + } + + vector tempCondensity = *nodeExtraHeat_W; + normalize(tempCondensity); + // set condensity based on normalized vector + setCondensity( tempCondensity[0], tempCondensity[1], tempCondensity[2], tempCondensity[3], + tempCondensity[4], tempCondensity[5], tempCondensity[6], tempCondensity[7], + tempCondensity[8], tempCondensity[9], tempCondensity[10], tempCondensity[11] ); + + perfMap.clear(); + perfMap.reserve(2); + + perfMap.push_back({ + 50, // Temperature (T_F) + { watts, 0.0, 0.0 }, // Input Power Coefficients (inputPower_coeffs) + { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) + }); + + perfMap.push_back({ + 67, // Temperature (T_F) + { watts, 0.0, 0.0 }, // Input Power Coefficients (inputPower_coeffs) + { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) + }); + + // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() + calcDerivedHeatingValues(); +} +//////////////////////////////////////////////////////////////////////////// void HPWH::HeatSource::addTurnOnLogic(HeatingLogic logic){ this->turnOnLogicSet.push_back(logic); @@ -2395,6 +2462,22 @@ void HPWH::calcDerivedValues(){ // tank node density (number of calculation nodes per regular node) nodeDensity = numNodes / 12; + // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() + calcDerivedHeatingValues(); + + //heat source ability to depress temp + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { + setOfSources[i].depressesTemperature = true; + } + else if (setOfSources[i].typeOfHeatSource == TYPE_resistance) { + setOfSources[i].depressesTemperature = false; + } + } + +} + +void HPWH::calcDerivedHeatingValues(){ //condentropy/shrinkage double condentropy = 0; double alpha = 1, beta = 2; // Mapping from condentropy to shrinkage @@ -2417,7 +2500,6 @@ void HPWH::calcDerivedValues(){ } } - //lowest node int lowest = 0; for (int i = 0; i < numHeatSources; i++) { @@ -2450,7 +2532,8 @@ void HPWH::calcDerivedValues(){ for (int i = 0; i < numHeatSources; i++) { if (setOfSources[i].typeOfHeatSource == HPWH::TYPE_compressor) { compressorIndex = i; // NOTE: Maybe won't work with multiple compressors (last compressor will be used) - } else { + } + else { for (int j = 0; j < CONDENSITY_SIZE; j++) { if (setOfSources[i].condensity[j] > 0.0 && j < lowestElementPos) { lowestElementIndex = i; @@ -2467,21 +2550,9 @@ void HPWH::calcDerivedValues(){ if (hpwhVerbosity >= VRB_emetic) { msg(outputString, " lowestElementIndex : %d \n", lowestElementIndex); } - - //heat source ability to depress temp - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { - setOfSources[i].depressesTemperature = true; - } - else if (setOfSources[i].typeOfHeatSource == TYPE_resistance) { - setOfSources[i].depressesTemperature = false; - } - } - - - } + // Used to check a few inputs after the initialization of a tank model from a preset or a file. int HPWH::checkInputs(){ int returnVal = 0; @@ -3508,7 +3579,17 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //should eventually put tankmixes to true when testing progresses tankMixesOnDraw = false; - numHeatSources = 0; + //////////////////////////////////////////////////// + numHeatSources = 1; + + HeatSource extra(this); + + //compressor values + extra.isOn = false; + extra.isVIP = false; + extra.typeOfHeatSource = TYPE_extra; + extra.configuration = HeatSource::CONFIG_WRAPPED; + } //basic compressor tank for testing diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index a2c52759..b3c46841 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -146,7 +146,8 @@ class HPWH { enum HEATSOURCE_TYPE { TYPE_none, /**< a default to check to make sure it's been set */ TYPE_resistance, /**< a resistance element */ - TYPE_compressor /**< a vapor cycle compressor */ + TYPE_compressor, /**< a vapor cycle compressor */ + TYPE_extra /**< an extra element to add user defined heat*/ }; /** specifies the unit type for outputs in the CSV file-s */ @@ -391,9 +392,11 @@ class HPWH { /** An overloaded function that uses some member variables, instead of taking them as inputs */ int runOneStep(double drawVolume_L, double ambientT_C, - double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0.) { + double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0., + vector* nodeExtraHeat_W = NULL) { return runOneStep(member_inletT_C, drawVolume_L, ambientT_C, - externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C); + externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C, + nodeExtraHeat_W); }; /** Setters for the what are typically input variables */ void setInletT(double newInletT_C) {member_inletT_C = newInletT_C;}; @@ -414,6 +417,9 @@ class HPWH { void turnAllHeatSourcesOff(); /**< disengage each heat source */ + void addExtraHeat(vector* nodeExtraHeat_W); + /**< adds extra heat defined by the user. Where nodeExtraHeat[] is a vector of heat quantities to be added during the step. nodeExtraHeat[ 0] would go to bottom node, 1 to next etc. */ + double tankAvg_C(const std::vector nodeWeights) const; /**< functions to calculate what the temperature in a portion of the tank is */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 18f5f62a..5b342e83 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,13 +20,13 @@ set(testNames ) set(modelNames - #StorageTank - AOSmithPHPT60 - AOSmithHPTU80 - Sanden80 - RheemHB50 - Stiebel220e - GE502014 + StorageTank + # AOSmithPHPT60 + # AOSmithHPTU80 +# Sanden80 +# RheemHB50 +# Stiebel220e + #GE502014 ) set(lockoutTestModels diff --git a/test/main.cc b/test/main.cc index b8ec01af..7a1c511a 100644 --- a/test/main.cc +++ b/test/main.cc @@ -253,7 +253,7 @@ int main(int argc, char *argv[]) // ------------------------------------- Simulate --------------------------------------- // - + vector* nodeExtraHeat_W = NULL; // Loop over the minutes in the test cout << "Now Simulating " << minutesToRun << " Minutes of the Test\n"; for(i = 0; i < minutesToRun; i++) { @@ -275,6 +275,13 @@ int main(int argc, char *argv[]) } else if(allSchedules[4][i] == 2) { drStatus = HPWH::DR_ENGAGE; } + + if ( i == 15 || i== 100 || i== 150){ + nodeExtraHeat_W = { 1000, 20000, 3000, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + } + else{ + nodeExtraHeat_W = NULL; + } // Run the step hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) GAL_TO_L(allSchedules[1][i]), // Flow in gallons @@ -282,8 +289,8 @@ int main(int argc, char *argv[]) allSchedules[3][i], // External Temp (C) drStatus, // DDR Status (now an enum. Fixed for now as allow) 1.0, // Minutes per step - 1. * GAL_TO_L(allSchedules[1][i]), allSchedules[0][i]); - // 0., 0.) ; + 1. * GAL_TO_L(allSchedules[1][i]), allSchedules[0][i], + 0., 0., nodeExtraHeat_W); // Copy current status into the output file From f25f62a084ac4e5f288bb70dc889241fe808af27 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 21 Jan 2020 16:26:56 -0800 Subject: [PATCH 02/22] Debugged At least mostly debugged build. StorageTank is the only preset tank model that uses the extra heat input feature runOneStep takes a pointer to a double with units W at the end --- HPWH.cc | 5449 +++++++++++++++++++++++++++++++++++++++++++ HPWH.in.hh | 740 ++++++ src/HPWH.cc | 82 +- src/HPWH.in.hh | 13 +- test/CMakeLists.txt | 10 +- test/main.cc | 46 +- 6 files changed, 6271 insertions(+), 69 deletions(-) create mode 100644 HPWH.cc create mode 100644 HPWH.in.hh diff --git a/HPWH.cc b/HPWH.cc new file mode 100644 index 00000000..1b706880 --- /dev/null +++ b/HPWH.cc @@ -0,0 +1,5449 @@ +/* +Copyright (c) 2014-2016 Ecotope Inc. +All rights reserved. + + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + + + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + + + +* Neither the name of the copyright holders nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission from the copyright holders. + + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "HPWH.hh" +#include +#include +#include +#include +#include + +using std::endl; +using std::cout; +using std::string; + +const float HPWH::DENSITYWATER_kgperL = 0.995f; +const float HPWH::KWATER_WpermC = 0.62f; +const float HPWH::CPWATER_kJperkgC = 4.180f; +const float HPWH::HEATDIST_MINVALUE = 0.0001f; +const float HPWH::UNINITIALIZED_LOCATIONTEMP = -500.f; + +//ugh, this should be in the header +const std::string HPWH::version_maint = HPWHVRSN_META; + +#define SETPOINT_FIX // #define to include fixes for + // setpoint-below-water-temp issues + // 1-22-2017 + +//the HPWH functions +//the publics +HPWH::HPWH() : +simHasFailed(true), isHeating(false), setpointFixed(false), hpwhVerbosity(VRB_reluctant), // hpwhVerbosity(VRB_silent), +messageCallback(NULL), messageCallbackContextPtr(NULL), numHeatSources(0), +setOfSources(NULL), tankTemps_C(NULL), nextTankTemps_C(NULL), doTempDepression(false), locationTemperature_C(UNINITIALIZED_LOCATIONTEMP), +doInversionMixing(true), doConduction(true), inletHeight(0), inlet2Height(0) +{ } + +HPWH::HPWH(const HPWH &hpwh){ + simHasFailed = hpwh.simHasFailed; + + hpwhVerbosity = hpwh.hpwhVerbosity; + + //these should actually be the same pointers + messageCallback = hpwh.messageCallback; + messageCallbackContextPtr = hpwh.messageCallbackContextPtr; + + isHeating = hpwh.isHeating; + + numHeatSources = hpwh.numHeatSources; + setOfSources = new HeatSource[numHeatSources]; + for (int i = 0; i < numHeatSources; i++) { + setOfSources[i] = hpwh.setOfSources[i]; + setOfSources[i].hpwh = this; + } + + + + tankVolume_L = hpwh.tankVolume_L; + tankUA_kJperHrC = hpwh.tankUA_kJperHrC; + + setpoint_C = hpwh.setpoint_C; + numNodes = hpwh.numNodes; + nodeDensity = hpwh.nodeDensity; + tankTemps_C = new double[numNodes]; + nextTankTemps_C = new double[numNodes]; + for (int i = 0; i < numNodes; i++) { + tankTemps_C[i] = hpwh.tankTemps_C[i]; + nextTankTemps_C[i] = hpwh.nextTankTemps_C[i]; + } + inletHeight = hpwh.inletHeight; + inlet2Height = hpwh.inlet2Height; + + outletTemp_C = hpwh.outletTemp_C; + energyRemovedFromEnvironment_kWh = hpwh.energyRemovedFromEnvironment_kWh; + standbyLosses_kWh = hpwh.standbyLosses_kWh; + + tankMixesOnDraw = hpwh.tankMixesOnDraw; + doTempDepression = hpwh.doTempDepression; + + doInversionMixing = hpwh.doInversionMixing; + doConduction = hpwh.doConduction; + + locationTemperature_C = hpwh.locationTemperature_C; + +} + +HPWH & HPWH::operator=(const HPWH &hpwh){ + if (this == &hpwh) { + return *this; + } + + + simHasFailed = hpwh.simHasFailed; + + hpwhVerbosity = hpwh.hpwhVerbosity; + + //these should actually be the same pointers + messageCallback = hpwh.messageCallback; + messageCallbackContextPtr = hpwh.messageCallbackContextPtr; + + isHeating = hpwh.isHeating; + + numHeatSources = hpwh.numHeatSources; + + delete[] setOfSources; + setOfSources = new HeatSource[numHeatSources]; + for (int i = 0; i < numHeatSources; i++) { + setOfSources[i] = hpwh.setOfSources[i]; + setOfSources[i].hpwh = this; + //HeatSource assignment will fail (causing the simulation to fail) if a + //HeatSource has backups/companions. + //This could be dealt with in this function (tricky), but the HeatSource + //assignment can't know where its backup/companion pointer goes so it either + //fails or silently does something that's not at all useful. + //I prefer it to fail. -NDK 1/2016 + } + + + tankVolume_L = hpwh.tankVolume_L; + tankUA_kJperHrC = hpwh.tankUA_kJperHrC; + + setpoint_C = hpwh.setpoint_C; + numNodes = hpwh.numNodes; + nodeDensity = hpwh.nodeDensity; + + delete[] tankTemps_C; + delete[] nextTankTemps_C; + tankTemps_C = new double[numNodes]; + nextTankTemps_C = new double[numNodes]; + for (int i = 0; i < numNodes; i++) { + tankTemps_C[i] = hpwh.tankTemps_C[i]; + nextTankTemps_C[i] = hpwh.nextTankTemps_C[i]; + } + inletHeight = hpwh.inletHeight; + inlet2Height = hpwh.inlet2Height; + + outletTemp_C = hpwh.outletTemp_C; + energyRemovedFromEnvironment_kWh = hpwh.energyRemovedFromEnvironment_kWh; + standbyLosses_kWh = hpwh.standbyLosses_kWh; + + tankMixesOnDraw = hpwh.tankMixesOnDraw; + doTempDepression = hpwh.doTempDepression; + + doInversionMixing = hpwh.doInversionMixing; + doConduction = hpwh.doConduction; + + locationTemperature_C = hpwh.locationTemperature_C; + + return *this; +} + +HPWH::~HPWH() { + delete[] tankTemps_C; + delete[] nextTankTemps_C; + delete[] setOfSources; +} + +string HPWH::getVersion(){ + std::stringstream version; + + version << version_major << '.' << version_minor << '.' << version_patch << version_maint; + + return version.str(); +} + + +int HPWH::runOneStep(double inletT_C, double drawVolume_L, + double tankAmbientT_C, double heatSourceAmbientT_C, + DRMODES DRstatus, double minutesPerStep, + double inletVol2_L, double inletT2_C, + std::vector* nodeExtraHeat_W) { + //returns 0 on successful completion, HPWH_ABORT on failure + + //check for errors + if (doTempDepression == true && minutesPerStep != 1) { + msg("minutesPerStep must equal one for temperature depression to work. \n"); + simHasFailed = true; + return HPWH_ABORT; + } + + + if (hpwhVerbosity >= VRB_typical) { + msg("Beginning runOneStep. \nTank Temps: "); + printTankTemps(); + msg("Step Inputs: InletT_C: %.2lf, drawVolume_L: %.2lf, tankAmbientT_C: %.2lf, heatSourceAmbientT_C: %.2lf, DRstatus: %d, minutesPerStep: %.2lf \n", + inletT_C, drawVolume_L, tankAmbientT_C, heatSourceAmbientT_C, DRstatus, minutesPerStep); + } + //is the failure flag is set, don't run + if (simHasFailed) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("simHasFailed is set, aborting. \n"); + } + return HPWH_ABORT; + } + + + + //reset the output variables + outletTemp_C = 0; + energyRemovedFromEnvironment_kWh = 0; + standbyLosses_kWh = 0; + + for (int i = 0; i < numHeatSources; i++) { + setOfSources[i].runtime_min = 0; + setOfSources[i].energyInput_kWh = 0; + setOfSources[i].energyOutput_kWh = 0; + } + + // if you are doing temp. depression, set tank and heatSource ambient temps + // to the tracked locationTemperature + double temperatureGoal = tankAmbientT_C; + if (doTempDepression) { + if (locationTemperature_C == UNINITIALIZED_LOCATIONTEMP) { + locationTemperature_C = tankAmbientT_C; + } + tankAmbientT_C = locationTemperature_C; + heatSourceAmbientT_C = locationTemperature_C; + } + + + //process draws and standby losses + updateTankTemps(drawVolume_L, inletT_C, tankAmbientT_C, minutesPerStep, inletVol2_L, inletT2_C); + + + //do HeatSource choice + for (int i = 0; i < numHeatSources; i++) { + if (hpwhVerbosity >= VRB_emetic) { + msg("Heat source choice:\theatsource %d can choose from %lu turn on logics and %lu shut off logics\n", i, setOfSources[i].turnOnLogicSet.size(), setOfSources[i].shutOffLogicSet.size()); + } + if (isHeating == true) { + //check if anything that is on needs to turn off (generally for lowT cutoffs) + //things that just turn on later this step are checked for this in shouldHeat + if (setOfSources[i].isEngaged() && setOfSources[i].shutsOff()) { + setOfSources[i].disengageHeatSource(); + //check if the backup heat source would have to shut off too + if (setOfSources[i].backupHeatSource != NULL && setOfSources[i].backupHeatSource->shutsOff() != true) { + //and if not, go ahead and turn it on + setOfSources[i].backupHeatSource->engageHeatSource(); + } + } + + //if there's a priority HeatSource (e.g. upper resistor) and it needs to + //come on, then turn everything off and start it up + if (setOfSources[i].isVIP) { + if (hpwhVerbosity >= VRB_emetic) { + msg("\tVIP check"); + } + if (setOfSources[i].shouldHeat()) { + turnAllHeatSourcesOff(); + setOfSources[i].engageHeatSource(); + //stop looking if the VIP needs to run + break; + } + } + } + //if nothing is currently on, then check if something should come on + else /* (isHeating == false) */ { + if (setOfSources[i].shouldHeat()) { + setOfSources[i].engageHeatSource(); + //engaging a heat source sets isHeating to true, so this will only trigger once + } + } + + } //end loop over heat sources + + if (hpwhVerbosity >= VRB_emetic){ + msg("after heat source choosing: "); + for (int i = 0; i < numHeatSources; i++) { + msg("heat source %d: %d \t", i, setOfSources[i].isEngaged()); + } + msg("\n"); + } + + + //change the things according to DR schedule + if (DRstatus == DR_BLOCK) { + //force off + turnAllHeatSourcesOff(); + isHeating = false; + } + else if (DRstatus == DR_ALLOW) { + //do nothing + } + else if (DRstatus == DR_ENGAGE) { + //if nothing else is on, force the first heat source on + //this may or may not be desired behavior, pending more research (and funding) + if (areAllHeatSourcesOff() == true) { + if (compressorIndex > -1) { + setOfSources[compressorIndex].engageHeatSource(); + } else if (lowestElementIndex > -1) { + setOfSources[lowestElementIndex].engageHeatSource(); + } + } + } + + //do heating logic + double minutesToRun = minutesPerStep; + + for (int i = 0; i < numHeatSources; i++) { + // check/apply lock-outs + if (hpwhVerbosity >= VRB_emetic) { + msg("Checking lock-out logic for heat source %d:\n", i); + } + if (setOfSources[i].shouldLockOut(heatSourceAmbientT_C)) { + setOfSources[i].lockOutHeatSource(); + } + if (setOfSources[i].shouldUnlock(heatSourceAmbientT_C)) { + setOfSources[i].unlockHeatSource(); + } + + //going through in order, check if the heat source is on + if (setOfSources[i].isEngaged()) { + + HeatSource* heatSourcePtr; + if (setOfSources[i].isLockedOut() && setOfSources[i].backupHeatSource != NULL) { + heatSourcePtr = setOfSources[i].backupHeatSource; + } else { + heatSourcePtr = &setOfSources[i]; + } + + //and add heat if it is + heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesPerStep); + //if it finished early + if (heatSourcePtr->runtime_min < minutesToRun) { + //debugging message handling + if (hpwhVerbosity >= VRB_emetic){ + msg("done heating! runtime_min minutesToRun %.2lf %.2lf\n", heatSourcePtr->runtime_min, minutesToRun); + } + + //subtract time it ran and turn it off + minutesToRun -= heatSourcePtr->runtime_min; + setOfSources[i].disengageHeatSource(); + //and if there's a heat source that follows this heat source (regardless of lockout) that's able to come on, + if (setOfSources[i].followedByHeatSource != NULL && setOfSources[i].followedByHeatSource->shutsOff() == false) { + //turn it on + setOfSources[i].followedByHeatSource->engageHeatSource(); + } + } + } + } + + if (areAllHeatSourcesOff() == true) { + isHeating = false; + } + + + //If theres extra user defined heat to add -> Add extra heat! + if (nodeExtraHeat_W != NULL) { + msg("runOneStep entering addExtraHeat if\n"); + addExtraHeat(nodeExtraHeat_W, tankAmbientT_C, minutesToRun); + } + + + + //track the depressed local temperature + if (doTempDepression) { + bool compressorRan = false; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].isEngaged() && !setOfSources[i].isLockedOut() && setOfSources[i].depressesTemperature) { + compressorRan = true; + } + } + + if (compressorRan){ + temperatureGoal -= maxDepression_C; //hardcoded 4.5 degree total drop - from experimental data. Changed to an input + } + else{ + //otherwise, do nothing, we're going back to ambient + } + + // shrink the gap by the same percentage every minute - that gives us + // exponential behavior the percentage was determined by a fit to + // experimental data - 9.4 minute half life and 4.5 degree total drop + //minus-equals is important, and fits with the order of locationTemperature + //and temperatureGoal, so as to not use fabs() and conditional tests + locationTemperature_C -= (locationTemperature_C - temperatureGoal)*(1 - 0.9289); + } + + //settle outputs + + //outletTemp_C and standbyLosses_kWh are taken care of in updateTankTemps + + //sum energyRemovedFromEnvironment_kWh for each heat source; + for (int i = 0; i < numHeatSources; i++) { + energyRemovedFromEnvironment_kWh += (setOfSources[i].energyOutput_kWh - setOfSources[i].energyInput_kWh); + } + + //cursory check for inverted temperature profile + if (tankTemps_C[numNodes-1] < tankTemps_C[0]) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("The top of the tank is cooler than the bottom. \n"); + } + } + + if (simHasFailed) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("The simulation has encountered an error. \n"); + } + return HPWH_ABORT; + } + + + if (hpwhVerbosity >= VRB_typical) { + msg("Ending runOneStep. \n\n\n\n"); + } + return 0; //successful completion of the step returns 0 +} //end runOneStep + + +int HPWH::runNSteps(int N, double *inletT_C, double *drawVolume_L, + double *tankAmbientT_C, double *heatSourceAmbientT_C, + DRMODES *DRstatus, double minutesPerStep) { + //returns 0 on successful completion, HPWH_ABORT on failure + + //these are all the accumulating variables we'll need + double energyRemovedFromEnvironment_kWh_SUM = 0; + double standbyLosses_kWh_SUM = 0; + double outletTemp_C_AVG = 0; + double totalDrawVolume_L = 0; + std::vector heatSources_runTimes_SUM(numHeatSources); + std::vector heatSources_energyInputs_SUM(numHeatSources); + std::vector heatSources_energyOutputs_SUM(numHeatSources); + + if (hpwhVerbosity >= VRB_typical) { + msg("Begin runNSteps. \n"); + } + //run the sim one step at a time, accumulating the outputs as you go + for (int i = 0; i < N; i++) { + runOneStep(inletT_C[i], drawVolume_L[i], tankAmbientT_C[i], heatSourceAmbientT_C[i], + DRstatus[i], minutesPerStep); + + if (simHasFailed) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("RunNSteps has encountered an error on step %d of N and has ceased running. \n", i + 1); + } + return HPWH_ABORT; + } + + energyRemovedFromEnvironment_kWh_SUM += energyRemovedFromEnvironment_kWh; + standbyLosses_kWh_SUM += standbyLosses_kWh; + + outletTemp_C_AVG += outletTemp_C * drawVolume_L[i]; + totalDrawVolume_L += drawVolume_L[i]; + + for (int j = 0; j < numHeatSources; j++) { + heatSources_runTimes_SUM[j] += getNthHeatSourceRunTime(j); + heatSources_energyInputs_SUM[j] += getNthHeatSourceEnergyInput(j); + heatSources_energyOutputs_SUM[j] += getNthHeatSourceEnergyOutput(j); + } + + //print minutely output + if (hpwhVerbosity == VRB_minuteOut) { + msg("%f,%f,%f,", tankAmbientT_C[i], drawVolume_L[i], inletT_C[i]); + for (int j = 0; j < numHeatSources; j++) { + msg("%f,%f,", getNthHeatSourceEnergyInput(j), getNthHeatSourceEnergyOutput(j)); + } + msg("%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n", + tankTemps_C[0 * numNodes / 12], tankTemps_C[1 * numNodes / 12], + tankTemps_C[2 * numNodes / 12], tankTemps_C[3 * numNodes / 12], + tankTemps_C[4 * numNodes / 12], tankTemps_C[5 * numNodes / 12], + tankTemps_C[6 * numNodes / 12], tankTemps_C[7 * numNodes / 12], + tankTemps_C[8 * numNodes / 12], tankTemps_C[9 * numNodes / 12], + tankTemps_C[10 * numNodes / 12], tankTemps_C[11 * numNodes / 12], + getNthSimTcouple(1, 6), getNthSimTcouple(2, 6), getNthSimTcouple(3, 6), + getNthSimTcouple(4, 6), getNthSimTcouple(5, 6), getNthSimTcouple(6, 6)); + } + + } + //finish weighted avg. of outlet temp by dividing by the total drawn volume + outletTemp_C_AVG /= totalDrawVolume_L; + + //now, reassign all of the accumulated values to their original spots + energyRemovedFromEnvironment_kWh = energyRemovedFromEnvironment_kWh_SUM; + standbyLosses_kWh = standbyLosses_kWh_SUM; + outletTemp_C = outletTemp_C_AVG; + + for (int i = 0; i < numHeatSources; i++) { + setOfSources[i].runtime_min = heatSources_runTimes_SUM[i]; + setOfSources[i].energyInput_kWh = heatSources_energyInputs_SUM[i]; + setOfSources[i].energyOutput_kWh = heatSources_energyOutputs_SUM[i]; + } + + if (hpwhVerbosity >= VRB_typical) { + msg("Ending runNSteps. \n\n\n\n"); + } + return 0; +} + + + +void HPWH::setVerbosity(VERBOSITY hpwhVrb){ + hpwhVerbosity = hpwhVrb; +} +void HPWH::setMessageCallback(void(*callbackFunc)(const string message, void* contextPtr), void* contextPtr){ + messageCallback = callbackFunc; + messageCallbackContextPtr = contextPtr; +} +void HPWH::sayMessage(const string message) const{ + if (messageCallback != NULL) { + (*messageCallback)(message, messageCallbackContextPtr); + } + else { + std::cout << message; + } +} +void HPWH::msg(const char* fmt, ...) const { + va_list ap; va_start(ap, fmt); + msgV(fmt, ap); +} +void HPWH::msgV(const char* fmt, va_list ap /*=NULL*/) const { + char outputString[MAXOUTSTRING]; + + const char* p; + if (ap) { +#if defined( _MSC_VER) + vsprintf_s< MAXOUTSTRING>(outputString, fmt, ap); +#else + vsnprintf(outputString, MAXOUTSTRING, fmt, ap); +#endif + p = outputString; + } + else { + p = fmt; + } + sayMessage(p); +} // HPWH::msgV + + +void HPWH::printHeatSourceInfo(){ + std::stringstream ss; + double runtime = 0, outputVar = 0; + + ss << std::left; + ss << std::fixed; + ss << std::setprecision(2); + for (int i = 0; i < getNumHeatSources(); i++) { + ss << "heat source " << i << ": " << isNthHeatSourceRunning(i) << "\t\t"; + } + ss << endl; + + for (int i = 0; i < getNumHeatSources(); i++) { + ss << "input energy kwh: " << std::setw(7) << getNthHeatSourceEnergyInput(i) << "\t"; + } + ss << endl; + + for (int i = 0; i < getNumHeatSources(); i++) { + runtime = getNthHeatSourceRunTime(i); + if (runtime != 0) { + outputVar = getNthHeatSourceEnergyInput(i) / (runtime / 60.0); + } + else { + outputVar = 0; + } + ss << "input power kw: " << std::setw(7) << outputVar << "\t\t"; + } + ss << endl; + + for (int i = 0; i < getNumHeatSources(); i++) { + ss << "output energy kwh: " << std::setw(7) << getNthHeatSourceEnergyOutput(i, UNITS_KWH) << "\t"; + } + ss << endl; + + for (int i = 0; i < getNumHeatSources(); i++) { + runtime = getNthHeatSourceRunTime(i); + if (runtime != 0) { + outputVar = getNthHeatSourceEnergyOutput(i, UNITS_KWH) / (runtime / 60.0); + } + else { + outputVar = 0; + } + ss << "output power kw: " << std::setw(7) << outputVar << "\t"; + } + ss << endl; + + for (int i = 0; i < getNumHeatSources(); i++) { + ss << "run time min: " << std::setw(7) << getNthHeatSourceRunTime(i) << "\t\t"; + } + ss << endl << endl << endl; + + + msg(ss.str().c_str()); +} + + +void HPWH::printTankTemps() { + std::stringstream ss; + + ss << std::left; + + for (int i = 0; i < getNumNodes(); i++) { + ss << std::setw(9) << getTankNodeTemp(i) << " "; + } + ss << endl; + + msg(ss.str().c_str()); +} + + +// public members to write to CSV file +int HPWH::WriteCSVHeading(FILE* outFILE, const char* preamble, int nTCouples, int options) const { + + bool doIP = (options & CSVOPT_IPUNITS) != 0; + + fprintf(outFILE, "%s", preamble); + + const char* pfx = ""; + for (int iHS = 0; iHS < getNumHeatSources(); iHS++) { + fprintf(outFILE, "%sh_src%dIn (Wh),h_src%dOut (Wh)", pfx, iHS + 1, iHS + 1); + pfx = ","; + } + + for (int iTC = 0; iTC < nTCouples; iTC++) { + fprintf(outFILE, ",tcouple%d (%s)", iTC + 1, doIP ? "F":"C"); + } + + fprintf(outFILE, "\n"); + + return 0; +} + +int HPWH::WriteCSVRow(FILE* outFILE, const char* preamble, int nTCouples, int options) const { + + bool doIP = (options & CSVOPT_IPUNITS) != 0; + + fprintf(outFILE, "%s", preamble); + + const char* pfx = ""; + for (int iHS = 0; iHS < getNumHeatSources(); iHS++) { + fprintf(outFILE, "%s%0.2f,%0.2f", pfx, getNthHeatSourceEnergyInput(iHS, UNITS_KWH)*1000., + getNthHeatSourceEnergyOutput(iHS, UNITS_KWH)*1000.); + pfx = ","; + } + + for (int iTC = 0; iTC < nTCouples; iTC++) { + fprintf(outFILE, ",%0.2f", getNthSimTcouple(iTC + 1, nTCouples, doIP ? UNITS_F : UNITS_C)); + } + + fprintf(outFILE, "\n"); + + return 0; +} + + +bool HPWH::isSetpointFixed(){ + return setpointFixed; +} + +int HPWH::setSetpoint(double newSetpoint, UNITS units /*=UNITS_C*/) { + if (setpointFixed == true) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Unwilling to set setpoint for your currently selected model. \n"); + } + return HPWH_ABORT; + } + else{ + if (units == UNITS_C) { + setpoint_C = newSetpoint; + } + else if (units == UNITS_F) { + setpoint_C = (F_TO_C(newSetpoint)); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for setSetpoint. \n"); + } + return HPWH_ABORT; + } + } + return 0; +} +double HPWH::getSetpoint(){ + return setpoint_C; +} + + +int HPWH::resetTankToSetpoint(){ + for (int i = 0; i < numNodes; i++) { + tankTemps_C[i] = setpoint_C; + } + return 0; +} + + +int HPWH::setAirFlowFreedom(double fanFraction) { + if (fanFraction < 0 || fanFraction > 1) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to set the fan fraction outside of bounds. \n"); + } + simHasFailed = true; + return HPWH_ABORT; + } + else { + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { + setOfSources[i].airflowFreedom = fanFraction; + } + } + } + return 0; +} + +int HPWH::setDoTempDepression(bool doTempDepress) { + this->doTempDepression = doTempDepress; + return 0; +} + +int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/){ + //Uses the UA before the function is called and adjusts the A part of the UA to match the input volume given getTankSurfaceArea(). + double HPWH_size_L; + double oldA = getTankSurfaceArea(UNITS_FT2); + + if (units == UNITS_L) { + HPWH_size_L = HPWH_size; + } + else if (units == UNITS_GAL) { + HPWH_size_L = GAL_TO_L(HPWH_size); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for setTankSize_adjustUA. \n"); + } + return HPWH_ABORT; + } + setTankSize(HPWH_size_L, UNITS_L); + setUA(tankUA_kJperHrC / oldA * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); + return 0; +} + +double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/){ + // returns tank surface area, old defualt was in ft2 + // Based off 88 insulated storage tanks currently available on the market from Sanden, AOSmith, HTP, Rheem, and Niles. + // Using the same form of equation given in RACM 2016 App B, equation 41. + double value = 1.492 * pow(L_TO_GAL(tankVolume_L), 0.6666) + 5.068*pow(L_TO_GAL(tankVolume_L), 0.3333) - 10.913; + + if (units == UNITS_FT2) { + return value; + } + else if (units == UNITS_M2) { + return FT2_TO_M2(value); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getTankSurfaceArea. \n"); + } + return HPWH_ABORT; + } +} + +double HPWH::getTankRadius(UNITS units /*=UNITS_FT*/){ + // returns tank radius, ft for use in calculation of heat loss in the bottom and top of the tank. + // Based off 88 insulated storage tanks currently available on the market from Sanden, AOSmith, HTP, Rheem, and Niles. + double value = 0.2244 * pow(L_TO_GAL(tankVolume_L), 0.333) + 0.0749; + + if (units == UNITS_FT) { + return value; + } + else if (units == UNITS_M) { + return FT2_TO_M2(value); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getTankRadius. \n"); + } + return HPWH_ABORT; + } +} + +int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/) { + if (HPWH_size <= 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to set the tank volume outside of bounds. \n"); + } + simHasFailed = true; + return HPWH_ABORT; + } + else { + if (units == UNITS_L) { + this->tankVolume_L = HPWH_size; + } + else if (units == UNITS_GAL) { + this->tankVolume_L = (GAL_TO_L(HPWH_size)); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for setTankSize. \n"); + } + return HPWH_ABORT; + } + } + return 0; +} +int HPWH::setDoInversionMixing(bool doInvMix) { + this->doInversionMixing = doInvMix; + return 0; +} +int HPWH::setDoConduction(bool doCondu) { + this->doConduction = doCondu; + return 0; +} + +int HPWH::setUA(double UA, UNITS units /*=UNITS_kJperHrC*/) { + if (units == UNITS_kJperHrC) { + tankUA_kJperHrC = UA; + } + else if (units == UNITS_BTUperHrF) { + tankUA_kJperHrC = UAf_TO_UAc(UA); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for setUA. \n"); + } + return HPWH_ABORT; + } + return 0; +} + +int HPWH::getUA(double& UA, UNITS units /*=UNITS_kJperHrC*/) const { + UA = tankUA_kJperHrC; + if (units == UNITS_kJperHrC) { + // UA is already in correct units + } + else if (units == UNITS_BTUperHrF) { + UA = UA / UAf_TO_UAc( 1.); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getUA. \n"); + } + UA = -1.; + return HPWH_ABORT; + } + return 0; +} + +int HPWH::setInletByFraction(double fractionalHeight){ + return setNodeNumFromFractionalHeight(fractionalHeight, inletHeight); +} +int HPWH::setInlet2ByFraction(double fractionalHeight){ + return setNodeNumFromFractionalHeight(fractionalHeight, inlet2Height); +} + +int HPWH::setNodeNumFromFractionalHeight(double fractionalHeight, int &inletNum){ + if (fractionalHeight > 1. || fractionalHeight < 0.){ + if (hpwhVerbosity >= VRB_reluctant) { + msg("Out of bounds fraction for setInletByFraction \n"); + } + return HPWH_ABORT; + } + + int node = (int)std::floor(numNodes*fractionalHeight); + inletNum = (node == numNodes) ? numNodes - 1 : node; + + return 0; +} +int HPWH::getInletHeight(int whichInlet){ + if (whichInlet == 1) { + return inletHeight; + } + else if (whichInlet == 2) { + return inlet2Height; + } + else + { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Invalid inlet chosen in getInletHeight \n"); + } + return HPWH_ABORT; + } +} + +int HPWH::setMaxTempDepression(double maxDepression, UNITS units /*=UNITS_C*/) { + if(units == UNITS_C) { + this->maxDepression_C = maxDepression; + } + else if(units == UNITS_F) { + this->maxDepression_C = F_TO_C(maxDepression); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for max Temp Depression. \n"); + } + return HPWH_ABORT; + } + return 0; +} + +HPWH::HeatingLogic HPWH::topThird(double d) const { + std::vector nodeWeights; + for (auto i : {9,10,11,12}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("top third", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::topThird_absolute(double d) const { + std::vector nodeWeights; + for (auto i : {9,10,11,12}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("top third absolute", nodeWeights, d, true); +} + + +HPWH::HeatingLogic HPWH::bottomThird(double d) const { + std::vector nodeWeights; + for (auto i : {1,2,3,4}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("bottom third", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::bottomSixth(double d) const { + std::vector nodeWeights; + for (auto i : {1,2}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("bottom sixth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::secondSixth(double d) const { + std::vector nodeWeights; + for (auto i : {3,4}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("second sixth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::thirdSixth(double d) const { + std::vector nodeWeights; + for (auto i : {5,6}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("third sixth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::fourthSixth(double d) const { + std::vector nodeWeights; + for (auto i : {7,8}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("fourth sixth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::fifthSixth(double d) const { + std::vector nodeWeights; + for (auto i : {9,10}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("fifth sixth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::topSixth(double d) const { + std::vector nodeWeights; + for (auto i : {11,12}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("top sixth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::bottomHalf(double d) const { + std::vector nodeWeights; + for (auto i : {1,2,3,4,5,6}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("bottom half", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::bottomTwelth(double d) const { + std::vector nodeWeights; + for (auto i : {7,8,9,10,11,12}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("bottom twelth", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::standby(double d) const { + std::vector nodeWeights; + nodeWeights.emplace_back(13); // uses very top computation node + return HPWH::HeatingLogic("standby", nodeWeights, d); +} + +HPWH::HeatingLogic HPWH::topNodeMaxTemp(double d) const { + std::vector nodeWeights; + nodeWeights.emplace_back(13); // uses very top computation node + return HPWH::HeatingLogic("top node", nodeWeights, d, true, std::greater()); +} + +HPWH::HeatingLogic HPWH::bottomNodeMaxTemp(double d) const { + std::vector nodeWeights; + nodeWeights.emplace_back(0); // uses very bottom computation node + return HPWH::HeatingLogic("bottom node", nodeWeights, d, true, std::greater()); +} + +HPWH::HeatingLogic HPWH::bottomTwelthMaxTemp(double d) const { + std::vector nodeWeights; + nodeWeights.emplace_back(1); + return HPWH::HeatingLogic("bottom twelth", nodeWeights, d, true, std::greater()); +} + +HPWH::HeatingLogic HPWH::largeDraw(double d) const { + std::vector nodeWeights; + for (auto i : {1,2,3,4}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("large draw", nodeWeights, d, true); +} + +HPWH::HeatingLogic HPWH::largerDraw(double d) const { + std::vector nodeWeights; + for (auto i : {1,2,3,4,5,6}) { + nodeWeights.emplace_back(i); + } + return HPWH::HeatingLogic("larger draw", nodeWeights, d, true); +} + +int HPWH::getNumNodes() const { + return numNodes; +} + + +double HPWH::getTankNodeTemp(int nodeNum, UNITS units /*=UNITS_C*/) const { + if (nodeNum >= numNodes || nodeNum < 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to access the temperature of a tank node that does not exist. \n"); + } + return double(HPWH_ABORT); + } + else{ + double result = tankTemps_C[nodeNum]; + if (result == double(HPWH_ABORT)) { + return result; + } + if (units == UNITS_C) { + return result; + } + else if (units == UNITS_F) { + return C_TO_F(result); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getTankNodeTemp. \n"); + } + return double(HPWH_ABORT); + } + } +} + + +double HPWH::getNthSimTcouple(int iTCouple, int nTCouple, UNITS units /*=UNITS_C*/) const { + if (iTCouple > nTCouple || iTCouple < 1) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to access a simulated thermocouple that does not exist. \n"); + } + return double(HPWH_ABORT); + } + else if (nTCouple > numNodes) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have more simulated thermocouples than nodes. \n"); + } + return double(HPWH_ABORT); + } + else { + double weight = (double)numNodes / (double)nTCouple; + double start_ind = (iTCouple - 1) * weight; + int ind = (int)std::ceil(start_ind); + + double averageTemp_C = 0.0; + + // Check any intial fraction of nodes + averageTemp_C += getTankNodeTemp((int)std::floor(start_ind), UNITS_C) * ((double)ind - start_ind); + weight -= ((double)ind - start_ind); + + // Check the full nodes + while (weight >= 1.0) { + averageTemp_C += getTankNodeTemp(ind, UNITS_C); + weight -= 1.0; + ind += 1; + } + + // Check any leftover + if (weight > 0.) { + averageTemp_C += getTankNodeTemp(ind, UNITS_C) * weight; + } + // Divide by the original weight to get the true average + averageTemp_C /= ((double)numNodes / (double)nTCouple); + + if (units == UNITS_C) { + return averageTemp_C; + } + else if (units == UNITS_F) { + return C_TO_F(averageTemp_C); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getNthSimTcouple. \n"); + } + return double(HPWH_ABORT); + } + } +} + + +int HPWH::getNumHeatSources() const { + return numHeatSources; +} + + +double HPWH::getNthHeatSourceEnergyInput(int N, UNITS units /*=UNITS_KWH*/) const { + //energy used by the heat source is positive - this should always be positive + if (N >= numHeatSources || N < 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to access the energy input of a heat source that does not exist. \n"); + } + return double(HPWH_ABORT); + } + + if (units == UNITS_KWH) { + return setOfSources[N].energyInput_kWh; + } + else if (units == UNITS_BTU) { + return KWH_TO_BTU(setOfSources[N].energyInput_kWh); + } + else if (units == UNITS_KJ) { + return KWH_TO_KJ(setOfSources[N].energyInput_kWh); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getNthHeatSourceEnergyInput. \n"); + } + return double(HPWH_ABORT); + } +} +double HPWH::getNthHeatSourceEnergyOutput(int N, UNITS units /*=UNITS_KWH*/) const { + //returns energy from the heat source into the water - this should always be positive + if (N >= numHeatSources || N < 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to access the energy output of a heat source that does not exist. \n"); + } + return double(HPWH_ABORT); + } + + if (units == UNITS_KWH) { + return setOfSources[N].energyOutput_kWh; + } + else if (units == UNITS_BTU) { + return KWH_TO_BTU(setOfSources[N].energyOutput_kWh); + } + else if (units == UNITS_KJ) { + return KWH_TO_KJ(setOfSources[N].energyOutput_kWh); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getNthHeatSourceEnergyInput. \n"); + } + return double(HPWH_ABORT); + } +} + + +double HPWH::getNthHeatSourceRunTime(int N) const { + if (N >= numHeatSources || N < 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to access the run time of a heat source that does not exist. \n"); + } + return double(HPWH_ABORT); + } + return setOfSources[N].runtime_min; +} + + +int HPWH::isNthHeatSourceRunning(int N) const{ + if (N >= numHeatSources || N < 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have attempted to access the status of a heat source that does not exist. \n"); + } + return HPWH_ABORT; + } + if (setOfSources[N].isEngaged()){ + return 1; + } + else{ + return 0; + } +} + + +HPWH::HEATSOURCE_TYPE HPWH::getNthHeatSourceType(int N) const{ + return setOfSources[N].typeOfHeatSource; +} + + +double HPWH::getTankSize(UNITS units) const { + if (units == UNITS_L) { + return tankVolume_L; + } + else if (units == UNITS_GAL) { + return L_TO_GAL(tankVolume_L); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getTankSize. \n"); + } + return HPWH_ABORT; + } +} + + +double HPWH::getOutletTemp(UNITS units /*=UNITS_C*/) const { + + if (units == UNITS_C) { + return outletTemp_C; + } + else if (units == UNITS_F) { + return C_TO_F(outletTemp_C); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getOutletTemp. \n"); + } + return double(HPWH_ABORT); + } +} + +double HPWH::getEnergyRemovedFromEnvironment(UNITS units /*=UNITS_KWH*/) const { + //moving heat from the space to the water is the positive direction + if (units == UNITS_KWH) { + return energyRemovedFromEnvironment_kWh; + } + else if (units == UNITS_BTU) { + return KWH_TO_BTU(energyRemovedFromEnvironment_kWh); + } + else if (units == UNITS_KJ){ + return KWH_TO_KJ(energyRemovedFromEnvironment_kWh); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getEnergyRemovedFromEnvironment. \n"); + } + return double(HPWH_ABORT); + } +} + +double HPWH::getStandbyLosses(UNITS units /*=UNITS_KWH*/) const { + //moving heat from the water to the space is the positive direction + if (units == UNITS_KWH) { + return standbyLosses_kWh; + } + else if (units == UNITS_BTU) { + return KWH_TO_BTU(standbyLosses_kWh); + } + else if (units == UNITS_KJ){ + return KWH_TO_KJ(standbyLosses_kWh); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for getStandbyLosses. \n"); + } + return double(HPWH_ABORT); + } +} + +double HPWH::getTankHeatContent_kJ() const { + // returns tank heat content relative to 0 C using kJ + + //get average tank temperature + double avgTemp = 0.0; + for (int i = 0; i < numNodes; i++) { + avgTemp += tankTemps_C[i]; + } + avgTemp /= numNodes; + + double totalHeat = avgTemp * DENSITYWATER_kgperL * CPWATER_kJperkgC * tankVolume_L; + return totalHeat; +} + +double HPWH::getLocationTemp_C() const { + return locationTemperature_C; +} + + + + +//the privates +void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbientT_C, double minutesPerStep, + double inletVol2_L, double inletT2_C) { + //set up some useful variables for calculations + double volPerNode_LperNode = tankVolume_L / numNodes; + double drawFraction; + this->outletTemp_C = 0; + double nodeInletFraction, cumInletFraction, drawVolume_N, nodeInletTV; + + if (drawVolume_L > 0){ + + //calculate how many nodes to draw (wholeNodesToDraw), and the remainder (drawFraction) + if (inletVol2_L > drawVolume_L) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Volume in inlet 2 is greater than the draw volume. \n"); + } + simHasFailed = true; + return; + } + + // Check which inlet is higher; + int highInletH; + double highInletV, highInletT; + int lowInletH; + double lowInletT, lowInletV; + if (inletHeight > inlet2Height){ + highInletH = inletHeight; + highInletV = drawVolume_L - inletVol2_L; + highInletT = inletT_C; + lowInletH = inlet2Height; + lowInletT = inletT2_C; + lowInletV = inletVol2_L; + } + else { + highInletH = inlet2Height; + highInletV = inletVol2_L; + highInletT = inletT2_C; + lowInletH = inletHeight; + lowInletT = inletT_C; + lowInletV = drawVolume_L - inletVol2_L; + } + + //calculate how many nodes to draw (drawVolume_N) + drawVolume_N = drawVolume_L / volPerNode_LperNode; + if (drawVolume_N > numNodes) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Drawing more than the tank volume in one step is undefined behavior. Terminating simulation. \n"); + } + simHasFailed = true; + return; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + + while (drawVolume_N > 0) { + + // Draw one node at a time + drawFraction = drawVolume_N > 1. ? 1. : drawVolume_N; + + //add temperature for outletT average + outletTemp_C += drawFraction * tankTemps_C[numNodes - 1]; + + cumInletFraction = 0.; + for (int i = numNodes - 1; i >= lowInletH; i--) { + + // Reset inlet inputs at this node. + nodeInletFraction = 0.; + nodeInletTV = 0.; + + // Sum of all inlets Vi*Ti at this node + if (i == highInletH) { + nodeInletTV += highInletV * drawFraction / drawVolume_L * highInletT; + nodeInletFraction += highInletV * drawFraction / drawVolume_L; + } + if (i == lowInletH) { + nodeInletTV += lowInletV * drawFraction / drawVolume_L * lowInletT; + nodeInletFraction += lowInletV * drawFraction / drawVolume_L; + + break; // if this is the bottom inlet break out of the four loop and use the boundary condition equation. + } + + // Look at the volume and temperature fluxes into this node + tankTemps_C[i] = (1. - (drawFraction - cumInletFraction)) * tankTemps_C[i] + + nodeInletTV + + (drawFraction - (cumInletFraction + nodeInletFraction)) * tankTemps_C[i - 1]; + + cumInletFraction += nodeInletFraction; + + } + + // Boundary condition equation because it shouldn't take anything from tankTemps_C[i - 1] but it also might not exist. + tankTemps_C[lowInletH] = (1. - (drawFraction - cumInletFraction)) * tankTemps_C[lowInletH] + nodeInletTV; + + drawVolume_N -= drawFraction; + + if (doInversionMixing) { + mixTankInversions(); + } + } + + + //fill in average outlet T - it is a weighted averaged, with weights == nodes drawn + this->outletTemp_C /= (drawVolume_L / volPerNode_LperNode); + + ///////////////////////////////////////////////////////////////////////////////////////////////// + + //Account for mixing at the bottom of the tank + if (tankMixesOnDraw == true && drawVolume_L > 0) { + int mixedBelowNode = numNodes / 3; + double ave = 0; + + for (int i = 0; i < mixedBelowNode; i++) { + ave += tankTemps_C[i]; + } + ave /= mixedBelowNode; + + for (int i = 0; i < mixedBelowNode; i++) { + tankTemps_C[i] += ((ave - tankTemps_C[i]) / 3.0); + } + } + + } //end if(draw_volume_L > 0) + + + // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. + // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions + // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. + // height estimate from Rheem along with the volume is used to get the radius and node_height + const double rad = getTankRadius(UNITS_M); + const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); + const double node_height = height / numNodes; + + // The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_bt + UA_r is the total tank area. + const double UA_bt = tankUA_kJperHrC * rad / (2.0 * (height + rad)); + + // UA_r is the faction of the area of the cylinder that's not the top or bottom. + const double UA_r = height / (height + rad); + + if (doConduction) { + + // Get the "constant" tau for the stability condition and the conduction calculation + const double tau = KWATER_WpermC / (CPWATER_kJperkgC * 1000.0 * DENSITYWATER_kgperL * 1000.0 * (node_height * node_height)) * minutesPerStep * 60.0; + if (tau > 0.5) { + msg("The stability condition for conduction has failed, these results are going to be interesting!\n"); + } + + // Boundary condition for the finite difference. + const double bc = 2.0 * tau * UA_bt * node_height / KWATER_WpermC; + + // Boundary nodes for finite difference + nextTankTemps_C[0] = (1.0 - 2.0 * tau - bc) * tankTemps_C[0] + 2.0 * tau * tankTemps_C[1] + bc * tankAmbientT_C; + nextTankTemps_C[numNodes - 1] = (1.0 - 2.0 * tau - bc) * tankTemps_C[numNodes - 1] + 2.0 * tau * tankTemps_C[numNodes - 2] + bc * tankAmbientT_C; + + // Internal nodes for the finite difference + for (int i = 1; i < numNodes - 1; i++) { + nextTankTemps_C[i] = tankTemps_C[i] + tau * (tankTemps_C[i + 1] - 2.0 * tankTemps_C[i] + tankTemps_C[i - 1]); + } + + // nextTankTemps_C gets assigns to tankTemps_C at the bottom of the function after q_UA. + // UA loss from the sides are found at the bottom of the function. + double standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kJ += (tankUA_kJperHrC * UA_bt * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); + } + else { // Ignore tank conduction and calculate UA losses from top and bottom. UA loss from the sides are found at the bottom of the function + + for (int i = 0; i < numNodes; i++) { + nextTankTemps_C[i] = tankTemps_C[i]; + } + + //kJ's lost as standby in the current time step for the top node. + double standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); + + nextTankTemps_C[0] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); + + //kJ's lost as standby in the current time step for the bottom node. + standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); + + nextTankTemps_C[numNodes - 1] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); + // UA loss from the sides are found at the bottom of the function. + + } + + + //calculate standby losses from the sides of the tank + for (int i = 0; i < numNodes; i++) { + //faction of tank area on the sides + //kJ's lost as standby in the current time step for each node. + double standbyLosses_kJ = (tankUA_kJperHrC * UA_r / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); + + //The effect of standby loss on temperature in each node + nextTankTemps_C[i] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); + } + + + // Assign the new temporary tank temps to the real tank temps. + for (int i = 0; i < numNodes; i++) tankTemps_C[i] = nextTankTemps_C[i]; + + // check for inverted temperature profile + if (doInversionMixing) { + mixTankInversions(); + } + +} //end updateTankTemps + + +// Inversion mixing modeled after bigladder EnergyPlus code PK +void HPWH::mixTankInversions() { + bool hasInversion; + const double volumePerNode_L = tankVolume_L / numNodes; + //int numdos = 0; + + do { + hasInversion = false; + //Start from the top and check downwards + for (int i = numNodes-1; i >= 0; i--) { + if (tankTemps_C[i] < tankTemps_C[i - 1]) { + // Temperature inversion! + hasInversion = true; + + //Mix this inversion mixing temperature by averaging all of the inverted nodes together together. + double Tmixed = 0.0; + double massMixed = 0.0; + int m; + for (m = i; m >= 0; m--) { + Tmixed += tankTemps_C[m] * (volumePerNode_L * DENSITYWATER_kgperL); + massMixed += (volumePerNode_L * DENSITYWATER_kgperL); + if ((m == 0) || (Tmixed / massMixed > tankTemps_C[m - 1])) { + break; + } + } + Tmixed /= massMixed; + + // Assign the tank temps from i to k + for (int k = i; k >= m; k--) tankTemps_C[k] = Tmixed; + } + + } + + } while ( hasInversion ); +} + + +void HPWH::addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesToRun){ + if ((*nodeExtraHeat_W).size() != 12){ + if (hpwhVerbosity >= VRB_reluctant) { + msg("nodeExtraHeat_KWH (%i) does not have 12 nodes \n", (*nodeExtraHeat_W).size(), numNodes); + } + simHasFailed = true; + } + + + msg("in ExtraHeat check numheatsources \n"); + for (int i = 0; i < numHeatSources; i++){ + if (setOfSources[i].typeOfHeatSource = TYPE_extra) { + + // Set up the extra heat source + setOfSources[i].setupExtraHeat(nodeExtraHeat_W); + + msg("setupExtraHeat done \n"); + + // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() + calcDerivedHeatingValues(); + msg("calcDerivedHeatingValues done \n"); + + // add heat + setOfSources[i].addHeat(tankAmbientT_C, minutesToRun); + msg("setOfSources[i].addHeat(tankAmbientT_C, minutesToRun) done"); + + } + } +} +/////////////////////////////////////////////////////////////////////////////////// + + +void HPWH::turnAllHeatSourcesOff() { + for (int i = 0; i < numHeatSources; i++) { + setOfSources[i].disengageHeatSource(); + } + isHeating = false; +} + + +bool HPWH::areAllHeatSourcesOff() const { + bool allOff = true; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].isEngaged() == true) { + allOff = false; + } + } + return allOff; +} + +double HPWH::tankAvg_C(const std::vector nodeWeights) const { + double sum = 0; + double totWeight = 0; + + for (auto nodeWeight : nodeWeights) { + // bottom calc node only + if (nodeWeight.nodeNum == 0) { + sum += tankTemps_C[0] * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + // top calc node only + else if (nodeWeight.nodeNum == 13) { + sum += tankTemps_C[numNodes - 1] * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + else { + for (int n = 0; n < nodeDensity; ++n) { + int calcNode = (nodeWeight.nodeNum - 1) * nodeDensity + n; + sum += tankTemps_C[calcNode] * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + } + } + return sum / totWeight; +} + +//these are the HeatSource functions +//the public functions +HPWH::HeatSource::HeatSource(HPWH *parentInput) + :hpwh(parentInput), isOn(false), lockedOut(false), backupHeatSource(NULL), companionHeatSource(NULL), + followedByHeatSource(NULL), minT(-273.15), maxT(100), hysteresis_dC(0), airflowFreedom(1.0), + typeOfHeatSource(TYPE_none) {} + +HPWH::HeatSource::HeatSource(const HeatSource &hSource){ + hpwh = hSource.hpwh; + isOn = hSource.isOn; + lockedOut = hSource.lockedOut; + + runtime_min = hSource.runtime_min; + energyInput_kWh = hSource.energyInput_kWh; + energyOutput_kWh = hSource.energyOutput_kWh; + + isVIP = hSource.isVIP; + + if (hSource.backupHeatSource != NULL || hSource.companionHeatSource != NULL || hSource.followedByHeatSource != NULL) { + hpwh->simHasFailed = true; + if (hpwh->hpwhVerbosity >= VRB_reluctant) { + hpwh->msg("HeatSources cannot be copied if they contain pointers to other HeatSources\n"); + } + } + + for (int i = 0; i < CONDENSITY_SIZE; i++) { + condensity[i] = hSource.condensity[i]; + } + shrinkage = hSource.shrinkage; + + perfMap = hSource.perfMap; + + //i think vector assignment works correctly here + turnOnLogicSet = hSource.turnOnLogicSet; + shutOffLogicSet = hSource.shutOffLogicSet; + + minT = hSource.minT; + maxT = hSource.maxT; + hysteresis_dC = hSource.hysteresis_dC; + + depressesTemperature = hSource.depressesTemperature; + airflowFreedom = hSource.airflowFreedom; + + configuration = hSource.configuration; + typeOfHeatSource = hSource.typeOfHeatSource; + + lowestNode = hSource.lowestNode; + + +} + +HPWH::HeatSource& HPWH::HeatSource::operator=(const HeatSource &hSource){ + if (this == &hSource) { + return *this; + } + + hpwh = hSource.hpwh; + isOn = hSource.isOn; + lockedOut = hSource.lockedOut; + + runtime_min = hSource.runtime_min; + energyInput_kWh = hSource.energyInput_kWh; + energyOutput_kWh = hSource.energyOutput_kWh; + + isVIP = hSource.isVIP; + + if (hSource.backupHeatSource != NULL || hSource.companionHeatSource != NULL || hSource.followedByHeatSource != NULL) { + hpwh->simHasFailed = true; + if (hpwh->hpwhVerbosity >= VRB_reluctant) { + hpwh->msg("HeatSources cannot be copied if they contain pointers to other HeatSources\n"); + } + } + else { + companionHeatSource = NULL; + backupHeatSource = NULL; + followedByHeatSource = NULL; + } + + for (int i = 0; i < CONDENSITY_SIZE; i++) { + condensity[i] = hSource.condensity[i]; + } + shrinkage = hSource.shrinkage; + + perfMap = hSource.perfMap; + + //i think vector assignment works correctly here + turnOnLogicSet = hSource.turnOnLogicSet; + shutOffLogicSet = hSource.shutOffLogicSet; + + minT = hSource.minT; + maxT = hSource.maxT; + hysteresis_dC = hSource.hysteresis_dC; + + depressesTemperature = hSource.depressesTemperature; + airflowFreedom = hSource.airflowFreedom; + + configuration = hSource.configuration; + typeOfHeatSource = hSource.typeOfHeatSource; + + lowestNode = hSource.lowestNode; + + return *this; +} + + + +void HPWH::HeatSource::setCondensity(double cnd1, double cnd2, double cnd3, double cnd4, + double cnd5, double cnd6, double cnd7, double cnd8, + double cnd9, double cnd10, double cnd11, double cnd12) { + condensity[0] = cnd1; + condensity[1] = cnd2; + condensity[2] = cnd3; + condensity[3] = cnd4; + condensity[4] = cnd5; + condensity[5] = cnd6; + condensity[6] = cnd7; + condensity[7] = cnd8; + condensity[8] = cnd9; + condensity[9] = cnd10; + condensity[10] = cnd11; + condensity[11] = cnd12; +} + +int HPWH::HeatSource::findParent() const { + for (int i = 0; i < hpwh->numHeatSources; ++i) { + if (this == hpwh->setOfSources[i].backupHeatSource) { + return i; + } + } + return -1; +} + +bool HPWH::HeatSource::isEngaged() const { + return isOn; +} + +bool HPWH::HeatSource::isLockedOut() const { + return lockedOut; +} + +void HPWH::HeatSource::lockOutHeatSource() { + lockedOut = true; +} + +void HPWH::HeatSource::unlockHeatSource() { + lockedOut = false; +} + +bool HPWH::HeatSource::shouldLockOut(double heatSourceAmbientT_C) const { + + // if it's already locked out, keep it locked out + if (isLockedOut() == true) { + return true; + } + else { + //when the "external" temperature is too cold - typically used for compressor low temp. cutoffs + //when running, use hysteresis + bool lock = false; + if (isEngaged() == true && heatSourceAmbientT_C < minT - hysteresis_dC) { + lock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { + hpwh->msg("\tlock-out: running below minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); + } + } + //when not running, don't use hysteresis + else if (isEngaged() == false && heatSourceAmbientT_C < minT) { + lock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { + hpwh->msg("\tlock-out: already below minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); + } + } + + //when the "external" temperature is too warm - typically used for resistance lockout + //when running, use hysteresis + if (isEngaged() == true && heatSourceAmbientT_C > maxT + hysteresis_dC) { + lock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { + hpwh->msg("\tlock-out: running above maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); + } + } + //when not running, don't use hysteresis + else if (isEngaged() == false && heatSourceAmbientT_C > maxT) { + lock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { + hpwh->msg("\tlock-out: already above maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); + } + } + if (lock == true && backupHeatSource == NULL) { + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { + hpwh->msg("\nWARNING: lock-out triggered, but no backupHeatSource defined. Simulation will continue without lock-out"); + } + lock = false; + } + if (hpwh->hpwhVerbosity >= VRB_typical) { + hpwh->msg("\n"); + } + return lock; + } +} + +bool HPWH::HeatSource::shouldUnlock(double heatSourceAmbientT_C) const { + + // if it's already unlocked, keep it unlocked + if (isLockedOut() == false) { + return true; + } + else { + //when the "external" temperature is no longer too cold or too warm + //when running, use hysteresis + bool unlock = false; + if (isEngaged() == true && heatSourceAmbientT_C > minT + hysteresis_dC && heatSourceAmbientT_C < maxT - hysteresis_dC) { + unlock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C > minT + hysteresis_dC) { + hpwh->msg("\tunlock: running above minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); + } + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C < maxT - hysteresis_dC) { + hpwh->msg("\tunlock: running below maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); + } + } + //when not running, don't use hysteresis + else if (isEngaged() == false && heatSourceAmbientT_C > minT && heatSourceAmbientT_C < maxT) { + unlock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C > minT) { + hpwh->msg("\tunlock: already above minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); + } + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C < maxT) { + hpwh->msg("\tunlock: already below maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); + } + } + if (hpwh->hpwhVerbosity >= VRB_typical) { + hpwh->msg("\n"); + } + return unlock; + } +} + +void HPWH::HeatSource::engageHeatSource() { + isOn = true; + hpwh->isHeating = true; + if (companionHeatSource != NULL && + companionHeatSource->shutsOff() != true && + companionHeatSource->isEngaged() == false) { + companionHeatSource->engageHeatSource(); + } +} + + +void HPWH::HeatSource::disengageHeatSource() { + isOn = false; +} + +bool HPWH::HeatSource::shouldHeat() const { + //return true if the heat source logic tells it to come on, false if it doesn't, + //or if an unsepcified selector was used + bool shouldEngage = false; + + for (int i = 0; i < (int)turnOnLogicSet.size(); i++) { + if (hpwh->hpwhVerbosity >= VRB_emetic) { + hpwh->msg("\tshouldHeat logic: %s ", turnOnLogicSet[i].description.c_str()); + } + + double average = hpwh->tankAvg_C(turnOnLogicSet[i].nodeWeights); + double comparison; + + if (turnOnLogicSet[i].isAbsolute) { + comparison = turnOnLogicSet[i].decisionPoint; + } else { + comparison = hpwh->setpoint_C - turnOnLogicSet[i].decisionPoint; + } + + if (turnOnLogicSet[i].compare(average, comparison)) { + shouldEngage = true; + + //debugging message handling + if (hpwh->hpwhVerbosity >= VRB_typical) { + hpwh->msg("engages!\n"); + } + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("average: %.2lf \t setpoint: %.2lf \t decisionPoint: %.2lf \t comparison: %2.1f\n", average, hpwh->setpoint_C, turnOnLogicSet[i].decisionPoint, comparison); + } + } + + //quit searching the logics if one of them turns it on + if (shouldEngage) break; + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("returns: %d \t", shouldEngage); + } + } //end loop over set of logic conditions + + //if everything else wants it to come on, but if it would shut off anyways don't turn it on + if (shouldEngage == true && shutsOff() == true) { + shouldEngage = false; + if (hpwh->hpwhVerbosity >= VRB_typical) { + hpwh->msg("but is denied by shutsOff"); + } + } + + if (hpwh->hpwhVerbosity >= VRB_typical) { + hpwh->msg("\n"); + } + return shouldEngage; +} + + +bool HPWH::HeatSource::shutsOff() const { + bool shutOff = false; + + if (hpwh->tankTemps_C[0] >= hpwh->setpoint_C) { + shutOff = true; + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("shutsOff bottom node hot: %.2d C \n returns true", hpwh->tankTemps_C[0]); + } + return shutOff; + } + + for (int i = 0; i < (int)shutOffLogicSet.size(); i++) { + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("\tshutsOff logic: %s ", shutOffLogicSet[i].description.c_str()); + } + + double average = hpwh->tankAvg_C(shutOffLogicSet[i].nodeWeights); + double comparison; + + if (shutOffLogicSet[i].isAbsolute) { + comparison = shutOffLogicSet[i].decisionPoint; + } else { + comparison = hpwh->setpoint_C - shutOffLogicSet[i].decisionPoint; + } + + if (shutOffLogicSet[i].compare(average, comparison)) { + shutOff = true; + + //debugging message handling + if (hpwh->hpwhVerbosity >= VRB_typical) { + hpwh->msg("shuts down %s\n", shutOffLogicSet[i].description.c_str()); + } + } + } + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("returns: %d \n", shutOff); + } + return shutOff; +} + + +void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { + double input_BTUperHr, cap_BTUperHr, cop, captmp_kJ, leftoverCap_kJ = 0.0; + + // set the leftover capacity of the Heat Source to 0, so the first round of + // passing it on works + leftoverCap_kJ = 0.0; + + switch (configuration){ + case CONFIG_SUBMERGED: + case CONFIG_WRAPPED: + { + static std::vector heatDistribution(hpwh->numNodes); + //clear the heatDistribution vector, since it's static it is still holding the + //distribution from the last go around + heatDistribution.clear(); + //calcHeatDist takes care of the swooping for wrapped configurations + calcHeatDist(heatDistribution); + + // calculate capacity btu/hr, input btu/hr, and cop + getCapacity(externalT_C, getCondenserTemp(), input_BTUperHr, cap_BTUperHr, cop); + + //some outputs for debugging + if (hpwh->hpwhVerbosity >= VRB_typical){ + hpwh->msg("capacity_kWh %.2lf \t\t cap_BTUperHr %.2lf \n", BTU_TO_KWH(cap_BTUperHr)*(minutesToRun) / 60.0, cap_BTUperHr); + } + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("heatDistribution: %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf \n", heatDistribution[0], heatDistribution[1], heatDistribution[2], heatDistribution[3], heatDistribution[4], heatDistribution[5], heatDistribution[6], heatDistribution[7], heatDistribution[8], heatDistribution[9], heatDistribution[10], heatDistribution[11]); + } + //the loop over nodes here is intentional - essentially each node that has + //some amount of heatDistribution acts as a separate resistive element + //maybe start from the top and go down? test this with graphs + for (int i = hpwh->numNodes - 1; i >= 0; i--){ + //for(int i = 0; i < hpwh->numNodes; i++){ + captmp_kJ = BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0 * heatDistribution[i]); + if (captmp_kJ != 0){ + //add leftoverCap to the next run, and keep passing it on + leftoverCap_kJ = addHeatAboveNode(captmp_kJ + leftoverCap_kJ, i, minutesToRun); + } + } + + //after you've done everything, any leftover capacity is time that didn't run + this->runtime_min = (1.0 - (leftoverCap_kJ / BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0))) * minutesToRun; +#if 1 // error check, 1-22-2017 + if (runtime_min < 0.) + if (hpwh->hpwhVerbosity >= VRB_reluctant) + hpwh->msg("Internal error: Negative runtime = %0.3f min\n", runtime_min); +#endif + } + break; + + case CONFIG_EXTERNAL: + //Else the heat source is external. Sanden system is only current example + //capacity is calculated internal to this function, and cap/input_BTUperHr, cop are outputs + this->runtime_min = addHeatExternal(externalT_C, minutesToRun, cap_BTUperHr, input_BTUperHr, cop); + break; + } + + // Write the input & output energy + energyInput_kWh = BTU_TO_KWH(input_BTUperHr * runtime_min / 60.0); + energyOutput_kWh = BTU_TO_KWH(cap_BTUperHr * runtime_min / 60.0); +} + + + +//the private functions +void HPWH::HeatSource::sortPerformanceMap() { + std::sort(perfMap.begin(), perfMap.end(), + [](const HPWH::HeatSource::perfPoint & a, const HPWH::HeatSource::perfPoint & b) -> bool { + return a.T_F < b.T_F; + }); +} + + +double HPWH::HeatSource::expitFunc(double x, double offset) { + double val; + val = 1 / (1 + exp(x - offset)); + return val; +} + + +void HPWH::HeatSource::normalize(std::vector &distribution) { + double sum_tmp = 0.0; + + for (unsigned int i = 0; i < distribution.size(); i++) { + sum_tmp += distribution[i]; + } + for (unsigned int i = 0; i < distribution.size(); i++) { + if (sum_tmp > 0.0) { + distribution[i] /= sum_tmp; + } + else { + distribution[i] = 0.0; + } + //this gives a very slight speed improvement (milliseconds per simulated year) + if (distribution[i] < HEATDIST_MINVALUE) { + distribution[i] = 0; + } + } +} + + +double HPWH::HeatSource::getCondenserTemp() { + double condenserTemp_C = 0.0; + int tempNodesPerCondensityNode = hpwh->numNodes / CONDENSITY_SIZE; + int j = 0; + + for (int i = 0; i < hpwh->numNodes; i++) { + j = i / tempNodesPerCondensityNode; + if (condensity[j] != 0) { + condenserTemp_C += (condensity[j] / tempNodesPerCondensityNode) * hpwh->tankTemps_C[i]; + //the weights don't need to be added to divide out later because they should always sum to 1 + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("condenserTemp_C:\t %.2lf \ti:\t %d \tj\t %d \tcondensity[j]:\t %.2lf \ttankTemps_C[i]:\t %.2lf\n", condenserTemp_C, i, j, condensity[j], hpwh->tankTemps_C[i]); + } + } + } + if (hpwh->hpwhVerbosity >= VRB_typical){ + hpwh->msg("condenser temp %.2lf \n", condenserTemp_C); + } + return condenserTemp_C; +} + + +void HPWH::HeatSource::getCapacity(double externalT_C, double condenserTemp_C, double &input_BTUperHr, double &cap_BTUperHr, double &cop) { + double COP_T1, COP_T2; //cop at ambient temperatures T1 and T2 + double inputPower_T1_Watts, inputPower_T2_Watts; //input power at ambient temperatures T1 and T2 + double externalT_F, condenserTemp_F; + + // Convert Celsius to Fahrenheit for the curve fits + condenserTemp_F = C_TO_F(condenserTemp_C); + externalT_F = C_TO_F(externalT_C); + + // Get bounding performance map points for interpolation/extrapolation + bool extrapolate = false; + size_t i_prev = 0; + size_t i_next = 1; + for (size_t i = 0; i < perfMap.size(); ++i) { + if (externalT_F < perfMap[i].T_F){ + if (i == 0) { + extrapolate = true; + i_prev = 0; + i_next = 1; + } else { + i_prev = i - 1; + i_next = i; + } + break; + } else { + if (i == perfMap.size() - 1) { + extrapolate = true; + i_prev = i - 1; + i_next = i; + break; + } + } + } + + + // Calculate COP and Input Power at each of the two reference temepratures + COP_T1 = perfMap[i_prev].COP_coeffs[0]; + COP_T1 += perfMap[i_prev].COP_coeffs[1] * condenserTemp_F; + COP_T1 += perfMap[i_prev].COP_coeffs[2] * condenserTemp_F * condenserTemp_F; + + COP_T2 = perfMap[i_next].COP_coeffs[0]; + COP_T2 += perfMap[i_next].COP_coeffs[1] * condenserTemp_F; + COP_T2 += perfMap[i_next].COP_coeffs[2] * condenserTemp_F * condenserTemp_F; + + inputPower_T1_Watts = perfMap[i_prev].inputPower_coeffs[0]; + inputPower_T1_Watts += perfMap[i_prev].inputPower_coeffs[1] * condenserTemp_F; + inputPower_T1_Watts += perfMap[i_prev].inputPower_coeffs[2] * condenserTemp_F * condenserTemp_F; + + inputPower_T2_Watts = perfMap[i_next].inputPower_coeffs[0]; + inputPower_T2_Watts += perfMap[i_next].inputPower_coeffs[1] * condenserTemp_F; + inputPower_T2_Watts += perfMap[i_next].inputPower_coeffs[2] * condenserTemp_F * condenserTemp_F; + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("inputPower_T1_constant_W linear_WperF quadratic_WperF2 \t%.2lf %.2lf %.2lf \n", perfMap[0].inputPower_coeffs[0], perfMap[0].inputPower_coeffs[1], perfMap[0].inputPower_coeffs[2]); + hpwh->msg("inputPower_T2_constant_W linear_WperF quadratic_WperF2 \t%.2lf %.2lf %.2lf \n", perfMap[1].inputPower_coeffs[0], perfMap[1].inputPower_coeffs[1], perfMap[1].inputPower_coeffs[2]); + hpwh->msg("inputPower_T1_Watts: %.2lf \tinputPower_T2_Watts: %.2lf \n", inputPower_T1_Watts, inputPower_T2_Watts); + + if (extrapolate) { + hpwh->msg("Warning performance extrapolation\n\tExternal Temperature: %.2lf\tNearest temperatures: %.2lf, %.2lf \n\n", externalT_F, perfMap[i_prev].T_F, perfMap[i_next].T_F); + } + + } + + // Interpolate to get COP and input power at the current ambient temperature + cop = COP_T1 + (externalT_F - perfMap[i_prev].T_F) * ((COP_T2 - COP_T1) / (perfMap[i_next].T_F - perfMap[i_prev].T_F)); + input_BTUperHr = KWH_TO_BTU((inputPower_T1_Watts + (externalT_F - perfMap[i_prev].T_F) * + ((inputPower_T2_Watts - inputPower_T1_Watts) + / (perfMap[i_next].T_F - perfMap[i_prev].T_F)) + ) / 1000.0); //1000 converts w to kw + cap_BTUperHr = cop * input_BTUperHr; + + //here is where the scaling for flow restriction happens + //the input power doesn't change, we just scale the cop by a small percentage + //that is based on the flow rate. The equation is a fit to three points, + //measured experimentally - 12 percent reduction at 150 cfm, 10 percent at + //200, and 0 at 375. Flow is expressed as fraction of full flow. + if (airflowFreedom != 1){ + double airflow = 375 * airflowFreedom; + cop *= 0.00056*airflow + 0.79; + } + if (hpwh->hpwhVerbosity >= VRB_typical){ + hpwh->msg("cop: %.2lf \tinput_BTUperHr: %.2lf \tcap_BTUperHr: %.2lf \n", cop, input_BTUperHr, cap_BTUperHr); + } +} + + +void HPWH::HeatSource::calcHeatDist(std::vector &heatDistribution) { + + // Populate the vector of heat distribution + for (int i = 0; i < hpwh->numNodes; i++) { + if (i < lowestNode) { + heatDistribution.push_back(0); + } + else { + int k; + if (configuration == CONFIG_SUBMERGED) { // Inside the tank, no swoopiness required + //intentional integer division + k = i / int(hpwh->numNodes / CONDENSITY_SIZE); + heatDistribution.push_back(condensity[k]); + } + else if (configuration == CONFIG_WRAPPED) { // Wrapped around the tank, send through the logistic function + double temp = 0; //temp for temporary not temperature + double offset = 5.0 / 1.8; + temp = expitFunc((hpwh->tankTemps_C[i] - hpwh->tankTemps_C[lowestNode]) / this->shrinkage, offset); + temp *= (hpwh->setpoint_C - hpwh->tankTemps_C[i]); +#if defined( SETPOINT_FIX) + if (temp < 0.) + temp = 0.; +#endif + heatDistribution.push_back(temp); + } + } + } + normalize(heatDistribution); + +} + + +double HPWH::HeatSource::addHeatAboveNode(double cap_kJ, int node, double minutesToRun) { + double Q_kJ, deltaT_C, targetTemp_C; + int setPointNodeNum; + + double volumePerNode_L = hpwh->tankVolume_L / hpwh->numNodes; + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("node %2d cap_kwh %.4lf \n", node, KJ_TO_KWH(cap_kJ)); + } + + // find the first node (from the bottom) that does not have the same temperature as the one above it + // if they all have the same temp., use the top node, hpwh->numNodes-1 + setPointNodeNum = node; + for (int i = node; i < hpwh->numNodes - 1; i++){ + if (hpwh->tankTemps_C[i] != hpwh->tankTemps_C[i + 1]) { + break; + } + else{ + setPointNodeNum = i + 1; + } + } + + // maximum heat deliverable in this timestep + while (cap_kJ > 0 && setPointNodeNum < hpwh->numNodes) { + // if the whole tank is at the same temp, the target temp is the setpoint + if (setPointNodeNum == (hpwh->numNodes - 1)) { + targetTemp_C = hpwh->setpoint_C; + } + //otherwise the target temp is the first non-equal-temp node + else { + targetTemp_C = hpwh->tankTemps_C[setPointNodeNum + 1]; + } + + deltaT_C = targetTemp_C - hpwh->tankTemps_C[setPointNodeNum]; + + //heat needed to bring all equal temp. nodes up to the temp of the next node. kJ + Q_kJ = CPWATER_kJperkgC * volumePerNode_L * DENSITYWATER_kgperL * (setPointNodeNum + 1 - node) * deltaT_C; + + //Running the rest of the time won't recover + if (Q_kJ > cap_kJ){ + for (int j = node; j <= setPointNodeNum; j++) { + hpwh->tankTemps_C[j] += cap_kJ / CPWATER_kJperkgC / volumePerNode_L / DENSITYWATER_kgperL / (setPointNodeNum + 1 - node); + } + cap_kJ = 0; + } +#if defined( SETPOINT_FIX) + else if (Q_kJ > 0.) + { // temp will recover by/before end of timestep + for (int j = node; j <= setPointNodeNum; j++) + hpwh->tankTemps_C[j] = targetTemp_C; + cap_kJ -= Q_kJ; + } + setPointNodeNum++; +#else + //temp will recover by/before end of timestep + else{ + for (int j = node; j <= setPointNodeNum; j++){ + hpwh->tankTemps_C[j] = targetTemp_C; + } + setPointNodeNum++; + cap_kJ -= Q_kJ; + } +#endif + } + + //return the unused capacity + return cap_kJ; +} + + +double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun, double &cap_BTUperHr, double &input_BTUperHr, double &cop) { + double heatingCapacity_kJ, deltaT_C, timeUsed_min, nodeHeat_kJperNode, nodeFrac; + double inputTemp_BTUperHr = 0, capTemp_BTUperHr = 0, copTemp = 0; + double volumePerNode_LperNode = hpwh->tankVolume_L / hpwh->numNodes; + double timeRemaining_min = minutesToRun; + + input_BTUperHr = 0; + cap_BTUperHr = 0; + cop = 0; + + do{ + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("bottom tank temp: %.2lf \n", hpwh->tankTemps_C[0]); + } + + //how much heat is available this timestep + getCapacity(externalT_C, hpwh->tankTemps_C[0], inputTemp_BTUperHr, capTemp_BTUperHr, copTemp); + heatingCapacity_kJ = BTU_TO_KJ(capTemp_BTUperHr * (minutesToRun / 60.0)); + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("\theatingCapacity_kJ stepwise: %.2lf \n", heatingCapacity_kJ); + } + + //adjust capacity for how much time is left in this step + heatingCapacity_kJ = heatingCapacity_kJ * (timeRemaining_min / minutesToRun); + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("\theatingCapacity_kJ remaining this node: %.2lf \n", heatingCapacity_kJ); + } + + //calculate what percentage of the bottom node can be heated to setpoint + //with amount of heat available this timestep + deltaT_C = hpwh->setpoint_C - hpwh->tankTemps_C[0]; + nodeHeat_kJperNode = volumePerNode_LperNode * DENSITYWATER_kgperL * CPWATER_kJperkgC * deltaT_C; + //protect against dividing by zero - if bottom node is at (or above) setpoint, + //add no heat + if (nodeHeat_kJperNode <= 0) { + nodeFrac = 0; + } + else { + nodeFrac = heatingCapacity_kJ / nodeHeat_kJperNode; + } + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("nodeHeat_kJperNode: %.2lf nodeFrac: %.2lf \n\n", nodeHeat_kJperNode, nodeFrac); + } + //if more than one, round down to 1 and subtract the amount of time it would + //take to heat that node from the timeRemaining + if (nodeFrac > 1){ + nodeFrac = 1; + timeUsed_min = (nodeHeat_kJperNode / heatingCapacity_kJ)*timeRemaining_min; + timeRemaining_min -= timeUsed_min; + } + //otherwise just the fraction available + //this should make heatingCapacity == 0 if nodeFrac < 1 + else{ + timeUsed_min = timeRemaining_min; + timeRemaining_min = 0; + } + + //move all nodes down, mixing if less than a full node + for (int n = 0; n < hpwh->numNodes - 1; n++) { + hpwh->tankTemps_C[n] = hpwh->tankTemps_C[n] * (1 - nodeFrac) + hpwh->tankTemps_C[n + 1] * nodeFrac; + } + //add water to top node, heated to setpoint + hpwh->tankTemps_C[hpwh->numNodes - 1] = hpwh->tankTemps_C[hpwh->numNodes - 1] * (1 - nodeFrac) + hpwh->setpoint_C * nodeFrac; + + + //track outputs - weight by the time ran + input_BTUperHr += inputTemp_BTUperHr*timeUsed_min; + cap_BTUperHr += capTemp_BTUperHr*timeUsed_min; + cop += copTemp*timeUsed_min; + + + //if there's still time remaining and you haven't heated to the cutoff + //specified in shutsOff logic, keep heating + } while (timeRemaining_min > 0 && shutsOff() != true); + + //divide outputs by sum of weight - the total time ran + input_BTUperHr /= (minutesToRun - timeRemaining_min); + cap_BTUperHr /= (minutesToRun - timeRemaining_min); + cop /= (minutesToRun - timeRemaining_min); + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("final remaining time: %.2lf \n", timeRemaining_min); + } + //return the time left + return minutesToRun - timeRemaining_min; +} + + + + + + +void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { + int i; + + isOn = false; + isVIP = false; + for (i = 0; i < CONDENSITY_SIZE; i++) { + condensity[i] = 0; + } + condensity[node] = 1; + + perfMap.reserve(2); + + perfMap.push_back({ + 50, // Temperature (T_F) + {Watts, 0.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {1.0, 0.0, 0.0} // COP Coefficients (COP_coeffs) + }); + + perfMap.push_back({ + 67, // Temperature (T_F) + {Watts, 0.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {1.0, 0.0, 0.0} // COP Coefficients (COP_coeffs) + }); + + configuration = CONFIG_SUBMERGED; //immersed in tank + + typeOfHeatSource = TYPE_resistance; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +void HPWH::HeatSource::setupExtraHeat(std::vector* nodeExtraHeat_W) { + + //get sum of vector + double watts = 0.0; + for (unsigned int i = 0; i < (*nodeExtraHeat_W).size(); i++) { + watts += (*nodeExtraHeat_W)[i]; + } + + std::vector tempCondensity = *nodeExtraHeat_W; + normalize(tempCondensity); + // set condensity based on normalized vector + setCondensity( tempCondensity[0], tempCondensity[1], tempCondensity[2], tempCondensity[3], + tempCondensity[4], tempCondensity[5], tempCondensity[6], tempCondensity[7], + tempCondensity[8], tempCondensity[9], tempCondensity[10], tempCondensity[11] ); + + perfMap.clear(); + perfMap.reserve(2); + + perfMap.push_back({ + 50, // Temperature (T_F) + { watts, 0.0, 0.0 }, // Input Power Coefficients (inputPower_coeffs) + { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) + }); + + perfMap.push_back({ + 67, // Temperature (T_F) + { watts, 0.0, 0.0 }, // Input Power Coefficients (inputPower_coeffs) + { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) + }); + +} +//////////////////////////////////////////////////////////////////////////// + +void HPWH::HeatSource::addTurnOnLogic(HeatingLogic logic){ + this->turnOnLogicSet.push_back(logic); +} +void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { + this->shutOffLogicSet.push_back(logic); +} + +void HPWH::calcDerivedValues(){ + // tank node density (number of calculation nodes per regular node) + nodeDensity = numNodes / 12; + + // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() + calcDerivedHeatingValues(); + + //heat source ability to depress temp + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { + setOfSources[i].depressesTemperature = true; + } + else if (setOfSources[i].typeOfHeatSource == TYPE_resistance) { + setOfSources[i].depressesTemperature = false; + } + } +} + +void HPWH::calcDerivedHeatingValues(){ + static char outputString[MAXOUTSTRING]; //this is used for debugging outputs + + //condentropy/shrinkage + double condentropy = 0; + double alpha = 1, beta = 2; // Mapping from condentropy to shrinkage + for (int i = 0; i < numHeatSources; i++) { + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, "Heat Source %d \n", i); + } + + // Calculate condentropy and ==> shrinkage + condentropy = 0; + for (int j = 0; j < CONDENSITY_SIZE; j++) { + if (setOfSources[i].condensity[j] > 0) { + condentropy -= setOfSources[i].condensity[j] * log(setOfSources[i].condensity[j]); + if (hpwhVerbosity >= VRB_emetic) msg(outputString, "condentropy %.2lf \n", condentropy); + } + } + setOfSources[i].shrinkage = alpha + condentropy * beta; + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, "shrinkage %.2lf \n\n", setOfSources[i].shrinkage); + } + } + + //lowest node + int lowest = 0; + for (int i = 0; i < numHeatSources; i++) { + lowest = 0; + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, "Heat Source %d \n", i); + } + + for (int j = 0; j < numNodes; j++) { + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, "j: %d j/ (numNodes/CONDENSITY_SIZE) %d \n", j, j / (numNodes / CONDENSITY_SIZE)); + } + + if (setOfSources[i].condensity[(j / (numNodes / CONDENSITY_SIZE))] > 0) { + lowest = j; + break; + } + } + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, " lowest : %d \n", lowest); + } + + setOfSources[i].lowestNode = lowest; + } + + // define condenser index and lowest resistance element index + compressorIndex = -1; // Default = No compressor + lowestElementIndex = -1; // Default = No resistance elements + int lowestElementPos = CONDENSITY_SIZE; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].typeOfHeatSource == HPWH::TYPE_compressor) { + compressorIndex = i; // NOTE: Maybe won't work with multiple compressors (last compressor will be used) + } + else { + for (int j = 0; j < CONDENSITY_SIZE; j++) { + if (setOfSources[i].condensity[j] > 0.0 && j < lowestElementPos) { + lowestElementIndex = i; + lowestElementPos = j; + break; + } + } + } + } + + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, " compressorIndex : %d \n", compressorIndex); + } + if (hpwhVerbosity >= VRB_emetic) { + msg(outputString, " lowestElementIndex : %d \n", lowestElementIndex); + } +} + + +// Used to check a few inputs after the initialization of a tank model from a preset or a file. +int HPWH::checkInputs(){ + int returnVal = 0; + //use a returnVal so that all checks are processed and error messages written + + if (numHeatSources <= 0 && hpwhModel != MODELS_StorageTank ) { + msg("You must have at least one HeatSource.\n"); + returnVal = HPWH_ABORT; + } + if ((numNodes % 12) != 0) { + msg("The number of nodes must be a multiple of 12"); + returnVal = HPWH_ABORT; + } + + + double condensitySum; + //loop through all heat sources to check each for malconfigurations + for (int i = 0; i < numHeatSources; i++) { + //check the heat source type to make sure it has been set + if (setOfSources[i].typeOfHeatSource == TYPE_none) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Heat source %d does not have a specified type. Initialization failed.\n", i); + } + returnVal = HPWH_ABORT; + } + //check to make sure there is at least one onlogic or parent with onlogic + int parent = setOfSources[i].findParent(); + if (setOfSources[i].turnOnLogicSet.size() == 0 && (parent == -1 || setOfSources[parent].turnOnLogicSet.size() == 0)) { + msg("You must specify at least one logic to turn on the element or the element must be set as a backup for another heat source with at least one logic."); + returnVal = HPWH_ABORT; + } + //check is condensity sums to 1 + condensitySum = 0; + for (int j = 0; j < CONDENSITY_SIZE; j++) condensitySum += setOfSources[i].condensity[j]; + if (fabs(condensitySum - 1.0) > 1e-6) { + msg("The condensity for heatsource %d does not sum to 1. \n", i); + msg("It sums to %f \n", condensitySum); + returnVal = HPWH_ABORT; + } + //check that air flows are all set properly + if (setOfSources[i].airflowFreedom > 1.0 || setOfSources[i].airflowFreedom <= 0.0) { + msg("The airflowFreedom must be between 0 and 1 for heatsource %d. \n", i); + returnVal = HPWH_ABORT; + } + + + } + + //Check if the UA is out of bounds + if (tankUA_kJperHrC < 0.0) { + msg("The tankUA_kJperHrC is less than 0 for a HPWH, it must be greater than 0, tankUA_kJperHrC is: %f \n", tankUA_kJperHrC); + returnVal = HPWH_ABORT; + } + + + + //if there's no failures, return 0 + return returnVal; +} + +#ifndef HPWH_ABRIDGED +int HPWH::HPWHinit_file(string configFile){ + simHasFailed = true; //this gets cleared on successful completion of init + + //clear out old stuff if you're re-initializing + delete[] tankTemps_C; + delete[] nextTankTemps_C; + delete[] setOfSources; + + + //open file, check and report errors + std::ifstream inputFILE; + inputFILE.open(configFile.c_str()); + if (!inputFILE.is_open()) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Input file failed to open. \n"); + } + return HPWH_ABORT; + } + + //some variables that will be handy + int heatsource, sourceNum, nTemps; + string tempString, units; + double tempDouble, dblArray[12]; + + //being file processing, line by line + string line_s; + std::stringstream line_ss; + string token; + while (std::getline(inputFILE, line_s)){ + line_ss.clear(); + line_ss.str(line_s); + + //grab the first word, and start comparing + line_ss >> token; + if (token.at(0) == '#' || line_s.empty()) { + //if you hit a comment, skip to next line + continue; + } + else if (token == "numNodes") { + line_ss >> numNodes; + } + else if (token == "volume") { + line_ss >> tempDouble >> units; + if (units == "gal") tempDouble = GAL_TO_L(tempDouble); + else if (units == "L"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s. \n", token.c_str()); + } + return HPWH_ABORT; + } + tankVolume_L = tempDouble; + } + else if (token == "UA") { + line_ss >> tempDouble >> units; + if (units != "kJperHrC") { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s. \n", token.c_str()); + } + return HPWH_ABORT; + } + tankUA_kJperHrC = tempDouble; + } + else if (token == "depressTemp") { + line_ss >> tempString; + if (tempString == "true") { + doTempDepression = true; + } + else if (tempString == "false") { + doTempDepression = false; + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper value for %s\n", token.c_str()); + } + return HPWH_ABORT; + } + } + else if (token == "mixOnDraw") { + line_ss >> tempString; + if (tempString == "true") { + tankMixesOnDraw = true; + } + else if (tempString == "false") { + tankMixesOnDraw = false; + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper value for %s\n", token.c_str()); + } + return HPWH_ABORT; + } + } + else if (token == "setpoint") { + line_ss >> tempDouble >> units; + if (units == "F") tempDouble = F_TO_C(tempDouble); + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s. \n", token.c_str()); + } + return HPWH_ABORT; + } + setpoint_C = tempDouble; + //tank will be set to setpoint at end of function + } + else if (token == "setpointFixed") { + line_ss >> tempString; + if (tempString == "true") setpointFixed = true; + else if (tempString == "false") setpointFixed = false; + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper value for %s\n", token.c_str()); + } + return HPWH_ABORT; + } + } + else if (token == "verbosity") { + line_ss >> token; + if (token == "silent") { + hpwhVerbosity = VRB_silent; + } + else if (token == "reluctant") { + hpwhVerbosity = VRB_reluctant; + } + else if (token == "typical") { + hpwhVerbosity = VRB_typical; + } + else if (token == "emetic") { + hpwhVerbosity = VRB_emetic; + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect verbosity on input. \n"); + } + return HPWH_ABORT; + } + } + + else if (token == "numHeatSources") { + line_ss >> numHeatSources; + setOfSources = new HeatSource[numHeatSources]; + for (int i = 0; i < numHeatSources; i++) { + setOfSources[i] = HeatSource(this); + } + } + else if (token == "heatsource") { + if (numHeatSources == 0) { + msg("You must specify the number of heatsources before setting their properties. \n"); + return HPWH_ABORT; + } + line_ss >> heatsource >> token; + if (token == "isVIP") { + line_ss >> tempString; + if (tempString == "true") setOfSources[heatsource].isVIP = true; + else if (tempString == "false") setOfSources[heatsource].isVIP = false; + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper value for %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + else if (token == "isOn") { + line_ss >> tempString; + if (tempString == "true") setOfSources[heatsource].isOn = true; + else if (tempString == "false") setOfSources[heatsource].isOn = false; + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper value for %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + else if (token == "minT") { + line_ss >> tempDouble >> units; + if (units == "F") tempDouble = F_TO_C(tempDouble); + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s. \n", token.c_str()); + } + return HPWH_ABORT; + } + setOfSources[heatsource].minT = tempDouble; + } + else if (token == "maxT") { + line_ss >> tempDouble >> units; + if (units == "F") tempDouble = F_TO_C(tempDouble); + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s. \n", token.c_str()); + } + return HPWH_ABORT; + } + setOfSources[heatsource].maxT = tempDouble; + } + else if (token == "onlogic" || token == "offlogic") { + line_ss >> tempString; + if (tempString == "nodes") { + std::vector nodeNums; + std::vector weights; + std::string nextToken; + line_ss >> nextToken; + while (std::regex_match(nextToken, std::regex("\\d+"))) { + int nodeNum = std::stoi(nextToken); + if (nodeNum > 13 || nodeNum < 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Node number for heatsource %d %s must be between 0 and 13. \n", heatsource, token.c_str()); + } + return HPWH_ABORT; + } + nodeNums.push_back(nodeNum); + line_ss >> nextToken; + } + if (nextToken == "weights") { + line_ss >> nextToken; + while (std::regex_match(nextToken, std::regex("-?\\d*\\.\\d+(?:e-?\\d+)?"))) { + weights.push_back(std::stod(nextToken)); + line_ss >> nextToken; + } + } else { + for (auto n : nodeNums) { + n += 0; // used to get rid of unused variable compiler warning + weights.push_back(1.0); + } + } + if (nodeNums.size() != weights.size()) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Number of weights for heatsource %d %s (%d) does not macht number of nodes for %s (%d). \n", heatsource, token.c_str(), weights.size(), token.c_str(), nodeNums.size()); + } + return HPWH_ABORT; + } + if (nextToken != "absolute" && nextToken != "relative") { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper definition, \"%s\", for heat source %d %s. Should be \"relative\" or \"absoute\".\n", nextToken.c_str(), heatsource, token.c_str()); + } + return HPWH_ABORT; + } + bool absolute = (nextToken == "absolute"); + std::string compareStr; + line_ss >> compareStr >> tempDouble >> units; + std::function compare; + if (compareStr == "<") compare = std::less(); + else if (compareStr == ">") compare = std::greater(); + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper comparison, \"%s\", for heat source %d %s. Should be \"<\" or \">\".\n", compareStr.c_str(), heatsource, token.c_str()); + } + return HPWH_ABORT; + } + if (units == "F") { + if (absolute) { + tempDouble = F_TO_C(tempDouble); + } + else { + tempDouble = dF_TO_dC(tempDouble); + } + } + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + std::vector nodeWeights; + for (size_t i = 0; i < nodeNums.size(); i++ ) { + nodeWeights.emplace_back(nodeNums[i],weights[i]); + } + HPWH::HeatingLogic logic("custom", nodeWeights, tempDouble, absolute, compare); + if (token == "onlogic") { + setOfSources[heatsource].addTurnOnLogic(logic); + } else { // "offlogic" + setOfSources[heatsource].addShutOffLogic(logic); + } + } + else if (token == "onlogic") { + line_ss >> tempDouble >> units; + if (units == "F") tempDouble = dF_TO_dC(tempDouble); + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + if (tempString == "topThird") { + setOfSources[heatsource].addTurnOnLogic(HPWH::topThird(tempDouble)); + } + else if (tempString == "bottomThird") { + setOfSources[heatsource].addTurnOnLogic(HPWH::bottomThird(tempDouble)); + } + else if (tempString == "standby") { + setOfSources[heatsource].addTurnOnLogic(HPWH::standby(tempDouble)); + } + else if (tempString == "bottomSixth") { + setOfSources[heatsource].addTurnOnLogic(HPWH::bottomSixth(tempDouble)); + } + else if (tempString == "secondSixth") { + setOfSources[heatsource].addTurnOnLogic(HPWH::secondSixth(tempDouble)); + } + else if (tempString == "thirdSixth") { + setOfSources[heatsource].addTurnOnLogic(HPWH::thirdSixth(tempDouble)); + } + else if (tempString == "fourthSixth") { + setOfSources[heatsource].addTurnOnLogic(HPWH::fourthSixth(tempDouble)); + } + else if (tempString == "fifthSixth") { + setOfSources[heatsource].addTurnOnLogic(HPWH::fifthSixth(tempDouble)); + } + else if (tempString == "topSixth") { + setOfSources[heatsource].addTurnOnLogic(HPWH::topSixth(tempDouble)); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + else if (token == "offlogic") { + line_ss >> tempDouble >> units; + if (units == "F") tempDouble = F_TO_C(tempDouble); + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + if (tempString == "topNodeMaxTemp") { + setOfSources[heatsource].addShutOffLogic(HPWH::topNodeMaxTemp(tempDouble)); + } + else if (tempString == "bottomNodeMaxTemp") { + setOfSources[heatsource].addShutOffLogic(HPWH::bottomNodeMaxTemp(tempDouble)); + } + else if (tempString == "bottomTwelthMaxTemp") { + setOfSources[heatsource].addShutOffLogic(HPWH::bottomTwelthMaxTemp(tempDouble)); + } + else if (tempString == "largeDraw") { + setOfSources[heatsource].addShutOffLogic(HPWH::largeDraw(tempDouble)); + } + else if (tempString == "largerDraw") { + setOfSources[heatsource].addShutOffLogic(HPWH::largerDraw(tempDouble)); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + } + else if (token == "type") { + line_ss >> tempString; + if (tempString == "resistor") { + setOfSources[heatsource].typeOfHeatSource = TYPE_resistance; + } + else if (tempString == "compressor") { + setOfSources[heatsource].typeOfHeatSource = TYPE_compressor; + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + else if (token == "coilConfig") { + line_ss >> tempString; + if (tempString == "wrapped") { + setOfSources[heatsource].configuration = HeatSource::CONFIG_WRAPPED; + } + else if (tempString == "submerged") { + setOfSources[heatsource].configuration = HeatSource::CONFIG_SUBMERGED; + } + else if (tempString == "external") { + setOfSources[heatsource].configuration = HeatSource::CONFIG_EXTERNAL; + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + else if (token == "condensity") { + line_ss >> dblArray[0] >> dblArray[1] >> dblArray[2] >> dblArray[3] >> dblArray[4] >> dblArray[5] >> dblArray[6] >> dblArray[7] >> dblArray[8] >> dblArray[9] >> dblArray[10] >> dblArray[11]; + setOfSources[heatsource].setCondensity(dblArray[0], dblArray[1], dblArray[2], dblArray[3], dblArray[4], dblArray[5], dblArray[6], dblArray[7], dblArray[8], dblArray[9], dblArray[10], dblArray[11]); + } + else if (token == "nTemps") { + line_ss >> nTemps; + setOfSources[heatsource].perfMap.resize(nTemps); + } + else if (std::regex_match(token, std::regex("T\\d+"))){ + std::smatch match; + std::regex_match(token, match, std::regex("T(\\d+)")); + nTemps = std::stoi(match[1].str()); + int maxTemps = setOfSources[heatsource].perfMap.size(); + + if (maxTemps < nTemps) { + if (maxTemps == 0) { + if (true || hpwhVerbosity >= VRB_reluctant){ + msg("%s specified for heatsource %d before definition of nTemps. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + else { + if (true || hpwhVerbosity >= VRB_reluctant){ + msg("Incorrect specification for %s from heatsource %d. nTemps, %d, is less than %d. \n", token.c_str(), heatsource, maxTemps, nTemps); + } + return HPWH_ABORT; + } + } + line_ss >> tempDouble >> units; + // if (units == "F") tempDouble = F_TO_C(tempDouble); + if (units == "F"); + // else if (units == "C") ; //do nothing, lol + else if (units == "C") tempDouble = C_TO_F(tempDouble); + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + setOfSources[heatsource].perfMap[nTemps - 1].T_F = tempDouble; + } + else if (std::regex_match(token, std::regex("(?:inPow|cop)T\\d+(?:const|lin|quad)"))){ + std::smatch match; + std::regex_match(token, match, std::regex("(inPow|cop)T(\\d+)(const|lin|quad)")); + string var = match[1].str(); + nTemps = std::stoi(match[2].str()); + string coeff = match[3].str(); + int coeff_num; + if (coeff == "const"){ + coeff_num = 0; + } + else if (coeff == "lin"){ + coeff_num = 1; + } + else if (coeff == "quad"){ + coeff_num = 2; + } + + int maxTemps = setOfSources[heatsource].perfMap.size(); + + if (maxTemps < nTemps) { + if (maxTemps == 0) { + if (hpwhVerbosity >= VRB_reluctant){ + msg("%s specified for heatsource %d before definition of nTemps. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + else { + if (hpwhVerbosity >= VRB_reluctant){ + msg("Incorrect specification for %s from heatsource %d. nTemps, %d, is less than %d. \n", token.c_str(), heatsource, maxTemps, nTemps); + } + return HPWH_ABORT; + } + } + line_ss >> tempDouble; + + if (var == "inPow") { + setOfSources[heatsource].perfMap[nTemps - 1].inputPower_coeffs[coeff_num] = tempDouble; + } + else if (var == "cop") { + setOfSources[heatsource].perfMap[nTemps - 1].COP_coeffs[coeff_num] = tempDouble; + } + } + else if (token == "hysteresis"){ + line_ss >> tempDouble >> units; + if (units == "F") tempDouble = dF_TO_dC(tempDouble); + else if (units == "C"); //do nothing, lol + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + setOfSources[heatsource].hysteresis_dC = tempDouble; + } + else if (token == "backupSource"){ + line_ss >> sourceNum; + setOfSources[heatsource].backupHeatSource = &setOfSources[sourceNum]; + } + else if (token == "companionSource"){ + line_ss >> sourceNum; + setOfSources[heatsource].companionHeatSource = &setOfSources[sourceNum]; + } + else if (token == "followedBySource"){ + line_ss >> sourceNum; + setOfSources[heatsource].followedByHeatSource = &setOfSources[sourceNum]; + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Improper specifier (%s) for heat source %d\n", token.c_str(), heatsource); + } + } + + } //end heatsource options + else { + msg("Improper keyword: %s \n", token.c_str()); + return HPWH_ABORT; + } + + } //end while over lines + + + //take care of the non-input processing + hpwhModel = MODELS_CustomFile; + + tankTemps_C = new double[numNodes]; + resetTankToSetpoint(); + + nextTankTemps_C = new double[numNodes]; + + isHeating = false; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].isOn) { + isHeating = true; + } + setOfSources[i].sortPerformanceMap(); + } + + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) { + return HPWH_ABORT; + } + simHasFailed = false; + return 0; +} +#endif + +int HPWH::HPWHinit_resTank(){ + //a default resistance tank, nominal 50 gallons, 0.95 EF, standard double 4.5 kW elements + return this->HPWHinit_resTank(GAL_TO_L(47.5), 0.95, 4500, 4500); +} +int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPower_W, double lowerPower_W){ + //low power element will cause divide by zero/negative UA in EF -> UA conversion + if (lowerPower_W < 550) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Resistance tank wattage below 550 W. DOES NOT COMPUTE\n"); + } + return HPWH_ABORT; + } + if (energyFactor <= 0) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Energy Factor less than zero. DOES NOT COMPUTE\n"); + } + return HPWH_ABORT; + } + + //use tank size setting function since it has bounds checking + int failure = this->setTankSize(tankVol_L); + if (failure == HPWH_ABORT) { + return failure; + } + + + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + nextTankTemps_C = new double[numNodes]; + + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 2; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + resistiveElementBottom.setupAsResistiveElement(0, lowerPower_W); + resistiveElementTop.setupAsResistiveElement(8, upperPower_W); + + //standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.isVIP = true; + + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + + // (1/EnFac + 1/RecovEff) / (67.5 * ((24/41094) - 1/(RecovEff * Power_btuperHr))) + double recoveryEfficiency = 0.98; + double numerator = (1.0 / energyFactor) - (1.0 / recoveryEfficiency); + double temp = 1.0 / (recoveryEfficiency * lowerPower_W*3.41443); + double denominator = 67.5 * ((24.0 / 41094.0) - temp); + tankUA_kJperHrC = UAf_TO_UAc(numerator / denominator); + + if (tankUA_kJperHrC < 0.) { + if (hpwhVerbosity >= VRB_reluctant && tankUA_kJperHrC < -0.1) { + msg("Computed tankUA_kJperHrC is less than 0, and is reset to 0."); + } + tankUA_kJperHrC = 0.0; + } + + hpwhModel = MODELS_CustomResTank; + + //calculate oft-used derived values + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) return HPWH_ABORT; + + isHeating = false; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].isOn) { + isHeating = true; + } + setOfSources[i].sortPerformanceMap(); + } + + + if (hpwhVerbosity >= VRB_emetic){ + for (int i = 0; i < numHeatSources; i++) { + msg("heat source %d: %p \n", i, &setOfSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + return 0; //successful init returns 0 +} + +int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C){ + //return 0 on success, HPWH_ABORT for failure + simHasFailed = true; //this gets cleared on successful completion of init + + //clear out old stuff if you're re-initializing + delete[] tankTemps_C; + delete[] setOfSources; + + + + + //except where noted, these values are taken from MODELS_GE2014STDMode on 5/17/16 + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + nextTankTemps_C = new double[numNodes]; + + //start tank off at setpoint + resetTankToSetpoint(); + + //custom settings - these are set later + //tankVolume_L = GAL_TO_L(45); + //tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(45.); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + //this is set customly, from input + //resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(19.6605))); + resistiveElementTop.addTurnOnLogic(HPWH::topThird(resUse_C)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); + + //custom adjustment for poorer performance + //compressor.addShutOffLogic(HPWH::lowT(F_TO_C(37))); + + + //end section of parameters from GE model + + + + + //set tank volume from input + //use tank size setting function since it has bounds checking + int failure = this->setTankSize(tankVol_L); + if (failure == HPWH_ABORT) { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Failure to set tank size in generic hpwh init."); + } + return failure; + } + + + // derive conservative (high) UA from tank volume + // curve fit by Jim Lutz, 5-25-2016 + double tankVol_gal = tankVol_L / GAL_TO_L(1.); + double v1 = 7.5156316175 * pow(tankVol_gal, 0.33) + 5.9995357658; + tankUA_kJperHrC = 0.0076183819 * v1 * v1; + + + + + //do a linear interpolation to scale COP curve constant, using measured values + // Chip's attempt 24-May-2014 + double uefSpan = 3.4 - 2.0; + + //force COP to be 70% of GE at UEF 2 and 95% at UEF 3.4 + //use a fudge factor to scale cop and input power in tandem to maintain constant capacity + double fUEF = (energyFactor - 2.0) / uefSpan; + double genericFudge = (1. - fUEF)*.7 + fUEF*.95; + + compressor.perfMap[0].COP_coeffs[0] *= genericFudge; + compressor.perfMap[0].COP_coeffs[1] *= genericFudge; + compressor.perfMap[0].COP_coeffs[2] *= genericFudge; + + compressor.perfMap[1].COP_coeffs[0] *= genericFudge; + compressor.perfMap[1].COP_coeffs[1] *= genericFudge; + compressor.perfMap[1].COP_coeffs[2] *= genericFudge; + + compressor.perfMap[0].inputPower_coeffs[0] /= genericFudge; + compressor.perfMap[0].inputPower_coeffs[1] /= genericFudge; + compressor.perfMap[0].inputPower_coeffs[2] /= genericFudge; + + compressor.perfMap[1].inputPower_coeffs[0] /= genericFudge; + compressor.perfMap[1].inputPower_coeffs[1] /= genericFudge; + compressor.perfMap[1].inputPower_coeffs[2] /= genericFudge; + + + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + + + //standard finishing up init, borrowed from init function + + hpwhModel = MODELS_genericCustomUEF; + + //calculate oft-used derived values + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) { + return HPWH_ABORT; + } + + isHeating = false; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].isOn) { + isHeating = true; + } + setOfSources[i].sortPerformanceMap(); + } + + if (hpwhVerbosity >= VRB_emetic){ + for (int i = 0; i < numHeatSources; i++) { + msg("heat source %d: %p \n", i, &setOfSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + + return 0; +} + + + + +int HPWH::HPWHinit_presets(MODELS presetNum) { + //return 0 on success, HPWH_ABORT for failure + simHasFailed = true; //this gets cleared on successful completion of init + + //clear out old stuff if you're re-initializing + delete[] tankTemps_C; + delete[] setOfSources; + + + //resistive with no UA losses for testing + if (presetNum == MODELS_restankNoUA) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 0; //0 to turn off + + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 2; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(8, 4500); + + //standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.isVIP = true; + + //assign heat sources into array in order of priority + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + } + + //resistive tank with massive UA loss for testing + else if (presetNum == MODELS_restankHugeUA) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = 50; + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 120; + tankUA_kJperHrC = 500; //0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + + + numHeatSources = 2; + setOfSources = new HeatSource[numHeatSources]; + + //set up a resistive element at the bottom, 4500 kW + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(9, 4500); + + //standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); + resistiveElementTop.isVIP = true; + + + //assign heat sources into array in order of priority + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + + } + + //realistic resistive tank + else if (presetNum == MODELS_restankRealistic) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 10; //0 to turn off + + doTempDepression = false; + //should eventually put tankmixes to true when testing progresses + tankMixesOnDraw = false; + + numHeatSources = 2; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(9, 4500); + + //standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); + resistiveElementTop.isVIP = true; + + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + } + + else if (presetNum == MODELS_StorageTank) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(80); + tankUA_kJperHrC = 10; //0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + + //////////////////////////////////////////////////// + numHeatSources = 1; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource extra(this); + + //compressor values + extra.isOn = false; + extra.isVIP = false; + extra.typeOfHeatSource = TYPE_extra; + extra.configuration = HeatSource::CONFIG_WRAPPED; + + extra.addTurnOnLogic(HPWH::bottomThird(-200)); + extra.hysteresis_dC = 0.; + extra.minT = 0; + extra.maxT = 200; + + //initial guess, will get reset based on the input heat vector + extra.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + + setOfSources[0] = extra; + } + + //basic compressor tank for testing + else if (presetNum == MODELS_basicIntegrated) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = 50; + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 120; + tankUA_kJperHrC = 10; //0 to turn off + //tankUA_kJperHrC = 0; //0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + HeatSource compressor(this); + + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(9, 4500); + + resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); + + //standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); + resistiveElementTop.isVIP = true; + + + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double oneSixth = 1.0 / 6.0; + compressor.setCondensity(oneSixth, oneSixth, oneSixth, oneSixth, oneSixth, oneSixth, 0, 0, 0, 0, 0, 0); + + //GE tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {0.290 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {4.49, -0.0187, -0.0000133} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {0.375 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {5.60, -0.0252, 0.00000254} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = 0; + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(4); + compressor.configuration = HeatSource::CONFIG_WRAPPED; //wrapped around tank + + compressor.addTurnOnLogic(HPWH::bottomThird(20)); + compressor.addTurnOnLogic(HPWH::standby(15)); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = compressor; + setOfSources[2] = resistiveElementBottom; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + + //simple external style for testing + else if (presetNum == MODELS_externalTest) { + numNodes = 96; + tankTemps_C = new double[numNodes]; + setpoint_C = 50; + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 120; + //tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 0; //0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + + numHeatSources = 1; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + //GE tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {0.290 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {4.49, -0.0187, -0.0000133} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {0.375 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {5.60, -0.0252, 0.00000254} // COP Coefficients (COP_coeffs) + }); + + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = 0; //no hysteresis + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + + compressor.addTurnOnLogic(HPWH::bottomThird(20)); + compressor.addTurnOnLogic(HPWH::standby(15)); + + //lowT cutoff + compressor.addShutOffLogic(HPWH::bottomNodeMaxTemp(20)); + + + //set everything in its places + setOfSources[0] = compressor; + } + //voltex 60 gallon + else if (presetNum == MODELS_AOSmithPHPT60) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 215.8; + tankUA_kJperHrC = 7.31; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 5.0; + compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {0.467 * 1000, 0.00281 * 1000, 0.0000072 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {4.86, -0.0222, -0.00001} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {0.541 * 1000, 0.00147 * 1000, 0.0000176 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {6.58, -0.0392, 0.0000407} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(45.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(4); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4250); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 2000); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); + + //logic conditions + double compStart = dF_TO_dC(43.6); + double standbyT = dF_TO_dC(23.8); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(compStart)); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(25.0))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = compressor; + setOfSources[2] = resistiveElementBottom; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_AOSmithPHPT80) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 283.9; + tankUA_kJperHrC = 8.8; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 5.0; + compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {0.467 * 1000, 0.00281 * 1000, 0.0000072 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {4.86, -0.0222, -0.00001} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {0.541 * 1000, 0.00147 * 1000, 0.0000176 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {6.58, -0.0392, 0.0000407} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(45.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(4); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4250); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 2000); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); + + + //logic conditions + double compStart = dF_TO_dC(43.6); + double standbyT = dF_TO_dC(23.8); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(compStart)); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(25.0))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = compressor; + setOfSources[2] = resistiveElementBottom; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_GE2012) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 172; + tankUA_kJperHrC = 6.8; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 5.0; + compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {0.3 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {4.7, -0.0210, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {0.378 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) + {4.8, -0.0167, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(45.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(4); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4200); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4200); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); + + + //logic conditions + // double compStart = dF_TO_dC(24.4); + double compStart = dF_TO_dC(40.0); + double standbyT = dF_TO_dC(5.2); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + // compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(66))); + compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(compStart)); + //resistiveElementBottom.addShutOffLogic(HPWH::lowTreheat(lowTcutoff)); + //GE element never turns off? + + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31.0))); + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28.0))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = compressor; + setOfSources[2] = resistiveElementBottom; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_Sanden80) { + numNodes = 96; + tankTemps_C = new double[numNodes]; + setpoint_C = 65; + setpointFixed = true; + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 315; + //tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 7; + + doTempDepression = false; + tankMixesOnDraw = false; + + numHeatSources = 1; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(5); + + compressor.perfMap.push_back({ + 17, // Temperature (T_F) + {1650, 5.5, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {3.2, -0.015, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 35, // Temperature (T_F) + {1100, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {3.7, -0.015, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {880, 3.1, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.25, -0.025, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {740, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {6.2, -0.03, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {790, 2, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.15, -0.04, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.hysteresis_dC = 4; + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + + + std::vector nodeWeights; + nodeWeights.emplace_back(8); + compressor.addTurnOnLogic(HPWH::HeatingLogic("eighth node absolute", nodeWeights, F_TO_C(113), true)); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); + + //lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic(HPWH::HeatingLogic("bottom node absolute", nodeWeights1, F_TO_C(135), true, std::greater())); + compressor.depressesTemperature = false; //no temp depression + + //set everything in its places + setOfSources[0] = compressor; + } + else if (presetNum == MODELS_Sanden40) { + numNodes = 96; + tankTemps_C = new double[numNodes]; + setpoint_C = 65; + setpointFixed = true; + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 160; + //tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 5; + + doTempDepression = false; + tankMixesOnDraw = false; + + numHeatSources = 1; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(5); + + compressor.perfMap.push_back({ + 17, // Temperature (T_F) + {1650, 5.5, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {3.2, -0.015, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 35, // Temperature (T_F) + {1100, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {3.7, -0.015, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {880, 3.1, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.25, -0.025, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {740, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {6.2, -0.03, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {790, 2, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.15, -0.04, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.hysteresis_dC = 4; + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + + + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(HPWH::HeatingLogic("fourth node absolute", nodeWeights, F_TO_C(113), true)); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); + + //lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic(HPWH::HeatingLogic("bottom node absolute", nodeWeights1, F_TO_C(135), true, std::greater())); + compressor.depressesTemperature = false; //no temp depression + + //set everything in its places + setOfSources[0] = compressor; + } + else if (presetNum == MODELS_AOSmithHPTU50 || presetNum == MODELS_RheemHBDR2250 || presetNum == MODELS_RheemHBDR4550) { + numNodes = 24; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 171; + tankUA_kJperHrC = 6; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 5.0; + compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); + + // performance map + compressor.perfMap.reserve(3); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(42.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + if (presetNum == MODELS_RheemHBDR2250) { + resistiveElementTop.setupAsResistiveElement(8, 2250); + } else { + resistiveElementTop.setupAsResistiveElement(8, 4500); + } + resistiveElementTop.isVIP = true; + + //bottom resistor values + if (presetNum == MODELS_RheemHBDR2250) { + resistiveElementBottom.setupAsResistiveElement(0, 2250); + } else { + resistiveElementBottom.setupAsResistiveElement(0, 4500); + } + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + double compStart = dF_TO_dC(35); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); + + + std::vector nodeWeights; + nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); + resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); +// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + setOfSources[0].companionHeatSource = &setOfSources[2]; + + + } + else if (presetNum == MODELS_AOSmithHPTU66 || presetNum == MODELS_RheemHBDR2265 || presetNum == MODELS_RheemHBDR4565) { + numNodes = 24; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + if (presetNum == MODELS_AOSmithHPTU66) { + tankVolume_L = 244.6; + } else { + tankVolume_L = 221.4; + } + tankUA_kJperHrC = 8; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + // performance map + compressor.perfMap.reserve(3); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(42.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + if (presetNum == MODELS_RheemHBDR2265) { + resistiveElementTop.setupAsResistiveElement(8, 2250); + } else { + resistiveElementTop.setupAsResistiveElement(8, 4500); + } + resistiveElementTop.isVIP = true; + + //bottom resistor values + if (presetNum == MODELS_RheemHBDR2265) { + resistiveElementBottom.setupAsResistiveElement(0, 2250); + } else { + resistiveElementBottom.setupAsResistiveElement(0, 4500); + } + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + double compStart = dF_TO_dC(35); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); + + std::vector nodeWeights; + nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); + resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); +// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + setOfSources[0].companionHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_AOSmithHPTU80 || presetNum == MODELS_RheemHBDR2280 || presetNum == MODELS_RheemHBDR4580) { + numNodes = 24; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 299.5; + tankUA_kJperHrC = 9; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; + + double split = 1.0 / 3.0; + compressor.setCondensity(split, split, split, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(3); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(42.0); + compressor.maxT = F_TO_C(120.0); + compressor.hysteresis_dC = dF_TO_dC(1); + + //top resistor values + if (presetNum == MODELS_RheemHBDR2280) { + resistiveElementTop.setupAsResistiveElement(8, 2250); + } else { + resistiveElementTop.setupAsResistiveElement(8, 4500); + } + resistiveElementTop.isVIP = true; + + //bottom resistor values + if (presetNum == MODELS_RheemHBDR2280) { + resistiveElementBottom.setupAsResistiveElement(0, 2250); + } else { + resistiveElementBottom.setupAsResistiveElement(0, 4500); + } + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + double compStart = dF_TO_dC(35); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); + + std::vector nodeWeights; +// nodeWeights.emplace_back(9); nodeWeights.emplace_back(10); + nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); + resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); +// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + setOfSources[0].companionHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_AOSmithHPTU80_DR) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = 283.9; + tankUA_kJperHrC = 9; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {142.6, 2.152, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {6.989258, -0.038320, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {120.14, 2.513, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {8.188, -0.0432, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(42.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + double compStart = dF_TO_dC(34.1636); + double standbyT = dF_TO_dC(7.1528); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80.108))); + + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(39.9691))); + resistiveElementTop.addTurnOnLogic(HPWH::topThird_absolute(F_TO_C(87))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_GE2014STDMode) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(19.6605))); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); + // compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_GE2014STDMode_80) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(19.6605))); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); + compressor.minT = F_TO_C(37); + // compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_GE2014) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.addShutOffLogic(HPWH::topNodeMaxTemp(F_TO_C(116.6358))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(11.0648))); + // compressor.addShutOffLogic(HPWH::largerDraw(F_TO_C(62.4074))); + + resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_GE2014_80) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.addShutOffLogic(HPWH::topNodeMaxTemp(F_TO_C(116.6358))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(11.0648))); + // compressor.addShutOffLogic(HPWH::largerDraw(F_TO_C(62.4074))); + + resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_GE2014_80DR) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.addTurnOnLogic(HPWH::topThird_absolute(F_TO_C(87))); + // resistiveElementTop.addShutOffLogic(HPWH::topNodeMaxTemp(F_TO_C(116.6358))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(11.0648))); + // compressor.addShutOffLogic(HPWH::largerDraw(F_TO_C(62.4074))); + + resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); + // resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_RheemHB50) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 7; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 47, // Temperature (T_F) + {280, 4.97342, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.634009, -0.029485, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {280, 5.35992, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {6.3, -0.03, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.hysteresis_dC = dF_TO_dC(1); + compressor.minT = F_TO_C(40.0); + compressor.maxT = F_TO_C(120.0); + + compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4200); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 2250); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + double compStart = dF_TO_dC(38); + double standbyT = dF_TO_dC(13.2639); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(76.7747))); + + resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); + + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_Stiebel220E) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(56); + //tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 9; + + doTempDepression = false; + tankMixesOnDraw = false; + + numHeatSources = 2; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElement(this); + + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + resistiveElement.setupAsResistiveElement(0, 1500); + resistiveElement.hysteresis_dC = dF_TO_dC(0); + + compressor.setCondensity(0, 0.12, 0.22, 0.22, 0.22, 0.22, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {295.55337, 2.28518, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.744118, -0.025946, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {282.2126, 2.82001, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {8.012112, -0.039394, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(32.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = 0; //no hysteresis + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + compressor.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(6.5509))); + compressor.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); + + compressor.depressesTemperature = false; //no temp depression + + //set everything in its places + setOfSources[0] = compressor; + setOfSources[1] = resistiveElement; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[0].backupHeatSource = &setOfSources[1]; + + } + else if (presetNum == MODELS_Generic1) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 9; + doTempDepression = false; + tankMixesOnDraw = true; + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {472.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {2.942642, -0.0125954, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {439.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {3.95076, -0.01638033, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(45.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40.0))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); + compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(80))); + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(110))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_Generic2) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 7.5; + doTempDepression = false; + tankMixesOnDraw = true; + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {272.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {4.042642, -0.0205954, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {239.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.25076, -0.02638033, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(40.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); + compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(60))); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(80))); + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(40))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_Generic3) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 5; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + //voltex60 tier 1 values + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {172.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.242642, -0.0285954, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {139.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {6.75076, -0.03638033, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(35.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementBottom.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); + compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(55))); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(60))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(40))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = compressor; + setOfSources[2] = resistiveElementBottom; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + else if (presetNum == MODELS_UEF2generic) { + numNodes = 12; + tankTemps_C = new double[numNodes]; + setpoint_C = F_TO_C(127.0); + + //start tank off at setpoint + resetTankToSetpoint(); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + numHeatSources = 3; + setOfSources = new HeatSource[numHeatSources]; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + //compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + double split = 1.0 / 4.0; + compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {4.29, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 70, // Temperature (T_F) + {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.61, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + + //top resistor values + resistiveElementTop.setupAsResistiveElement(6, 4500); + resistiveElementTop.isVIP = true; + + //bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4000); + resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + //logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(18.6605))); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); + + compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); + + //set everything in its places + setOfSources[0] = resistiveElementTop; + setOfSources[1] = resistiveElementBottom; + setOfSources[2] = compressor; + + //and you have to do this after putting them into setOfSources, otherwise + //you don't get the right pointers + setOfSources[2].backupHeatSource = &setOfSources[1]; + setOfSources[1].backupHeatSource = &setOfSources[2]; + + setOfSources[0].followedByHeatSource = &setOfSources[1]; + setOfSources[1].followedByHeatSource = &setOfSources[2]; + + } + + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have tried to select a preset model which does not exist. \n"); + } + return HPWH_ABORT; + } + + // initialize nextTankTemps_C + nextTankTemps_C = new double[numNodes]; + + hpwhModel = presetNum; + + //calculate oft-used derived values + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) { + return HPWH_ABORT; + } + + isHeating = false; + for (int i = 0; i < numHeatSources; i++) { + if (setOfSources[i].isOn) { + isHeating = true; + } + setOfSources[i].sortPerformanceMap(); + } + + if (hpwhVerbosity >= VRB_emetic){ + for (int i = 0; i < numHeatSources; i++) { + msg("heat source %d: %p \n", i, &setOfSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + return 0; //successful init returns 0 +} //end HPWHinit_presets diff --git a/HPWH.in.hh b/HPWH.in.hh new file mode 100644 index 00000000..b3782ac6 --- /dev/null +++ b/HPWH.in.hh @@ -0,0 +1,740 @@ +#ifndef HPWH_hh +#define HPWH_hh + +#include +#include +#include +#include +#include +#include + +#include +#include //for exit +#include + +//#define HPWH_ABRIDGED +/**< If HPWH_ABRIDGED is defined, then some function definitions will be + * excluded from compiling. This is done in order to reduce the size of the + * final compiled code. */ + +#define HPWHVRSN_MAJOR @HPWHsim_VRSN_MAJOR@ +#define HPWHVRSN_MINOR @HPWHsim_VRSN_MINOR@ +#define HPWHVRSN_PATCH @HPWHsim_VRSN_PATCH@ +#define HPWHVRSN_META "@HPWHsim_VRSN_META@" + +class HPWH { + public: + static const int version_major = HPWHVRSN_MAJOR; + static const int version_minor = HPWHVRSN_MINOR; + static const int version_patch = HPWHVRSN_PATCH; + static const std::string version_maint; // Initialized in source file (HPWH.cc) + + + static const float DENSITYWATER_kgperL; + static const float KWATER_WpermC; + static const float CPWATER_kJperkgC; + static const int CONDENSITY_SIZE = 12; /**< this must be an integer, and only the value 12 + //change at your own risk */ + static const int MAXOUTSTRING = 200; /**< this is the maximum length for a debuging output string */ + static const float HEATDIST_MINVALUE; /**< any amount of heat distribution less than this is reduced to 0 + //this saves on computations */ + static const float UNINITIALIZED_LOCATIONTEMP; /**< this is used to tell the + simulation when the location temperature has not been initialized */ + + HPWH(); /**< default constructor */ + HPWH(const HPWH &hpwh); /**< copy constructor */ + HPWH & operator=(const HPWH &hpwh); /**< assignment operator */ + ~HPWH(); /**< destructor just a couple dynamic arrays to destroy - could be replaced by vectors eventually? */ + + ///specifies the various modes for the Demand Response (DR) abilities + ///values may vary - names should be used + enum DRMODES{ + DR_BLOCK = 0, /**= comparisons, so the numerical order is relevant + enum VERBOSITY { + VRB_silent = 0, /**< print no outputs */ + VRB_reluctant = 10, /**< print only outputs for fatal errors */ + VRB_minuteOut = 15, /**< print minutely output */ + VRB_typical = 20, /**< print some basic debugging info */ + VRB_emetic = 30 /**< print all the things */ + }; + + + enum UNITS{ + UNITS_C, /**< celsius */ + UNITS_F, /**< fahrenheit */ + UNITS_KWH, /**< kilowatt hours */ + UNITS_BTU, /**< british thermal units */ + UNITS_KJ, /**< kilojoules */ + UNITS_GAL, /**< gallons */ + UNITS_L, /**< liters */ + UNITS_kJperHrC, /**< UA, metric units */ + UNITS_BTUperHrF, /**< UA, imperial units */ + UNITS_FT, /**< feet */ + UNITS_M, /**< meters */ + UNITS_FT2, /**< square feet */ + UNITS_M2, /**< square meters */ + }; + + /** specifies the type of heat source */ + enum HEATSOURCE_TYPE { + TYPE_none, /**< a default to check to make sure it's been set */ + TYPE_resistance, /**< a resistance element */ + TYPE_compressor, /**< a vapor cycle compressor */ + TYPE_extra /**< an extra element to add user defined heat*/ + }; + + /** specifies the unit type for outputs in the CSV file-s */ + enum CSVOPTIONS { + CSVOPT_NONE, + CSVOPT_IPUNITS + }; + + struct NodeWeight { + int nodeNum; + double weight; + NodeWeight(int n, double w) : nodeNum(n), weight(w) {}; + + NodeWeight(int n) : nodeNum(n), weight(1.0) {}; + }; + + struct HeatingLogic { + std::string description; // for debug purposes + std::vector nodeWeights; + double decisionPoint; + bool isAbsolute; + std::function compare; + HeatingLogic(std::string desc, std::vector n, double d, bool a=false, std::function c=std::less()) : + description(desc), nodeWeights(n), decisionPoint(d), isAbsolute(a), compare(c) {}; + }; + + HeatingLogic topThird(double d) const; + HeatingLogic topThird_absolute(double d) const; + HeatingLogic bottomThird(double d) const; + HeatingLogic bottomHalf(double d) const; + HeatingLogic bottomTwelth(double d) const; + HeatingLogic bottomSixth(double d) const; + HeatingLogic secondSixth(double d) const; + HeatingLogic thirdSixth(double d) const; + HeatingLogic fourthSixth(double d) const; + HeatingLogic fifthSixth(double d) const; + HeatingLogic topSixth(double d) const; + HeatingLogic standby(double d) const; + HeatingLogic topNodeMaxTemp(double d) const; + HeatingLogic bottomNodeMaxTemp(double d) const; + HeatingLogic bottomTwelthMaxTemp(double d) const; + HeatingLogic largeDraw(double d) const; + HeatingLogic largerDraw(double d) const; + + + ///this is the value that the public functions will return in case of a simulation + ///destroying error + static const int HPWH_ABORT = -274000; + + static std::string getVersion(); + /**< This function returns a string with the current version number */ + + int HPWHinit_presets(MODELS presetNum); + /**< This function will load in a set of parameters that are hardcoded in this function - + * which particular set of parameters is selected by presetNum. + * This is similar to the way the HPWHsim currently operates, as used in SEEM, + * but not quite as versatile. + * My impression is that this could be a useful input paradigm for CSE + * + * The return value is 0 for successful initialization, HPWH_ABORT otherwise + */ + + int HPWHinit_file(std::string configFile); + /**< This function will load in a set of parameters from a file + * The file name is the input - there should be at most one set of parameters per file + * This is useful for testing new variations, and for the sort of variability + * that we typically do when creating SEEM runs + * Appropriate use of this function can be found in the documentation + * + * + * The return value is 0 for successful initialization, HPWH_ABORT otherwise + */ + + int HPWHinit_resTank(); /**< Default resistance tank, EF 0.95, volume 47.5 */ + int HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPower_W, double lowerPower_W); + /**< This function will initialize a HPWH object to be a resistance tank. Since + * resistance tanks are so simple, they can be specified with only four variables: + * tank volume, energy factor, and the power of the upper and lower elements. Energy + * factor is converted into UA internally, although an external setter for UA is + * also provided in case the energy factor is unknown. + * + * Several assumptions regarding the tank configuration are assumed: the lower element + * is at the bottom, the upper element is at the top third. The logics are also set + * to standard setting, with upper as VIP activating when the top third is too cold. + */ + + int HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C); + /**< This function will initialize a HPWH object to be a non-specific HPWH model + * with an energy factor as specified. Since energy + * factor is not strongly correlated with energy use, most settings + * are taken from the GE2015_STDMode model. + */ + + int runOneStep(double inletT_C, double drawVolume_L, + double ambientT_C, double externalT_C, + DRMODES DRstatus, double minutesPerStep, + double inletVol2_L = 0., double inletT2_C = 0., + std::vector* nodeExtraHeat_W = NULL); + /**< This function will progress the simulation forward in time by one step + * all calculated outputs are stored in private variables and accessed through functions + * + * The return value is 0 for successful simulation run, HPWH_ABORT otherwise + */ + + int runNSteps(int N, double *inletT_C, double *drawVolume_L, + double *tankAmbientT_C, double *heatSourceAmbientT_C, + DRMODES *DRstatus, double minutesPerStep); + /**< This function will progress the simulation forward in time by N (equal) steps + * The calculated values will be summed or averaged, as appropriate, and + * then stored in the usual variables to be accessed through functions + * + * The return value is 0 for successful simulation run, HPWH_ABORT otherwise + */ + + + void setVerbosity(VERBOSITY hpwhVrb); + /**< sets the verbosity to the specified level */ + void setMessageCallback( void (*callbackFunc)(const std::string message, void* pContext), void* pContext); + /**< sets the function to be used for message passing */ + void printHeatSourceInfo(); + /**< this prints out the heat source info, nicely formatted + specifically input/output energy/power, and runtime + will print to cout if messageCallback pointer is unspecified + does not use verbosity, as it is public and expected to be called only when needed */ + void printTankTemps(); + /**< this prints out all the node temps, kind of nicely formatted + does not use verbosity, as it is public and expected to be called only when needed */ + int WriteCSVHeading(FILE* outFILE, const char* preamble = "", int nTCouples = 6, int options = CSVOPT_NONE) const; + int WriteCSVRow(FILE* outFILE, const char* preamble = "", int nTCouples = 6, int options = CSVOPT_NONE) const; + /**< a couple of function to write the outputs to a file + they both will return 0 for success + the preamble should be supplied with a trailing comma, as these functions do + not add one. Additionally, a newline is written with each call. */ + + + + bool isSetpointFixed(); /**< is the setpoint allowed to be changed */ + int setSetpoint(double newSetpoint, UNITS units = UNITS_C);/** nTCouple, or incorrect units */ + + int getNumHeatSources() const; + /**< returns the number of heat sources */ + double getNthHeatSourceEnergyInput(int N, UNITS units = UNITS_KWH) const; + /**< returns the energy input to the Nth heat source, with the specified units + energy used by the heat source is positive - should always be positive + returns HPWH_ABORT for N out of bounds or incorrect units */ + + double getNthHeatSourceEnergyOutput(int N, UNITS units = UNITS_KWH) const; + /**< returns the energy output from the Nth heat source, with the specified units + energy put into the water is positive - should always be positive + returns HPWH_ABORT for N out of bounds or incorrect units */ + double getNthHeatSourceRunTime(int N) const; + /**< returns the run time for the Nth heat source, in minutes + note: they may sum to more than 1 time step for concurrently running heat sources + returns HPWH_ABORT for N out of bounds */ + int isNthHeatSourceRunning(int N) const; + /**< returns 1 if the Nth heat source is currently engaged, 0 if it is not, and + returns HPWH_ABORT for N out of bounds */ + HEATSOURCE_TYPE getNthHeatSourceType(int N) const; + /**< returns the enum value for what type of heat source the Nth heat source is */ + + + double getOutletTemp(UNITS units = UNITS_C) const; + /**< returns the outlet temperature in the specified units + returns 0 when no draw occurs, or HPWH_ABORT for incorrect unit specifier */ + + double getEnergyRemovedFromEnvironment(UNITS units = UNITS_KWH) const; + /**< get the total energy removed from the environment by all heat sources in specified units + (not net energy - does not include standby) + moving heat from the space to the water is the positive direction + returns HPWH_ABORT for incorrect units */ + + double getStandbyLosses(UNITS units = UNITS_KWH) const; + /**< get the amount of heat lost through the tank in specified units + moving heat from the water to the space is the positive direction + negative should occur seldom + returns HPWH_ABORT for incorrect units */ + + double getTankHeatContent_kJ() const; + /**< get the heat content of the tank, relative to zero celsius + * returns using kilojoules */ + + /** An overloaded function that uses some member variables, instead of taking them as inputs */ + int runOneStep(double drawVolume_L, double ambientT_C, + double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0., + std::vector* nodeExtraHeat_W = NULL) { + return runOneStep(member_inletT_C, drawVolume_L, ambientT_C, + externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C, + nodeExtraHeat_W); + }; + + + /** Setters for the what are typically input variables */ + void setInletT(double newInletT_C) {member_inletT_C = newInletT_C;}; + void setMinutesPerStep(double newMinutesPerStep) {member_minutesPerStep = newMinutesPerStep;}; + + double getLocationTemp_C() const; + int setMaxTempDepression(double maxDepression, UNITS units = UNITS_C); + + + private: + class HeatSource; + + void updateTankTemps(double draw, double inletT, double ambientT, double minutesPerStep, double inletVol2_L, double inletT2_L); + void mixTankInversions(); + /**< Mixes the any temperature inversions in the tank after all the temperature calculations */ + bool areAllHeatSourcesOff() const; + /**< test if all the heat sources are off */ + void turnAllHeatSourcesOff(); + /**< disengage each heat source */ + + void addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesPerStep); + /**< adds extra heat defined by the user. Where nodeExtraHeat[] is a vector of heat quantities to be added during the step. nodeExtraHeat[ 0] would go to bottom node, 1 to next etc. */ + + double tankAvg_C(const std::vector nodeWeights) const; + + /**< functions to calculate what the temperature in a portion of the tank is */ + + void calcDerivedValues(); + /**< a helper function for the inits */ + void calcDerivedHeatingValues(); + /**< a helper for the helper, calculating condentropy and the lowest node*/ + + int checkInputs(); + /**< a helper function to run a few checks on the HPWH input parameters */ + + + void sayMessage(const std::string message) const; + /**< if the messagePriority is >= the hpwh verbosity, + either pass your message out to the callback function or print it to cout + otherwise do nothing */ + void msg( const char* fmt, ...) const; + void msgV( const char* fmt, va_list ap=NULL) const; + + bool simHasFailed; + /**< did an internal error cause the simulation to fail? */ + + bool isHeating; + /**< is the hpwh currently heating or not? */ + + bool setpointFixed; + /**< does the HPWH allow the setpoint to vary */ + + VERBOSITY hpwhVerbosity; + /**< an enum to let the sim know how much output to say */ + + void (*messageCallback)(const std::string message, void* contextPtr); + /**< function pointer to indicate an external message processing function */ + void* messageCallbackContextPtr; + /**< caller context pointer for external message processing */ + + + MODELS hpwhModel; + /**< The hpwh should know which preset initialized it, or if it was from a file */ + + int numHeatSources; + /**< how many heat sources this HPWH has */ + HeatSource *setOfSources; + /**< an array containing the HeatSources, in order of priority */ + + int compressorIndex; + /**< The index of the compressor heat source (set to -1 if no compressor)*/ + + int lowestElementIndex; + /**< The index of the lowest resistance element heat source (set to -1 if no resistance elements)*/ + + int numNodes; + /**< the number of nodes in the tank - must be >= 12, in multiples of 12 */ + + int nodeDensity; + /**< the number of calculation nodes in a logical node */ + + int inletHeight; + /**< the number of a node in the tank that the inlet water enters the tank at, must be between 0 and numNodes-1 */ + + int inlet2Height; + /**< the number of a node in the tank that the 2nd inlet water enters the tank at, must be between 0 and numNodes-1 */ + + double tankVolume_L; + /**< the volume in liters of the tank */ + double tankUA_kJperHrC; + /**< the UA of the tank, in metric units */ + + double setpoint_C; + /**< the setpoint of the tank */ + double *tankTemps_C; + /**< an array holding the temperature of each node - 0 is the bottom node, numNodes is the top */ + double *nextTankTemps_C; + /**< an array holding the future temperature of each node for the conduction calculation - 0 is the bottom node, numNodes is the top */ + + + // Some outputs + double outletTemp_C; + /**< the temperature of the outlet water - taken from top of tank, 0 if no flow */ + double energyRemovedFromEnvironment_kWh; + /**< the total energy removed from the environment, to heat the water */ + double standbyLosses_kWh; + /**< the amount of heat lost to standby */ + + + // special variables for adding abilities + bool tankMixesOnDraw; + /**< whether or not the bottom third of the tank should mix during draws */ + bool doTempDepression; + /**< whether the HPWH should use the alternate ambient temperature that + gets depressed when a compressor is running + NOTE: this only works for 1 minute steps */ + double locationTemperature_C; + /**< this is the special location temperature that stands in for the the + ambient temperature if you are doing temp. depression */ + double maxDepression_C = 2.5; + /** a couple variables to hold values which are typically inputs */ + double member_inletT_C; + double member_minutesPerStep; + + bool doInversionMixing; + /**< If and only if true will model temperature inversion mixing in the tank */ + + bool doConduction; + /**< If and only if true will model conduction between the internal nodes of the tank */ + +}; //end of HPWH class + + + + +class HPWH::HeatSource { + public: + friend class HPWH; + + HeatSource(){} /**< default constructor, does not create a useful HeatSource */ + HeatSource(HPWH *parentHPWH); + /**< constructor assigns a pointer to the hpwh that owns this heat source */ + HeatSource(const HeatSource &hSource); ///copy constructor + HeatSource& operator=(const HeatSource &hSource); ///assignment operator + /**< the copy constructor and assignment operator basically just checks if there + are backup/companion pointers - these can't be copied */ + + void setupAsResistiveElement(int node, double Watts); + /**< configure the heat source to be a resisive element, positioned at the + specified node, with the specified power in watts */ + void setupExtraHeat(std::vector* nodeExtraHeat_W); + /**< Configure a user defined heat source added as extra, based off using + nodeExtraHeat_W as the total watt input and the condensity*/ + + bool isEngaged() const; + /**< return whether or not the heat source is engaged */ + void engageHeatSource(); + /**< turn heat source on, i.e. set isEngaged to TRUE */ + void disengageHeatSource(); + /**< turn heat source off, i.e. set isEngaged to FALSE */ + + bool isLockedOut() const; + /**< return whether or not the heat source is locked out */ + void lockOutHeatSource(); + /**< lockout heat source, i.e. set isLockedOut to TRUE */ + void unlockHeatSource(); + /**< unlock heat source, i.e. set isLockedOut to FALSE */ + + bool shouldLockOut(double heatSourceAmbientT_C) const; + /**< queries the heat source as to whether it should lock out */ + bool shouldUnlock(double heatSourceAmbientT_C) const; + /**< queries the heat source as to whether it should unlock */ + bool shouldHeat() const; + /**< queries the heat source as to whether or not it should turn on */ + bool shutsOff() const; + /**< queries the heat source whether should shut off */ + + int findParent() const; + /**< returns the index of the heat source where this heat source is a backup. + returns -1 if none found. */ + + void addHeat_temp(double externalT_C, double minutesPerStep); + void addHeat(double externalT_C, double minutesToRun); + /**< adds heat to the hpwh - this is the function that interprets the + various configurations (internal/external, resistance/heat pump) to add heat */ + + void setCondensity(double cnd1, double cnd2, double cnd3, double cnd4, + double cnd5, double cnd6, double cnd7, double cnd8, + double cnd9, double cnd10, double cnd11, double cnd12); + /**< a function to set the condensity values, it pretties up the init funcs. */ + + + private: + //start with a few type definitions + enum COIL_CONFIG { + CONFIG_SUBMERGED, + CONFIG_WRAPPED, + CONFIG_EXTERNAL + }; + + /** the creator of the heat source, necessary to access HPWH variables */ + HPWH *hpwh; + + // these are the heat source state/output variables + bool isOn; + /**< is the heat source running or not */ + + bool lockedOut; + /**< is the heat source locked out */ + + // some outputs + double runtime_min; + /**< this is the percentage of the step that the heat source was running */ + double energyInput_kWh; + /**< the energy used by the heat source */ + double energyOutput_kWh; + /**< the energy put into the water by the heat source */ + +// these are the heat source property variables + bool isVIP; + /**< is this heat source a high priority heat source? (e.g. upper resisitor) */ + HeatSource* backupHeatSource; + /**< a pointer to the heat source which serves as backup to this one + should be NULL if no backup exists */ + HeatSource* companionHeatSource; + /**< a pointer to the heat source which will run concurrently with this one + it still will only turn on if shutsOff is false */ + + HeatSource* followedByHeatSource; + /**< a pointer to the heat source which will attempt to run after this one */ + + double condensity[CONDENSITY_SIZE]; + /**< The condensity function is always composed of 12 nodes. + It represents the location within the tank where heat will be distributed, + and it also is used to calculate the condenser temperature for inputPower/COP calcs. + It is conceptually linked to the way condenser coils are wrapped around + (or within) the tank, however a resistance heat source can also be simulated + by specifying the entire condensity in one node. */ + double shrinkage; + /**< the shrinkage is a derived value, using parameters alpha, beta, + and the condentropy, which is derived from the condensity + alpha and beta are not intended to be settable + see the hpwh_init functions for calculation of shrinkage */ + + struct perfPoint { + double T_F; + double inputPower_coeffs[3]; // c0 + c1*T + c2*T*T + double COP_coeffs[3]; // c0 + c1*T + c2*T*T + }; + + std::vector perfMap; + /**< A map with input/COP quadratic curve coefficients at a given external temperature */ + + /** a vector to hold the set of logical choices for turning this element on */ + std::vector turnOnLogicSet; + /** a vector to hold the set of logical choices that can cause an element to turn off */ + std::vector shutOffLogicSet; + + void addTurnOnLogic(HeatingLogic logic); + void addShutOffLogic(HeatingLogic logic); + /**< these are two small functions to remove some of the cruft in initiation functions */ + + double minT; + /**< minimum operating temperature of HPWH environment */ + + double maxT; + /**< maximum operating temperature of HPWH environment */ + + double hysteresis_dC; + /**< a hysteresis term that prevents short cycling due to heat pump self-interaction + when the heat source is engaged, it is subtracted from lowT cutoffs and + added to lowTreheat cutoffs */ + + bool depressesTemperature; + /**< heat pumps can depress the temperature of their space in certain instances - + whether or not this occurs is a bool in HPWH, but a heat source must + know if it is capable of contributing to this effect or not + NOTE: this only works for 1 minute steps + ALSO: this is set according the the heat source type, not user-specified */ + + double airflowFreedom; + /**< airflowFreedom is the fraction of full flow. This is used to de-rate compressor + cop (not capacity) for cases where the air flow is restricted - typically ducting */ + + + COIL_CONFIG configuration; /**< submerged, wrapped, external */ + HEATSOURCE_TYPE typeOfHeatSource; /**< compressor, resistance */ + + int lowestNode; + /**< hold the number of the first non-zero condensity entry */ + + + + + // some private functions, mostly used for heating the water with the addHeat function + + double addHeatAboveNode(double cap_kJ, int node, double minutesToRun); + /**< adds heat to the set of nodes that are at the same temperature, above the + specified node number */ + double addHeatExternal(double externalT_C, double minutesToRun, double &cap_BTUperHr, double &input_BTUperHr, double &cop); + /**< Add heat from a source outside of the tank. Assume the condensity is where + the water is drawn from and hot water is put at the top of the tank. */ + + /** I wrote some methods to help with the add heat interface - MJL */ + void getCapacity(double externalT_C, double condenserTemp_C, double &input_BTUperHr, double &cap_BTUperHr, double &cop); + void calcHeatDist(std::vector &heatDistribution); + + double getCondenserTemp(); + /**< returns the temperature of the condensor - it's a weighted average of the + tank temperature, using the condensity as weights */ + + void sortPerformanceMap(); + /**< sorts the Performance Map by increasing external temperatures */ + + /**< A few helper functions */ + double expitFunc(double x, double offset); + void normalize(std::vector &distribution); + +}; // end of HeatSource class + + +// a few extra functions for unit converesion +inline double dF_TO_dC(double temperature) { return (temperature*5.0/9.0); } +inline double F_TO_C(double temperature) { return ((temperature - 32.0)*5.0/9.0); } +inline double C_TO_F(double temperature) { return (((9.0/5.0)*temperature) + 32.0); } +inline double KWH_TO_BTU(double kwh) { return (3412.14 * kwh); } +inline double KWH_TO_KJ(double kwh) { return (kwh * 3600.0); } +inline double BTU_TO_KWH(double btu) { return (btu / 3412.14); } +inline double KJ_TO_KWH(double kj) { return (kj/3600.0); } +inline double BTU_TO_KJ(double btu) { return (btu * 1.055); } +inline double GAL_TO_L(double gallons) { return (gallons * 3.78541); } +inline double L_TO_GAL(double liters) { return (liters / 3.78541); } +inline double UAf_TO_UAc(double UAf) { return (UAf * 1.8 / 0.9478); } + +inline double FT_TO_M(double feet) { return (feet / 3.2808); } +inline double FT2_TO_M2(double feet2) { return (feet2 / 10.7640); } + +#endif diff --git a/src/HPWH.cc b/src/HPWH.cc index 57126afc..e3aeee85 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -204,8 +204,8 @@ string HPWH::getVersion(){ int HPWH::runOneStep(double inletT_C, double drawVolume_L, double tankAmbientT_C, double heatSourceAmbientT_C, DRMODES DRstatus, double minutesPerStep, - double inletVol2_L, double inletT2_C, - vector* nodeExtraHeat_W) { + double inletVol2_L, double inletT2_C, + std::vector* nodeExtraHeat_W) { //returns 0 on successful completion, HPWH_ABORT on failure //check for errors @@ -356,7 +356,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, } //and add heat if it is - heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesToRun); + heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesPerStep); //if it finished early if (heatSourcePtr->runtime_min < minutesToRun) { //debugging message handling @@ -380,11 +380,15 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, isHeating = false; } + //If theres extra user defined heat to add -> Add extra heat! if (nodeExtraHeat_W != NULL) { - addExtraHeat(nodeExtraHeat_W); + msg("runOneStep entering addExtraHeat if\n"); + addExtraHeat(nodeExtraHeat_W, tankAmbientT_C, minutesToRun); } + + //track the depressed local temperature if (doTempDepression) { bool compressorRan = false; @@ -1559,29 +1563,25 @@ void HPWH::mixTankInversions() { } -void addExtraHeat(vector* nodeExtraHeat_W){ - if ((*nodeExtraHeat_W).size() > numNodes){ +void HPWH::addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesToRun){ + if ((*nodeExtraHeat_W).size() != 12){ if (hpwhVerbosity >= VRB_reluctant) { - msg("nodeExtraHeat_KWH has more nodes (%i) than the HPWH tank (%i) \n", (*nodeExtraHeat_KWH).size(), numNodes); + msg("nodeExtraHeat_KWH (%i) does not have 12 nodes \n", (*nodeExtraHeat_W).size(), numNodes); } - return HPWH_ABORT; + simHasFailed = true; } - - double minutesToRun = minutesPerStep; - - for (i = 0; i < numHeatSources; i++){ - if (setOfSources[i].typeOfHeatSource = TYPE_extra) { - + + for (int i = 0; i < numHeatSources; i++){ + if (setOfSources[i].typeOfHeatSource == TYPE_extra) { + // Set up the extra heat source - setOfSources[i].setupExtraHeat(vector* nodeExtraHeat_W)) - + setOfSources[i].setupExtraHeat(nodeExtraHeat_W); + + // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() + calcDerivedHeatingValues(); + // add heat - setOfSources[i].addHeat(heatSourceAmbientT_C, minutesToRun); - - if (doInversionMixing) { - mixTankInversions(); - } - break; + setOfSources[i].addHeat(tankAmbientT_C, minutesToRun); } } } @@ -2382,8 +2382,6 @@ double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun - - void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { int i; @@ -2414,20 +2412,21 @@ void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { } ////////////////////////////////////////////////////////////////////////////////////////////////////// -void HPWH::HeatSource::setupExtraHeat(vector* nodeExtraHeat_W)) { +void HPWH::HeatSource::setupExtraHeat(std::vector* nodeExtraHeat_W) { //get sum of vector - double watts = 0.0;\ - for (int i = 0; i < (*nodeExtraHeat_W).size(); i++) { + double watts = 0.0; + for (unsigned int i = 0; i < (*nodeExtraHeat_W).size(); i++) { watts += (*nodeExtraHeat_W)[i]; } - vector tempCondensity = *nodeExtraHeat_W; + std::vector tempCondensity = *nodeExtraHeat_W; normalize(tempCondensity); // set condensity based on normalized vector setCondensity( tempCondensity[0], tempCondensity[1], tempCondensity[2], tempCondensity[3], tempCondensity[4], tempCondensity[5], tempCondensity[6], tempCondensity[7], tempCondensity[8], tempCondensity[9], tempCondensity[10], tempCondensity[11] ); + perfMap.clear(); perfMap.reserve(2); @@ -2444,8 +2443,6 @@ void HPWH::HeatSource::setupExtraHeat(vector* nodeExtraHeat_W)) { { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) }); - // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() - calcDerivedHeatingValues(); } //////////////////////////////////////////////////////////////////////////// @@ -2457,8 +2454,6 @@ void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { } void HPWH::calcDerivedValues(){ - static char outputString[MAXOUTSTRING]; //this is used for debugging outputs - // tank node density (number of calculation nodes per regular node) nodeDensity = numNodes / 12; @@ -2474,10 +2469,11 @@ void HPWH::calcDerivedValues(){ setOfSources[i].depressesTemperature = false; } } - } void HPWH::calcDerivedHeatingValues(){ + static char outputString[MAXOUTSTRING]; //this is used for debugging outputs + //condentropy/shrinkage double condentropy = 0; double alpha = 1, beta = 2; // Mapping from condentropy to shrinkage @@ -2568,7 +2564,6 @@ int HPWH::checkInputs(){ } - double condensitySum; //loop through all heat sources to check each for malconfigurations for (int i = 0; i < numHeatSources; i++) { @@ -2589,7 +2584,7 @@ int HPWH::checkInputs(){ condensitySum = 0; for (int j = 0; j < CONDENSITY_SIZE; j++) condensitySum += setOfSources[i].condensity[j]; if (fabs(condensitySum - 1.0) > 1e-6) { - msg("The condensity for hearsource %d does not sum to 1. \n", i); + msg("The condensity for heatsource %d does not sum to 1. \n", i); msg("It sums to %f \n", condensitySum); returnVal = HPWH_ABORT; } @@ -2601,7 +2596,7 @@ int HPWH::checkInputs(){ } - + //Check if the UA is out of bounds if (tankUA_kJperHrC < 0.0) { msg("The tankUA_kJperHrC is less than 0 for a HPWH, it must be greater than 0, tankUA_kJperHrC is: %f \n", tankUA_kJperHrC); @@ -3576,20 +3571,27 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { tankUA_kJperHrC = 10; //0 to turn off doTempDepression = false; - //should eventually put tankmixes to true when testing progresses tankMixesOnDraw = false; //////////////////////////////////////////////////// numHeatSources = 1; - + setOfSources = new HeatSource[numHeatSources]; + HeatSource extra(this); - + //compressor values extra.isOn = false; extra.isVIP = false; extra.typeOfHeatSource = TYPE_extra; extra.configuration = HeatSource::CONFIG_WRAPPED; - + + extra.addTurnOnLogic(HPWH::bottomThird(200)); + + //initial guess, will get reset based on the input heat vector + extra.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + + setOfSources[0] = extra; } //basic compressor tank for testing diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index b3c46841..95614243 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -244,7 +244,8 @@ class HPWH { int runOneStep(double inletT_C, double drawVolume_L, double ambientT_C, double externalT_C, DRMODES DRstatus, double minutesPerStep, - double inletVol2_L = 0., double inletT2_C = 0.); + double inletVol2_L = 0., double inletT2_C = 0., + std::vector* nodeExtraHeat_W = NULL); /**< This function will progress the simulation forward in time by one step * all calculated outputs are stored in private variables and accessed through functions * @@ -417,7 +418,7 @@ class HPWH { void turnAllHeatSourcesOff(); /**< disengage each heat source */ - void addExtraHeat(vector* nodeExtraHeat_W); + void addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesPerStep); /**< adds extra heat defined by the user. Where nodeExtraHeat[] is a vector of heat quantities to be added during the step. nodeExtraHeat[ 0] would go to bottom node, 1 to next etc. */ double tankAvg_C(const std::vector nodeWeights) const; @@ -425,7 +426,10 @@ class HPWH { /**< functions to calculate what the temperature in a portion of the tank is */ void calcDerivedValues(); - /**< a helper function for the inits, calculating condentropy and the lowest node */ + /**< a helper function for the inits */ + void calcDerivedHeatingValues(); + /**< a helper for the helper, calculating condentropy and the lowest node*/ + int checkInputs(); /**< a helper function to run a few checks on the HPWH input parameters */ @@ -544,6 +548,9 @@ class HPWH::HeatSource { void setupAsResistiveElement(int node, double Watts); /**< configure the heat source to be a resisive element, positioned at the specified node, with the specified power in watts */ + void setupExtraHeat(std::vector* nodeExtraHeat_W); + /**< Configure a user defined heat source added as extra, based off using + nodeExtraHeat_W as the total watt input and the condensity*/ bool isEngaged() const; /**< return whether or not the heat source is engaged */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5b342e83..f3b3f0a1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,16 +13,16 @@ set(testNames test30 test50 test70 - test95 - testLockout - testDr + #test95 + # testLockout + # testDr testSandenCombi ) set(modelNames StorageTank - # AOSmithPHPT60 - # AOSmithHPTU80 + AOSmithPHPT60 + AOSmithHPTU80 # Sanden80 # RheemHB50 # Stiebel220e diff --git a/test/main.cc b/test/main.cc index 7a1c511a..2a637647 100644 --- a/test/main.cc +++ b/test/main.cc @@ -184,6 +184,7 @@ int main(int argc, char *argv[]) doInvMix = 1; inletH = 0.0; cout << "Running: " << input2 << ", " << input1 << ", " << input3 << endl; + while(controlFile >> var1 >> testVal) { if(var1 == "setpoint") { // If a setpoint was specified then override the default newSetpoint = testVal; @@ -207,7 +208,6 @@ int main(int argc, char *argv[]) exit(1); } - // ------------------------------------- Read Schedules--------------------------------------- // scheduleNames.push_back("inletT"); scheduleNames.push_back("draw"); @@ -252,11 +252,13 @@ int main(int argc, char *argv[]) // ------------------------------------- Simulate --------------------------------------- // + cout << "Now Simulating " << minutesToRun << " Minutes of the Test\n"; - vector* nodeExtraHeat_W = NULL; + std::vector nodeExtraHeat_W; + std::vector* vectptr = NULL; // Loop over the minutes in the test - cout << "Now Simulating " << minutesToRun << " Minutes of the Test\n"; for(i = 0; i < minutesToRun; i++) { + if(DEBUG) { cout << "Now on minute " << i << "\n"; } @@ -275,22 +277,24 @@ int main(int argc, char *argv[]) } else if(allSchedules[4][i] == 2) { drStatus = HPWH::DR_ENGAGE; } - + + vectptr = NULL; if ( i == 15 || i== 100 || i== 150){ - nodeExtraHeat_W = { 1000, 20000, 3000, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - } - else{ - nodeExtraHeat_W = NULL; + nodeExtraHeat_W = { 1000., 2000., 3000., 4., 5, 6, 7, 8, 9, 10, 11, 12 }; + vectptr = &nodeExtraHeat_W; + cout << "Now on minute " << i << "\n"; + } + // Run the step - hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) - GAL_TO_L(allSchedules[1][i]), // Flow in gallons - airTemp2, // Ambient Temp (C) - allSchedules[3][i], // External Temp (C) - drStatus, // DDR Status (now an enum. Fixed for now as allow) - 1.0, // Minutes per step - 1. * GAL_TO_L(allSchedules[1][i]), allSchedules[0][i], - 0., 0., nodeExtraHeat_W); + hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) + GAL_TO_L(allSchedules[1][i]), // Flow in gallons + airTemp2, // Ambient Temp (C) + allSchedules[3][i], // External Temp (C) + drStatus, // DDR Status (now an enum. Fixed for now as allow) + 1.0, // Minutes per step + 1. * GAL_TO_L(allSchedules[1][i]), allSchedules[0][i], + vectptr); // Copy current status into the output file @@ -363,11 +367,11 @@ int readSchedule(schedule &scheduleArray, string scheduleFileName, long minutesO } //print out the whole schedule - if(DEBUG == 1){ - for(i = 0; (unsigned)i < scheduleArray.size(); i++){ - cout << "scheduleArray[" << i << "] = " << scheduleArray[i] << "\n"; - } - } +// if(DEBUG == 1){ +// for(i = 0; (unsigned)i < scheduleArray.size(); i++){ +// cout << "scheduleArray[" << i << "] = " << scheduleArray[i] << "\n"; +// } +// } inputFile.close(); From c7fcc6cc2c10badf3f5ea76ed4dbc5229800fb2f Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 21 Jan 2020 17:02:12 -0800 Subject: [PATCH 03/22] Checking --- HPWH.cc | 5449 -------------------------------------------------- HPWH.in.hh | 740 ------- src/HPWH.cc | 3 +- test/main.cc | 4 +- 4 files changed, 3 insertions(+), 6193 deletions(-) delete mode 100644 HPWH.cc delete mode 100644 HPWH.in.hh diff --git a/HPWH.cc b/HPWH.cc deleted file mode 100644 index 1b706880..00000000 --- a/HPWH.cc +++ /dev/null @@ -1,5449 +0,0 @@ -/* -Copyright (c) 2014-2016 Ecotope Inc. -All rights reserved. - - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - - -* Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - - - -* Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - - - -* Neither the name of the copyright holders nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission from the copyright holders. - - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - -#include "HPWH.hh" -#include -#include -#include -#include -#include - -using std::endl; -using std::cout; -using std::string; - -const float HPWH::DENSITYWATER_kgperL = 0.995f; -const float HPWH::KWATER_WpermC = 0.62f; -const float HPWH::CPWATER_kJperkgC = 4.180f; -const float HPWH::HEATDIST_MINVALUE = 0.0001f; -const float HPWH::UNINITIALIZED_LOCATIONTEMP = -500.f; - -//ugh, this should be in the header -const std::string HPWH::version_maint = HPWHVRSN_META; - -#define SETPOINT_FIX // #define to include fixes for - // setpoint-below-water-temp issues - // 1-22-2017 - -//the HPWH functions -//the publics -HPWH::HPWH() : -simHasFailed(true), isHeating(false), setpointFixed(false), hpwhVerbosity(VRB_reluctant), // hpwhVerbosity(VRB_silent), -messageCallback(NULL), messageCallbackContextPtr(NULL), numHeatSources(0), -setOfSources(NULL), tankTemps_C(NULL), nextTankTemps_C(NULL), doTempDepression(false), locationTemperature_C(UNINITIALIZED_LOCATIONTEMP), -doInversionMixing(true), doConduction(true), inletHeight(0), inlet2Height(0) -{ } - -HPWH::HPWH(const HPWH &hpwh){ - simHasFailed = hpwh.simHasFailed; - - hpwhVerbosity = hpwh.hpwhVerbosity; - - //these should actually be the same pointers - messageCallback = hpwh.messageCallback; - messageCallbackContextPtr = hpwh.messageCallbackContextPtr; - - isHeating = hpwh.isHeating; - - numHeatSources = hpwh.numHeatSources; - setOfSources = new HeatSource[numHeatSources]; - for (int i = 0; i < numHeatSources; i++) { - setOfSources[i] = hpwh.setOfSources[i]; - setOfSources[i].hpwh = this; - } - - - - tankVolume_L = hpwh.tankVolume_L; - tankUA_kJperHrC = hpwh.tankUA_kJperHrC; - - setpoint_C = hpwh.setpoint_C; - numNodes = hpwh.numNodes; - nodeDensity = hpwh.nodeDensity; - tankTemps_C = new double[numNodes]; - nextTankTemps_C = new double[numNodes]; - for (int i = 0; i < numNodes; i++) { - tankTemps_C[i] = hpwh.tankTemps_C[i]; - nextTankTemps_C[i] = hpwh.nextTankTemps_C[i]; - } - inletHeight = hpwh.inletHeight; - inlet2Height = hpwh.inlet2Height; - - outletTemp_C = hpwh.outletTemp_C; - energyRemovedFromEnvironment_kWh = hpwh.energyRemovedFromEnvironment_kWh; - standbyLosses_kWh = hpwh.standbyLosses_kWh; - - tankMixesOnDraw = hpwh.tankMixesOnDraw; - doTempDepression = hpwh.doTempDepression; - - doInversionMixing = hpwh.doInversionMixing; - doConduction = hpwh.doConduction; - - locationTemperature_C = hpwh.locationTemperature_C; - -} - -HPWH & HPWH::operator=(const HPWH &hpwh){ - if (this == &hpwh) { - return *this; - } - - - simHasFailed = hpwh.simHasFailed; - - hpwhVerbosity = hpwh.hpwhVerbosity; - - //these should actually be the same pointers - messageCallback = hpwh.messageCallback; - messageCallbackContextPtr = hpwh.messageCallbackContextPtr; - - isHeating = hpwh.isHeating; - - numHeatSources = hpwh.numHeatSources; - - delete[] setOfSources; - setOfSources = new HeatSource[numHeatSources]; - for (int i = 0; i < numHeatSources; i++) { - setOfSources[i] = hpwh.setOfSources[i]; - setOfSources[i].hpwh = this; - //HeatSource assignment will fail (causing the simulation to fail) if a - //HeatSource has backups/companions. - //This could be dealt with in this function (tricky), but the HeatSource - //assignment can't know where its backup/companion pointer goes so it either - //fails or silently does something that's not at all useful. - //I prefer it to fail. -NDK 1/2016 - } - - - tankVolume_L = hpwh.tankVolume_L; - tankUA_kJperHrC = hpwh.tankUA_kJperHrC; - - setpoint_C = hpwh.setpoint_C; - numNodes = hpwh.numNodes; - nodeDensity = hpwh.nodeDensity; - - delete[] tankTemps_C; - delete[] nextTankTemps_C; - tankTemps_C = new double[numNodes]; - nextTankTemps_C = new double[numNodes]; - for (int i = 0; i < numNodes; i++) { - tankTemps_C[i] = hpwh.tankTemps_C[i]; - nextTankTemps_C[i] = hpwh.nextTankTemps_C[i]; - } - inletHeight = hpwh.inletHeight; - inlet2Height = hpwh.inlet2Height; - - outletTemp_C = hpwh.outletTemp_C; - energyRemovedFromEnvironment_kWh = hpwh.energyRemovedFromEnvironment_kWh; - standbyLosses_kWh = hpwh.standbyLosses_kWh; - - tankMixesOnDraw = hpwh.tankMixesOnDraw; - doTempDepression = hpwh.doTempDepression; - - doInversionMixing = hpwh.doInversionMixing; - doConduction = hpwh.doConduction; - - locationTemperature_C = hpwh.locationTemperature_C; - - return *this; -} - -HPWH::~HPWH() { - delete[] tankTemps_C; - delete[] nextTankTemps_C; - delete[] setOfSources; -} - -string HPWH::getVersion(){ - std::stringstream version; - - version << version_major << '.' << version_minor << '.' << version_patch << version_maint; - - return version.str(); -} - - -int HPWH::runOneStep(double inletT_C, double drawVolume_L, - double tankAmbientT_C, double heatSourceAmbientT_C, - DRMODES DRstatus, double minutesPerStep, - double inletVol2_L, double inletT2_C, - std::vector* nodeExtraHeat_W) { - //returns 0 on successful completion, HPWH_ABORT on failure - - //check for errors - if (doTempDepression == true && minutesPerStep != 1) { - msg("minutesPerStep must equal one for temperature depression to work. \n"); - simHasFailed = true; - return HPWH_ABORT; - } - - - if (hpwhVerbosity >= VRB_typical) { - msg("Beginning runOneStep. \nTank Temps: "); - printTankTemps(); - msg("Step Inputs: InletT_C: %.2lf, drawVolume_L: %.2lf, tankAmbientT_C: %.2lf, heatSourceAmbientT_C: %.2lf, DRstatus: %d, minutesPerStep: %.2lf \n", - inletT_C, drawVolume_L, tankAmbientT_C, heatSourceAmbientT_C, DRstatus, minutesPerStep); - } - //is the failure flag is set, don't run - if (simHasFailed) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("simHasFailed is set, aborting. \n"); - } - return HPWH_ABORT; - } - - - - //reset the output variables - outletTemp_C = 0; - energyRemovedFromEnvironment_kWh = 0; - standbyLosses_kWh = 0; - - for (int i = 0; i < numHeatSources; i++) { - setOfSources[i].runtime_min = 0; - setOfSources[i].energyInput_kWh = 0; - setOfSources[i].energyOutput_kWh = 0; - } - - // if you are doing temp. depression, set tank and heatSource ambient temps - // to the tracked locationTemperature - double temperatureGoal = tankAmbientT_C; - if (doTempDepression) { - if (locationTemperature_C == UNINITIALIZED_LOCATIONTEMP) { - locationTemperature_C = tankAmbientT_C; - } - tankAmbientT_C = locationTemperature_C; - heatSourceAmbientT_C = locationTemperature_C; - } - - - //process draws and standby losses - updateTankTemps(drawVolume_L, inletT_C, tankAmbientT_C, minutesPerStep, inletVol2_L, inletT2_C); - - - //do HeatSource choice - for (int i = 0; i < numHeatSources; i++) { - if (hpwhVerbosity >= VRB_emetic) { - msg("Heat source choice:\theatsource %d can choose from %lu turn on logics and %lu shut off logics\n", i, setOfSources[i].turnOnLogicSet.size(), setOfSources[i].shutOffLogicSet.size()); - } - if (isHeating == true) { - //check if anything that is on needs to turn off (generally for lowT cutoffs) - //things that just turn on later this step are checked for this in shouldHeat - if (setOfSources[i].isEngaged() && setOfSources[i].shutsOff()) { - setOfSources[i].disengageHeatSource(); - //check if the backup heat source would have to shut off too - if (setOfSources[i].backupHeatSource != NULL && setOfSources[i].backupHeatSource->shutsOff() != true) { - //and if not, go ahead and turn it on - setOfSources[i].backupHeatSource->engageHeatSource(); - } - } - - //if there's a priority HeatSource (e.g. upper resistor) and it needs to - //come on, then turn everything off and start it up - if (setOfSources[i].isVIP) { - if (hpwhVerbosity >= VRB_emetic) { - msg("\tVIP check"); - } - if (setOfSources[i].shouldHeat()) { - turnAllHeatSourcesOff(); - setOfSources[i].engageHeatSource(); - //stop looking if the VIP needs to run - break; - } - } - } - //if nothing is currently on, then check if something should come on - else /* (isHeating == false) */ { - if (setOfSources[i].shouldHeat()) { - setOfSources[i].engageHeatSource(); - //engaging a heat source sets isHeating to true, so this will only trigger once - } - } - - } //end loop over heat sources - - if (hpwhVerbosity >= VRB_emetic){ - msg("after heat source choosing: "); - for (int i = 0; i < numHeatSources; i++) { - msg("heat source %d: %d \t", i, setOfSources[i].isEngaged()); - } - msg("\n"); - } - - - //change the things according to DR schedule - if (DRstatus == DR_BLOCK) { - //force off - turnAllHeatSourcesOff(); - isHeating = false; - } - else if (DRstatus == DR_ALLOW) { - //do nothing - } - else if (DRstatus == DR_ENGAGE) { - //if nothing else is on, force the first heat source on - //this may or may not be desired behavior, pending more research (and funding) - if (areAllHeatSourcesOff() == true) { - if (compressorIndex > -1) { - setOfSources[compressorIndex].engageHeatSource(); - } else if (lowestElementIndex > -1) { - setOfSources[lowestElementIndex].engageHeatSource(); - } - } - } - - //do heating logic - double minutesToRun = minutesPerStep; - - for (int i = 0; i < numHeatSources; i++) { - // check/apply lock-outs - if (hpwhVerbosity >= VRB_emetic) { - msg("Checking lock-out logic for heat source %d:\n", i); - } - if (setOfSources[i].shouldLockOut(heatSourceAmbientT_C)) { - setOfSources[i].lockOutHeatSource(); - } - if (setOfSources[i].shouldUnlock(heatSourceAmbientT_C)) { - setOfSources[i].unlockHeatSource(); - } - - //going through in order, check if the heat source is on - if (setOfSources[i].isEngaged()) { - - HeatSource* heatSourcePtr; - if (setOfSources[i].isLockedOut() && setOfSources[i].backupHeatSource != NULL) { - heatSourcePtr = setOfSources[i].backupHeatSource; - } else { - heatSourcePtr = &setOfSources[i]; - } - - //and add heat if it is - heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesPerStep); - //if it finished early - if (heatSourcePtr->runtime_min < minutesToRun) { - //debugging message handling - if (hpwhVerbosity >= VRB_emetic){ - msg("done heating! runtime_min minutesToRun %.2lf %.2lf\n", heatSourcePtr->runtime_min, minutesToRun); - } - - //subtract time it ran and turn it off - minutesToRun -= heatSourcePtr->runtime_min; - setOfSources[i].disengageHeatSource(); - //and if there's a heat source that follows this heat source (regardless of lockout) that's able to come on, - if (setOfSources[i].followedByHeatSource != NULL && setOfSources[i].followedByHeatSource->shutsOff() == false) { - //turn it on - setOfSources[i].followedByHeatSource->engageHeatSource(); - } - } - } - } - - if (areAllHeatSourcesOff() == true) { - isHeating = false; - } - - - //If theres extra user defined heat to add -> Add extra heat! - if (nodeExtraHeat_W != NULL) { - msg("runOneStep entering addExtraHeat if\n"); - addExtraHeat(nodeExtraHeat_W, tankAmbientT_C, minutesToRun); - } - - - - //track the depressed local temperature - if (doTempDepression) { - bool compressorRan = false; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].isEngaged() && !setOfSources[i].isLockedOut() && setOfSources[i].depressesTemperature) { - compressorRan = true; - } - } - - if (compressorRan){ - temperatureGoal -= maxDepression_C; //hardcoded 4.5 degree total drop - from experimental data. Changed to an input - } - else{ - //otherwise, do nothing, we're going back to ambient - } - - // shrink the gap by the same percentage every minute - that gives us - // exponential behavior the percentage was determined by a fit to - // experimental data - 9.4 minute half life and 4.5 degree total drop - //minus-equals is important, and fits with the order of locationTemperature - //and temperatureGoal, so as to not use fabs() and conditional tests - locationTemperature_C -= (locationTemperature_C - temperatureGoal)*(1 - 0.9289); - } - - //settle outputs - - //outletTemp_C and standbyLosses_kWh are taken care of in updateTankTemps - - //sum energyRemovedFromEnvironment_kWh for each heat source; - for (int i = 0; i < numHeatSources; i++) { - energyRemovedFromEnvironment_kWh += (setOfSources[i].energyOutput_kWh - setOfSources[i].energyInput_kWh); - } - - //cursory check for inverted temperature profile - if (tankTemps_C[numNodes-1] < tankTemps_C[0]) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("The top of the tank is cooler than the bottom. \n"); - } - } - - if (simHasFailed) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("The simulation has encountered an error. \n"); - } - return HPWH_ABORT; - } - - - if (hpwhVerbosity >= VRB_typical) { - msg("Ending runOneStep. \n\n\n\n"); - } - return 0; //successful completion of the step returns 0 -} //end runOneStep - - -int HPWH::runNSteps(int N, double *inletT_C, double *drawVolume_L, - double *tankAmbientT_C, double *heatSourceAmbientT_C, - DRMODES *DRstatus, double minutesPerStep) { - //returns 0 on successful completion, HPWH_ABORT on failure - - //these are all the accumulating variables we'll need - double energyRemovedFromEnvironment_kWh_SUM = 0; - double standbyLosses_kWh_SUM = 0; - double outletTemp_C_AVG = 0; - double totalDrawVolume_L = 0; - std::vector heatSources_runTimes_SUM(numHeatSources); - std::vector heatSources_energyInputs_SUM(numHeatSources); - std::vector heatSources_energyOutputs_SUM(numHeatSources); - - if (hpwhVerbosity >= VRB_typical) { - msg("Begin runNSteps. \n"); - } - //run the sim one step at a time, accumulating the outputs as you go - for (int i = 0; i < N; i++) { - runOneStep(inletT_C[i], drawVolume_L[i], tankAmbientT_C[i], heatSourceAmbientT_C[i], - DRstatus[i], minutesPerStep); - - if (simHasFailed) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("RunNSteps has encountered an error on step %d of N and has ceased running. \n", i + 1); - } - return HPWH_ABORT; - } - - energyRemovedFromEnvironment_kWh_SUM += energyRemovedFromEnvironment_kWh; - standbyLosses_kWh_SUM += standbyLosses_kWh; - - outletTemp_C_AVG += outletTemp_C * drawVolume_L[i]; - totalDrawVolume_L += drawVolume_L[i]; - - for (int j = 0; j < numHeatSources; j++) { - heatSources_runTimes_SUM[j] += getNthHeatSourceRunTime(j); - heatSources_energyInputs_SUM[j] += getNthHeatSourceEnergyInput(j); - heatSources_energyOutputs_SUM[j] += getNthHeatSourceEnergyOutput(j); - } - - //print minutely output - if (hpwhVerbosity == VRB_minuteOut) { - msg("%f,%f,%f,", tankAmbientT_C[i], drawVolume_L[i], inletT_C[i]); - for (int j = 0; j < numHeatSources; j++) { - msg("%f,%f,", getNthHeatSourceEnergyInput(j), getNthHeatSourceEnergyOutput(j)); - } - msg("%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n", - tankTemps_C[0 * numNodes / 12], tankTemps_C[1 * numNodes / 12], - tankTemps_C[2 * numNodes / 12], tankTemps_C[3 * numNodes / 12], - tankTemps_C[4 * numNodes / 12], tankTemps_C[5 * numNodes / 12], - tankTemps_C[6 * numNodes / 12], tankTemps_C[7 * numNodes / 12], - tankTemps_C[8 * numNodes / 12], tankTemps_C[9 * numNodes / 12], - tankTemps_C[10 * numNodes / 12], tankTemps_C[11 * numNodes / 12], - getNthSimTcouple(1, 6), getNthSimTcouple(2, 6), getNthSimTcouple(3, 6), - getNthSimTcouple(4, 6), getNthSimTcouple(5, 6), getNthSimTcouple(6, 6)); - } - - } - //finish weighted avg. of outlet temp by dividing by the total drawn volume - outletTemp_C_AVG /= totalDrawVolume_L; - - //now, reassign all of the accumulated values to their original spots - energyRemovedFromEnvironment_kWh = energyRemovedFromEnvironment_kWh_SUM; - standbyLosses_kWh = standbyLosses_kWh_SUM; - outletTemp_C = outletTemp_C_AVG; - - for (int i = 0; i < numHeatSources; i++) { - setOfSources[i].runtime_min = heatSources_runTimes_SUM[i]; - setOfSources[i].energyInput_kWh = heatSources_energyInputs_SUM[i]; - setOfSources[i].energyOutput_kWh = heatSources_energyOutputs_SUM[i]; - } - - if (hpwhVerbosity >= VRB_typical) { - msg("Ending runNSteps. \n\n\n\n"); - } - return 0; -} - - - -void HPWH::setVerbosity(VERBOSITY hpwhVrb){ - hpwhVerbosity = hpwhVrb; -} -void HPWH::setMessageCallback(void(*callbackFunc)(const string message, void* contextPtr), void* contextPtr){ - messageCallback = callbackFunc; - messageCallbackContextPtr = contextPtr; -} -void HPWH::sayMessage(const string message) const{ - if (messageCallback != NULL) { - (*messageCallback)(message, messageCallbackContextPtr); - } - else { - std::cout << message; - } -} -void HPWH::msg(const char* fmt, ...) const { - va_list ap; va_start(ap, fmt); - msgV(fmt, ap); -} -void HPWH::msgV(const char* fmt, va_list ap /*=NULL*/) const { - char outputString[MAXOUTSTRING]; - - const char* p; - if (ap) { -#if defined( _MSC_VER) - vsprintf_s< MAXOUTSTRING>(outputString, fmt, ap); -#else - vsnprintf(outputString, MAXOUTSTRING, fmt, ap); -#endif - p = outputString; - } - else { - p = fmt; - } - sayMessage(p); -} // HPWH::msgV - - -void HPWH::printHeatSourceInfo(){ - std::stringstream ss; - double runtime = 0, outputVar = 0; - - ss << std::left; - ss << std::fixed; - ss << std::setprecision(2); - for (int i = 0; i < getNumHeatSources(); i++) { - ss << "heat source " << i << ": " << isNthHeatSourceRunning(i) << "\t\t"; - } - ss << endl; - - for (int i = 0; i < getNumHeatSources(); i++) { - ss << "input energy kwh: " << std::setw(7) << getNthHeatSourceEnergyInput(i) << "\t"; - } - ss << endl; - - for (int i = 0; i < getNumHeatSources(); i++) { - runtime = getNthHeatSourceRunTime(i); - if (runtime != 0) { - outputVar = getNthHeatSourceEnergyInput(i) / (runtime / 60.0); - } - else { - outputVar = 0; - } - ss << "input power kw: " << std::setw(7) << outputVar << "\t\t"; - } - ss << endl; - - for (int i = 0; i < getNumHeatSources(); i++) { - ss << "output energy kwh: " << std::setw(7) << getNthHeatSourceEnergyOutput(i, UNITS_KWH) << "\t"; - } - ss << endl; - - for (int i = 0; i < getNumHeatSources(); i++) { - runtime = getNthHeatSourceRunTime(i); - if (runtime != 0) { - outputVar = getNthHeatSourceEnergyOutput(i, UNITS_KWH) / (runtime / 60.0); - } - else { - outputVar = 0; - } - ss << "output power kw: " << std::setw(7) << outputVar << "\t"; - } - ss << endl; - - for (int i = 0; i < getNumHeatSources(); i++) { - ss << "run time min: " << std::setw(7) << getNthHeatSourceRunTime(i) << "\t\t"; - } - ss << endl << endl << endl; - - - msg(ss.str().c_str()); -} - - -void HPWH::printTankTemps() { - std::stringstream ss; - - ss << std::left; - - for (int i = 0; i < getNumNodes(); i++) { - ss << std::setw(9) << getTankNodeTemp(i) << " "; - } - ss << endl; - - msg(ss.str().c_str()); -} - - -// public members to write to CSV file -int HPWH::WriteCSVHeading(FILE* outFILE, const char* preamble, int nTCouples, int options) const { - - bool doIP = (options & CSVOPT_IPUNITS) != 0; - - fprintf(outFILE, "%s", preamble); - - const char* pfx = ""; - for (int iHS = 0; iHS < getNumHeatSources(); iHS++) { - fprintf(outFILE, "%sh_src%dIn (Wh),h_src%dOut (Wh)", pfx, iHS + 1, iHS + 1); - pfx = ","; - } - - for (int iTC = 0; iTC < nTCouples; iTC++) { - fprintf(outFILE, ",tcouple%d (%s)", iTC + 1, doIP ? "F":"C"); - } - - fprintf(outFILE, "\n"); - - return 0; -} - -int HPWH::WriteCSVRow(FILE* outFILE, const char* preamble, int nTCouples, int options) const { - - bool doIP = (options & CSVOPT_IPUNITS) != 0; - - fprintf(outFILE, "%s", preamble); - - const char* pfx = ""; - for (int iHS = 0; iHS < getNumHeatSources(); iHS++) { - fprintf(outFILE, "%s%0.2f,%0.2f", pfx, getNthHeatSourceEnergyInput(iHS, UNITS_KWH)*1000., - getNthHeatSourceEnergyOutput(iHS, UNITS_KWH)*1000.); - pfx = ","; - } - - for (int iTC = 0; iTC < nTCouples; iTC++) { - fprintf(outFILE, ",%0.2f", getNthSimTcouple(iTC + 1, nTCouples, doIP ? UNITS_F : UNITS_C)); - } - - fprintf(outFILE, "\n"); - - return 0; -} - - -bool HPWH::isSetpointFixed(){ - return setpointFixed; -} - -int HPWH::setSetpoint(double newSetpoint, UNITS units /*=UNITS_C*/) { - if (setpointFixed == true) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Unwilling to set setpoint for your currently selected model. \n"); - } - return HPWH_ABORT; - } - else{ - if (units == UNITS_C) { - setpoint_C = newSetpoint; - } - else if (units == UNITS_F) { - setpoint_C = (F_TO_C(newSetpoint)); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setSetpoint. \n"); - } - return HPWH_ABORT; - } - } - return 0; -} -double HPWH::getSetpoint(){ - return setpoint_C; -} - - -int HPWH::resetTankToSetpoint(){ - for (int i = 0; i < numNodes; i++) { - tankTemps_C[i] = setpoint_C; - } - return 0; -} - - -int HPWH::setAirFlowFreedom(double fanFraction) { - if (fanFraction < 0 || fanFraction > 1) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to set the fan fraction outside of bounds. \n"); - } - simHasFailed = true; - return HPWH_ABORT; - } - else { - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { - setOfSources[i].airflowFreedom = fanFraction; - } - } - } - return 0; -} - -int HPWH::setDoTempDepression(bool doTempDepress) { - this->doTempDepression = doTempDepress; - return 0; -} - -int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/){ - //Uses the UA before the function is called and adjusts the A part of the UA to match the input volume given getTankSurfaceArea(). - double HPWH_size_L; - double oldA = getTankSurfaceArea(UNITS_FT2); - - if (units == UNITS_L) { - HPWH_size_L = HPWH_size; - } - else if (units == UNITS_GAL) { - HPWH_size_L = GAL_TO_L(HPWH_size); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setTankSize_adjustUA. \n"); - } - return HPWH_ABORT; - } - setTankSize(HPWH_size_L, UNITS_L); - setUA(tankUA_kJperHrC / oldA * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); - return 0; -} - -double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/){ - // returns tank surface area, old defualt was in ft2 - // Based off 88 insulated storage tanks currently available on the market from Sanden, AOSmith, HTP, Rheem, and Niles. - // Using the same form of equation given in RACM 2016 App B, equation 41. - double value = 1.492 * pow(L_TO_GAL(tankVolume_L), 0.6666) + 5.068*pow(L_TO_GAL(tankVolume_L), 0.3333) - 10.913; - - if (units == UNITS_FT2) { - return value; - } - else if (units == UNITS_M2) { - return FT2_TO_M2(value); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getTankSurfaceArea. \n"); - } - return HPWH_ABORT; - } -} - -double HPWH::getTankRadius(UNITS units /*=UNITS_FT*/){ - // returns tank radius, ft for use in calculation of heat loss in the bottom and top of the tank. - // Based off 88 insulated storage tanks currently available on the market from Sanden, AOSmith, HTP, Rheem, and Niles. - double value = 0.2244 * pow(L_TO_GAL(tankVolume_L), 0.333) + 0.0749; - - if (units == UNITS_FT) { - return value; - } - else if (units == UNITS_M) { - return FT2_TO_M2(value); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getTankRadius. \n"); - } - return HPWH_ABORT; - } -} - -int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/) { - if (HPWH_size <= 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to set the tank volume outside of bounds. \n"); - } - simHasFailed = true; - return HPWH_ABORT; - } - else { - if (units == UNITS_L) { - this->tankVolume_L = HPWH_size; - } - else if (units == UNITS_GAL) { - this->tankVolume_L = (GAL_TO_L(HPWH_size)); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setTankSize. \n"); - } - return HPWH_ABORT; - } - } - return 0; -} -int HPWH::setDoInversionMixing(bool doInvMix) { - this->doInversionMixing = doInvMix; - return 0; -} -int HPWH::setDoConduction(bool doCondu) { - this->doConduction = doCondu; - return 0; -} - -int HPWH::setUA(double UA, UNITS units /*=UNITS_kJperHrC*/) { - if (units == UNITS_kJperHrC) { - tankUA_kJperHrC = UA; - } - else if (units == UNITS_BTUperHrF) { - tankUA_kJperHrC = UAf_TO_UAc(UA); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setUA. \n"); - } - return HPWH_ABORT; - } - return 0; -} - -int HPWH::getUA(double& UA, UNITS units /*=UNITS_kJperHrC*/) const { - UA = tankUA_kJperHrC; - if (units == UNITS_kJperHrC) { - // UA is already in correct units - } - else if (units == UNITS_BTUperHrF) { - UA = UA / UAf_TO_UAc( 1.); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getUA. \n"); - } - UA = -1.; - return HPWH_ABORT; - } - return 0; -} - -int HPWH::setInletByFraction(double fractionalHeight){ - return setNodeNumFromFractionalHeight(fractionalHeight, inletHeight); -} -int HPWH::setInlet2ByFraction(double fractionalHeight){ - return setNodeNumFromFractionalHeight(fractionalHeight, inlet2Height); -} - -int HPWH::setNodeNumFromFractionalHeight(double fractionalHeight, int &inletNum){ - if (fractionalHeight > 1. || fractionalHeight < 0.){ - if (hpwhVerbosity >= VRB_reluctant) { - msg("Out of bounds fraction for setInletByFraction \n"); - } - return HPWH_ABORT; - } - - int node = (int)std::floor(numNodes*fractionalHeight); - inletNum = (node == numNodes) ? numNodes - 1 : node; - - return 0; -} -int HPWH::getInletHeight(int whichInlet){ - if (whichInlet == 1) { - return inletHeight; - } - else if (whichInlet == 2) { - return inlet2Height; - } - else - { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Invalid inlet chosen in getInletHeight \n"); - } - return HPWH_ABORT; - } -} - -int HPWH::setMaxTempDepression(double maxDepression, UNITS units /*=UNITS_C*/) { - if(units == UNITS_C) { - this->maxDepression_C = maxDepression; - } - else if(units == UNITS_F) { - this->maxDepression_C = F_TO_C(maxDepression); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for max Temp Depression. \n"); - } - return HPWH_ABORT; - } - return 0; -} - -HPWH::HeatingLogic HPWH::topThird(double d) const { - std::vector nodeWeights; - for (auto i : {9,10,11,12}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("top third", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::topThird_absolute(double d) const { - std::vector nodeWeights; - for (auto i : {9,10,11,12}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("top third absolute", nodeWeights, d, true); -} - - -HPWH::HeatingLogic HPWH::bottomThird(double d) const { - std::vector nodeWeights; - for (auto i : {1,2,3,4}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("bottom third", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::bottomSixth(double d) const { - std::vector nodeWeights; - for (auto i : {1,2}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("bottom sixth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::secondSixth(double d) const { - std::vector nodeWeights; - for (auto i : {3,4}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("second sixth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::thirdSixth(double d) const { - std::vector nodeWeights; - for (auto i : {5,6}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("third sixth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::fourthSixth(double d) const { - std::vector nodeWeights; - for (auto i : {7,8}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("fourth sixth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::fifthSixth(double d) const { - std::vector nodeWeights; - for (auto i : {9,10}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("fifth sixth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::topSixth(double d) const { - std::vector nodeWeights; - for (auto i : {11,12}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("top sixth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::bottomHalf(double d) const { - std::vector nodeWeights; - for (auto i : {1,2,3,4,5,6}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("bottom half", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::bottomTwelth(double d) const { - std::vector nodeWeights; - for (auto i : {7,8,9,10,11,12}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("bottom twelth", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::standby(double d) const { - std::vector nodeWeights; - nodeWeights.emplace_back(13); // uses very top computation node - return HPWH::HeatingLogic("standby", nodeWeights, d); -} - -HPWH::HeatingLogic HPWH::topNodeMaxTemp(double d) const { - std::vector nodeWeights; - nodeWeights.emplace_back(13); // uses very top computation node - return HPWH::HeatingLogic("top node", nodeWeights, d, true, std::greater()); -} - -HPWH::HeatingLogic HPWH::bottomNodeMaxTemp(double d) const { - std::vector nodeWeights; - nodeWeights.emplace_back(0); // uses very bottom computation node - return HPWH::HeatingLogic("bottom node", nodeWeights, d, true, std::greater()); -} - -HPWH::HeatingLogic HPWH::bottomTwelthMaxTemp(double d) const { - std::vector nodeWeights; - nodeWeights.emplace_back(1); - return HPWH::HeatingLogic("bottom twelth", nodeWeights, d, true, std::greater()); -} - -HPWH::HeatingLogic HPWH::largeDraw(double d) const { - std::vector nodeWeights; - for (auto i : {1,2,3,4}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("large draw", nodeWeights, d, true); -} - -HPWH::HeatingLogic HPWH::largerDraw(double d) const { - std::vector nodeWeights; - for (auto i : {1,2,3,4,5,6}) { - nodeWeights.emplace_back(i); - } - return HPWH::HeatingLogic("larger draw", nodeWeights, d, true); -} - -int HPWH::getNumNodes() const { - return numNodes; -} - - -double HPWH::getTankNodeTemp(int nodeNum, UNITS units /*=UNITS_C*/) const { - if (nodeNum >= numNodes || nodeNum < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access the temperature of a tank node that does not exist. \n"); - } - return double(HPWH_ABORT); - } - else{ - double result = tankTemps_C[nodeNum]; - if (result == double(HPWH_ABORT)) { - return result; - } - if (units == UNITS_C) { - return result; - } - else if (units == UNITS_F) { - return C_TO_F(result); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getTankNodeTemp. \n"); - } - return double(HPWH_ABORT); - } - } -} - - -double HPWH::getNthSimTcouple(int iTCouple, int nTCouple, UNITS units /*=UNITS_C*/) const { - if (iTCouple > nTCouple || iTCouple < 1) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access a simulated thermocouple that does not exist. \n"); - } - return double(HPWH_ABORT); - } - else if (nTCouple > numNodes) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have more simulated thermocouples than nodes. \n"); - } - return double(HPWH_ABORT); - } - else { - double weight = (double)numNodes / (double)nTCouple; - double start_ind = (iTCouple - 1) * weight; - int ind = (int)std::ceil(start_ind); - - double averageTemp_C = 0.0; - - // Check any intial fraction of nodes - averageTemp_C += getTankNodeTemp((int)std::floor(start_ind), UNITS_C) * ((double)ind - start_ind); - weight -= ((double)ind - start_ind); - - // Check the full nodes - while (weight >= 1.0) { - averageTemp_C += getTankNodeTemp(ind, UNITS_C); - weight -= 1.0; - ind += 1; - } - - // Check any leftover - if (weight > 0.) { - averageTemp_C += getTankNodeTemp(ind, UNITS_C) * weight; - } - // Divide by the original weight to get the true average - averageTemp_C /= ((double)numNodes / (double)nTCouple); - - if (units == UNITS_C) { - return averageTemp_C; - } - else if (units == UNITS_F) { - return C_TO_F(averageTemp_C); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getNthSimTcouple. \n"); - } - return double(HPWH_ABORT); - } - } -} - - -int HPWH::getNumHeatSources() const { - return numHeatSources; -} - - -double HPWH::getNthHeatSourceEnergyInput(int N, UNITS units /*=UNITS_KWH*/) const { - //energy used by the heat source is positive - this should always be positive - if (N >= numHeatSources || N < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access the energy input of a heat source that does not exist. \n"); - } - return double(HPWH_ABORT); - } - - if (units == UNITS_KWH) { - return setOfSources[N].energyInput_kWh; - } - else if (units == UNITS_BTU) { - return KWH_TO_BTU(setOfSources[N].energyInput_kWh); - } - else if (units == UNITS_KJ) { - return KWH_TO_KJ(setOfSources[N].energyInput_kWh); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getNthHeatSourceEnergyInput. \n"); - } - return double(HPWH_ABORT); - } -} -double HPWH::getNthHeatSourceEnergyOutput(int N, UNITS units /*=UNITS_KWH*/) const { - //returns energy from the heat source into the water - this should always be positive - if (N >= numHeatSources || N < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access the energy output of a heat source that does not exist. \n"); - } - return double(HPWH_ABORT); - } - - if (units == UNITS_KWH) { - return setOfSources[N].energyOutput_kWh; - } - else if (units == UNITS_BTU) { - return KWH_TO_BTU(setOfSources[N].energyOutput_kWh); - } - else if (units == UNITS_KJ) { - return KWH_TO_KJ(setOfSources[N].energyOutput_kWh); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getNthHeatSourceEnergyInput. \n"); - } - return double(HPWH_ABORT); - } -} - - -double HPWH::getNthHeatSourceRunTime(int N) const { - if (N >= numHeatSources || N < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access the run time of a heat source that does not exist. \n"); - } - return double(HPWH_ABORT); - } - return setOfSources[N].runtime_min; -} - - -int HPWH::isNthHeatSourceRunning(int N) const{ - if (N >= numHeatSources || N < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access the status of a heat source that does not exist. \n"); - } - return HPWH_ABORT; - } - if (setOfSources[N].isEngaged()){ - return 1; - } - else{ - return 0; - } -} - - -HPWH::HEATSOURCE_TYPE HPWH::getNthHeatSourceType(int N) const{ - return setOfSources[N].typeOfHeatSource; -} - - -double HPWH::getTankSize(UNITS units) const { - if (units == UNITS_L) { - return tankVolume_L; - } - else if (units == UNITS_GAL) { - return L_TO_GAL(tankVolume_L); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getTankSize. \n"); - } - return HPWH_ABORT; - } -} - - -double HPWH::getOutletTemp(UNITS units /*=UNITS_C*/) const { - - if (units == UNITS_C) { - return outletTemp_C; - } - else if (units == UNITS_F) { - return C_TO_F(outletTemp_C); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getOutletTemp. \n"); - } - return double(HPWH_ABORT); - } -} - -double HPWH::getEnergyRemovedFromEnvironment(UNITS units /*=UNITS_KWH*/) const { - //moving heat from the space to the water is the positive direction - if (units == UNITS_KWH) { - return energyRemovedFromEnvironment_kWh; - } - else if (units == UNITS_BTU) { - return KWH_TO_BTU(energyRemovedFromEnvironment_kWh); - } - else if (units == UNITS_KJ){ - return KWH_TO_KJ(energyRemovedFromEnvironment_kWh); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getEnergyRemovedFromEnvironment. \n"); - } - return double(HPWH_ABORT); - } -} - -double HPWH::getStandbyLosses(UNITS units /*=UNITS_KWH*/) const { - //moving heat from the water to the space is the positive direction - if (units == UNITS_KWH) { - return standbyLosses_kWh; - } - else if (units == UNITS_BTU) { - return KWH_TO_BTU(standbyLosses_kWh); - } - else if (units == UNITS_KJ){ - return KWH_TO_KJ(standbyLosses_kWh); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getStandbyLosses. \n"); - } - return double(HPWH_ABORT); - } -} - -double HPWH::getTankHeatContent_kJ() const { - // returns tank heat content relative to 0 C using kJ - - //get average tank temperature - double avgTemp = 0.0; - for (int i = 0; i < numNodes; i++) { - avgTemp += tankTemps_C[i]; - } - avgTemp /= numNodes; - - double totalHeat = avgTemp * DENSITYWATER_kgperL * CPWATER_kJperkgC * tankVolume_L; - return totalHeat; -} - -double HPWH::getLocationTemp_C() const { - return locationTemperature_C; -} - - - - -//the privates -void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbientT_C, double minutesPerStep, - double inletVol2_L, double inletT2_C) { - //set up some useful variables for calculations - double volPerNode_LperNode = tankVolume_L / numNodes; - double drawFraction; - this->outletTemp_C = 0; - double nodeInletFraction, cumInletFraction, drawVolume_N, nodeInletTV; - - if (drawVolume_L > 0){ - - //calculate how many nodes to draw (wholeNodesToDraw), and the remainder (drawFraction) - if (inletVol2_L > drawVolume_L) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Volume in inlet 2 is greater than the draw volume. \n"); - } - simHasFailed = true; - return; - } - - // Check which inlet is higher; - int highInletH; - double highInletV, highInletT; - int lowInletH; - double lowInletT, lowInletV; - if (inletHeight > inlet2Height){ - highInletH = inletHeight; - highInletV = drawVolume_L - inletVol2_L; - highInletT = inletT_C; - lowInletH = inlet2Height; - lowInletT = inletT2_C; - lowInletV = inletVol2_L; - } - else { - highInletH = inlet2Height; - highInletV = inletVol2_L; - highInletT = inletT2_C; - lowInletH = inletHeight; - lowInletT = inletT_C; - lowInletV = drawVolume_L - inletVol2_L; - } - - //calculate how many nodes to draw (drawVolume_N) - drawVolume_N = drawVolume_L / volPerNode_LperNode; - if (drawVolume_N > numNodes) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Drawing more than the tank volume in one step is undefined behavior. Terminating simulation. \n"); - } - simHasFailed = true; - return; - } - - ///////////////////////////////////////////////////////////////////////////////////////////////// - - while (drawVolume_N > 0) { - - // Draw one node at a time - drawFraction = drawVolume_N > 1. ? 1. : drawVolume_N; - - //add temperature for outletT average - outletTemp_C += drawFraction * tankTemps_C[numNodes - 1]; - - cumInletFraction = 0.; - for (int i = numNodes - 1; i >= lowInletH; i--) { - - // Reset inlet inputs at this node. - nodeInletFraction = 0.; - nodeInletTV = 0.; - - // Sum of all inlets Vi*Ti at this node - if (i == highInletH) { - nodeInletTV += highInletV * drawFraction / drawVolume_L * highInletT; - nodeInletFraction += highInletV * drawFraction / drawVolume_L; - } - if (i == lowInletH) { - nodeInletTV += lowInletV * drawFraction / drawVolume_L * lowInletT; - nodeInletFraction += lowInletV * drawFraction / drawVolume_L; - - break; // if this is the bottom inlet break out of the four loop and use the boundary condition equation. - } - - // Look at the volume and temperature fluxes into this node - tankTemps_C[i] = (1. - (drawFraction - cumInletFraction)) * tankTemps_C[i] + - nodeInletTV + - (drawFraction - (cumInletFraction + nodeInletFraction)) * tankTemps_C[i - 1]; - - cumInletFraction += nodeInletFraction; - - } - - // Boundary condition equation because it shouldn't take anything from tankTemps_C[i - 1] but it also might not exist. - tankTemps_C[lowInletH] = (1. - (drawFraction - cumInletFraction)) * tankTemps_C[lowInletH] + nodeInletTV; - - drawVolume_N -= drawFraction; - - if (doInversionMixing) { - mixTankInversions(); - } - } - - - //fill in average outlet T - it is a weighted averaged, with weights == nodes drawn - this->outletTemp_C /= (drawVolume_L / volPerNode_LperNode); - - ///////////////////////////////////////////////////////////////////////////////////////////////// - - //Account for mixing at the bottom of the tank - if (tankMixesOnDraw == true && drawVolume_L > 0) { - int mixedBelowNode = numNodes / 3; - double ave = 0; - - for (int i = 0; i < mixedBelowNode; i++) { - ave += tankTemps_C[i]; - } - ave /= mixedBelowNode; - - for (int i = 0; i < mixedBelowNode; i++) { - tankTemps_C[i] += ((ave - tankTemps_C[i]) / 3.0); - } - } - - } //end if(draw_volume_L > 0) - - - // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. - // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions - // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. - // height estimate from Rheem along with the volume is used to get the radius and node_height - const double rad = getTankRadius(UNITS_M); - const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); - const double node_height = height / numNodes; - - // The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_bt + UA_r is the total tank area. - const double UA_bt = tankUA_kJperHrC * rad / (2.0 * (height + rad)); - - // UA_r is the faction of the area of the cylinder that's not the top or bottom. - const double UA_r = height / (height + rad); - - if (doConduction) { - - // Get the "constant" tau for the stability condition and the conduction calculation - const double tau = KWATER_WpermC / (CPWATER_kJperkgC * 1000.0 * DENSITYWATER_kgperL * 1000.0 * (node_height * node_height)) * minutesPerStep * 60.0; - if (tau > 0.5) { - msg("The stability condition for conduction has failed, these results are going to be interesting!\n"); - } - - // Boundary condition for the finite difference. - const double bc = 2.0 * tau * UA_bt * node_height / KWATER_WpermC; - - // Boundary nodes for finite difference - nextTankTemps_C[0] = (1.0 - 2.0 * tau - bc) * tankTemps_C[0] + 2.0 * tau * tankTemps_C[1] + bc * tankAmbientT_C; - nextTankTemps_C[numNodes - 1] = (1.0 - 2.0 * tau - bc) * tankTemps_C[numNodes - 1] + 2.0 * tau * tankTemps_C[numNodes - 2] + bc * tankAmbientT_C; - - // Internal nodes for the finite difference - for (int i = 1; i < numNodes - 1; i++) { - nextTankTemps_C[i] = tankTemps_C[i] + tau * (tankTemps_C[i + 1] - 2.0 * tankTemps_C[i] + tankTemps_C[i - 1]); - } - - // nextTankTemps_C gets assigns to tankTemps_C at the bottom of the function after q_UA. - // UA loss from the sides are found at the bottom of the function. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kJ += (tankUA_kJperHrC * UA_bt * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); - } - else { // Ignore tank conduction and calculate UA losses from top and bottom. UA loss from the sides are found at the bottom of the function - - for (int i = 0; i < numNodes; i++) { - nextTankTemps_C[i] = tankTemps_C[i]; - } - - //kJ's lost as standby in the current time step for the top node. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); - - nextTankTemps_C[0] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); - - //kJ's lost as standby in the current time step for the bottom node. - standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); - - nextTankTemps_C[numNodes - 1] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); - // UA loss from the sides are found at the bottom of the function. - - } - - - //calculate standby losses from the sides of the tank - for (int i = 0; i < numNodes; i++) { - //faction of tank area on the sides - //kJ's lost as standby in the current time step for each node. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_r / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); - - //The effect of standby loss on temperature in each node - nextTankTemps_C[i] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); - } - - - // Assign the new temporary tank temps to the real tank temps. - for (int i = 0; i < numNodes; i++) tankTemps_C[i] = nextTankTemps_C[i]; - - // check for inverted temperature profile - if (doInversionMixing) { - mixTankInversions(); - } - -} //end updateTankTemps - - -// Inversion mixing modeled after bigladder EnergyPlus code PK -void HPWH::mixTankInversions() { - bool hasInversion; - const double volumePerNode_L = tankVolume_L / numNodes; - //int numdos = 0; - - do { - hasInversion = false; - //Start from the top and check downwards - for (int i = numNodes-1; i >= 0; i--) { - if (tankTemps_C[i] < tankTemps_C[i - 1]) { - // Temperature inversion! - hasInversion = true; - - //Mix this inversion mixing temperature by averaging all of the inverted nodes together together. - double Tmixed = 0.0; - double massMixed = 0.0; - int m; - for (m = i; m >= 0; m--) { - Tmixed += tankTemps_C[m] * (volumePerNode_L * DENSITYWATER_kgperL); - massMixed += (volumePerNode_L * DENSITYWATER_kgperL); - if ((m == 0) || (Tmixed / massMixed > tankTemps_C[m - 1])) { - break; - } - } - Tmixed /= massMixed; - - // Assign the tank temps from i to k - for (int k = i; k >= m; k--) tankTemps_C[k] = Tmixed; - } - - } - - } while ( hasInversion ); -} - - -void HPWH::addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesToRun){ - if ((*nodeExtraHeat_W).size() != 12){ - if (hpwhVerbosity >= VRB_reluctant) { - msg("nodeExtraHeat_KWH (%i) does not have 12 nodes \n", (*nodeExtraHeat_W).size(), numNodes); - } - simHasFailed = true; - } - - - msg("in ExtraHeat check numheatsources \n"); - for (int i = 0; i < numHeatSources; i++){ - if (setOfSources[i].typeOfHeatSource = TYPE_extra) { - - // Set up the extra heat source - setOfSources[i].setupExtraHeat(nodeExtraHeat_W); - - msg("setupExtraHeat done \n"); - - // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() - calcDerivedHeatingValues(); - msg("calcDerivedHeatingValues done \n"); - - // add heat - setOfSources[i].addHeat(tankAmbientT_C, minutesToRun); - msg("setOfSources[i].addHeat(tankAmbientT_C, minutesToRun) done"); - - } - } -} -/////////////////////////////////////////////////////////////////////////////////// - - -void HPWH::turnAllHeatSourcesOff() { - for (int i = 0; i < numHeatSources; i++) { - setOfSources[i].disengageHeatSource(); - } - isHeating = false; -} - - -bool HPWH::areAllHeatSourcesOff() const { - bool allOff = true; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].isEngaged() == true) { - allOff = false; - } - } - return allOff; -} - -double HPWH::tankAvg_C(const std::vector nodeWeights) const { - double sum = 0; - double totWeight = 0; - - for (auto nodeWeight : nodeWeights) { - // bottom calc node only - if (nodeWeight.nodeNum == 0) { - sum += tankTemps_C[0] * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - // top calc node only - else if (nodeWeight.nodeNum == 13) { - sum += tankTemps_C[numNodes - 1] * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - else { - for (int n = 0; n < nodeDensity; ++n) { - int calcNode = (nodeWeight.nodeNum - 1) * nodeDensity + n; - sum += tankTemps_C[calcNode] * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - } - } - return sum / totWeight; -} - -//these are the HeatSource functions -//the public functions -HPWH::HeatSource::HeatSource(HPWH *parentInput) - :hpwh(parentInput), isOn(false), lockedOut(false), backupHeatSource(NULL), companionHeatSource(NULL), - followedByHeatSource(NULL), minT(-273.15), maxT(100), hysteresis_dC(0), airflowFreedom(1.0), - typeOfHeatSource(TYPE_none) {} - -HPWH::HeatSource::HeatSource(const HeatSource &hSource){ - hpwh = hSource.hpwh; - isOn = hSource.isOn; - lockedOut = hSource.lockedOut; - - runtime_min = hSource.runtime_min; - energyInput_kWh = hSource.energyInput_kWh; - energyOutput_kWh = hSource.energyOutput_kWh; - - isVIP = hSource.isVIP; - - if (hSource.backupHeatSource != NULL || hSource.companionHeatSource != NULL || hSource.followedByHeatSource != NULL) { - hpwh->simHasFailed = true; - if (hpwh->hpwhVerbosity >= VRB_reluctant) { - hpwh->msg("HeatSources cannot be copied if they contain pointers to other HeatSources\n"); - } - } - - for (int i = 0; i < CONDENSITY_SIZE; i++) { - condensity[i] = hSource.condensity[i]; - } - shrinkage = hSource.shrinkage; - - perfMap = hSource.perfMap; - - //i think vector assignment works correctly here - turnOnLogicSet = hSource.turnOnLogicSet; - shutOffLogicSet = hSource.shutOffLogicSet; - - minT = hSource.minT; - maxT = hSource.maxT; - hysteresis_dC = hSource.hysteresis_dC; - - depressesTemperature = hSource.depressesTemperature; - airflowFreedom = hSource.airflowFreedom; - - configuration = hSource.configuration; - typeOfHeatSource = hSource.typeOfHeatSource; - - lowestNode = hSource.lowestNode; - - -} - -HPWH::HeatSource& HPWH::HeatSource::operator=(const HeatSource &hSource){ - if (this == &hSource) { - return *this; - } - - hpwh = hSource.hpwh; - isOn = hSource.isOn; - lockedOut = hSource.lockedOut; - - runtime_min = hSource.runtime_min; - energyInput_kWh = hSource.energyInput_kWh; - energyOutput_kWh = hSource.energyOutput_kWh; - - isVIP = hSource.isVIP; - - if (hSource.backupHeatSource != NULL || hSource.companionHeatSource != NULL || hSource.followedByHeatSource != NULL) { - hpwh->simHasFailed = true; - if (hpwh->hpwhVerbosity >= VRB_reluctant) { - hpwh->msg("HeatSources cannot be copied if they contain pointers to other HeatSources\n"); - } - } - else { - companionHeatSource = NULL; - backupHeatSource = NULL; - followedByHeatSource = NULL; - } - - for (int i = 0; i < CONDENSITY_SIZE; i++) { - condensity[i] = hSource.condensity[i]; - } - shrinkage = hSource.shrinkage; - - perfMap = hSource.perfMap; - - //i think vector assignment works correctly here - turnOnLogicSet = hSource.turnOnLogicSet; - shutOffLogicSet = hSource.shutOffLogicSet; - - minT = hSource.minT; - maxT = hSource.maxT; - hysteresis_dC = hSource.hysteresis_dC; - - depressesTemperature = hSource.depressesTemperature; - airflowFreedom = hSource.airflowFreedom; - - configuration = hSource.configuration; - typeOfHeatSource = hSource.typeOfHeatSource; - - lowestNode = hSource.lowestNode; - - return *this; -} - - - -void HPWH::HeatSource::setCondensity(double cnd1, double cnd2, double cnd3, double cnd4, - double cnd5, double cnd6, double cnd7, double cnd8, - double cnd9, double cnd10, double cnd11, double cnd12) { - condensity[0] = cnd1; - condensity[1] = cnd2; - condensity[2] = cnd3; - condensity[3] = cnd4; - condensity[4] = cnd5; - condensity[5] = cnd6; - condensity[6] = cnd7; - condensity[7] = cnd8; - condensity[8] = cnd9; - condensity[9] = cnd10; - condensity[10] = cnd11; - condensity[11] = cnd12; -} - -int HPWH::HeatSource::findParent() const { - for (int i = 0; i < hpwh->numHeatSources; ++i) { - if (this == hpwh->setOfSources[i].backupHeatSource) { - return i; - } - } - return -1; -} - -bool HPWH::HeatSource::isEngaged() const { - return isOn; -} - -bool HPWH::HeatSource::isLockedOut() const { - return lockedOut; -} - -void HPWH::HeatSource::lockOutHeatSource() { - lockedOut = true; -} - -void HPWH::HeatSource::unlockHeatSource() { - lockedOut = false; -} - -bool HPWH::HeatSource::shouldLockOut(double heatSourceAmbientT_C) const { - - // if it's already locked out, keep it locked out - if (isLockedOut() == true) { - return true; - } - else { - //when the "external" temperature is too cold - typically used for compressor low temp. cutoffs - //when running, use hysteresis - bool lock = false; - if (isEngaged() == true && heatSourceAmbientT_C < minT - hysteresis_dC) { - lock = true; - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { - hpwh->msg("\tlock-out: running below minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); - } - } - //when not running, don't use hysteresis - else if (isEngaged() == false && heatSourceAmbientT_C < minT) { - lock = true; - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { - hpwh->msg("\tlock-out: already below minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); - } - } - - //when the "external" temperature is too warm - typically used for resistance lockout - //when running, use hysteresis - if (isEngaged() == true && heatSourceAmbientT_C > maxT + hysteresis_dC) { - lock = true; - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { - hpwh->msg("\tlock-out: running above maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); - } - } - //when not running, don't use hysteresis - else if (isEngaged() == false && heatSourceAmbientT_C > maxT) { - lock = true; - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { - hpwh->msg("\tlock-out: already above maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); - } - } - if (lock == true && backupHeatSource == NULL) { - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { - hpwh->msg("\nWARNING: lock-out triggered, but no backupHeatSource defined. Simulation will continue without lock-out"); - } - lock = false; - } - if (hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("\n"); - } - return lock; - } -} - -bool HPWH::HeatSource::shouldUnlock(double heatSourceAmbientT_C) const { - - // if it's already unlocked, keep it unlocked - if (isLockedOut() == false) { - return true; - } - else { - //when the "external" temperature is no longer too cold or too warm - //when running, use hysteresis - bool unlock = false; - if (isEngaged() == true && heatSourceAmbientT_C > minT + hysteresis_dC && heatSourceAmbientT_C < maxT - hysteresis_dC) { - unlock = true; - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C > minT + hysteresis_dC) { - hpwh->msg("\tunlock: running above minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); - } - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C < maxT - hysteresis_dC) { - hpwh->msg("\tunlock: running below maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); - } - } - //when not running, don't use hysteresis - else if (isEngaged() == false && heatSourceAmbientT_C > minT && heatSourceAmbientT_C < maxT) { - unlock = true; - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C > minT) { - hpwh->msg("\tunlock: already above minT\tambient: %.2f\tminT: %.2f", heatSourceAmbientT_C, minT); - } - if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic && heatSourceAmbientT_C < maxT) { - hpwh->msg("\tunlock: already below maxT\tambient: %.2f\tmaxT: %.2f", heatSourceAmbientT_C, maxT); - } - } - if (hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("\n"); - } - return unlock; - } -} - -void HPWH::HeatSource::engageHeatSource() { - isOn = true; - hpwh->isHeating = true; - if (companionHeatSource != NULL && - companionHeatSource->shutsOff() != true && - companionHeatSource->isEngaged() == false) { - companionHeatSource->engageHeatSource(); - } -} - - -void HPWH::HeatSource::disengageHeatSource() { - isOn = false; -} - -bool HPWH::HeatSource::shouldHeat() const { - //return true if the heat source logic tells it to come on, false if it doesn't, - //or if an unsepcified selector was used - bool shouldEngage = false; - - for (int i = 0; i < (int)turnOnLogicSet.size(); i++) { - if (hpwh->hpwhVerbosity >= VRB_emetic) { - hpwh->msg("\tshouldHeat logic: %s ", turnOnLogicSet[i].description.c_str()); - } - - double average = hpwh->tankAvg_C(turnOnLogicSet[i].nodeWeights); - double comparison; - - if (turnOnLogicSet[i].isAbsolute) { - comparison = turnOnLogicSet[i].decisionPoint; - } else { - comparison = hpwh->setpoint_C - turnOnLogicSet[i].decisionPoint; - } - - if (turnOnLogicSet[i].compare(average, comparison)) { - shouldEngage = true; - - //debugging message handling - if (hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("engages!\n"); - } - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("average: %.2lf \t setpoint: %.2lf \t decisionPoint: %.2lf \t comparison: %2.1f\n", average, hpwh->setpoint_C, turnOnLogicSet[i].decisionPoint, comparison); - } - } - - //quit searching the logics if one of them turns it on - if (shouldEngage) break; - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("returns: %d \t", shouldEngage); - } - } //end loop over set of logic conditions - - //if everything else wants it to come on, but if it would shut off anyways don't turn it on - if (shouldEngage == true && shutsOff() == true) { - shouldEngage = false; - if (hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("but is denied by shutsOff"); - } - } - - if (hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("\n"); - } - return shouldEngage; -} - - -bool HPWH::HeatSource::shutsOff() const { - bool shutOff = false; - - if (hpwh->tankTemps_C[0] >= hpwh->setpoint_C) { - shutOff = true; - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("shutsOff bottom node hot: %.2d C \n returns true", hpwh->tankTemps_C[0]); - } - return shutOff; - } - - for (int i = 0; i < (int)shutOffLogicSet.size(); i++) { - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("\tshutsOff logic: %s ", shutOffLogicSet[i].description.c_str()); - } - - double average = hpwh->tankAvg_C(shutOffLogicSet[i].nodeWeights); - double comparison; - - if (shutOffLogicSet[i].isAbsolute) { - comparison = shutOffLogicSet[i].decisionPoint; - } else { - comparison = hpwh->setpoint_C - shutOffLogicSet[i].decisionPoint; - } - - if (shutOffLogicSet[i].compare(average, comparison)) { - shutOff = true; - - //debugging message handling - if (hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("shuts down %s\n", shutOffLogicSet[i].description.c_str()); - } - } - } - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("returns: %d \n", shutOff); - } - return shutOff; -} - - -void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { - double input_BTUperHr, cap_BTUperHr, cop, captmp_kJ, leftoverCap_kJ = 0.0; - - // set the leftover capacity of the Heat Source to 0, so the first round of - // passing it on works - leftoverCap_kJ = 0.0; - - switch (configuration){ - case CONFIG_SUBMERGED: - case CONFIG_WRAPPED: - { - static std::vector heatDistribution(hpwh->numNodes); - //clear the heatDistribution vector, since it's static it is still holding the - //distribution from the last go around - heatDistribution.clear(); - //calcHeatDist takes care of the swooping for wrapped configurations - calcHeatDist(heatDistribution); - - // calculate capacity btu/hr, input btu/hr, and cop - getCapacity(externalT_C, getCondenserTemp(), input_BTUperHr, cap_BTUperHr, cop); - - //some outputs for debugging - if (hpwh->hpwhVerbosity >= VRB_typical){ - hpwh->msg("capacity_kWh %.2lf \t\t cap_BTUperHr %.2lf \n", BTU_TO_KWH(cap_BTUperHr)*(minutesToRun) / 60.0, cap_BTUperHr); - } - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("heatDistribution: %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf \n", heatDistribution[0], heatDistribution[1], heatDistribution[2], heatDistribution[3], heatDistribution[4], heatDistribution[5], heatDistribution[6], heatDistribution[7], heatDistribution[8], heatDistribution[9], heatDistribution[10], heatDistribution[11]); - } - //the loop over nodes here is intentional - essentially each node that has - //some amount of heatDistribution acts as a separate resistive element - //maybe start from the top and go down? test this with graphs - for (int i = hpwh->numNodes - 1; i >= 0; i--){ - //for(int i = 0; i < hpwh->numNodes; i++){ - captmp_kJ = BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0 * heatDistribution[i]); - if (captmp_kJ != 0){ - //add leftoverCap to the next run, and keep passing it on - leftoverCap_kJ = addHeatAboveNode(captmp_kJ + leftoverCap_kJ, i, minutesToRun); - } - } - - //after you've done everything, any leftover capacity is time that didn't run - this->runtime_min = (1.0 - (leftoverCap_kJ / BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0))) * minutesToRun; -#if 1 // error check, 1-22-2017 - if (runtime_min < 0.) - if (hpwh->hpwhVerbosity >= VRB_reluctant) - hpwh->msg("Internal error: Negative runtime = %0.3f min\n", runtime_min); -#endif - } - break; - - case CONFIG_EXTERNAL: - //Else the heat source is external. Sanden system is only current example - //capacity is calculated internal to this function, and cap/input_BTUperHr, cop are outputs - this->runtime_min = addHeatExternal(externalT_C, minutesToRun, cap_BTUperHr, input_BTUperHr, cop); - break; - } - - // Write the input & output energy - energyInput_kWh = BTU_TO_KWH(input_BTUperHr * runtime_min / 60.0); - energyOutput_kWh = BTU_TO_KWH(cap_BTUperHr * runtime_min / 60.0); -} - - - -//the private functions -void HPWH::HeatSource::sortPerformanceMap() { - std::sort(perfMap.begin(), perfMap.end(), - [](const HPWH::HeatSource::perfPoint & a, const HPWH::HeatSource::perfPoint & b) -> bool { - return a.T_F < b.T_F; - }); -} - - -double HPWH::HeatSource::expitFunc(double x, double offset) { - double val; - val = 1 / (1 + exp(x - offset)); - return val; -} - - -void HPWH::HeatSource::normalize(std::vector &distribution) { - double sum_tmp = 0.0; - - for (unsigned int i = 0; i < distribution.size(); i++) { - sum_tmp += distribution[i]; - } - for (unsigned int i = 0; i < distribution.size(); i++) { - if (sum_tmp > 0.0) { - distribution[i] /= sum_tmp; - } - else { - distribution[i] = 0.0; - } - //this gives a very slight speed improvement (milliseconds per simulated year) - if (distribution[i] < HEATDIST_MINVALUE) { - distribution[i] = 0; - } - } -} - - -double HPWH::HeatSource::getCondenserTemp() { - double condenserTemp_C = 0.0; - int tempNodesPerCondensityNode = hpwh->numNodes / CONDENSITY_SIZE; - int j = 0; - - for (int i = 0; i < hpwh->numNodes; i++) { - j = i / tempNodesPerCondensityNode; - if (condensity[j] != 0) { - condenserTemp_C += (condensity[j] / tempNodesPerCondensityNode) * hpwh->tankTemps_C[i]; - //the weights don't need to be added to divide out later because they should always sum to 1 - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("condenserTemp_C:\t %.2lf \ti:\t %d \tj\t %d \tcondensity[j]:\t %.2lf \ttankTemps_C[i]:\t %.2lf\n", condenserTemp_C, i, j, condensity[j], hpwh->tankTemps_C[i]); - } - } - } - if (hpwh->hpwhVerbosity >= VRB_typical){ - hpwh->msg("condenser temp %.2lf \n", condenserTemp_C); - } - return condenserTemp_C; -} - - -void HPWH::HeatSource::getCapacity(double externalT_C, double condenserTemp_C, double &input_BTUperHr, double &cap_BTUperHr, double &cop) { - double COP_T1, COP_T2; //cop at ambient temperatures T1 and T2 - double inputPower_T1_Watts, inputPower_T2_Watts; //input power at ambient temperatures T1 and T2 - double externalT_F, condenserTemp_F; - - // Convert Celsius to Fahrenheit for the curve fits - condenserTemp_F = C_TO_F(condenserTemp_C); - externalT_F = C_TO_F(externalT_C); - - // Get bounding performance map points for interpolation/extrapolation - bool extrapolate = false; - size_t i_prev = 0; - size_t i_next = 1; - for (size_t i = 0; i < perfMap.size(); ++i) { - if (externalT_F < perfMap[i].T_F){ - if (i == 0) { - extrapolate = true; - i_prev = 0; - i_next = 1; - } else { - i_prev = i - 1; - i_next = i; - } - break; - } else { - if (i == perfMap.size() - 1) { - extrapolate = true; - i_prev = i - 1; - i_next = i; - break; - } - } - } - - - // Calculate COP and Input Power at each of the two reference temepratures - COP_T1 = perfMap[i_prev].COP_coeffs[0]; - COP_T1 += perfMap[i_prev].COP_coeffs[1] * condenserTemp_F; - COP_T1 += perfMap[i_prev].COP_coeffs[2] * condenserTemp_F * condenserTemp_F; - - COP_T2 = perfMap[i_next].COP_coeffs[0]; - COP_T2 += perfMap[i_next].COP_coeffs[1] * condenserTemp_F; - COP_T2 += perfMap[i_next].COP_coeffs[2] * condenserTemp_F * condenserTemp_F; - - inputPower_T1_Watts = perfMap[i_prev].inputPower_coeffs[0]; - inputPower_T1_Watts += perfMap[i_prev].inputPower_coeffs[1] * condenserTemp_F; - inputPower_T1_Watts += perfMap[i_prev].inputPower_coeffs[2] * condenserTemp_F * condenserTemp_F; - - inputPower_T2_Watts = perfMap[i_next].inputPower_coeffs[0]; - inputPower_T2_Watts += perfMap[i_next].inputPower_coeffs[1] * condenserTemp_F; - inputPower_T2_Watts += perfMap[i_next].inputPower_coeffs[2] * condenserTemp_F * condenserTemp_F; - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("inputPower_T1_constant_W linear_WperF quadratic_WperF2 \t%.2lf %.2lf %.2lf \n", perfMap[0].inputPower_coeffs[0], perfMap[0].inputPower_coeffs[1], perfMap[0].inputPower_coeffs[2]); - hpwh->msg("inputPower_T2_constant_W linear_WperF quadratic_WperF2 \t%.2lf %.2lf %.2lf \n", perfMap[1].inputPower_coeffs[0], perfMap[1].inputPower_coeffs[1], perfMap[1].inputPower_coeffs[2]); - hpwh->msg("inputPower_T1_Watts: %.2lf \tinputPower_T2_Watts: %.2lf \n", inputPower_T1_Watts, inputPower_T2_Watts); - - if (extrapolate) { - hpwh->msg("Warning performance extrapolation\n\tExternal Temperature: %.2lf\tNearest temperatures: %.2lf, %.2lf \n\n", externalT_F, perfMap[i_prev].T_F, perfMap[i_next].T_F); - } - - } - - // Interpolate to get COP and input power at the current ambient temperature - cop = COP_T1 + (externalT_F - perfMap[i_prev].T_F) * ((COP_T2 - COP_T1) / (perfMap[i_next].T_F - perfMap[i_prev].T_F)); - input_BTUperHr = KWH_TO_BTU((inputPower_T1_Watts + (externalT_F - perfMap[i_prev].T_F) * - ((inputPower_T2_Watts - inputPower_T1_Watts) - / (perfMap[i_next].T_F - perfMap[i_prev].T_F)) - ) / 1000.0); //1000 converts w to kw - cap_BTUperHr = cop * input_BTUperHr; - - //here is where the scaling for flow restriction happens - //the input power doesn't change, we just scale the cop by a small percentage - //that is based on the flow rate. The equation is a fit to three points, - //measured experimentally - 12 percent reduction at 150 cfm, 10 percent at - //200, and 0 at 375. Flow is expressed as fraction of full flow. - if (airflowFreedom != 1){ - double airflow = 375 * airflowFreedom; - cop *= 0.00056*airflow + 0.79; - } - if (hpwh->hpwhVerbosity >= VRB_typical){ - hpwh->msg("cop: %.2lf \tinput_BTUperHr: %.2lf \tcap_BTUperHr: %.2lf \n", cop, input_BTUperHr, cap_BTUperHr); - } -} - - -void HPWH::HeatSource::calcHeatDist(std::vector &heatDistribution) { - - // Populate the vector of heat distribution - for (int i = 0; i < hpwh->numNodes; i++) { - if (i < lowestNode) { - heatDistribution.push_back(0); - } - else { - int k; - if (configuration == CONFIG_SUBMERGED) { // Inside the tank, no swoopiness required - //intentional integer division - k = i / int(hpwh->numNodes / CONDENSITY_SIZE); - heatDistribution.push_back(condensity[k]); - } - else if (configuration == CONFIG_WRAPPED) { // Wrapped around the tank, send through the logistic function - double temp = 0; //temp for temporary not temperature - double offset = 5.0 / 1.8; - temp = expitFunc((hpwh->tankTemps_C[i] - hpwh->tankTemps_C[lowestNode]) / this->shrinkage, offset); - temp *= (hpwh->setpoint_C - hpwh->tankTemps_C[i]); -#if defined( SETPOINT_FIX) - if (temp < 0.) - temp = 0.; -#endif - heatDistribution.push_back(temp); - } - } - } - normalize(heatDistribution); - -} - - -double HPWH::HeatSource::addHeatAboveNode(double cap_kJ, int node, double minutesToRun) { - double Q_kJ, deltaT_C, targetTemp_C; - int setPointNodeNum; - - double volumePerNode_L = hpwh->tankVolume_L / hpwh->numNodes; - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("node %2d cap_kwh %.4lf \n", node, KJ_TO_KWH(cap_kJ)); - } - - // find the first node (from the bottom) that does not have the same temperature as the one above it - // if they all have the same temp., use the top node, hpwh->numNodes-1 - setPointNodeNum = node; - for (int i = node; i < hpwh->numNodes - 1; i++){ - if (hpwh->tankTemps_C[i] != hpwh->tankTemps_C[i + 1]) { - break; - } - else{ - setPointNodeNum = i + 1; - } - } - - // maximum heat deliverable in this timestep - while (cap_kJ > 0 && setPointNodeNum < hpwh->numNodes) { - // if the whole tank is at the same temp, the target temp is the setpoint - if (setPointNodeNum == (hpwh->numNodes - 1)) { - targetTemp_C = hpwh->setpoint_C; - } - //otherwise the target temp is the first non-equal-temp node - else { - targetTemp_C = hpwh->tankTemps_C[setPointNodeNum + 1]; - } - - deltaT_C = targetTemp_C - hpwh->tankTemps_C[setPointNodeNum]; - - //heat needed to bring all equal temp. nodes up to the temp of the next node. kJ - Q_kJ = CPWATER_kJperkgC * volumePerNode_L * DENSITYWATER_kgperL * (setPointNodeNum + 1 - node) * deltaT_C; - - //Running the rest of the time won't recover - if (Q_kJ > cap_kJ){ - for (int j = node; j <= setPointNodeNum; j++) { - hpwh->tankTemps_C[j] += cap_kJ / CPWATER_kJperkgC / volumePerNode_L / DENSITYWATER_kgperL / (setPointNodeNum + 1 - node); - } - cap_kJ = 0; - } -#if defined( SETPOINT_FIX) - else if (Q_kJ > 0.) - { // temp will recover by/before end of timestep - for (int j = node; j <= setPointNodeNum; j++) - hpwh->tankTemps_C[j] = targetTemp_C; - cap_kJ -= Q_kJ; - } - setPointNodeNum++; -#else - //temp will recover by/before end of timestep - else{ - for (int j = node; j <= setPointNodeNum; j++){ - hpwh->tankTemps_C[j] = targetTemp_C; - } - setPointNodeNum++; - cap_kJ -= Q_kJ; - } -#endif - } - - //return the unused capacity - return cap_kJ; -} - - -double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun, double &cap_BTUperHr, double &input_BTUperHr, double &cop) { - double heatingCapacity_kJ, deltaT_C, timeUsed_min, nodeHeat_kJperNode, nodeFrac; - double inputTemp_BTUperHr = 0, capTemp_BTUperHr = 0, copTemp = 0; - double volumePerNode_LperNode = hpwh->tankVolume_L / hpwh->numNodes; - double timeRemaining_min = minutesToRun; - - input_BTUperHr = 0; - cap_BTUperHr = 0; - cop = 0; - - do{ - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("bottom tank temp: %.2lf \n", hpwh->tankTemps_C[0]); - } - - //how much heat is available this timestep - getCapacity(externalT_C, hpwh->tankTemps_C[0], inputTemp_BTUperHr, capTemp_BTUperHr, copTemp); - heatingCapacity_kJ = BTU_TO_KJ(capTemp_BTUperHr * (minutesToRun / 60.0)); - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("\theatingCapacity_kJ stepwise: %.2lf \n", heatingCapacity_kJ); - } - - //adjust capacity for how much time is left in this step - heatingCapacity_kJ = heatingCapacity_kJ * (timeRemaining_min / minutesToRun); - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("\theatingCapacity_kJ remaining this node: %.2lf \n", heatingCapacity_kJ); - } - - //calculate what percentage of the bottom node can be heated to setpoint - //with amount of heat available this timestep - deltaT_C = hpwh->setpoint_C - hpwh->tankTemps_C[0]; - nodeHeat_kJperNode = volumePerNode_LperNode * DENSITYWATER_kgperL * CPWATER_kJperkgC * deltaT_C; - //protect against dividing by zero - if bottom node is at (or above) setpoint, - //add no heat - if (nodeHeat_kJperNode <= 0) { - nodeFrac = 0; - } - else { - nodeFrac = heatingCapacity_kJ / nodeHeat_kJperNode; - } - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("nodeHeat_kJperNode: %.2lf nodeFrac: %.2lf \n\n", nodeHeat_kJperNode, nodeFrac); - } - //if more than one, round down to 1 and subtract the amount of time it would - //take to heat that node from the timeRemaining - if (nodeFrac > 1){ - nodeFrac = 1; - timeUsed_min = (nodeHeat_kJperNode / heatingCapacity_kJ)*timeRemaining_min; - timeRemaining_min -= timeUsed_min; - } - //otherwise just the fraction available - //this should make heatingCapacity == 0 if nodeFrac < 1 - else{ - timeUsed_min = timeRemaining_min; - timeRemaining_min = 0; - } - - //move all nodes down, mixing if less than a full node - for (int n = 0; n < hpwh->numNodes - 1; n++) { - hpwh->tankTemps_C[n] = hpwh->tankTemps_C[n] * (1 - nodeFrac) + hpwh->tankTemps_C[n + 1] * nodeFrac; - } - //add water to top node, heated to setpoint - hpwh->tankTemps_C[hpwh->numNodes - 1] = hpwh->tankTemps_C[hpwh->numNodes - 1] * (1 - nodeFrac) + hpwh->setpoint_C * nodeFrac; - - - //track outputs - weight by the time ran - input_BTUperHr += inputTemp_BTUperHr*timeUsed_min; - cap_BTUperHr += capTemp_BTUperHr*timeUsed_min; - cop += copTemp*timeUsed_min; - - - //if there's still time remaining and you haven't heated to the cutoff - //specified in shutsOff logic, keep heating - } while (timeRemaining_min > 0 && shutsOff() != true); - - //divide outputs by sum of weight - the total time ran - input_BTUperHr /= (minutesToRun - timeRemaining_min); - cap_BTUperHr /= (minutesToRun - timeRemaining_min); - cop /= (minutesToRun - timeRemaining_min); - - if (hpwh->hpwhVerbosity >= VRB_emetic){ - hpwh->msg("final remaining time: %.2lf \n", timeRemaining_min); - } - //return the time left - return minutesToRun - timeRemaining_min; -} - - - - - - -void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { - int i; - - isOn = false; - isVIP = false; - for (i = 0; i < CONDENSITY_SIZE; i++) { - condensity[i] = 0; - } - condensity[node] = 1; - - perfMap.reserve(2); - - perfMap.push_back({ - 50, // Temperature (T_F) - {Watts, 0.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {1.0, 0.0, 0.0} // COP Coefficients (COP_coeffs) - }); - - perfMap.push_back({ - 67, // Temperature (T_F) - {Watts, 0.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {1.0, 0.0, 0.0} // COP Coefficients (COP_coeffs) - }); - - configuration = CONFIG_SUBMERGED; //immersed in tank - - typeOfHeatSource = TYPE_resistance; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// -void HPWH::HeatSource::setupExtraHeat(std::vector* nodeExtraHeat_W) { - - //get sum of vector - double watts = 0.0; - for (unsigned int i = 0; i < (*nodeExtraHeat_W).size(); i++) { - watts += (*nodeExtraHeat_W)[i]; - } - - std::vector tempCondensity = *nodeExtraHeat_W; - normalize(tempCondensity); - // set condensity based on normalized vector - setCondensity( tempCondensity[0], tempCondensity[1], tempCondensity[2], tempCondensity[3], - tempCondensity[4], tempCondensity[5], tempCondensity[6], tempCondensity[7], - tempCondensity[8], tempCondensity[9], tempCondensity[10], tempCondensity[11] ); - - perfMap.clear(); - perfMap.reserve(2); - - perfMap.push_back({ - 50, // Temperature (T_F) - { watts, 0.0, 0.0 }, // Input Power Coefficients (inputPower_coeffs) - { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) - }); - - perfMap.push_back({ - 67, // Temperature (T_F) - { watts, 0.0, 0.0 }, // Input Power Coefficients (inputPower_coeffs) - { 1.0, 0.0, 0.0 } // COP Coefficients (COP_coeffs) - }); - -} -//////////////////////////////////////////////////////////////////////////// - -void HPWH::HeatSource::addTurnOnLogic(HeatingLogic logic){ - this->turnOnLogicSet.push_back(logic); -} -void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { - this->shutOffLogicSet.push_back(logic); -} - -void HPWH::calcDerivedValues(){ - // tank node density (number of calculation nodes per regular node) - nodeDensity = numNodes / 12; - - // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() - calcDerivedHeatingValues(); - - //heat source ability to depress temp - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { - setOfSources[i].depressesTemperature = true; - } - else if (setOfSources[i].typeOfHeatSource == TYPE_resistance) { - setOfSources[i].depressesTemperature = false; - } - } -} - -void HPWH::calcDerivedHeatingValues(){ - static char outputString[MAXOUTSTRING]; //this is used for debugging outputs - - //condentropy/shrinkage - double condentropy = 0; - double alpha = 1, beta = 2; // Mapping from condentropy to shrinkage - for (int i = 0; i < numHeatSources; i++) { - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, "Heat Source %d \n", i); - } - - // Calculate condentropy and ==> shrinkage - condentropy = 0; - for (int j = 0; j < CONDENSITY_SIZE; j++) { - if (setOfSources[i].condensity[j] > 0) { - condentropy -= setOfSources[i].condensity[j] * log(setOfSources[i].condensity[j]); - if (hpwhVerbosity >= VRB_emetic) msg(outputString, "condentropy %.2lf \n", condentropy); - } - } - setOfSources[i].shrinkage = alpha + condentropy * beta; - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, "shrinkage %.2lf \n\n", setOfSources[i].shrinkage); - } - } - - //lowest node - int lowest = 0; - for (int i = 0; i < numHeatSources; i++) { - lowest = 0; - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, "Heat Source %d \n", i); - } - - for (int j = 0; j < numNodes; j++) { - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, "j: %d j/ (numNodes/CONDENSITY_SIZE) %d \n", j, j / (numNodes / CONDENSITY_SIZE)); - } - - if (setOfSources[i].condensity[(j / (numNodes / CONDENSITY_SIZE))] > 0) { - lowest = j; - break; - } - } - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, " lowest : %d \n", lowest); - } - - setOfSources[i].lowestNode = lowest; - } - - // define condenser index and lowest resistance element index - compressorIndex = -1; // Default = No compressor - lowestElementIndex = -1; // Default = No resistance elements - int lowestElementPos = CONDENSITY_SIZE; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].typeOfHeatSource == HPWH::TYPE_compressor) { - compressorIndex = i; // NOTE: Maybe won't work with multiple compressors (last compressor will be used) - } - else { - for (int j = 0; j < CONDENSITY_SIZE; j++) { - if (setOfSources[i].condensity[j] > 0.0 && j < lowestElementPos) { - lowestElementIndex = i; - lowestElementPos = j; - break; - } - } - } - } - - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, " compressorIndex : %d \n", compressorIndex); - } - if (hpwhVerbosity >= VRB_emetic) { - msg(outputString, " lowestElementIndex : %d \n", lowestElementIndex); - } -} - - -// Used to check a few inputs after the initialization of a tank model from a preset or a file. -int HPWH::checkInputs(){ - int returnVal = 0; - //use a returnVal so that all checks are processed and error messages written - - if (numHeatSources <= 0 && hpwhModel != MODELS_StorageTank ) { - msg("You must have at least one HeatSource.\n"); - returnVal = HPWH_ABORT; - } - if ((numNodes % 12) != 0) { - msg("The number of nodes must be a multiple of 12"); - returnVal = HPWH_ABORT; - } - - - double condensitySum; - //loop through all heat sources to check each for malconfigurations - for (int i = 0; i < numHeatSources; i++) { - //check the heat source type to make sure it has been set - if (setOfSources[i].typeOfHeatSource == TYPE_none) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Heat source %d does not have a specified type. Initialization failed.\n", i); - } - returnVal = HPWH_ABORT; - } - //check to make sure there is at least one onlogic or parent with onlogic - int parent = setOfSources[i].findParent(); - if (setOfSources[i].turnOnLogicSet.size() == 0 && (parent == -1 || setOfSources[parent].turnOnLogicSet.size() == 0)) { - msg("You must specify at least one logic to turn on the element or the element must be set as a backup for another heat source with at least one logic."); - returnVal = HPWH_ABORT; - } - //check is condensity sums to 1 - condensitySum = 0; - for (int j = 0; j < CONDENSITY_SIZE; j++) condensitySum += setOfSources[i].condensity[j]; - if (fabs(condensitySum - 1.0) > 1e-6) { - msg("The condensity for heatsource %d does not sum to 1. \n", i); - msg("It sums to %f \n", condensitySum); - returnVal = HPWH_ABORT; - } - //check that air flows are all set properly - if (setOfSources[i].airflowFreedom > 1.0 || setOfSources[i].airflowFreedom <= 0.0) { - msg("The airflowFreedom must be between 0 and 1 for heatsource %d. \n", i); - returnVal = HPWH_ABORT; - } - - - } - - //Check if the UA is out of bounds - if (tankUA_kJperHrC < 0.0) { - msg("The tankUA_kJperHrC is less than 0 for a HPWH, it must be greater than 0, tankUA_kJperHrC is: %f \n", tankUA_kJperHrC); - returnVal = HPWH_ABORT; - } - - - - //if there's no failures, return 0 - return returnVal; -} - -#ifndef HPWH_ABRIDGED -int HPWH::HPWHinit_file(string configFile){ - simHasFailed = true; //this gets cleared on successful completion of init - - //clear out old stuff if you're re-initializing - delete[] tankTemps_C; - delete[] nextTankTemps_C; - delete[] setOfSources; - - - //open file, check and report errors - std::ifstream inputFILE; - inputFILE.open(configFile.c_str()); - if (!inputFILE.is_open()) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Input file failed to open. \n"); - } - return HPWH_ABORT; - } - - //some variables that will be handy - int heatsource, sourceNum, nTemps; - string tempString, units; - double tempDouble, dblArray[12]; - - //being file processing, line by line - string line_s; - std::stringstream line_ss; - string token; - while (std::getline(inputFILE, line_s)){ - line_ss.clear(); - line_ss.str(line_s); - - //grab the first word, and start comparing - line_ss >> token; - if (token.at(0) == '#' || line_s.empty()) { - //if you hit a comment, skip to next line - continue; - } - else if (token == "numNodes") { - line_ss >> numNodes; - } - else if (token == "volume") { - line_ss >> tempDouble >> units; - if (units == "gal") tempDouble = GAL_TO_L(tempDouble); - else if (units == "L"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s. \n", token.c_str()); - } - return HPWH_ABORT; - } - tankVolume_L = tempDouble; - } - else if (token == "UA") { - line_ss >> tempDouble >> units; - if (units != "kJperHrC") { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s. \n", token.c_str()); - } - return HPWH_ABORT; - } - tankUA_kJperHrC = tempDouble; - } - else if (token == "depressTemp") { - line_ss >> tempString; - if (tempString == "true") { - doTempDepression = true; - } - else if (tempString == "false") { - doTempDepression = false; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper value for %s\n", token.c_str()); - } - return HPWH_ABORT; - } - } - else if (token == "mixOnDraw") { - line_ss >> tempString; - if (tempString == "true") { - tankMixesOnDraw = true; - } - else if (tempString == "false") { - tankMixesOnDraw = false; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper value for %s\n", token.c_str()); - } - return HPWH_ABORT; - } - } - else if (token == "setpoint") { - line_ss >> tempDouble >> units; - if (units == "F") tempDouble = F_TO_C(tempDouble); - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s. \n", token.c_str()); - } - return HPWH_ABORT; - } - setpoint_C = tempDouble; - //tank will be set to setpoint at end of function - } - else if (token == "setpointFixed") { - line_ss >> tempString; - if (tempString == "true") setpointFixed = true; - else if (tempString == "false") setpointFixed = false; - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper value for %s\n", token.c_str()); - } - return HPWH_ABORT; - } - } - else if (token == "verbosity") { - line_ss >> token; - if (token == "silent") { - hpwhVerbosity = VRB_silent; - } - else if (token == "reluctant") { - hpwhVerbosity = VRB_reluctant; - } - else if (token == "typical") { - hpwhVerbosity = VRB_typical; - } - else if (token == "emetic") { - hpwhVerbosity = VRB_emetic; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect verbosity on input. \n"); - } - return HPWH_ABORT; - } - } - - else if (token == "numHeatSources") { - line_ss >> numHeatSources; - setOfSources = new HeatSource[numHeatSources]; - for (int i = 0; i < numHeatSources; i++) { - setOfSources[i] = HeatSource(this); - } - } - else if (token == "heatsource") { - if (numHeatSources == 0) { - msg("You must specify the number of heatsources before setting their properties. \n"); - return HPWH_ABORT; - } - line_ss >> heatsource >> token; - if (token == "isVIP") { - line_ss >> tempString; - if (tempString == "true") setOfSources[heatsource].isVIP = true; - else if (tempString == "false") setOfSources[heatsource].isVIP = false; - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper value for %s for heat source %d\n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - } - else if (token == "isOn") { - line_ss >> tempString; - if (tempString == "true") setOfSources[heatsource].isOn = true; - else if (tempString == "false") setOfSources[heatsource].isOn = false; - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper value for %s for heat source %d\n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - } - else if (token == "minT") { - line_ss >> tempDouble >> units; - if (units == "F") tempDouble = F_TO_C(tempDouble); - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s. \n", token.c_str()); - } - return HPWH_ABORT; - } - setOfSources[heatsource].minT = tempDouble; - } - else if (token == "maxT") { - line_ss >> tempDouble >> units; - if (units == "F") tempDouble = F_TO_C(tempDouble); - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s. \n", token.c_str()); - } - return HPWH_ABORT; - } - setOfSources[heatsource].maxT = tempDouble; - } - else if (token == "onlogic" || token == "offlogic") { - line_ss >> tempString; - if (tempString == "nodes") { - std::vector nodeNums; - std::vector weights; - std::string nextToken; - line_ss >> nextToken; - while (std::regex_match(nextToken, std::regex("\\d+"))) { - int nodeNum = std::stoi(nextToken); - if (nodeNum > 13 || nodeNum < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Node number for heatsource %d %s must be between 0 and 13. \n", heatsource, token.c_str()); - } - return HPWH_ABORT; - } - nodeNums.push_back(nodeNum); - line_ss >> nextToken; - } - if (nextToken == "weights") { - line_ss >> nextToken; - while (std::regex_match(nextToken, std::regex("-?\\d*\\.\\d+(?:e-?\\d+)?"))) { - weights.push_back(std::stod(nextToken)); - line_ss >> nextToken; - } - } else { - for (auto n : nodeNums) { - n += 0; // used to get rid of unused variable compiler warning - weights.push_back(1.0); - } - } - if (nodeNums.size() != weights.size()) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Number of weights for heatsource %d %s (%d) does not macht number of nodes for %s (%d). \n", heatsource, token.c_str(), weights.size(), token.c_str(), nodeNums.size()); - } - return HPWH_ABORT; - } - if (nextToken != "absolute" && nextToken != "relative") { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper definition, \"%s\", for heat source %d %s. Should be \"relative\" or \"absoute\".\n", nextToken.c_str(), heatsource, token.c_str()); - } - return HPWH_ABORT; - } - bool absolute = (nextToken == "absolute"); - std::string compareStr; - line_ss >> compareStr >> tempDouble >> units; - std::function compare; - if (compareStr == "<") compare = std::less(); - else if (compareStr == ">") compare = std::greater(); - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper comparison, \"%s\", for heat source %d %s. Should be \"<\" or \">\".\n", compareStr.c_str(), heatsource, token.c_str()); - } - return HPWH_ABORT; - } - if (units == "F") { - if (absolute) { - tempDouble = F_TO_C(tempDouble); - } - else { - tempDouble = dF_TO_dC(tempDouble); - } - } - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - std::vector nodeWeights; - for (size_t i = 0; i < nodeNums.size(); i++ ) { - nodeWeights.emplace_back(nodeNums[i],weights[i]); - } - HPWH::HeatingLogic logic("custom", nodeWeights, tempDouble, absolute, compare); - if (token == "onlogic") { - setOfSources[heatsource].addTurnOnLogic(logic); - } else { // "offlogic" - setOfSources[heatsource].addShutOffLogic(logic); - } - } - else if (token == "onlogic") { - line_ss >> tempDouble >> units; - if (units == "F") tempDouble = dF_TO_dC(tempDouble); - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - if (tempString == "topThird") { - setOfSources[heatsource].addTurnOnLogic(HPWH::topThird(tempDouble)); - } - else if (tempString == "bottomThird") { - setOfSources[heatsource].addTurnOnLogic(HPWH::bottomThird(tempDouble)); - } - else if (tempString == "standby") { - setOfSources[heatsource].addTurnOnLogic(HPWH::standby(tempDouble)); - } - else if (tempString == "bottomSixth") { - setOfSources[heatsource].addTurnOnLogic(HPWH::bottomSixth(tempDouble)); - } - else if (tempString == "secondSixth") { - setOfSources[heatsource].addTurnOnLogic(HPWH::secondSixth(tempDouble)); - } - else if (tempString == "thirdSixth") { - setOfSources[heatsource].addTurnOnLogic(HPWH::thirdSixth(tempDouble)); - } - else if (tempString == "fourthSixth") { - setOfSources[heatsource].addTurnOnLogic(HPWH::fourthSixth(tempDouble)); - } - else if (tempString == "fifthSixth") { - setOfSources[heatsource].addTurnOnLogic(HPWH::fifthSixth(tempDouble)); - } - else if (tempString == "topSixth") { - setOfSources[heatsource].addTurnOnLogic(HPWH::topSixth(tempDouble)); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - } - else if (token == "offlogic") { - line_ss >> tempDouble >> units; - if (units == "F") tempDouble = F_TO_C(tempDouble); - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - if (tempString == "topNodeMaxTemp") { - setOfSources[heatsource].addShutOffLogic(HPWH::topNodeMaxTemp(tempDouble)); - } - else if (tempString == "bottomNodeMaxTemp") { - setOfSources[heatsource].addShutOffLogic(HPWH::bottomNodeMaxTemp(tempDouble)); - } - else if (tempString == "bottomTwelthMaxTemp") { - setOfSources[heatsource].addShutOffLogic(HPWH::bottomTwelthMaxTemp(tempDouble)); - } - else if (tempString == "largeDraw") { - setOfSources[heatsource].addShutOffLogic(HPWH::largeDraw(tempDouble)); - } - else if (tempString == "largerDraw") { - setOfSources[heatsource].addShutOffLogic(HPWH::largerDraw(tempDouble)); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - } - } - else if (token == "type") { - line_ss >> tempString; - if (tempString == "resistor") { - setOfSources[heatsource].typeOfHeatSource = TYPE_resistance; - } - else if (tempString == "compressor") { - setOfSources[heatsource].typeOfHeatSource = TYPE_compressor; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - } - else if (token == "coilConfig") { - line_ss >> tempString; - if (tempString == "wrapped") { - setOfSources[heatsource].configuration = HeatSource::CONFIG_WRAPPED; - } - else if (tempString == "submerged") { - setOfSources[heatsource].configuration = HeatSource::CONFIG_SUBMERGED; - } - else if (tempString == "external") { - setOfSources[heatsource].configuration = HeatSource::CONFIG_EXTERNAL; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - } - else if (token == "condensity") { - line_ss >> dblArray[0] >> dblArray[1] >> dblArray[2] >> dblArray[3] >> dblArray[4] >> dblArray[5] >> dblArray[6] >> dblArray[7] >> dblArray[8] >> dblArray[9] >> dblArray[10] >> dblArray[11]; - setOfSources[heatsource].setCondensity(dblArray[0], dblArray[1], dblArray[2], dblArray[3], dblArray[4], dblArray[5], dblArray[6], dblArray[7], dblArray[8], dblArray[9], dblArray[10], dblArray[11]); - } - else if (token == "nTemps") { - line_ss >> nTemps; - setOfSources[heatsource].perfMap.resize(nTemps); - } - else if (std::regex_match(token, std::regex("T\\d+"))){ - std::smatch match; - std::regex_match(token, match, std::regex("T(\\d+)")); - nTemps = std::stoi(match[1].str()); - int maxTemps = setOfSources[heatsource].perfMap.size(); - - if (maxTemps < nTemps) { - if (maxTemps == 0) { - if (true || hpwhVerbosity >= VRB_reluctant){ - msg("%s specified for heatsource %d before definition of nTemps. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - else { - if (true || hpwhVerbosity >= VRB_reluctant){ - msg("Incorrect specification for %s from heatsource %d. nTemps, %d, is less than %d. \n", token.c_str(), heatsource, maxTemps, nTemps); - } - return HPWH_ABORT; - } - } - line_ss >> tempDouble >> units; - // if (units == "F") tempDouble = F_TO_C(tempDouble); - if (units == "F"); - // else if (units == "C") ; //do nothing, lol - else if (units == "C") tempDouble = C_TO_F(tempDouble); - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - setOfSources[heatsource].perfMap[nTemps - 1].T_F = tempDouble; - } - else if (std::regex_match(token, std::regex("(?:inPow|cop)T\\d+(?:const|lin|quad)"))){ - std::smatch match; - std::regex_match(token, match, std::regex("(inPow|cop)T(\\d+)(const|lin|quad)")); - string var = match[1].str(); - nTemps = std::stoi(match[2].str()); - string coeff = match[3].str(); - int coeff_num; - if (coeff == "const"){ - coeff_num = 0; - } - else if (coeff == "lin"){ - coeff_num = 1; - } - else if (coeff == "quad"){ - coeff_num = 2; - } - - int maxTemps = setOfSources[heatsource].perfMap.size(); - - if (maxTemps < nTemps) { - if (maxTemps == 0) { - if (hpwhVerbosity >= VRB_reluctant){ - msg("%s specified for heatsource %d before definition of nTemps. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - else { - if (hpwhVerbosity >= VRB_reluctant){ - msg("Incorrect specification for %s from heatsource %d. nTemps, %d, is less than %d. \n", token.c_str(), heatsource, maxTemps, nTemps); - } - return HPWH_ABORT; - } - } - line_ss >> tempDouble; - - if (var == "inPow") { - setOfSources[heatsource].perfMap[nTemps - 1].inputPower_coeffs[coeff_num] = tempDouble; - } - else if (var == "cop") { - setOfSources[heatsource].perfMap[nTemps - 1].COP_coeffs[coeff_num] = tempDouble; - } - } - else if (token == "hysteresis"){ - line_ss >> tempDouble >> units; - if (units == "F") tempDouble = dF_TO_dC(tempDouble); - else if (units == "C"); //do nothing, lol - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s from heatsource %d. \n", token.c_str(), heatsource); - } - return HPWH_ABORT; - } - setOfSources[heatsource].hysteresis_dC = tempDouble; - } - else if (token == "backupSource"){ - line_ss >> sourceNum; - setOfSources[heatsource].backupHeatSource = &setOfSources[sourceNum]; - } - else if (token == "companionSource"){ - line_ss >> sourceNum; - setOfSources[heatsource].companionHeatSource = &setOfSources[sourceNum]; - } - else if (token == "followedBySource"){ - line_ss >> sourceNum; - setOfSources[heatsource].followedByHeatSource = &setOfSources[sourceNum]; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Improper specifier (%s) for heat source %d\n", token.c_str(), heatsource); - } - } - - } //end heatsource options - else { - msg("Improper keyword: %s \n", token.c_str()); - return HPWH_ABORT; - } - - } //end while over lines - - - //take care of the non-input processing - hpwhModel = MODELS_CustomFile; - - tankTemps_C = new double[numNodes]; - resetTankToSetpoint(); - - nextTankTemps_C = new double[numNodes]; - - isHeating = false; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].isOn) { - isHeating = true; - } - setOfSources[i].sortPerformanceMap(); - } - - calcDerivedValues(); - - if (checkInputs() == HPWH_ABORT) { - return HPWH_ABORT; - } - simHasFailed = false; - return 0; -} -#endif - -int HPWH::HPWHinit_resTank(){ - //a default resistance tank, nominal 50 gallons, 0.95 EF, standard double 4.5 kW elements - return this->HPWHinit_resTank(GAL_TO_L(47.5), 0.95, 4500, 4500); -} -int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPower_W, double lowerPower_W){ - //low power element will cause divide by zero/negative UA in EF -> UA conversion - if (lowerPower_W < 550) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Resistance tank wattage below 550 W. DOES NOT COMPUTE\n"); - } - return HPWH_ABORT; - } - if (energyFactor <= 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Energy Factor less than zero. DOES NOT COMPUTE\n"); - } - return HPWH_ABORT; - } - - //use tank size setting function since it has bounds checking - int failure = this->setTankSize(tankVol_L); - if (failure == HPWH_ABORT) { - return failure; - } - - - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - nextTankTemps_C = new double[numNodes]; - - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 2; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - resistiveElementBottom.setupAsResistiveElement(0, lowerPower_W); - resistiveElementTop.setupAsResistiveElement(8, upperPower_W); - - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.isVIP = true; - - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - - // (1/EnFac + 1/RecovEff) / (67.5 * ((24/41094) - 1/(RecovEff * Power_btuperHr))) - double recoveryEfficiency = 0.98; - double numerator = (1.0 / energyFactor) - (1.0 / recoveryEfficiency); - double temp = 1.0 / (recoveryEfficiency * lowerPower_W*3.41443); - double denominator = 67.5 * ((24.0 / 41094.0) - temp); - tankUA_kJperHrC = UAf_TO_UAc(numerator / denominator); - - if (tankUA_kJperHrC < 0.) { - if (hpwhVerbosity >= VRB_reluctant && tankUA_kJperHrC < -0.1) { - msg("Computed tankUA_kJperHrC is less than 0, and is reset to 0."); - } - tankUA_kJperHrC = 0.0; - } - - hpwhModel = MODELS_CustomResTank; - - //calculate oft-used derived values - calcDerivedValues(); - - if (checkInputs() == HPWH_ABORT) return HPWH_ABORT; - - isHeating = false; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].isOn) { - isHeating = true; - } - setOfSources[i].sortPerformanceMap(); - } - - - if (hpwhVerbosity >= VRB_emetic){ - for (int i = 0; i < numHeatSources; i++) { - msg("heat source %d: %p \n", i, &setOfSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - return 0; //successful init returns 0 -} - -int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C){ - //return 0 on success, HPWH_ABORT for failure - simHasFailed = true; //this gets cleared on successful completion of init - - //clear out old stuff if you're re-initializing - delete[] tankTemps_C; - delete[] setOfSources; - - - - - //except where noted, these values are taken from MODELS_GE2014STDMode on 5/17/16 - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - nextTankTemps_C = new double[numNodes]; - - //start tank off at setpoint - resetTankToSetpoint(); - - //custom settings - these are set later - //tankVolume_L = GAL_TO_L(45); - //tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(45.); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - //this is set customly, from input - //resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(19.6605))); - resistiveElementTop.addTurnOnLogic(HPWH::topThird(resUse_C)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); - - //custom adjustment for poorer performance - //compressor.addShutOffLogic(HPWH::lowT(F_TO_C(37))); - - - //end section of parameters from GE model - - - - - //set tank volume from input - //use tank size setting function since it has bounds checking - int failure = this->setTankSize(tankVol_L); - if (failure == HPWH_ABORT) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Failure to set tank size in generic hpwh init."); - } - return failure; - } - - - // derive conservative (high) UA from tank volume - // curve fit by Jim Lutz, 5-25-2016 - double tankVol_gal = tankVol_L / GAL_TO_L(1.); - double v1 = 7.5156316175 * pow(tankVol_gal, 0.33) + 5.9995357658; - tankUA_kJperHrC = 0.0076183819 * v1 * v1; - - - - - //do a linear interpolation to scale COP curve constant, using measured values - // Chip's attempt 24-May-2014 - double uefSpan = 3.4 - 2.0; - - //force COP to be 70% of GE at UEF 2 and 95% at UEF 3.4 - //use a fudge factor to scale cop and input power in tandem to maintain constant capacity - double fUEF = (energyFactor - 2.0) / uefSpan; - double genericFudge = (1. - fUEF)*.7 + fUEF*.95; - - compressor.perfMap[0].COP_coeffs[0] *= genericFudge; - compressor.perfMap[0].COP_coeffs[1] *= genericFudge; - compressor.perfMap[0].COP_coeffs[2] *= genericFudge; - - compressor.perfMap[1].COP_coeffs[0] *= genericFudge; - compressor.perfMap[1].COP_coeffs[1] *= genericFudge; - compressor.perfMap[1].COP_coeffs[2] *= genericFudge; - - compressor.perfMap[0].inputPower_coeffs[0] /= genericFudge; - compressor.perfMap[0].inputPower_coeffs[1] /= genericFudge; - compressor.perfMap[0].inputPower_coeffs[2] /= genericFudge; - - compressor.perfMap[1].inputPower_coeffs[0] /= genericFudge; - compressor.perfMap[1].inputPower_coeffs[1] /= genericFudge; - compressor.perfMap[1].inputPower_coeffs[2] /= genericFudge; - - - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - - - //standard finishing up init, borrowed from init function - - hpwhModel = MODELS_genericCustomUEF; - - //calculate oft-used derived values - calcDerivedValues(); - - if (checkInputs() == HPWH_ABORT) { - return HPWH_ABORT; - } - - isHeating = false; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].isOn) { - isHeating = true; - } - setOfSources[i].sortPerformanceMap(); - } - - if (hpwhVerbosity >= VRB_emetic){ - for (int i = 0; i < numHeatSources; i++) { - msg("heat source %d: %p \n", i, &setOfSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - - return 0; -} - - - - -int HPWH::HPWHinit_presets(MODELS presetNum) { - //return 0 on success, HPWH_ABORT for failure - simHasFailed = true; //this gets cleared on successful completion of init - - //clear out old stuff if you're re-initializing - delete[] tankTemps_C; - delete[] setOfSources; - - - //resistive with no UA losses for testing - if (presetNum == MODELS_restankNoUA) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 0; //0 to turn off - - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 2; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementTop.setupAsResistiveElement(8, 4500); - - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.isVIP = true; - - //assign heat sources into array in order of priority - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - } - - //resistive tank with massive UA loss for testing - else if (presetNum == MODELS_restankHugeUA) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = 50; - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 120; - tankUA_kJperHrC = 500; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - - - numHeatSources = 2; - setOfSources = new HeatSource[numHeatSources]; - - //set up a resistive element at the bottom, 4500 kW - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementTop.setupAsResistiveElement(9, 4500); - - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); - resistiveElementTop.isVIP = true; - - - //assign heat sources into array in order of priority - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - - } - - //realistic resistive tank - else if (presetNum == MODELS_restankRealistic) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 10; //0 to turn off - - doTempDepression = false; - //should eventually put tankmixes to true when testing progresses - tankMixesOnDraw = false; - - numHeatSources = 2; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementTop.setupAsResistiveElement(9, 4500); - - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); - resistiveElementTop.isVIP = true; - - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - } - - else if (presetNum == MODELS_StorageTank) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(80); - tankUA_kJperHrC = 10; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - - //////////////////////////////////////////////////// - numHeatSources = 1; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource extra(this); - - //compressor values - extra.isOn = false; - extra.isVIP = false; - extra.typeOfHeatSource = TYPE_extra; - extra.configuration = HeatSource::CONFIG_WRAPPED; - - extra.addTurnOnLogic(HPWH::bottomThird(-200)); - extra.hysteresis_dC = 0.; - extra.minT = 0; - extra.maxT = 200; - - //initial guess, will get reset based on the input heat vector - extra.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - - setOfSources[0] = extra; - } - - //basic compressor tank for testing - else if (presetNum == MODELS_basicIntegrated) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = 50; - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 120; - tankUA_kJperHrC = 10; //0 to turn off - //tankUA_kJperHrC = 0; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - HeatSource compressor(this); - - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementTop.setupAsResistiveElement(9, 4500); - - resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); - - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); - resistiveElementTop.isVIP = true; - - - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double oneSixth = 1.0 / 6.0; - compressor.setCondensity(oneSixth, oneSixth, oneSixth, oneSixth, oneSixth, oneSixth, 0, 0, 0, 0, 0, 0); - - //GE tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {0.290 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {4.49, -0.0187, -0.0000133} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {0.375 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {5.60, -0.0252, 0.00000254} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = 0; - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(4); - compressor.configuration = HeatSource::CONFIG_WRAPPED; //wrapped around tank - - compressor.addTurnOnLogic(HPWH::bottomThird(20)); - compressor.addTurnOnLogic(HPWH::standby(15)); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = compressor; - setOfSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - - //simple external style for testing - else if (presetNum == MODELS_externalTest) { - numNodes = 96; - tankTemps_C = new double[numNodes]; - setpoint_C = 50; - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 120; - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 0; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - - numHeatSources = 1; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - //GE tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {0.290 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {4.49, -0.0187, -0.0000133} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {0.375 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {5.60, -0.0252, 0.00000254} // COP Coefficients (COP_coeffs) - }); - - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = 0; //no hysteresis - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - - compressor.addTurnOnLogic(HPWH::bottomThird(20)); - compressor.addTurnOnLogic(HPWH::standby(15)); - - //lowT cutoff - compressor.addShutOffLogic(HPWH::bottomNodeMaxTemp(20)); - - - //set everything in its places - setOfSources[0] = compressor; - } - //voltex 60 gallon - else if (presetNum == MODELS_AOSmithPHPT60) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 215.8; - tankUA_kJperHrC = 7.31; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 5.0; - compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {0.467 * 1000, 0.00281 * 1000, 0.0000072 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {4.86, -0.0222, -0.00001} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {0.541 * 1000, 0.00147 * 1000, 0.0000176 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {6.58, -0.0392, 0.0000407} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(45.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(4); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4250); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 2000); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); - - //logic conditions - double compStart = dF_TO_dC(43.6); - double standbyT = dF_TO_dC(23.8); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(compStart)); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(25.0))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = compressor; - setOfSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_AOSmithPHPT80) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 283.9; - tankUA_kJperHrC = 8.8; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 5.0; - compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {0.467 * 1000, 0.00281 * 1000, 0.0000072 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {4.86, -0.0222, -0.00001} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {0.541 * 1000, 0.00147 * 1000, 0.0000176 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {6.58, -0.0392, 0.0000407} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(45.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(4); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4250); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 2000); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); - - - //logic conditions - double compStart = dF_TO_dC(43.6); - double standbyT = dF_TO_dC(23.8); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(compStart)); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(25.0))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = compressor; - setOfSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_GE2012) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 172; - tankUA_kJperHrC = 6.8; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 5.0; - compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {0.3 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {4.7, -0.0210, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {0.378 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) - {4.8, -0.0167, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(45.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(4); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4200); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4200); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); - - - //logic conditions - // double compStart = dF_TO_dC(24.4); - double compStart = dF_TO_dC(40.0); - double standbyT = dF_TO_dC(5.2); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - // compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(66))); - compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(compStart)); - //resistiveElementBottom.addShutOffLogic(HPWH::lowTreheat(lowTcutoff)); - //GE element never turns off? - - // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31.0))); - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28.0))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = compressor; - setOfSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_Sanden80) { - numNodes = 96; - tankTemps_C = new double[numNodes]; - setpoint_C = 65; - setpointFixed = true; - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 315; - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 7; - - doTempDepression = false; - tankMixesOnDraw = false; - - numHeatSources = 1; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(5); - - compressor.perfMap.push_back({ - 17, // Temperature (T_F) - {1650, 5.5, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {3.2, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 35, // Temperature (T_F) - {1100, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {3.7, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {880, 3.1, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.25, -0.025, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {740, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {6.2, -0.03, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {790, 2, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.15, -0.04, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.hysteresis_dC = 4; - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - - - std::vector nodeWeights; - nodeWeights.emplace_back(8); - compressor.addTurnOnLogic(HPWH::HeatingLogic("eighth node absolute", nodeWeights, F_TO_C(113), true)); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(HPWH::HeatingLogic("bottom node absolute", nodeWeights1, F_TO_C(135), true, std::greater())); - compressor.depressesTemperature = false; //no temp depression - - //set everything in its places - setOfSources[0] = compressor; - } - else if (presetNum == MODELS_Sanden40) { - numNodes = 96; - tankTemps_C = new double[numNodes]; - setpoint_C = 65; - setpointFixed = true; - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 160; - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 5; - - doTempDepression = false; - tankMixesOnDraw = false; - - numHeatSources = 1; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(5); - - compressor.perfMap.push_back({ - 17, // Temperature (T_F) - {1650, 5.5, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {3.2, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 35, // Temperature (T_F) - {1100, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {3.7, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {880, 3.1, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.25, -0.025, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {740, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {6.2, -0.03, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {790, 2, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.15, -0.04, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.hysteresis_dC = 4; - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - - - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(HPWH::HeatingLogic("fourth node absolute", nodeWeights, F_TO_C(113), true)); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(HPWH::HeatingLogic("bottom node absolute", nodeWeights1, F_TO_C(135), true, std::greater())); - compressor.depressesTemperature = false; //no temp depression - - //set everything in its places - setOfSources[0] = compressor; - } - else if (presetNum == MODELS_AOSmithHPTU50 || presetNum == MODELS_RheemHBDR2250 || presetNum == MODELS_RheemHBDR4550) { - numNodes = 24; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 171; - tankUA_kJperHrC = 6; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 5.0; - compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); - - // performance map - compressor.perfMap.reserve(3); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(42.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - if (presetNum == MODELS_RheemHBDR2250) { - resistiveElementTop.setupAsResistiveElement(8, 2250); - } else { - resistiveElementTop.setupAsResistiveElement(8, 4500); - } - resistiveElementTop.isVIP = true; - - //bottom resistor values - if (presetNum == MODELS_RheemHBDR2250) { - resistiveElementBottom.setupAsResistiveElement(0, 2250); - } else { - resistiveElementBottom.setupAsResistiveElement(0, 4500); - } - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - double compStart = dF_TO_dC(35); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); - - - std::vector nodeWeights; - nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); - resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); -// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - setOfSources[0].companionHeatSource = &setOfSources[2]; - - - } - else if (presetNum == MODELS_AOSmithHPTU66 || presetNum == MODELS_RheemHBDR2265 || presetNum == MODELS_RheemHBDR4565) { - numNodes = 24; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - if (presetNum == MODELS_AOSmithHPTU66) { - tankVolume_L = 244.6; - } else { - tankVolume_L = 221.4; - } - tankUA_kJperHrC = 8; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - // performance map - compressor.perfMap.reserve(3); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(42.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - if (presetNum == MODELS_RheemHBDR2265) { - resistiveElementTop.setupAsResistiveElement(8, 2250); - } else { - resistiveElementTop.setupAsResistiveElement(8, 4500); - } - resistiveElementTop.isVIP = true; - - //bottom resistor values - if (presetNum == MODELS_RheemHBDR2265) { - resistiveElementBottom.setupAsResistiveElement(0, 2250); - } else { - resistiveElementBottom.setupAsResistiveElement(0, 4500); - } - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - double compStart = dF_TO_dC(35); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); - - std::vector nodeWeights; - nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); - resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); -// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - setOfSources[0].companionHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_AOSmithHPTU80 || presetNum == MODELS_RheemHBDR2280 || presetNum == MODELS_RheemHBDR4580) { - numNodes = 24; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 299.5; - tankUA_kJperHrC = 9; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; - - double split = 1.0 / 3.0; - compressor.setCondensity(split, split, split, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(3); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(42.0); - compressor.maxT = F_TO_C(120.0); - compressor.hysteresis_dC = dF_TO_dC(1); - - //top resistor values - if (presetNum == MODELS_RheemHBDR2280) { - resistiveElementTop.setupAsResistiveElement(8, 2250); - } else { - resistiveElementTop.setupAsResistiveElement(8, 4500); - } - resistiveElementTop.isVIP = true; - - //bottom resistor values - if (presetNum == MODELS_RheemHBDR2280) { - resistiveElementBottom.setupAsResistiveElement(0, 2250); - } else { - resistiveElementBottom.setupAsResistiveElement(0, 4500); - } - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - double compStart = dF_TO_dC(35); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); - - std::vector nodeWeights; -// nodeWeights.emplace_back(9); nodeWeights.emplace_back(10); - nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); - resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); -// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - setOfSources[0].companionHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_AOSmithHPTU80_DR) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = 283.9; - tankUA_kJperHrC = 9; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {142.6, 2.152, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {6.989258, -0.038320, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {120.14, 2.513, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {8.188, -0.0432, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(42.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - double compStart = dF_TO_dC(34.1636); - double standbyT = dF_TO_dC(7.1528); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80.108))); - - // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(39.9691))); - resistiveElementTop.addTurnOnLogic(HPWH::topThird_absolute(F_TO_C(87))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_GE2014STDMode) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(19.6605))); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); - // compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_GE2014STDMode_80) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(19.6605))); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); - compressor.minT = F_TO_C(37); - // compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_GE2014) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.addShutOffLogic(HPWH::topNodeMaxTemp(F_TO_C(116.6358))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(11.0648))); - // compressor.addShutOffLogic(HPWH::largerDraw(F_TO_C(62.4074))); - - resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_GE2014_80) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.addShutOffLogic(HPWH::topNodeMaxTemp(F_TO_C(116.6358))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(11.0648))); - // compressor.addShutOffLogic(HPWH::largerDraw(F_TO_C(62.4074))); - - resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_GE2014_80DR) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.addTurnOnLogic(HPWH::topThird_absolute(F_TO_C(87))); - // resistiveElementTop.addShutOffLogic(HPWH::topNodeMaxTemp(F_TO_C(116.6358))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(11.0648))); - // compressor.addShutOffLogic(HPWH::largerDraw(F_TO_C(62.4074))); - - resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); - // resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_RheemHB50) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 7; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 47, // Temperature (T_F) - {280, 4.97342, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.634009, -0.029485, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {280, 5.35992, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {6.3, -0.03, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.hysteresis_dC = dF_TO_dC(1); - compressor.minT = F_TO_C(40.0); - compressor.maxT = F_TO_C(120.0); - - compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4200); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 2250); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - double compStart = dF_TO_dC(38); - double standbyT = dF_TO_dC(13.2639); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(76.7747))); - - resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); - - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_Stiebel220E) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(56); - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 9; - - doTempDepression = false; - tankMixesOnDraw = false; - - numHeatSources = 2; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElement(this); - - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - resistiveElement.setupAsResistiveElement(0, 1500); - resistiveElement.hysteresis_dC = dF_TO_dC(0); - - compressor.setCondensity(0, 0.12, 0.22, 0.22, 0.22, 0.22, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {295.55337, 2.28518, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.744118, -0.025946, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {282.2126, 2.82001, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {8.012112, -0.039394, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(32.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = 0; //no hysteresis - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - compressor.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(6.5509))); - compressor.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); - - compressor.depressesTemperature = false; //no temp depression - - //set everything in its places - setOfSources[0] = compressor; - setOfSources[1] = resistiveElement; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[0].backupHeatSource = &setOfSources[1]; - - } - else if (presetNum == MODELS_Generic1) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 9; - doTempDepression = false; - tankMixesOnDraw = true; - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {472.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {2.942642, -0.0125954, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {439.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {3.95076, -0.01638033, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(45.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40.0))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(65))); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(80))); - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(110))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_Generic2) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 7.5; - doTempDepression = false; - tankMixesOnDraw = true; - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {272.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {4.042642, -0.0205954, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {239.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.25076, -0.02638033, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(40.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(60))); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(80))); - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(40))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_Generic3) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 5; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - //voltex60 tier 1 values - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {172.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.242642, -0.0285954, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {139.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {6.75076, -0.03638033, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(35.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementBottom.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - compressor.addShutOffLogic(HPWH::largeDraw(F_TO_C(55))); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(60))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(40))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = compressor; - setOfSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - else if (presetNum == MODELS_UEF2generic) { - numNodes = 12; - tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - numHeatSources = 3; - setOfSources = new HeatSource[numHeatSources]; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - double split = 1.0 / 4.0; - compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {4.29, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 70, // Temperature (T_F) - {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.61, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(6, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4000); - resistiveElementBottom.setCondensity(0, 0.2, 0.8, 0, 0, 0, 0, 0, 0, 0, 0, 0); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(18.6605))); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(86.1111))); - - compressor.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(33.6883))); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(12.392))); - - //set everything in its places - setOfSources[0] = resistiveElementTop; - setOfSources[1] = resistiveElementBottom; - setOfSources[2] = compressor; - - //and you have to do this after putting them into setOfSources, otherwise - //you don't get the right pointers - setOfSources[2].backupHeatSource = &setOfSources[1]; - setOfSources[1].backupHeatSource = &setOfSources[2]; - - setOfSources[0].followedByHeatSource = &setOfSources[1]; - setOfSources[1].followedByHeatSource = &setOfSources[2]; - - } - - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have tried to select a preset model which does not exist. \n"); - } - return HPWH_ABORT; - } - - // initialize nextTankTemps_C - nextTankTemps_C = new double[numNodes]; - - hpwhModel = presetNum; - - //calculate oft-used derived values - calcDerivedValues(); - - if (checkInputs() == HPWH_ABORT) { - return HPWH_ABORT; - } - - isHeating = false; - for (int i = 0; i < numHeatSources; i++) { - if (setOfSources[i].isOn) { - isHeating = true; - } - setOfSources[i].sortPerformanceMap(); - } - - if (hpwhVerbosity >= VRB_emetic){ - for (int i = 0; i < numHeatSources; i++) { - msg("heat source %d: %p \n", i, &setOfSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - return 0; //successful init returns 0 -} //end HPWHinit_presets diff --git a/HPWH.in.hh b/HPWH.in.hh deleted file mode 100644 index b3782ac6..00000000 --- a/HPWH.in.hh +++ /dev/null @@ -1,740 +0,0 @@ -#ifndef HPWH_hh -#define HPWH_hh - -#include -#include -#include -#include -#include -#include - -#include -#include //for exit -#include - -//#define HPWH_ABRIDGED -/**< If HPWH_ABRIDGED is defined, then some function definitions will be - * excluded from compiling. This is done in order to reduce the size of the - * final compiled code. */ - -#define HPWHVRSN_MAJOR @HPWHsim_VRSN_MAJOR@ -#define HPWHVRSN_MINOR @HPWHsim_VRSN_MINOR@ -#define HPWHVRSN_PATCH @HPWHsim_VRSN_PATCH@ -#define HPWHVRSN_META "@HPWHsim_VRSN_META@" - -class HPWH { - public: - static const int version_major = HPWHVRSN_MAJOR; - static const int version_minor = HPWHVRSN_MINOR; - static const int version_patch = HPWHVRSN_PATCH; - static const std::string version_maint; // Initialized in source file (HPWH.cc) - - - static const float DENSITYWATER_kgperL; - static const float KWATER_WpermC; - static const float CPWATER_kJperkgC; - static const int CONDENSITY_SIZE = 12; /**< this must be an integer, and only the value 12 - //change at your own risk */ - static const int MAXOUTSTRING = 200; /**< this is the maximum length for a debuging output string */ - static const float HEATDIST_MINVALUE; /**< any amount of heat distribution less than this is reduced to 0 - //this saves on computations */ - static const float UNINITIALIZED_LOCATIONTEMP; /**< this is used to tell the - simulation when the location temperature has not been initialized */ - - HPWH(); /**< default constructor */ - HPWH(const HPWH &hpwh); /**< copy constructor */ - HPWH & operator=(const HPWH &hpwh); /**< assignment operator */ - ~HPWH(); /**< destructor just a couple dynamic arrays to destroy - could be replaced by vectors eventually? */ - - ///specifies the various modes for the Demand Response (DR) abilities - ///values may vary - names should be used - enum DRMODES{ - DR_BLOCK = 0, /**= comparisons, so the numerical order is relevant - enum VERBOSITY { - VRB_silent = 0, /**< print no outputs */ - VRB_reluctant = 10, /**< print only outputs for fatal errors */ - VRB_minuteOut = 15, /**< print minutely output */ - VRB_typical = 20, /**< print some basic debugging info */ - VRB_emetic = 30 /**< print all the things */ - }; - - - enum UNITS{ - UNITS_C, /**< celsius */ - UNITS_F, /**< fahrenheit */ - UNITS_KWH, /**< kilowatt hours */ - UNITS_BTU, /**< british thermal units */ - UNITS_KJ, /**< kilojoules */ - UNITS_GAL, /**< gallons */ - UNITS_L, /**< liters */ - UNITS_kJperHrC, /**< UA, metric units */ - UNITS_BTUperHrF, /**< UA, imperial units */ - UNITS_FT, /**< feet */ - UNITS_M, /**< meters */ - UNITS_FT2, /**< square feet */ - UNITS_M2, /**< square meters */ - }; - - /** specifies the type of heat source */ - enum HEATSOURCE_TYPE { - TYPE_none, /**< a default to check to make sure it's been set */ - TYPE_resistance, /**< a resistance element */ - TYPE_compressor, /**< a vapor cycle compressor */ - TYPE_extra /**< an extra element to add user defined heat*/ - }; - - /** specifies the unit type for outputs in the CSV file-s */ - enum CSVOPTIONS { - CSVOPT_NONE, - CSVOPT_IPUNITS - }; - - struct NodeWeight { - int nodeNum; - double weight; - NodeWeight(int n, double w) : nodeNum(n), weight(w) {}; - - NodeWeight(int n) : nodeNum(n), weight(1.0) {}; - }; - - struct HeatingLogic { - std::string description; // for debug purposes - std::vector nodeWeights; - double decisionPoint; - bool isAbsolute; - std::function compare; - HeatingLogic(std::string desc, std::vector n, double d, bool a=false, std::function c=std::less()) : - description(desc), nodeWeights(n), decisionPoint(d), isAbsolute(a), compare(c) {}; - }; - - HeatingLogic topThird(double d) const; - HeatingLogic topThird_absolute(double d) const; - HeatingLogic bottomThird(double d) const; - HeatingLogic bottomHalf(double d) const; - HeatingLogic bottomTwelth(double d) const; - HeatingLogic bottomSixth(double d) const; - HeatingLogic secondSixth(double d) const; - HeatingLogic thirdSixth(double d) const; - HeatingLogic fourthSixth(double d) const; - HeatingLogic fifthSixth(double d) const; - HeatingLogic topSixth(double d) const; - HeatingLogic standby(double d) const; - HeatingLogic topNodeMaxTemp(double d) const; - HeatingLogic bottomNodeMaxTemp(double d) const; - HeatingLogic bottomTwelthMaxTemp(double d) const; - HeatingLogic largeDraw(double d) const; - HeatingLogic largerDraw(double d) const; - - - ///this is the value that the public functions will return in case of a simulation - ///destroying error - static const int HPWH_ABORT = -274000; - - static std::string getVersion(); - /**< This function returns a string with the current version number */ - - int HPWHinit_presets(MODELS presetNum); - /**< This function will load in a set of parameters that are hardcoded in this function - - * which particular set of parameters is selected by presetNum. - * This is similar to the way the HPWHsim currently operates, as used in SEEM, - * but not quite as versatile. - * My impression is that this could be a useful input paradigm for CSE - * - * The return value is 0 for successful initialization, HPWH_ABORT otherwise - */ - - int HPWHinit_file(std::string configFile); - /**< This function will load in a set of parameters from a file - * The file name is the input - there should be at most one set of parameters per file - * This is useful for testing new variations, and for the sort of variability - * that we typically do when creating SEEM runs - * Appropriate use of this function can be found in the documentation - * - * - * The return value is 0 for successful initialization, HPWH_ABORT otherwise - */ - - int HPWHinit_resTank(); /**< Default resistance tank, EF 0.95, volume 47.5 */ - int HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPower_W, double lowerPower_W); - /**< This function will initialize a HPWH object to be a resistance tank. Since - * resistance tanks are so simple, they can be specified with only four variables: - * tank volume, energy factor, and the power of the upper and lower elements. Energy - * factor is converted into UA internally, although an external setter for UA is - * also provided in case the energy factor is unknown. - * - * Several assumptions regarding the tank configuration are assumed: the lower element - * is at the bottom, the upper element is at the top third. The logics are also set - * to standard setting, with upper as VIP activating when the top third is too cold. - */ - - int HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C); - /**< This function will initialize a HPWH object to be a non-specific HPWH model - * with an energy factor as specified. Since energy - * factor is not strongly correlated with energy use, most settings - * are taken from the GE2015_STDMode model. - */ - - int runOneStep(double inletT_C, double drawVolume_L, - double ambientT_C, double externalT_C, - DRMODES DRstatus, double minutesPerStep, - double inletVol2_L = 0., double inletT2_C = 0., - std::vector* nodeExtraHeat_W = NULL); - /**< This function will progress the simulation forward in time by one step - * all calculated outputs are stored in private variables and accessed through functions - * - * The return value is 0 for successful simulation run, HPWH_ABORT otherwise - */ - - int runNSteps(int N, double *inletT_C, double *drawVolume_L, - double *tankAmbientT_C, double *heatSourceAmbientT_C, - DRMODES *DRstatus, double minutesPerStep); - /**< This function will progress the simulation forward in time by N (equal) steps - * The calculated values will be summed or averaged, as appropriate, and - * then stored in the usual variables to be accessed through functions - * - * The return value is 0 for successful simulation run, HPWH_ABORT otherwise - */ - - - void setVerbosity(VERBOSITY hpwhVrb); - /**< sets the verbosity to the specified level */ - void setMessageCallback( void (*callbackFunc)(const std::string message, void* pContext), void* pContext); - /**< sets the function to be used for message passing */ - void printHeatSourceInfo(); - /**< this prints out the heat source info, nicely formatted - specifically input/output energy/power, and runtime - will print to cout if messageCallback pointer is unspecified - does not use verbosity, as it is public and expected to be called only when needed */ - void printTankTemps(); - /**< this prints out all the node temps, kind of nicely formatted - does not use verbosity, as it is public and expected to be called only when needed */ - int WriteCSVHeading(FILE* outFILE, const char* preamble = "", int nTCouples = 6, int options = CSVOPT_NONE) const; - int WriteCSVRow(FILE* outFILE, const char* preamble = "", int nTCouples = 6, int options = CSVOPT_NONE) const; - /**< a couple of function to write the outputs to a file - they both will return 0 for success - the preamble should be supplied with a trailing comma, as these functions do - not add one. Additionally, a newline is written with each call. */ - - - - bool isSetpointFixed(); /**< is the setpoint allowed to be changed */ - int setSetpoint(double newSetpoint, UNITS units = UNITS_C);/** nTCouple, or incorrect units */ - - int getNumHeatSources() const; - /**< returns the number of heat sources */ - double getNthHeatSourceEnergyInput(int N, UNITS units = UNITS_KWH) const; - /**< returns the energy input to the Nth heat source, with the specified units - energy used by the heat source is positive - should always be positive - returns HPWH_ABORT for N out of bounds or incorrect units */ - - double getNthHeatSourceEnergyOutput(int N, UNITS units = UNITS_KWH) const; - /**< returns the energy output from the Nth heat source, with the specified units - energy put into the water is positive - should always be positive - returns HPWH_ABORT for N out of bounds or incorrect units */ - double getNthHeatSourceRunTime(int N) const; - /**< returns the run time for the Nth heat source, in minutes - note: they may sum to more than 1 time step for concurrently running heat sources - returns HPWH_ABORT for N out of bounds */ - int isNthHeatSourceRunning(int N) const; - /**< returns 1 if the Nth heat source is currently engaged, 0 if it is not, and - returns HPWH_ABORT for N out of bounds */ - HEATSOURCE_TYPE getNthHeatSourceType(int N) const; - /**< returns the enum value for what type of heat source the Nth heat source is */ - - - double getOutletTemp(UNITS units = UNITS_C) const; - /**< returns the outlet temperature in the specified units - returns 0 when no draw occurs, or HPWH_ABORT for incorrect unit specifier */ - - double getEnergyRemovedFromEnvironment(UNITS units = UNITS_KWH) const; - /**< get the total energy removed from the environment by all heat sources in specified units - (not net energy - does not include standby) - moving heat from the space to the water is the positive direction - returns HPWH_ABORT for incorrect units */ - - double getStandbyLosses(UNITS units = UNITS_KWH) const; - /**< get the amount of heat lost through the tank in specified units - moving heat from the water to the space is the positive direction - negative should occur seldom - returns HPWH_ABORT for incorrect units */ - - double getTankHeatContent_kJ() const; - /**< get the heat content of the tank, relative to zero celsius - * returns using kilojoules */ - - /** An overloaded function that uses some member variables, instead of taking them as inputs */ - int runOneStep(double drawVolume_L, double ambientT_C, - double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0., - std::vector* nodeExtraHeat_W = NULL) { - return runOneStep(member_inletT_C, drawVolume_L, ambientT_C, - externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C, - nodeExtraHeat_W); - }; - - - /** Setters for the what are typically input variables */ - void setInletT(double newInletT_C) {member_inletT_C = newInletT_C;}; - void setMinutesPerStep(double newMinutesPerStep) {member_minutesPerStep = newMinutesPerStep;}; - - double getLocationTemp_C() const; - int setMaxTempDepression(double maxDepression, UNITS units = UNITS_C); - - - private: - class HeatSource; - - void updateTankTemps(double draw, double inletT, double ambientT, double minutesPerStep, double inletVol2_L, double inletT2_L); - void mixTankInversions(); - /**< Mixes the any temperature inversions in the tank after all the temperature calculations */ - bool areAllHeatSourcesOff() const; - /**< test if all the heat sources are off */ - void turnAllHeatSourcesOff(); - /**< disengage each heat source */ - - void addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesPerStep); - /**< adds extra heat defined by the user. Where nodeExtraHeat[] is a vector of heat quantities to be added during the step. nodeExtraHeat[ 0] would go to bottom node, 1 to next etc. */ - - double tankAvg_C(const std::vector nodeWeights) const; - - /**< functions to calculate what the temperature in a portion of the tank is */ - - void calcDerivedValues(); - /**< a helper function for the inits */ - void calcDerivedHeatingValues(); - /**< a helper for the helper, calculating condentropy and the lowest node*/ - - int checkInputs(); - /**< a helper function to run a few checks on the HPWH input parameters */ - - - void sayMessage(const std::string message) const; - /**< if the messagePriority is >= the hpwh verbosity, - either pass your message out to the callback function or print it to cout - otherwise do nothing */ - void msg( const char* fmt, ...) const; - void msgV( const char* fmt, va_list ap=NULL) const; - - bool simHasFailed; - /**< did an internal error cause the simulation to fail? */ - - bool isHeating; - /**< is the hpwh currently heating or not? */ - - bool setpointFixed; - /**< does the HPWH allow the setpoint to vary */ - - VERBOSITY hpwhVerbosity; - /**< an enum to let the sim know how much output to say */ - - void (*messageCallback)(const std::string message, void* contextPtr); - /**< function pointer to indicate an external message processing function */ - void* messageCallbackContextPtr; - /**< caller context pointer for external message processing */ - - - MODELS hpwhModel; - /**< The hpwh should know which preset initialized it, or if it was from a file */ - - int numHeatSources; - /**< how many heat sources this HPWH has */ - HeatSource *setOfSources; - /**< an array containing the HeatSources, in order of priority */ - - int compressorIndex; - /**< The index of the compressor heat source (set to -1 if no compressor)*/ - - int lowestElementIndex; - /**< The index of the lowest resistance element heat source (set to -1 if no resistance elements)*/ - - int numNodes; - /**< the number of nodes in the tank - must be >= 12, in multiples of 12 */ - - int nodeDensity; - /**< the number of calculation nodes in a logical node */ - - int inletHeight; - /**< the number of a node in the tank that the inlet water enters the tank at, must be between 0 and numNodes-1 */ - - int inlet2Height; - /**< the number of a node in the tank that the 2nd inlet water enters the tank at, must be between 0 and numNodes-1 */ - - double tankVolume_L; - /**< the volume in liters of the tank */ - double tankUA_kJperHrC; - /**< the UA of the tank, in metric units */ - - double setpoint_C; - /**< the setpoint of the tank */ - double *tankTemps_C; - /**< an array holding the temperature of each node - 0 is the bottom node, numNodes is the top */ - double *nextTankTemps_C; - /**< an array holding the future temperature of each node for the conduction calculation - 0 is the bottom node, numNodes is the top */ - - - // Some outputs - double outletTemp_C; - /**< the temperature of the outlet water - taken from top of tank, 0 if no flow */ - double energyRemovedFromEnvironment_kWh; - /**< the total energy removed from the environment, to heat the water */ - double standbyLosses_kWh; - /**< the amount of heat lost to standby */ - - - // special variables for adding abilities - bool tankMixesOnDraw; - /**< whether or not the bottom third of the tank should mix during draws */ - bool doTempDepression; - /**< whether the HPWH should use the alternate ambient temperature that - gets depressed when a compressor is running - NOTE: this only works for 1 minute steps */ - double locationTemperature_C; - /**< this is the special location temperature that stands in for the the - ambient temperature if you are doing temp. depression */ - double maxDepression_C = 2.5; - /** a couple variables to hold values which are typically inputs */ - double member_inletT_C; - double member_minutesPerStep; - - bool doInversionMixing; - /**< If and only if true will model temperature inversion mixing in the tank */ - - bool doConduction; - /**< If and only if true will model conduction between the internal nodes of the tank */ - -}; //end of HPWH class - - - - -class HPWH::HeatSource { - public: - friend class HPWH; - - HeatSource(){} /**< default constructor, does not create a useful HeatSource */ - HeatSource(HPWH *parentHPWH); - /**< constructor assigns a pointer to the hpwh that owns this heat source */ - HeatSource(const HeatSource &hSource); ///copy constructor - HeatSource& operator=(const HeatSource &hSource); ///assignment operator - /**< the copy constructor and assignment operator basically just checks if there - are backup/companion pointers - these can't be copied */ - - void setupAsResistiveElement(int node, double Watts); - /**< configure the heat source to be a resisive element, positioned at the - specified node, with the specified power in watts */ - void setupExtraHeat(std::vector* nodeExtraHeat_W); - /**< Configure a user defined heat source added as extra, based off using - nodeExtraHeat_W as the total watt input and the condensity*/ - - bool isEngaged() const; - /**< return whether or not the heat source is engaged */ - void engageHeatSource(); - /**< turn heat source on, i.e. set isEngaged to TRUE */ - void disengageHeatSource(); - /**< turn heat source off, i.e. set isEngaged to FALSE */ - - bool isLockedOut() const; - /**< return whether or not the heat source is locked out */ - void lockOutHeatSource(); - /**< lockout heat source, i.e. set isLockedOut to TRUE */ - void unlockHeatSource(); - /**< unlock heat source, i.e. set isLockedOut to FALSE */ - - bool shouldLockOut(double heatSourceAmbientT_C) const; - /**< queries the heat source as to whether it should lock out */ - bool shouldUnlock(double heatSourceAmbientT_C) const; - /**< queries the heat source as to whether it should unlock */ - bool shouldHeat() const; - /**< queries the heat source as to whether or not it should turn on */ - bool shutsOff() const; - /**< queries the heat source whether should shut off */ - - int findParent() const; - /**< returns the index of the heat source where this heat source is a backup. - returns -1 if none found. */ - - void addHeat_temp(double externalT_C, double minutesPerStep); - void addHeat(double externalT_C, double minutesToRun); - /**< adds heat to the hpwh - this is the function that interprets the - various configurations (internal/external, resistance/heat pump) to add heat */ - - void setCondensity(double cnd1, double cnd2, double cnd3, double cnd4, - double cnd5, double cnd6, double cnd7, double cnd8, - double cnd9, double cnd10, double cnd11, double cnd12); - /**< a function to set the condensity values, it pretties up the init funcs. */ - - - private: - //start with a few type definitions - enum COIL_CONFIG { - CONFIG_SUBMERGED, - CONFIG_WRAPPED, - CONFIG_EXTERNAL - }; - - /** the creator of the heat source, necessary to access HPWH variables */ - HPWH *hpwh; - - // these are the heat source state/output variables - bool isOn; - /**< is the heat source running or not */ - - bool lockedOut; - /**< is the heat source locked out */ - - // some outputs - double runtime_min; - /**< this is the percentage of the step that the heat source was running */ - double energyInput_kWh; - /**< the energy used by the heat source */ - double energyOutput_kWh; - /**< the energy put into the water by the heat source */ - -// these are the heat source property variables - bool isVIP; - /**< is this heat source a high priority heat source? (e.g. upper resisitor) */ - HeatSource* backupHeatSource; - /**< a pointer to the heat source which serves as backup to this one - should be NULL if no backup exists */ - HeatSource* companionHeatSource; - /**< a pointer to the heat source which will run concurrently with this one - it still will only turn on if shutsOff is false */ - - HeatSource* followedByHeatSource; - /**< a pointer to the heat source which will attempt to run after this one */ - - double condensity[CONDENSITY_SIZE]; - /**< The condensity function is always composed of 12 nodes. - It represents the location within the tank where heat will be distributed, - and it also is used to calculate the condenser temperature for inputPower/COP calcs. - It is conceptually linked to the way condenser coils are wrapped around - (or within) the tank, however a resistance heat source can also be simulated - by specifying the entire condensity in one node. */ - double shrinkage; - /**< the shrinkage is a derived value, using parameters alpha, beta, - and the condentropy, which is derived from the condensity - alpha and beta are not intended to be settable - see the hpwh_init functions for calculation of shrinkage */ - - struct perfPoint { - double T_F; - double inputPower_coeffs[3]; // c0 + c1*T + c2*T*T - double COP_coeffs[3]; // c0 + c1*T + c2*T*T - }; - - std::vector perfMap; - /**< A map with input/COP quadratic curve coefficients at a given external temperature */ - - /** a vector to hold the set of logical choices for turning this element on */ - std::vector turnOnLogicSet; - /** a vector to hold the set of logical choices that can cause an element to turn off */ - std::vector shutOffLogicSet; - - void addTurnOnLogic(HeatingLogic logic); - void addShutOffLogic(HeatingLogic logic); - /**< these are two small functions to remove some of the cruft in initiation functions */ - - double minT; - /**< minimum operating temperature of HPWH environment */ - - double maxT; - /**< maximum operating temperature of HPWH environment */ - - double hysteresis_dC; - /**< a hysteresis term that prevents short cycling due to heat pump self-interaction - when the heat source is engaged, it is subtracted from lowT cutoffs and - added to lowTreheat cutoffs */ - - bool depressesTemperature; - /**< heat pumps can depress the temperature of their space in certain instances - - whether or not this occurs is a bool in HPWH, but a heat source must - know if it is capable of contributing to this effect or not - NOTE: this only works for 1 minute steps - ALSO: this is set according the the heat source type, not user-specified */ - - double airflowFreedom; - /**< airflowFreedom is the fraction of full flow. This is used to de-rate compressor - cop (not capacity) for cases where the air flow is restricted - typically ducting */ - - - COIL_CONFIG configuration; /**< submerged, wrapped, external */ - HEATSOURCE_TYPE typeOfHeatSource; /**< compressor, resistance */ - - int lowestNode; - /**< hold the number of the first non-zero condensity entry */ - - - - - // some private functions, mostly used for heating the water with the addHeat function - - double addHeatAboveNode(double cap_kJ, int node, double minutesToRun); - /**< adds heat to the set of nodes that are at the same temperature, above the - specified node number */ - double addHeatExternal(double externalT_C, double minutesToRun, double &cap_BTUperHr, double &input_BTUperHr, double &cop); - /**< Add heat from a source outside of the tank. Assume the condensity is where - the water is drawn from and hot water is put at the top of the tank. */ - - /** I wrote some methods to help with the add heat interface - MJL */ - void getCapacity(double externalT_C, double condenserTemp_C, double &input_BTUperHr, double &cap_BTUperHr, double &cop); - void calcHeatDist(std::vector &heatDistribution); - - double getCondenserTemp(); - /**< returns the temperature of the condensor - it's a weighted average of the - tank temperature, using the condensity as weights */ - - void sortPerformanceMap(); - /**< sorts the Performance Map by increasing external temperatures */ - - /**< A few helper functions */ - double expitFunc(double x, double offset); - void normalize(std::vector &distribution); - -}; // end of HeatSource class - - -// a few extra functions for unit converesion -inline double dF_TO_dC(double temperature) { return (temperature*5.0/9.0); } -inline double F_TO_C(double temperature) { return ((temperature - 32.0)*5.0/9.0); } -inline double C_TO_F(double temperature) { return (((9.0/5.0)*temperature) + 32.0); } -inline double KWH_TO_BTU(double kwh) { return (3412.14 * kwh); } -inline double KWH_TO_KJ(double kwh) { return (kwh * 3600.0); } -inline double BTU_TO_KWH(double btu) { return (btu / 3412.14); } -inline double KJ_TO_KWH(double kj) { return (kj/3600.0); } -inline double BTU_TO_KJ(double btu) { return (btu * 1.055); } -inline double GAL_TO_L(double gallons) { return (gallons * 3.78541); } -inline double L_TO_GAL(double liters) { return (liters / 3.78541); } -inline double UAf_TO_UAc(double UAf) { return (UAf * 1.8 / 0.9478); } - -inline double FT_TO_M(double feet) { return (feet / 3.2808); } -inline double FT2_TO_M2(double feet2) { return (feet2 / 10.7640); } - -#endif diff --git a/src/HPWH.cc b/src/HPWH.cc index e3aeee85..439914da 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -2022,7 +2022,7 @@ void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { //after you've done everything, any leftover capacity is time that didn't run this->runtime_min = (1.0 - (leftoverCap_kJ / BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0))) * minutesToRun; #if 1 // error check, 1-22-2017 - if (runtime_min < 0.) + if (runtime_min < -0.001) if (hpwh->hpwhVerbosity >= VRB_reluctant) hpwh->msg("Internal error: Negative runtime = %0.3f min\n", runtime_min); #endif @@ -3590,7 +3590,6 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //initial guess, will get reset based on the input heat vector extra.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - setOfSources[0] = extra; } diff --git a/test/main.cc b/test/main.cc index 2a637647..6b195646 100644 --- a/test/main.cc +++ b/test/main.cc @@ -279,8 +279,8 @@ int main(int argc, char *argv[]) } vectptr = NULL; - if ( i == 15 || i== 100 || i== 150){ - nodeExtraHeat_W = { 1000., 2000., 3000., 4., 5, 6, 7, 8, 9, 10, 11, 12 }; + if ( i == 15 || i== 16 || i== 17){ + nodeExtraHeat_W = { 25000, 2., 3., 4., 5, 6, 7, 8, 9, 10, 11, 12 }; vectptr = &nodeExtraHeat_W; cout << "Now on minute " << i << "\n"; From c3c87170d93fa9ff19abbd57e9d4d26711ed3d31 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 21 Jan 2020 17:02:40 -0800 Subject: [PATCH 04/22] Update HPWH.in.hh --- src/HPWH.in.hh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index 95614243..b3782ac6 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -392,13 +392,15 @@ class HPWH { * returns using kilojoules */ /** An overloaded function that uses some member variables, instead of taking them as inputs */ - int runOneStep(double drawVolume_L, double ambientT_C, - double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0., - vector* nodeExtraHeat_W = NULL) { - return runOneStep(member_inletT_C, drawVolume_L, ambientT_C, - externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C, - nodeExtraHeat_W); - }; + int runOneStep(double drawVolume_L, double ambientT_C, + double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0., + std::vector* nodeExtraHeat_W = NULL) { + return runOneStep(member_inletT_C, drawVolume_L, ambientT_C, + externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C, + nodeExtraHeat_W); + }; + + /** Setters for the what are typically input variables */ void setInletT(double newInletT_C) {member_inletT_C = newInletT_C;}; void setMinutesPerStep(double newMinutesPerStep) {member_minutesPerStep = newMinutesPerStep;}; From 3eee596b0bfd56c53bc6d3fd653d7df5eb67ae40 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 21 Jan 2020 17:05:54 -0800 Subject: [PATCH 05/22] Negative Runtime Error Msg Fix --- src/HPWH.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 292b9e3c..03b79938 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -1990,7 +1990,7 @@ void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { //after you've done everything, any leftover capacity is time that didn't run this->runtime_min = (1.0 - (leftoverCap_kJ / BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0))) * minutesToRun; #if 1 // error check, 1-22-2017 - if (runtime_min < 0.) + if (runtime_min < -0.001) if (hpwh->hpwhVerbosity >= VRB_reluctant) hpwh->msg("Internal error: Negative runtime = %0.3f min\n", runtime_min); #endif From 55a462d16ca54c7fb8ec42ec8518348cbf418014 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Wed, 22 Jan 2020 08:46:00 -0800 Subject: [PATCH 06/22] Pass Regression Tests --- src/HPWH.cc | 2 +- test/main.cc | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 439914da..330af859 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -356,7 +356,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, } //and add heat if it is - heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesPerStep); + heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesToRun); //if it finished early if (heatSourcePtr->runtime_min < minutesToRun) { //debugging message handling diff --git a/test/main.cc b/test/main.cc index 6b195646..1e1ef38c 100644 --- a/test/main.cc +++ b/test/main.cc @@ -278,14 +278,14 @@ int main(int argc, char *argv[]) drStatus = HPWH::DR_ENGAGE; } - vectptr = NULL; - if ( i == 15 || i== 16 || i== 17){ - nodeExtraHeat_W = { 25000, 2., 3., 4., 5, 6, 7, 8, 9, 10, 11, 12 }; - vectptr = &nodeExtraHeat_W; - cout << "Now on minute " << i << "\n"; - - } - +//vectptr = NULL; +//if ( i == 15 || i== 16 || i== 17){ +// nodeExtraHeat_W = { 25000, 2., 3., 4., 5, 6, 7, 8, 9, 10, 11, 12 }; +// vectptr = &nodeExtraHeat_W; +// cout << "Now on minute " << i << "\n"; +// +//} +// // Run the step hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) GAL_TO_L(allSchedules[1][i]), // Flow in gallons From c0068c626dbcb627c150faec862789d537b267b2 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Wed, 22 Jan 2020 10:30:22 -0800 Subject: [PATCH 07/22] Removed Messages --- src/HPWH.cc | 23 +++++++++++------------ src/HPWH.in.hh | 12 ++++++------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 330af859..3b94ea7d 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -205,7 +205,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, double tankAmbientT_C, double heatSourceAmbientT_C, DRMODES DRstatus, double minutesPerStep, double inletVol2_L, double inletT2_C, - std::vector* nodeExtraHeat_W) { + std::vector* nodePowerExtra_W) { //returns 0 on successful completion, HPWH_ABORT on failure //check for errors @@ -382,9 +382,8 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, //If theres extra user defined heat to add -> Add extra heat! - if (nodeExtraHeat_W != NULL) { - msg("runOneStep entering addExtraHeat if\n"); - addExtraHeat(nodeExtraHeat_W, tankAmbientT_C, minutesToRun); + if (nodePowerExtra_W != NULL) { + addExtraHeat(nodePowerExtra_W, tankAmbientT_C, minutesToRun); } @@ -1563,10 +1562,10 @@ void HPWH::mixTankInversions() { } -void HPWH::addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesToRun){ - if ((*nodeExtraHeat_W).size() != 12){ +void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbientT_C, double minutesToRun){ + if ((*nodePowerExtra_W).size() != 12){ if (hpwhVerbosity >= VRB_reluctant) { - msg("nodeExtraHeat_KWH (%i) does not have 12 nodes \n", (*nodeExtraHeat_W).size(), numNodes); + msg("nodeExtraHeat_KWH (%i) does not have 12 nodes \n", (*nodePowerExtra_W).size(), numNodes); } simHasFailed = true; } @@ -1575,7 +1574,7 @@ void HPWH::addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbient if (setOfSources[i].typeOfHeatSource == TYPE_extra) { // Set up the extra heat source - setOfSources[i].setupExtraHeat(nodeExtraHeat_W); + setOfSources[i].setupExtraHeat(nodePowerExtra_W); // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() calcDerivedHeatingValues(); @@ -2412,15 +2411,15 @@ void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { } ////////////////////////////////////////////////////////////////////////////////////////////////////// -void HPWH::HeatSource::setupExtraHeat(std::vector* nodeExtraHeat_W) { +void HPWH::HeatSource::setupExtraHeat(std::vector* nodePowerExtra_W) { //get sum of vector double watts = 0.0; - for (unsigned int i = 0; i < (*nodeExtraHeat_W).size(); i++) { - watts += (*nodeExtraHeat_W)[i]; + for (unsigned int i = 0; i < (*nodePowerExtra_W).size(); i++) { + watts += (*nodePowerExtra_W)[i]; } - std::vector tempCondensity = *nodeExtraHeat_W; + std::vector tempCondensity = *nodePowerExtra_W; normalize(tempCondensity); // set condensity based on normalized vector setCondensity( tempCondensity[0], tempCondensity[1], tempCondensity[2], tempCondensity[3], diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index b3782ac6..7b7281b8 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -245,7 +245,7 @@ class HPWH { double ambientT_C, double externalT_C, DRMODES DRstatus, double minutesPerStep, double inletVol2_L = 0., double inletT2_C = 0., - std::vector* nodeExtraHeat_W = NULL); + std::vector* nodePowerExtra_W = NULL); /**< This function will progress the simulation forward in time by one step * all calculated outputs are stored in private variables and accessed through functions * @@ -394,10 +394,10 @@ class HPWH { /** An overloaded function that uses some member variables, instead of taking them as inputs */ int runOneStep(double drawVolume_L, double ambientT_C, double externalT_C, DRMODES DRstatus, double inletVol2_L = 0., double inletT2_C = 0., - std::vector* nodeExtraHeat_W = NULL) { + std::vector* nodePowerExtra_W = NULL) { return runOneStep(member_inletT_C, drawVolume_L, ambientT_C, externalT_C, DRstatus, member_minutesPerStep, inletVol2_L, inletT2_C, - nodeExtraHeat_W); + nodePowerExtra_W); }; @@ -420,7 +420,7 @@ class HPWH { void turnAllHeatSourcesOff(); /**< disengage each heat source */ - void addExtraHeat(std::vector* nodeExtraHeat_W, double tankAmbientT_C, double minutesPerStep); + void addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbientT_C, double minutesPerStep); /**< adds extra heat defined by the user. Where nodeExtraHeat[] is a vector of heat quantities to be added during the step. nodeExtraHeat[ 0] would go to bottom node, 1 to next etc. */ double tankAvg_C(const std::vector nodeWeights) const; @@ -550,9 +550,9 @@ class HPWH::HeatSource { void setupAsResistiveElement(int node, double Watts); /**< configure the heat source to be a resisive element, positioned at the specified node, with the specified power in watts */ - void setupExtraHeat(std::vector* nodeExtraHeat_W); + void setupExtraHeat(std::vector* nodePowerExtra_W); /**< Configure a user defined heat source added as extra, based off using - nodeExtraHeat_W as the total watt input and the condensity*/ + nodePowerExtra_W as the total watt input and the condensity*/ bool isEngaged() const; /**< return whether or not the heat source is engaged */ From 60f23e1a670443c800a63d72600a2d5a639420d9 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Wed, 22 Jan 2020 11:45:49 -0800 Subject: [PATCH 08/22] Any size ptr --- src/HPWH.cc | 25 ++++++++++++++++++------- test/main.cc | 14 ++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 3b94ea7d..ecfab264 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -382,7 +382,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, //If theres extra user defined heat to add -> Add extra heat! - if (nodePowerExtra_W != NULL) { + if (nodePowerExtra_W != NULL && (*nodePowerExtra_W).size() != 0) { addExtraHeat(nodePowerExtra_W, tankAmbientT_C, minutesToRun); } @@ -1563,9 +1563,9 @@ void HPWH::mixTankInversions() { void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbientT_C, double minutesToRun){ - if ((*nodePowerExtra_W).size() != 12){ + if ((*nodePowerExtra_W).size() > CONDENSITY_SIZE){ if (hpwhVerbosity >= VRB_reluctant) { - msg("nodeExtraHeat_KWH (%i) does not have 12 nodes \n", (*nodePowerExtra_W).size(), numNodes); + msg("nodeExtraHeat_KWH (%i) has size greater than %d \n", (*nodePowerExtra_W).size(), CONDENSITY_SIZE); } simHasFailed = true; } @@ -2412,20 +2412,31 @@ void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { ////////////////////////////////////////////////////////////////////////////////////////////////////// void HPWH::HeatSource::setupExtraHeat(std::vector* nodePowerExtra_W) { - - //get sum of vector + + std::vector tempCondensity(CONDENSITY_SIZE); double watts = 0.0; for (unsigned int i = 0; i < (*nodePowerExtra_W).size(); i++) { + //get sum of vector watts += (*nodePowerExtra_W)[i]; + + //put into vector for normalization + tempCondensity[i] = (*nodePowerExtra_W)[i]; } - std::vector tempCondensity = *nodePowerExtra_W; normalize(tempCondensity); + + if (hpwh->hpwhVerbosity >= VRB_emetic){ + hpwh->msg("extra heat condensity: "); + for (unsigned int i = 0; i < tempCondensity.size(); i++) { + hpwh->msg("C[%d]: %f", i, tempCondensity[i]); + } + hpwh->msg("\n "); + } + // set condensity based on normalized vector setCondensity( tempCondensity[0], tempCondensity[1], tempCondensity[2], tempCondensity[3], tempCondensity[4], tempCondensity[5], tempCondensity[6], tempCondensity[7], tempCondensity[8], tempCondensity[9], tempCondensity[10], tempCondensity[11] ); - perfMap.clear(); perfMap.reserve(2); diff --git a/test/main.cc b/test/main.cc index 1e1ef38c..e673cf7f 100644 --- a/test/main.cc +++ b/test/main.cc @@ -278,14 +278,12 @@ int main(int argc, char *argv[]) drStatus = HPWH::DR_ENGAGE; } -//vectptr = NULL; -//if ( i == 15 || i== 16 || i== 17){ -// nodeExtraHeat_W = { 25000, 2., 3., 4., 5, 6, 7, 8, 9, 10, 11, 12 }; -// vectptr = &nodeExtraHeat_W; -// cout << "Now on minute " << i << "\n"; -// -//} -// + vectptr = NULL; + if ( i == 15 || i== 16 || i== 17){ + nodeExtraHeat_W = {20000 }; + vectptr = &nodeExtraHeat_W; + } + // Run the step hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) GAL_TO_L(allSchedules[1][i]), // Flow in gallons From 9fbfb661b387ed1791ea2fcf2fafde8ebabeeeb8 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Wed, 22 Jan 2020 16:13:02 -0800 Subject: [PATCH 09/22] Zeroed Electricity --- src/HPWH.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index ecfab264..e3c93639 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -1581,6 +1581,12 @@ void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbien // add heat setOfSources[i].addHeat(tankAmbientT_C, minutesToRun); + + setOfSources[i].perfMap.clear(); + + // 0 out to ignore + setOfSources[i].energyInput_kWh = 0.0; + setOfSources[i].energyOutput_kWh = 0.0; } } } @@ -3572,11 +3578,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { else if (presetNum == MODELS_StorageTank) { numNodes = 12; tankTemps_C = new double[numNodes]; - setpoint_C = F_TO_C(127.0); + setpoint_C = 52; //start tank off at setpoint resetTankToSetpoint(); + setpoint_C = 800; + tankVolume_L = GAL_TO_L(80); tankUA_kJperHrC = 10; //0 to turn off @@ -3595,11 +3603,11 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { extra.typeOfHeatSource = TYPE_extra; extra.configuration = HeatSource::CONFIG_WRAPPED; - extra.addTurnOnLogic(HPWH::bottomThird(200)); - + extra.addTurnOnLogic(HPWH::topThird_absolute(1)); + //initial guess, will get reset based on the input heat vector extra.setCondensity(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - + setOfSources[0] = extra; } From d0db6b726a669c2f2c8e5442a603e3030ea14106 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Thu, 23 Jan 2020 11:39:22 -0800 Subject: [PATCH 10/22] Testing --- src/HPWH.cc | 16 ++++++++++------ test/main.cc | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index e3c93639..6e19d36b 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -383,7 +383,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, //If theres extra user defined heat to add -> Add extra heat! if (nodePowerExtra_W != NULL && (*nodePowerExtra_W).size() != 0) { - addExtraHeat(nodePowerExtra_W, tankAmbientT_C, minutesToRun); + addExtraHeat(nodePowerExtra_W, tankAmbientT_C, minutesPerStep); } @@ -1562,14 +1562,19 @@ void HPWH::mixTankInversions() { } -void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbientT_C, double minutesToRun){ +void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbientT_C, double minutesPerStep){ if ((*nodePowerExtra_W).size() > CONDENSITY_SIZE){ if (hpwhVerbosity >= VRB_reluctant) { msg("nodeExtraHeat_KWH (%i) has size greater than %d \n", (*nodePowerExtra_W).size(), CONDENSITY_SIZE); } simHasFailed = true; } - + + //for (unsigned int i = 0; i < (*nodePowerExtra_W).size(); i++){ + // tankTemps_C[i] += (*nodePowerExtra_W)[i] * minutesPerStep * 60. / (CPWATER_kJperkgC * 1000. * DENSITYWATER_kgperL * tankVolume_L / numNodes); + //} + //mixTankInversions(); + for (int i = 0; i < numHeatSources; i++){ if (setOfSources[i].typeOfHeatSource == TYPE_extra) { @@ -1581,10 +1586,9 @@ void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbien // add heat setOfSources[i].addHeat(tankAmbientT_C, minutesToRun); - - setOfSources[i].perfMap.clear(); - // 0 out to ignore + // 0 out to ignore features + setOfSources[i].perfMap.clear(); setOfSources[i].energyInput_kWh = 0.0; setOfSources[i].energyOutput_kWh = 0.0; } diff --git a/test/main.cc b/test/main.cc index e673cf7f..09947dc7 100644 --- a/test/main.cc +++ b/test/main.cc @@ -280,7 +280,7 @@ int main(int argc, char *argv[]) vectptr = NULL; if ( i == 15 || i== 16 || i== 17){ - nodeExtraHeat_W = {20000 }; + nodeExtraHeat_W = {200000 }; vectptr = &nodeExtraHeat_W; } From 6f4968705da45f066e345385d2e156dbff71e3b9 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Fri, 24 Jan 2020 15:43:30 -0800 Subject: [PATCH 11/22] Debug Draw over the volume of the tank --- src/HPWH.cc | 17 ++++++++++++----- test/main.cc | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 292b9e3c..3a749a5d 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -1350,15 +1350,22 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi lowInletT = inletT_C; lowInletV = drawVolume_L - inletVol2_L; } - //calculate how many nodes to draw (drawVolume_N) drawVolume_N = drawVolume_L / volPerNode_LperNode; - if (drawVolume_N > numNodes) { + if (drawVolume_L > tankVolume_L) { if (hpwhVerbosity >= VRB_reluctant) { - msg("Drawing more than the tank volume in one step is undefined behavior. Terminating simulation. \n"); + //msg("WARNING: Drawing more than the tank volume in one step is undefined behavior. Terminating simulation. \n"); + msg("WARNING: Drawing more than the tank volume in one step is undefined behavior. Continuing simulation at your own risk. \n"); } - simHasFailed = true; - return; + //simHasFailed = true; + //return; + for (int i = 0; i < numNodes; i++){ + outletTemp_C += tankTemps_C[i]; + tankTemps_C[i] = (inletT_C * (drawVolume_L - inletVol2_L) + inletT2_C * inletVol2_L) / drawVolume_L; + } + outletTemp_C = (outletTemp_C*tankVolume_L + tankTemps_C[0] * (drawVolume_L - tankVolume_L)) * numNodes; + + drawVolume_N = -1.; } ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/main.cc b/test/main.cc index b8ec01af..45a2d9e8 100644 --- a/test/main.cc +++ b/test/main.cc @@ -275,7 +275,10 @@ int main(int argc, char *argv[]) } else if(allSchedules[4][i] == 2) { drStatus = HPWH::DR_ENGAGE; } + if (i == 3) { allSchedules[1][i] = 80.1; } // Run the step + cout << "Now on minute " << i << "\n"; + hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) GAL_TO_L(allSchedules[1][i]), // Flow in gallons airTemp2, // Ambient Temp (C) From 7f7bd5f0fe28b6b7f834ce3fbc19b4bdd046cb1d Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Fri, 24 Jan 2020 16:19:04 -0800 Subject: [PATCH 12/22] Fix Outlet Temp --- src/HPWH.cc | 4 ++-- test/main.cc | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 3a749a5d..8ef53148 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -1363,9 +1363,9 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi outletTemp_C += tankTemps_C[i]; tankTemps_C[i] = (inletT_C * (drawVolume_L - inletVol2_L) + inletT2_C * inletVol2_L) / drawVolume_L; } - outletTemp_C = (outletTemp_C*tankVolume_L + tankTemps_C[0] * (drawVolume_L - tankVolume_L)) * numNodes; + outletTemp_C = (outletTemp_C / numNodes * tankVolume_L + tankTemps_C[0] * (drawVolume_L - tankVolume_L))/drawVolume_L * (drawVolume_L / volPerNode_LperNode); - drawVolume_N = -1.; + drawVolume_N = 0.; } ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/main.cc b/test/main.cc index 45a2d9e8..bc093051 100644 --- a/test/main.cc +++ b/test/main.cc @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) ifstream controlFile; string strPreamble; - string strHead = "minutes,Ta,inletT,draw,"; + string strHead = "minutes,Ta,inletT,draw,outletT"; //....................................... //process command line arguments @@ -275,7 +275,7 @@ int main(int argc, char *argv[]) } else if(allSchedules[4][i] == 2) { drStatus = HPWH::DR_ENGAGE; } - if (i == 3) { allSchedules[1][i] = 80.1; } + if (i == 1) { allSchedules[1][i] = 80.1; } // Run the step cout << "Now on minute " << i << "\n"; @@ -294,7 +294,9 @@ int main(int argc, char *argv[]) airTemp2 = hpwh.getLocationTemp_C(); } - strPreamble = std::to_string(i) + ", " + std::to_string(airTemp2) + ", " + std::to_string(allSchedules[0][i]) + ", " + std::to_string(allSchedules[1][i]) + ", "; + strPreamble = std::to_string(i) + ", " + std::to_string(airTemp2) + ", " + + std::to_string(allSchedules[0][i]) + ", " + std::to_string(allSchedules[1][i]) + ", " + + std::to_string(hpwh.getOutletTemp()) + ","; hpwh.WriteCSVRow(outputFile, strPreamble.c_str(), nTestTCouples, 0); } From d1278b07dadfd531fcad0758b71b5cd643d3c59a Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Mon, 27 Jan 2020 15:18:36 -0800 Subject: [PATCH 13/22] Debug minutes of run --- src/HPWH.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 6e19d36b..e9340ed8 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -1585,7 +1585,7 @@ void HPWH::addExtraHeat(std::vector* nodePowerExtra_W, double tankAmbien calcDerivedHeatingValues(); // add heat - setOfSources[i].addHeat(tankAmbientT_C, minutesToRun); + setOfSources[i].addHeat(tankAmbientT_C, minutesPerStep); // 0 out to ignore features setOfSources[i].perfMap.clear(); From 0d2cf26cb5088977548bf19980bab61482701a82 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Mon, 27 Jan 2020 16:37:50 -0800 Subject: [PATCH 14/22] Set constants outside timestep Moves many constants that were calculated every time step to the initialization process. --- src/HPWH.cc | 78 +++++++++++++++++++++++++++++++++++--------------- src/HPWH.in.hh | 32 ++++++++++++++------- 2 files changed, 77 insertions(+), 33 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 292b9e3c..ccfd7ce0 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -119,6 +119,10 @@ HPWH::HPWH(const HPWH &hpwh){ locationTemperature_C = hpwh.locationTemperature_C; + volPerNode_LperNode = hpwh.volPerNode_LperNode; + node_height = hpwh.node_height; + UA_top = hpwh.UA_top; + UA_rad = hpwh.UA_rad; } HPWH & HPWH::operator=(const HPWH &hpwh){ @@ -183,6 +187,11 @@ HPWH & HPWH::operator=(const HPWH &hpwh){ locationTemperature_C = hpwh.locationTemperature_C; + volPerNode_LperNode = hpwh.volPerNode_LperNode; + node_height = hpwh.node_height; + UA_top = hpwh.UA_top; + UA_rad = hpwh.UA_rad; + return *this; } @@ -736,7 +745,7 @@ int HPWH::setDoTempDepression(bool doTempDepress) { int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/){ //Uses the UA before the function is called and adjusts the A part of the UA to match the input volume given getTankSurfaceArea(). double HPWH_size_L; - double oldA = getTankSurfaceArea(UNITS_FT2); + double oldArea = getTankSurfaceArea(UNITS_FT2); if (units == UNITS_L) { HPWH_size_L = HPWH_size; @@ -751,7 +760,7 @@ int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/){ return HPWH_ABORT; } setTankSize(HPWH_size_L, UNITS_L); - setUA(tankUA_kJperHrC / oldA * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); + setUA(tankUA_kJperHrC / oldArea * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); return 0; } @@ -816,6 +825,7 @@ int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/) { return HPWH_ABORT; } } + calcUAandSizeConstants(); return 0; } int HPWH::setDoInversionMixing(bool doInvMix) { @@ -840,6 +850,7 @@ int HPWH::setUA(double UA, UNITS units /*=UNITS_kJperHrC*/) { } return HPWH_ABORT; } + calcUAandSizeConstants(); return 0; } @@ -1313,7 +1324,6 @@ double HPWH::getLocationTemp_C() const { void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbientT_C, double minutesPerStep, double inletVol2_L, double inletT2_C) { //set up some useful variables for calculations - double volPerNode_LperNode = tankVolume_L / numNodes; double drawFraction; this->outletTemp_C = 0; double nodeInletFraction, cumInletFraction, drawVolume_N, nodeInletTV; @@ -1433,19 +1443,19 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } //end if(draw_volume_L > 0) - // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. - // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions - // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. - // height estimate from Rheem along with the volume is used to get the radius and node_height - const double rad = getTankRadius(UNITS_M); - const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); - const double node_height = height / numNodes; - - // The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_bt + UA_r is the total tank area. - const double UA_bt = tankUA_kJperHrC * rad / (2.0 * (height + rad)); - - // UA_r is the faction of the area of the cylinder that's not the top or bottom. - const double UA_r = height / (height + rad); +//// calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. +//// model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions +//// on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. +//// height estimate from Rheem along with the volume is used to get the radius and node_height +//const double rad = getTankRadius(UNITS_M); +//const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); +//const double node_height = height / numNodes; +// +//// The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_top + UA_radad is the total tank area. +//const double UA_top = tankUA_kJperHrC * rad / (2.0 * (height + rad)); +// +//// UA_rad is the faction of the area of the cylinder that's not the top or bottom. +//const double UA_rad = height / (height + rad); if (doConduction) { @@ -1456,7 +1466,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } // Boundary condition for the finite difference. - const double bc = 2.0 * tau * UA_bt * node_height / KWATER_WpermC; + const double bc = 2.0 * tau * tankUA_kJperHrC * UA_top * node_height / KWATER_WpermC; // Boundary nodes for finite difference nextTankTemps_C[0] = (1.0 - 2.0 * tau - bc) * tankTemps_C[0] + 2.0 * tau * tankTemps_C[1] + bc * tankAmbientT_C; @@ -1469,8 +1479,8 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi // nextTankTemps_C gets assigns to tankTemps_C at the bottom of the function after q_UA. // UA loss from the sides are found at the bottom of the function. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kJ += (tankUA_kJperHrC * UA_bt * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * UA_top * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kJ += (tankUA_kJperHrC * UA_top * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); } else { // Ignore tank conduction and calculate UA losses from top and bottom. UA loss from the sides are found at the bottom of the function @@ -1480,13 +1490,13 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } //kJ's lost as standby in the current time step for the top node. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * UA_top * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); nextTankTemps_C[0] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); //kJ's lost as standby in the current time step for the bottom node. - standbyLosses_kJ = (tankUA_kJperHrC * UA_bt * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kJ = (tankUA_kJperHrC * UA_top * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); nextTankTemps_C[numNodes - 1] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); @@ -1499,7 +1509,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi for (int i = 0; i < numNodes; i++) { //faction of tank area on the sides //kJ's lost as standby in the current time step for each node. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_r / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * UA_rad / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); //The effect of standby loss on temperature in each node @@ -2389,6 +2399,27 @@ void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { this->shutOffLogicSet.push_back(logic); } +void HPWH::calcUAandSizeConstants() { + // gets called when initializing and reseting tank sizes or UA + + volPerNode_LperNode = tankVolume_L / numNodes; + + // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. + // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions + // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. + // height estimate from Rheem along with the volume is used to get the radius and node_height + const double tank_rad = getTankRadius(UNITS_M); + const double tank_height = tankVolume_L / (1000.0 * 3.14159 * tank_rad * tank_rad); + + node_height = tank_height / numNodes; + + // The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_top + UA_rad is the total tank area. + UA_top = tank_rad / (2.0 * (tank_height + tank_rad)); + + // UA_rad is the faction of the area of the cylinder that's not the top or bottom. + UA_rad = tank_height / (tank_height + tank_rad); +} + void HPWH::calcDerivedValues(){ static char outputString[MAXOUTSTRING]; //this is used for debugging outputs @@ -2478,9 +2509,10 @@ void HPWH::calcDerivedValues(){ } } + calcUAandSizeConstants(); +} -} // Used to check a few inputs after the initialization of a tank model from a preset or a file. int HPWH::checkInputs(){ diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index a2c52759..1cd626cf 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -420,6 +420,8 @@ class HPWH { void calcDerivedValues(); /**< a helper function for the inits, calculating condentropy and the lowest node */ + void calcUAandSizeConstants(); + /**< a helper function to set constants for the UA and tank size*/ int checkInputs(); /**< a helper function to run a few checks on the HPWH input parameters */ @@ -457,29 +459,39 @@ class HPWH { HeatSource *setOfSources; /**< an array containing the HeatSources, in order of priority */ - int compressorIndex; - /**< The index of the compressor heat source (set to -1 if no compressor)*/ + int compressorIndex; + /**< The index of the compressor heat source (set to -1 if no compressor)*/ - int lowestElementIndex; - /**< The index of the lowest resistance element heat source (set to -1 if no resistance elements)*/ + int lowestElementIndex; + /**< The index of the lowest resistance element heat source (set to -1 if no resistance elements)*/ int numNodes; /**< the number of nodes in the tank - must be >= 12, in multiples of 12 */ - int nodeDensity; - /**< the number of calculation nodes in a logical node */ + int nodeDensity; + /**< the number of calculation nodes in a logical node */ - int inletHeight; - /**< the number of a node in the tank that the inlet water enters the tank at, must be between 0 and numNodes-1 */ + int inletHeight; + /**< the number of a node in the tank that the inlet water enters the tank at, must be between 0 and numNodes-1 */ - int inlet2Height; - /**< the number of a node in the tank that the 2nd inlet water enters the tank at, must be between 0 and numNodes-1 */ + int inlet2Height; + /**< the number of a node in the tank that the 2nd inlet water enters the tank at, must be between 0 and numNodes-1 */ double tankVolume_L; /**< the volume in liters of the tank */ double tankUA_kJperHrC; /**< the UA of the tank, in metric units */ + double volPerNode_LperNode; + /**< the volume in liters of a single node */ + double node_height; + /**< the height in meters of the one node */ + double UA_top; + /**< the fraction of the UA on the top and bottom of the tank, assuming it's a cylinder */ + double UA_rad; + /**< the fraction of the UA on the sides of the tank, assuming it's a cylinder */ + + double setpoint_C; /**< the setpoint of the tank */ double *tankTemps_C; From 2e2ce85ee87caa4f6af5713f1130fe7bc0feca1a Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Mon, 27 Jan 2020 17:18:50 -0800 Subject: [PATCH 15/22] Clean Comments --- src/HPWH.cc | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index ccfd7ce0..04226554 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -1443,20 +1443,10 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } //end if(draw_volume_L > 0) -//// calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. -//// model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions -//// on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. -//// height estimate from Rheem along with the volume is used to get the radius and node_height -//const double rad = getTankRadius(UNITS_M); -//const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); -//const double node_height = height / numNodes; -// -//// The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_top + UA_radad is the total tank area. -//const double UA_top = tankUA_kJperHrC * rad / (2.0 * (height + rad)); -// -//// UA_rad is the faction of the area of the cylinder that's not the top or bottom. -//const double UA_rad = height / (height + rad); - + // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. + // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions + // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. + // height estimate from Rheem along with the volume is used to get the radius and node_height if (doConduction) { // Get the "constant" tau for the stability condition and the conduction calculation @@ -2404,16 +2394,16 @@ void HPWH::calcUAandSizeConstants() { volPerNode_LperNode = tankVolume_L / numNodes; - // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. - // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions - // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. - // height estimate from Rheem along with the volume is used to get the radius and node_height + const double tank_rad = getTankRadius(UNITS_M); const double tank_height = tankVolume_L / (1000.0 * 3.14159 * tank_rad * tank_rad); node_height = tank_height / numNodes; - - // The fraction of UA that is on the top or the bottom of the tank. So 2 * UA_top + UA_rad is the total tank area. + + // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions + // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. + // We assume the UA is divided only by the surface area of the tank and ignore fittings, etc. + // The fraction of UA that is on the top or the bottom of the tank, such that 2 * UA_top + UA_rad is the total tank area. UA_top = tank_rad / (2.0 * (tank_height + tank_rad)); // UA_rad is the faction of the area of the cylinder that's not the top or bottom. From 9f2a88792f6631f5cc97c6b9ee1e28db77ff796f Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 28 Jan 2020 11:30:16 -0800 Subject: [PATCH 16/22] Better Names --- src/HPWH.cc | 28 ++++++++++++++-------------- src/HPWH.in.hh | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 04226554..47486291 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -121,8 +121,8 @@ HPWH::HPWH(const HPWH &hpwh){ volPerNode_LperNode = hpwh.volPerNode_LperNode; node_height = hpwh.node_height; - UA_top = hpwh.UA_top; - UA_rad = hpwh.UA_rad; + fracAreaTop = hpwh.fracAreaTop; + fracAreaTop = hpwh.fracAreaTop; } HPWH & HPWH::operator=(const HPWH &hpwh){ @@ -189,8 +189,8 @@ HPWH & HPWH::operator=(const HPWH &hpwh){ volPerNode_LperNode = hpwh.volPerNode_LperNode; node_height = hpwh.node_height; - UA_top = hpwh.UA_top; - UA_rad = hpwh.UA_rad; + fracAreaTop = hpwh.fracAreaTop; + fracAreaTop = hpwh.fracAreaTop; return *this; } @@ -1456,7 +1456,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } // Boundary condition for the finite difference. - const double bc = 2.0 * tau * tankUA_kJperHrC * UA_top * node_height / KWATER_WpermC; + const double bc = 2.0 * tau * tankUA_kJperHrC * fracAreaTop * node_height / KWATER_WpermC; // Boundary nodes for finite difference nextTankTemps_C[0] = (1.0 - 2.0 * tau - bc) * tankTemps_C[0] + 2.0 * tau * tankTemps_C[1] + bc * tankAmbientT_C; @@ -1469,8 +1469,8 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi // nextTankTemps_C gets assigns to tankTemps_C at the bottom of the function after q_UA. // UA loss from the sides are found at the bottom of the function. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_top * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kJ += (tankUA_kJperHrC * UA_top * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaTop * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kJ += (tankUA_kJperHrC * fracAreaTop * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); } else { // Ignore tank conduction and calculate UA losses from top and bottom. UA loss from the sides are found at the bottom of the function @@ -1480,13 +1480,13 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } //kJ's lost as standby in the current time step for the top node. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_top * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaTop * (tankTemps_C[0] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); nextTankTemps_C[0] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); //kJ's lost as standby in the current time step for the bottom node. - standbyLosses_kJ = (tankUA_kJperHrC * UA_top * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kJ = (tankUA_kJperHrC * fracAreaTop * (tankTemps_C[numNodes - 1] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); nextTankTemps_C[numNodes - 1] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); @@ -1499,7 +1499,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi for (int i = 0; i < numNodes; i++) { //faction of tank area on the sides //kJ's lost as standby in the current time step for each node. - double standbyLosses_kJ = (tankUA_kJperHrC * UA_rad / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaTop / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); //The effect of standby loss on temperature in each node @@ -2403,11 +2403,11 @@ void HPWH::calcUAandSizeConstants() { // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. // We assume the UA is divided only by the surface area of the tank and ignore fittings, etc. - // The fraction of UA that is on the top or the bottom of the tank, such that 2 * UA_top + UA_rad is the total tank area. - UA_top = tank_rad / (2.0 * (tank_height + tank_rad)); + // The fraction of UA that is on the top or the bottom of the tank, such that 2 * fracAreaTop + fracAreaTop is the total tank area. + fracAreaTop = tank_rad / (2.0 * (tank_height + tank_rad)); - // UA_rad is the faction of the area of the cylinder that's not the top or bottom. - UA_rad = tank_height / (tank_height + tank_rad); + // fracAreaTop is the faction of the area of the cylinder that's not the top or bottom. + fracAreaTop = tank_height / (tank_height + tank_rad); } void HPWH::calcDerivedValues(){ diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index 1cd626cf..e69f0e47 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -486,9 +486,9 @@ class HPWH { /**< the volume in liters of a single node */ double node_height; /**< the height in meters of the one node */ - double UA_top; + double fracAreaTop; /**< the fraction of the UA on the top and bottom of the tank, assuming it's a cylinder */ - double UA_rad; + double fracAreaSide; /**< the fraction of the UA on the sides of the tank, assuming it's a cylinder */ From da1746bb9e3ec3eb79a2eaf9f615c96b2d65ee59 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 28 Jan 2020 11:50:56 -0800 Subject: [PATCH 17/22] Update HPWH.cc --- src/HPWH.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 47486291..cc8dd919 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -122,7 +122,7 @@ HPWH::HPWH(const HPWH &hpwh){ volPerNode_LperNode = hpwh.volPerNode_LperNode; node_height = hpwh.node_height; fracAreaTop = hpwh.fracAreaTop; - fracAreaTop = hpwh.fracAreaTop; + fracAreaSide = hpwh.fracAreaSide; } HPWH & HPWH::operator=(const HPWH &hpwh){ @@ -190,7 +190,7 @@ HPWH & HPWH::operator=(const HPWH &hpwh){ volPerNode_LperNode = hpwh.volPerNode_LperNode; node_height = hpwh.node_height; fracAreaTop = hpwh.fracAreaTop; - fracAreaTop = hpwh.fracAreaTop; + fracAreaSide = hpwh.fracAreaSide; return *this; } @@ -1499,7 +1499,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi for (int i = 0; i < numNodes; i++) { //faction of tank area on the sides //kJ's lost as standby in the current time step for each node. - double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaTop / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); + double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaSide / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); //The effect of standby loss on temperature in each node @@ -2403,11 +2403,11 @@ void HPWH::calcUAandSizeConstants() { // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. // We assume the UA is divided only by the surface area of the tank and ignore fittings, etc. - // The fraction of UA that is on the top or the bottom of the tank, such that 2 * fracAreaTop + fracAreaTop is the total tank area. + // The fraction of UA that is on the top or the bottom of the tank, such that 2 * fracAreaTop + fracAreaSide is the total tank area. fracAreaTop = tank_rad / (2.0 * (tank_height + tank_rad)); - // fracAreaTop is the faction of the area of the cylinder that's not the top or bottom. - fracAreaTop = tank_height / (tank_height + tank_rad); + // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. + fracAreaSide = tank_height / (tank_height + tank_rad); } void HPWH::calcDerivedValues(){ @@ -4295,7 +4295,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { resetTankToSetpoint(); tankVolume_L = 299.5; - tankUA_kJperHrC = 9; + tankUA_kJperHrC = 0; doTempDepression = false; tankMixesOnDraw = true; From f35adf306cc1ba541210160cf20046745f35de12 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 28 Jan 2020 16:18:00 -0800 Subject: [PATCH 18/22] Update HPWH.cc --- src/HPWH.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index cc8dd919..ce4d816e 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -4295,7 +4295,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { resetTankToSetpoint(); tankVolume_L = 299.5; - tankUA_kJperHrC = 0; + tankUA_kJperHrC = 9; doTempDepression = false; tankMixesOnDraw = true; From bfd1f9f7bbba5bc11e01486050c4121a24e4ec9b Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 28 Jan 2020 16:25:31 -0800 Subject: [PATCH 19/22] Appropriate Naming --- src/HPWH.cc | 7 +++---- src/HPWH.in.hh | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index ce4d816e..949abf14 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -825,7 +825,7 @@ int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/) { return HPWH_ABORT; } } - calcUAandSizeConstants(); + calcSizeConstants(); return 0; } int HPWH::setDoInversionMixing(bool doInvMix) { @@ -850,7 +850,6 @@ int HPWH::setUA(double UA, UNITS units /*=UNITS_kJperHrC*/) { } return HPWH_ABORT; } - calcUAandSizeConstants(); return 0; } @@ -2389,7 +2388,7 @@ void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { this->shutOffLogicSet.push_back(logic); } -void HPWH::calcUAandSizeConstants() { +void HPWH::calcSizeConstants() { // gets called when initializing and reseting tank sizes or UA volPerNode_LperNode = tankVolume_L / numNodes; @@ -2499,7 +2498,7 @@ void HPWH::calcDerivedValues(){ } } - calcUAandSizeConstants(); + calcSizeConstants(); } diff --git a/src/HPWH.in.hh b/src/HPWH.in.hh index e69f0e47..64eb7782 100644 --- a/src/HPWH.in.hh +++ b/src/HPWH.in.hh @@ -420,7 +420,7 @@ class HPWH { void calcDerivedValues(); /**< a helper function for the inits, calculating condentropy and the lowest node */ - void calcUAandSizeConstants(); + void calcSizeConstants(); /**< a helper function to set constants for the UA and tank size*/ int checkInputs(); /**< a helper function to run a few checks on the HPWH input parameters */ From 0c366e97a491234db2baf3974ed43672509d728d Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Tue, 28 Jan 2020 16:42:19 -0800 Subject: [PATCH 20/22] testfix --- src/HPWH.cc | 611 ++++++++++++++++++++++++++-------------------------- 1 file changed, 303 insertions(+), 308 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 949abf14..4cb58428 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -60,19 +60,19 @@ const float HPWH::UNINITIALIZED_LOCATIONTEMP = -500.f; const std::string HPWH::version_maint = HPWHVRSN_META; #define SETPOINT_FIX // #define to include fixes for - // setpoint-below-water-temp issues - // 1-22-2017 +// setpoint-below-water-temp issues +// 1-22-2017 //the HPWH functions //the publics HPWH::HPWH() : -simHasFailed(true), isHeating(false), setpointFixed(false), hpwhVerbosity(VRB_silent), -messageCallback(NULL), messageCallbackContextPtr(NULL), numHeatSources(0), -setOfSources(NULL), tankTemps_C(NULL), nextTankTemps_C(NULL), doTempDepression(false), locationTemperature_C(UNINITIALIZED_LOCATIONTEMP), -doInversionMixing(true), doConduction(true), inletHeight(0), inlet2Height(0) + simHasFailed(true), isHeating(false), setpointFixed(false), hpwhVerbosity(VRB_silent), + messageCallback(NULL), messageCallbackContextPtr(NULL), numHeatSources(0), + setOfSources(NULL), tankTemps_C(NULL), nextTankTemps_C(NULL), doTempDepression(false), locationTemperature_C(UNINITIALIZED_LOCATIONTEMP), + doInversionMixing(true), doConduction(true), inletHeight(0), inlet2Height(0) { } -HPWH::HPWH(const HPWH &hpwh){ +HPWH::HPWH(const HPWH &hpwh) { simHasFailed = hpwh.simHasFailed; hpwhVerbosity = hpwh.hpwhVerbosity; @@ -114,18 +114,14 @@ HPWH::HPWH(const HPWH &hpwh){ tankMixesOnDraw = hpwh.tankMixesOnDraw; doTempDepression = hpwh.doTempDepression; - doInversionMixing = hpwh.doInversionMixing; + doInversionMixing = hpwh.doInversionMixing; doConduction = hpwh.doConduction; locationTemperature_C = hpwh.locationTemperature_C; - volPerNode_LperNode = hpwh.volPerNode_LperNode; - node_height = hpwh.node_height; - fracAreaTop = hpwh.fracAreaTop; - fracAreaSide = hpwh.fracAreaSide; } -HPWH & HPWH::operator=(const HPWH &hpwh){ +HPWH & HPWH::operator=(const HPWH &hpwh) { if (this == &hpwh) { return *this; } @@ -187,11 +183,6 @@ HPWH & HPWH::operator=(const HPWH &hpwh){ locationTemperature_C = hpwh.locationTemperature_C; - volPerNode_LperNode = hpwh.volPerNode_LperNode; - node_height = hpwh.node_height; - fracAreaTop = hpwh.fracAreaTop; - fracAreaSide = hpwh.fracAreaSide; - return *this; } @@ -201,7 +192,7 @@ HPWH::~HPWH() { delete[] setOfSources; } -string HPWH::getVersion(){ +string HPWH::getVersion() { std::stringstream version; version << version_major << '.' << version_minor << '.' << version_patch << version_maint; @@ -212,7 +203,7 @@ string HPWH::getVersion(){ int HPWH::runOneStep(double inletT_C, double drawVolume_L, double tankAmbientT_C, double heatSourceAmbientT_C, - DRMODES DRstatus, double minutesPerStep, + DRMODES DRstatus, double minutesPerStep, double inletVol2_L, double inletT2_C) { //returns 0 on successful completion, HPWH_ABORT on failure @@ -266,7 +257,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, //process draws and standby losses updateTankTemps(drawVolume_L, inletT_C, tankAmbientT_C, minutesPerStep, inletVol2_L, inletT2_C); - + //do HeatSource choice for (int i = 0; i < numHeatSources; i++) { if (hpwhVerbosity >= VRB_emetic) { @@ -308,7 +299,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, } //end loop over heat sources - if (hpwhVerbosity >= VRB_emetic){ + if (hpwhVerbosity >= VRB_emetic) { msg("after heat source choosing: "); for (int i = 0; i < numHeatSources; i++) { msg("heat source %d: %d \t", i, setOfSources[i].isEngaged()); @@ -332,7 +323,8 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, if (areAllHeatSourcesOff() == true) { if (compressorIndex > -1) { setOfSources[compressorIndex].engageHeatSource(); - } else if (lowestElementIndex > -1) { + } + else if (lowestElementIndex > -1) { setOfSources[lowestElementIndex].engageHeatSource(); } } @@ -359,7 +351,8 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, HeatSource* heatSourcePtr; if (setOfSources[i].isLockedOut() && setOfSources[i].backupHeatSource != NULL) { heatSourcePtr = setOfSources[i].backupHeatSource; - } else { + } + else { heatSourcePtr = &setOfSources[i]; } @@ -368,7 +361,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, //if it finished early if (heatSourcePtr->runtime_min < minutesToRun) { //debugging message handling - if (hpwhVerbosity >= VRB_emetic){ + if (hpwhVerbosity >= VRB_emetic) { msg("done heating! runtime_min minutesToRun %.2lf %.2lf\n", heatSourcePtr->runtime_min, minutesToRun); } @@ -398,10 +391,10 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, } } - if (compressorRan){ + if (compressorRan) { temperatureGoal -= maxDepression_C; //hardcoded 4.5 degree total drop - from experimental data. Changed to an input } - else{ + else { //otherwise, do nothing, we're going back to ambient } @@ -423,7 +416,7 @@ int HPWH::runOneStep(double inletT_C, double drawVolume_L, } //cursory check for inverted temperature profile - if (tankTemps_C[numNodes-1] < tankTemps_C[0]) { + if (tankTemps_C[numNodes - 1] < tankTemps_C[0]) { if (hpwhVerbosity >= VRB_reluctant) { msg("The top of the tank is cooler than the bottom. \n"); } @@ -526,14 +519,14 @@ int HPWH::runNSteps(int N, double *inletT_C, double *drawVolume_L, -void HPWH::setVerbosity(VERBOSITY hpwhVrb){ +void HPWH::setVerbosity(VERBOSITY hpwhVrb) { hpwhVerbosity = hpwhVrb; } -void HPWH::setMessageCallback(void(*callbackFunc)(const string message, void* contextPtr), void* contextPtr){ +void HPWH::setMessageCallback(void(*callbackFunc)(const string message, void* contextPtr), void* contextPtr) { messageCallback = callbackFunc; messageCallbackContextPtr = contextPtr; } -void HPWH::sayMessage(const string message) const{ +void HPWH::sayMessage(const string message) const { if (messageCallback != NULL) { (*messageCallback)(message, messageCallbackContextPtr); } @@ -564,7 +557,7 @@ void HPWH::msgV(const char* fmt, va_list ap /*=NULL*/) const { } // HPWH::msgV -void HPWH::printHeatSourceInfo(){ +void HPWH::printHeatSourceInfo() { std::stringstream ss; double runtime = 0, outputVar = 0; @@ -583,11 +576,11 @@ void HPWH::printHeatSourceInfo(){ for (int i = 0; i < getNumHeatSources(); i++) { runtime = getNthHeatSourceRunTime(i); - if (runtime != 0) { + if (runtime != 0) { outputVar = getNthHeatSourceEnergyInput(i) / (runtime / 60.0); } - else { - outputVar = 0; + else { + outputVar = 0; } ss << "input power kw: " << std::setw(7) << outputVar << "\t\t"; } @@ -600,11 +593,11 @@ void HPWH::printHeatSourceInfo(){ for (int i = 0; i < getNumHeatSources(); i++) { runtime = getNthHeatSourceRunTime(i); - if (runtime != 0) { + if (runtime != 0) { outputVar = getNthHeatSourceEnergyOutput(i, UNITS_KWH) / (runtime / 60.0); } - else { - outputVar = 0; + else { + outputVar = 0; } ss << "output power kw: " << std::setw(7) << outputVar << "\t"; } @@ -648,7 +641,7 @@ int HPWH::WriteCSVHeading(FILE* outFILE, const char* preamble, int nTCouples, in } for (int iTC = 0; iTC < nTCouples; iTC++) { - fprintf(outFILE, ",tcouple%d (%s)", iTC + 1, doIP ? "F":"C"); + fprintf(outFILE, ",tcouple%d (%s)", iTC + 1, doIP ? "F" : "C"); } fprintf(outFILE, "\n"); @@ -679,7 +672,7 @@ int HPWH::WriteCSVRow(FILE* outFILE, const char* preamble, int nTCouples, int op } -bool HPWH::isSetpointFixed(){ +bool HPWH::isSetpointFixed() { return setpointFixed; } @@ -690,7 +683,7 @@ int HPWH::setSetpoint(double newSetpoint, UNITS units /*=UNITS_C*/) { } return HPWH_ABORT; } - else{ + else { if (units == UNITS_C) { setpoint_C = newSetpoint; } @@ -706,12 +699,12 @@ int HPWH::setSetpoint(double newSetpoint, UNITS units /*=UNITS_C*/) { } return 0; } -double HPWH::getSetpoint(){ +double HPWH::getSetpoint() { return setpoint_C; } -int HPWH::resetTankToSetpoint(){ +int HPWH::resetTankToSetpoint() { for (int i = 0; i < numNodes; i++) { tankTemps_C[i] = setpoint_C; } @@ -742,10 +735,10 @@ int HPWH::setDoTempDepression(bool doTempDepress) { return 0; } -int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/){ +int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/) { //Uses the UA before the function is called and adjusts the A part of the UA to match the input volume given getTankSurfaceArea(). - double HPWH_size_L; - double oldArea = getTankSurfaceArea(UNITS_FT2); + double HPWH_size_L; + double oldA = getTankSurfaceArea(UNITS_FT2); if (units == UNITS_L) { HPWH_size_L = HPWH_size; @@ -760,11 +753,11 @@ int HPWH::setTankSize_adjustUA(double HPWH_size, UNITS units /*=UNITS_L*/){ return HPWH_ABORT; } setTankSize(HPWH_size_L, UNITS_L); - setUA(tankUA_kJperHrC / oldArea * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); + setUA(tankUA_kJperHrC / oldA * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); return 0; } -double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/){ +double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/) { // returns tank surface area, old defualt was in ft2 // Based off 88 insulated storage tanks currently available on the market from Sanden, AOSmith, HTP, Rheem, and Niles. // Using the same form of equation given in RACM 2016 App B, equation 41. @@ -784,7 +777,7 @@ double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/){ } } -double HPWH::getTankRadius(UNITS units /*=UNITS_FT*/){ +double HPWH::getTankRadius(UNITS units /*=UNITS_FT*/) { // returns tank radius, ft for use in calculation of heat loss in the bottom and top of the tank. // Based off 88 insulated storage tanks currently available on the market from Sanden, AOSmith, HTP, Rheem, and Niles. double value = 0.2244 * pow(L_TO_GAL(tankVolume_L), 0.333) + 0.0749; @@ -825,7 +818,6 @@ int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/) { return HPWH_ABORT; } } - calcSizeConstants(); return 0; } int HPWH::setDoInversionMixing(bool doInvMix) { @@ -859,7 +851,7 @@ int HPWH::getUA(double& UA, UNITS units /*=UNITS_kJperHrC*/) const { // UA is already in correct units } else if (units == UNITS_BTUperHrF) { - UA = UA / UAf_TO_UAc( 1.); + UA = UA / UAf_TO_UAc(1.); } else { if (hpwhVerbosity >= VRB_reluctant) { @@ -871,15 +863,15 @@ int HPWH::getUA(double& UA, UNITS units /*=UNITS_kJperHrC*/) const { return 0; } -int HPWH::setInletByFraction(double fractionalHeight){ +int HPWH::setInletByFraction(double fractionalHeight) { return setNodeNumFromFractionalHeight(fractionalHeight, inletHeight); } -int HPWH::setInlet2ByFraction(double fractionalHeight){ +int HPWH::setInlet2ByFraction(double fractionalHeight) { return setNodeNumFromFractionalHeight(fractionalHeight, inlet2Height); } -int HPWH::setNodeNumFromFractionalHeight(double fractionalHeight, int &inletNum){ - if (fractionalHeight > 1. || fractionalHeight < 0.){ +int HPWH::setNodeNumFromFractionalHeight(double fractionalHeight, int &inletNum) { + if (fractionalHeight > 1. || fractionalHeight < 0.) { if (hpwhVerbosity >= VRB_reluctant) { msg("Out of bounds fraction for setInletByFraction \n"); } @@ -891,7 +883,7 @@ int HPWH::setNodeNumFromFractionalHeight(double fractionalHeight, int &inletNum) return 0; } -int HPWH::getInletHeight(int whichInlet){ +int HPWH::getInletHeight(int whichInlet) { if (whichInlet == 1) { return inletHeight; } @@ -908,33 +900,33 @@ int HPWH::getInletHeight(int whichInlet){ } int HPWH::setMaxTempDepression(double maxDepression, UNITS units /*=UNITS_C*/) { - if(units == UNITS_C) { - this->maxDepression_C = maxDepression; - } - else if(units == UNITS_F) { - this->maxDepression_C = F_TO_C(maxDepression); - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for max Temp Depression. \n"); - } - return HPWH_ABORT; - } - return 0; + if (units == UNITS_C) { + this->maxDepression_C = maxDepression; + } + else if (units == UNITS_F) { + this->maxDepression_C = F_TO_C(maxDepression); + } + else { + if (hpwhVerbosity >= VRB_reluctant) { + msg("Incorrect unit specification for max Temp Depression. \n"); + } + return HPWH_ABORT; + } + return 0; } HPWH::HeatingLogic HPWH::topThird(double d) const { std::vector nodeWeights; - for (auto i : {9,10,11,12}) { - nodeWeights.emplace_back(i); + for (auto i : { 9,10,11,12 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("top third", nodeWeights, d); } HPWH::HeatingLogic HPWH::topThird_absolute(double d) const { std::vector nodeWeights; - for (auto i : {9,10,11,12}) { - nodeWeights.emplace_back(i); + for (auto i : { 9,10,11,12 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("top third absolute", nodeWeights, d, true); } @@ -942,72 +934,72 @@ HPWH::HeatingLogic HPWH::topThird_absolute(double d) const { HPWH::HeatingLogic HPWH::bottomThird(double d) const { std::vector nodeWeights; - for (auto i : {1,2,3,4}) { - nodeWeights.emplace_back(i); + for (auto i : { 1,2,3,4 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("bottom third", nodeWeights, d); } HPWH::HeatingLogic HPWH::bottomSixth(double d) const { std::vector nodeWeights; - for (auto i : {1,2}) { - nodeWeights.emplace_back(i); + for (auto i : { 1,2 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("bottom sixth", nodeWeights, d); } HPWH::HeatingLogic HPWH::secondSixth(double d) const { std::vector nodeWeights; - for (auto i : {3,4}) { - nodeWeights.emplace_back(i); + for (auto i : { 3,4 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("second sixth", nodeWeights, d); } HPWH::HeatingLogic HPWH::thirdSixth(double d) const { std::vector nodeWeights; - for (auto i : {5,6}) { - nodeWeights.emplace_back(i); + for (auto i : { 5,6 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("third sixth", nodeWeights, d); } HPWH::HeatingLogic HPWH::fourthSixth(double d) const { std::vector nodeWeights; - for (auto i : {7,8}) { - nodeWeights.emplace_back(i); + for (auto i : { 7,8 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("fourth sixth", nodeWeights, d); } HPWH::HeatingLogic HPWH::fifthSixth(double d) const { std::vector nodeWeights; - for (auto i : {9,10}) { - nodeWeights.emplace_back(i); + for (auto i : { 9,10 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("fifth sixth", nodeWeights, d); } HPWH::HeatingLogic HPWH::topSixth(double d) const { std::vector nodeWeights; - for (auto i : {11,12}) { - nodeWeights.emplace_back(i); + for (auto i : { 11,12 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("top sixth", nodeWeights, d); } HPWH::HeatingLogic HPWH::bottomHalf(double d) const { std::vector nodeWeights; - for (auto i : {1,2,3,4,5,6}) { - nodeWeights.emplace_back(i); + for (auto i : { 1,2,3,4,5,6 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("bottom half", nodeWeights, d); } HPWH::HeatingLogic HPWH::bottomTwelth(double d) const { std::vector nodeWeights; - for (auto i : {7,8,9,10,11,12}) { - nodeWeights.emplace_back(i); + for (auto i : { 7,8,9,10,11,12 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("bottom twelth", nodeWeights, d); } @@ -1038,16 +1030,16 @@ HPWH::HeatingLogic HPWH::bottomTwelthMaxTemp(double d) const { HPWH::HeatingLogic HPWH::largeDraw(double d) const { std::vector nodeWeights; - for (auto i : {1,2,3,4}) { - nodeWeights.emplace_back(i); + for (auto i : { 1,2,3,4 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("large draw", nodeWeights, d, true); } HPWH::HeatingLogic HPWH::largerDraw(double d) const { std::vector nodeWeights; - for (auto i : {1,2,3,4,5,6}) { - nodeWeights.emplace_back(i); + for (auto i : { 1,2,3,4,5,6 }) { + nodeWeights.emplace_back(i); } return HPWH::HeatingLogic("larger draw", nodeWeights, d, true); } @@ -1064,7 +1056,7 @@ double HPWH::getTankNodeTemp(int nodeNum, UNITS units /*=UNITS_C*/) const { } return double(HPWH_ABORT); } - else{ + else { double result = tankTemps_C[nodeNum]; if (result == double(HPWH_ABORT)) { return result; @@ -1207,23 +1199,23 @@ double HPWH::getNthHeatSourceRunTime(int N) const { } -int HPWH::isNthHeatSourceRunning(int N) const{ +int HPWH::isNthHeatSourceRunning(int N) const { if (N >= numHeatSources || N < 0) { if (hpwhVerbosity >= VRB_reluctant) { msg("You have attempted to access the status of a heat source that does not exist. \n"); } return HPWH_ABORT; } - if (setOfSources[N].isEngaged()){ + if (setOfSources[N].isEngaged()) { return 1; } - else{ + else { return 0; } } -HPWH::HEATSOURCE_TYPE HPWH::getNthHeatSourceType(int N) const{ +HPWH::HEATSOURCE_TYPE HPWH::getNthHeatSourceType(int N) const { return setOfSources[N].typeOfHeatSource; } @@ -1268,7 +1260,7 @@ double HPWH::getEnergyRemovedFromEnvironment(UNITS units /*=UNITS_KWH*/) const { else if (units == UNITS_BTU) { return KWH_TO_BTU(energyRemovedFromEnvironment_kWh); } - else if (units == UNITS_KJ){ + else if (units == UNITS_KJ) { return KWH_TO_KJ(energyRemovedFromEnvironment_kWh); } else { @@ -1287,7 +1279,7 @@ double HPWH::getStandbyLosses(UNITS units /*=UNITS_KWH*/) const { else if (units == UNITS_BTU) { return KWH_TO_BTU(standbyLosses_kWh); } - else if (units == UNITS_KJ){ + else if (units == UNITS_KJ) { return KWH_TO_KJ(standbyLosses_kWh); } else { @@ -1313,7 +1305,7 @@ double HPWH::getTankHeatContent_kJ() const { } double HPWH::getLocationTemp_C() const { - return locationTemperature_C; + return locationTemperature_C; } @@ -1323,12 +1315,13 @@ double HPWH::getLocationTemp_C() const { void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbientT_C, double minutesPerStep, double inletVol2_L, double inletT2_C) { //set up some useful variables for calculations + double volPerNode_LperNode = tankVolume_L / numNodes; double drawFraction; this->outletTemp_C = 0; double nodeInletFraction, cumInletFraction, drawVolume_N, nodeInletTV; - if (drawVolume_L > 0){ - + if (drawVolume_L > 0) { + //calculate how many nodes to draw (wholeNodesToDraw), and the remainder (drawFraction) if (inletVol2_L > drawVolume_L) { if (hpwhVerbosity >= VRB_reluctant) { @@ -1343,7 +1336,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi double highInletV, highInletT; int lowInletH; double lowInletT, lowInletV; - if (inletHeight > inlet2Height){ + if (inletHeight > inlet2Height) { highInletH = inletHeight; highInletV = drawVolume_L - inletVol2_L; highInletT = inletT_C; @@ -1389,12 +1382,12 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi // Sum of all inlets Vi*Ti at this node if (i == highInletH) { - nodeInletTV += highInletV * drawFraction / drawVolume_L * highInletT; - nodeInletFraction += highInletV * drawFraction / drawVolume_L; + nodeInletTV += highInletV * drawFraction / drawVolume_L * highInletT; + nodeInletFraction += highInletV * drawFraction / drawVolume_L; } if (i == lowInletH) { - nodeInletTV += lowInletV * drawFraction / drawVolume_L * lowInletT; - nodeInletFraction += lowInletV * drawFraction / drawVolume_L; + nodeInletTV += lowInletV * drawFraction / drawVolume_L * lowInletT; + nodeInletFraction += lowInletV * drawFraction / drawVolume_L; break; // if this is the bottom inlet break out of the four loop and use the boundary condition equation. } @@ -1405,7 +1398,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi (drawFraction - (cumInletFraction + nodeInletFraction)) * tankTemps_C[i - 1]; cumInletFraction += nodeInletFraction; - + } // Boundary condition equation because it shouldn't take anything from tankTemps_C[i - 1] but it also might not exist. @@ -1446,6 +1439,16 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. // height estimate from Rheem along with the volume is used to get the radius and node_height + const double rad = getTankRadius(UNITS_M); + const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); + const double node_height = height / numNodes; + + // The fraction of UA that is on the top or the bottom of the tank. So 2 * fracAreaTop + fracAreaSide is the total tank area. + const double fracAreaTop = rad / (2.0 * (height + rad)); + + // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. + const double fracAreaSide = height / (height + rad); + if (doConduction) { // Get the "constant" tau for the stability condition and the conduction calculation @@ -1455,7 +1458,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } // Boundary condition for the finite difference. - const double bc = 2.0 * tau * tankUA_kJperHrC * fracAreaTop * node_height / KWATER_WpermC; + const double bc = 2.0 * tau * tankUA_kJperHrC * fracAreaTop * node_height / KWATER_WpermC; // Boundary nodes for finite difference nextTankTemps_C[0] = (1.0 - 2.0 * tau - bc) * tankTemps_C[0] + 2.0 * tau * tankTemps_C[1] + bc * tankAmbientT_C; @@ -1473,7 +1476,7 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); } else { // Ignore tank conduction and calculate UA losses from top and bottom. UA loss from the sides are found at the bottom of the function - + for (int i = 0; i < numNodes; i++) { nextTankTemps_C[i] = tankTemps_C[i]; } @@ -1496,15 +1499,15 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi //calculate standby losses from the sides of the tank for (int i = 0; i < numNodes; i++) { - //faction of tank area on the sides - //kJ's lost as standby in the current time step for each node. - double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaSide / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); - standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); + //faction of tank area on the sides + //kJ's lost as standby in the current time step for each node. + double standbyLosses_kJ = (tankUA_kJperHrC * fracAreaSide / numNodes * (tankTemps_C[i] - tankAmbientT_C) * (minutesPerStep / 60.0)); + standbyLosses_kWh += KJ_TO_KWH(standbyLosses_kJ); - //The effect of standby loss on temperature in each node - nextTankTemps_C[i] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); + //The effect of standby loss on temperature in each node + nextTankTemps_C[i] -= standbyLosses_kJ / ((volPerNode_LperNode * DENSITYWATER_kgperL) * CPWATER_kJperkgC); } - + // Assign the new temporary tank temps to the real tank temps. for (int i = 0; i < numNodes; i++) tankTemps_C[i] = nextTankTemps_C[i]; @@ -1526,11 +1529,11 @@ void HPWH::mixTankInversions() { do { hasInversion = false; //Start from the top and check downwards - for (int i = numNodes-1; i >= 0; i--) { + for (int i = numNodes - 1; i >= 0; i--) { if (tankTemps_C[i] < tankTemps_C[i - 1]) { // Temperature inversion! hasInversion = true; - + //Mix this inversion mixing temperature by averaging all of the inverted nodes together together. double Tmixed = 0.0; double massMixed = 0.0; @@ -1547,10 +1550,10 @@ void HPWH::mixTankInversions() { // Assign the tank temps from i to k for (int k = i; k >= m; k--) tankTemps_C[k] = Tmixed; } - + } - - } while ( hasInversion ); + + } while (hasInversion); } /////////////////////////////////////////////////////////////////////////////////// @@ -1606,7 +1609,7 @@ HPWH::HeatSource::HeatSource(HPWH *parentInput) followedByHeatSource(NULL), minT(-273.15), maxT(100), hysteresis_dC(0), airflowFreedom(1.0), typeOfHeatSource(TYPE_none) {} -HPWH::HeatSource::HeatSource(const HeatSource &hSource){ +HPWH::HeatSource::HeatSource(const HeatSource &hSource) { hpwh = hSource.hpwh; isOn = hSource.isOn; lockedOut = hSource.lockedOut; @@ -1650,7 +1653,7 @@ HPWH::HeatSource::HeatSource(const HeatSource &hSource){ } -HPWH::HeatSource& HPWH::HeatSource::operator=(const HeatSource &hSource){ +HPWH::HeatSource& HPWH::HeatSource::operator=(const HeatSource &hSource) { if (this == &hSource) { return *this; } @@ -1865,7 +1868,8 @@ bool HPWH::HeatSource::shouldHeat() const { if (turnOnLogicSet[i].isAbsolute) { comparison = turnOnLogicSet[i].decisionPoint; - } else { + } + else { comparison = hpwh->setpoint_C - turnOnLogicSet[i].decisionPoint; } @@ -1876,7 +1880,7 @@ bool HPWH::HeatSource::shouldHeat() const { if (hpwh->hpwhVerbosity >= VRB_typical) { hpwh->msg("engages!\n"); } - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("average: %.2lf \t setpoint: %.2lf \t decisionPoint: %.2lf \t comparison: %2.1f\n", average, hpwh->setpoint_C, turnOnLogicSet[i].decisionPoint, comparison); } } @@ -1884,7 +1888,7 @@ bool HPWH::HeatSource::shouldHeat() const { //quit searching the logics if one of them turns it on if (shouldEngage) break; - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("returns: %d \t", shouldEngage); } } //end loop over set of logic conditions @@ -1909,14 +1913,14 @@ bool HPWH::HeatSource::shutsOff() const { if (hpwh->tankTemps_C[0] >= hpwh->setpoint_C) { shutOff = true; - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("shutsOff bottom node hot: %.2d C \n returns true", hpwh->tankTemps_C[0]); } return shutOff; } for (int i = 0; i < (int)shutOffLogicSet.size(); i++) { - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("\tshutsOff logic: %s ", shutOffLogicSet[i].description.c_str()); } @@ -1925,7 +1929,8 @@ bool HPWH::HeatSource::shutsOff() const { if (shutOffLogicSet[i].isAbsolute) { comparison = shutOffLogicSet[i].decisionPoint; - } else { + } + else { comparison = hpwh->setpoint_C - shutOffLogicSet[i].decisionPoint; } @@ -1939,7 +1944,7 @@ bool HPWH::HeatSource::shutsOff() const { } } - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("returns: %d \n", shutOff); } return shutOff; @@ -1953,7 +1958,7 @@ void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { // passing it on works leftoverCap_kJ = 0.0; - switch (configuration){ + switch (configuration) { case CONFIG_SUBMERGED: case CONFIG_WRAPPED: { @@ -1968,19 +1973,19 @@ void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { getCapacity(externalT_C, getCondenserTemp(), input_BTUperHr, cap_BTUperHr, cop); //some outputs for debugging - if (hpwh->hpwhVerbosity >= VRB_typical){ + if (hpwh->hpwhVerbosity >= VRB_typical) { hpwh->msg("capacity_kWh %.2lf \t\t cap_BTUperHr %.2lf \n", BTU_TO_KWH(cap_BTUperHr)*(minutesToRun) / 60.0, cap_BTUperHr); } - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("heatDistribution: %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf %4.3lf \n", heatDistribution[0], heatDistribution[1], heatDistribution[2], heatDistribution[3], heatDistribution[4], heatDistribution[5], heatDistribution[6], heatDistribution[7], heatDistribution[8], heatDistribution[9], heatDistribution[10], heatDistribution[11]); } //the loop over nodes here is intentional - essentially each node that has //some amount of heatDistribution acts as a separate resistive element //maybe start from the top and go down? test this with graphs - for (int i = hpwh->numNodes - 1; i >= 0; i--){ + for (int i = hpwh->numNodes - 1; i >= 0; i--) { //for(int i = 0; i < hpwh->numNodes; i++){ captmp_kJ = BTU_TO_KJ(cap_BTUperHr * minutesToRun / 60.0 * heatDistribution[i]); - if (captmp_kJ != 0){ + if (captmp_kJ != 0) { //add leftoverCap to the next run, and keep passing it on leftoverCap_kJ = addHeatAboveNode(captmp_kJ + leftoverCap_kJ, i, minutesToRun); } @@ -2013,7 +2018,7 @@ void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) { //the private functions void HPWH::HeatSource::sortPerformanceMap() { std::sort(perfMap.begin(), perfMap.end(), - [](const HPWH::HeatSource::perfPoint & a, const HPWH::HeatSource::perfPoint & b) -> bool { + [](const HPWH::HeatSource::perfPoint & a, const HPWH::HeatSource::perfPoint & b) -> bool { return a.T_F < b.T_F; }); } @@ -2058,12 +2063,12 @@ double HPWH::HeatSource::getCondenserTemp() { condenserTemp_C += (condensity[j] / tempNodesPerCondensityNode) * hpwh->tankTemps_C[i]; //the weights don't need to be added to divide out later because they should always sum to 1 - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("condenserTemp_C:\t %.2lf \ti:\t %d \tj\t %d \tcondensity[j]:\t %.2lf \ttankTemps_C[i]:\t %.2lf\n", condenserTemp_C, i, j, condensity[j], hpwh->tankTemps_C[i]); } } } - if (hpwh->hpwhVerbosity >= VRB_typical){ + if (hpwh->hpwhVerbosity >= VRB_typical) { hpwh->msg("condenser temp %.2lf \n", condenserTemp_C); } return condenserTemp_C; @@ -2084,17 +2089,19 @@ void HPWH::HeatSource::getCapacity(double externalT_C, double condenserTemp_C, d size_t i_prev = 0; size_t i_next = 1; for (size_t i = 0; i < perfMap.size(); ++i) { - if (externalT_F < perfMap[i].T_F){ + if (externalT_F < perfMap[i].T_F) { if (i == 0) { extrapolate = true; i_prev = 0; i_next = 1; - } else { + } + else { i_prev = i - 1; i_next = i; } break; - } else { + } + else { if (i == perfMap.size() - 1) { extrapolate = true; i_prev = i - 1; @@ -2122,7 +2129,7 @@ void HPWH::HeatSource::getCapacity(double externalT_C, double condenserTemp_C, d inputPower_T2_Watts += perfMap[i_next].inputPower_coeffs[1] * condenserTemp_F; inputPower_T2_Watts += perfMap[i_next].inputPower_coeffs[2] * condenserTemp_F * condenserTemp_F; - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("inputPower_T1_constant_W linear_WperF quadratic_WperF2 \t%.2lf %.2lf %.2lf \n", perfMap[0].inputPower_coeffs[0], perfMap[0].inputPower_coeffs[1], perfMap[0].inputPower_coeffs[2]); hpwh->msg("inputPower_T2_constant_W linear_WperF quadratic_WperF2 \t%.2lf %.2lf %.2lf \n", perfMap[1].inputPower_coeffs[0], perfMap[1].inputPower_coeffs[1], perfMap[1].inputPower_coeffs[2]); hpwh->msg("inputPower_T1_Watts: %.2lf \tinputPower_T2_Watts: %.2lf \n", inputPower_T1_Watts, inputPower_T2_Watts); @@ -2137,7 +2144,7 @@ void HPWH::HeatSource::getCapacity(double externalT_C, double condenserTemp_C, d cop = COP_T1 + (externalT_F - perfMap[i_prev].T_F) * ((COP_T2 - COP_T1) / (perfMap[i_next].T_F - perfMap[i_prev].T_F)); input_BTUperHr = KWH_TO_BTU((inputPower_T1_Watts + (externalT_F - perfMap[i_prev].T_F) * ((inputPower_T2_Watts - inputPower_T1_Watts) - / (perfMap[i_next].T_F - perfMap[i_prev].T_F)) + / (perfMap[i_next].T_F - perfMap[i_prev].T_F)) ) / 1000.0); //1000 converts w to kw cap_BTUperHr = cop * input_BTUperHr; @@ -2146,11 +2153,11 @@ void HPWH::HeatSource::getCapacity(double externalT_C, double condenserTemp_C, d //that is based on the flow rate. The equation is a fit to three points, //measured experimentally - 12 percent reduction at 150 cfm, 10 percent at //200, and 0 at 375. Flow is expressed as fraction of full flow. - if (airflowFreedom != 1){ + if (airflowFreedom != 1) { double airflow = 375 * airflowFreedom; cop *= 0.00056*airflow + 0.79; } - if (hpwh->hpwhVerbosity >= VRB_typical){ + if (hpwh->hpwhVerbosity >= VRB_typical) { hpwh->msg("cop: %.2lf \tinput_BTUperHr: %.2lf \tcap_BTUperHr: %.2lf \n", cop, input_BTUperHr, cap_BTUperHr); } } @@ -2194,18 +2201,18 @@ double HPWH::HeatSource::addHeatAboveNode(double cap_kJ, int node, double minute double volumePerNode_L = hpwh->tankVolume_L / hpwh->numNodes; - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("node %2d cap_kwh %.4lf \n", node, KJ_TO_KWH(cap_kJ)); } // find the first node (from the bottom) that does not have the same temperature as the one above it // if they all have the same temp., use the top node, hpwh->numNodes-1 setPointNodeNum = node; - for (int i = node; i < hpwh->numNodes - 1; i++){ + for (int i = node; i < hpwh->numNodes - 1; i++) { if (hpwh->tankTemps_C[i] != hpwh->tankTemps_C[i + 1]) { break; } - else{ + else { setPointNodeNum = i + 1; } } @@ -2227,7 +2234,7 @@ double HPWH::HeatSource::addHeatAboveNode(double cap_kJ, int node, double minute Q_kJ = CPWATER_kJperkgC * volumePerNode_L * DENSITYWATER_kgperL * (setPointNodeNum + 1 - node) * deltaT_C; //Running the rest of the time won't recover - if (Q_kJ > cap_kJ){ + if (Q_kJ > cap_kJ) { for (int j = node; j <= setPointNodeNum; j++) { hpwh->tankTemps_C[j] += cap_kJ / CPWATER_kJperkgC / volumePerNode_L / DENSITYWATER_kgperL / (setPointNodeNum + 1 - node); } @@ -2243,8 +2250,8 @@ double HPWH::HeatSource::addHeatAboveNode(double cap_kJ, int node, double minute setPointNodeNum++; #else //temp will recover by/before end of timestep - else{ - for (int j = node; j <= setPointNodeNum; j++){ + else { + for (int j = node; j <= setPointNodeNum; j++) { hpwh->tankTemps_C[j] = targetTemp_C; } setPointNodeNum++; @@ -2268,21 +2275,21 @@ double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun cap_BTUperHr = 0; cop = 0; - do{ - if (hpwh->hpwhVerbosity >= VRB_emetic){ + do { + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("bottom tank temp: %.2lf \n", hpwh->tankTemps_C[0]); } //how much heat is available this timestep getCapacity(externalT_C, hpwh->tankTemps_C[0], inputTemp_BTUperHr, capTemp_BTUperHr, copTemp); heatingCapacity_kJ = BTU_TO_KJ(capTemp_BTUperHr * (minutesToRun / 60.0)); - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("\theatingCapacity_kJ stepwise: %.2lf \n", heatingCapacity_kJ); } //adjust capacity for how much time is left in this step heatingCapacity_kJ = heatingCapacity_kJ * (timeRemaining_min / minutesToRun); - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("\theatingCapacity_kJ remaining this node: %.2lf \n", heatingCapacity_kJ); } @@ -2292,26 +2299,26 @@ double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun nodeHeat_kJperNode = volumePerNode_LperNode * DENSITYWATER_kgperL * CPWATER_kJperkgC * deltaT_C; //protect against dividing by zero - if bottom node is at (or above) setpoint, //add no heat - if (nodeHeat_kJperNode <= 0) { - nodeFrac = 0; + if (nodeHeat_kJperNode <= 0) { + nodeFrac = 0; } - else { - nodeFrac = heatingCapacity_kJ / nodeHeat_kJperNode; + else { + nodeFrac = heatingCapacity_kJ / nodeHeat_kJperNode; } - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("nodeHeat_kJperNode: %.2lf nodeFrac: %.2lf \n\n", nodeHeat_kJperNode, nodeFrac); } //if more than one, round down to 1 and subtract the amount of time it would //take to heat that node from the timeRemaining - if (nodeFrac > 1){ + if (nodeFrac > 1) { nodeFrac = 1; timeUsed_min = (nodeHeat_kJperNode / heatingCapacity_kJ)*timeRemaining_min; timeRemaining_min -= timeUsed_min; } //otherwise just the fraction available //this should make heatingCapacity == 0 if nodeFrac < 1 - else{ + else { timeUsed_min = timeRemaining_min; timeRemaining_min = 0; } @@ -2325,9 +2332,9 @@ double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun //track outputs - weight by the time ran - input_BTUperHr += inputTemp_BTUperHr*timeUsed_min; - cap_BTUperHr += capTemp_BTUperHr*timeUsed_min; - cop += copTemp*timeUsed_min; + input_BTUperHr += inputTemp_BTUperHr * timeUsed_min; + cap_BTUperHr += capTemp_BTUperHr * timeUsed_min; + cop += copTemp * timeUsed_min; //if there's still time remaining and you haven't heated to the cutoff @@ -2339,7 +2346,7 @@ double HPWH::HeatSource::addHeatExternal(double externalT_C, double minutesToRun cap_BTUperHr /= (minutesToRun - timeRemaining_min); cop /= (minutesToRun - timeRemaining_min); - if (hpwh->hpwhVerbosity >= VRB_emetic){ + if (hpwh->hpwhVerbosity >= VRB_emetic) { hpwh->msg("final remaining time: %.2lf \n", timeRemaining_min); } //return the time left @@ -2367,13 +2374,13 @@ void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { 50, // Temperature (T_F) {Watts, 0.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) {1.0, 0.0, 0.0} // COP Coefficients (COP_coeffs) - }); + }); perfMap.push_back({ 67, // Temperature (T_F) {Watts, 0.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) {1.0, 0.0, 0.0} // COP Coefficients (COP_coeffs) - }); + }); configuration = CONFIG_SUBMERGED; //immersed in tank @@ -2381,35 +2388,14 @@ void HPWH::HeatSource::setupAsResistiveElement(int node, double Watts) { } -void HPWH::HeatSource::addTurnOnLogic(HeatingLogic logic){ +void HPWH::HeatSource::addTurnOnLogic(HeatingLogic logic) { this->turnOnLogicSet.push_back(logic); } void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { this->shutOffLogicSet.push_back(logic); } -void HPWH::calcSizeConstants() { - // gets called when initializing and reseting tank sizes or UA - - volPerNode_LperNode = tankVolume_L / numNodes; - - - const double tank_rad = getTankRadius(UNITS_M); - const double tank_height = tankVolume_L / (1000.0 * 3.14159 * tank_rad * tank_rad); - - node_height = tank_height / numNodes; - - // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions - // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. - // We assume the UA is divided only by the surface area of the tank and ignore fittings, etc. - // The fraction of UA that is on the top or the bottom of the tank, such that 2 * fracAreaTop + fracAreaSide is the total tank area. - fracAreaTop = tank_rad / (2.0 * (tank_height + tank_rad)); - - // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. - fracAreaSide = tank_height / (tank_height + tank_rad); -} - -void HPWH::calcDerivedValues(){ +void HPWH::calcDerivedValues() { static char outputString[MAXOUTSTRING]; //this is used for debugging outputs // tank node density (number of calculation nodes per regular node) @@ -2470,7 +2456,8 @@ void HPWH::calcDerivedValues(){ for (int i = 0; i < numHeatSources; i++) { if (setOfSources[i].typeOfHeatSource == HPWH::TYPE_compressor) { compressorIndex = i; // NOTE: Maybe won't work with multiple compressors (last compressor will be used) - } else { + } + else { for (int j = 0; j < CONDENSITY_SIZE; j++) { if (setOfSources[i].condensity[j] > 0.0 && j < lowestElementPos) { lowestElementIndex = i; @@ -2498,17 +2485,16 @@ void HPWH::calcDerivedValues(){ } } - calcSizeConstants(); -} +} // Used to check a few inputs after the initialization of a tank model from a preset or a file. -int HPWH::checkInputs(){ +int HPWH::checkInputs() { int returnVal = 0; //use a returnVal so that all checks are processed and error messages written - if (numHeatSources <= 0 && hpwhModel != MODELS_StorageTank ) { + if (numHeatSources <= 0 && hpwhModel != MODELS_StorageTank) { msg("You must have at least one HeatSource.\n"); returnVal = HPWH_ABORT; } @@ -2565,7 +2551,7 @@ int HPWH::checkInputs(){ } #ifndef HPWH_ABRIDGED -int HPWH::HPWHinit_file(string configFile){ +int HPWH::HPWHinit_file(string configFile) { simHasFailed = true; //this gets cleared on successful completion of init //clear out old stuff if you're re-initializing @@ -2593,7 +2579,7 @@ int HPWH::HPWHinit_file(string configFile){ string line_s; std::stringstream line_ss; string token; - while (std::getline(inputFILE, line_s)){ + while (std::getline(inputFILE, line_s)) { line_ss.clear(); line_ss.str(line_s); @@ -2787,7 +2773,8 @@ int HPWH::HPWHinit_file(string configFile){ weights.push_back(std::stod(nextToken)); line_ss >> nextToken; } - } else { + } + else { for (auto n : nodeNums) { n += 0; // used to get rid of unused variable compiler warning weights.push_back(1.0); @@ -2808,7 +2795,7 @@ int HPWH::HPWHinit_file(string configFile){ bool absolute = (nextToken == "absolute"); std::string compareStr; line_ss >> compareStr >> tempDouble >> units; - std::function compare; + std::function compare; if (compareStr == "<") compare = std::less(); else if (compareStr == ">") compare = std::greater(); else { @@ -2833,13 +2820,14 @@ int HPWH::HPWHinit_file(string configFile){ return HPWH_ABORT; } std::vector nodeWeights; - for (size_t i = 0; i < nodeNums.size(); i++ ) { - nodeWeights.emplace_back(nodeNums[i],weights[i]); + for (size_t i = 0; i < nodeNums.size(); i++) { + nodeWeights.emplace_back(nodeNums[i], weights[i]); } HPWH::HeatingLogic logic("custom", nodeWeights, tempDouble, absolute, compare); if (token == "onlogic") { setOfSources[heatsource].addTurnOnLogic(logic); - } else { // "offlogic" + } + else { // "offlogic" setOfSources[heatsource].addShutOffLogic(logic); } } @@ -2961,7 +2949,7 @@ int HPWH::HPWHinit_file(string configFile){ line_ss >> nTemps; setOfSources[heatsource].perfMap.resize(nTemps); } - else if (std::regex_match(token, std::regex("T\\d+"))){ + else if (std::regex_match(token, std::regex("T\\d+"))) { std::smatch match; std::regex_match(token, match, std::regex("T(\\d+)")); nTemps = std::stoi(match[1].str()); @@ -2969,13 +2957,13 @@ int HPWH::HPWHinit_file(string configFile){ if (maxTemps < nTemps) { if (maxTemps == 0) { - if (true || hpwhVerbosity >= VRB_reluctant){ + if (true || hpwhVerbosity >= VRB_reluctant) { msg("%s specified for heatsource %d before definition of nTemps. \n", token.c_str(), heatsource); } return HPWH_ABORT; } else { - if (true || hpwhVerbosity >= VRB_reluctant){ + if (true || hpwhVerbosity >= VRB_reluctant) { msg("Incorrect specification for %s from heatsource %d. nTemps, %d, is less than %d. \n", token.c_str(), heatsource, maxTemps, nTemps); } return HPWH_ABORT; @@ -2994,20 +2982,20 @@ int HPWH::HPWHinit_file(string configFile){ } setOfSources[heatsource].perfMap[nTemps - 1].T_F = tempDouble; } - else if (std::regex_match(token, std::regex("(?:inPow|cop)T\\d+(?:const|lin|quad)"))){ + else if (std::regex_match(token, std::regex("(?:inPow|cop)T\\d+(?:const|lin|quad)"))) { std::smatch match; std::regex_match(token, match, std::regex("(inPow|cop)T(\\d+)(const|lin|quad)")); string var = match[1].str(); nTemps = std::stoi(match[2].str()); string coeff = match[3].str(); int coeff_num; - if (coeff == "const"){ + if (coeff == "const") { coeff_num = 0; } - else if (coeff == "lin"){ + else if (coeff == "lin") { coeff_num = 1; } - else if (coeff == "quad"){ + else if (coeff == "quad") { coeff_num = 2; } @@ -3015,13 +3003,13 @@ int HPWH::HPWHinit_file(string configFile){ if (maxTemps < nTemps) { if (maxTemps == 0) { - if (hpwhVerbosity >= VRB_reluctant){ + if (hpwhVerbosity >= VRB_reluctant) { msg("%s specified for heatsource %d before definition of nTemps. \n", token.c_str(), heatsource); } return HPWH_ABORT; } else { - if (hpwhVerbosity >= VRB_reluctant){ + if (hpwhVerbosity >= VRB_reluctant) { msg("Incorrect specification for %s from heatsource %d. nTemps, %d, is less than %d. \n", token.c_str(), heatsource, maxTemps, nTemps); } return HPWH_ABORT; @@ -3036,7 +3024,7 @@ int HPWH::HPWHinit_file(string configFile){ setOfSources[heatsource].perfMap[nTemps - 1].COP_coeffs[coeff_num] = tempDouble; } } - else if (token == "hysteresis"){ + else if (token == "hysteresis") { line_ss >> tempDouble >> units; if (units == "F") tempDouble = dF_TO_dC(tempDouble); else if (units == "C"); //do nothing, lol @@ -3048,15 +3036,15 @@ int HPWH::HPWHinit_file(string configFile){ } setOfSources[heatsource].hysteresis_dC = tempDouble; } - else if (token == "backupSource"){ + else if (token == "backupSource") { line_ss >> sourceNum; setOfSources[heatsource].backupHeatSource = &setOfSources[sourceNum]; } - else if (token == "companionSource"){ + else if (token == "companionSource") { line_ss >> sourceNum; setOfSources[heatsource].companionHeatSource = &setOfSources[sourceNum]; } - else if (token == "followedBySource"){ + else if (token == "followedBySource") { line_ss >> sourceNum; setOfSources[heatsource].followedByHeatSource = &setOfSources[sourceNum]; } @@ -3101,11 +3089,11 @@ int HPWH::HPWHinit_file(string configFile){ } #endif -int HPWH::HPWHinit_resTank(){ +int HPWH::HPWHinit_resTank() { //a default resistance tank, nominal 50 gallons, 0.95 EF, standard double 4.5 kW elements return this->HPWHinit_resTank(GAL_TO_L(47.5), 0.95, 4500, 4500); } -int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPower_W, double lowerPower_W){ +int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPower_W, double lowerPower_W) { //low power element will cause divide by zero/negative UA in EF -> UA conversion if (lowerPower_W < 550) { if (hpwhVerbosity >= VRB_reluctant) { @@ -3165,7 +3153,7 @@ int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPo double numerator = (1.0 / energyFactor) - (1.0 / recoveryEfficiency); double temp = 1.0 / (recoveryEfficiency * lowerPower_W*3.41443); double denominator = 67.5 * ((24.0 / 41094.0) - temp); - tankUA_kJperHrC = UAf_TO_UAc(numerator / denominator); + tankUA_kJperHrC = UAf_TO_UAc(numerator / denominator); if (tankUA_kJperHrC < 0.) { if (hpwhVerbosity >= VRB_reluctant && tankUA_kJperHrC < -0.1) { @@ -3190,7 +3178,7 @@ int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPo } - if (hpwhVerbosity >= VRB_emetic){ + if (hpwhVerbosity >= VRB_emetic) { for (int i = 0; i < numHeatSources; i++) { msg("heat source %d: %p \n", i, &setOfSources[i]); } @@ -3201,7 +3189,7 @@ int HPWH::HPWHinit_resTank(double tankVol_L, double energyFactor, double upperPo return 0; //successful init returns 0 } -int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C){ +int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C) { //return 0 on success, HPWH_ABORT for failure simHasFailed = true; //this gets cleared on successful completion of init @@ -3250,13 +3238,13 @@ int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double res 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(45.); compressor.maxT = F_TO_C(120.); @@ -3318,7 +3306,7 @@ int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double res //force COP to be 70% of GE at UEF 2 and 95% at UEF 3.4 //use a fudge factor to scale cop and input power in tandem to maintain constant capacity double fUEF = (energyFactor - 2.0) / uefSpan; - double genericFudge = (1. - fUEF)*.7 + fUEF*.95; + double genericFudge = (1. - fUEF)*.7 + fUEF * .95; compressor.perfMap[0].COP_coeffs[0] *= genericFudge; compressor.perfMap[0].COP_coeffs[1] *= genericFudge; @@ -3373,7 +3361,7 @@ int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double res setOfSources[i].sortPerformanceMap(); } - if (hpwhVerbosity >= VRB_emetic){ + if (hpwhVerbosity >= VRB_emetic) { for (int i = 0; i < numHeatSources; i++) { msg("heat source %d: %p \n", i, &setOfSources[i]); } @@ -3513,7 +3501,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { setOfSources[0].followedByHeatSource = &setOfSources[1]; } - + else if (presetNum == MODELS_StorageTank) { numNodes = 12; tankTemps_C = new double[numNodes]; @@ -3582,13 +3570,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {0.290 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) {4.49, -0.0187, -0.0000133} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {0.375 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) {5.60, -0.0252, 0.00000254} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = 0; compressor.maxT = F_TO_C(120.); @@ -3647,13 +3635,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {0.290 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) {4.49, -0.0187, -0.0000133} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {0.375 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) {5.60, -0.0252, 0.00000254} // COP Coefficients (COP_coeffs) - }); + }); compressor.maxT = F_TO_C(120.); compressor.hysteresis_dC = 0; //no hysteresis @@ -3706,13 +3694,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {0.467 * 1000, 0.00281 * 1000, 0.0000072 * 1000}, // Input Power Coefficients (inputPower_coeffs) {4.86, -0.0222, -0.00001} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {0.541 * 1000, 0.00147 * 1000, 0.0000176 * 1000}, // Input Power Coefficients (inputPower_coeffs) {6.58, -0.0392, 0.0000407} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(45.0); compressor.maxT = F_TO_C(120.); @@ -3788,13 +3776,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {0.467 * 1000, 0.00281 * 1000, 0.0000072 * 1000}, // Input Power Coefficients (inputPower_coeffs) {4.86, -0.0222, -0.00001} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {0.541 * 1000, 0.00147 * 1000, 0.0000176 * 1000}, // Input Power Coefficients (inputPower_coeffs) {6.58, -0.0392, 0.0000407} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(45.0); compressor.maxT = F_TO_C(120.); @@ -3870,13 +3858,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {0.3 * 1000, 0.00159 * 1000, 0.00000107 * 1000}, // Input Power Coefficients (inputPower_coeffs) {4.7, -0.0210, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {0.378 * 1000, 0.00121 * 1000, 0.00000216 * 1000}, // Input Power Coefficients (inputPower_coeffs) {4.8, -0.0167, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(45.0); compressor.maxT = F_TO_C(120.); @@ -3956,31 +3944,31 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 17, // Temperature (T_F) {1650, 5.5, 0.0}, // Input Power Coefficients (inputPower_coeffs) {3.2, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 35, // Temperature (T_F) {1100, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) {3.7, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 50, // Temperature (T_F) {880, 3.1, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.25, -0.025, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {740, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) {6.2, -0.03, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 95, // Temperature (T_F) {790, 2, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.15, -0.04, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.hysteresis_dC = 4; compressor.configuration = HeatSource::CONFIG_EXTERNAL; @@ -4033,31 +4021,31 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 17, // Temperature (T_F) {1650, 5.5, 0.0}, // Input Power Coefficients (inputPower_coeffs) {3.2, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 35, // Temperature (T_F) {1100, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) {3.7, -0.015, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 50, // Temperature (T_F) {880, 3.1, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.25, -0.025, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {740, 4.0, 0.0}, // Input Power Coefficients (inputPower_coeffs) {6.2, -0.03, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 95, // Temperature (T_F) {790, 2, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.15, -0.04, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.hysteresis_dC = 4; compressor.configuration = HeatSource::CONFIG_EXTERNAL; @@ -4106,26 +4094,26 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { double split = 1.0 / 5.0; compressor.setCondensity(split, split, split, split, split, 0, 0, 0, 0, 0, 0, 0); - // performance map + // performance map compressor.perfMap.reserve(3); compressor.perfMap.push_back({ 50, // Temperature (T_F) {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 95, // Temperature (T_F) {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(42.0); compressor.maxT = F_TO_C(120.); @@ -4135,7 +4123,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //top resistor values if (presetNum == MODELS_RheemHBDR2250) { resistiveElementTop.setupAsResistiveElement(8, 2250); - } else { + } + else { resistiveElementTop.setupAsResistiveElement(8, 4500); } resistiveElementTop.isVIP = true; @@ -4143,7 +4132,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //bottom resistor values if (presetNum == MODELS_RheemHBDR2250) { resistiveElementBottom.setupAsResistiveElement(0, 2250); - } else { + } + else { resistiveElementBottom.setupAsResistiveElement(0, 4500); } resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); @@ -4160,10 +4150,10 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { std::vector nodeWeights; nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); -// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28))); + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28))); - //set everything in its places + //set everything in its places setOfSources[0] = resistiveElementTop; setOfSources[1] = resistiveElementBottom; setOfSources[2] = compressor; @@ -4176,7 +4166,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { setOfSources[0].followedByHeatSource = &setOfSources[1]; setOfSources[1].followedByHeatSource = &setOfSources[2]; - setOfSources[0].companionHeatSource = &setOfSources[2]; + setOfSources[0].companionHeatSource = &setOfSources[2]; } @@ -4190,7 +4180,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { if (presetNum == MODELS_AOSmithHPTU66) { tankVolume_L = 244.6; - } else { + } + else { tankVolume_L = 221.4; } tankUA_kJperHrC = 8; @@ -4213,26 +4204,26 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { double split = 1.0 / 4.0; compressor.setCondensity(split, split, split, split, 0, 0, 0, 0, 0, 0, 0, 0); - // performance map + // performance map compressor.perfMap.reserve(3); compressor.perfMap.push_back({ 50, // Temperature (T_F) {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 95, // Temperature (T_F) {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(42.0); compressor.maxT = F_TO_C(120.); @@ -4242,7 +4233,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //top resistor values if (presetNum == MODELS_RheemHBDR2265) { resistiveElementTop.setupAsResistiveElement(8, 2250); - } else { + } + else { resistiveElementTop.setupAsResistiveElement(8, 4500); } resistiveElementTop.isVIP = true; @@ -4250,7 +4242,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //bottom resistor values if (presetNum == MODELS_RheemHBDR2265) { resistiveElementBottom.setupAsResistiveElement(0, 2250); - } else { + } + else { resistiveElementBottom.setupAsResistiveElement(0, 4500); } resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); @@ -4266,10 +4259,10 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { std::vector nodeWeights; nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); -// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31))); + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31))); - //set everything in its places + //set everything in its places setOfSources[0] = resistiveElementTop; setOfSources[1] = resistiveElementBottom; setOfSources[2] = compressor; @@ -4282,7 +4275,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { setOfSources[0].followedByHeatSource = &setOfSources[1]; setOfSources[1].followedByHeatSource = &setOfSources[2]; - setOfSources[0].companionHeatSource = &setOfSources[2]; + setOfSources[0].companionHeatSource = &setOfSources[2]; } else if (presetNum == MODELS_AOSmithHPTU80 || presetNum == MODELS_RheemHBDR2280 || presetNum == MODELS_RheemHBDR4580) { @@ -4321,19 +4314,19 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {170, 2.02, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.93, -0.027, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {144.5, 2.42, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.67, -0.037, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 95, // Temperature (T_F) {94.1, 3.15, 0.0}, // Input Power Coefficients (inputPower_coeffs) {11.1, -0.056, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(42.0); compressor.maxT = F_TO_C(120.0); @@ -4342,7 +4335,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //top resistor values if (presetNum == MODELS_RheemHBDR2280) { resistiveElementTop.setupAsResistiveElement(8, 2250); - } else { + } + else { resistiveElementTop.setupAsResistiveElement(8, 4500); } resistiveElementTop.isVIP = true; @@ -4350,7 +4344,8 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { //bottom resistor values if (presetNum == MODELS_RheemHBDR2280) { resistiveElementBottom.setupAsResistiveElement(0, 2250); - } else { + } + else { resistiveElementBottom.setupAsResistiveElement(0, 4500); } resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); @@ -4364,13 +4359,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(100))); std::vector nodeWeights; -// nodeWeights.emplace_back(9); nodeWeights.emplace_back(10); + // nodeWeights.emplace_back(9); nodeWeights.emplace_back(10); nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); resistiveElementTop.addTurnOnLogic(HPWH::HeatingLogic("top sixth absolute", nodeWeights, F_TO_C(105), true)); -// resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); - //set everything in its places + //set everything in its places setOfSources[0] = resistiveElementTop; setOfSources[1] = resistiveElementBottom; setOfSources[2] = compressor; @@ -4383,7 +4378,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { setOfSources[0].followedByHeatSource = &setOfSources[1]; setOfSources[1].followedByHeatSource = &setOfSources[2]; - setOfSources[0].companionHeatSource = &setOfSources[2]; + setOfSources[0].companionHeatSource = &setOfSources[2]; } else if (presetNum == MODELS_AOSmithHPTU80_DR) { @@ -4422,13 +4417,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {142.6, 2.152, 0.0}, // Input Power Coefficients (inputPower_coeffs) {6.989258, -0.038320, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {120.14, 2.513, 0.0}, // Input Power Coefficients (inputPower_coeffs) {8.188, -0.0432, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(42.0); compressor.maxT = F_TO_C(120.); @@ -4452,7 +4447,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelthMaxTemp(F_TO_C(80.108))); // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(39.9691))); - resistiveElementTop.addTurnOnLogic(HPWH::topThird_absolute(F_TO_C(87))); + resistiveElementTop.addTurnOnLogic(HPWH::topThird_absolute(F_TO_C(87))); //set everything in its places @@ -4504,13 +4499,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(37.0); compressor.maxT = F_TO_C(120.); @@ -4584,13 +4579,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); //top resistor values resistiveElementTop.setupAsResistiveElement(6, 4500); @@ -4661,13 +4656,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(37.0); compressor.maxT = F_TO_C(120.); @@ -4745,13 +4740,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(37.0); compressor.maxT = F_TO_C(120.); @@ -4829,13 +4824,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.4977772, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {7.207307, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(37.0); compressor.maxT = F_TO_C(120.); @@ -4914,15 +4909,15 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 47, // Temperature (T_F) {280, 4.97342, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.634009, -0.029485, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {280, 5.35992, 0.0}, // Input Power Coefficients (inputPower_coeffs) {6.3, -0.03, 0.0} // COP Coefficients (COP_coeffs) - }); + }); - compressor.hysteresis_dC = dF_TO_dC(1); + compressor.hysteresis_dC = dF_TO_dC(1); compressor.minT = F_TO_C(40.0); compressor.maxT = F_TO_C(120.0); @@ -4997,13 +4992,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {295.55337, 2.28518, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.744118, -0.025946, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {282.2126, 2.82001, 0.0}, // Input Power Coefficients (inputPower_coeffs) {8.012112, -0.039394, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(32.0); compressor.maxT = F_TO_C(120.); @@ -5055,13 +5050,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {472.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) {2.942642, -0.0125954, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {439.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) {3.95076, -0.01638033, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(45.0); compressor.maxT = F_TO_C(120.); @@ -5131,13 +5126,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {272.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) {4.042642, -0.0205954, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {239.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.25076, -0.02638033, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(40.0); compressor.maxT = F_TO_C(120.); @@ -5212,13 +5207,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {172.58616, 2.09340, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.242642, -0.0285954, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 67, // Temperature (T_F) {139.5615, 2.62997, 0.0}, // Input Power Coefficients (inputPower_coeffs) {6.75076, -0.03638033, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(35.0); compressor.maxT = F_TO_C(120.); @@ -5292,13 +5287,13 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { 50, // Temperature (T_F) {187.064124, 1.939747, 0.0}, // Input Power Coefficients (inputPower_coeffs) {4.29, -0.0243008, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.perfMap.push_back({ 70, // Temperature (T_F) {148.0418, 2.553291, 0.0}, // Input Power Coefficients (inputPower_coeffs) {5.61, -0.0335265, 0.0} // COP Coefficients (COP_coeffs) - }); + }); compressor.minT = F_TO_C(37.0); compressor.maxT = F_TO_C(120.); @@ -5338,9 +5333,9 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { } else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have tried to select a preset model which does not exist. \n"); - } + if (hpwhVerbosity >= VRB_reluctant) { + msg("You have tried to select a preset model which does not exist. \n"); + } return HPWH_ABORT; } @@ -5364,7 +5359,7 @@ int HPWH::HPWHinit_presets(MODELS presetNum) { setOfSources[i].sortPerformanceMap(); } - if (hpwhVerbosity >= VRB_emetic){ + if (hpwhVerbosity >= VRB_emetic) { for (int i = 0; i < numHeatSources; i++) { msg("heat source %d: %p \n", i, &setOfSources[i]); } From 36878a8227ee35143572f6ac1a7c4be198e0f479 Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Wed, 29 Jan 2020 15:44:14 -0800 Subject: [PATCH 21/22] calcSizeConstants Working version, passes tests. --- src/HPWH.cc | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 4cb58428..892e4b90 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -118,7 +118,11 @@ HPWH::HPWH(const HPWH &hpwh) { doConduction = hpwh.doConduction; locationTemperature_C = hpwh.locationTemperature_C; - + + volPerNode_LperNode = hpwh.volPerNode_LperNode; + node_height = hpwh.node_height; + fracAreaTop = hpwh.fracAreaTop; + fracAreaSide = hpwh.fracAreaSide; } HPWH & HPWH::operator=(const HPWH &hpwh) { @@ -183,6 +187,11 @@ HPWH & HPWH::operator=(const HPWH &hpwh) { locationTemperature_C = hpwh.locationTemperature_C; + volPerNode_LperNode = hpwh.volPerNode_LperNode; + node_height = hpwh.node_height; + fracAreaTop = hpwh.fracAreaTop; + fracAreaSide = hpwh.fracAreaSide; + return *this; } @@ -818,6 +827,9 @@ int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/) { return HPWH_ABORT; } } + + calcSizeConstants(); + return 0; } int HPWH::setDoInversionMixing(bool doInvMix) { @@ -1315,7 +1327,6 @@ double HPWH::getLocationTemp_C() const { void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbientT_C, double minutesPerStep, double inletVol2_L, double inletT2_C) { //set up some useful variables for calculations - double volPerNode_LperNode = tankVolume_L / numNodes; double drawFraction; this->outletTemp_C = 0; double nodeInletFraction, cumInletFraction, drawVolume_N, nodeInletTV; @@ -1435,20 +1446,6 @@ void HPWH::updateTankTemps(double drawVolume_L, double inletT_C, double tankAmbi } //end if(draw_volume_L > 0) - // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. - // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions - // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. - // height estimate from Rheem along with the volume is used to get the radius and node_height - const double rad = getTankRadius(UNITS_M); - const double height = tankVolume_L / (1000.0 * 3.14159 * rad * rad); - const double node_height = height / numNodes; - - // The fraction of UA that is on the top or the bottom of the tank. So 2 * fracAreaTop + fracAreaSide is the total tank area. - const double fracAreaTop = rad / (2.0 * (height + rad)); - - // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. - const double fracAreaSide = height / (height + rad); - if (doConduction) { // Get the "constant" tau for the stability condition and the conduction calculation @@ -2394,6 +2391,23 @@ void HPWH::HeatSource::addTurnOnLogic(HeatingLogic logic) { void HPWH::HeatSource::addShutOffLogic(HeatingLogic logic) { this->shutOffLogicSet.push_back(logic); } +void HPWH::calcSizeConstants() { + // calculate conduction between the nodes AND heat loss by node with top and bottom having greater surface area. + // model uses explicit finite difference to find conductive heat exchange between the tank nodes with the boundary conditions + // on the top and bottom node being the fraction of UA that corresponds to the top and bottom of the tank. + // height estimate from Rheem along with the volume is used to get the radius and node_height + const double tank_rad = getTankRadius(UNITS_M); + const double tank_height = tankVolume_L / (1000.0 * 3.14159 * tank_rad * tank_rad); + volPerNode_LperNode = tankVolume_L / numNodes; + + node_height = tank_height / numNodes; + + // The fraction of UA that is on the top or the bottom of the tank. So 2 * fracAreaTop + fracAreaSide is the total tank area. + fracAreaTop = tank_rad / (2.0 * (tank_height + tank_rad)); + + // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. + fracAreaSide = tank_height / (tank_height + tank_rad); +} void HPWH::calcDerivedValues() { static char outputString[MAXOUTSTRING]; //this is used for debugging outputs @@ -2485,7 +2499,7 @@ void HPWH::calcDerivedValues() { } } - + calcSizeConstants(); } From 315f0bb60345323a8e0cde0122602bbca8a32e1c Mon Sep 17 00:00:00 2001 From: pmskintner <50156325+pmskintner@users.noreply.github.com> Date: Thu, 6 Feb 2020 11:38:20 -0800 Subject: [PATCH 22/22] Fix Conflicts, Remove Debuging Code --- src/HPWH.cc | 5 ++--- test/CMakeLists.txt | 16 ++++++++-------- test/main.cc | 13 ++++--------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/HPWH.cc b/src/HPWH.cc index 41cf47c9..6aeeec80 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -2502,14 +2502,14 @@ void HPWH::calcSizeConstants() { } void HPWH::calcDerivedValues() { - static char outputString[MAXOUTSTRING]; //this is used for debugging outputs - // tank node density (number of calculation nodes per regular node) nodeDensity = numNodes / 12; // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() calcDerivedHeatingValues(); + calcSizeConstants(); + //heat source ability to depress temp for (int i = 0; i < numHeatSources; i++) { if (setOfSources[i].typeOfHeatSource == TYPE_compressor) { @@ -2607,7 +2607,6 @@ void HPWH::calcDerivedHeatingValues(){ } } - calcSizeConstants(); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f3b3f0a1..9d05ff52 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,20 +13,20 @@ set(testNames test30 test50 test70 - #test95 - # testLockout - # testDr + test95 + testLockout + testDr testSandenCombi ) set(modelNames - StorageTank +# StorageTank AOSmithPHPT60 AOSmithHPTU80 -# Sanden80 -# RheemHB50 -# Stiebel220e - #GE502014 + Sanden80 + RheemHB50 + Stiebel220e + GE502014 ) set(lockoutTestModels diff --git a/test/main.cc b/test/main.cc index c7f6d434..bf67818c 100644 --- a/test/main.cc +++ b/test/main.cc @@ -60,7 +60,8 @@ int main(int argc, char *argv[]) ifstream controlFile; string strPreamble; - string strHead = "minutes,Ta,inletT,draw,outletT"; + // string strHead = "minutes,Ta,inletT,draw,outletT,"; + string strHead = "minutes,Ta,inletT,draw,"; //....................................... //process command line arguments @@ -278,12 +279,6 @@ int main(int argc, char *argv[]) drStatus = HPWH::DR_ENGAGE; } -// vectptr = NULL; -// if ( i == 15 || i== 16 || i== 17){ -// nodeExtraHeat_W = {200000 }; - // vectptr = &nodeExtraHeat_W; - //} - // Run the step hpwh.runOneStep(allSchedules[0][i], // Inlet water temperature (C) GAL_TO_L(allSchedules[1][i]), // Flow in gallons @@ -300,8 +295,8 @@ int main(int argc, char *argv[]) } strPreamble = std::to_string(i) + ", " + std::to_string(airTemp2) + ", " + - std::to_string(allSchedules[0][i]) + ", " + std::to_string(allSchedules[1][i]) + ", " + - std::to_string(hpwh.getOutletTemp()) + ","; + std::to_string(allSchedules[0][i]) + ", " + std::to_string(allSchedules[1][i]) + ", ";// + + //std::to_string(hpwh.getOutletTemp()) + ","; hpwh.WriteCSVRow(outputFile, strPreamble.c_str(), nTestTCouples, 0); }