diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..4b891d19 --- /dev/null +++ b/.clang-format @@ -0,0 +1,234 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +# AlignConsecutiveShortCaseStatements: # clang-format 17 +# Enabled: false +# AcrossEmptyLines: false +# AcrossComments: false +# AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +# AllowBreakBeforeNoexceptSpecifier: Never # clang-format 17 +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BraceWrapping: # relevant only if BreakBeforeBraces==Custom + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Allman +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: AfterComma +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: false +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: true +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + # BinaryMinDigits: 0 + Decimal: 0 + # DecimalMinDigits: 0 + Hex: 0 + # HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +# KeepEmptyLinesAtEOF: false # clang-format 17 +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +# RemoveParentheses: Leave # clang-format 17 +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +#SpaceBeforeJsonColon: false # clang-format 17 +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: # to be tested + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +# SpacesInParens: Never # clang-format 17 +# SpacesInParensOptions: # # clang-format 17 +# InCStyleCasts: false +# InConditionalStatements: false +# InEmptyParentheses: false +# Other: false +SpacesInSquareBrackets: false +Standard: c++17 +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +# TypeNames: [] # clang-format 17 +UseTab: Never +# VerilogBreakBetweenInstancePorts: true # clang-format 17 +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml new file mode 100644 index 00000000..f4ea0fb6 --- /dev/null +++ b/.github/workflows/clang-format-check.yml @@ -0,0 +1,21 @@ +name: clang-format Check +on: [push, pull_request] +jobs: + formatting-check: + name: Formatting Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Project Name + run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV + - name: Run clang-format style check on implementation files. + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: '16' + check-path: 'src' + - name: Run clang-format style check on test files. + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: '16' + check-path: 'test' diff --git a/.gitignore b/.gitignore index b1c0b0dd..9d449f14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.* !/.gitignore *~ ~* diff --git a/src/HPWH.cc b/src/HPWH.cc index ed79317a..b615ffeb 100644 --- a/src/HPWH.cc +++ b/src/HPWH.cc @@ -47,8 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -using std::endl; using std::cout; +using std::endl; using std::string; const float HPWH::DENSITYWATER_kgperL = 0.995f; @@ -65,42 +65,47 @@ const double HPWH::MINSINGLEPASSLIFT = dF_TO_dC(15.); //----------------------------------------------------------------------------- /// @brief Samples a std::vector to extract a single value spanning the fractional -/// coordinate range from frac_begin to frac_end. +/// coordinate range from frac_begin to frac_end. /// @note Bounding fractions are clipped or swapped, if needed. /// @param[in] sampleValues Contains values to be sampled /// @param[in] beginFraction Lower (left) bounding fraction (0 to 1) -/// @param[in] endFraction Upper (right) bounding fraction (0 to 1) +/// @param[in] endFraction Upper (right) bounding fraction (0 to 1) /// @return Resampled value; 0 if undefined. //----------------------------------------------------------------------------- -double getResampledValue(const std::vector &sampleValues,double beginFraction,double endFraction) -{ - if(beginFraction > endFraction)std::swap(beginFraction,endFraction); - if(beginFraction < 0.) beginFraction = 0.; - if(endFraction >1.) endFraction = 1.; - - double nNodes = static_cast(sampleValues.size()); - auto beginIndex = static_cast(beginFraction * nNodes); - - double previousFraction = beginFraction; - double nextFraction = previousFraction; - - double totValueWeight = 0.; - double totWeight = 0.; - for(std::size_t index = beginIndex; nextFraction < endFraction; ++index) - { - nextFraction = static_cast(index + 1) / nNodes; - if(nextFraction > endFraction) - { - nextFraction = endFraction; - } - double weight = nextFraction - previousFraction; - totValueWeight += weight *sampleValues[index]; - totWeight += weight; - previousFraction = nextFraction; - } - double resampled_value = 0.; - if(totWeight > 0.) resampled_value = totValueWeight / totWeight; - return resampled_value; +double +getResampledValue(const std::vector& sampleValues, double beginFraction, double endFraction) +{ + if (beginFraction > endFraction) + std::swap(beginFraction, endFraction); + if (beginFraction < 0.) + beginFraction = 0.; + if (endFraction > 1.) + endFraction = 1.; + + double nNodes = static_cast(sampleValues.size()); + auto beginIndex = static_cast(beginFraction * nNodes); + + double previousFraction = beginFraction; + double nextFraction = previousFraction; + + double totValueWeight = 0.; + double totWeight = 0.; + for (std::size_t index = beginIndex; nextFraction < endFraction; ++index) + { + nextFraction = static_cast(index + 1) / nNodes; + if (nextFraction > endFraction) + { + nextFraction = endFraction; + } + double weight = nextFraction - previousFraction; + totValueWeight += weight * sampleValues[index]; + totWeight += weight; + previousFraction = nextFraction; + } + double resampled_value = 0.; + if (totWeight > 0.) + resampled_value = totValueWeight / totWeight; + return resampled_value; } //----------------------------------------------------------------------------- @@ -110,30 +115,34 @@ double getResampledValue(const std::vector &sampleValues,double beginFra /// @param[in] sampleValues Contains values to replace with /// @return Success: true; Failure: false //----------------------------------------------------------------------------- -bool resample(std::vector &values,const std::vector &sampleValues) +bool resample(std::vector& values, const std::vector& sampleValues) { - if(sampleValues.empty()) return false; + if (sampleValues.empty()) + return false; double actualSize = static_cast(values.size()); double sizeRatio = static_cast(sampleValues.size()) / actualSize; auto binSize = static_cast(1. / sizeRatio); double beginFraction = 0., endFraction; std::size_t index = 0; - while(index < actualSize) + while (index < actualSize) { auto value = static_cast(index); auto sampleIndex = static_cast(floor(value * sizeRatio)); - if(sampleIndex + 1. < (value + 1.) * sizeRatio) { // General case: no binning possible + if (sampleIndex + 1. < (value + 1.) * sizeRatio) + { // General case: no binning possible endFraction = static_cast(index + 1) / actualSize; - values[index] = getResampledValue(sampleValues,beginFraction,endFraction); + values[index] = getResampledValue(sampleValues, beginFraction, endFraction); ++index; } - else { // Special case: direct copy a single value to a bin + else + { // Special case: direct copy a single value to a bin std::size_t beginIndex = index; std::size_t adjustedBinSize = binSize; - if(binSize > 1) + if (binSize > 1) { // Find beginning of bin and number to copy - beginIndex = static_cast(ceil(sampleIndex/sizeRatio)); - adjustedBinSize = static_cast(floor((sampleIndex + 1)/sizeRatio) - ceil(sampleIndex/sizeRatio)); + beginIndex = static_cast(ceil(sampleIndex / sizeRatio)); + adjustedBinSize = static_cast(floor((sampleIndex + 1) / sizeRatio) - + ceil(sampleIndex / sizeRatio)); } std::fill_n(values.begin() + beginIndex, adjustedBinSize, sampleValues[sampleIndex]); index = beginIndex + adjustedBinSize; @@ -148,3872 +157,5187 @@ bool resample(std::vector &values,const std::vector &sampleValue /// @brief Resample an extensive property (e.g., heat) /// @note See definition of int resample. //----------------------------------------------------------------------------- -bool resampleExtensive(std::vector &values,const std::vector &sampleValues) -{ - if(resample(values,sampleValues)) - { - double scale = static_cast(sampleValues.size()) / static_cast(values.size()); - for(auto &value: values) - value *= scale; - return true; - } - return false; -} - -double expitFunc(double x,double offset) { - double val; - val = 1 / (1 + exp(x - offset)); - return val; -} - -void normalize(std::vector &distribution) { - size_t N = distribution.size(); - - bool normalization_needed = true; - - // Need to renormalize if negligible elements are zeroed. - while (normalization_needed) - { - normalization_needed = false; - double sum_tmp = 0.; - for(size_t i = 0; i < N; i++) { - sum_tmp += distribution[i]; - } - if(sum_tmp > 0.) { - for(size_t i = 0; i < N; i++) { - distribution[i] /= sum_tmp; - //this gives a very slight speed improvement (milliseconds per simulated year) - if(distribution[i] < HPWH::TOL_MINVALUE) { - if (distribution[i] > 0.) { - normalization_needed = true; - } - distribution[i] = 0.; - } - } - } - else { - for(size_t i = 0; i < N; i++) { - distribution[i] = 0.; - } - } - } +bool resampleExtensive(std::vector& values, const std::vector& sampleValues) +{ + if (resample(values, sampleValues)) + { + double scale = + static_cast(sampleValues.size()) / static_cast(values.size()); + for (auto& value : values) + value *= scale; + return true; + } + return false; +} + +double expitFunc(double x, double offset) +{ + double val; + val = 1 / (1 + exp(x - offset)); + return val; +} + +void normalize(std::vector& distribution) +{ + size_t N = distribution.size(); + + bool normalization_needed = true; + + // Need to renormalize if negligible elements are zeroed. + while (normalization_needed) + { + normalization_needed = false; + double sum_tmp = 0.; + for (size_t i = 0; i < N; i++) + { + sum_tmp += distribution[i]; + } + if (sum_tmp > 0.) + { + for (size_t i = 0; i < N; i++) + { + distribution[i] /= sum_tmp; + // this gives a very slight speed improvement (milliseconds per simulated year) + if (distribution[i] < HPWH::TOL_MINVALUE) + { + if (distribution[i] > 0.) + { + normalization_needed = true; + } + distribution[i] = 0.; + } + } + } + else + { + for (size_t i = 0; i < N; i++) + { + distribution[i] = 0.; + } + } + } } //----------------------------------------------------------------------------- -/// @brief Finds the lowest tank node with non-zero weighting +/// @brief Finds the lowest tank node with non-zero weighting /// @param[in] nodeDist weighting to be applied /// @param[in] numTankNodes number of nodes in tank /// @returns index of lowest tank node //----------------------------------------------------------------------------- -int findLowestNode(const std::vector &nodeDist,const int numTankNodes){ - int lowest = 0; - const int distSize = static_cast(nodeDist.size()); - double nodeRatio = static_cast(numTankNodes) / distSize; +int findLowestNode(const std::vector& nodeDist, const int numTankNodes) +{ + int lowest = 0; + const int distSize = static_cast(nodeDist.size()); + double nodeRatio = static_cast(numTankNodes) / distSize; - for(auto j = 0; j < distSize; ++j) { - if(nodeDist[j] > 0.) { - lowest = static_cast(nodeRatio * j); - break; - } - } + for (auto j = 0; j < distSize; ++j) + { + if (nodeDist[j] > 0.) + { + lowest = static_cast(nodeRatio * j); + break; + } + } - return lowest; + return lowest; } //----------------------------------------------------------------------------- -/// @brief Calculates a width parameter for a thermal distribution +/// @brief Calculates a width parameter for a thermal distribution /// @param[in] nodeDist original distribution from which theraml distribution /// is derived -/// @returns width parameter (in degC) +/// @returns width parameter (in degC) //----------------------------------------------------------------------------- -double findShrinkageT_C(const std::vector &nodeDist){ - double alphaT_C = 1.,betaT_C = 2.; - double condentropy = 0.; - for(std::size_t iNode = 0; iNode < nodeDist.size(); ++iNode) { - double dist = nodeDist[iNode]; - if(dist > 0.) { - condentropy -= dist * log(dist); - } - } - // condentropy shifts as ln(# of condensity nodes) - double size_factor = static_cast(nodeDist.size()) / HPWH::CONDENSITY_SIZE; - double standard_condentropy = condentropy - log(size_factor); - - return alphaT_C + standard_condentropy * betaT_C; +double findShrinkageT_C(const std::vector& nodeDist) +{ + double alphaT_C = 1., betaT_C = 2.; + double condentropy = 0.; + for (std::size_t iNode = 0; iNode < nodeDist.size(); ++iNode) + { + double dist = nodeDist[iNode]; + if (dist > 0.) + { + condentropy -= dist * log(dist); + } + } + // condentropy shifts as ln(# of condensity nodes) + double size_factor = static_cast(nodeDist.size()) / HPWH::CONDENSITY_SIZE; + double standard_condentropy = condentropy - log(size_factor); + + return alphaT_C + standard_condentropy * betaT_C; } //----------------------------------------------------------------------------- -/// @brief Calculates a thermal distribution for heat distribution. +/// @brief Calculates a thermal distribution for heat distribution. /// @note Fails if all nodeTemp_C values exceed setpointT_C /// @param[out] thermalDist resulting thermal distribution; does not require pre-allocation /// @param[in] shrinkageT_C width of distribution /// @param[in] lowestNode index of lowest non-zero contribution /// @param[in] nodeTemp_C node temperatures -/// @param[in] setpointT_C distribution parameter +/// @param[in] setpointT_C distribution parameter //----------------------------------------------------------------------------- -void calcThermalDist( - std::vector &thermalDist, - const double shrinkageT_C, - const int lowestNode, - const std::vector &nodeT_C, - const double setpointT_C) { - - thermalDist.resize(nodeT_C.size()); - - // Populate the vector of heat distribution - double totDist = 0.; - for(int i = 0; i < static_cast(nodeT_C.size()); i++) { - double dist = 0.; - if(i >= lowestNode){ - double Toffset_C = 5.0 / 1.8; // 5 degF - double offset = Toffset_C / 1.; // should be dimensionless - dist = expitFunc((nodeT_C[i] - nodeT_C[lowestNode]) / shrinkageT_C,offset); - dist *= (setpointT_C - nodeT_C[i]); - if(dist < 0.) - dist = 0.; - } - thermalDist[i] = dist; - totDist += dist; - } - - if(totDist > 0.) { - normalize(thermalDist); - } - else { - thermalDist.assign(thermalDist.size(), 1./static_cast(thermalDist.size())); - } +void calcThermalDist(std::vector& thermalDist, + const double shrinkageT_C, + const int lowestNode, + const std::vector& nodeT_C, + const double setpointT_C) +{ + + thermalDist.resize(nodeT_C.size()); + + // Populate the vector of heat distribution + double totDist = 0.; + for (int i = 0; i < static_cast(nodeT_C.size()); i++) + { + double dist = 0.; + if (i >= lowestNode) + { + double Toffset_C = 5.0 / 1.8; // 5 degF + double offset = Toffset_C / 1.; // should be dimensionless + dist = expitFunc((nodeT_C[i] - nodeT_C[lowestNode]) / shrinkageT_C, offset); + dist *= (setpointT_C - nodeT_C[i]); + if (dist < 0.) + dist = 0.; + } + thermalDist[i] = dist; + totDist += dist; + } + if (totDist > 0.) + { + normalize(thermalDist); + } + else + { + thermalDist.assign(thermalDist.size(), 1. / static_cast(thermalDist.size())); + } } void HPWH::setMinutesPerStep(const double minutesPerStep_in) { - minutesPerStep = minutesPerStep_in; - secondsPerStep = sec_per_min * minutesPerStep; - hoursPerStep = minutesPerStep / min_per_hr; + minutesPerStep = minutesPerStep_in; + secondsPerStep = sec_per_min * minutesPerStep; + hoursPerStep = minutesPerStep / min_per_hr; }; // public HPWH functions -HPWH::HPWH(): messageCallback(NULL),messageCallbackContextPtr(NULL),hpwhVerbosity(VRB_silent) +HPWH::HPWH() : messageCallback(NULL), messageCallbackContextPtr(NULL), hpwhVerbosity(VRB_silent) { - setAllDefaults(); + setAllDefaults(); }; -void HPWH::setAllDefaults() { - tankTemps_C.clear(); - nextTankTemps_C.clear(); - heatSources.clear(); - - simHasFailed = true; isHeating = false; setpointFixed = false; tankSizeFixed = true; canScale = false; - member_inletT_C = HPWH_ABORT; - currentSoCFraction = 1.; - doTempDepression = false; - locationTemperature_C = UNINITIALIZED_LOCATIONTEMP; - mixBelowFractionOnDraw = 1. / 3.; - doInversionMixing = true; doConduction = true; - inletHeight = 0; inlet2Height = 0; fittingsUA_kJperHrC = 0.; - prevDRstatus = DR_ALLOW; timerLimitTOT = 60.; timerTOT = 0.; - usesSoCLogic = false; - setMinutesPerStep(1.0); - hpwhVerbosity = VRB_minuteOut; - hasHeatExchanger = false; - heatExchangerEffectiveness = 0.9; -} - -HPWH::HPWH(const HPWH &hpwh) { - *this = hpwh; -} - -HPWH & HPWH::operator=(const HPWH &hpwh) { - if(this == &hpwh) { - return *this; - } +void HPWH::setAllDefaults() +{ + tankTemps_C.clear(); + nextTankTemps_C.clear(); + heatSources.clear(); + + simHasFailed = true; + isHeating = false; + setpointFixed = false; + tankSizeFixed = true; + canScale = false; + member_inletT_C = HPWH_ABORT; + currentSoCFraction = 1.; + doTempDepression = false; + locationTemperature_C = UNINITIALIZED_LOCATIONTEMP; + mixBelowFractionOnDraw = 1. / 3.; + doInversionMixing = true; + doConduction = true; + inletHeight = 0; + inlet2Height = 0; + fittingsUA_kJperHrC = 0.; + prevDRstatus = DR_ALLOW; + timerLimitTOT = 60.; + timerTOT = 0.; + usesSoCLogic = false; + setMinutesPerStep(1.0); + hpwhVerbosity = VRB_minuteOut; + hasHeatExchanger = false; + heatExchangerEffectiveness = 0.9; +} + +HPWH::HPWH(const HPWH& hpwh) { *this = hpwh; } + +HPWH& HPWH::operator=(const HPWH& hpwh) +{ + if (this == &hpwh) + { + return *this; + } - simHasFailed = hpwh.simHasFailed; + simHasFailed = hpwh.simHasFailed; - hpwhVerbosity = hpwh.hpwhVerbosity; + hpwhVerbosity = hpwh.hpwhVerbosity; - //these should actually be the same pointers - messageCallback = hpwh.messageCallback; - messageCallbackContextPtr = hpwh.messageCallbackContextPtr; + // these should actually be the same pointers + messageCallback = hpwh.messageCallback; + messageCallbackContextPtr = hpwh.messageCallbackContextPtr; - isHeating = hpwh.isHeating; + isHeating = hpwh.isHeating; - heatSources = hpwh.heatSources; - for(auto &heatSource: heatSources) { - heatSource.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 - } + heatSources = hpwh.heatSources; + for (auto& heatSource : heatSources) + { + heatSource.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; - fittingsUA_kJperHrC = hpwh.fittingsUA_kJperHrC; + tankVolume_L = hpwh.tankVolume_L; + tankUA_kJperHrC = hpwh.tankUA_kJperHrC; + fittingsUA_kJperHrC = hpwh.fittingsUA_kJperHrC; - setpoint_C = hpwh.setpoint_C; + setpoint_C = hpwh.setpoint_C; - tankTemps_C = hpwh.tankTemps_C; - nextTankTemps_C = hpwh.nextTankTemps_C; + tankTemps_C = hpwh.tankTemps_C; + nextTankTemps_C = hpwh.nextTankTemps_C; - inletHeight = hpwh.inletHeight; - inlet2Height = hpwh.inlet2Height; + inletHeight = hpwh.inletHeight; + inlet2Height = hpwh.inlet2Height; - outletTemp_C = hpwh.outletTemp_C; - condenserInlet_C = hpwh.condenserInlet_C; - condenserOutlet_C = hpwh.condenserOutlet_C; - externalVolumeHeated_L = hpwh.externalVolumeHeated_L; - energyRemovedFromEnvironment_kWh = hpwh.energyRemovedFromEnvironment_kWh; - standbyLosses_kWh = hpwh.standbyLosses_kWh; + outletTemp_C = hpwh.outletTemp_C; + condenserInlet_C = hpwh.condenserInlet_C; + condenserOutlet_C = hpwh.condenserOutlet_C; + externalVolumeHeated_L = hpwh.externalVolumeHeated_L; + energyRemovedFromEnvironment_kWh = hpwh.energyRemovedFromEnvironment_kWh; + standbyLosses_kWh = hpwh.standbyLosses_kWh; - tankMixesOnDraw = hpwh.tankMixesOnDraw; - mixBelowFractionOnDraw = hpwh.mixBelowFractionOnDraw; + tankMixesOnDraw = hpwh.tankMixesOnDraw; + mixBelowFractionOnDraw = hpwh.mixBelowFractionOnDraw; - doTempDepression = hpwh.doTempDepression; + doTempDepression = hpwh.doTempDepression; - doInversionMixing = hpwh.doInversionMixing; - doConduction = hpwh.doConduction; + doInversionMixing = hpwh.doInversionMixing; + doConduction = hpwh.doConduction; - locationTemperature_C = hpwh.locationTemperature_C; + locationTemperature_C = hpwh.locationTemperature_C; - prevDRstatus = hpwh.prevDRstatus; - timerLimitTOT = hpwh.timerLimitTOT; + prevDRstatus = hpwh.prevDRstatus; + timerLimitTOT = hpwh.timerLimitTOT; - usesSoCLogic = hpwh.usesSoCLogic; + usesSoCLogic = hpwh.usesSoCLogic; - nodeVolume_L = hpwh.nodeVolume_L; - nodeHeight_m = hpwh.nodeHeight_m; - fracAreaTop = hpwh.fracAreaTop; - fracAreaSide = hpwh.fracAreaSide; - return *this; + nodeVolume_L = hpwh.nodeVolume_L; + nodeHeight_m = hpwh.nodeHeight_m; + fracAreaTop = hpwh.fracAreaTop; + fracAreaSide = hpwh.fracAreaSide; + return *this; } -HPWH::~HPWH() { -} +HPWH::~HPWH() {} int HPWH::runOneStep(double drawVolume_L, - double tankAmbientT_C,double heatSourceAmbientT_C, - DRMODES DRstatus, - double inletVol2_L,double inletT2_C, - std::vector* extraHeatDist_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((DRstatus & (DR_TOO | DR_TOT))) { - if(hpwhVerbosity >= VRB_typical) { - msg("DR_TOO | DR_TOT use conflicting logic sets. The logic will follow a DR_TOT scheme \n"); - } - } - - 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", - member_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.; - condenserInlet_C = 0.; - condenserOutlet_C = 0.; - externalVolumeHeated_L = 0.; - energyRemovedFromEnvironment_kWh = 0.; - standbyLosses_kWh = 0.; - - for(int i = 0; i < getNumHeatSources(); i++) { - heatSources[i].runtime_min = 0; - heatSources[i].energyInput_kWh = 0.; - heatSources[i].energyOutput_kWh = 0.; - } - extraEnergyInput_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,member_inletT_C,tankAmbientT_C,inletVol2_L,inletT2_C); - - updateSoCIfNecessary(); - - // First Logic DR checks ////////////////////////////////////////////////////////////////// - - // If the DR signal includes a top off but the previous signal did not, then top it off! - if((DRstatus & DR_LOC) != 0 && (DRstatus & DR_LOR) != 0) { - turnAllHeatSourcesOff(); // turns off isheating - if(hpwhVerbosity >= VRB_emetic) { - msg("DR_LOC | DR_LOC everything off, DRstatus = %i \n",DRstatus); - } - } else { //do normal check - if(((DRstatus & DR_TOO) != 0 || (DRstatus & DR_TOT) != 0) && timerTOT == 0) { - - // turn on the compressor and last resistance element. - if(hasACompressor()) { - heatSources[compressorIndex].engageHeatSource(DRstatus); - } - if(lowestElementIndex >= 0) { - heatSources[lowestElementIndex].engageHeatSource(DRstatus); - } - - if(hpwhVerbosity >= VRB_emetic) { - msg("TURNED ON DR_TOO engaged compressor and lowest resistance element, DRstatus = %i \n",DRstatus); - } - } - - //do HeatSource choice - for(int i = 0; i < getNumHeatSources(); 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,heatSources[i].turnOnLogicSet.size(),heatSources[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(heatSources[i].isEngaged() && heatSources[i].shutsOff()) { - heatSources[i].disengageHeatSource(); - //check if the backup heat source would have to shut off too - if(heatSources[i].backupHeatSource != NULL && heatSources[i].backupHeatSource->shutsOff() != true) { - //and if not, go ahead and turn it on - heatSources[i].backupHeatSource->engageHeatSource(DRstatus); - } - } - - //if there's a priority HeatSource (e.g. upper resistor) and it needs to - //come on, then turn off and start it up - if(heatSources[i].isVIP) { - if(hpwhVerbosity >= VRB_emetic) { - msg("\tVIP check"); - } - if(heatSources[i].shouldHeat()) { - if(shouldDRLockOut(heatSources[i].typeOfHeatSource,DRstatus)) { - if(hasACompressor()) { - heatSources[compressorIndex].engageHeatSource(DRstatus); - break; - } - } else { - turnAllHeatSourcesOff(); - heatSources[i].engageHeatSource(DRstatus); - //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(heatSources[i].shouldHeat()) { - heatSources[i].engageHeatSource(DRstatus); - //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 < getNumHeatSources(); i++) { - msg("heat source %d: %d \t",i,heatSources[i].isEngaged()); - } - msg("\n"); - } - - //do heating logic - double minutesToRun = minutesPerStep; - for(int i = 0; i < getNumHeatSources(); i++) { - // check/apply lock-outs - if(hpwhVerbosity >= VRB_emetic) { - msg("Checking lock-out logic for heat source %d:\n",i); - } - if(shouldDRLockOut(heatSources[i].typeOfHeatSource,DRstatus)) { - heatSources[i].lockOutHeatSource(); - if(hpwhVerbosity >= VRB_emetic) { - msg("Locked out heat source, DRstatus = %i\n",DRstatus); - } - } else - { - // locks or unlocks the heat source - heatSources[i].toLockOrUnlock(heatSourceAmbientT_C); - } - if(heatSources[i].isLockedOut() && heatSources[i].backupHeatSource == NULL) { - heatSources[i].disengageHeatSource(); - if(hpwhVerbosity >= HPWH::VRB_emetic) { - msg("\nWARNING: lock-out triggered, but no backupHeatSource defined. Simulation will continue will lock out the heat source."); - } - } - - //going through in order, check if the heat source is on - if(heatSources[i].isEngaged()) { - - HeatSource* heatSourcePtr; - if(heatSources[i].isLockedOut() && heatSources[i].backupHeatSource != NULL) { - - // Check that the backup isn't locked out too or already engaged then it will heat on its own. - if(heatSources[i].backupHeatSource->toLockOrUnlock(heatSourceAmbientT_C) || - shouldDRLockOut(heatSources[i].backupHeatSource->typeOfHeatSource,DRstatus) || //){ - heatSources[i].backupHeatSource->isEngaged()) { - continue; - } - // Don't turn the backup electric resistance heat source on if the VIP resistance element is on . - else if(VIPIndex >= 0 && heatSources[VIPIndex].isOn && - heatSources[i].backupHeatSource->isAResistance()) { - if(hpwhVerbosity >= VRB_typical) { - msg("Locked out back up heat source AND the engaged heat source %i, DRstatus = %i\n",i,DRstatus); - } - continue; - } else { - heatSourcePtr = heatSources[i].backupHeatSource; - } - } else { - heatSourcePtr = &heatSources[i]; - } - - addHeatParent(heatSourcePtr,heatSourceAmbientT_C,minutesToRun); - - //if it finished early. i.e. shuts off early like if the heatsource met setpoint or maxed out - 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; - heatSources[i].disengageHeatSource(); - //and if there's a heat source that follows this heat source (regardless of lockout) that's able to come on, - if(heatSources[i].followedByHeatSource != NULL && heatSources[i].followedByHeatSource->shutsOff() == false) { - //turn it on - heatSources[i].followedByHeatSource->engageHeatSource(DRstatus); - } - // or if there heat source can't produce hotter water (i.e. it's maxed out) and the tank still isn't at setpoint. - // the compressor should get locked out when the maxedOut is true but have to run the resistance first during this - // timestep to make sure tank is above the max temperature for the compressor. - else if(heatSources[i].maxedOut() && heatSources[i].backupHeatSource != NULL) { - - // Check that the backup isn't locked out or already engaged then it will heat or already heated on it's own. - if(!heatSources[i].backupHeatSource->toLockOrUnlock(heatSourceAmbientT_C) && //If not locked out - !shouldDRLockOut(heatSources[i].backupHeatSource->typeOfHeatSource,DRstatus) && // and not DR locked out - !heatSources[i].backupHeatSource->isEngaged()) { // and not already engaged - - HeatSource* backupHeatSourcePtr = heatSources[i].backupHeatSource; - // turn it on - backupHeatSourcePtr->engageHeatSource(DRstatus); - // add heat if it hasn't heated up this whole minute already - if(backupHeatSourcePtr->runtime_min >= 0.) { - addHeatParent(backupHeatSourcePtr,heatSourceAmbientT_C,minutesToRun - backupHeatSourcePtr->runtime_min); - } - } - } - } - } // heat source not engaged - } // end while iHS heat source - } - if(areAllHeatSourcesOff() == true) { - isHeating = false; - } - //If there's extra user defined heat to add -> Add extra heat! - if(extraHeatDist_W != NULL && (*extraHeatDist_W).size() != 0) { - addExtraHeat(*extraHeatDist_W); - updateSoCIfNecessary(); - } - - //track the depressed local temperature - if(doTempDepression) { - bool compressorRan = false; - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isEngaged() && !heatSources[i].isLockedOut() && heatSources[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 < getNumHeatSources(); i++) { - energyRemovedFromEnvironment_kWh += (heatSources[i].energyOutput_kWh - heatSources[i].energyInput_kWh); - } - - //cursory check for inverted temperature profile - if(tankTemps_C[getNumNodes() - 1] < tankTemps_C[0]) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("The top of the tank is cooler than the bottom. \n"); - } - } - - // Handle DR timer - prevDRstatus = DRstatus; - // DR check for TOT to increase timer. - timerTOT += minutesPerStep; - // Restart the time if we're over the limit or the command is not a top off. - if((DRstatus & DR_TOT) != 0 && timerTOT >= timerLimitTOT) { - resetTopOffTimer(); - } else if((DRstatus & DR_TOO) == 0 && (DRstatus & DR_TOT) == 0) { - resetTopOffTimer(); - } - - 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) { - //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(getNumHeatSources()); - std::vector heatSources_energyInputs_SUM(getNumHeatSources()); - std::vector heatSources_energyOutputs_SUM(getNumHeatSources()); - - 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]); - - 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 < getNumHeatSources(); 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 < getNumHeatSources(); j++) { - msg("%f,%f,",getNthHeatSourceEnergyInput(j),getNthHeatSourceEnergyOutput(j)); - } - - std::vector displayTemps_C(10); - resampleIntensive(displayTemps_C, tankTemps_C); - bool first = true; - for (auto &displayTemp: displayTemps_C) - { - if (first) - first = false; - else - msg(","); - - msg("%f",displayTemp); - } - - for (int k = 1; k < 7; ++k) - { - if (first) - first = false; - else - msg(","); - - msg("%f", getNthSimTcouple(k,6)); - } - - msg("\n"); - } - - } - //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 < getNumHeatSources(); i++) { - heatSources[i].runtime_min = heatSources_runTimes_SUM[i]; - heatSources[i].energyInput_kWh = heatSources_energyInputs_SUM[i]; - heatSources[i].energyOutput_kWh = heatSources_energyOutputs_SUM[i]; - } - - if(hpwhVerbosity >= VRB_typical) { - msg("Ending runNSteps. \n\n\n\n"); - } - return 0; -} - -void HPWH::addHeatParent(HeatSource *heatSourcePtr,double heatSourceAmbientT_C,double minutesToRun) { - - double tempSetpoint_C = -273.15; - - // Check the air temprature and setpoint against maxOut_at_LowT - if(heatSourcePtr->isACompressor()) { - if(heatSourceAmbientT_C <= heatSourcePtr->maxOut_at_LowT.airT_C && - setpoint_C >= heatSourcePtr->maxOut_at_LowT.outT_C) - { - tempSetpoint_C = setpoint_C; //Store setpoint - setSetpoint(heatSourcePtr->maxOut_at_LowT.outT_C); // Reset to new setpoint as this is used in the add heat calc - } - } - //and add heat if it is - heatSourcePtr->addHeat(heatSourceAmbientT_C,minutesToRun); - - //Change the setpoint back to what it was pre-compressor depression - if(tempSetpoint_C > -273.15) { - setSetpoint(tempSetpoint_C); - } -} - - -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 + double tankAmbientT_C, + double heatSourceAmbientT_C, + DRMODES DRstatus, + double inletVol2_L, + double inletT2_C, + std::vector* extraHeatDist_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; + } -void HPWH::printHeatSourceInfo() { - std::stringstream ss; - double runtime = 0,outputVar = 0; + if ((DRstatus & (DR_TOO | DR_TOT))) + { + if (hpwhVerbosity >= VRB_typical) + { + msg("DR_TOO | DR_TOT use conflicting logic sets. The logic will follow a DR_TOT scheme " + " \n"); + } + } - 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; + 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", + member_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; + } - for(int i = 0; i < getNumHeatSources(); i++) { - ss << "input energy kwh: " << std::setw(7) << getNthHeatSourceEnergyInput(i) << "\t"; - } - ss << endl; + // reset the output variables + outletTemp_C = 0.; + condenserInlet_C = 0.; + condenserOutlet_C = 0.; + externalVolumeHeated_L = 0.; + energyRemovedFromEnvironment_kWh = 0.; + standbyLosses_kWh = 0.; - 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++) + { + heatSources[i].runtime_min = 0; + heatSources[i].energyInput_kWh = 0.; + heatSources[i].energyOutput_kWh = 0.; + } + extraEnergyInput_kWh = 0.; - for(int i = 0; i < getNumHeatSources(); i++) { - ss << "output energy kwh: " << std::setw(7) << getNthHeatSourceEnergyOutput(i,UNITS_KWH) << "\t"; - } - ss << endl; + // 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; + } - 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; + // process draws and standby losses + updateTankTemps(drawVolume_L, member_inletT_C, tankAmbientT_C, inletVol2_L, inletT2_C); - for(int i = 0; i < getNumHeatSources(); i++) { - ss << "run time min: " << std::setw(7) << getNthHeatSourceRunTime(i) << "\t\t"; - } - ss << endl << endl << endl; + updateSoCIfNecessary(); + // First Logic DR checks ////////////////////////////////////////////////////////////////// - msg(ss.str().c_str()); -} + // If the DR signal includes a top off but the previous signal did not, then top it off! + if ((DRstatus & DR_LOC) != 0 && (DRstatus & DR_LOR) != 0) + { + turnAllHeatSourcesOff(); // turns off isheating + if (hpwhVerbosity >= VRB_emetic) + { + msg("DR_LOC | DR_LOC everything off, DRstatus = %i \n", DRstatus); + } + } + else + { // do normal check + if (((DRstatus & DR_TOO) != 0 || (DRstatus & DR_TOT) != 0) && timerTOT == 0) + { + + // turn on the compressor and last resistance element. + if (hasACompressor()) + { + heatSources[compressorIndex].engageHeatSource(DRstatus); + } + if (lowestElementIndex >= 0) + { + heatSources[lowestElementIndex].engageHeatSource(DRstatus); + } + if (hpwhVerbosity >= VRB_emetic) + { + msg("TURNED ON DR_TOO engaged compressor and lowest resistance element, DRstatus = " + "%i \n", + DRstatus); + } + } -void HPWH::printTankTemps() { - std::stringstream ss; + // do HeatSource choice + for (int i = 0; i < getNumHeatSources(); 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, + heatSources[i].turnOnLogicSet.size(), + heatSources[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 (heatSources[i].isEngaged() && heatSources[i].shutsOff()) + { + heatSources[i].disengageHeatSource(); + // check if the backup heat source would have to shut off too + if (heatSources[i].backupHeatSource != NULL && + heatSources[i].backupHeatSource->shutsOff() != true) + { + // and if not, go ahead and turn it on + heatSources[i].backupHeatSource->engageHeatSource(DRstatus); + } + } + + // if there's a priority HeatSource (e.g. upper resistor) and it needs to + // come on, then turn off and start it up + if (heatSources[i].isVIP) + { + if (hpwhVerbosity >= VRB_emetic) + { + msg("\tVIP check"); + } + if (heatSources[i].shouldHeat()) + { + if (shouldDRLockOut(heatSources[i].typeOfHeatSource, DRstatus)) + { + if (hasACompressor()) + { + heatSources[compressorIndex].engageHeatSource(DRstatus); + break; + } + } + else + { + turnAllHeatSourcesOff(); + heatSources[i].engageHeatSource(DRstatus); + // 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 (heatSources[i].shouldHeat()) + { + heatSources[i].engageHeatSource(DRstatus); + // engaging a heat source sets isHeating to true, so this will only trigger once + } + } - ss << std::left; + } // end loop over heat sources - for(int i = 0; i < getNumNodes(); i++) { - ss << std::setw(9) << getTankNodeTemp(i) << " "; - } - ss << endl; + if (hpwhVerbosity >= VRB_emetic) + { + msg("after heat source choosing: "); + for (int i = 0; i < getNumHeatSources(); i++) + { + msg("heat source %d: %d \t", i, heatSources[i].isEngaged()); + } + msg("\n"); + } - msg(ss.str().c_str()); -} + // do heating logic + double minutesToRun = minutesPerStep; + for (int i = 0; i < getNumHeatSources(); i++) + { + // check/apply lock-outs + if (hpwhVerbosity >= VRB_emetic) + { + msg("Checking lock-out logic for heat source %d:\n", i); + } + if (shouldDRLockOut(heatSources[i].typeOfHeatSource, DRstatus)) + { + heatSources[i].lockOutHeatSource(); + if (hpwhVerbosity >= VRB_emetic) + { + msg("Locked out heat source, DRstatus = %i\n", DRstatus); + } + } + else + { + // locks or unlocks the heat source + heatSources[i].toLockOrUnlock(heatSourceAmbientT_C); + } + if (heatSources[i].isLockedOut() && heatSources[i].backupHeatSource == NULL) + { + heatSources[i].disengageHeatSource(); + if (hpwhVerbosity >= HPWH::VRB_emetic) + { + msg("\nWARNING: lock-out triggered, but no backupHeatSource defined. " + "Simulation will continue will lock out the heat source."); + } + } -// public members to write to CSV file -int HPWH::WriteCSVHeading(FILE* outFILE,const char* preamble,int nTCouples,int options) const { + // going through in order, check if the heat source is on + if (heatSources[i].isEngaged()) + { + + HeatSource* heatSourcePtr; + if (heatSources[i].isLockedOut() && heatSources[i].backupHeatSource != NULL) + { + + // Check that the backup isn't locked out too or already engaged then it will + // heat on its own. + if (heatSources[i].backupHeatSource->toLockOrUnlock(heatSourceAmbientT_C) || + shouldDRLockOut(heatSources[i].backupHeatSource->typeOfHeatSource, + DRstatus) || //){ + heatSources[i].backupHeatSource->isEngaged()) + { + continue; + } + // Don't turn the backup electric resistance heat source on if the VIP + // resistance element is on . + else if (VIPIndex >= 0 && heatSources[VIPIndex].isOn && + heatSources[i].backupHeatSource->isAResistance()) + { + if (hpwhVerbosity >= VRB_typical) + { + msg("Locked out back up heat source AND the engaged heat source %i, " + "DRstatus = %i\n", + i, + DRstatus); + } + continue; + } + else + { + heatSourcePtr = heatSources[i].backupHeatSource; + } + } + else + { + heatSourcePtr = &heatSources[i]; + } + + addHeatParent(heatSourcePtr, heatSourceAmbientT_C, minutesToRun); + + // if it finished early. i.e. shuts off early like if the heatsource met setpoint or + // maxed out + 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; + heatSources[i].disengageHeatSource(); + // and if there's a heat source that follows this heat source (regardless of + // lockout) that's able to come on, + if (heatSources[i].followedByHeatSource != NULL && + heatSources[i].followedByHeatSource->shutsOff() == false) + { + // turn it on + heatSources[i].followedByHeatSource->engageHeatSource(DRstatus); + } + // or if there heat source can't produce hotter water (i.e. it's maxed out) and + // the tank still isn't at setpoint. the compressor should get locked out when + // the maxedOut is true but have to run the resistance first during this + // timestep to make sure tank is above the max temperature for the compressor. + else if (heatSources[i].maxedOut() && heatSources[i].backupHeatSource != NULL) + { + + // Check that the backup isn't locked out or already engaged then it will + // heat or already heated on it's own. + if (!heatSources[i].backupHeatSource->toLockOrUnlock( + heatSourceAmbientT_C) && // If not locked out + !shouldDRLockOut(heatSources[i].backupHeatSource->typeOfHeatSource, + DRstatus) && // and not DR locked out + !heatSources[i].backupHeatSource->isEngaged()) + { // and not already engaged + + HeatSource* backupHeatSourcePtr = heatSources[i].backupHeatSource; + // turn it on + backupHeatSourcePtr->engageHeatSource(DRstatus); + // add heat if it hasn't heated up this whole minute already + if (backupHeatSourcePtr->runtime_min >= 0.) + { + addHeatParent(backupHeatSourcePtr, + heatSourceAmbientT_C, + minutesToRun - backupHeatSourcePtr->runtime_min); + } + } + } + } + } // heat source not engaged + } // end while iHS heat source + } + if (areAllHeatSourcesOff() == true) + { + isHeating = false; + } + // If there's extra user defined heat to add -> Add extra heat! + if (extraHeatDist_W != NULL && (*extraHeatDist_W).size() != 0) + { + addExtraHeat(*extraHeatDist_W); + updateSoCIfNecessary(); + } - bool doIP = (options & CSVOPT_IPUNITS) != 0; + // track the depressed local temperature + if (doTempDepression) + { + bool compressorRan = false; + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isEngaged() && !heatSources[i].isLockedOut() && + heatSources[i].depressesTemperature) + { + compressorRan = true; + } + } - fprintf(outFILE,"%s",preamble); + 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 + } - fprintf(outFILE,"%s","DRstatus"); + // 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); + } - for(int iHS = 0; iHS < getNumHeatSources(); iHS++) { - fprintf(outFILE,",h_src%dIn (Wh),h_src%dOut (Wh)",iHS + 1,iHS + 1); - } + // settle outputs - for(int iTC = 0; iTC < nTCouples; iTC++) { - fprintf(outFILE,",tcouple%d (%s)",iTC + 1,doIP ? "F" : "C"); - } + // outletTemp_C and standbyLosses_kWh are taken care of in updateTankTemps - fprintf(outFILE,",toutlet (%s)",doIP ? "F" : "C"); + // sum energyRemovedFromEnvironment_kWh for each heat source; + for (int i = 0; i < getNumHeatSources(); i++) + { + energyRemovedFromEnvironment_kWh += + (heatSources[i].energyOutput_kWh - heatSources[i].energyInput_kWh); + } - fprintf(outFILE,"\n"); + // cursory check for inverted temperature profile + if (tankTemps_C[getNumNodes() - 1] < tankTemps_C[0]) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The top of the tank is cooler than the bottom. \n"); + } + } - return 0; -} + // Handle DR timer + prevDRstatus = DRstatus; + // DR check for TOT to increase timer. + timerTOT += minutesPerStep; + // Restart the time if we're over the limit or the command is not a top off. + if ((DRstatus & DR_TOT) != 0 && timerTOT >= timerLimitTOT) + { + resetTopOffTimer(); + } + else if ((DRstatus & DR_TOO) == 0 && (DRstatus & DR_TOT) == 0) + { + resetTopOffTimer(); + } -int HPWH::WriteCSVRow(FILE* outFILE,const char* preamble,int nTCouples,int options) const { + if (simHasFailed) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The simulation has encountered an error. \n"); + } + return HPWH_ABORT; + } - bool doIP = (options & CSVOPT_IPUNITS) != 0; + if (hpwhVerbosity >= VRB_typical) + { + msg("Ending runOneStep. \n\n\n\n"); + } - fprintf(outFILE,"%s",preamble); + return 0; // successful completion of the step returns 0 +} // end runOneStep - fprintf(outFILE,"%i",prevDRstatus); +int HPWH::runNSteps(int N, + double* inletT_C, + double* drawVolume_L, + double* tankAmbientT_C, + double* heatSourceAmbientT_C, + DRMODES* DRstatus) +{ + // 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(getNumHeatSources()); + std::vector heatSources_energyInputs_SUM(getNumHeatSources()); + std::vector heatSources_energyOutputs_SUM(getNumHeatSources()); + + 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]); + + 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; + } - for(int iHS = 0; iHS < getNumHeatSources(); iHS++) { - fprintf(outFILE,",%0.2f,%0.2f",getNthHeatSourceEnergyInput(iHS,UNITS_KWH)*1000., - getNthHeatSourceEnergyOutput(iHS,UNITS_KWH)*1000.); - } + energyRemovedFromEnvironment_kWh_SUM += energyRemovedFromEnvironment_kWh; + standbyLosses_kWh_SUM += standbyLosses_kWh; - for(int iTC = 0; iTC < nTCouples; iTC++) { - fprintf(outFILE,",%0.2f",getNthSimTcouple(iTC + 1,nTCouples,doIP ? UNITS_F : UNITS_C)); - } - - if (options & HPWH::CSVOPT_IS_DRAWING) { - fprintf(outFILE,",%0.2f",doIP ? C_TO_F(outletTemp_C) : outletTemp_C); - } - else { - fprintf(outFILE,","); - } + outletTemp_C_AVG += outletTemp_C * drawVolume_L[i]; + totalDrawVolume_L += drawVolume_L[i]; - fprintf(outFILE,"\n"); + for (int j = 0; j < getNumHeatSources(); j++) + { + heatSources_runTimes_SUM[j] += getNthHeatSourceRunTime(j); + heatSources_energyInputs_SUM[j] += getNthHeatSourceEnergyInput(j); + heatSources_energyOutputs_SUM[j] += getNthHeatSourceEnergyOutput(j); + } - return 0; -} - -bool HPWH::isSetpointFixed() const{ - return setpointFixed; -} - -int HPWH::setSetpoint(double newSetpoint,UNITS units /*=UNITS_C*/) { - - double newSetpoint_C,temp; - string why; - if(units == UNITS_C) { - newSetpoint_C = newSetpoint; - } else if(units == UNITS_F) { - newSetpoint_C = F_TO_C(newSetpoint); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setSetpoint. \n"); - } - return HPWH_ABORT; - } - if(!isNewSetpointPossible(newSetpoint_C,temp,why)) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Unwilling to set this setpoint for the currently selected model, max setpoint is %f C. %s\n",temp,why.c_str()); - } - return HPWH_ABORT; - } - - setpoint_C = newSetpoint_C; - - return 0; -} - -double HPWH::getSetpoint(UNITS units /*=UNITS_C*/) const{ - if(units == UNITS_C) { - return setpoint_C; - } else if(units == UNITS_F) { - return C_TO_F(setpoint_C); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getSetpoint. \n"); - } - return HPWH_ABORT; - } -} - -double HPWH::getMaxCompressorSetpoint(UNITS units /*=UNITS_C*/) const { - - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Unit does not have a compressor \n"); - } - return HPWH_ABORT; - } - - double returnVal = heatSources[compressorIndex].maxSetpoint_C; - if(units == UNITS_C) { - returnVal = returnVal; - } else if(units == UNITS_F) { - returnVal = C_TO_F(returnVal); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getMaxCompressorSetpoint. \n"); - } - return HPWH_ABORT; - } - return returnVal; -} - -bool HPWH::isNewSetpointPossible(double newSetpoint,double& maxAllowedSetpoint,string& why,UNITS units /*=UNITS_C*/) const { - double newSetpoint_C; - double maxAllowedSetpoint_C = -273.15; - if(units == UNITS_C) { - newSetpoint_C = newSetpoint; - } else if(units == UNITS_F) { - newSetpoint_C = F_TO_C(newSetpoint); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for isNewSetpointPossible. \n"); - } - return false; - } - bool returnVal = false; - - if(isSetpointFixed()) { - returnVal = (newSetpoint == setpoint_C); - maxAllowedSetpoint_C = setpoint_C; - if(!returnVal) { - why = "The set point is fixed for the currently selected model."; - } - } else { - - if(hasACompressor()) { // If there's a compressor lets check the new setpoint against the compressor's max setpoint - - maxAllowedSetpoint_C = heatSources[compressorIndex].maxSetpoint_C - - heatSources[compressorIndex].secondaryHeatExchanger.hotSideTemperatureOffset_dC; - - if(newSetpoint_C > maxAllowedSetpoint_C && lowestElementIndex == -1) { - why = "The compressor cannot meet the setpoint temperature and there is no resistance backup."; - returnVal = false; - } else { - returnVal = true; - } - } - if(lowestElementIndex >= 0) { // If there's a resistance element lets check the new setpoint against the its max setpoint - maxAllowedSetpoint_C = heatSources[lowestElementIndex].maxSetpoint_C; - - if(newSetpoint_C > maxAllowedSetpoint_C) { - why = "The resistance elements cannot produce water this hot."; - returnVal = false; - } else { - returnVal = true; - } - } else if(lowestElementIndex == -1 && !hasACompressor()) { // There are no heat sources here! - if(hpwhModel == MODELS_StorageTank) { - returnVal = true; // The one pass the storage tank doesn't have any heating elements so sure change the setpoint it does nothing! - } else { - why = "There aren't any heat sources to check the new setpoint against!"; - returnVal = false; - } - } - } - - if(units == UNITS_C) { - maxAllowedSetpoint = maxAllowedSetpoint_C; - } else if(units == UNITS_F) { - maxAllowedSetpoint = C_TO_F(maxAllowedSetpoint_C); - } - return returnVal; -} - -double HPWH::calcSoCFraction(double tMains_C,double tMinUseful_C,double tMax_C) const { - // Note that volume is ignored in here since with even nodes it cancels out of the SoC fractional equation - if(tMains_C >= tMinUseful_C) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("tMains_C is greater than or equal tMinUseful_C. \n"); - } - return HPWH_ABORT; - } - if(tMinUseful_C > tMax_C) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("tMinUseful_C is greater tMax_C. \n"); - } - return HPWH_ABORT; - } - - double chargeEquivalent = 0.; - for(auto &T: tankTemps_C) { - chargeEquivalent += getChargePerNode(tMains_C,tMinUseful_C,T); - } - double maxSoC = getNumNodes() * getChargePerNode(tMains_C,tMinUseful_C,tMax_C); - return chargeEquivalent / maxSoC; -} - -double HPWH::getSoCFraction() const { - return currentSoCFraction; -} - -void HPWH::calcAndSetSoCFraction() { - double newSoCFraction = -1.; - - std::shared_ptr logicSoC = std::dynamic_pointer_cast(heatSources[compressorIndex].turnOnLogicSet[0]); - newSoCFraction = calcSoCFraction(logicSoC->getMainsT_C(),logicSoC->getTempMinUseful_C()); - - currentSoCFraction = newSoCFraction; -} - -double HPWH::getChargePerNode(double tCold,double tMix,double tHot) const { - if(tHot < tMix) { - return 0.; - } - return (tHot - tCold) / (tMix - tCold); -} - -double HPWH::getMinOperatingTemp(UNITS units /*=UNITS_C*/) const { - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("No compressor found in this HPWH. \n"); - } - return HPWH_ABORT; - } - if(units == UNITS_C) { - return heatSources[compressorIndex].minT; - } else if(units == UNITS_F) { - return C_TO_F(heatSources[compressorIndex].minT); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getMinOperatingTemp.\n"); - } - return HPWH_ABORT; - } -} - -int HPWH::resetTankToSetpoint() { - return setTankToTemperature(setpoint_C); -} - -int HPWH::setTankToTemperature(double temp_C) { - return setTankLayerTemperatures({temp_C}); -} + // 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 < getNumHeatSources(); j++) + { + msg("%f,%f,", getNthHeatSourceEnergyInput(j), getNthHeatSourceEnergyOutput(j)); + } -//----------------------------------------------------------------------------- -/// @brief Assigns new temps provided from a std::vector to tankTemps_C. -/// @param[in] setTankTemps new tank temps (arbitrary non-zero size) -/// @param[in] units temp units in setTankTemps (default = UNITS_C) -/// @return Success: 0; Failure: HPWH_ABORT -//----------------------------------------------------------------------------- -int HPWH::setTankLayerTemperatures(std::vector setTankTemps,const UNITS units) -{ - if((units != UNITS_C) && (units != UNITS_F)) - { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setSetpoint. \n"); - } - return HPWH_ABORT; - } - - std::size_t numSetNodes = setTankTemps.size(); - if(numSetNodes == 0) - { - if(hpwhVerbosity >= VRB_reluctant) { - msg("No temperatures provided.\n"); - } - return HPWH_ABORT; - } - - // convert setTankTemps to °C, if necessary - if(units == UNITS_F) - for(auto &T: setTankTemps) - T = F_TO_C(T); - - // set node temps - if(!resampleIntensive(tankTemps_C,setTankTemps)) - return HPWH_ABORT; - - return 0; -} - -void HPWH::getTankTemps(std::vector &tankTemps) { - tankTemps = tankTemps_C; -} - -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 < getNumHeatSources(); i++) { - if(heatSources[i].isACompressor()) { - heatSources[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*/,bool forceChange /*=false*/) { - //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,forceChange); - setUA(tankUA_kJperHrC / oldA * getTankSurfaceArea(UNITS_FT2),UNITS_kJperHrC); - return 0; -} - -/*static*/ double HPWH::getTankSurfaceArea(double vol,UNITS volUnits/*=UNITS_L*/,UNITS surfAUnits /*=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. - // Corresponds to the inner tank with volume tankVolume_L with the assumption that the aspect ratio is the same - // as the outer dimenisions of the whole unit. - double radius = getTankRadius(vol,volUnits,UNITS_FT); - - double value = 2. * 3.14159 * pow(radius,2) * (ASPECTRATIO + 1.); - - if(value >= 0.) - { - if(surfAUnits == UNITS_M2) - value = FT2_TO_M2(value); - else if(surfAUnits != UNITS_FT2) - value = -1.; - } - return value; -} - -double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/) const { - // 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. - // Corresponds to the inner tank with volume tankVolume_L with the assumption that the aspect ratio is the same - // as the outer dimenisions of the whole unit. - double value = getTankSurfaceArea(tankVolume_L,UNITS_L,units); - if(value < 0.) - { - if(hpwhVerbosity >= VRB_reluctant) - msg("Incorrect unit specification for getTankSurfaceArea. \n"); - value = HPWH_ABORT; - } - return value; -} - -/*static*/ double HPWH::getTankRadius(double vol,UNITS volUnits/*=UNITS_L*/,UNITS radiusUnits /*=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, - // assumes the aspect ratio for the outer measurements is the same is the actual tank. - double volft3 = - volUnits == UNITS_L ? L_TO_FT3(vol) - : volUnits == UNITS_GAL ? L_TO_FT3(GAL_TO_L(vol)) - : -1.; - - double value = -1.; - if(volft3 >= 0.) - { - value = pow(volft3 / 3.14159 / ASPECTRATIO,1. / 3.); - if(radiusUnits == UNITS_M) - value = FT_TO_M(value); - else if(radiusUnits != UNITS_FT) - value = -1.; - } - return value; -} - -double HPWH::getTankRadius(UNITS units /*=UNITS_FT*/) const{ - // 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, - // assumes the aspect ratio for the outer measurements is the same is the actual tank. - - double value = getTankRadius(tankVolume_L,UNITS_L,units); - - if(value < 0.) - { - if(hpwhVerbosity >= VRB_reluctant) - msg("Incorrect unit specification for getTankRadius. \n"); - value = HPWH_ABORT; - } - return value; -} - - -bool HPWH::isTankSizeFixed() const{ - return tankSizeFixed; -} - -int HPWH::setTankSize(double HPWH_size,UNITS units /*=UNITS_L*/,bool forceChange /*=false*/) { - if(isTankSizeFixed() && !forceChange) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not change the tank size for your currently selected model. \n"); - } - return HPWH_ABORT; - } - 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; - } - } - - calcSizeConstants(); - - 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::setFittingsUA(double UA,UNITS units /*=UNITS_kJperHrC*/) { - if(units == UNITS_kJperHrC) { - fittingsUA_kJperHrC = UA; - } else if(units == UNITS_BTUperHrF) { - fittingsUA_kJperHrC = UAf_TO_UAc(UA); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for setFittingsUA. \n"); - } - return HPWH_ABORT; - } - return 0; -} -int HPWH::getFittingsUA(double& UA,UNITS units /*=UNITS_kJperHrC*/) const { - UA = fittingsUA_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::setExternalInletHeightByFraction(double fractionalHeight) { - return setExternalPortHeightByFraction(fractionalHeight,1); -} -int HPWH::setExternalOutletHeightByFraction(double fractionalHeight) { - return setExternalPortHeightByFraction(fractionalHeight,2); -} - -int HPWH::setExternalPortHeightByFraction(double fractionalHeight,int whichExternalPort) { - if(!hasExternalHeatSource()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Does not have an external heat source \n"); - } - return HPWH_ABORT; - } - - int returnVal = 0; - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) { - if(whichExternalPort == 1) { - returnVal = setNodeNumFromFractionalHeight(fractionalHeight,heatSources[i].externalInletHeight); - } else { - returnVal = setNodeNumFromFractionalHeight(fractionalHeight,heatSources[i].externalOutletHeight); - } - - if(returnVal == HPWH_ABORT) { - return returnVal; - } - } - } - return returnVal; -} - -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(getNumNodes() * fractionalHeight); - inletNum = (node == getNumNodes()) ? getIndexTopNode() : node; - - return 0; -} - -int HPWH::getExternalInletHeight() const { - if(!hasExternalHeatSource()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Does not have an external heat source \n"); - } - return HPWH_ABORT; - } - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) { - return heatSources[i].externalInletHeight; // Return the first one since all external sources have some ports - } - } - return HPWH_ABORT; -} -int HPWH::getExternalOutletHeight() const { - if(!hasExternalHeatSource()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Does not have an external heat source \n"); - } - return HPWH_ABORT; - } - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) { - return heatSources[i].externalOutletHeight; // Return the first one since all external sources have some ports - } - } - return HPWH_ABORT; -} - -int HPWH::setTimerLimitTOT(double limit_min) { - if(limit_min > 24.*60. || limit_min < 0.) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Out of bounds time limit for setTimerLimitTOT \n"); - } - return HPWH_ABORT; - } - - timerLimitTOT = limit_min; - - return 0; -} - -double HPWH::getTimerLimitTOT_minute() const { - return timerLimitTOT; -} - -int HPWH::getInletHeight(int whichInlet) const { - 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; -} - -bool HPWH::hasEnteringWaterHighTempShutOff(int heatSourceIndex) { - bool retVal = false; - if(heatSourceIndex >= getNumHeatSources() || heatSourceIndex < 0) { - return retVal; - } - if(heatSources[heatSourceIndex].shutOffLogicSet.size() == 0) { - return retVal; - } - - for(std::shared_ptr shutOffLogic : heatSources[heatSourceIndex].shutOffLogicSet) { - if(shutOffLogic->getIsEnteringWaterHighTempShutoff()) { - retVal = true; - break; - } - } - return retVal; -} - -int HPWH::setEnteringWaterHighTempShutOff(double highTemp,bool tempIsAbsolute,int heatSourceIndex,UNITS unit /*=UNITS_C*/) { - if(!hasEnteringWaterHighTempShutOff(heatSourceIndex)) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to acess a heating logic that does not exist. \n"); - } - return HPWH_ABORT; - } - - double highTemp_C; - if(unit == UNITS_C) { - highTemp_C = highTemp; - } else if(unit == UNITS_F) { - highTemp_C = F_TO_C(highTemp); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for set Entering Water High Temp Shut Off. \n"); - } - return HPWH_ABORT; - } - - bool highTempIsNotValid = false; - if(tempIsAbsolute) { - // check differnce with setpoint - if(setpoint_C - highTemp_C < MINSINGLEPASSLIFT) { - highTempIsNotValid = true; - } - } else { - if(highTemp_C < MINSINGLEPASSLIFT) { - highTempIsNotValid = true; - } - } - if(highTempIsNotValid) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("High temperature shut off is too close to the setpoint, excpected a minimum difference of %.2lf.\n", - MINSINGLEPASSLIFT); - } - return HPWH_ABORT; - } - - for(std::shared_ptr shutOffLogic : heatSources[heatSourceIndex].shutOffLogicSet) { - if(shutOffLogic->getIsEnteringWaterHighTempShutoff()) { - std::dynamic_pointer_cast(shutOffLogic)->setDecisionPoint(highTemp_C,tempIsAbsolute); - break; - } - } - return 0; -} - -int HPWH::setTargetSoCFraction(double target) { - if(!isSoCControlled()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not set target state of charge if HPWH is not using state of charge controls."); - } - return HPWH_ABORT; - } - if(target < 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not set a negative target state of charge."); - } - return HPWH_ABORT; - } - - for(int i = 0; i < getNumHeatSources(); i++) { - for(std::shared_ptr logic : heatSources[i].shutOffLogicSet) { - if(!logic->getIsEnteringWaterHighTempShutoff()) { - logic->setDecisionPoint(target); - } - } - for(std::shared_ptr logic : heatSources[i].turnOnLogicSet) { - logic->setDecisionPoint(target); - } - } - return 0; -} - -bool HPWH::isSoCControlled() const { - return usesSoCLogic; -} - -bool HPWH::canUseSoCControls() { - bool retVal = true; - if(getCompressorCoilConfig() != HPWH::HeatSource::CONFIG_EXTERNAL) { - retVal = false; - } - return retVal; -} - -int HPWH::switchToSoCControls(double targetSoC,double hysteresisFraction /*= 0.05*/,double tempMinUseful /*= 43.333*/,bool constantMainsT /*= false*/, - double mainsT /*= 18.333*/,UNITS tempUnit /*= UNITS_C*/) { - if(!canUseSoCControls()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Cannot set up state of charge controls for integrated or wrapped HPWHs.\n"); - } - return HPWH_ABORT; - } - - double tempMinUseful_C,mainsT_C; - if(tempUnit == UNITS_C) { - tempMinUseful_C = tempMinUseful; - mainsT_C = mainsT; - } else if(tempUnit == UNITS_F) { - tempMinUseful_C = F_TO_C(tempMinUseful); - mainsT_C = F_TO_C(mainsT); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for set Enterinh Water High Temp Shut Off.\n"); - } - return HPWH_ABORT; - } - - if(mainsT_C >= tempMinUseful_C) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("The mains temperature can't be equal to or greater than the minimum useful temperature.\n"); - } - return HPWH_ABORT; - } - - for(int i = 0; i < getNumHeatSources(); i++) { - heatSources[i].clearAllTurnOnLogic(); - - heatSources[i].shutOffLogicSet.erase(std::remove_if(heatSources[i].shutOffLogicSet.begin(), - heatSources[i].shutOffLogicSet.end(), - [&](const auto logic)-> bool - { return !logic->getIsEnteringWaterHighTempShutoff(); }), - heatSources[i].shutOffLogicSet.end()); - - heatSources[i].shutOffLogicSet.push_back(shutOffSoC("SoC Shut Off",targetSoC,hysteresisFraction,tempMinUseful_C,constantMainsT,mainsT_C)); - heatSources[i].turnOnLogicSet.push_back(turnOnSoC("SoC Turn On",targetSoC,hysteresisFraction,tempMinUseful_C,constantMainsT,mainsT_C)); - } - - usesSoCLogic = true; - - return 0; -} - -std::shared_ptr HPWH::turnOnSoC(string desc,double targetSoC,double hystFract,double tempMinUseful_C, - bool constantMainsT,double mainsT_C) { - return std::make_shared(desc,targetSoC,this,-hystFract,tempMinUseful_C,constantMainsT,mainsT_C); -} - -std::shared_ptr HPWH::shutOffSoC(string desc,double targetSoC,double hystFract,double tempMinUseful_C, - bool constantMainsT,double mainsT_C) { - return std::make_shared(desc,targetSoC,this,hystFract,tempMinUseful_C,constantMainsT,mainsT_C,std::greater()); -} + std::vector displayTemps_C(10); + resampleIntensive(displayTemps_C, tankTemps_C); + bool first = true; + for (auto& displayTemp : displayTemps_C) + { + if (first) + first = false; + else + msg(","); + + msg("%f", displayTemp); + } -//----------------------------------------------------------------------------- -/// @brief Builds a vector of logic node weights referred to a fixed number of -/// nodes given by LOGIC_NODE_SIZE. -/// @param[in] bottomFraction Lower bounding fraction (0 to 1) -/// @param[in] topFraction Upper bounding fraction (0 to 1) -/// @return vector of node weights -//----------------------------------------------------------------------------- -std::vector HPWH::getNodeWeightRange(double bottomFraction, double topFraction) { - std::vector nodeWeights; - if (topFraction < bottomFraction) std::swap(bottomFraction, topFraction); - auto bottomIndex = static_cast(bottomFraction * LOGIC_NODE_SIZE); - auto topIndex = static_cast(topFraction * LOGIC_NODE_SIZE); - for (auto index = bottomIndex; index < topIndex; ++index) { - nodeWeights.emplace_back(static_cast(index) + 1); - } - return nodeWeights; -} + for (int k = 1; k < 7; ++k) + { + if (first) + first = false; + else + msg(","); -std::shared_ptr HPWH::wholeTank(double decisionPoint,const UNITS units /* = UNITS_C */, const bool absolute /* = false */) { - std::vector nodeWeights = getNodeWeightRange(0., 1.); - double decisionPoint_C = convertTempToC(decisionPoint,units,absolute); - return std::make_shared("whole tank",nodeWeights,decisionPoint_C,this,absolute); -} + msg("%f", getNthSimTcouple(k, 6)); + } -std::shared_ptr HPWH::topThird(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(2./3., 1.); - return std::make_shared("top third",nodeWeights,decisionPoint,this); -} + msg("\n"); + } + } + // finish weighted avg. of outlet temp by dividing by the total drawn volume + outletTemp_C_AVG /= totalDrawVolume_L; -std::shared_ptr HPWH::topThird_absolute(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(2./3., 1.); - return std::make_shared("top third absolute",nodeWeights,decisionPoint,this,true); -} + // 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; -std::shared_ptr HPWH::secondThird(double decisionPoint,const UNITS units /* = UNITS_C */, const bool absolute /* = false */) { - std::vector nodeWeights = getNodeWeightRange(1./3., 2./3.); - double decisionPoint_C = convertTempToC(decisionPoint,units,absolute); - return std::make_shared("second third",nodeWeights,decisionPoint_C,this,absolute); -} + for (int i = 0; i < getNumHeatSources(); i++) + { + heatSources[i].runtime_min = heatSources_runTimes_SUM[i]; + heatSources[i].energyInput_kWh = heatSources_energyInputs_SUM[i]; + heatSources[i].energyOutput_kWh = heatSources_energyOutputs_SUM[i]; + } -std::shared_ptr HPWH::bottomThird(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./3.); - return std::make_shared("bottom third",nodeWeights,decisionPoint,this); + if (hpwhVerbosity >= VRB_typical) + { + msg("Ending runNSteps. \n\n\n\n"); + } + return 0; } -std::shared_ptr HPWH::bottomSixth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./6.); - return std::make_shared("bottom sixth",nodeWeights,decisionPoint,this); -} +void HPWH::addHeatParent(HeatSource* heatSourcePtr, + double heatSourceAmbientT_C, + double minutesToRun) +{ -std::shared_ptr HPWH::bottomSixth_absolute(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./6.); - return std::make_shared("bottom sixth absolute",nodeWeights,decisionPoint,this,true); -} + double tempSetpoint_C = -273.15; -std::shared_ptr HPWH::secondSixth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(1./6., 2./6.); - return std::make_shared("second sixth",nodeWeights,decisionPoint,this); -} + // Check the air temprature and setpoint against maxOut_at_LowT + if (heatSourcePtr->isACompressor()) + { + if (heatSourceAmbientT_C <= heatSourcePtr->maxOut_at_LowT.airT_C && + setpoint_C >= heatSourcePtr->maxOut_at_LowT.outT_C) + { + tempSetpoint_C = setpoint_C; // Store setpoint + setSetpoint(heatSourcePtr->maxOut_at_LowT + .outT_C); // Reset to new setpoint as this is used in the add heat calc + } + } + // and add heat if it is + heatSourcePtr->addHeat(heatSourceAmbientT_C, minutesToRun); -std::shared_ptr HPWH::thirdSixth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(2./6., 3./6.); - return std::make_shared("third sixth",nodeWeights,decisionPoint,this); + // Change the setpoint back to what it was pre-compressor depression + if (tempSetpoint_C > -273.15) + { + setSetpoint(tempSetpoint_C); + } } -std::shared_ptr HPWH::fourthSixth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(3./6., 4./6.); - return std::make_shared("fourth sixth",nodeWeights,decisionPoint,this); +void HPWH::setVerbosity(VERBOSITY hpwhVrb) { hpwhVerbosity = hpwhVrb; } +void HPWH::setMessageCallback(void (*callbackFunc)(const string message, void* contextPtr), + void* contextPtr) +{ + messageCallback = callbackFunc; + messageCallbackContextPtr = contextPtr; } - -std::shared_ptr HPWH::fifthSixth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(4./6., 5./6.); - return std::make_shared("fifth sixth",nodeWeights,decisionPoint,this); +void HPWH::sayMessage(const string message) const +{ + if (messageCallback != NULL) + { + (*messageCallback)(message, messageCallbackContextPtr); + } + else + { + std::cout << message; + } } - -std::shared_ptr HPWH::topSixth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(5./6., 1.); - return std::make_shared("top sixth",nodeWeights,decisionPoint,this); +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]; -std::shared_ptr HPWH::bottomHalf(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./2.); - return std::make_shared("bottom half",nodeWeights,decisionPoint,this); -} + const char* p; + if (ap) + { +#if defined(_MSC_VER) + vsprintf_s(outputString, fmt, ap); +#else + vsnprintf(outputString, MAXOUTSTRING, fmt, ap); +#endif + p = outputString; + } + else + { + p = fmt; + } + sayMessage(p); +} // HPWH::msgV -std::shared_ptr HPWH::bottomTwelfth(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./12.); - return std::make_shared("bottom twelfth",nodeWeights,decisionPoint,this); -} +void HPWH::printHeatSourceInfo() +{ + std::stringstream ss; + double runtime = 0, outputVar = 0; -std::shared_ptr HPWH::standby(double decisionPoint) { - std::vector nodeWeights; - nodeWeights.emplace_back(LOGIC_NODE_SIZE + 1); // uses very top computation node - return std::make_shared("standby",nodeWeights,decisionPoint,this); -} + 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; -std::shared_ptr HPWH::topNodeMaxTemp(double decisionPoint) { - std::vector nodeWeights; - nodeWeights.emplace_back(LOGIC_NODE_SIZE + 1); // uses very top computation node - return std::make_shared("top node",nodeWeights,decisionPoint,this,true,std::greater()); -} + for (int i = 0; i < getNumHeatSources(); i++) + { + ss << "input energy kwh: " << std::setw(7) << getNthHeatSourceEnergyInput(i) << "\t"; + } + ss << endl; -std::shared_ptr HPWH::bottomNodeMaxTemp(double decisionPoint,bool isEnteringWaterHighTempShutoff /*=false*/) { - std::vector nodeWeights; - nodeWeights.emplace_back(0); // uses very bottom computation node - return std::make_shared("bottom node",nodeWeights,decisionPoint,this,true,std::greater(),isEnteringWaterHighTempShutoff); -} + 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; -std::shared_ptr HPWH::bottomTwelfthMaxTemp(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./12.); - return std::make_shared("bottom twelfth",nodeWeights,decisionPoint,this,true,std::greater()); -} + for (int i = 0; i < getNumHeatSources(); i++) + { + ss << "output energy kwh: " << std::setw(7) << getNthHeatSourceEnergyOutput(i, UNITS_KWH) + << "\t"; + } + ss << endl; -std::shared_ptr HPWH::topThirdMaxTemp(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(2./3., 1.); - return std::make_shared("top third",nodeWeights,decisionPoint,this,true,std::greater()); -} + 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; -std::shared_ptr HPWH::bottomSixthMaxTemp(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./6.); - return std::make_shared("bottom sixth",nodeWeights,decisionPoint,this,true,std::greater()); -} + for (int i = 0; i < getNumHeatSources(); i++) + { + ss << "run time min: " << std::setw(7) << getNthHeatSourceRunTime(i) << "\t\t"; + } + ss << endl << endl << endl; -std::shared_ptr HPWH::secondSixthMaxTemp(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(1./6., 2./6.); - return std::make_shared("second sixth",nodeWeights,decisionPoint,this,true,std::greater()); + msg(ss.str().c_str()); } -std::shared_ptr HPWH::fifthSixthMaxTemp(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(4./6., 5./6.); - return std::make_shared("top sixth",nodeWeights,decisionPoint,this,true,std::greater()); -} +void HPWH::printTankTemps() +{ + std::stringstream ss; -std::shared_ptr HPWH::topSixthMaxTemp(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(5./6., 1.); - return std::make_shared("top sixth",nodeWeights,decisionPoint,this,true,std::greater()); -} + ss << std::left; -std::shared_ptr HPWH::largeDraw(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./4.); - return std::make_shared("large draw",nodeWeights,decisionPoint,this,true); -} + for (int i = 0; i < getNumNodes(); i++) + { + ss << std::setw(9) << getTankNodeTemp(i) << " "; + } + ss << endl; -std::shared_ptr HPWH::largerDraw(double decisionPoint) { - std::vector nodeWeights = getNodeWeightRange(0., 1./2.); - return std::make_shared("larger draw",nodeWeights,decisionPoint,this,true); + msg(ss.str().c_str()); } -void HPWH::setNumNodes(const std::size_t num_nodes) +// public members to write to CSV file +int HPWH::WriteCSVHeading(FILE* outFILE, const char* preamble, int nTCouples, int options) const { - tankTemps_C.resize(num_nodes); - nextTankTemps_C.resize(num_nodes); -} - -int HPWH::getNumNodes() const -{ - return static_cast(tankTemps_C.size()); -} - -int HPWH::getIndexTopNode() const -{ - return getNumNodes() - 1; -} - -double HPWH::getTankNodeTemp(int nodeNum,UNITS units /*=UNITS_C*/) const { - if(tankTemps_C.empty()) { - 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)) { can't happen? - // 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); - } - double beginFraction = static_cast(iTCouple - 1.) / static_cast(nTCouple); - double endFraction = static_cast(iTCouple) / static_cast(nTCouple); - - double simTcoupleTemp_C = getResampledValue(tankTemps_C,beginFraction,endFraction); - if(units == UNITS_C) { - return simTcoupleTemp_C; - } else if(units == UNITS_F) { - return C_TO_F(simTcoupleTemp_C); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getNthSimTcouple. \n"); - } - return double(HPWH_ABORT); - } - -} - -int HPWH::getNumHeatSources() const { - return static_cast(heatSources.size()); -} - -int HPWH::getCompressorIndex() const { - return compressorIndex; -} - -int HPWH::getNumResistanceElements() const { - int count = 0; - for(int i = 0; i < getNumHeatSources(); i++) { - count += heatSources[i].isAResistance() ? 1 : 0; - } - return count; -} - -double HPWH::getCompressorCapacity(double airTemp /*=19.722*/,double inletTemp /*=14.444*/,double outTemp /*=57.222*/, - UNITS pwrUnit /*=UNITS_KW*/,UNITS tempUnit /*=UNITS_C*/) { - // calculate capacity btu/hr, input btu/hr, and cop - double capTemp_BTUperHr,inputTemp_BTUperHr,copTemp; // temporary variables - double airTemp_C,inletTemp_C,outTemp_C; - - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model does not have a compressor. \n"); - } - return double(HPWH_ABORT); - } - - if(tempUnit == UNITS_C) { - airTemp_C = airTemp; - inletTemp_C = inletTemp; - outTemp_C = outTemp; - } else if(tempUnit == UNITS_F) { - airTemp_C = F_TO_C(airTemp); - inletTemp_C = F_TO_C(inletTemp); - outTemp_C = F_TO_C(outTemp); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for temperatures in getCompressorCapacity. \n"); - } - return double(HPWH_ABORT); - } - - if(airTemp_C < heatSources[compressorIndex].minT || airTemp_C > heatSources[compressorIndex].maxT) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("The compress does not operate at the specified air temperature. \n"); - } - return double(HPWH_ABORT); - } - - double maxAllowedSetpoint_C = heatSources[compressorIndex].maxSetpoint_C - - heatSources[compressorIndex].secondaryHeatExchanger.hotSideTemperatureOffset_dC; - if(outTemp_C > maxAllowedSetpoint_C) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Inputted outlet temperature of the compressor is higher than can be produced."); - } - return double(HPWH_ABORT); - } - - if(heatSources[compressorIndex].isExternalMultipass()) { - double averageTemp_C = (outTemp_C + inletTemp_C) / 2.; - heatSources[compressorIndex].getCapacityMP(airTemp_C,averageTemp_C,inputTemp_BTUperHr,capTemp_BTUperHr,copTemp); - } else { - heatSources[compressorIndex].getCapacity(airTemp_C,inletTemp_C,outTemp_C,inputTemp_BTUperHr,capTemp_BTUperHr,copTemp); - } - - double outputCapacity = capTemp_BTUperHr; - if(pwrUnit == UNITS_KW) { - outputCapacity = BTU_TO_KWH(capTemp_BTUperHr); - } else if(pwrUnit != UNITS_BTUperHr) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for capacity in getCompressorCapacity. \n"); - } - return double(HPWH_ABORT); - } - - return outputCapacity; -} - -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 >= getNumHeatSources() || 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 heatSources[N].energyInput_kWh; - } else if(units == UNITS_BTU) { - return KWH_TO_BTU(heatSources[N].energyInput_kWh); - } else if(units == UNITS_KJ) { - return KWH_TO_KJ(heatSources[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 >= getNumHeatSources() || 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 heatSources[N].energyOutput_kWh; - } else if(units == UNITS_BTU) { - return KWH_TO_BTU(heatSources[N].energyOutput_kWh); - } else if(units == UNITS_KJ) { - return KWH_TO_KJ(heatSources[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 >= getNumHeatSources() || 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 heatSources[N].runtime_min; -} - - -int HPWH::isNthHeatSourceRunning(int N) const { - if(N >= getNumHeatSources() || 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(heatSources[N].isEngaged()) { - return 1; - } else { - return 0; - } -} - - -HPWH::HEATSOURCE_TYPE HPWH::getNthHeatSourceType(int N) const { - if(N >= getNumHeatSources() || N < 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("You have attempted to access the type of a heat source that does not exist. \n"); - } - return HEATSOURCE_TYPE(HPWH_ABORT); - } - return heatSources[N].typeOfHeatSource; -} - - -double HPWH::getTankSize(UNITS units /*=UNITS_L*/) 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::getCondenserWaterInletTemp(UNITS units /*=UNITS_C*/) const { - if(units == UNITS_C) { - return condenserInlet_C; - } else if(units == UNITS_F) { - return C_TO_F(condenserInlet_C); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getCondenserWaterInletTemp. \n"); - } - return double(HPWH_ABORT); - } -} - -double HPWH::getCondenserWaterOutletTemp(UNITS units /*=UNITS_C*/) const { - if(units == UNITS_C) { - return condenserOutlet_C; - } else if(units == UNITS_F) { - return C_TO_F(condenserOutlet_C); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getCondenserWaterInletTemp. \n"); - } - return double(HPWH_ABORT); - } -} - -double HPWH::getExternalVolumeHeated(UNITS units /*=UNITS_L*/) const { - if(units == UNITS_L) { - return externalVolumeHeated_L; - } else if(units == UNITS_GAL) { - return L_TO_GAL(externalVolumeHeated_L); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getExternalVolumeHeated. \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 < getNumNodes(); i++) { - avgTemp += tankTemps_C[i]; - } - avgTemp /= getNumNodes(); - - double totalHeat = avgTemp * DENSITYWATER_kgperL * CPWATER_kJperkgC * tankVolume_L; - return totalHeat; -} - -double HPWH::getLocationTemp_C() const { - return locationTemperature_C; -} - -int HPWH::getHPWHModel() const { - return hpwhModel; -} -int HPWH::getCompressorCoilConfig() const { - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model does not have a compressor. \n"); - } - return HPWH_ABORT; - } - return heatSources[compressorIndex].configuration; -} -bool HPWH::isCompressorMultipass() const { - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model does not have a compressor. \n"); - } - return HPWH_ABORT; - } - return heatSources[compressorIndex].isMultipass; -} -bool HPWH::isCompressoExternalMultipass() const { - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model does not have a compressor. \n"); - } - return HPWH_ABORT; - } - return heatSources[compressorIndex].isExternalMultipass(); -} - -bool HPWH::hasACompressor() const { - return compressorIndex >= 0; -} - - -bool HPWH::hasExternalHeatSource() const { - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) { - return true; - } - } - return false; -} - -double HPWH::getExternalMPFlowRate(UNITS units /*=UNITS_GPM*/) const { - if(!isCompressoExternalMultipass()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Does not have an external multipass heat source \n"); - } - return HPWH_ABORT; - } - - if(units == HPWH::UNITS_LPS) { - return heatSources[compressorIndex].mpFlowRate_LPS; - } else if(units == HPWH::UNITS_GPM) { - return LPS_TO_GPM(heatSources[compressorIndex].mpFlowRate_LPS); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getExternalMPFlowRate. \n"); - } - return (double)HPWH_ABORT; - } -} - -double HPWH::getCompressorMinRuntime(UNITS units /*=UNITS_MIN*/) const { - - if(hasACompressor()) { - double min_minutes = 10.; - - if(units == UNITS_MIN) { - return min_minutes; - } else if(units == UNITS_SEC) { - return MIN_TO_SEC(min_minutes); - } else if(units == UNITS_HR) { - return MIN_TO_HR(min_minutes); - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for getCompressorMinRunTime. \n"); - } - return (double)HPWH_ABORT; - } - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("This HPWH has no compressor. \n"); - } - return (double)HPWH_ABORT; - } -} - -int HPWH::getSizingFractions(double& aquaFract,double& useableFract) const { - double aFract = 1.; - double useFract = 1.; - - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model does not have a compressor. \n"); - } - return HPWH_ABORT; - } else if(usesSoCLogic) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model uses SOC control logic and does not have a definition for sizing fractions. \n"); - } - return HPWH_ABORT; - } - - // Every compressor must have at least one on logic - for(std::shared_ptr onLogic : heatSources[compressorIndex].turnOnLogicSet) { - double tempA; - - if(hpwhVerbosity >= VRB_emetic) { - msg("\tturnon logic: %s ",onLogic->description.c_str()); - } - tempA = onLogic->nodeWeightAvgFract(); // if standby logic will return 1 - aFract = tempA < aFract ? tempA : aFract; - } - aquaFract = aFract; - - // Compressors don't need to have an off logic - if(heatSources[compressorIndex].shutOffLogicSet.size() != 0) { - for(std::shared_ptr offLogic : heatSources[compressorIndex].shutOffLogicSet) { - - double tempUse; - - if(hpwhVerbosity >= VRB_emetic) { - msg("\tshutsOff logic: %s ",offLogic->description.c_str()); - } - if(offLogic->description == "large draw" || offLogic->description == "larger draw") { - tempUse = 1.; // These logics are just for checking if there's a big draw to switch to RE - } else { - tempUse = 1. - offLogic->nodeWeightAvgFract(); - } - useFract = tempUse < useFract ? tempUse : useFract; // Will return the smallest case of the useable fraction for multiple off logics - } - useableFract = useFract; - } else { - if(hpwhVerbosity >= VRB_emetic) { - msg("\no shutoff logics present"); - } - useableFract = 1.; - } - - // Check if double's are approximately equally and adjust the relationship so it follows the relationship we expect. - // The tolerance plays with 0.1 mm in position if the tank is 1m tall... - double temp = 1. - useableFract; - if(aboutEqual(aquaFract,temp)) { - useableFract = 1. - aquaFract + TOL_MINVALUE; - } - - return 0; -} - -bool HPWH::isHPWHScalable() const { - return canScale; -} - -int HPWH::setScaleHPWHCapacityCOP(double scaleCapacity /*=1.0*/,double scaleCOP /*=1.0*/) { - if(!isHPWHScalable()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not scale the HPWH Capacity or COP \n"); - } - return HPWH_ABORT; - } - if(!hasACompressor()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Current model does not have a compressor. \n"); - } - return HPWH_ABORT; - } - if(scaleCapacity <= 0 || scaleCOP <= 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not scale the HPWH Capacity or COP to 0 or less than 0 \n"); - } - return HPWH_ABORT; - } - - for(auto &perfP : heatSources[compressorIndex].perfMap) { - if(scaleCapacity != 1.) { - std::transform(perfP.inputPower_coeffs.begin(),perfP.inputPower_coeffs.end(),perfP.inputPower_coeffs.begin(), - std::bind(std::multiplies(),std::placeholders::_1,scaleCapacity)); - } - if(scaleCOP != 1.) { - std::transform(perfP.COP_coeffs.begin(),perfP.COP_coeffs.end(),perfP.COP_coeffs.begin(), - std::bind(std::multiplies(),std::placeholders::_1,scaleCOP)); - } - } - - return 0; -} - -int HPWH::setCompressorOutputCapacity(double newCapacity,double airTemp /*=19.722*/, - double inletTemp /*=14.444*/,double outTemp /*=57.222*/, - UNITS pwrUnit /*=UNITS_KW*/,UNITS tempUnit /*=UNITS_C*/) { - - double oldCapacity = getCompressorCapacity(airTemp,inletTemp,outTemp,pwrUnit,tempUnit); - if(oldCapacity == double(HPWH_ABORT)) { - return HPWH_ABORT; - } - - double scale = newCapacity / oldCapacity; - return setScaleHPWHCapacityCOP(scale,1.); // Scale the compressor capacity -} - - -int HPWH::setResistanceCapacity(double power,int which /*=-1*/,UNITS pwrUnit /*=UNITS_KW*/) { - - //Input checks - if(!isHPWHScalable()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not scale the resistance elements \n"); - } - return HPWH_ABORT; - } - if(getNumResistanceElements() == 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("There are no resistance elements to set capacity for \n"); - } - return HPWH_ABORT; - } - if(which < -1 || which > getNumResistanceElements() - 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Out of bounds value for which in setResistanceCapacity()\n"); - } - return HPWH_ABORT; - } - if(power < 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Can not have a negative input power \n"); - } - return HPWH_ABORT; - } - //Unit conversion - double watts; - if(pwrUnit == UNITS_KW) { - watts = power * 1000; // kW to W - } else if(pwrUnit == UNITS_BTUperHr) { - watts = BTU_TO_KWH(power) * 1000; // BTU to kW then kW to W - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for capacity in setResistanceCapacity. \n"); - } - return HPWH_ABORT; - } - - //Whew so many checks... - if(which == -1) { - // Just get all the elements - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isAResistance()) { - heatSources[i].changeResistanceWatts(watts); - } - } - } else { - heatSources[resistanceHeightMap[which].index].changeResistanceWatts(watts); - - // Then check for repeats in the position - int pos = resistanceHeightMap[which].position; - for(int i = 0; i < getNumResistanceElements(); i++) { - if(which != i && resistanceHeightMap[i].position == pos) { - heatSources[resistanceHeightMap[i].index].changeResistanceWatts(watts); - } - } - } - - return 0; -} - -double HPWH::getResistanceCapacity(int which /*=-1*/,UNITS pwrUnit /*=UNITS_KW*/) { - - //Input checks - if(getNumResistanceElements() == 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("There are no resistance elements to return capacity for \n"); - } - return HPWH_ABORT; - } - if(which < -1 || which > getNumResistanceElements() - 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Out of bounds value for which in getResistanceCapacity()\n"); - } - return HPWH_ABORT; - } - - double returnPower = 0; - if(which == -1) { - // Just get all the elements - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isAResistance()) { - returnPower += heatSources[i].perfMap[0].inputPower_coeffs[0]; - } - } - } else { - // get the power from "which" element by height - returnPower += heatSources[resistanceHeightMap[which].index].perfMap[0].inputPower_coeffs[0]; - - // Then check for repeats in the position - int pos = resistanceHeightMap[which].position; - for(int i = 0; i < getNumResistanceElements(); i++) { - if(which != i && resistanceHeightMap[i].position == pos) { - returnPower += heatSources[resistanceHeightMap[i].index].perfMap[0].inputPower_coeffs[0]; - } - } - } - - //Unit conversion - if(pwrUnit == UNITS_KW) { - returnPower /= 1000.; // W to KW - } else if(pwrUnit == UNITS_BTUperHr) { - returnPower = KWH_TO_BTU(returnPower/1000.); // W to BTU/hr - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect unit specification for capacity in getResistanceCapacity. \n"); - } - return HPWH_ABORT; - } - - return returnPower; -} - -int HPWH::getResistancePosition(int elementIndex) const { - - if(elementIndex < 0 || elementIndex > getNumHeatSources() - 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Out of bounds value for which in getResistancePosition\n"); - } - return HPWH_ABORT; - } - - if(!heatSources[elementIndex].isAResistance()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("This index is not a resistance element\n"); - } - return HPWH_ABORT; - } - - for(int i = 0; i < heatSources[elementIndex].getCondensitySize(); i++) { - if(heatSources[elementIndex].condensity[i] == 1) { // res elements have a condenstiy of 1 at a specific node - return i; - } - } - return HPWH_ABORT; -} - -//the privates -void HPWH::updateTankTemps(double drawVolume_L,double inletT_C,double tankAmbientT_C, - double inletVol2_L,double inletT2_C) { - - outletTemp_C = 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) { - 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; - } - - if (hasHeatExchanger) {// heat-exchange models - - const double densityTank_kgperL = DENSITYWATER_kgperL; - const double CpTank_kJperkgC = CPWATER_kJperkgC; - - double C_Node_kJperC = CpTank_kJperkgC * densityTank_kgperL * nodeVolume_L; - double C_draw_kJperC = CPWATER_kJperkgC * DENSITYWATER_kgperL * drawVolume_L; - - outletTemp_C = inletT_C; - for (auto &nodeTemp: tankTemps_C) { - double maxHeatExchange_kJ = C_draw_kJperC * (nodeTemp - outletTemp_C); - double heatExchange_kJ = nodeHeatExchangerEffectiveness * maxHeatExchange_kJ; - - nodeTemp -= heatExchange_kJ / C_Node_kJperC; - outletTemp_C += heatExchange_kJ / C_draw_kJperC; - } - } - else { - //calculate how many nodes to draw (drawVolume_N) - double drawVolume_N = drawVolume_L / nodeVolume_L; - if(drawVolume_L > tankVolume_L) { - //if (hpwhVerbosity >= VRB_reluctant) { - // //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; - for(int i = 0; i < getNumNodes(); 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 / getNumNodes() * tankVolume_L + tankTemps_C[0] * (drawVolume_L - tankVolume_L)) - / drawVolume_L * drawVolume_N; - - drawVolume_N = 0.; - } - - while(drawVolume_N > 0) { - - // Draw one node at a time - double drawFraction = drawVolume_N > 1. ? 1. : drawVolume_N; - double nodeInletTV = 0.; - - //add temperature for outletT average - outletTemp_C += drawFraction * tankTemps_C[getNumNodes() - 1]; - - double cumInletFraction = 0.; - for(int i = getNumNodes() - 1; i >= lowInletH; i--) { - - - // Reset inlet inputs at this node. - double 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; - - mixTankInversions(); - } - - //fill in average outlet T - it is a weighted averaged, with weights == nodes drawn - outletTemp_C /= (drawVolume_L / nodeVolume_L); - } - - //Account for mixing at the bottom of the tank - if(tankMixesOnDraw && drawVolume_L > 0.) { - int mixedBelowNode = (int)(getNumNodes() * mixBelowFractionOnDraw); - mixTankNodes(0,mixedBelowNode,3.0); - } - - } //end if(draw_volume_L > 0) - - // Initialize newTankTemps_C - nextTankTemps_C = tankTemps_C; - - double standbyLossesBottom_kJ = 0.; - double standbyLossesTop_kJ = 0.; - double standbyLossesSides_kJ = 0.; - - // Standby losses from the top and bottom of the tank - { - auto standbyLossRate_kJperHrC = tankUA_kJperHrC * fracAreaTop; - - standbyLossesBottom_kJ = standbyLossRate_kJperHrC * hoursPerStep * (tankTemps_C[0] - tankAmbientT_C); - standbyLossesTop_kJ = standbyLossRate_kJperHrC * hoursPerStep * (tankTemps_C[getNumNodes() - 1] - tankAmbientT_C); - - nextTankTemps_C.front() -= standbyLossesBottom_kJ / nodeCp_kJperC; - nextTankTemps_C.back() -= standbyLossesTop_kJ / nodeCp_kJperC; - } - - // Standby losses from the sides of the tank - { - auto standbyLossRate_kJperHrC = (tankUA_kJperHrC * fracAreaSide + fittingsUA_kJperHrC) / getNumNodes(); - for(int i = 0; i < getNumNodes(); i++) { - double standbyLosses_kJ = standbyLossRate_kJperHrC * hoursPerStep * (tankTemps_C[i] - tankAmbientT_C); - standbyLossesSides_kJ += standbyLosses_kJ; - - nextTankTemps_C[i] -= standbyLosses_kJ / nodeCp_kJperC; - } - } - - // Heat transfer between nodes - 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) - * (nodeHeight_m * nodeHeight_m)) * secondsPerStep; - if(tau > 0.5) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("The stability condition for conduction has failed, these results are going to be interesting!\n"); - } - simHasFailed = true; - return; - } + bool doIP = (options & CSVOPT_IPUNITS) != 0; - // End nodes - if (getNumNodes() > 1) { // inner edges of top and bottom nodes - nextTankTemps_C.front() += 2. * tau * (tankTemps_C[1] - tankTemps_C.front()); - nextTankTemps_C.back() += 2. * tau * (tankTemps_C[getNumNodes() - 2] - tankTemps_C.back()); - } + fprintf(outFILE, "%s", preamble); - // Internal nodes - for(int i = 1; i < getNumNodes() - 1; i++) { - nextTankTemps_C[i] += 2. * tau * (tankTemps_C[i + 1] - 2. * tankTemps_C[i] + tankTemps_C[i - 1]); - } - } + fprintf(outFILE, "%s", "DRstatus"); - // Update tankTemps_C - tankTemps_C = nextTankTemps_C; - - standbyLosses_kWh += KJ_TO_KWH(standbyLossesBottom_kJ + standbyLossesTop_kJ + standbyLossesSides_kJ); - - // check for inverted temperature profile - mixTankInversions(); - -} //end updateTankTemps + for (int iHS = 0; iHS < getNumHeatSources(); iHS++) + { + fprintf(outFILE, ",h_src%dIn (Wh),h_src%dOut (Wh)", iHS + 1, iHS + 1); + } -void HPWH::updateSoCIfNecessary() { - if(usesSoCLogic) { - calcAndSetSoCFraction(); - } -} + for (int iTC = 0; iTC < nTCouples; iTC++) + { + fprintf(outFILE, ",tcouple%d (%s)", iTC + 1, doIP ? "F" : "C"); + } -// Inversion mixing modeled after bigladder EnergyPlus code PK -void HPWH::mixTankInversions() { - bool hasInversion; - const double volumePerNode_L = tankVolume_L / getNumNodes(); - //int numdos = 0; - if(doInversionMixing) { - do { - hasInversion = false; - //Start from the top and check downwards - for(int i = getNumNodes() - 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); - } -} + fprintf(outFILE, ",toutlet (%s)", doIP ? "F" : "C"); -//----------------------------------------------------------------------------- -/// @brief Adds heat amount qAdd_kJ at and above the node with index nodeNum. -/// Returns unused heat to prevent exceeding maximum or setpoint. -/// @note Moved from HPWH::HeatSource -/// @param[in] qAdd_kJ Amount of heat to add -/// @param[in] nodeNum Lowest node at which to add heat -/// @param[in] maxT_C Maximum allowable temperature to maintain -//----------------------------------------------------------------------------- -double HPWH::addHeatAboveNode(double qAdd_kJ,int nodeNum,const double maxT_C) { - - // Do not exceed maxT_C or setpoint - double maxHeatToT_C = std::min(maxT_C,setpoint_C); - - if(hpwhVerbosity >= VRB_emetic) { - msg("node %2d cap_kwh %.4lf \n",nodeNum,KJ_TO_KWH(qAdd_kJ)); - } - - // find number of nodes at or above nodeNum with the same temperature - int numNodesToHeat = 1; - for(int i = nodeNum; i < getNumNodes() - 1; i++) { - if(tankTemps_C[i] != tankTemps_C[i + 1]) { - break; - } else { - numNodesToHeat++; - } - } - - while((qAdd_kJ > 0.) && (nodeNum + numNodesToHeat - 1 < getNumNodes())) { - - // assume there is another node above the equal-temp nodes - int targetTempNodeNum = nodeNum + numNodesToHeat; - - double heatToT_C; - if(targetTempNodeNum > (getNumNodes() - 1)) { - // no nodes above the equal-temp nodes; target temperature is the maximum - heatToT_C = maxHeatToT_C; - } - else { - heatToT_C = tankTemps_C[targetTempNodeNum]; - if(heatToT_C > maxHeatToT_C) { - // Ensure temperature does not exceed maximum - heatToT_C = maxHeatToT_C; - } - } - - // heat needed to bring all equal-temp nodes up to heatToT_C - double qIncrement_kJ = nodeCp_kJperC * numNodesToHeat * (heatToT_C - tankTemps_C[nodeNum]); - - if(qIncrement_kJ > qAdd_kJ) { - // insufficient heat to reach heatToT_C; use all available heat - heatToT_C = tankTemps_C[nodeNum] + qAdd_kJ / nodeCp_kJperC / numNodesToHeat; - for(int j = 0; j < numNodesToHeat; ++j) { - tankTemps_C[nodeNum + j] = heatToT_C; - } - qAdd_kJ = 0.; - } - else if(qIncrement_kJ > 0.) - { // add qIncrement_kJ to raise all equal-temp-nodes to heatToT_C - for(int j = 0; j < numNodesToHeat; ++j) - tankTemps_C[nodeNum + j] = heatToT_C; - qAdd_kJ -= qIncrement_kJ; - } - numNodesToHeat++; - } - - // return any unused heat - return qAdd_kJ; -} + fprintf(outFILE, "\n"); -//----------------------------------------------------------------------------- -/// @brief Adds extra heat amount qAdd_kJ at and above the node with index nodeNum. -/// Does not limit final temperatures. -/// @param[in] qAdd_kJ Amount of heat to add -/// @param[in] nodeNum Lowest node at which to add heat -//----------------------------------------------------------------------------- -void HPWH::addExtraHeatAboveNode(double qAdd_kJ,const int nodeNum) { - - if(hpwhVerbosity >= VRB_emetic) { - msg("node %2d cap_kwh %.4lf \n",nodeNum,KJ_TO_KWH(qAdd_kJ)); - } - - // find number of nodes at or above nodeNum with the same temperature - int numNodesToHeat = 1; - for(int i = nodeNum; i < getNumNodes() - 1; i++) { - if(tankTemps_C[i] != tankTemps_C[i + 1]) { - break; - } else { - numNodesToHeat++; - } - } - - while((qAdd_kJ > 0.) && (nodeNum + numNodesToHeat - 1 < getNumNodes())) { - - // assume there is another node above the equal-temp nodes - int targetTempNodeNum = nodeNum + numNodesToHeat; - - double heatToT_C; - if(targetTempNodeNum > (getNumNodes() - 1)) { - // no nodes above the equal-temp nodes; target temperature limited by the heat available - heatToT_C = tankTemps_C[nodeNum] + qAdd_kJ / nodeCp_kJperC / numNodesToHeat; - } - else { - heatToT_C = tankTemps_C[targetTempNodeNum]; - } - - // heat needed to bring all equal-temp nodes up to heatToT_C - double qIncrement_kJ = nodeCp_kJperC * numNodesToHeat * (heatToT_C - tankTemps_C[nodeNum]); - - if(qIncrement_kJ > qAdd_kJ) { - // insufficient heat to reach heatToT_C; use all available heat - heatToT_C = tankTemps_C[nodeNum] + qAdd_kJ / nodeCp_kJperC / numNodesToHeat; - for(int j = 0; j < numNodesToHeat; ++j) { - tankTemps_C[nodeNum + j] = heatToT_C; - } - qAdd_kJ = 0.; - } - else if(qIncrement_kJ > 0.) - { // add qIncrement_kJ to raise all equal-temp-nodes to heatToT_C - for(int j = 0; j < numNodesToHeat; ++j) - tankTemps_C[nodeNum + j] = heatToT_C; - qAdd_kJ -= qIncrement_kJ; - } - numNodesToHeat++; - } + return 0; } -//----------------------------------------------------------------------------- -/// @brief Modifies a heat distribution using a thermal distribution. -/// @param[in,out] heatDistribution_W The distribution to be modified -//----------------------------------------------------------------------------- -void HPWH::modifyHeatDistribution(std::vector &heatDistribution_W) +int HPWH::WriteCSVRow(FILE* outFILE, const char* preamble, int nTCouples, int options) const { - double totalHeat_W = 0.; - for(auto &heatDist_W: heatDistribution_W) - totalHeat_W += heatDist_W; - if(totalHeat_W == 0.) - return; + bool doIP = (options & CSVOPT_IPUNITS) != 0; - for(auto &heatDist_W: heatDistribution_W) - heatDist_W /= totalHeat_W; + fprintf(outFILE, "%s", preamble); - double shrinkageT_C = findShrinkageT_C(heatDistribution_W); - int lowestNode = findLowestNode(heatDistribution_W,getNumNodes()); + fprintf(outFILE, "%i", prevDRstatus); - std::vector modHeatDistribution_W; - calcThermalDist(modHeatDistribution_W,shrinkageT_C,lowestNode,tankTemps_C,setpoint_C); - - heatDistribution_W = modHeatDistribution_W; - for(auto &heatDist_W: heatDistribution_W) - heatDist_W *= totalHeat_W; -} + for (int iHS = 0; iHS < getNumHeatSources(); iHS++) + { + fprintf(outFILE, + ",%0.2f,%0.2f", + getNthHeatSourceEnergyInput(iHS, UNITS_KWH) * 1000., + getNthHeatSourceEnergyOutput(iHS, UNITS_KWH) * 1000.); + } -//----------------------------------------------------------------------------- -/// @brief Adds extra heat to tank. -/// @param[in] extraHeatDist_W A distribution of extra heat to add -//----------------------------------------------------------------------------- -void HPWH::addExtraHeat(std::vector &extraHeatDist_W){ + for (int iTC = 0; iTC < nTCouples; iTC++) + { + fprintf(outFILE, ",%0.2f", getNthSimTcouple(iTC + 1, nTCouples, doIP ? UNITS_F : UNITS_C)); + } - auto modHeatDistribution_W = extraHeatDist_W; - modifyHeatDistribution(modHeatDistribution_W); + if (options & HPWH::CSVOPT_IS_DRAWING) + { + fprintf(outFILE, ",%0.2f", doIP ? C_TO_F(outletTemp_C) : outletTemp_C); + } + else + { + fprintf(outFILE, ","); + } - std::vector heatDistribution_W(getNumNodes()); - resampleExtensive(heatDistribution_W,modHeatDistribution_W); + fprintf(outFILE, "\n"); - // Unnecessary unit conversions used here to match former method - double tot_qAdded_BTUperHr = 0.; - for(int i = getNumNodes() - 1; i >= 0; i--) { - if(heatDistribution_W[i] != 0) { - double qAdd_BTUperHr = KWH_TO_BTU(heatDistribution_W[i] / 1000.); - double qAdd_KJ = BTU_TO_KJ(qAdd_BTUperHr * minutesPerStep / min_per_hr); - addExtraHeatAboveNode(qAdd_KJ,i); - tot_qAdded_BTUperHr += qAdd_BTUperHr; - } - } - // Write the input & output energy - extraEnergyInput_kWh = BTU_TO_KWH(tot_qAdded_BTUperHr * minutesPerStep / min_per_hr); + return 0; } -/////////////////////////////////////////////////////////////////////////////////// - -void HPWH::turnAllHeatSourcesOff() { - for(int i = 0; i < getNumHeatSources(); i++) { - heatSources[i].disengageHeatSource(); - } - isHeating = false; -} - -bool HPWH::areAllHeatSourcesOff() const { - bool allOff = true; - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isEngaged() == true) { - allOff = false; - } - } - return allOff; -} - -double HPWH::tankAvg_C(const std::vector nodeWeights) const { - double sum = 0; - double totWeight = 0; - - std::vector resampledTankTemps(LOGIC_NODE_SIZE); - resample(resampledTankTemps, tankTemps_C); - - for (auto &nodeWeight : nodeWeights) { - if (nodeWeight.nodeNum == 0) { // bottom node only - sum += tankTemps_C.front() * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - else if (nodeWeight.nodeNum > LOGIC_NODE_SIZE) { // top node only - sum += tankTemps_C.back() * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - else { // general case; sum over all weighted nodes - sum += resampledTankTemps[static_cast(nodeWeight.nodeNum - 1)] * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - } - return sum / totWeight; -} - -void HPWH::mixTankNodes(int mixedAboveNode,int mixedBelowNode,double mixFactor) { - double ave = 0.; - double numAvgNodes = (double)(mixedBelowNode - mixedAboveNode); - for(int i = mixedAboveNode; i < mixedBelowNode; i++) { - ave += tankTemps_C[i]; - } - ave /= numAvgNodes; - - for(int i = mixedAboveNode; i < mixedBelowNode; i++) { - tankTemps_C[i] += ((ave - tankTemps_C[i]) / mixFactor); - //tankTemps_C[i] = tankTemps_C[i] * (1.0 - 1.0 / mixFactor) + ave / mixFactor; - } -} - -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. - // The assumption is that the aspect ratio is the same for all tanks and is the same for the outside measurements of the unit - // and the inner water tank. - const double tankRad_m = getTankRadius(UNITS_M); - const double tankHeight_m = ASPECTRATIO * tankRad_m; - - nodeVolume_L = tankVolume_L / getNumNodes(); - nodeMass_kg = nodeVolume_L * DENSITYWATER_kgperL; - nodeCp_kJperC = nodeMass_kg * CPWATER_kJperkgC; - nodeHeight_m = tankHeight_m / getNumNodes(); - - // 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 = tankRad_m / (2.0 * (tankHeight_m + tankRad_m)); - - // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. - fracAreaSide = tankHeight_m / (tankHeight_m + tankRad_m); - - /// Single-node heat-exchange effectiveness - nodeHeatExchangerEffectiveness = 1. - pow(1. - heatExchangerEffectiveness, 1./ static_cast(getNumNodes())); -} - -void HPWH::calcDerivedValues() { - // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() - calcDerivedHeatingValues(); - - calcSizeConstants(); - - mapResRelativePosToHeatSources(); - - //heat source ability to depress temp - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isACompressor()) { - heatSources[i].depressesTemperature = true; - } else if(heatSources[i].isAResistance()) { - heatSources[i].depressesTemperature = false; - } - } -} - -void HPWH::calcDerivedHeatingValues(){ - static char outputString[MAXOUTSTRING]; //this is used for debugging outputs - - // find condentropy/shrinkage - for(int i = 0; i < getNumHeatSources(); ++i) { - heatSources[i].Tshrinkage_C = findShrinkageT_C(heatSources[i].condensity); - - if(hpwhVerbosity >= VRB_emetic) { - msg(outputString,"Heat Source %d \n",i); - msg(outputString,"shrinkage %.2lf \n\n",heatSources[i].Tshrinkage_C); - } - } - - // find lowest node - for(int i = 0; i < getNumHeatSources(); i++) { - heatSources[i].lowestNode = findLowestNode(heatSources[i].condensity,getNumNodes()); - - if(hpwhVerbosity >= VRB_emetic) { - msg(outputString,"Heat Source %d \n",i); - msg(outputString," lowest : %d \n",heatSources[i].lowestNode); - } - } - - // define condenser index and lowest resistance element index - compressorIndex = -1; // Default = No compressor - lowestElementIndex = -1; // Default = No resistance elements - highestElementIndex = -1; // Default = No resistance elements - VIPIndex = -1; // Default = No VIP element - double lowestPos = 1.; - double highestPos = 0.; // -1 to make sure a an element on the bottom can still be identified. - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isACompressor()) { - compressorIndex = i; // NOTE: Maybe won't work with multiple compressors (last compressor will be used) - } else if(heatSources[i].isAResistance()){ - // Gets VIP element index - if(heatSources[i].isVIP) { - if(VIPIndex == -1) { - VIPIndex = i; - } else { - if(hpwhVerbosity >= VRB_minuteOut) { - msg("More than one resistance element is assigned to VIP"); - }; - } - } - int condensitySize = heatSources[i].getCondensitySize(); - for(int j = 0; j < condensitySize; ++j) { - double pos = static_cast(j) / condensitySize; - if((heatSources[i].condensity[j] > 0.) && (pos < lowestPos)) { - lowestElementIndex = i; - lowestPos = pos; - } - if((heatSources[i].condensity[j] > 0.) && (pos >= highestPos)) { - highestElementIndex = i; - highestPos = pos; - } - } - } - } - if(hpwhVerbosity >= VRB_emetic) { - msg(outputString," compressorIndex : %d \n",compressorIndex); - msg(outputString," lowestElementIndex : %d \n",lowestElementIndex); - msg(outputString," highestElementIndex : %d \n",highestElementIndex); - } - if(hpwhVerbosity >= VRB_emetic) { - msg(outputString," VIPIndex : %d \n",VIPIndex); - } - - //heat source ability to depress temp - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isACompressor()) { - heatSources[i].depressesTemperature = true; - } else if(heatSources[i].isAResistance()) { - heatSources[i].depressesTemperature = false; - } - } - -} - -void HPWH::mapResRelativePosToHeatSources() { - resistanceHeightMap.clear(); - resistanceHeightMap.reserve(getNumResistanceElements()); - - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isAResistance()) { - resistanceHeightMap.push_back({i, - getResistancePosition(i) - }); - } - } - - // Sort by height, low to high - std::sort(resistanceHeightMap.begin(),resistanceHeightMap.end(), - [](const HPWH::resPoint & a,const HPWH::resPoint & b) -> bool { - return a.position < b.position; // (5 < 5) // evaluates to false - }); -} +bool HPWH::isSetpointFixed() const { return setpointFixed; } -// 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(getNumHeatSources() <= 0 && hpwhModel != MODELS_StorageTank) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("You must have at least one HeatSource.\n"); - } - returnVal = HPWH_ABORT; - } - - double condensitySum; - //loop through all heat sources to check each for malconfigurations - for(int i = 0; i < getNumHeatSources(); i++) { - //check the heat source type to make sure it has been set - if(heatSources[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 = heatSources[i].findParent(); - if(heatSources[i].turnOnLogicSet.size() == 0 && (parent == -1 || heatSources[parent].turnOnLogicSet.size() == 0)) { - if(hpwhVerbosity >= VRB_reluctant) { - 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; - } - - // Validate on logics - for(std::shared_ptr logic : heatSources[i].turnOnLogicSet) { - if(!logic->isValid()) { - returnVal = HPWH_ABORT; - if(hpwhVerbosity >= VRB_reluctant) { - msg("On logic at index %i is invalid",i); - } - } - } - // Validate off logics - for(std::shared_ptr logic : heatSources[i].shutOffLogicSet) { - if(!logic->isValid()) { - returnVal = HPWH_ABORT; - if(hpwhVerbosity >= VRB_reluctant) { - msg("Off logic at index %i is invalid",i); - } - } - } - - //check is condensity sums to 1 - condensitySum = 0; - - for(int j = 0; j < heatSources[i].getCondensitySize(); j++) condensitySum += heatSources[i].condensity[j]; - if(fabs(condensitySum - 1.0) > 1e-6) { - if(hpwhVerbosity >= VRB_reluctant) { - 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(heatSources[i].airflowFreedom > 1.0 || heatSources[i].airflowFreedom <= 0.0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("The airflowFreedom must be between 0 and 1 for heatsource %d. \n",i); - } - returnVal = HPWH_ABORT; - } - - if(heatSources[i].isACompressor()) { - if(heatSources[i].doDefrost) { - if(heatSources[i].defrostMap.size() < 3) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Defrost logic set to true but no valid defrost map of length 3 or greater set. \n"); - } - returnVal = HPWH_ABORT; - } - if(heatSources[i].configuration != HeatSource::CONFIG_EXTERNAL) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Defrost is only simulated for external compressors. \n"); - } - returnVal = HPWH_ABORT; - } - } - } - if(heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) { - - if(heatSources[i].shutOffLogicSet.size() != 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("External heat sources can only have one shut off logic. \n "); - } - returnVal = HPWH_ABORT; - } - if(0 > heatSources[i].externalOutletHeight || heatSources[i].externalOutletHeight > getNumNodes() - 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("External heat sources need an external outlet height within the bounds from from 0 to numNodes-1. \n"); - } - returnVal = HPWH_ABORT; - } - if(0 > heatSources[i].externalInletHeight || heatSources[i].externalInletHeight > getNumNodes() - 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("External heat sources need an external inlet height within the bounds from from 0 to numNodes-1. \n"); - } - returnVal = HPWH_ABORT; - } - } else { - if(heatSources[i].secondaryHeatExchanger.extraPumpPower_W != 0 || heatSources[i].secondaryHeatExchanger.extraPumpPower_W) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Heatsource %d is not an external heat source but has an external secondary heat exchanger. \n",i); - } - returnVal = HPWH_ABORT; - } - } - - - // Check performance map - // perfGrid and perfGridValues, and the length of vectors in perfGridValues are equal and that ; - if(heatSources[i].useBtwxtGrid) { - // If useBtwxtGrid is true that the perfMap is empty - if(heatSources[i].perfMap.size() != 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Using the grid lookups but a regression based perforamnce map is given \n"); - } - returnVal = HPWH_ABORT; - } - - // Check length of vectors in perfGridValue are equal - if(heatSources[i].perfGridValues[0].size() != heatSources[i].perfGridValues[1].size() - && heatSources[i].perfGridValues[0].size() != 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("When using grid lookups for perfmance the vectors in perfGridValues must be the same length. \n"); - } - returnVal = HPWH_ABORT; - } - - // Check perfGrid's vectors lengths multiplied together == the perfGridValues vector lengths - size_t expLength = 1; - for(const auto& v : heatSources[i].perfGrid) - { - expLength *= v.size(); - } - if(expLength != heatSources[i].perfGridValues[0].size()) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("When using grid lookups for perfmance the vectors in perfGridValues must be the same length. \n"); - } - returnVal = HPWH_ABORT; - } - } else { - // Check that perfmap only has 1 point if config_external and multipass - if(heatSources[i].isExternalMultipass() && heatSources[i].perfMap.size() != 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("External multipass heat sources must have a perfMap of only one point with regression equations. \n"); - } - returnVal = HPWH_ABORT; - } - } - } - - // Check that the on logic and off logics are ordered properly - if(hasACompressor()) { - double aquaF = 0.,useF = 1.; - getSizingFractions(aquaF,useF); - if(aquaF < (1. - useF)) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("The relationship between the on logic and off logic is not supported. The off logic is beneath the on logic."); - } - returnVal = HPWH_ABORT; - } - } - - double maxTemp; string why; - double tempSetpoint = setpoint_C; - if(!isNewSetpointPossible(tempSetpoint,maxTemp,why)) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Cannot set new setpoint. %s",why.c_str()); - } - returnVal = HPWH_ABORT; - } - - //Check if the UA is out of bounds - if(tankUA_kJperHrC < 0.0) { - if(hpwhVerbosity >= VRB_reluctant) { - 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; - } - - // Check single-node heat-exchange effectiveness validity - if( heatExchangerEffectiveness > 1.) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Heat-exchanger effectiveness cannot exceed 1.\n"); - } - returnVal = HPWH_ABORT; - } - - //if there's no failures, return 0 - return returnVal; -} - -bool HPWH::shouldDRLockOut(HEATSOURCE_TYPE hs,DRMODES DR_signal) const { - - if(hs == TYPE_compressor && (DR_signal & DR_LOC) != 0) { - return true; - } else if(hs == TYPE_resistance && (DR_signal & DR_LOR) != 0) { - return true; - } - return false; -} - -void HPWH::resetTopOffTimer() { - timerTOT = 0.; -} +int HPWH::setSetpoint(double newSetpoint, UNITS units /*=UNITS_C*/) +{ -//----------------------------------------------------------------------------- -/// @brief Checks whether energy is balanced during a simulation step. + double newSetpoint_C, temp; + string why; + if (units == UNITS_C) + { + newSetpoint_C = newSetpoint; + } + else if (units == UNITS_F) + { + newSetpoint_C = F_TO_C(newSetpoint); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for setSetpoint. \n"); + } + return HPWH_ABORT; + } + if (!isNewSetpointPossible(newSetpoint_C, temp, why)) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Unwilling to set this setpoint for the currently selected model, max setpoint is " + "%f C. %s\n", + temp, + why.c_str()); + } + return HPWH_ABORT; + } + + setpoint_C = newSetpoint_C; + + return 0; +} + +double HPWH::getSetpoint(UNITS units /*=UNITS_C*/) const +{ + if (units == UNITS_C) + { + return setpoint_C; + } + else if (units == UNITS_F) + { + return C_TO_F(setpoint_C); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getSetpoint. \n"); + } + return HPWH_ABORT; + } +} + +double HPWH::getMaxCompressorSetpoint(UNITS units /*=UNITS_C*/) const +{ + + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Unit does not have a compressor \n"); + } + return HPWH_ABORT; + } + + double returnVal = heatSources[compressorIndex].maxSetpoint_C; + if (units == UNITS_C) + { + returnVal = returnVal; + } + else if (units == UNITS_F) + { + returnVal = C_TO_F(returnVal); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getMaxCompressorSetpoint. \n"); + } + return HPWH_ABORT; + } + return returnVal; +} + +bool HPWH::isNewSetpointPossible(double newSetpoint, + double& maxAllowedSetpoint, + string& why, + UNITS units /*=UNITS_C*/) const +{ + double newSetpoint_C; + double maxAllowedSetpoint_C = -273.15; + if (units == UNITS_C) + { + newSetpoint_C = newSetpoint; + } + else if (units == UNITS_F) + { + newSetpoint_C = F_TO_C(newSetpoint); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for isNewSetpointPossible. \n"); + } + return false; + } + bool returnVal = false; + + if (isSetpointFixed()) + { + returnVal = (newSetpoint == setpoint_C); + maxAllowedSetpoint_C = setpoint_C; + if (!returnVal) + { + why = "The set point is fixed for the currently selected model."; + } + } + else + { + + if (hasACompressor()) + { // If there's a compressor lets check the new setpoint against the compressor's max + // setpoint + + maxAllowedSetpoint_C = + heatSources[compressorIndex].maxSetpoint_C - + heatSources[compressorIndex].secondaryHeatExchanger.hotSideTemperatureOffset_dC; + + if (newSetpoint_C > maxAllowedSetpoint_C && lowestElementIndex == -1) + { + why = "The compressor cannot meet the setpoint temperature and there is no " + "resistance backup."; + returnVal = false; + } + else + { + returnVal = true; + } + } + if (lowestElementIndex >= 0) + { // If there's a resistance element lets check the new setpoint against the its max + // setpoint + maxAllowedSetpoint_C = heatSources[lowestElementIndex].maxSetpoint_C; + + if (newSetpoint_C > maxAllowedSetpoint_C) + { + why = "The resistance elements cannot produce water this hot."; + returnVal = false; + } + else + { + returnVal = true; + } + } + else if (lowestElementIndex == -1 && !hasACompressor()) + { // There are no heat sources here! + if (hpwhModel == MODELS_StorageTank) + { + returnVal = true; // The one pass the storage tank doesn't have any heating elements + // so sure change the setpoint it does nothing! + } + else + { + why = "There aren't any heat sources to check the new setpoint against!"; + returnVal = false; + } + } + } + + if (units == UNITS_C) + { + maxAllowedSetpoint = maxAllowedSetpoint_C; + } + else if (units == UNITS_F) + { + maxAllowedSetpoint = C_TO_F(maxAllowedSetpoint_C); + } + return returnVal; +} + +double HPWH::calcSoCFraction(double tMains_C, double tMinUseful_C, double tMax_C) const +{ + // Note that volume is ignored in here since with even nodes it cancels out of the SoC + // fractional equation + if (tMains_C >= tMinUseful_C) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("tMains_C is greater than or equal tMinUseful_C. \n"); + } + return HPWH_ABORT; + } + if (tMinUseful_C > tMax_C) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("tMinUseful_C is greater tMax_C. \n"); + } + return HPWH_ABORT; + } + + double chargeEquivalent = 0.; + for (auto& T : tankTemps_C) + { + chargeEquivalent += getChargePerNode(tMains_C, tMinUseful_C, T); + } + double maxSoC = getNumNodes() * getChargePerNode(tMains_C, tMinUseful_C, tMax_C); + return chargeEquivalent / maxSoC; +} + +double HPWH::getSoCFraction() const { return currentSoCFraction; } + +void HPWH::calcAndSetSoCFraction() +{ + double newSoCFraction = -1.; + + std::shared_ptr logicSoC = + std::dynamic_pointer_cast( + heatSources[compressorIndex].turnOnLogicSet[0]); + newSoCFraction = calcSoCFraction(logicSoC->getMainsT_C(), logicSoC->getTempMinUseful_C()); + + currentSoCFraction = newSoCFraction; +} + +double HPWH::getChargePerNode(double tCold, double tMix, double tHot) const +{ + if (tHot < tMix) + { + return 0.; + } + return (tHot - tCold) / (tMix - tCold); +} + +double HPWH::getMinOperatingTemp(UNITS units /*=UNITS_C*/) const +{ + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("No compressor found in this HPWH. \n"); + } + return HPWH_ABORT; + } + if (units == UNITS_C) + { + return heatSources[compressorIndex].minT; + } + else if (units == UNITS_F) + { + return C_TO_F(heatSources[compressorIndex].minT); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getMinOperatingTemp.\n"); + } + return HPWH_ABORT; + } +} + +int HPWH::resetTankToSetpoint() { return setTankToTemperature(setpoint_C); } + +int HPWH::setTankToTemperature(double temp_C) { return setTankLayerTemperatures({temp_C}); } + +//----------------------------------------------------------------------------- +/// @brief Assigns new temps provided from a std::vector to tankTemps_C. +/// @param[in] setTankTemps new tank temps (arbitrary non-zero size) +/// @param[in] units temp units in setTankTemps (default = UNITS_C) +/// @return Success: 0; Failure: HPWH_ABORT +//----------------------------------------------------------------------------- +int HPWH::setTankLayerTemperatures(std::vector setTankTemps, const UNITS units) +{ + if ((units != UNITS_C) && (units != UNITS_F)) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for setSetpoint. \n"); + } + return HPWH_ABORT; + } + + std::size_t numSetNodes = setTankTemps.size(); + if (numSetNodes == 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("No temperatures provided.\n"); + } + return HPWH_ABORT; + } + + // convert setTankTemps to �C, if necessary + if (units == UNITS_F) + for (auto& T : setTankTemps) + T = F_TO_C(T); + + // set node temps + if (!resampleIntensive(tankTemps_C, setTankTemps)) + return HPWH_ABORT; + + return 0; +} + +void HPWH::getTankTemps(std::vector& tankTemps) { tankTemps = tankTemps_C; } + +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 < getNumHeatSources(); i++) + { + if (heatSources[i].isACompressor()) + { + heatSources[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*/, + bool forceChange /*=false*/) +{ + // 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, forceChange); + setUA(tankUA_kJperHrC / oldA * getTankSurfaceArea(UNITS_FT2), UNITS_kJperHrC); + return 0; +} + +/*static*/ double +HPWH::getTankSurfaceArea(double vol, UNITS volUnits /*=UNITS_L*/, UNITS surfAUnits /*=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. Corresponds to the inner tank with volume tankVolume_L with the + // assumption that the aspect ratio is the same as the outer dimenisions of the whole unit. + double radius = getTankRadius(vol, volUnits, UNITS_FT); + + double value = 2. * 3.14159 * pow(radius, 2) * (ASPECTRATIO + 1.); + + if (value >= 0.) + { + if (surfAUnits == UNITS_M2) + value = FT2_TO_M2(value); + else if (surfAUnits != UNITS_FT2) + value = -1.; + } + return value; +} + +double HPWH::getTankSurfaceArea(UNITS units /*=UNITS_FT2*/) const +{ + // 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. Corresponds to the inner tank with volume tankVolume_L with the + // assumption that the aspect ratio is the same as the outer dimenisions of the whole unit. + double value = getTankSurfaceArea(tankVolume_L, UNITS_L, units); + if (value < 0.) + { + if (hpwhVerbosity >= VRB_reluctant) + msg("Incorrect unit specification for getTankSurfaceArea. \n"); + value = HPWH_ABORT; + } + return value; +} + +/*static*/ double +HPWH::getTankRadius(double vol, UNITS volUnits /*=UNITS_L*/, UNITS radiusUnits /*=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, assumes the aspect ratio for the outer measurements is the same is the + // actual tank. + double volft3 = volUnits == UNITS_L ? L_TO_FT3(vol) + : volUnits == UNITS_GAL ? L_TO_FT3(GAL_TO_L(vol)) + : -1.; + + double value = -1.; + if (volft3 >= 0.) + { + value = pow(volft3 / 3.14159 / ASPECTRATIO, 1. / 3.); + if (radiusUnits == UNITS_M) + value = FT_TO_M(value); + else if (radiusUnits != UNITS_FT) + value = -1.; + } + return value; +} + +double HPWH::getTankRadius(UNITS units /*=UNITS_FT*/) const +{ + // 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, assumes the aspect ratio for the outer measurements is the + // same is the actual tank. + + double value = getTankRadius(tankVolume_L, UNITS_L, units); + + if (value < 0.) + { + if (hpwhVerbosity >= VRB_reluctant) + msg("Incorrect unit specification for getTankRadius. \n"); + value = HPWH_ABORT; + } + return value; +} + +bool HPWH::isTankSizeFixed() const { return tankSizeFixed; } + +int HPWH::setTankSize(double HPWH_size, UNITS units /*=UNITS_L*/, bool forceChange /*=false*/) +{ + if (isTankSizeFixed() && !forceChange) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not change the tank size for your currently selected model. \n"); + } + return HPWH_ABORT; + } + 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; + } + } + + calcSizeConstants(); + + 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::setFittingsUA(double UA, UNITS units /*=UNITS_kJperHrC*/) +{ + if (units == UNITS_kJperHrC) + { + fittingsUA_kJperHrC = UA; + } + else if (units == UNITS_BTUperHrF) + { + fittingsUA_kJperHrC = UAf_TO_UAc(UA); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for setFittingsUA. \n"); + } + return HPWH_ABORT; + } + return 0; +} +int HPWH::getFittingsUA(double& UA, UNITS units /*=UNITS_kJperHrC*/) const +{ + UA = fittingsUA_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::setExternalInletHeightByFraction(double fractionalHeight) +{ + return setExternalPortHeightByFraction(fractionalHeight, 1); +} +int HPWH::setExternalOutletHeightByFraction(double fractionalHeight) +{ + return setExternalPortHeightByFraction(fractionalHeight, 2); +} + +int HPWH::setExternalPortHeightByFraction(double fractionalHeight, int whichExternalPort) +{ + if (!hasExternalHeatSource()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Does not have an external heat source \n"); + } + return HPWH_ABORT; + } + + int returnVal = 0; + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) + { + if (whichExternalPort == 1) + { + returnVal = setNodeNumFromFractionalHeight(fractionalHeight, + heatSources[i].externalInletHeight); + } + else + { + returnVal = setNodeNumFromFractionalHeight(fractionalHeight, + heatSources[i].externalOutletHeight); + } + + if (returnVal == HPWH_ABORT) + { + return returnVal; + } + } + } + return returnVal; +} + +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(getNumNodes() * fractionalHeight); + inletNum = (node == getNumNodes()) ? getIndexTopNode() : node; + + return 0; +} + +int HPWH::getExternalInletHeight() const +{ + if (!hasExternalHeatSource()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Does not have an external heat source \n"); + } + return HPWH_ABORT; + } + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) + { + return heatSources[i].externalInletHeight; // Return the first one since all external + // sources have some ports + } + } + return HPWH_ABORT; +} +int HPWH::getExternalOutletHeight() const +{ + if (!hasExternalHeatSource()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Does not have an external heat source \n"); + } + return HPWH_ABORT; + } + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) + { + return heatSources[i].externalOutletHeight; // Return the first one since all external + // sources have some ports + } + } + return HPWH_ABORT; +} + +int HPWH::setTimerLimitTOT(double limit_min) +{ + if (limit_min > 24. * 60. || limit_min < 0.) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Out of bounds time limit for setTimerLimitTOT \n"); + } + return HPWH_ABORT; + } + + timerLimitTOT = limit_min; + + return 0; +} + +double HPWH::getTimerLimitTOT_minute() const { return timerLimitTOT; } + +int HPWH::getInletHeight(int whichInlet) const +{ + 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; +} + +bool HPWH::hasEnteringWaterHighTempShutOff(int heatSourceIndex) +{ + bool retVal = false; + if (heatSourceIndex >= getNumHeatSources() || heatSourceIndex < 0) + { + return retVal; + } + if (heatSources[heatSourceIndex].shutOffLogicSet.size() == 0) + { + return retVal; + } + + for (std::shared_ptr shutOffLogic : heatSources[heatSourceIndex].shutOffLogicSet) + { + if (shutOffLogic->getIsEnteringWaterHighTempShutoff()) + { + retVal = true; + break; + } + } + return retVal; +} + +int HPWH::setEnteringWaterHighTempShutOff(double highTemp, + bool tempIsAbsolute, + int heatSourceIndex, + UNITS unit /*=UNITS_C*/) +{ + if (!hasEnteringWaterHighTempShutOff(heatSourceIndex)) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("You have attempted to acess a heating logic that does not exist. \n"); + } + return HPWH_ABORT; + } + + double highTemp_C; + if (unit == UNITS_C) + { + highTemp_C = highTemp; + } + else if (unit == UNITS_F) + { + highTemp_C = F_TO_C(highTemp); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for set Entering Water High Temp Shut Off. \n"); + } + return HPWH_ABORT; + } + + bool highTempIsNotValid = false; + if (tempIsAbsolute) + { + // check differnce with setpoint + if (setpoint_C - highTemp_C < MINSINGLEPASSLIFT) + { + highTempIsNotValid = true; + } + } + else + { + if (highTemp_C < MINSINGLEPASSLIFT) + { + highTempIsNotValid = true; + } + } + if (highTempIsNotValid) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("High temperature shut off is too close to the setpoint, excpected a minimum " + "difference of %.2lf.\n", + MINSINGLEPASSLIFT); + } + return HPWH_ABORT; + } + + for (std::shared_ptr shutOffLogic : heatSources[heatSourceIndex].shutOffLogicSet) + { + if (shutOffLogic->getIsEnteringWaterHighTempShutoff()) + { + std::dynamic_pointer_cast(shutOffLogic) + ->setDecisionPoint(highTemp_C, tempIsAbsolute); + break; + } + } + return 0; +} + +int HPWH::setTargetSoCFraction(double target) +{ + if (!isSoCControlled()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not set target state of charge if HPWH is not using state of charge " + "controls."); + } + return HPWH_ABORT; + } + if (target < 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not set a negative target state of charge."); + } + return HPWH_ABORT; + } + + for (int i = 0; i < getNumHeatSources(); i++) + { + for (std::shared_ptr logic : heatSources[i].shutOffLogicSet) + { + if (!logic->getIsEnteringWaterHighTempShutoff()) + { + logic->setDecisionPoint(target); + } + } + for (std::shared_ptr logic : heatSources[i].turnOnLogicSet) + { + logic->setDecisionPoint(target); + } + } + return 0; +} + +bool HPWH::isSoCControlled() const { return usesSoCLogic; } + +bool HPWH::canUseSoCControls() +{ + bool retVal = true; + if (getCompressorCoilConfig() != HPWH::HeatSource::CONFIG_EXTERNAL) + { + retVal = false; + } + return retVal; +} + +int HPWH::switchToSoCControls(double targetSoC, + double hysteresisFraction /*= 0.05*/, + double tempMinUseful /*= 43.333*/, + bool constantMainsT /*= false*/, + double mainsT /*= 18.333*/, + UNITS tempUnit /*= UNITS_C*/) +{ + if (!canUseSoCControls()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Cannot set up state of charge controls for integrated or wrapped HPWHs.\n"); + } + return HPWH_ABORT; + } + + double tempMinUseful_C, mainsT_C; + if (tempUnit == UNITS_C) + { + tempMinUseful_C = tempMinUseful; + mainsT_C = mainsT; + } + else if (tempUnit == UNITS_F) + { + tempMinUseful_C = F_TO_C(tempMinUseful); + mainsT_C = F_TO_C(mainsT); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for set Enterinh Water High Temp Shut Off.\n"); + } + return HPWH_ABORT; + } + + if (mainsT_C >= tempMinUseful_C) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The mains temperature can't be equal to or greater than the minimum useful " + "temperature.\n"); + } + return HPWH_ABORT; + } + + for (int i = 0; i < getNumHeatSources(); i++) + { + heatSources[i].clearAllTurnOnLogic(); + + heatSources[i].shutOffLogicSet.erase( + std::remove_if(heatSources[i].shutOffLogicSet.begin(), + heatSources[i].shutOffLogicSet.end(), + [&](const auto logic) -> bool + { return !logic->getIsEnteringWaterHighTempShutoff(); }), + heatSources[i].shutOffLogicSet.end()); + + heatSources[i].shutOffLogicSet.push_back(shutOffSoC("SoC Shut Off", + targetSoC, + hysteresisFraction, + tempMinUseful_C, + constantMainsT, + mainsT_C)); + heatSources[i].turnOnLogicSet.push_back(turnOnSoC("SoC Turn On", + targetSoC, + hysteresisFraction, + tempMinUseful_C, + constantMainsT, + mainsT_C)); + } + + usesSoCLogic = true; + + return 0; +} + +std::shared_ptr HPWH::turnOnSoC(string desc, + double targetSoC, + double hystFract, + double tempMinUseful_C, + bool constantMainsT, + double mainsT_C) +{ + return std::make_shared( + desc, targetSoC, this, -hystFract, tempMinUseful_C, constantMainsT, mainsT_C); +} + +std::shared_ptr HPWH::shutOffSoC(string desc, + double targetSoC, + double hystFract, + double tempMinUseful_C, + bool constantMainsT, + double mainsT_C) +{ + return std::make_shared(desc, + targetSoC, + this, + hystFract, + tempMinUseful_C, + constantMainsT, + mainsT_C, + std::greater()); +} + +//----------------------------------------------------------------------------- +/// @brief Builds a vector of logic node weights referred to a fixed number of +/// nodes given by LOGIC_NODE_SIZE. +/// @param[in] bottomFraction Lower bounding fraction (0 to 1) +/// @param[in] topFraction Upper bounding fraction (0 to 1) +/// @return vector of node weights +//----------------------------------------------------------------------------- +std::vector HPWH::getNodeWeightRange(double bottomFraction, double topFraction) +{ + std::vector nodeWeights; + if (topFraction < bottomFraction) + std::swap(bottomFraction, topFraction); + auto bottomIndex = static_cast(bottomFraction * LOGIC_NODE_SIZE); + auto topIndex = static_cast(topFraction * LOGIC_NODE_SIZE); + for (auto index = bottomIndex; index < topIndex; ++index) + { + nodeWeights.emplace_back(static_cast(index) + 1); + } + return nodeWeights; +} + +std::shared_ptr HPWH::wholeTank(double decisionPoint, + const UNITS units /* = UNITS_C */, + const bool absolute /* = false */) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1.); + double decisionPoint_C = convertTempToC(decisionPoint, units, absolute); + return std::make_shared( + "whole tank", nodeWeights, decisionPoint_C, this, absolute); +} + +std::shared_ptr HPWH::topThird(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(2. / 3., 1.); + return std::make_shared( + "top third", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::topThird_absolute(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(2. / 3., 1.); + return std::make_shared( + "top third absolute", nodeWeights, decisionPoint, this, true); +} + +std::shared_ptr HPWH::secondThird(double decisionPoint, + const UNITS units /* = UNITS_C */, + const bool absolute /* = false */) +{ + std::vector nodeWeights = getNodeWeightRange(1. / 3., 2. / 3.); + double decisionPoint_C = convertTempToC(decisionPoint, units, absolute); + return std::make_shared( + "second third", nodeWeights, decisionPoint_C, this, absolute); +} + +std::shared_ptr HPWH::bottomThird(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 3.); + return std::make_shared( + "bottom third", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::bottomSixth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 6.); + return std::make_shared( + "bottom sixth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::bottomSixth_absolute(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 6.); + return std::make_shared( + "bottom sixth absolute", nodeWeights, decisionPoint, this, true); +} + +std::shared_ptr HPWH::secondSixth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(1. / 6., 2. / 6.); + return std::make_shared( + "second sixth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::thirdSixth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(2. / 6., 3. / 6.); + return std::make_shared( + "third sixth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::fourthSixth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(3. / 6., 4. / 6.); + return std::make_shared( + "fourth sixth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::fifthSixth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(4. / 6., 5. / 6.); + return std::make_shared( + "fifth sixth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::topSixth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(5. / 6., 1.); + return std::make_shared( + "top sixth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::bottomHalf(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 2.); + return std::make_shared( + "bottom half", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::bottomTwelfth(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 12.); + return std::make_shared( + "bottom twelfth", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::standby(double decisionPoint) +{ + std::vector nodeWeights; + nodeWeights.emplace_back(LOGIC_NODE_SIZE + 1); // uses very top computation node + return std::make_shared( + "standby", nodeWeights, decisionPoint, this); +} + +std::shared_ptr HPWH::topNodeMaxTemp(double decisionPoint) +{ + std::vector nodeWeights; + nodeWeights.emplace_back(LOGIC_NODE_SIZE + 1); // uses very top computation node + return std::make_shared( + "top node", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr +HPWH::bottomNodeMaxTemp(double decisionPoint, bool isEnteringWaterHighTempShutoff /*=false*/) +{ + std::vector nodeWeights; + nodeWeights.emplace_back(0); // uses very bottom computation node + return std::make_shared("bottom node", + nodeWeights, + decisionPoint, + this, + true, + std::greater(), + isEnteringWaterHighTempShutoff); +} + +std::shared_ptr HPWH::bottomTwelfthMaxTemp(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 12.); + return std::make_shared( + "bottom twelfth", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr HPWH::topThirdMaxTemp(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(2. / 3., 1.); + return std::make_shared( + "top third", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr HPWH::bottomSixthMaxTemp(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 6.); + return std::make_shared( + "bottom sixth", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr HPWH::secondSixthMaxTemp(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(1. / 6., 2. / 6.); + return std::make_shared( + "second sixth", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr HPWH::fifthSixthMaxTemp(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(4. / 6., 5. / 6.); + return std::make_shared( + "top sixth", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr HPWH::topSixthMaxTemp(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(5. / 6., 1.); + return std::make_shared( + "top sixth", nodeWeights, decisionPoint, this, true, std::greater()); +} + +std::shared_ptr HPWH::largeDraw(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 4.); + return std::make_shared( + "large draw", nodeWeights, decisionPoint, this, true); +} + +std::shared_ptr HPWH::largerDraw(double decisionPoint) +{ + std::vector nodeWeights = getNodeWeightRange(0., 1. / 2.); + return std::make_shared( + "larger draw", nodeWeights, decisionPoint, this, true); +} + +void HPWH::setNumNodes(const std::size_t num_nodes) +{ + tankTemps_C.resize(num_nodes); + nextTankTemps_C.resize(num_nodes); +} + +int HPWH::getNumNodes() const { return static_cast(tankTemps_C.size()); } + +int HPWH::getIndexTopNode() const { return getNumNodes() - 1; } + +double HPWH::getTankNodeTemp(int nodeNum, UNITS units /*=UNITS_C*/) const +{ + if (tankTemps_C.empty()) + { + 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)) { can't happen? + // 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); + } + double beginFraction = static_cast(iTCouple - 1.) / static_cast(nTCouple); + double endFraction = static_cast(iTCouple) / static_cast(nTCouple); + + double simTcoupleTemp_C = getResampledValue(tankTemps_C, beginFraction, endFraction); + if (units == UNITS_C) + { + return simTcoupleTemp_C; + } + else if (units == UNITS_F) + { + return C_TO_F(simTcoupleTemp_C); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getNthSimTcouple. \n"); + } + return double(HPWH_ABORT); + } +} + +int HPWH::getNumHeatSources() const { return static_cast(heatSources.size()); } + +int HPWH::getCompressorIndex() const { return compressorIndex; } + +int HPWH::getNumResistanceElements() const +{ + int count = 0; + for (int i = 0; i < getNumHeatSources(); i++) + { + count += heatSources[i].isAResistance() ? 1 : 0; + } + return count; +} + +double HPWH::getCompressorCapacity(double airTemp /*=19.722*/, + double inletTemp /*=14.444*/, + double outTemp /*=57.222*/, + UNITS pwrUnit /*=UNITS_KW*/, + UNITS tempUnit /*=UNITS_C*/) +{ + // calculate capacity btu/hr, input btu/hr, and cop + double capTemp_BTUperHr, inputTemp_BTUperHr, copTemp; // temporary variables + double airTemp_C, inletTemp_C, outTemp_C; + + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model does not have a compressor. \n"); + } + return double(HPWH_ABORT); + } + + if (tempUnit == UNITS_C) + { + airTemp_C = airTemp; + inletTemp_C = inletTemp; + outTemp_C = outTemp; + } + else if (tempUnit == UNITS_F) + { + airTemp_C = F_TO_C(airTemp); + inletTemp_C = F_TO_C(inletTemp); + outTemp_C = F_TO_C(outTemp); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for temperatures in getCompressorCapacity. \n"); + } + return double(HPWH_ABORT); + } + + if (airTemp_C < heatSources[compressorIndex].minT || + airTemp_C > heatSources[compressorIndex].maxT) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The compress does not operate at the specified air temperature. \n"); + } + return double(HPWH_ABORT); + } + + double maxAllowedSetpoint_C = + heatSources[compressorIndex].maxSetpoint_C - + heatSources[compressorIndex].secondaryHeatExchanger.hotSideTemperatureOffset_dC; + if (outTemp_C > maxAllowedSetpoint_C) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Inputted outlet temperature of the compressor is higher than can be produced."); + } + return double(HPWH_ABORT); + } + + if (heatSources[compressorIndex].isExternalMultipass()) + { + double averageTemp_C = (outTemp_C + inletTemp_C) / 2.; + heatSources[compressorIndex].getCapacityMP( + airTemp_C, averageTemp_C, inputTemp_BTUperHr, capTemp_BTUperHr, copTemp); + } + else + { + heatSources[compressorIndex].getCapacity( + airTemp_C, inletTemp_C, outTemp_C, inputTemp_BTUperHr, capTemp_BTUperHr, copTemp); + } + + double outputCapacity = capTemp_BTUperHr; + if (pwrUnit == UNITS_KW) + { + outputCapacity = BTU_TO_KWH(capTemp_BTUperHr); + } + else if (pwrUnit != UNITS_BTUperHr) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for capacity in getCompressorCapacity. \n"); + } + return double(HPWH_ABORT); + } + + return outputCapacity; +} + +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 >= getNumHeatSources() || 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 heatSources[N].energyInput_kWh; + } + else if (units == UNITS_BTU) + { + return KWH_TO_BTU(heatSources[N].energyInput_kWh); + } + else if (units == UNITS_KJ) + { + return KWH_TO_KJ(heatSources[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 >= getNumHeatSources() || 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 heatSources[N].energyOutput_kWh; + } + else if (units == UNITS_BTU) + { + return KWH_TO_BTU(heatSources[N].energyOutput_kWh); + } + else if (units == UNITS_KJ) + { + return KWH_TO_KJ(heatSources[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 >= getNumHeatSources() || 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 heatSources[N].runtime_min; +} + +int HPWH::isNthHeatSourceRunning(int N) const +{ + if (N >= getNumHeatSources() || 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 (heatSources[N].isEngaged()) + { + return 1; + } + else + { + return 0; + } +} + +HPWH::HEATSOURCE_TYPE HPWH::getNthHeatSourceType(int N) const +{ + if (N >= getNumHeatSources() || N < 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("You have attempted to access the type of a heat source that does not exist. \n"); + } + return HEATSOURCE_TYPE(HPWH_ABORT); + } + return heatSources[N].typeOfHeatSource; +} + +double HPWH::getTankSize(UNITS units /*=UNITS_L*/) 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::getCondenserWaterInletTemp(UNITS units /*=UNITS_C*/) const +{ + if (units == UNITS_C) + { + return condenserInlet_C; + } + else if (units == UNITS_F) + { + return C_TO_F(condenserInlet_C); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getCondenserWaterInletTemp. \n"); + } + return double(HPWH_ABORT); + } +} + +double HPWH::getCondenserWaterOutletTemp(UNITS units /*=UNITS_C*/) const +{ + if (units == UNITS_C) + { + return condenserOutlet_C; + } + else if (units == UNITS_F) + { + return C_TO_F(condenserOutlet_C); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getCondenserWaterInletTemp. \n"); + } + return double(HPWH_ABORT); + } +} + +double HPWH::getExternalVolumeHeated(UNITS units /*=UNITS_L*/) const +{ + if (units == UNITS_L) + { + return externalVolumeHeated_L; + } + else if (units == UNITS_GAL) + { + return L_TO_GAL(externalVolumeHeated_L); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getExternalVolumeHeated. \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 < getNumNodes(); i++) + { + avgTemp += tankTemps_C[i]; + } + avgTemp /= getNumNodes(); + + double totalHeat = avgTemp * DENSITYWATER_kgperL * CPWATER_kJperkgC * tankVolume_L; + return totalHeat; +} + +double HPWH::getLocationTemp_C() const { return locationTemperature_C; } + +int HPWH::getHPWHModel() const { return hpwhModel; } +int HPWH::getCompressorCoilConfig() const +{ + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model does not have a compressor. \n"); + } + return HPWH_ABORT; + } + return heatSources[compressorIndex].configuration; +} +bool HPWH::isCompressorMultipass() const +{ + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model does not have a compressor. \n"); + } + return HPWH_ABORT; + } + return heatSources[compressorIndex].isMultipass; +} +bool HPWH::isCompressoExternalMultipass() const +{ + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model does not have a compressor. \n"); + } + return HPWH_ABORT; + } + return heatSources[compressorIndex].isExternalMultipass(); +} + +bool HPWH::hasACompressor() const { return compressorIndex >= 0; } + +bool HPWH::hasExternalHeatSource() const +{ + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) + { + return true; + } + } + return false; +} + +double HPWH::getExternalMPFlowRate(UNITS units /*=UNITS_GPM*/) const +{ + if (!isCompressoExternalMultipass()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Does not have an external multipass heat source \n"); + } + return HPWH_ABORT; + } + + if (units == HPWH::UNITS_LPS) + { + return heatSources[compressorIndex].mpFlowRate_LPS; + } + else if (units == HPWH::UNITS_GPM) + { + return LPS_TO_GPM(heatSources[compressorIndex].mpFlowRate_LPS); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getExternalMPFlowRate. \n"); + } + return (double)HPWH_ABORT; + } +} + +double HPWH::getCompressorMinRuntime(UNITS units /*=UNITS_MIN*/) const +{ + + if (hasACompressor()) + { + double min_minutes = 10.; + + if (units == UNITS_MIN) + { + return min_minutes; + } + else if (units == UNITS_SEC) + { + return MIN_TO_SEC(min_minutes); + } + else if (units == UNITS_HR) + { + return MIN_TO_HR(min_minutes); + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for getCompressorMinRunTime. \n"); + } + return (double)HPWH_ABORT; + } + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("This HPWH has no compressor. \n"); + } + return (double)HPWH_ABORT; + } +} + +int HPWH::getSizingFractions(double& aquaFract, double& useableFract) const +{ + double aFract = 1.; + double useFract = 1.; + + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model does not have a compressor. \n"); + } + return HPWH_ABORT; + } + else if (usesSoCLogic) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model uses SOC control logic and does not have a definition for sizing " + "fractions. \n"); + } + return HPWH_ABORT; + } + + // Every compressor must have at least one on logic + for (std::shared_ptr onLogic : heatSources[compressorIndex].turnOnLogicSet) + { + double tempA; + + if (hpwhVerbosity >= VRB_emetic) + { + msg("\tturnon logic: %s ", onLogic->description.c_str()); + } + tempA = onLogic->nodeWeightAvgFract(); // if standby logic will return 1 + aFract = tempA < aFract ? tempA : aFract; + } + aquaFract = aFract; + + // Compressors don't need to have an off logic + if (heatSources[compressorIndex].shutOffLogicSet.size() != 0) + { + for (std::shared_ptr offLogic : heatSources[compressorIndex].shutOffLogicSet) + { + + double tempUse; + + if (hpwhVerbosity >= VRB_emetic) + { + msg("\tshutsOff logic: %s ", offLogic->description.c_str()); + } + if (offLogic->description == "large draw" || offLogic->description == "larger draw") + { + tempUse = + 1.; // These logics are just for checking if there's a big draw to switch to RE + } + else + { + tempUse = 1. - offLogic->nodeWeightAvgFract(); + } + useFract = + tempUse < useFract ? tempUse : useFract; // Will return the smallest case of the + // useable fraction for multiple off logics + } + useableFract = useFract; + } + else + { + if (hpwhVerbosity >= VRB_emetic) + { + msg("\no shutoff logics present"); + } + useableFract = 1.; + } + + // Check if double's are approximately equally and adjust the relationship so it follows the + // relationship we expect. The tolerance plays with 0.1 mm in position if the tank is 1m tall... + double temp = 1. - useableFract; + if (aboutEqual(aquaFract, temp)) + { + useableFract = 1. - aquaFract + TOL_MINVALUE; + } + + return 0; +} + +bool HPWH::isHPWHScalable() const { return canScale; } + +int HPWH::setScaleHPWHCapacityCOP(double scaleCapacity /*=1.0*/, double scaleCOP /*=1.0*/) +{ + if (!isHPWHScalable()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not scale the HPWH Capacity or COP \n"); + } + return HPWH_ABORT; + } + if (!hasACompressor()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Current model does not have a compressor. \n"); + } + return HPWH_ABORT; + } + if (scaleCapacity <= 0 || scaleCOP <= 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not scale the HPWH Capacity or COP to 0 or less than 0 \n"); + } + return HPWH_ABORT; + } + + for (auto& perfP : heatSources[compressorIndex].perfMap) + { + if (scaleCapacity != 1.) + { + std::transform( + perfP.inputPower_coeffs.begin(), + perfP.inputPower_coeffs.end(), + perfP.inputPower_coeffs.begin(), + std::bind(std::multiplies(), std::placeholders::_1, scaleCapacity)); + } + if (scaleCOP != 1.) + { + std::transform(perfP.COP_coeffs.begin(), + perfP.COP_coeffs.end(), + perfP.COP_coeffs.begin(), + std::bind(std::multiplies(), std::placeholders::_1, scaleCOP)); + } + } + + return 0; +} + +int HPWH::setCompressorOutputCapacity(double newCapacity, + double airTemp /*=19.722*/, + double inletTemp /*=14.444*/, + double outTemp /*=57.222*/, + UNITS pwrUnit /*=UNITS_KW*/, + UNITS tempUnit /*=UNITS_C*/) +{ + + double oldCapacity = getCompressorCapacity(airTemp, inletTemp, outTemp, pwrUnit, tempUnit); + if (oldCapacity == double(HPWH_ABORT)) + { + return HPWH_ABORT; + } + + double scale = newCapacity / oldCapacity; + return setScaleHPWHCapacityCOP(scale, 1.); // Scale the compressor capacity +} + +int HPWH::setResistanceCapacity(double power, int which /*=-1*/, UNITS pwrUnit /*=UNITS_KW*/) +{ + + // Input checks + if (!isHPWHScalable()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not scale the resistance elements \n"); + } + return HPWH_ABORT; + } + if (getNumResistanceElements() == 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("There are no resistance elements to set capacity for \n"); + } + return HPWH_ABORT; + } + if (which < -1 || which > getNumResistanceElements() - 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Out of bounds value for which in setResistanceCapacity()\n"); + } + return HPWH_ABORT; + } + if (power < 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Can not have a negative input power \n"); + } + return HPWH_ABORT; + } + // Unit conversion + double watts; + if (pwrUnit == UNITS_KW) + { + watts = power * 1000; // kW to W + } + else if (pwrUnit == UNITS_BTUperHr) + { + watts = BTU_TO_KWH(power) * 1000; // BTU to kW then kW to W + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for capacity in setResistanceCapacity. \n"); + } + return HPWH_ABORT; + } + + // Whew so many checks... + if (which == -1) + { + // Just get all the elements + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isAResistance()) + { + heatSources[i].changeResistanceWatts(watts); + } + } + } + else + { + heatSources[resistanceHeightMap[which].index].changeResistanceWatts(watts); + + // Then check for repeats in the position + int pos = resistanceHeightMap[which].position; + for (int i = 0; i < getNumResistanceElements(); i++) + { + if (which != i && resistanceHeightMap[i].position == pos) + { + heatSources[resistanceHeightMap[i].index].changeResistanceWatts(watts); + } + } + } + + return 0; +} + +double HPWH::getResistanceCapacity(int which /*=-1*/, UNITS pwrUnit /*=UNITS_KW*/) +{ + + // Input checks + if (getNumResistanceElements() == 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("There are no resistance elements to return capacity for \n"); + } + return HPWH_ABORT; + } + if (which < -1 || which > getNumResistanceElements() - 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Out of bounds value for which in getResistanceCapacity()\n"); + } + return HPWH_ABORT; + } + + double returnPower = 0; + if (which == -1) + { + // Just get all the elements + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isAResistance()) + { + returnPower += heatSources[i].perfMap[0].inputPower_coeffs[0]; + } + } + } + else + { + // get the power from "which" element by height + returnPower += + heatSources[resistanceHeightMap[which].index].perfMap[0].inputPower_coeffs[0]; + + // Then check for repeats in the position + int pos = resistanceHeightMap[which].position; + for (int i = 0; i < getNumResistanceElements(); i++) + { + if (which != i && resistanceHeightMap[i].position == pos) + { + returnPower += + heatSources[resistanceHeightMap[i].index].perfMap[0].inputPower_coeffs[0]; + } + } + } + + // Unit conversion + if (pwrUnit == UNITS_KW) + { + returnPower /= 1000.; // W to KW + } + else if (pwrUnit == UNITS_BTUperHr) + { + returnPower = KWH_TO_BTU(returnPower / 1000.); // W to BTU/hr + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect unit specification for capacity in getResistanceCapacity. \n"); + } + return HPWH_ABORT; + } + + return returnPower; +} + +int HPWH::getResistancePosition(int elementIndex) const +{ + + if (elementIndex < 0 || elementIndex > getNumHeatSources() - 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Out of bounds value for which in getResistancePosition\n"); + } + return HPWH_ABORT; + } + + if (!heatSources[elementIndex].isAResistance()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("This index is not a resistance element\n"); + } + return HPWH_ABORT; + } + + for (int i = 0; i < heatSources[elementIndex].getCondensitySize(); i++) + { + if (heatSources[elementIndex].condensity[i] == 1) + { // res elements have a condenstiy of 1 at a specific node + return i; + } + } + return HPWH_ABORT; +} + +// the privates +void HPWH::updateTankTemps(double drawVolume_L, + double inletT_C, + double tankAmbientT_C, + double inletVol2_L, + double inletT2_C) +{ + + outletTemp_C = 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) + { + 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; + } + + if (hasHeatExchanger) + { // heat-exchange models + + const double densityTank_kgperL = DENSITYWATER_kgperL; + const double CpTank_kJperkgC = CPWATER_kJperkgC; + + double C_Node_kJperC = CpTank_kJperkgC * densityTank_kgperL * nodeVolume_L; + double C_draw_kJperC = CPWATER_kJperkgC * DENSITYWATER_kgperL * drawVolume_L; + + outletTemp_C = inletT_C; + for (auto& nodeTemp : tankTemps_C) + { + double maxHeatExchange_kJ = C_draw_kJperC * (nodeTemp - outletTemp_C); + double heatExchange_kJ = nodeHeatExchangerEffectiveness * maxHeatExchange_kJ; + + nodeTemp -= heatExchange_kJ / C_Node_kJperC; + outletTemp_C += heatExchange_kJ / C_draw_kJperC; + } + } + else + { + // calculate how many nodes to draw (drawVolume_N) + double drawVolume_N = drawVolume_L / nodeVolume_L; + if (drawVolume_L > tankVolume_L) + { + // if (hpwhVerbosity >= VRB_reluctant) { + // //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; + for (int i = 0; i < getNumNodes(); 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 / getNumNodes() * tankVolume_L + + tankTemps_C[0] * (drawVolume_L - tankVolume_L)) / + drawVolume_L * drawVolume_N; + + drawVolume_N = 0.; + } + + while (drawVolume_N > 0) + { + + // Draw one node at a time + double drawFraction = drawVolume_N > 1. ? 1. : drawVolume_N; + double nodeInletTV = 0.; + + // add temperature for outletT average + outletTemp_C += drawFraction * tankTemps_C[getNumNodes() - 1]; + + double cumInletFraction = 0.; + for (int i = getNumNodes() - 1; i >= lowInletH; i--) + { + + // Reset inlet inputs at this node. + double 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; + + mixTankInversions(); + } + + // fill in average outlet T - it is a weighted averaged, with weights == nodes drawn + outletTemp_C /= (drawVolume_L / nodeVolume_L); + } + + // Account for mixing at the bottom of the tank + if (tankMixesOnDraw && drawVolume_L > 0.) + { + int mixedBelowNode = (int)(getNumNodes() * mixBelowFractionOnDraw); + mixTankNodes(0, mixedBelowNode, 3.0); + } + + } // end if(draw_volume_L > 0) + + // Initialize newTankTemps_C + nextTankTemps_C = tankTemps_C; + + double standbyLossesBottom_kJ = 0.; + double standbyLossesTop_kJ = 0.; + double standbyLossesSides_kJ = 0.; + + // Standby losses from the top and bottom of the tank + { + auto standbyLossRate_kJperHrC = tankUA_kJperHrC * fracAreaTop; + + standbyLossesBottom_kJ = + standbyLossRate_kJperHrC * hoursPerStep * (tankTemps_C[0] - tankAmbientT_C); + standbyLossesTop_kJ = standbyLossRate_kJperHrC * hoursPerStep * + (tankTemps_C[getNumNodes() - 1] - tankAmbientT_C); + + nextTankTemps_C.front() -= standbyLossesBottom_kJ / nodeCp_kJperC; + nextTankTemps_C.back() -= standbyLossesTop_kJ / nodeCp_kJperC; + } + + // Standby losses from the sides of the tank + { + auto standbyLossRate_kJperHrC = + (tankUA_kJperHrC * fracAreaSide + fittingsUA_kJperHrC) / getNumNodes(); + for (int i = 0; i < getNumNodes(); i++) + { + double standbyLosses_kJ = + standbyLossRate_kJperHrC * hoursPerStep * (tankTemps_C[i] - tankAmbientT_C); + standbyLossesSides_kJ += standbyLosses_kJ; + + nextTankTemps_C[i] -= standbyLosses_kJ / nodeCp_kJperC; + } + } + + // Heat transfer between nodes + 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) * + (nodeHeight_m * nodeHeight_m)) * + secondsPerStep; + if (tau > 0.5) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The stability condition for conduction has failed, these results are going to " + "be interesting!\n"); + } + simHasFailed = true; + return; + } + + // End nodes + if (getNumNodes() > 1) + { // inner edges of top and bottom nodes + nextTankTemps_C.front() += 2. * tau * (tankTemps_C[1] - tankTemps_C.front()); + nextTankTemps_C.back() += + 2. * tau * (tankTemps_C[getNumNodes() - 2] - tankTemps_C.back()); + } + + // Internal nodes + for (int i = 1; i < getNumNodes() - 1; i++) + { + nextTankTemps_C[i] += + 2. * tau * (tankTemps_C[i + 1] - 2. * tankTemps_C[i] + tankTemps_C[i - 1]); + } + } + + // Update tankTemps_C + tankTemps_C = nextTankTemps_C; + + standbyLosses_kWh += + KJ_TO_KWH(standbyLossesBottom_kJ + standbyLossesTop_kJ + standbyLossesSides_kJ); + + // check for inverted temperature profile + mixTankInversions(); + +} // end updateTankTemps + +void HPWH::updateSoCIfNecessary() +{ + if (usesSoCLogic) + { + calcAndSetSoCFraction(); + } +} + +// Inversion mixing modeled after bigladder EnergyPlus code PK +void HPWH::mixTankInversions() +{ + bool hasInversion; + const double volumePerNode_L = tankVolume_L / getNumNodes(); + // int numdos = 0; + if (doInversionMixing) + { + do + { + hasInversion = false; + // Start from the top and check downwards + for (int i = getNumNodes() - 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); + } +} + +//----------------------------------------------------------------------------- +/// @brief Adds heat amount qAdd_kJ at and above the node with index nodeNum. +/// Returns unused heat to prevent exceeding maximum or setpoint. +/// @note Moved from HPWH::HeatSource +/// @param[in] qAdd_kJ Amount of heat to add +/// @param[in] nodeNum Lowest node at which to add heat +/// @param[in] maxT_C Maximum allowable temperature to maintain +//----------------------------------------------------------------------------- +double HPWH::addHeatAboveNode(double qAdd_kJ, int nodeNum, const double maxT_C) +{ + + // Do not exceed maxT_C or setpoint + double maxHeatToT_C = std::min(maxT_C, setpoint_C); + + if (hpwhVerbosity >= VRB_emetic) + { + msg("node %2d cap_kwh %.4lf \n", nodeNum, KJ_TO_KWH(qAdd_kJ)); + } + + // find number of nodes at or above nodeNum with the same temperature + int numNodesToHeat = 1; + for (int i = nodeNum; i < getNumNodes() - 1; i++) + { + if (tankTemps_C[i] != tankTemps_C[i + 1]) + { + break; + } + else + { + numNodesToHeat++; + } + } + + while ((qAdd_kJ > 0.) && (nodeNum + numNodesToHeat - 1 < getNumNodes())) + { + + // assume there is another node above the equal-temp nodes + int targetTempNodeNum = nodeNum + numNodesToHeat; + + double heatToT_C; + if (targetTempNodeNum > (getNumNodes() - 1)) + { + // no nodes above the equal-temp nodes; target temperature is the maximum + heatToT_C = maxHeatToT_C; + } + else + { + heatToT_C = tankTemps_C[targetTempNodeNum]; + if (heatToT_C > maxHeatToT_C) + { + // Ensure temperature does not exceed maximum + heatToT_C = maxHeatToT_C; + } + } + + // heat needed to bring all equal-temp nodes up to heatToT_C + double qIncrement_kJ = nodeCp_kJperC * numNodesToHeat * (heatToT_C - tankTemps_C[nodeNum]); + + if (qIncrement_kJ > qAdd_kJ) + { + // insufficient heat to reach heatToT_C; use all available heat + heatToT_C = tankTemps_C[nodeNum] + qAdd_kJ / nodeCp_kJperC / numNodesToHeat; + for (int j = 0; j < numNodesToHeat; ++j) + { + tankTemps_C[nodeNum + j] = heatToT_C; + } + qAdd_kJ = 0.; + } + else if (qIncrement_kJ > 0.) + { // add qIncrement_kJ to raise all equal-temp-nodes to heatToT_C + for (int j = 0; j < numNodesToHeat; ++j) + tankTemps_C[nodeNum + j] = heatToT_C; + qAdd_kJ -= qIncrement_kJ; + } + numNodesToHeat++; + } + + // return any unused heat + return qAdd_kJ; +} + +//----------------------------------------------------------------------------- +/// @brief Adds extra heat amount qAdd_kJ at and above the node with index nodeNum. +/// Does not limit final temperatures. +/// @param[in] qAdd_kJ Amount of heat to add +/// @param[in] nodeNum Lowest node at which to add heat +//----------------------------------------------------------------------------- +void HPWH::addExtraHeatAboveNode(double qAdd_kJ, const int nodeNum) +{ + + if (hpwhVerbosity >= VRB_emetic) + { + msg("node %2d cap_kwh %.4lf \n", nodeNum, KJ_TO_KWH(qAdd_kJ)); + } + + // find number of nodes at or above nodeNum with the same temperature + int numNodesToHeat = 1; + for (int i = nodeNum; i < getNumNodes() - 1; i++) + { + if (tankTemps_C[i] != tankTemps_C[i + 1]) + { + break; + } + else + { + numNodesToHeat++; + } + } + + while ((qAdd_kJ > 0.) && (nodeNum + numNodesToHeat - 1 < getNumNodes())) + { + + // assume there is another node above the equal-temp nodes + int targetTempNodeNum = nodeNum + numNodesToHeat; + + double heatToT_C; + if (targetTempNodeNum > (getNumNodes() - 1)) + { + // no nodes above the equal-temp nodes; target temperature limited by the heat available + heatToT_C = tankTemps_C[nodeNum] + qAdd_kJ / nodeCp_kJperC / numNodesToHeat; + } + else + { + heatToT_C = tankTemps_C[targetTempNodeNum]; + } + + // heat needed to bring all equal-temp nodes up to heatToT_C + double qIncrement_kJ = nodeCp_kJperC * numNodesToHeat * (heatToT_C - tankTemps_C[nodeNum]); + + if (qIncrement_kJ > qAdd_kJ) + { + // insufficient heat to reach heatToT_C; use all available heat + heatToT_C = tankTemps_C[nodeNum] + qAdd_kJ / nodeCp_kJperC / numNodesToHeat; + for (int j = 0; j < numNodesToHeat; ++j) + { + tankTemps_C[nodeNum + j] = heatToT_C; + } + qAdd_kJ = 0.; + } + else if (qIncrement_kJ > 0.) + { // add qIncrement_kJ to raise all equal-temp-nodes to heatToT_C + for (int j = 0; j < numNodesToHeat; ++j) + tankTemps_C[nodeNum + j] = heatToT_C; + qAdd_kJ -= qIncrement_kJ; + } + numNodesToHeat++; + } +} + +//----------------------------------------------------------------------------- +/// @brief Modifies a heat distribution using a thermal distribution. +/// @param[in,out] heatDistribution_W The distribution to be modified +//----------------------------------------------------------------------------- +void HPWH::modifyHeatDistribution(std::vector& heatDistribution_W) +{ + double totalHeat_W = 0.; + for (auto& heatDist_W : heatDistribution_W) + totalHeat_W += heatDist_W; + + if (totalHeat_W == 0.) + return; + + for (auto& heatDist_W : heatDistribution_W) + heatDist_W /= totalHeat_W; + + double shrinkageT_C = findShrinkageT_C(heatDistribution_W); + int lowestNode = findLowestNode(heatDistribution_W, getNumNodes()); + + std::vector modHeatDistribution_W; + calcThermalDist(modHeatDistribution_W, shrinkageT_C, lowestNode, tankTemps_C, setpoint_C); + + heatDistribution_W = modHeatDistribution_W; + for (auto& heatDist_W : heatDistribution_W) + heatDist_W *= totalHeat_W; +} + +//----------------------------------------------------------------------------- +/// @brief Adds extra heat to tank. +/// @param[in] extraHeatDist_W A distribution of extra heat to add +//----------------------------------------------------------------------------- +void HPWH::addExtraHeat(std::vector& extraHeatDist_W) +{ + + auto modHeatDistribution_W = extraHeatDist_W; + modifyHeatDistribution(modHeatDistribution_W); + + std::vector heatDistribution_W(getNumNodes()); + resampleExtensive(heatDistribution_W, modHeatDistribution_W); + + // Unnecessary unit conversions used here to match former method + double tot_qAdded_BTUperHr = 0.; + for (int i = getNumNodes() - 1; i >= 0; i--) + { + if (heatDistribution_W[i] != 0) + { + double qAdd_BTUperHr = KWH_TO_BTU(heatDistribution_W[i] / 1000.); + double qAdd_KJ = BTU_TO_KJ(qAdd_BTUperHr * minutesPerStep / min_per_hr); + addExtraHeatAboveNode(qAdd_KJ, i); + tot_qAdded_BTUperHr += qAdd_BTUperHr; + } + } + // Write the input & output energy + extraEnergyInput_kWh = BTU_TO_KWH(tot_qAdded_BTUperHr * minutesPerStep / min_per_hr); +} + +/////////////////////////////////////////////////////////////////////////////////// + +void HPWH::turnAllHeatSourcesOff() +{ + for (int i = 0; i < getNumHeatSources(); i++) + { + heatSources[i].disengageHeatSource(); + } + isHeating = false; +} + +bool HPWH::areAllHeatSourcesOff() const +{ + bool allOff = true; + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isEngaged() == true) + { + allOff = false; + } + } + return allOff; +} + +double HPWH::tankAvg_C(const std::vector nodeWeights) const +{ + double sum = 0; + double totWeight = 0; + + std::vector resampledTankTemps(LOGIC_NODE_SIZE); + resample(resampledTankTemps, tankTemps_C); + + for (auto& nodeWeight : nodeWeights) + { + if (nodeWeight.nodeNum == 0) + { // bottom node only + sum += tankTemps_C.front() * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + else if (nodeWeight.nodeNum > LOGIC_NODE_SIZE) + { // top node only + sum += tankTemps_C.back() * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + else + { // general case; sum over all weighted nodes + sum += resampledTankTemps[static_cast(nodeWeight.nodeNum - 1)] * + nodeWeight.weight; + totWeight += nodeWeight.weight; + } + } + return sum / totWeight; +} + +void HPWH::mixTankNodes(int mixedAboveNode, int mixedBelowNode, double mixFactor) +{ + double ave = 0.; + double numAvgNodes = (double)(mixedBelowNode - mixedAboveNode); + for (int i = mixedAboveNode; i < mixedBelowNode; i++) + { + ave += tankTemps_C[i]; + } + ave /= numAvgNodes; + + for (int i = mixedAboveNode; i < mixedBelowNode; i++) + { + tankTemps_C[i] += ((ave - tankTemps_C[i]) / mixFactor); + // tankTemps_C[i] = tankTemps_C[i] * (1.0 - 1.0 / mixFactor) + ave / mixFactor; + } +} + +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. The assumption is that the + // aspect ratio is the same for all tanks and is the same for the outside measurements of the + // unit and the inner water tank. + const double tankRad_m = getTankRadius(UNITS_M); + const double tankHeight_m = ASPECTRATIO * tankRad_m; + + nodeVolume_L = tankVolume_L / getNumNodes(); + nodeMass_kg = nodeVolume_L * DENSITYWATER_kgperL; + nodeCp_kJperC = nodeMass_kg * CPWATER_kJperkgC; + nodeHeight_m = tankHeight_m / getNumNodes(); + + // 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 = tankRad_m / (2.0 * (tankHeight_m + tankRad_m)); + + // fracAreaSide is the faction of the area of the cylinder that's not the top or bottom. + fracAreaSide = tankHeight_m / (tankHeight_m + tankRad_m); + + /// Single-node heat-exchange effectiveness + nodeHeatExchangerEffectiveness = + 1. - pow(1. - heatExchangerEffectiveness, 1. / static_cast(getNumNodes())); +} + +void HPWH::calcDerivedValues() +{ + // condentropy/shrinkage and lowestNode are now in calcDerivedHeatingValues() + calcDerivedHeatingValues(); + + calcSizeConstants(); + + mapResRelativePosToHeatSources(); + + // heat source ability to depress temp + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isACompressor()) + { + heatSources[i].depressesTemperature = true; + } + else if (heatSources[i].isAResistance()) + { + heatSources[i].depressesTemperature = false; + } + } +} + +void HPWH::calcDerivedHeatingValues() +{ + static char outputString[MAXOUTSTRING]; // this is used for debugging outputs + + // find condentropy/shrinkage + for (int i = 0; i < getNumHeatSources(); ++i) + { + heatSources[i].Tshrinkage_C = findShrinkageT_C(heatSources[i].condensity); + + if (hpwhVerbosity >= VRB_emetic) + { + msg(outputString, "Heat Source %d \n", i); + msg(outputString, "shrinkage %.2lf \n\n", heatSources[i].Tshrinkage_C); + } + } + + // find lowest node + for (int i = 0; i < getNumHeatSources(); i++) + { + heatSources[i].lowestNode = findLowestNode(heatSources[i].condensity, getNumNodes()); + + if (hpwhVerbosity >= VRB_emetic) + { + msg(outputString, "Heat Source %d \n", i); + msg(outputString, " lowest : %d \n", heatSources[i].lowestNode); + } + } + + // define condenser index and lowest resistance element index + compressorIndex = -1; // Default = No compressor + lowestElementIndex = -1; // Default = No resistance elements + highestElementIndex = -1; // Default = No resistance elements + VIPIndex = -1; // Default = No VIP element + double lowestPos = 1.; + double highestPos = 0.; // -1 to make sure a an element on the bottom can still be identified. + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isACompressor()) + { + compressorIndex = i; // NOTE: Maybe won't work with multiple compressors (last + // compressor will be used) + } + else if (heatSources[i].isAResistance()) + { + // Gets VIP element index + if (heatSources[i].isVIP) + { + if (VIPIndex == -1) + { + VIPIndex = i; + } + else + { + if (hpwhVerbosity >= VRB_minuteOut) + { + msg("More than one resistance element is assigned to VIP"); + }; + } + } + int condensitySize = heatSources[i].getCondensitySize(); + for (int j = 0; j < condensitySize; ++j) + { + double pos = static_cast(j) / condensitySize; + if ((heatSources[i].condensity[j] > 0.) && (pos < lowestPos)) + { + lowestElementIndex = i; + lowestPos = pos; + } + if ((heatSources[i].condensity[j] > 0.) && (pos >= highestPos)) + { + highestElementIndex = i; + highestPos = pos; + } + } + } + } + if (hpwhVerbosity >= VRB_emetic) + { + msg(outputString, " compressorIndex : %d \n", compressorIndex); + msg(outputString, " lowestElementIndex : %d \n", lowestElementIndex); + msg(outputString, " highestElementIndex : %d \n", highestElementIndex); + } + if (hpwhVerbosity >= VRB_emetic) + { + msg(outputString, " VIPIndex : %d \n", VIPIndex); + } + + // heat source ability to depress temp + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isACompressor()) + { + heatSources[i].depressesTemperature = true; + } + else if (heatSources[i].isAResistance()) + { + heatSources[i].depressesTemperature = false; + } + } +} + +void HPWH::mapResRelativePosToHeatSources() +{ + resistanceHeightMap.clear(); + resistanceHeightMap.reserve(getNumResistanceElements()); + + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isAResistance()) + { + resistanceHeightMap.push_back({i, getResistancePosition(i)}); + } + } + + // Sort by height, low to high + std::sort(resistanceHeightMap.begin(), + resistanceHeightMap.end(), + [](const HPWH::resPoint& a, const HPWH::resPoint& b) -> bool + { + return a.position < b.position; // (5 < 5) // evaluates to 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; + // use a returnVal so that all checks are processed and error messages written + + if (getNumHeatSources() <= 0 && hpwhModel != MODELS_StorageTank) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("You must have at least one HeatSource.\n"); + } + returnVal = HPWH_ABORT; + } + + double condensitySum; + // loop through all heat sources to check each for malconfigurations + for (int i = 0; i < getNumHeatSources(); i++) + { + // check the heat source type to make sure it has been set + if (heatSources[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 = heatSources[i].findParent(); + if (heatSources[i].turnOnLogicSet.size() == 0 && + (parent == -1 || heatSources[parent].turnOnLogicSet.size() == 0)) + { + if (hpwhVerbosity >= VRB_reluctant) + { + 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; + } + + // Validate on logics + for (std::shared_ptr logic : heatSources[i].turnOnLogicSet) + { + if (!logic->isValid()) + { + returnVal = HPWH_ABORT; + if (hpwhVerbosity >= VRB_reluctant) + { + msg("On logic at index %i is invalid", i); + } + } + } + // Validate off logics + for (std::shared_ptr logic : heatSources[i].shutOffLogicSet) + { + if (!logic->isValid()) + { + returnVal = HPWH_ABORT; + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Off logic at index %i is invalid", i); + } + } + } + + // check is condensity sums to 1 + condensitySum = 0; + + for (int j = 0; j < heatSources[i].getCondensitySize(); j++) + condensitySum += heatSources[i].condensity[j]; + if (fabs(condensitySum - 1.0) > 1e-6) + { + if (hpwhVerbosity >= VRB_reluctant) + { + 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 (heatSources[i].airflowFreedom > 1.0 || heatSources[i].airflowFreedom <= 0.0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The airflowFreedom must be between 0 and 1 for heatsource %d. \n", i); + } + returnVal = HPWH_ABORT; + } + + if (heatSources[i].isACompressor()) + { + if (heatSources[i].doDefrost) + { + if (heatSources[i].defrostMap.size() < 3) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Defrost logic set to true but no valid defrost map of length 3 or " + "greater set. \n"); + } + returnVal = HPWH_ABORT; + } + if (heatSources[i].configuration != HeatSource::CONFIG_EXTERNAL) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Defrost is only simulated for external compressors. \n"); + } + returnVal = HPWH_ABORT; + } + } + } + if (heatSources[i].configuration == HeatSource::CONFIG_EXTERNAL) + { + + if (heatSources[i].shutOffLogicSet.size() != 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("External heat sources can only have one shut off logic. \n "); + } + returnVal = HPWH_ABORT; + } + if (0 > heatSources[i].externalOutletHeight || + heatSources[i].externalOutletHeight > getNumNodes() - 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("External heat sources need an external outlet height within the bounds " + "from from 0 to numNodes-1. \n"); + } + returnVal = HPWH_ABORT; + } + if (0 > heatSources[i].externalInletHeight || + heatSources[i].externalInletHeight > getNumNodes() - 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("External heat sources need an external inlet height within the bounds " + "from from 0 to numNodes-1. \n"); + } + returnVal = HPWH_ABORT; + } + } + else + { + if (heatSources[i].secondaryHeatExchanger.extraPumpPower_W != 0 || + heatSources[i].secondaryHeatExchanger.extraPumpPower_W) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Heatsource %d is not an external heat source but has an external " + "secondary heat exchanger. \n", + i); + } + returnVal = HPWH_ABORT; + } + } + + // Check performance map + // perfGrid and perfGridValues, and the length of vectors in perfGridValues are equal and + // that ; + if (heatSources[i].useBtwxtGrid) + { + // If useBtwxtGrid is true that the perfMap is empty + if (heatSources[i].perfMap.size() != 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Using the grid lookups but a regression based perforamnce map is given " + "\n"); + } + returnVal = HPWH_ABORT; + } + + // Check length of vectors in perfGridValue are equal + if (heatSources[i].perfGridValues[0].size() != + heatSources[i].perfGridValues[1].size() && + heatSources[i].perfGridValues[0].size() != 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("When using grid lookups for perfmance the vectors in perfGridValues must " + "be the same length. \n"); + } + returnVal = HPWH_ABORT; + } + + // Check perfGrid's vectors lengths multiplied together == the perfGridValues vector + // lengths + size_t expLength = 1; + for (const auto& v : heatSources[i].perfGrid) + { + expLength *= v.size(); + } + if (expLength != heatSources[i].perfGridValues[0].size()) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("When using grid lookups for perfmance the vectors in perfGridValues must " + "be the same length. \n"); + } + returnVal = HPWH_ABORT; + } + } + else + { + // Check that perfmap only has 1 point if config_external and multipass + if (heatSources[i].isExternalMultipass() && heatSources[i].perfMap.size() != 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("External multipass heat sources must have a perfMap of only one point " + "with regression equations. \n"); + } + returnVal = HPWH_ABORT; + } + } + } + + // Check that the on logic and off logics are ordered properly + if (hasACompressor()) + { + double aquaF = 0., useF = 1.; + getSizingFractions(aquaF, useF); + if (aquaF < (1. - useF)) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("The relationship between the on logic and off logic is not supported. The off " + "logic is beneath the on logic."); + } + returnVal = HPWH_ABORT; + } + } + + double maxTemp; + string why; + double tempSetpoint = setpoint_C; + if (!isNewSetpointPossible(tempSetpoint, maxTemp, why)) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Cannot set new setpoint. %s", why.c_str()); + } + returnVal = HPWH_ABORT; + } + + // Check if the UA is out of bounds + if (tankUA_kJperHrC < 0.0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + 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; + } + + // Check single-node heat-exchange effectiveness validity + if (heatExchangerEffectiveness > 1.) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Heat-exchanger effectiveness cannot exceed 1.\n"); + } + returnVal = HPWH_ABORT; + } + + // if there's no failures, return 0 + return returnVal; +} + +bool HPWH::shouldDRLockOut(HEATSOURCE_TYPE hs, DRMODES DR_signal) const +{ + + if (hs == TYPE_compressor && (DR_signal & DR_LOC) != 0) + { + return true; + } + else if (hs == TYPE_resistance && (DR_signal & DR_LOR) != 0) + { + return true; + } + return false; +} + +void HPWH::resetTopOffTimer() { timerTOT = 0.; } + +//----------------------------------------------------------------------------- +/// @brief Checks whether energy is balanced during a simulation step. /// @note Used in test/main.cc /// @param[in] drawVol_L Water volume drawn during simulation step /// @param[in] prevHeatContent_kJ Heat content of tank prior to simulation step -/// @param[in] fracEnergyTolerance Fractional tolerance for energy imbalance +/// @param[in] fracEnergyTolerance Fractional tolerance for energy imbalance /// @return true if balanced; false otherwise. //----------------------------------------------------------------------------- -bool HPWH::isEnergyBalanced( - const double drawVol_L,const double prevHeatContent_kJ,const double fracEnergyTolerance /* = 0.001 */) -{ - // Check energy balancing. - double qInElectrical_kJ = 0.; - for (int iHS = 0; iHS < getNumHeatSources(); iHS++) { - qInElectrical_kJ += getNthHeatSourceEnergyInput(iHS, UNITS_KJ); - } - - double qInExtra_kJ = KWH_TO_KJ(extraEnergyInput_kWh); - double qInHeatSourceEnviron_kJ = getEnergyRemovedFromEnvironment(UNITS_KJ); - double qOutTankEnviron_kJ = KWH_TO_KJ(standbyLosses_kWh); - double qOutWater_kJ = drawVol_L * (outletTemp_C - member_inletT_C) * DENSITYWATER_kgperL * CPWATER_kJperkgC; // assumes only one inlet - double expectedTankHeatContent_kJ = - prevHeatContent_kJ // previous heat content - + qInElectrical_kJ // electrical energy delivered to heat sources - + qInExtra_kJ // extra energy delivered to heat sources - + qInHeatSourceEnviron_kJ // heat extracted from environment by condenser - - qOutTankEnviron_kJ // heat released from tank to environment - - qOutWater_kJ; // heat expelled to outlet by water flow - - double qBal_kJ = getTankHeatContent_kJ() - expectedTankHeatContent_kJ; - - double fracEnergyDiff = fabs(qBal_kJ) / std::max(prevHeatContent_kJ, 1.); - if(fracEnergyDiff > fracEnergyTolerance) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Energy-balance error: %f kJ, %f %% \n",qBal_kJ,100. * fracEnergyDiff); - } - return false; - } - return true; +bool HPWH::isEnergyBalanced(const double drawVol_L, + const double prevHeatContent_kJ, + const double fracEnergyTolerance /* = 0.001 */) +{ + // Check energy balancing. + double qInElectrical_kJ = 0.; + for (int iHS = 0; iHS < getNumHeatSources(); iHS++) + { + qInElectrical_kJ += getNthHeatSourceEnergyInput(iHS, UNITS_KJ); + } + + double qInExtra_kJ = KWH_TO_KJ(extraEnergyInput_kWh); + double qInHeatSourceEnviron_kJ = getEnergyRemovedFromEnvironment(UNITS_KJ); + double qOutTankEnviron_kJ = KWH_TO_KJ(standbyLosses_kWh); + double qOutWater_kJ = drawVol_L * (outletTemp_C - member_inletT_C) * DENSITYWATER_kgperL * + CPWATER_kJperkgC; // assumes only one inlet + double expectedTankHeatContent_kJ = + prevHeatContent_kJ // previous heat content + + qInElectrical_kJ // electrical energy delivered to heat sources + + qInExtra_kJ // extra energy delivered to heat sources + + qInHeatSourceEnviron_kJ // heat extracted from environment by condenser + - qOutTankEnviron_kJ // heat released from tank to environment + - qOutWater_kJ; // heat expelled to outlet by water flow + + double qBal_kJ = getTankHeatContent_kJ() - expectedTankHeatContent_kJ; + + double fracEnergyDiff = fabs(qBal_kJ) / std::max(prevHeatContent_kJ, 1.); + if (fracEnergyDiff > fracEnergyTolerance) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Energy-balance error: %f kJ, %f %% \n", qBal_kJ, 100. * fracEnergyDiff); + } + return false; + } + return true; } #ifndef HPWH_ABRIDGED -int HPWH::HPWHinit_file(string configFile) { - - setAllDefaults(); // reset all defaults if you're re-initilizing - // sets simHasFailed = true; this gets cleared on successful completion of init - // return 0 on success, HPWH_ABORT for failure - - //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,tempInt; - std::size_t num_nodes = 0, numHeatSources = 0; - bool hasInitialTankTemp = false; - double initalTankT_C = F_TO_C(120.); - - string tempString,units; - double tempDouble; - - //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 >> num_nodes; - } 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 == "mixBelowFractionOnDraw") { - line_ss >> tempDouble; - if(tempDouble < 0 || tempDouble > 1) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Out of bounds value for %s. Should be between 0 and 1. \n",token.c_str()); - } - return HPWH_ABORT; - } - mixBelowFractionOnDraw = tempDouble; - } 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 == "initialTankTemp") { - line_ss >> tempDouble >> units; - if(units == "F") tempDouble = F_TO_C(tempDouble); - else if(units == "C"); - else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect units specification for %s. \n",token.c_str()); - } - return HPWH_ABORT; - } - initalTankT_C = tempDouble; - hasInitialTankTemp = true; - } else if(token == "hasHeatExchanger") { - // false of this model uses heat exchange - line_ss >> tempString; - if(tempString == "true") hasHeatExchanger = true; - else if(tempString == "false") hasHeatExchanger = false; - else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Improper value for %s\n",token.c_str()); - } - return HPWH_ABORT; - } - } else if(token == "heatExchangerEffectiveness") { - // applies to heat-exchange models only - line_ss >> tempDouble; - heatExchangerEffectiveness = tempDouble; - } 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; - heatSources.reserve(numHeatSources); - for(int i = 0; i < numHeatSources; i++) { - heatSources.emplace_back(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") heatSources[heatsource].isVIP = true; - else if(tempString == "false") heatSources[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") heatSources[heatsource].isOn = true; - else if(tempString == "false") heatSources[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; - } - heatSources[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; - } - heatSources[heatsource].maxT = tempDouble; - } else if(token == "onlogic" || token == "offlogic" || token == "standbylogic") { - 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 > LOGIC_NODE_SIZE + 1 || nodeNum < 0) { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Node number for heatsource %d %s must be between 0 and %d. \n",heatsource,token.c_str(), LOGIC_NODE_SIZE + 1); - } - 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 match 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]); - } - std::shared_ptr logic = std::make_shared("custom",nodeWeights,tempDouble,this,absolute,compare); - if(token == "onlogic") { - heatSources[heatsource].addTurnOnLogic(logic); - } else if(token == "offlogic") { - heatSources[heatsource].addShutOffLogic(std::move(logic)); - } else { // standby logic - heatSources[heatsource].standbyLogic = std::make_shared("standby logic",nodeWeights,tempDouble,this,absolute,compare); - } - } else if(token == "onlogic") { - std::string nextToken; - line_ss >> nextToken; - bool absolute = (nextToken == "absolute"); - if(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; - } - line_ss >> tempDouble; - } - else { - tempDouble = std::stod(nextToken); - } - line_ss >> units; - 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; - } - if(tempString == "wholeTank") { - heatSources[heatsource].addTurnOnLogic(HPWH::wholeTank(tempDouble,UNITS_C,absolute)); - } else if(tempString == "topThird") { - heatSources[heatsource].addTurnOnLogic(HPWH::topThird(tempDouble)); - } else if(tempString == "bottomThird") { - heatSources[heatsource].addTurnOnLogic(HPWH::bottomThird(tempDouble)); - } else if(tempString == "standby") { - heatSources[heatsource].addTurnOnLogic(HPWH::standby(tempDouble)); - } else if(tempString == "bottomSixth") { - heatSources[heatsource].addTurnOnLogic(HPWH::bottomSixth(tempDouble)); - } else if(tempString == "secondSixth") { - heatSources[heatsource].addTurnOnLogic(HPWH::secondSixth(tempDouble)); - } else if(tempString == "thirdSixth") { - heatSources[heatsource].addTurnOnLogic(HPWH::thirdSixth(tempDouble)); - } else if(tempString == "fourthSixth") { - heatSources[heatsource].addTurnOnLogic(HPWH::fourthSixth(tempDouble)); - } else if(tempString == "fifthSixth") { - heatSources[heatsource].addTurnOnLogic(HPWH::fifthSixth(tempDouble)); - } else if(tempString == "topSixth") { - heatSources[heatsource].addTurnOnLogic(HPWH::topSixth(tempDouble)); - } else if(tempString == "bottomHalf") { - heatSources[heatsource].addTurnOnLogic(HPWH::bottomHalf(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") { - heatSources[heatsource].addShutOffLogic(HPWH::topNodeMaxTemp(tempDouble)); - } else if(tempString == "bottomNodeMaxTemp") { - heatSources[heatsource].addShutOffLogic(HPWH::bottomNodeMaxTemp(tempDouble)); - } else if(tempString == "bottomTwelfthMaxTemp") { - heatSources[heatsource].addShutOffLogic(HPWH::bottomTwelfthMaxTemp(tempDouble)); - } else if(tempString == "bottomSixthMaxTemp") { - heatSources[heatsource].addShutOffLogic(HPWH::bottomSixthMaxTemp(tempDouble)); - } else if(tempString == "largeDraw") { - heatSources[heatsource].addShutOffLogic(HPWH::largeDraw(tempDouble)); - } else if(tempString == "largerDraw") { - heatSources[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") { - heatSources[heatsource].typeOfHeatSource = TYPE_resistance; - } else if(tempString == "compressor") { - heatSources[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") { - heatSources[heatsource].configuration = HeatSource::CONFIG_WRAPPED; - } else if(tempString == "submerged") { - heatSources[heatsource].configuration = HeatSource::CONFIG_SUBMERGED; - } else if(tempString == "external") { - heatSources[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 == "heatCycle") { - line_ss >> tempString; - if(tempString == "singlepass") { - heatSources[heatsource].isMultipass = false; - } else if(tempString == "multipass") { - heatSources[heatsource].isMultipass = true; - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n",token.c_str(),heatsource); - } - return HPWH_ABORT; - } - } - - else if(token == "externalInlet") { - line_ss >> tempInt; - if(tempInt < num_nodes && tempInt >= 0) { - heatSources[heatsource].externalInletHeight = tempInt; - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n",token.c_str(),heatsource); - } - return HPWH_ABORT; - } - } else if(token == "externalOutlet") { - line_ss >> tempInt; - if(tempInt < num_nodes && tempInt >= 0) { - heatSources[heatsource].externalOutletHeight = tempInt; - } else { - if(hpwhVerbosity >= VRB_reluctant) { - msg("Improper %s for heat source %d\n",token.c_str(),heatsource); - } - return HPWH_ABORT; - } - } - - else if(token == "condensity") { - double x; - std::vector condensity; - while (line_ss >> x) - condensity.push_back(x); - heatSources[heatsource].setCondensity(condensity); - } else if(token == "nTemps") { - line_ss >> nTemps; - heatSources[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 = (int)heatSources[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 >> 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; - } - heatSources[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 = (int)heatSources[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") { - heatSources[heatsource].perfMap[nTemps - 1].inputPower_coeffs.push_back(tempDouble); - } else if(var == "cop") { - heatSources[heatsource].perfMap[nTemps - 1].COP_coeffs.push_back(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; - } - heatSources[heatsource].hysteresis_dC = tempDouble; - } else if(token == "backupSource") { - line_ss >> sourceNum; - heatSources[heatsource].backupHeatSource = &heatSources[sourceNum]; - } else if(token == "companionSource") { - line_ss >> sourceNum; - heatSources[heatsource].companionHeatSource = &heatSources[sourceNum]; - } else if(token == "followedBySource") { - line_ss >> sourceNum; - heatSources[heatsource].followedByHeatSource = &heatSources[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.resize(num_nodes); - - if (hasInitialTankTemp) - setTankToTemperature(initalTankT_C); - else - resetTankToSetpoint(); - - nextTankTemps_C.resize(num_nodes); - - isHeating = false; - for(int i = 0; i < getNumHeatSources(); i++) { - if(heatSources[i].isOn) { - isHeating = true; - } - heatSources[i].sortPerformanceMap(); - } - - calcDerivedValues(); - - if(checkInputs() == HPWH_ABORT) { - return HPWH_ABORT; - } - simHasFailed = false; - return 0; -} -#endif \ No newline at end of file +int HPWH::HPWHinit_file(string configFile) +{ + + setAllDefaults(); // reset all defaults if you're re-initilizing + // sets simHasFailed = true; this gets cleared on successful completion of init + // return 0 on success, HPWH_ABORT for failure + + // 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, tempInt; + std::size_t num_nodes = 0, numHeatSources = 0; + bool hasInitialTankTemp = false; + double initalTankT_C = F_TO_C(120.); + + string tempString, units; + double tempDouble; + + // 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 >> num_nodes; + } + 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 == "mixBelowFractionOnDraw") + { + line_ss >> tempDouble; + if (tempDouble < 0 || tempDouble > 1) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Out of bounds value for %s. Should be between 0 and 1. \n", token.c_str()); + } + return HPWH_ABORT; + } + mixBelowFractionOnDraw = tempDouble; + } + 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 == "initialTankTemp") + { + line_ss >> tempDouble >> units; + if (units == "F") + tempDouble = F_TO_C(tempDouble); + else if (units == "C") + ; + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect units specification for %s. \n", token.c_str()); + } + return HPWH_ABORT; + } + initalTankT_C = tempDouble; + hasInitialTankTemp = true; + } + else if (token == "hasHeatExchanger") + { + // false of this model uses heat exchange + line_ss >> tempString; + if (tempString == "true") + hasHeatExchanger = true; + else if (tempString == "false") + hasHeatExchanger = false; + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Improper value for %s\n", token.c_str()); + } + return HPWH_ABORT; + } + } + else if (token == "heatExchangerEffectiveness") + { + // applies to heat-exchange models only + line_ss >> tempDouble; + heatExchangerEffectiveness = tempDouble; + } + 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; + heatSources.reserve(numHeatSources); + for (int i = 0; i < numHeatSources; i++) + { + heatSources.emplace_back(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") + heatSources[heatsource].isVIP = true; + else if (tempString == "false") + heatSources[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") + heatSources[heatsource].isOn = true; + else if (tempString == "false") + heatSources[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; + } + heatSources[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; + } + heatSources[heatsource].maxT = tempDouble; + } + else if (token == "onlogic" || token == "offlogic" || token == "standbylogic") + { + 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 > LOGIC_NODE_SIZE + 1 || nodeNum < 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Node number for heatsource %d %s must be between 0 and %d. " + "\n", + heatsource, + token.c_str(), + LOGIC_NODE_SIZE + 1); + } + 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 match 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]); + } + std::shared_ptr logic = + std::make_shared( + "custom", nodeWeights, tempDouble, this, absolute, compare); + if (token == "onlogic") + { + heatSources[heatsource].addTurnOnLogic(logic); + } + else if (token == "offlogic") + { + heatSources[heatsource].addShutOffLogic(std::move(logic)); + } + else + { // standby logic + heatSources[heatsource].standbyLogic = + std::make_shared( + "standby logic", nodeWeights, tempDouble, this, absolute, compare); + } + } + else if (token == "onlogic") + { + std::string nextToken; + line_ss >> nextToken; + bool absolute = (nextToken == "absolute"); + if (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; + } + line_ss >> tempDouble; + } + else + { + tempDouble = std::stod(nextToken); + } + line_ss >> units; + 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; + } + if (tempString == "wholeTank") + { + heatSources[heatsource].addTurnOnLogic( + HPWH::wholeTank(tempDouble, UNITS_C, absolute)); + } + else if (tempString == "topThird") + { + heatSources[heatsource].addTurnOnLogic(HPWH::topThird(tempDouble)); + } + else if (tempString == "bottomThird") + { + heatSources[heatsource].addTurnOnLogic(HPWH::bottomThird(tempDouble)); + } + else if (tempString == "standby") + { + heatSources[heatsource].addTurnOnLogic(HPWH::standby(tempDouble)); + } + else if (tempString == "bottomSixth") + { + heatSources[heatsource].addTurnOnLogic(HPWH::bottomSixth(tempDouble)); + } + else if (tempString == "secondSixth") + { + heatSources[heatsource].addTurnOnLogic(HPWH::secondSixth(tempDouble)); + } + else if (tempString == "thirdSixth") + { + heatSources[heatsource].addTurnOnLogic(HPWH::thirdSixth(tempDouble)); + } + else if (tempString == "fourthSixth") + { + heatSources[heatsource].addTurnOnLogic(HPWH::fourthSixth(tempDouble)); + } + else if (tempString == "fifthSixth") + { + heatSources[heatsource].addTurnOnLogic(HPWH::fifthSixth(tempDouble)); + } + else if (tempString == "topSixth") + { + heatSources[heatsource].addTurnOnLogic(HPWH::topSixth(tempDouble)); + } + else if (tempString == "bottomHalf") + { + heatSources[heatsource].addTurnOnLogic(HPWH::bottomHalf(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") + { + heatSources[heatsource].addShutOffLogic(HPWH::topNodeMaxTemp(tempDouble)); + } + else if (tempString == "bottomNodeMaxTemp") + { + heatSources[heatsource].addShutOffLogic( + HPWH::bottomNodeMaxTemp(tempDouble)); + } + else if (tempString == "bottomTwelfthMaxTemp") + { + heatSources[heatsource].addShutOffLogic( + HPWH::bottomTwelfthMaxTemp(tempDouble)); + } + else if (tempString == "bottomSixthMaxTemp") + { + heatSources[heatsource].addShutOffLogic( + HPWH::bottomSixthMaxTemp(tempDouble)); + } + else if (tempString == "largeDraw") + { + heatSources[heatsource].addShutOffLogic(HPWH::largeDraw(tempDouble)); + } + else if (tempString == "largerDraw") + { + heatSources[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") + { + heatSources[heatsource].typeOfHeatSource = TYPE_resistance; + } + else if (tempString == "compressor") + { + heatSources[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") + { + heatSources[heatsource].configuration = HeatSource::CONFIG_WRAPPED; + } + else if (tempString == "submerged") + { + heatSources[heatsource].configuration = HeatSource::CONFIG_SUBMERGED; + } + else if (tempString == "external") + { + heatSources[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 == "heatCycle") + { + line_ss >> tempString; + if (tempString == "singlepass") + { + heatSources[heatsource].isMultipass = false; + } + else if (tempString == "multipass") + { + heatSources[heatsource].isMultipass = true; + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + + else if (token == "externalInlet") + { + line_ss >> tempInt; + if (tempInt < num_nodes && tempInt >= 0) + { + heatSources[heatsource].externalInletHeight = tempInt; + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + else if (token == "externalOutlet") + { + line_ss >> tempInt; + if (tempInt < num_nodes && tempInt >= 0) + { + heatSources[heatsource].externalOutletHeight = tempInt; + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Improper %s for heat source %d\n", token.c_str(), heatsource); + } + return HPWH_ABORT; + } + } + + else if (token == "condensity") + { + double x; + std::vector condensity; + while (line_ss >> x) + condensity.push_back(x); + heatSources[heatsource].setCondensity(condensity); + } + else if (token == "nTemps") + { + line_ss >> nTemps; + heatSources[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 = (int)heatSources[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 >> 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; + } + heatSources[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 = (int)heatSources[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") + { + heatSources[heatsource].perfMap[nTemps - 1].inputPower_coeffs.push_back( + tempDouble); + } + else if (var == "cop") + { + heatSources[heatsource].perfMap[nTemps - 1].COP_coeffs.push_back(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; + } + heatSources[heatsource].hysteresis_dC = tempDouble; + } + else if (token == "backupSource") + { + line_ss >> sourceNum; + heatSources[heatsource].backupHeatSource = &heatSources[sourceNum]; + } + else if (token == "companionSource") + { + line_ss >> sourceNum; + heatSources[heatsource].companionHeatSource = &heatSources[sourceNum]; + } + else if (token == "followedBySource") + { + line_ss >> sourceNum; + heatSources[heatsource].followedByHeatSource = &heatSources[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.resize(num_nodes); + + if (hasInitialTankTemp) + setTankToTemperature(initalTankT_C); + else + resetTankToSetpoint(); + + nextTankTemps_C.resize(num_nodes); + + isHeating = false; + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isOn) + { + isHeating = true; + } + heatSources[i].sortPerformanceMap(); + } + + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) + { + return HPWH_ABORT; + } + simHasFailed = false; + return 0; +} +#endif diff --git a/src/HPWH.hh b/src/HPWH.hh index 6d7e0437..ccdae473 100644 --- a/src/HPWH.hh +++ b/src/HPWH.hh @@ -10,1320 +10,1494 @@ #include #include -#include //for exit +#include //for exit #include -namespace Btwxt { class RegularGridInterpolator; }; +namespace Btwxt +{ +class RegularGridInterpolator; +}; -//#define HPWH_ABRIDGED +// #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. */ #include "HPWHversion.hh" -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; /** SANCO2 5-23 - MODELS_SANCO2_43 = 120, /**< SANCO2 43 gallon CO2 external heat pump */ - MODELS_SANCO2_83 = 121, /**< SANCO2 83 gallon CO2 external heat pump */ - MODELS_SANCO2_GS3_45HPA_US_SP = 122, /**< SANCO2 80 gallon CO2 external heat pump used for MF */ - MODELS_SANCO2_119 = 123, /**< SANCO2 120 gallon CO2 external heat pump */ - - // Sanden synomyms for backward compatability - // allow unmodified code using HPWHsim to build - MODELS_Sanden40 = MODELS_SANCO2_43, - MODELS_Sanden80 = MODELS_SANCO2_83, - MODELS_Sanden_GS3_45HPA_US_SP = MODELS_SANCO2_GS3_45HPA_US_SP, - MODELS_Sanden120 = MODELS_SANCO2_119, - - // The new-ish Rheem - MODELS_RheemHB50 = 140, /**< Rheem 2014 (?) Model */ - MODELS_RheemHBDR2250 = 141, /**< 50 gallon, 2250 W resistance Rheem HB Duct Ready */ - MODELS_RheemHBDR4550 = 142, /**< 50 gallon, 4500 W resistance Rheem HB Duct Ready */ - MODELS_RheemHBDR2265 = 143, /**< 65 gallon, 2250 W resistance Rheem HB Duct Ready */ - MODELS_RheemHBDR4565 = 144, /**< 65 gallon, 4500 W resistance Rheem HB Duct Ready */ - MODELS_RheemHBDR2280 = 145, /**< 80 gallon, 2250 W resistance Rheem HB Duct Ready */ - MODELS_RheemHBDR4580 = 146, /**< 80 gallon, 4500 W resistance Rheem HB Duct Ready */ - - // The new new Rheem - MODELS_Rheem2020Prem40 = 151, /**< 40 gallon, Rheem 2020 Premium */ - MODELS_Rheem2020Prem50 = 152, /**< 50 gallon, Rheem 2020 Premium */ - MODELS_Rheem2020Prem65 = 153, /**< 65 gallon, Rheem 2020 Premium */ - MODELS_Rheem2020Prem80 = 154, /**< 80 gallon, Rheem 2020 Premium */ - MODELS_Rheem2020Build40 = 155, /**< 40 gallon, Rheem 2020 Builder */ - MODELS_Rheem2020Build50 = 156, /**< 50 gallon, Rheem 2020 Builder */ - MODELS_Rheem2020Build65 = 157, /**< 65 gallon, Rheem 2020 Builder */ - MODELS_Rheem2020Build80 = 158, /**< 80 gallon, Rheem 2020 Builder */ - - // Rheem 120V dedicated-circuit product, no resistance elements - MODELS_RheemPlugInDedicated40 = 1160, /**< 40 gallon, Rheem 120V dedicated-circuit */ - MODELS_RheemPlugInDedicated50 = 1161, /**< 50 gallon, Rheem 120V dedicated-circuit */ - - // Rheem 120V shared-circuit products, no resistance elements. - MODELS_RheemPlugInShared40 = 1150, /**< 40 gallon, Rheem 120V shared-circuit */ - MODELS_RheemPlugInShared50 = 1151, /**< 50 gallon, Rheem 120V shared-circuit */ - MODELS_RheemPlugInShared65 = 1152, /**< 65 gallon, Rheem 120V shared-circuit */ - MODELS_RheemPlugInShared80 = 1153, /**< 80 gallon, Rheem 120V shared-circuit */ - - // The new-ish Stiebel - MODELS_Stiebel220E = 160, /**< Stiebel Eltron (2014 model?) */ - - // Generic water heaters, corresponding to the tiers 1, 2, and 3 - MODELS_Generic1 = 170, /**< Generic Tier 1 */ - MODELS_Generic2 = 171, /**< Generic Tier 2 */ - MODELS_Generic3 = 172, /**< Generic Tier 3 */ - MODELS_UEF2generic = 173, /**< UEF 2.0, modified GE2014STDMode case */ - MODELS_genericCustomUEF = 174, /**< used for creating "generic" model with custom uef*/ - - MODELS_AWHSTier3Generic40 = 175, /**< Generic AWHS Tier 3 50 gallons*/ - MODELS_AWHSTier3Generic50 = 176, /**< Generic AWHS Tier 3 50 gallons*/ - MODELS_AWHSTier3Generic65 = 177, /**< Generic AWHS Tier 3 65 gallons*/ - MODELS_AWHSTier3Generic80 = 178, /**< Generic AWHS Tier 3 80 gallons*/ - - MODELS_StorageTank = 180, /**< Generic Tank without heaters */ - MODELS_TamScalable_SP = 190, /** < HPWH input passed off a poor preforming SP model that has scalable input capacity and COP */ - MODELS_Scalable_MP = 191, /** < Lower performance MP model that has scalable input capacity and COP */ - - // Non-preset models - MODELS_CustomFile = 200, /**< HPWH parameters were input via file */ - MODELS_CustomResTank = 201, /**< HPWH parameters were input via HPWHinit_resTank */ - MODELS_CustomResTankGeneric = 202, /**< HPWH parameters were input via HPWHinit_commercialResTank */ - - // Larger Colmac models in single pass configuration - MODELS_ColmacCxV_5_SP = 210, /**< Colmac CxA_5 external heat pump in Single Pass Mode */ - MODELS_ColmacCxA_10_SP = 211, /**< Colmac CxA_10 external heat pump in Single Pass Mode */ - MODELS_ColmacCxA_15_SP = 212, /**< Colmac CxA_15 external heat pump in Single Pass Mode */ - MODELS_ColmacCxA_20_SP = 213, /**< Colmac CxA_20 external heat pump in Single Pass Mode */ - MODELS_ColmacCxA_25_SP = 214, /**< Colmac CxA_25 external heat pump in Single Pass Mode */ - MODELS_ColmacCxA_30_SP = 215, /**< Colmac CxA_30 external heat pump in Single Pass Mode */ - - // Larger Colmac models in multi pass configuration - MODELS_ColmacCxV_5_MP = 310, /**< Colmac CxA_5 external heat pump in Multi Pass Mode */ - MODELS_ColmacCxA_10_MP = 311, /**< Colmac CxA_10 external heat pump in Multi Pass Mode */ - MODELS_ColmacCxA_15_MP = 312, /**< Colmac CxA_15 external heat pump in Multi Pass Mode */ - MODELS_ColmacCxA_20_MP = 313, /**< Colmac CxA_20 external heat pump in Multi Pass Mode */ - MODELS_ColmacCxA_25_MP = 314, /**< Colmac CxA_25 external heat pump in Multi Pass Mode */ - MODELS_ColmacCxA_30_MP = 315, /**< Colmac CxA_30 external heat pump in Multi Pass Mode */ - - // Larger Nyle models in single pass configuration - MODELS_NyleC25A_SP = 230, /*< Nyle C25A external heat pump in Single Pass Mode */ - MODELS_NyleC60A_SP = 231, /*< Nyle C60A external heat pump in Single Pass Mode */ - MODELS_NyleC90A_SP = 232, /*< Nyle C90A external heat pump in Single Pass Mode */ - MODELS_NyleC125A_SP = 233, /*< Nyle C125A external heat pump in Single Pass Mode */ - MODELS_NyleC185A_SP = 234, /*< Nyle C185A external heat pump in Single Pass Mode */ - MODELS_NyleC250A_SP = 235, /*< Nyle C250A external heat pump in Single Pass Mode */ - // Larger Nyle models with the cold weather package! - MODELS_NyleC60A_C_SP = 241, /*< Nyle C60A external heat pump in Single Pass Mode */ - MODELS_NyleC90A_C_SP = 242, /*< Nyle C90A external heat pump in Single Pass Mode */ - MODELS_NyleC125A_C_SP = 243, /*< Nyle C125A external heat pump in Single Pass Mode */ - MODELS_NyleC185A_C_SP = 244, /*< Nyle C185A external heat pump in Single Pass Mode */ - MODELS_NyleC250A_C_SP = 245, /*< Nyle C250A external heat pump in Single Pass Mode */ - - // Mitsubishi Electric Trane - MODELS_MITSUBISHI_QAHV_N136TAU_HPB_SP = 250, /*< Mitsubishi Electric Trane QAHV external CO2 heat pump */ - - // Larger Nyle models in multi pass configuration - //MODELS_NyleC25A_MP = 330, /*< Nyle C25A external heat pump in Multi Pass Mode */ - MODELS_NyleC60A_MP = 331, /*< Nyle C60A external heat pump in Multi Pass Mode */ - MODELS_NyleC90A_MP = 332, /*< Nyle C90A external heat pump in Multi Pass Mode */ - MODELS_NyleC125A_MP = 333, /*< Nyle C125A external heat pump in Multi Pass Mode */ - MODELS_NyleC185A_MP = 334, /*< Nyle C185A external heat pump in Multi Pass Mode */ - MODELS_NyleC250A_MP = 335, /*< Nyle C250A external heat pump in Multi Pass Mode */ - - MODELS_NyleC60A_C_MP = 341, /*< Nyle C60A external heat pump in Multi Pass Mode */ - MODELS_NyleC90A_C_MP = 342, /*< Nyle C90A external heat pump in Multi Pass Mode */ - MODELS_NyleC125A_C_MP = 343, /*< Nyle C125A external heat pump in Multi Pass Mode */ - MODELS_NyleC185A_C_MP = 344, /*< Nyle C185A external heat pump in Multi Pass Mode */ - MODELS_NyleC250A_C_MP = 345, /*< Nyle C250A external heat pump in Multi Pass Mode */ - - // Large Rheem multi pass models - MODELS_RHEEM_HPHD60HNU_201_MP = 350, - MODELS_RHEEM_HPHD60VNU_201_MP = 351, - MODELS_RHEEM_HPHD135HNU_483_MP = 352, // really bad fit to data due to inconsistency in data - MODELS_RHEEM_HPHD135VNU_483_MP = 353, // really bad fit to data due to inconsistency in data - - MODELS_AquaThermAire = 400 // heat exchanger model - }; - - ///specifies the modes for writing output - ///the specified values are used for >= 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_KW, /**< kilowatt */ - UNITS_BTUperHr, /**< british thermal units per Hour */ - 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 */ - UNITS_MIN, /**< minutes */ - UNITS_SEC, /**< seconds */ - UNITS_HR, /**< hours */ - UNITS_GPM, /**< gallons per minute */ - UNITS_LPS /**< liters per second */ - }; - - /** 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 */ - }; - - /** specifies the extrapolation method based on Tair, from the perfmap for a heat source */ - enum EXTRAP_METHOD { - EXTRAP_LINEAR, /**< the default extrapolates linearly */ - EXTRAP_NEAREST /**< extrapolates using nearest neighbor, will just continue from closest point */ - }; - - /** specifies the unit type for outputs in the CSV file-s */ - enum CSVOPTIONS { - CSVOPT_NONE = 0, - CSVOPT_IPUNITS = 1 << 0, - CSVOPT_IS_DRAWING = 1 << 1 - }; - - 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 { - public: - std::string description; - std::function compare; - - HeatingLogic(std::string desc,double decisionPoint_in,HPWH *hpwh_in, - std::function c,bool isHTS): - description(desc),decisionPoint(decisionPoint_in),hpwh(hpwh_in),compare(c), - isEnteringWaterHighTempShutoff(isHTS) - {}; - - /**< checks that the input is all valid. */ - virtual const bool isValid() = 0; - /**< gets the value for comparing the tank value to, i.e. the target SoC */ - virtual const double getComparisonValue() = 0; - /**< gets the calculated value from the tank, i.e. SoC or tank average of node weights*/ - virtual const double getTankValue() = 0; - /**< function to calculate where the average node for a logic set is. */ - virtual const double nodeWeightAvgFract() = 0; - /**< gets the fraction of a node that has to be heated up to met the turnoff condition*/ - virtual const double getFractToMeetComparisonExternal() = 0; - - virtual int setDecisionPoint(double value) = 0; - double getDecisionPoint() { return decisionPoint; } - bool getIsEnteringWaterHighTempShutoff() { return isEnteringWaterHighTempShutoff; } - - protected: - double decisionPoint; - HPWH* hpwh; - bool isEnteringWaterHighTempShutoff; - }; - - struct SoCBasedHeatingLogic: HeatingLogic { - public: - SoCBasedHeatingLogic(std::string desc,double decisionPoint,HPWH *hpwh, - double hF = -0.05,double tM_C = 43.333,bool constMains = false,double mains_C = 18.333, - std::function c = std::less()): - HeatingLogic(desc,decisionPoint,hpwh,c,false), - hysteresisFraction(hF),tempMinUseful_C(tM_C), - useCostantMains(constMains),constantMains_C(mains_C) - {}; - const bool isValid(); - - const double getComparisonValue(); - const double getTankValue(); - const double nodeWeightAvgFract(); - const double getFractToMeetComparisonExternal(); - const double getMainsT_C(); - const double getTempMinUseful_C(); - int setDecisionPoint(double value); - int setConstantMainsTemperature(double mains_C); - - private: - double tempMinUseful_C; - double hysteresisFraction; - bool useCostantMains; - double constantMains_C; - }; - - struct TempBasedHeatingLogic: HeatingLogic { - public: - TempBasedHeatingLogic(std::string desc,std::vector n, - double decisionPoint,HPWH *hpwh,bool a = false, - std::function c = std::less(), - bool isHTS = false): - HeatingLogic(desc,decisionPoint,hpwh,c,isHTS), - nodeWeights(n),isAbsolute(a) - {}; - - const bool isValid(); - - const double getComparisonValue(); - const double getTankValue(); - const double nodeWeightAvgFract(); - const double getFractToMeetComparisonExternal(); - - int setDecisionPoint(double value); - int setDecisionPoint(double value,bool absolute); - - private: - const bool areNodeWeightsValid(); - - bool isAbsolute; - std::vector nodeWeights; - }; - - std::shared_ptr shutOffSoC(std::string desc,double targetSoC,double hystFract,double tempMinUseful_C, - bool constMains,double mains_C); - std::shared_ptr turnOnSoC(std::string desc,double targetSoC,double hystFract,double tempMinUseful_C, - bool constMains,double mains_C); - - std::shared_ptr wholeTank(double decisionPoint,const UNITS units = UNITS_C, const bool absolute = false); - std::shared_ptr topThird(double decisionPoint); - std::shared_ptr topThird_absolute(double decisionPoint); - std::shared_ptr secondThird(double decisionPoint,const UNITS units = UNITS_C, const bool absolute = false); - std::shared_ptr bottomThird(double decisionPoint); - std::shared_ptr bottomHalf(double decisionPoint) ; - std::shared_ptr bottomTwelfth(double decisionPoint); - std::shared_ptr bottomSixth(double decisionPoint); - std::shared_ptr bottomSixth_absolute(double decisionPoint); - std::shared_ptr secondSixth(double decisionPoint); - std::shared_ptr thirdSixth(double decisionPoint); - std::shared_ptr fourthSixth(double decisionPoint); - std::shared_ptr fifthSixth(double decisionPoint); - std::shared_ptr topSixth(double decisionPoint); - - std::shared_ptr standby(double decisionPoint); - std::shared_ptr topNodeMaxTemp(double decisionPoint); - std::shared_ptr bottomNodeMaxTemp(double decisionPoint,bool isEnteringWaterHighTempShutoff = false); - std::shared_ptr bottomTwelfthMaxTemp(double decisionPoint); - std::shared_ptr topThirdMaxTemp(double decisionPoint); - std::shared_ptr bottomSixthMaxTemp(double decisionPoint); - std::shared_ptr secondSixthMaxTemp(double decisionPoint); - std::shared_ptr fifthSixthMaxTemp(double decisionPoint); - std::shared_ptr topSixthMaxTemp(double decisionPoint); - - std::shared_ptr largeDraw(double decisionPoint); - std::shared_ptr largerDraw(double decisionPoint); - - ///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 reset all member variables to defaults and then - * 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_resTankGeneric(double tankVol_L,double rValue_M2KperW,double upperPower_W,double lowerPower_W); - /**< This function will initialize a HPWH object to be a generic resistance storage water heater, - * with a specific R-Value defined at initalization. - * - * Several assumptions regarding the tank configuration are assumed: the lower element - * is at the bottom, the upper element is at the top third. The controls are the same standard controls for - * the HPWHinit_resTank() - */ - - 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 drawVolume_L,double ambientT_C, - double externalT_C,DRMODES DRstatus,double inletVol2_L = 0.,double inletT2_C = 0., - std::vector* extraHeatDist_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 - */ - - /** An overloaded function that uses takes inletT_C */ - int runOneStep(double inletT_C,double drawVolume_L,double ambientT_C, - double externalT_C,DRMODES DRstatus,double inletVol2_L = 0.,double inletT2_C = 0., - std::vector* extraHeatDist_W = NULL) { - setInletT(inletT_C); - return runOneStep(drawVolume_L,ambientT_C, - externalT_C,DRstatus,inletVol2_L,inletT2_C, - extraHeatDist_W); - }; - - - int runNSteps(int N,double *inletT_C,double *drawVolume_L, - double *tankAmbientT_C,double *heatSourceAmbientT_C, - DRMODES *DRstatus); - /**< 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 - */ - - /** Setters for the what are typically input variables */ - void setInletT(double newInletT_C) { member_inletT_C = newInletT_C; }; - void setMinutesPerStep(double newMinutesPerStep); - - 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. */ - - /**< Sets the tank node temps based on the provided vector of temps, which are mapped onto the - existing nodes, regardless of numNodes. */ - int setTankLayerTemperatures(std::vector setTemps, const UNITS units = UNITS_C); - void getTankTemps(std::vector &tankTemps); - - bool isSetpointFixed() const; /**< is the setpoint allowed to be changed */ - int setSetpoint(double newSetpoint,UNITS units = UNITS_C);/** 0 minutes and < 1440 minutes. */ - double getTimerLimitTOT_minute() const; - /**< Returns the timer limit in minutes for the DR_TOT call. */ - - int getInletHeight(int whichInlet) const; - /**< returns the water inlet height node number */ - - /**< resizes the tankTemp_C and nextTankTemp_C node vectors */ - void setNumNodes(const std::size_t num_nodes); - - /**< returns the number of nodes */ - int getNumNodes() const; - - /**< returns the index of the top node */ - int getIndexTopNode() const; - - double getTankNodeTemp(int nodeNum,UNITS units = UNITS_C) const; - /**< returns the temperature of the water at the specified node - with specified units - or HPWH_ABORT for incorrect node number or unit failure */ - - double getNthSimTcouple(int iTCouple,int nTCouple,UNITS units = UNITS_C) const; - /**< returns the temperature from a set number of virtual "thermocouples" specified by nTCouple, - which are constructed from the node temperature array. Specify iTCouple from 1-nTCouple, - 1 at the bottom using specified units - returns HPWH_ABORT for iTCouple < 0, > nTCouple, or incorrect units */ - - int getNumHeatSources() const; - /**< returns the number of heat sources */ - - int getNumResistanceElements() const; - /**< returns the number of resistance elements */ - - int getCompressorIndex() const; - /**< returns the index of the compressor in the heat source array. - Note only supports HPWHs with one compressor, if multiple will return the last index - of a compressor */ - - double getCompressorCapacity(double airTemp = 19.722,double inletTemp = 14.444,double outTemp = 57.222, - UNITS pwrUnit = UNITS_KW,UNITS tempUnit = UNITS_C); - /**< Returns the heating output capacity of the compressor for the current HPWH model. - Note only supports HPWHs with one compressor, if multiple will return the last index - of a compressor. Outlet temperatures greater than the max allowable setpoints will return an error, but - for compressors with a fixed setpoint the */ - - int setCompressorOutputCapacity(double newCapacity,double airTemp = 19.722,double inletTemp = 14.444,double outTemp = 57.222, - UNITS pwrUnit = UNITS_KW,UNITS tempUnit = UNITS_C); - /**< Sets the heating output capacity of the compressor at the defined air, inlet water, and outlet temperatures. - For multi-pass models the capacity is set as the average between the inletTemp and outTemp since multi-pass models will increase - the water temperature only a few degrees at a time (i.e. maybe 10 degF) until the tank reaches the outTemp, the capacity at - inletTemp might not be accurate for the entire heating cycle. - Note only supports HPWHs with one compressor, if multiple will return the last index - of a compressor */ - - int setScaleHPWHCapacityCOP(double scaleCapacity = 1.,double scaleCOP = 1.); - /**< Scales the heatpump water heater input capacity and COP*/ - - int setResistanceCapacity(double power,int which = -1,UNITS pwrUNIT = UNITS_KW); - /**< Scale the resistance elements in the heat source list. Which heat source is chosen is changes is given by "which" - - If which (-1) sets all the resisistance elements in the tank. - - If which (0, 1, 2...) sets the resistance element in a low to high order. - So if there are 3 elements 0 is the bottom, 1 is the middle, and 2 is the top element, regardless of their order - in heatSources. If the elements exist on at the same node then all of the elements are set. - - The only valid values for which are between -1 and getNumResistanceElements()-1. Since which is defined as the - by the ordered height of the resistance elements it cannot refer to a compressor. - */ - - double getResistanceCapacity(int which = -1,UNITS pwrUNIT = UNITS_KW); - /**< Returns the resistance elements capacity. Which heat source is chosen is changes is given by "which" - - If which (-1) gets all the resisistance elements in the tank. - - If which (0, 1, 2...) sets the resistance element in a low to high order. - So if there are 3 elements 0 is the bottom, 1 is the middle, and 2 is the top element, regardless of their order - in heatSources. If the elements exist on at the same node then all of the elements are set. - - The only valid values for which are between -1 and getNumResistanceElements()-1. Since which is defined as the - by the ordered height of the resistance elements it cannot refer to a compressor. - */ - - int getResistancePosition(int elementIndex) const; - - 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 getCondenserWaterInletTemp(UNITS units = UNITS_C) const; - /**< returns the condenser inlet temperature in the specified units - returns 0 when no HP not running occurs, or HPWH_ABORT for incorrect unit specifier */ - - double getCondenserWaterOutletTemp(UNITS units = UNITS_C) const; - /**< returns the condenser outlet temperature in the specified units - returns 0 when no HP not running occurs, or HPWH_ABORT for incorrect unit specifier */ - - double getExternalVolumeHeated(UNITS units = UNITS_L) const; - /**< returns the volume of water heated in an external in the specified units - returns 0 when no external heat source is running */ - - 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 */ - - int getHPWHModel() const; - /**< get the model number of the HPWHsim model number of the hpwh */ - - int getCompressorCoilConfig() const; - bool isCompressorMultipass() const; - bool isCompressoExternalMultipass() const; - - bool hasACompressor() const; - /**< Returns if the HPWH model has a compressor or not, could be a storage or resistance tank. */ - - bool hasExternalHeatSource() const; - /**< Returns if the HPWH model has any external heat sources or not, could be a compressor or resistance element. */ - double getExternalMPFlowRate(UNITS units = UNITS_GPM) const; - /**< Returns the constant flow rate for an external multipass heat sources. */ - - - double getCompressorMinRuntime(UNITS units = UNITS_MIN) const; - - int getSizingFractions(double &aquafract,double &percentUseable) const; - /**< returns the fraction of total tank volume from the bottom up where the aquastat is - or the turn on logic for the compressor, and the USEable fraction of storage or 1 minus - where the shut off logic is for the compressor. If the logic spans multiple nodes it - returns the weighted average of the nodes */ - - bool isHPWHScalable() const; - /**< returns if the HPWH is scalable or not*/ - - bool shouldDRLockOut(HEATSOURCE_TYPE hs,DRMODES DR_signal) const; - /**< Checks the demand response signal against the different heat source types */ - - void resetTopOffTimer(); - /**< resets variables for timer associated with the DR_TOT call */ - - double getLocationTemp_C() const; - int setMaxTempDepression(double maxDepression,UNITS units = UNITS_C); - - bool hasEnteringWaterHighTempShutOff(int heatSourceIndex); - int setEnteringWaterHighTempShutOff(double highTemp,bool tempIsAbsolute,int heatSourceIndex,UNITS units = UNITS_C); - /**< functions to check for and set specific high temperature shut off logics. - HPWHs can only have one of these, which is at least typical */ - - int setTargetSoCFraction(double target); - - bool canUseSoCControls(); - - int switchToSoCControls(double targetSoC,double hysteresisFraction = 0.05,double tempMinUseful = 43.333,bool constantMainsT = false, - double mainsT = 18.333,UNITS tempUnit = UNITS_C); - - bool isSoCControlled() const; - - /// Checks whether energy is balanced during a simulation step. - bool isEnergyBalanced(const double drawVol_L,const double prevHeatContent_kJ,const double fracEnergyTolerance = 0.001); - - /// Overloaded version of above that allows specification of inlet temperature. - bool isEnergyBalanced(const double drawVol_L,double inletT_C_in,const double prevHeatContent_kJ,const double fracEnergyTolerance) { - setInletT(inletT_C_in); - return isEnergyBalanced(drawVol_L,prevHeatContent_kJ,fracEnergyTolerance); - } - - /// Addition of heat from a normal heat sources; return excess heat, if needed, to prevent exceeding maximum or setpoint - double addHeatAboveNode(double qAdd_kJ,const int nodeNum,const double maxT_C); - - /// Addition of extra heat handled separately from normal heat sources - void addExtraHeatAboveNode(double qAdd_kJ,const int nodeNum); - -private: - class HeatSource; - - void setAllDefaults(); /**< sets all the defaults default */ - - void updateTankTemps(double draw,double inletT_C,double ambientT_C,double inletVol2_L,double inletT2_L); - void mixTankInversions(); - /**< Mixes the any temperature inversions in the tank after all the temperature calculations */ - void updateSoCIfNecessary(); - - bool areAllHeatSourcesOff() const; - /**< test if all the heat sources are off */ - void turnAllHeatSourcesOff(); - /**< disengage each heat source */ - - void addHeatParent(HeatSource *heatSourcePtr,double heatSourceAmbientT_C,double minutesToRun); - - /// adds extra heat to the set of nodes that are at the same temperature, above the - /// specified node number - void modifyHeatDistribution(std::vector &heatDistribution); - void addExtraHeat(std::vector &extraHeatDist_W); - - /// "extra" heat added during a simulation step - double extraEnergyInput_kWh; - - double tankAvg_C(const std::vector nodeWeights) const; - /**< functions to calculate what the temperature in a portion of the tank is */ - - void mixTankNodes(int mixedAboveNode,int mixedBelowNode,double mixFactor); - /**< function to average the nodes in a tank together bewtween the mixed abovenode and mixed below node. */ - - void calcDerivedValues(); - /**< a helper function for the inits, calculating condentropy and the lowest node */ - void calcSizeConstants(); - /**< a helper function to set constants for the UA and tank size*/ - void calcDerivedHeatingValues(); - /**< a helper for the helper, calculating condentropy and the lowest node*/ - void mapResRelativePosToHeatSources(); - /**< a helper function for the inits, creating a mapping function for the position of the resistance elements - to their indexes in heatSources. */ - - int checkInputs(); - /**< a helper function to run a few checks on the HPWH input parameters */ - - double getChargePerNode(double tCold,double tMix,double tHot) const; - - void calcAndSetSoCFraction(); - - 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 */ - - bool tankSizeFixed; - /**< does the HPWH have a constant tank size or can it be changed */ - - bool canScale; - /**< can the HPWH scale capactiy and COP or not */ - - 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 */ - +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; /** SANCO2 5-23 + MODELS_SANCO2_43 = 120, /**< SANCO2 43 gallon CO2 external heat pump */ + MODELS_SANCO2_83 = 121, /**< SANCO2 83 gallon CO2 external heat pump */ + MODELS_SANCO2_GS3_45HPA_US_SP = + 122, /**< SANCO2 80 gallon CO2 external heat pump used for MF */ + MODELS_SANCO2_119 = 123, /**< SANCO2 120 gallon CO2 external heat pump */ + + // Sanden synomyms for backward compatability + // allow unmodified code using HPWHsim to build + MODELS_Sanden40 = MODELS_SANCO2_43, + MODELS_Sanden80 = MODELS_SANCO2_83, + MODELS_Sanden_GS3_45HPA_US_SP = MODELS_SANCO2_GS3_45HPA_US_SP, + MODELS_Sanden120 = MODELS_SANCO2_119, + + // The new-ish Rheem + MODELS_RheemHB50 = 140, /**< Rheem 2014 (?) Model */ + MODELS_RheemHBDR2250 = 141, /**< 50 gallon, 2250 W resistance Rheem HB Duct Ready */ + MODELS_RheemHBDR4550 = 142, /**< 50 gallon, 4500 W resistance Rheem HB Duct Ready */ + MODELS_RheemHBDR2265 = 143, /**< 65 gallon, 2250 W resistance Rheem HB Duct Ready */ + MODELS_RheemHBDR4565 = 144, /**< 65 gallon, 4500 W resistance Rheem HB Duct Ready */ + MODELS_RheemHBDR2280 = 145, /**< 80 gallon, 2250 W resistance Rheem HB Duct Ready */ + MODELS_RheemHBDR4580 = 146, /**< 80 gallon, 4500 W resistance Rheem HB Duct Ready */ + + // The new new Rheem + MODELS_Rheem2020Prem40 = 151, /**< 40 gallon, Rheem 2020 Premium */ + MODELS_Rheem2020Prem50 = 152, /**< 50 gallon, Rheem 2020 Premium */ + MODELS_Rheem2020Prem65 = 153, /**< 65 gallon, Rheem 2020 Premium */ + MODELS_Rheem2020Prem80 = 154, /**< 80 gallon, Rheem 2020 Premium */ + MODELS_Rheem2020Build40 = 155, /**< 40 gallon, Rheem 2020 Builder */ + MODELS_Rheem2020Build50 = 156, /**< 50 gallon, Rheem 2020 Builder */ + MODELS_Rheem2020Build65 = 157, /**< 65 gallon, Rheem 2020 Builder */ + MODELS_Rheem2020Build80 = 158, /**< 80 gallon, Rheem 2020 Builder */ + + // Rheem 120V dedicated-circuit product, no resistance elements + MODELS_RheemPlugInDedicated40 = 1160, /**< 40 gallon, Rheem 120V dedicated-circuit */ + MODELS_RheemPlugInDedicated50 = 1161, /**< 50 gallon, Rheem 120V dedicated-circuit */ + + // Rheem 120V shared-circuit products, no resistance elements. + MODELS_RheemPlugInShared40 = 1150, /**< 40 gallon, Rheem 120V shared-circuit */ + MODELS_RheemPlugInShared50 = 1151, /**< 50 gallon, Rheem 120V shared-circuit */ + MODELS_RheemPlugInShared65 = 1152, /**< 65 gallon, Rheem 120V shared-circuit */ + MODELS_RheemPlugInShared80 = 1153, /**< 80 gallon, Rheem 120V shared-circuit */ + + // The new-ish Stiebel + MODELS_Stiebel220E = 160, /**< Stiebel Eltron (2014 model?) */ + + // Generic water heaters, corresponding to the tiers 1, 2, and 3 + MODELS_Generic1 = 170, /**< Generic Tier 1 */ + MODELS_Generic2 = 171, /**< Generic Tier 2 */ + MODELS_Generic3 = 172, /**< Generic Tier 3 */ + MODELS_UEF2generic = 173, /**< UEF 2.0, modified GE2014STDMode case */ + MODELS_genericCustomUEF = 174, /**< used for creating "generic" model with custom uef*/ + + MODELS_AWHSTier3Generic40 = 175, /**< Generic AWHS Tier 3 50 gallons*/ + MODELS_AWHSTier3Generic50 = 176, /**< Generic AWHS Tier 3 50 gallons*/ + MODELS_AWHSTier3Generic65 = 177, /**< Generic AWHS Tier 3 65 gallons*/ + MODELS_AWHSTier3Generic80 = 178, /**< Generic AWHS Tier 3 80 gallons*/ + + MODELS_StorageTank = 180, /**< Generic Tank without heaters */ + MODELS_TamScalable_SP = 190, /** < HPWH input passed off a poor preforming SP model that has + scalable input capacity and COP */ + MODELS_Scalable_MP = + 191, /** < Lower performance MP model that has scalable input capacity and COP */ + + // Non-preset models + MODELS_CustomFile = 200, /**< HPWH parameters were input via file */ + MODELS_CustomResTank = 201, /**< HPWH parameters were input via HPWHinit_resTank */ + MODELS_CustomResTankGeneric = + 202, /**< HPWH parameters were input via HPWHinit_commercialResTank */ + + // Larger Colmac models in single pass configuration + MODELS_ColmacCxV_5_SP = 210, /**< Colmac CxA_5 external heat pump in Single Pass Mode */ + MODELS_ColmacCxA_10_SP = 211, /**< Colmac CxA_10 external heat pump in Single Pass Mode */ + MODELS_ColmacCxA_15_SP = 212, /**< Colmac CxA_15 external heat pump in Single Pass Mode */ + MODELS_ColmacCxA_20_SP = 213, /**< Colmac CxA_20 external heat pump in Single Pass Mode */ + MODELS_ColmacCxA_25_SP = 214, /**< Colmac CxA_25 external heat pump in Single Pass Mode */ + MODELS_ColmacCxA_30_SP = 215, /**< Colmac CxA_30 external heat pump in Single Pass Mode */ + + // Larger Colmac models in multi pass configuration + MODELS_ColmacCxV_5_MP = 310, /**< Colmac CxA_5 external heat pump in Multi Pass Mode */ + MODELS_ColmacCxA_10_MP = 311, /**< Colmac CxA_10 external heat pump in Multi Pass Mode */ + MODELS_ColmacCxA_15_MP = 312, /**< Colmac CxA_15 external heat pump in Multi Pass Mode */ + MODELS_ColmacCxA_20_MP = 313, /**< Colmac CxA_20 external heat pump in Multi Pass Mode */ + MODELS_ColmacCxA_25_MP = 314, /**< Colmac CxA_25 external heat pump in Multi Pass Mode */ + MODELS_ColmacCxA_30_MP = 315, /**< Colmac CxA_30 external heat pump in Multi Pass Mode */ + + // Larger Nyle models in single pass configuration + MODELS_NyleC25A_SP = 230, /*< Nyle C25A external heat pump in Single Pass Mode */ + MODELS_NyleC60A_SP = 231, /*< Nyle C60A external heat pump in Single Pass Mode */ + MODELS_NyleC90A_SP = 232, /*< Nyle C90A external heat pump in Single Pass Mode */ + MODELS_NyleC125A_SP = 233, /*< Nyle C125A external heat pump in Single Pass Mode */ + MODELS_NyleC185A_SP = 234, /*< Nyle C185A external heat pump in Single Pass Mode */ + MODELS_NyleC250A_SP = 235, /*< Nyle C250A external heat pump in Single Pass Mode */ + // Larger Nyle models with the cold weather package! + MODELS_NyleC60A_C_SP = 241, /*< Nyle C60A external heat pump in Single Pass Mode */ + MODELS_NyleC90A_C_SP = 242, /*< Nyle C90A external heat pump in Single Pass Mode */ + MODELS_NyleC125A_C_SP = 243, /*< Nyle C125A external heat pump in Single Pass Mode */ + MODELS_NyleC185A_C_SP = 244, /*< Nyle C185A external heat pump in Single Pass Mode */ + MODELS_NyleC250A_C_SP = 245, /*< Nyle C250A external heat pump in Single Pass Mode */ + + // Mitsubishi Electric Trane + MODELS_MITSUBISHI_QAHV_N136TAU_HPB_SP = + 250, /*< Mitsubishi Electric Trane QAHV external CO2 heat pump */ + + // Larger Nyle models in multi pass configuration + // MODELS_NyleC25A_MP = 330, /*< Nyle C25A external heat pump in Multi Pass Mode */ + MODELS_NyleC60A_MP = 331, /*< Nyle C60A external heat pump in Multi Pass Mode */ + MODELS_NyleC90A_MP = 332, /*< Nyle C90A external heat pump in Multi Pass Mode */ + MODELS_NyleC125A_MP = 333, /*< Nyle C125A external heat pump in Multi Pass Mode */ + MODELS_NyleC185A_MP = 334, /*< Nyle C185A external heat pump in Multi Pass Mode */ + MODELS_NyleC250A_MP = 335, /*< Nyle C250A external heat pump in Multi Pass Mode */ + + MODELS_NyleC60A_C_MP = 341, /*< Nyle C60A external heat pump in Multi Pass Mode */ + MODELS_NyleC90A_C_MP = 342, /*< Nyle C90A external heat pump in Multi Pass Mode */ + MODELS_NyleC125A_C_MP = 343, /*< Nyle C125A external heat pump in Multi Pass Mode */ + MODELS_NyleC185A_C_MP = 344, /*< Nyle C185A external heat pump in Multi Pass Mode */ + MODELS_NyleC250A_C_MP = 345, /*< Nyle C250A external heat pump in Multi Pass Mode */ + + // Large Rheem multi pass models + MODELS_RHEEM_HPHD60HNU_201_MP = 350, + MODELS_RHEEM_HPHD60VNU_201_MP = 351, + MODELS_RHEEM_HPHD135HNU_483_MP = 352, // really bad fit to data due to inconsistency in data + MODELS_RHEEM_HPHD135VNU_483_MP = 353, // really bad fit to data due to inconsistency in data + + MODELS_AquaThermAire = 400 // heat exchanger model + }; + + /// specifies the modes for writing output + /// the specified values are used for >= 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_KW, /**< kilowatt */ + UNITS_BTUperHr, /**< british thermal units per Hour */ + 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 */ + UNITS_MIN, /**< minutes */ + UNITS_SEC, /**< seconds */ + UNITS_HR, /**< hours */ + UNITS_GPM, /**< gallons per minute */ + UNITS_LPS /**< liters per second */ + }; + + /** 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 */ + }; + + /** specifies the extrapolation method based on Tair, from the perfmap for a heat source */ + enum EXTRAP_METHOD + { + EXTRAP_LINEAR, /**< the default extrapolates linearly */ + EXTRAP_NEAREST /**< extrapolates using nearest neighbor, will just continue from closest + point */ + }; + + /** specifies the unit type for outputs in the CSV file-s */ + enum CSVOPTIONS + { + CSVOPT_NONE = 0, + CSVOPT_IPUNITS = 1 << 0, + CSVOPT_IS_DRAWING = 1 << 1 + }; + + 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 + { + public: + std::string description; + std::function compare; + + HeatingLogic(std::string desc, + double decisionPoint_in, + HPWH* hpwh_in, + std::function c, + bool isHTS) + : description(desc) + , decisionPoint(decisionPoint_in) + , hpwh(hpwh_in) + , compare(c) + , isEnteringWaterHighTempShutoff(isHTS) {}; + + /**< checks that the input is all valid. */ + virtual const bool isValid() = 0; + /**< gets the value for comparing the tank value to, i.e. the target SoC */ + virtual const double getComparisonValue() = 0; + /**< gets the calculated value from the tank, i.e. SoC or tank average of node weights*/ + virtual const double getTankValue() = 0; + /**< function to calculate where the average node for a logic set is. */ + virtual const double nodeWeightAvgFract() = 0; + /**< gets the fraction of a node that has to be heated up to met the turnoff condition*/ + virtual const double getFractToMeetComparisonExternal() = 0; + + virtual int setDecisionPoint(double value) = 0; + double getDecisionPoint() { return decisionPoint; } + bool getIsEnteringWaterHighTempShutoff() { return isEnteringWaterHighTempShutoff; } + + protected: + double decisionPoint; + HPWH* hpwh; + bool isEnteringWaterHighTempShutoff; + }; + + struct SoCBasedHeatingLogic : HeatingLogic + { + public: + SoCBasedHeatingLogic(std::string desc, + double decisionPoint, + HPWH* hpwh, + double hF = -0.05, + double tM_C = 43.333, + bool constMains = false, + double mains_C = 18.333, + std::function c = std::less()) + : HeatingLogic(desc, decisionPoint, hpwh, c, false) + , hysteresisFraction(hF) + , tempMinUseful_C(tM_C) + , useCostantMains(constMains) + , constantMains_C(mains_C) {}; + const bool isValid(); + + const double getComparisonValue(); + const double getTankValue(); + const double nodeWeightAvgFract(); + const double getFractToMeetComparisonExternal(); + const double getMainsT_C(); + const double getTempMinUseful_C(); + int setDecisionPoint(double value); + int setConstantMainsTemperature(double mains_C); + + private: + double tempMinUseful_C; + double hysteresisFraction; + bool useCostantMains; + double constantMains_C; + }; + + struct TempBasedHeatingLogic : HeatingLogic + { + public: + TempBasedHeatingLogic(std::string desc, + std::vector n, + double decisionPoint, + HPWH* hpwh, + bool a = false, + std::function c = std::less(), + bool isHTS = false) + : HeatingLogic(desc, decisionPoint, hpwh, c, isHTS), nodeWeights(n), isAbsolute(a) {}; + + const bool isValid(); + + const double getComparisonValue(); + const double getTankValue(); + const double nodeWeightAvgFract(); + const double getFractToMeetComparisonExternal(); + + int setDecisionPoint(double value); + int setDecisionPoint(double value, bool absolute); + + private: + const bool areNodeWeightsValid(); + + bool isAbsolute; + std::vector nodeWeights; + }; + + std::shared_ptr shutOffSoC(std::string desc, + double targetSoC, + double hystFract, + double tempMinUseful_C, + bool constMains, + double mains_C); + std::shared_ptr turnOnSoC(std::string desc, + double targetSoC, + double hystFract, + double tempMinUseful_C, + bool constMains, + double mains_C); + + std::shared_ptr + wholeTank(double decisionPoint, const UNITS units = UNITS_C, const bool absolute = false); + std::shared_ptr topThird(double decisionPoint); + std::shared_ptr topThird_absolute(double decisionPoint); + std::shared_ptr + secondThird(double decisionPoint, const UNITS units = UNITS_C, const bool absolute = false); + std::shared_ptr bottomThird(double decisionPoint); + std::shared_ptr bottomHalf(double decisionPoint); + std::shared_ptr bottomTwelfth(double decisionPoint); + std::shared_ptr bottomSixth(double decisionPoint); + std::shared_ptr bottomSixth_absolute(double decisionPoint); + std::shared_ptr secondSixth(double decisionPoint); + std::shared_ptr thirdSixth(double decisionPoint); + std::shared_ptr fourthSixth(double decisionPoint); + std::shared_ptr fifthSixth(double decisionPoint); + std::shared_ptr topSixth(double decisionPoint); + + std::shared_ptr standby(double decisionPoint); + std::shared_ptr topNodeMaxTemp(double decisionPoint); + std::shared_ptr + bottomNodeMaxTemp(double decisionPoint, bool isEnteringWaterHighTempShutoff = false); + std::shared_ptr bottomTwelfthMaxTemp(double decisionPoint); + std::shared_ptr topThirdMaxTemp(double decisionPoint); + std::shared_ptr bottomSixthMaxTemp(double decisionPoint); + std::shared_ptr secondSixthMaxTemp(double decisionPoint); + std::shared_ptr fifthSixthMaxTemp(double decisionPoint); + std::shared_ptr topSixthMaxTemp(double decisionPoint); + + std::shared_ptr largeDraw(double decisionPoint); + std::shared_ptr largerDraw(double decisionPoint); + + /// 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 reset all member variables to defaults and then + * 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_resTankGeneric(double tankVol_L, + double rValue_M2KperW, + double upperPower_W, + double lowerPower_W); + /**< This function will initialize a HPWH object to be a generic resistance storage water + * heater, with a specific R-Value defined at initalization. + * + * Several assumptions regarding the tank configuration are assumed: the lower element + * is at the bottom, the upper element is at the top third. The controls are the same standard + * controls for the HPWHinit_resTank() + */ + + 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 drawVolume_L, + double ambientT_C, + double externalT_C, + DRMODES DRstatus, + double inletVol2_L = 0., + double inletT2_C = 0., + std::vector* extraHeatDist_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 + */ + + /** An overloaded function that uses takes inletT_C */ + int runOneStep(double inletT_C, + double drawVolume_L, + double ambientT_C, + double externalT_C, + DRMODES DRstatus, + double inletVol2_L = 0., + double inletT2_C = 0., + std::vector* extraHeatDist_W = NULL) + { + setInletT(inletT_C); + return runOneStep(drawVolume_L, + ambientT_C, + externalT_C, + DRstatus, + inletVol2_L, + inletT2_C, + extraHeatDist_W); + }; + + int runNSteps(int N, + double* inletT_C, + double* drawVolume_L, + double* tankAmbientT_C, + double* heatSourceAmbientT_C, + DRMODES* DRstatus); + /**< 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 + */ + + /** Setters for the what are typically input variables */ + void setInletT(double newInletT_C) { member_inletT_C = newInletT_C; }; + void setMinutesPerStep(double newMinutesPerStep); + + 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. */ + + /**< Sets the tank node temps based on the provided vector of temps, which are mapped onto the + existing nodes, regardless of numNodes. */ + int setTankLayerTemperatures(std::vector setTemps, const UNITS units = UNITS_C); + void getTankTemps(std::vector& tankTemps); + + bool isSetpointFixed() const; /**< is the setpoint allowed to be changed */ + int setSetpoint(double newSetpoint, UNITS units = UNITS_C); /** 0 minutes and < 1440 + * minutes. */ + double getTimerLimitTOT_minute() const; + /**< Returns the timer limit in minutes for the DR_TOT call. */ + + int getInletHeight(int whichInlet) const; + /**< returns the water inlet height node number */ + + /**< resizes the tankTemp_C and nextTankTemp_C node vectors */ + void setNumNodes(const std::size_t num_nodes); + + /**< returns the number of nodes */ + int getNumNodes() const; + + /**< returns the index of the top node */ + int getIndexTopNode() const; + + double getTankNodeTemp(int nodeNum, UNITS units = UNITS_C) const; + /**< returns the temperature of the water at the specified node - with specified units + or HPWH_ABORT for incorrect node number or unit failure */ + + double getNthSimTcouple(int iTCouple, int nTCouple, UNITS units = UNITS_C) const; + /**< returns the temperature from a set number of virtual "thermocouples" specified by nTCouple, + which are constructed from the node temperature array. Specify iTCouple from 1-nTCouple, + 1 at the bottom using specified units + returns HPWH_ABORT for iTCouple < 0, > nTCouple, or incorrect units */ + + int getNumHeatSources() const; + /**< returns the number of heat sources */ + + int getNumResistanceElements() const; + /**< returns the number of resistance elements */ + + int getCompressorIndex() const; + /**< returns the index of the compressor in the heat source array. + Note only supports HPWHs with one compressor, if multiple will return the last index + of a compressor */ + + double getCompressorCapacity(double airTemp = 19.722, + double inletTemp = 14.444, + double outTemp = 57.222, + UNITS pwrUnit = UNITS_KW, + UNITS tempUnit = UNITS_C); + /**< Returns the heating output capacity of the compressor for the current HPWH model. + Note only supports HPWHs with one compressor, if multiple will return the last index + of a compressor. Outlet temperatures greater than the max allowable setpoints will return an + error, but for compressors with a fixed setpoint the */ + + int setCompressorOutputCapacity(double newCapacity, + double airTemp = 19.722, + double inletTemp = 14.444, + double outTemp = 57.222, + UNITS pwrUnit = UNITS_KW, + UNITS tempUnit = UNITS_C); + /**< Sets the heating output capacity of the compressor at the defined air, inlet water, and + outlet temperatures. For multi-pass models the capacity is set as the average between the + inletTemp and outTemp since multi-pass models will increase the water temperature only a few + degrees at a time (i.e. maybe 10 degF) until the tank reaches the outTemp, the capacity at + inletTemp might not be accurate for the entire heating cycle. + Note only supports HPWHs with one compressor, if multiple will return the last index + of a compressor */ + + int setScaleHPWHCapacityCOP(double scaleCapacity = 1., double scaleCOP = 1.); + /**< Scales the heatpump water heater input capacity and COP*/ + + int setResistanceCapacity(double power, int which = -1, UNITS pwrUNIT = UNITS_KW); + /**< Scale the resistance elements in the heat source list. Which heat source is chosen is + changes is given by "which" + - If which (-1) sets all the resisistance elements in the tank. + - If which (0, 1, 2...) sets the resistance element in a low to high order. + So if there are 3 elements 0 is the bottom, 1 is the middle, and 2 is the top element, + regardless of their order in heatSources. If the elements exist on at the same node then all of + the elements are set. + + The only valid values for which are between -1 and getNumResistanceElements()-1. Since which is + defined as the by the ordered height of the resistance elements it cannot refer to a compressor. + */ + + double getResistanceCapacity(int which = -1, UNITS pwrUNIT = UNITS_KW); + /**< Returns the resistance elements capacity. Which heat source is chosen is changes is given + by "which" + - If which (-1) gets all the resisistance elements in the tank. + - If which (0, 1, 2...) sets the resistance element in a low to high order. + So if there are 3 elements 0 is the bottom, 1 is the middle, and 2 is the top element, + regardless of their order in heatSources. If the elements exist on at the same node then all of + the elements are set. + + The only valid values for which are between -1 and getNumResistanceElements()-1. Since which is + defined as the by the ordered height of the resistance elements it cannot refer to a compressor. + */ + + int getResistancePosition(int elementIndex) const; + + 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 getCondenserWaterInletTemp(UNITS units = UNITS_C) const; + /**< returns the condenser inlet temperature in the specified units + returns 0 when no HP not running occurs, or HPWH_ABORT for incorrect unit specifier */ + + double getCondenserWaterOutletTemp(UNITS units = UNITS_C) const; + /**< returns the condenser outlet temperature in the specified units + returns 0 when no HP not running occurs, or HPWH_ABORT for incorrect unit specifier */ + + double getExternalVolumeHeated(UNITS units = UNITS_L) const; + /**< returns the volume of water heated in an external in the specified units + returns 0 when no external heat source is running */ + + 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 */ + + int getHPWHModel() const; + /**< get the model number of the HPWHsim model number of the hpwh */ + + int getCompressorCoilConfig() const; + bool isCompressorMultipass() const; + bool isCompressoExternalMultipass() const; + + bool hasACompressor() const; + /**< Returns if the HPWH model has a compressor or not, could be a storage or resistance tank. + */ + + bool hasExternalHeatSource() const; + /**< Returns if the HPWH model has any external heat sources or not, could be a compressor or + * resistance element. */ + double getExternalMPFlowRate(UNITS units = UNITS_GPM) const; + /**< Returns the constant flow rate for an external multipass heat sources. */ + + double getCompressorMinRuntime(UNITS units = UNITS_MIN) const; + + int getSizingFractions(double& aquafract, double& percentUseable) const; + /**< returns the fraction of total tank volume from the bottom up where the aquastat is + or the turn on logic for the compressor, and the USEable fraction of storage or 1 minus + where the shut off logic is for the compressor. If the logic spans multiple nodes it + returns the weighted average of the nodes */ + + bool isHPWHScalable() const; + /**< returns if the HPWH is scalable or not*/ + + bool shouldDRLockOut(HEATSOURCE_TYPE hs, DRMODES DR_signal) const; + /**< Checks the demand response signal against the different heat source types */ + + void resetTopOffTimer(); + /**< resets variables for timer associated with the DR_TOT call */ + + double getLocationTemp_C() const; + int setMaxTempDepression(double maxDepression, UNITS units = UNITS_C); + + bool hasEnteringWaterHighTempShutOff(int heatSourceIndex); + int setEnteringWaterHighTempShutOff(double highTemp, + bool tempIsAbsolute, + int heatSourceIndex, + UNITS units = UNITS_C); + /**< functions to check for and set specific high temperature shut off logics. + HPWHs can only have one of these, which is at least typical */ + + int setTargetSoCFraction(double target); + + bool canUseSoCControls(); + + int switchToSoCControls(double targetSoC, + double hysteresisFraction = 0.05, + double tempMinUseful = 43.333, + bool constantMainsT = false, + double mainsT = 18.333, + UNITS tempUnit = UNITS_C); + + bool isSoCControlled() const; + + /// Checks whether energy is balanced during a simulation step. + bool isEnergyBalanced(const double drawVol_L, + const double prevHeatContent_kJ, + const double fracEnergyTolerance = 0.001); + + /// Overloaded version of above that allows specification of inlet temperature. + bool isEnergyBalanced(const double drawVol_L, + double inletT_C_in, + const double prevHeatContent_kJ, + const double fracEnergyTolerance) + { + setInletT(inletT_C_in); + return isEnergyBalanced(drawVol_L, prevHeatContent_kJ, fracEnergyTolerance); + } + /// Addition of heat from a normal heat sources; return excess heat, if needed, to prevent + /// exceeding maximum or setpoint + double addHeatAboveNode(double qAdd_kJ, const int nodeNum, const double maxT_C); - MODELS hpwhModel; - /**< The hpwh should know which preset initialized it, or if it was from a fileget */ + /// Addition of extra heat handled separately from normal heat sources + void addExtraHeatAboveNode(double qAdd_kJ, const int nodeNum); - // a std::vector containing the HeatSources, in order of priority - std::vector heatSources; + private: + class HeatSource; - int compressorIndex; - /**< The index of the compressor heat source (set to -1 if no compressor)*/ + void setAllDefaults(); /**< sets all the defaults default */ - int lowestElementIndex; - /**< The index of the lowest resistance element heat source (set to -1 if no resistance elements)*/ + void updateTankTemps( + double draw, double inletT_C, double ambientT_C, double inletVol2_L, double inletT2_L); + void mixTankInversions(); + /**< Mixes the any temperature inversions in the tank after all the temperature calculations */ + void updateSoCIfNecessary(); - int highestElementIndex; - /**< The index of the highest resistance element heat source. if only one element it equals lowestElementIndex (set to -1 if no resistance elements)*/ + bool areAllHeatSourcesOff() const; + /**< test if all the heat sources are off */ + void turnAllHeatSourcesOff(); + /**< disengage each heat source */ - int VIPIndex; - /**< The index of the VIP resistance element heat source (set to -1 if no VIP resistance elements)*/ + void addHeatParent(HeatSource* heatSourcePtr, double heatSourceAmbientT_C, double minutesToRun); - 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 */ + /// adds extra heat to the set of nodes that are at the same temperature, above the + /// specified node number + void modifyHeatDistribution(std::vector& heatDistribution); + void addExtraHeat(std::vector& extraHeatDist_W); - 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 */ + /// "extra" heat added during a simulation step + double extraEnergyInput_kWh; - /**< the volume in liters of the tank */ - double tankVolume_L; + double tankAvg_C(const std::vector nodeWeights) const; + /**< functions to calculate what the temperature in a portion of the tank is */ - /**< the UA of the tank, in metric units */ - double tankUA_kJperHrC; + void mixTankNodes(int mixedAboveNode, int mixedBelowNode, double mixFactor); + /**< function to average the nodes in a tank together bewtween the mixed abovenode and mixed + * below node. */ - /**< the UA of the fittings for the tank, in metric units */ - double fittingsUA_kJperHrC; + void calcDerivedValues(); + /**< a helper function for the inits, calculating condentropy and the lowest node */ + void calcSizeConstants(); + /**< a helper function to set constants for the UA and tank size*/ + void calcDerivedHeatingValues(); + /**< a helper for the helper, calculating condentropy and the lowest node*/ + void mapResRelativePosToHeatSources(); + /**< a helper function for the inits, creating a mapping function for the position of the + resistance elements to their indexes in heatSources. */ - /**< the volume (L) of a single node */ - double nodeVolume_L; + int checkInputs(); + /**< a helper function to run a few checks on the HPWH input parameters */ - /**< the mass of water (kg) in a single node */ - double nodeMass_kg; + double getChargePerNode(double tCold, double tMix, double tHot) const; - /**< the heat capacity of the water (kJ/�C) in a single node */ - double nodeCp_kJperC; + void calcAndSetSoCFraction(); - /**< the height in meters of the one node */ - double nodeHeight_m; + 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; - double fracAreaTop; - /**< the fraction of the UA on the top and bottom of the tank, assuming it's a cylinder */ - double fracAreaSide; - /**< the fraction of the UA on the sides of the tank, assuming it's a cylinder */ + bool simHasFailed; + /**< did an internal error cause the simulation to fail? */ - double currentSoCFraction; - /**< the current state of charge according to the logic */ + bool isHeating; + /**< is the hpwh currently heating or not? */ - double setpoint_C; - /**< the setpoint of the tank */ + bool setpointFixed; + /**< does the HPWH allow the setpoint to vary */ - /**< holds the temperature of each node - 0 is the bottom node */ - std::vector tankTemps_C; + bool tankSizeFixed; + /**< does the HPWH have a constant tank size or can it be changed */ - /**< holds the future temperature of each node for the conduction calculation - 0 is the bottom node */ - std::vector nextTankTemps_C; + bool canScale; + /**< can the HPWH scale capactiy and COP or not */ - DRMODES prevDRstatus; - /**< the DRstatus of the tank in the previous time step and at the end of runOneStep */ + VERBOSITY hpwhVerbosity; + /**< an enum to let the sim know how much output to say */ - double timerLimitTOT; - /**< the time limit in minutes on the timer when the compressor and resistance elements are turned back on, used with DR_TOT. */ - double timerTOT; - /**< the timer used for DR_TOT to turn on the compressor and resistance elements. */ + 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 */ - bool usesSoCLogic; + MODELS hpwhModel; + /**< The hpwh should know which preset initialized it, or if it was from a fileget */ - // Some outputs - double outletTemp_C; - /**< the temperature of the outlet water - taken from top of tank, 0 if no flow */ + // a std::vector containing the HeatSources, in order of priority + std::vector heatSources; - double condenserInlet_C; - /**< the temperature of the inlet water to the condensor either an average of tank nodes or taken from the bottom, 0 if no flow or no compressor */ - double condenserOutlet_C; - /**< the temperature of the outlet water from the condensor either, 0 if no flow or no compressor */ - double externalVolumeHeated_L; - /**< the volume of water heated by an external source, 0 if no flow or no external heat source */ + int compressorIndex; + /**< The index of the compressor heat source (set to -1 if no compressor)*/ - double energyRemovedFromEnvironment_kWh; - /**< the total energy removed from the environment, to heat the water */ - double standbyLosses_kWh; - /**< the amount of heat lost to standby */ + int lowestElementIndex; + /**< The index of the lowest resistance element heat source (set to -1 if no resistance + * elements)*/ - // special variables for adding abilities - bool tankMixesOnDraw; - /**< whether or not the bottom fraction (defined by mixBelowFraction) - of the tank should mix during draws */ - double mixBelowFractionOnDraw; - /**< mixes the tank below this fraction on draws iff tankMixesOnDraw */ + int highestElementIndex; + /**< The index of the highest resistance element heat source. if only one element it equals + * lowestElementIndex (set to -1 if no resistance elements)*/ - 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; + int VIPIndex; + /**< The index of the VIP resistance element heat source (set to -1 if no VIP resistance + * elements)*/ - double minutesPerStep = 1.; - double secondsPerStep,hoursPerStep; + 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 */ - bool doInversionMixing; - /**< If and only if true will model temperature inversion mixing in the tank */ + 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 */ - bool doConduction; - /**< If and only if true will model conduction between the internal nodes of the tank */ + /**< the volume in liters of the tank */ + double tankVolume_L; + + /**< the UA of the tank, in metric units */ + double tankUA_kJperHrC; - struct resPoint { - int index; - int position; - }; - std::vector resistanceHeightMap; - /**< A map from index of an resistance element in heatSources to position in the tank, its - is sorted by height from lowest to highest*/ - - /// Generates a vector of logical nodes - std::vector getNodeWeightRange(double bottomFraction,double topFraction); - - /// False: water is drawn from the tank itself; True: tank provides heat exchange only - bool hasHeatExchanger; - - /// Coefficient (0-1) of effectiveness for heat exchange between tank and water line (used by heat-exchange models only). - double heatExchangerEffectiveness; - - /// Coefficient (0-1) of effectiveness for heat exchange between a single tank node and water line (derived from heatExchangerEffectiveness). - double nodeHeatExchangerEffectiveness; - -}; //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,int condensitySize = CONDENSITY_SIZE); - /**< configure the heat source to be a resisive element, positioned at the - specified node, with the specified power in watts */ - - bool isEngaged() const; - /**< return whether or not the heat source is engaged */ - void engageHeatSource(DRMODES DRstatus = DR_ALLOW); - /**< 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 toLockOrUnlock(double heatSourceAmbientT_C); - /**< combines shouldLockOut and shouldUnlock to one master function which locks or unlocks the heatsource. Return boolean lockedOut (true if locked, false if unlocked)*/ - - 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 */ - - bool maxedOut() const; - /**< queries the heat source as to if it shouldn't produce hotter water and the tank isn't at setpoint. */ - - int findParent() const; - /**< returns the index of the heat source where this heat source is a backup. - returns -1 if none found. */ - - double fractToMeetComparisonExternal() const; - /**< calculates the distance the current state is from the shutOff logic for external configurations*/ - - 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 */ - - /// Assign new condensity values from supplied vector. Note the input vector is currently resampled - /// to preserve a condensity vector of size CONDENSITY_SIZE. - void setCondensity(const std::vector &condensity_in); - - int getCondensitySize() const; - - void linearInterp(double &ynew,double xnew,double x0,double x1,double y0,double y1); - /**< Does a simple linear interpolation between two points to the xnew point */ - - void regressedMethod(double &ynew,std::vector &coefficents,double x1,double x2,double x3); - /**< Does a calculation based on the ten term regression equation */ - - void regressedMethodMP(double &ynew,std::vector &coefficents,double x1,double x2); - /**< Does a calculation based on the five term regression equation for MP split systems */ - - void btwxtInterp(double& input_BTUperHr,double& cop,std::vector& target); - /**< Does a linear interpolation in btwxt to the target point*/ - - void setupDefrostMap(double derate35 = 0.8865); - /**< configure the heat source with a default for the defrost derating */ - void defrostDerate(double &to_derate,double airT_C); - /**< Derates the COP of a system based on the air temperature */ - -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 */ - - bool doDefrost; - /**< If and only if true will derate the COP of a compressor to simulate a defrost cycle */ - - // 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 */ - - // condensity is represented as a std::vector of size CONDENSITY_SIZE. - // 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. */ - std::vector condensity; - - double Tshrinkage_C; - /**< Tshrinkage_C is a derived from the condentropy (conditional entropy), - using the condensity and fixed parameters Talpha_C and Tbeta_C. - Talpha_C and Tbeta_C are not intended to be settable - see the hpwh_init functions for calculation of shrinkage */ - - struct perfPoint { - double T_F; - std::vector inputPower_coeffs; // c0 + c1*T + c2*T*T - std::vector COP_coeffs; // c0 + c1*T + c2*T*T - }; - - std::vector perfMap; - /**< A map with input/COP quadratic curve coefficients at a given external temperature */ - - std::vector< std::vector > perfGrid; - /**< The axis values defining the regular grid for the performance data. - SP would have 3 axis, MP would have 2 axis*/ - - std::vector< std::vector > perfGridValues; - /**< The values for input power and cop use matching to the grid. Should be long format with { { inputPower_W }, { COP } }. */ - - class Btwxt::RegularGridInterpolator *perfRGI; - /**< The grid interpolator used for mapping performance*/ - - bool useBtwxtGrid; - - /** 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; - /** a single logic that checks the bottom point is below a temperature so the system doesn't short cycle*/ - std::shared_ptr standbyLogic; - - /** some compressors have a resistance element for defrost*/ - struct resistanceElementDefrost - { - double inputPwr_kW; - double constTempLift_dF; - double onBelowT_F; - }; - resistanceElementDefrost resDefrost; - - struct defrostPoint { - double T_F; - double derate_fraction; - }; - std::vector defrostMap; - /**< A list of points for the defrost derate factor ordered by increasing external temperature */ - - struct maxOut_minAir { - double outT_C; - double airT_C; - }; - maxOut_minAir maxOut_at_LowT; - /**< maximum output temperature at the minimum operating temperature of HPWH environment (minT)*/ - - struct SecondaryHeatExchanger { - double coldSideTemperatureOffest_dC; - double hotSideTemperatureOffset_dC; - double extraPumpPower_W; - }; - - SecondaryHeatExchanger secondaryHeatExchanger; /**< adjustments for a approximating a secondary heat exchanger by adding extra input energy for the pump and - an increaes in the water to the incoming waater temperature to the heatpump*/ - - void addTurnOnLogic(std::shared_ptr logic); - void addShutOffLogic(std::shared_ptr logic); - /**< these are two small functions to remove some of the cruft in initiation functions */ - void clearAllTurnOnLogic(); - void clearAllShutOffLogic(); - void clearAllLogic(); - /**< these are two small functions to remove some of the cruft in initiation functions */ - - - - void changeResistanceWatts(double watts); - /**< function to change the resistance wattage */ - - bool isACompressor() const; - /**< returns if the heat source uses a compressor or not */ - bool isAResistance() const; - /**< returns if the heat source uses a resistance element or not */ - bool isExternalMultipass() const; - - double minT; - /**< minimum operating temperature of HPWH environment */ - - double maxT; - /**< maximum operating temperature of HPWH environment */ - - double maxSetpoint_C; - /**< the maximum setpoint of the heat source can create, used for compressors predominately */ - - 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 */ + /**< the UA of the fittings for the tank, in metric units */ + double fittingsUA_kJperHrC; - 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 */ + /**< the volume (L) of a single node */ + double nodeVolume_L; - 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 */ + /**< the mass of water (kg) in a single node */ + double nodeMass_kg; - int externalInletHeight; /** tankTemps_C; - // some private functions, mostly used for heating the water with the addHeat function + /**< holds the future temperature of each node for the conduction calculation - 0 is the bottom + * node */ + std::vector nextTankTemps_C; - 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. */ + DRMODES prevDRstatus; + /**< the DRstatus of the tank in the previous time step and at the end of runOneStep */ - /** I wrote some methods to help with the add heat interface - MJL */ - void getCapacity(double externalT_C,double condenserTemp_C,double setpointTemp_C,double &input_BTUperHr,double &cap_BTUperHr,double &cop); + double timerLimitTOT; + /**< the time limit in minutes on the timer when the compressor and resistance elements are + * turned back on, used with DR_TOT. */ + double timerTOT; + /**< the timer used for DR_TOT to turn on the compressor and resistance elements. */ - /** An overloaded function that uses uses the setpoint temperature */ - void getCapacity(double externalT_C,double condenserTemp_C,double &input_BTUperHr,double &cap_BTUperHr,double &cop) { - getCapacity(externalT_C,condenserTemp_C,hpwh->getSetpoint(),input_BTUperHr,cap_BTUperHr,cop); - }; - /** An equivalent getCapcity function just for multipass external (or split) HPWHs */ - void getCapacityMP(double externalT_C,double condenserTemp_C,double &input_BTUperHr,double &cap_BTUperHr,double &cop); + bool usesSoCLogic; - double calcMPOutletTemperature(double heatingCapacity_KW); - /**< returns the temperature of outlet of a external multipass hpwh */ + // Some outputs + double outletTemp_C; + /**< the temperature of the outlet water - taken from top of tank, 0 if no flow */ - void calcHeatDist(std::vector &heatDistribution); + double condenserInlet_C; + /**< the temperature of the inlet water to the condensor either an average of tank nodes or + * taken from the bottom, 0 if no flow or no compressor */ + double condenserOutlet_C; + /**< the temperature of the outlet water from the condensor either, 0 if no flow or no + * compressor */ + double externalVolumeHeated_L; + /**< the volume of water heated by an external source, 0 if no flow or no external heat source + */ - double getTankTemp() const; - /**< returns the tank temperature weighted by the condensity for this heat source */ + double energyRemovedFromEnvironment_kWh; + /**< the total energy removed from the environment, to heat the water */ + double standbyLosses_kWh; + /**< the amount of heat lost to standby */ - void sortPerformanceMap(); - /**< sorts the Performance Map by increasing external temperatures */ + // special variables for adding abilities + bool tankMixesOnDraw; + /**< whether or not the bottom fraction (defined by mixBelowFraction) + of the tank should mix during draws */ + double mixBelowFractionOnDraw; + /**< mixes the tank below this fraction on draws iff tankMixesOnDraw */ -}; // end of HeatSource class + 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 minutesPerStep = 1.; + double secondsPerStep, hoursPerStep; + + 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 */ -constexpr double BTUperKWH = 3412.14163312794; // https://www.rapidtables.com/convert/energy/kWh_to_BTU.html -constexpr double FperC = 9. / 5.; // degF / degC -constexpr double offsetF = 32.; // degF offset + struct resPoint + { + int index; + int position; + }; + std::vector resistanceHeightMap; + /**< A map from index of an resistance element in heatSources to position in the tank, its + is sorted by height from lowest to highest*/ + + /// Generates a vector of logical nodes + std::vector getNodeWeightRange(double bottomFraction, double topFraction); + + /// False: water is drawn from the tank itself; True: tank provides heat exchange only + bool hasHeatExchanger; + + /// Coefficient (0-1) of effectiveness for heat exchange between tank and water line (used by + /// heat-exchange models only). + double heatExchangerEffectiveness; + + /// Coefficient (0-1) of effectiveness for heat exchange between a single tank node and water + /// line (derived from heatExchangerEffectiveness). + double nodeHeatExchangerEffectiveness; + +}; // 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, int condensitySize = CONDENSITY_SIZE); + /**< configure the heat source to be a resisive element, positioned at the + specified node, with the specified power in watts */ + + bool isEngaged() const; + /**< return whether or not the heat source is engaged */ + void engageHeatSource(DRMODES DRstatus = DR_ALLOW); + /**< 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 toLockOrUnlock(double heatSourceAmbientT_C); + /**< combines shouldLockOut and shouldUnlock to one master function which locks or unlocks the + * heatsource. Return boolean lockedOut (true if locked, false if unlocked)*/ + + 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 */ + + bool maxedOut() const; + /**< queries the heat source as to if it shouldn't produce hotter water and the tank isn't at + * setpoint. */ + + int findParent() const; + /**< returns the index of the heat source where this heat source is a backup. + returns -1 if none found. */ + + double fractToMeetComparisonExternal() const; + /**< calculates the distance the current state is from the shutOff logic for external + * configurations*/ + + 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 */ + + /// Assign new condensity values from supplied vector. Note the input vector is currently + /// resampled to preserve a condensity vector of size CONDENSITY_SIZE. + void setCondensity(const std::vector& condensity_in); + + int getCondensitySize() const; + + void linearInterp(double& ynew, double xnew, double x0, double x1, double y0, double y1); + /**< Does a simple linear interpolation between two points to the xnew point */ + + void regressedMethod( + double& ynew, std::vector& coefficents, double x1, double x2, double x3); + /**< Does a calculation based on the ten term regression equation */ + + void regressedMethodMP(double& ynew, std::vector& coefficents, double x1, double x2); + /**< Does a calculation based on the five term regression equation for MP split systems */ + + void btwxtInterp(double& input_BTUperHr, double& cop, std::vector& target); + /**< Does a linear interpolation in btwxt to the target point*/ + + void setupDefrostMap(double derate35 = 0.8865); + /**< configure the heat source with a default for the defrost derating */ + void defrostDerate(double& to_derate, double airT_C); + /**< Derates the COP of a system based on the air temperature */ + + 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 */ + + bool doDefrost; + /**< If and only if true will derate the COP of a compressor to simulate a defrost cycle */ + + // 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 */ + + // condensity is represented as a std::vector of size CONDENSITY_SIZE. + // 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. */ + std::vector condensity; + + double Tshrinkage_C; + /**< Tshrinkage_C is a derived from the condentropy (conditional entropy), + using the condensity and fixed parameters Talpha_C and Tbeta_C. + Talpha_C and Tbeta_C are not intended to be settable + see the hpwh_init functions for calculation of shrinkage */ + + struct perfPoint + { + double T_F; + std::vector inputPower_coeffs; // c0 + c1*T + c2*T*T + std::vector COP_coeffs; // c0 + c1*T + c2*T*T + }; + + std::vector perfMap; + /**< A map with input/COP quadratic curve coefficients at a given external temperature */ + + std::vector> perfGrid; + /**< The axis values defining the regular grid for the performance data. + SP would have 3 axis, MP would have 2 axis*/ + + std::vector> perfGridValues; + /**< The values for input power and cop use matching to the grid. Should be long format with { { + * inputPower_W }, { COP } }. */ + + class Btwxt::RegularGridInterpolator* perfRGI; + /**< The grid interpolator used for mapping performance*/ + + bool useBtwxtGrid; + + /** 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; + /** a single logic that checks the bottom point is below a temperature so the system doesn't + * short cycle*/ + std::shared_ptr standbyLogic; + + /** some compressors have a resistance element for defrost*/ + struct resistanceElementDefrost + { + double inputPwr_kW; + double constTempLift_dF; + double onBelowT_F; + }; + resistanceElementDefrost resDefrost; + + struct defrostPoint + { + double T_F; + double derate_fraction; + }; + std::vector defrostMap; + /**< A list of points for the defrost derate factor ordered by increasing external temperature + */ + + struct maxOut_minAir + { + double outT_C; + double airT_C; + }; + maxOut_minAir maxOut_at_LowT; + /**< maximum output temperature at the minimum operating temperature of HPWH environment + * (minT)*/ + + struct SecondaryHeatExchanger + { + double coldSideTemperatureOffest_dC; + double hotSideTemperatureOffset_dC; + double extraPumpPower_W; + }; + + SecondaryHeatExchanger secondaryHeatExchanger; /**< adjustments for a approximating a secondary + heat exchanger by adding extra input energy for the pump and an increaes in the water to the + incoming waater temperature to the heatpump*/ + + void addTurnOnLogic(std::shared_ptr logic); + void addShutOffLogic(std::shared_ptr logic); + /**< these are two small functions to remove some of the cruft in initiation functions */ + void clearAllTurnOnLogic(); + void clearAllShutOffLogic(); + void clearAllLogic(); + /**< these are two small functions to remove some of the cruft in initiation functions */ + + void changeResistanceWatts(double watts); + /**< function to change the resistance wattage */ + + bool isACompressor() const; + /**< returns if the heat source uses a compressor or not */ + bool isAResistance() const; + /**< returns if the heat source uses a resistance element or not */ + bool isExternalMultipass() const; + + double minT; + /**< minimum operating temperature of HPWH environment */ + + double maxT; + /**< maximum operating temperature of HPWH environment */ + + double maxSetpoint_C; + /**< the maximum setpoint of the heat source can create, used for compressors predominately */ + + 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 */ + + int externalInletHeight; /**getSetpoint(), input_BTUperHr, cap_BTUperHr, cop); + }; + /** An equivalent getCapcity function just for multipass external (or split) HPWHs */ + void getCapacityMP(double externalT_C, + double condenserTemp_C, + double& input_BTUperHr, + double& cap_BTUperHr, + double& cop); + + double calcMPOutletTemperature(double heatingCapacity_KW); + /**< returns the temperature of outlet of a external multipass hpwh */ + + void calcHeatDist(std::vector& heatDistribution); + + double getTankTemp() const; + /**< returns the tank temperature weighted by the condensity for this heat source */ + + void sortPerformanceMap(); + /**< sorts the Performance Map by increasing external temperatures */ + +}; // end of HeatSource class + +constexpr double BTUperKWH = + 3412.14163312794; // https://www.rapidtables.com/convert/energy/kWh_to_BTU.html +constexpr double FperC = 9. / 5.; // degF / degC +constexpr double offsetF = 32.; // degF offset constexpr double sec_per_min = 60.; // seconds / min -constexpr double min_per_hr = 60.; // min / hr +constexpr double min_per_hr = 60.; // min / hr constexpr double sec_per_hr = sec_per_min * min_per_hr; // seconds / hr // a few extra functions for unit conversion @@ -1351,37 +1525,42 @@ inline double FT2_TO_M2(double feet2) { return (feet2 / 10.7640); } inline double MIN_TO_SEC(double minute) { return minute * sec_per_min; } inline double MIN_TO_HR(double minute) { return minute / min_per_hr; } -inline HPWH::DRMODES operator|(HPWH::DRMODES a,HPWH::DRMODES b) +inline HPWH::DRMODES operator|(HPWH::DRMODES a, HPWH::DRMODES b) { - return static_cast(static_cast(a) | static_cast(b)); + return static_cast(static_cast(a) | static_cast(b)); } -template< typename T> inline bool aboutEqual(T a,T b) { return fabs(a - b) < HPWH::TOL_MINVALUE; } +template +inline bool aboutEqual(T a, T b) +{ + return fabs(a - b) < HPWH::TOL_MINVALUE; +} /// Generate an absolute or relative temperature in degC. -inline double convertTempToC(const double T_F_or_C,const HPWH::UNITS units,const bool absolute){ - return (units == HPWH::UNITS_C) ? T_F_or_C : (absolute ? F_TO_C(T_F_or_C) : dF_TO_dC(T_F_or_C)); +inline double convertTempToC(const double T_F_or_C, const HPWH::UNITS units, const bool absolute) +{ + return (units == HPWH::UNITS_C) ? T_F_or_C : (absolute ? F_TO_C(T_F_or_C) : dF_TO_dC(T_F_or_C)); } // resampling utility functions -double getResampledValue(const std::vector &values,double beginFraction,double endFraction); -bool resample(std::vector &values,const std::vector &sampleValues); -inline bool resampleIntensive(std::vector &values,const std::vector &sampleValues) +double +getResampledValue(const std::vector& values, double beginFraction, double endFraction); +bool resample(std::vector& values, const std::vector& sampleValues); +inline bool resampleIntensive(std::vector& values, const std::vector& sampleValues) { - return resample(values,sampleValues); + return resample(values, sampleValues); } -bool resampleExtensive(std::vector &values,const std::vector &sampleValues); +bool resampleExtensive(std::vector& values, const std::vector& sampleValues); /// helper functions -double expitFunc(double x,double offset); -void normalize(std::vector &distribution); -int findLowestNode(const std::vector &nodeDist,const int numTankNodes); -double findShrinkageT_C(const std::vector &nodeDist); -void calcThermalDist( - std::vector &thermalDist, - const double shrinkageT_C, - const int lowestNode, - const std::vector &nodeTemp_C, - const double setpointT_C); +double expitFunc(double x, double offset); +void normalize(std::vector& distribution); +int findLowestNode(const std::vector& nodeDist, const int numTankNodes); +double findShrinkageT_C(const std::vector& nodeDist); +void calcThermalDist(std::vector& thermalDist, + const double shrinkageT_C, + const int lowestNode, + const std::vector& nodeTemp_C, + const double setpointT_C); #endif diff --git a/src/HPWHHeatSources.cc b/src/HPWHHeatSources.cc index 37ebcaf4..e193e7f7 100644 --- a/src/HPWHHeatSources.cc +++ b/src/HPWHHeatSources.cc @@ -5,916 +5,1167 @@ #include #include - // vendor +// vendor #include #include "HPWH.hh" -//public HPWH::HeatSource functions -HPWH::HeatSource::HeatSource(HPWH *parentInput) - :hpwh(parentInput),isOn(false),lockedOut(false),doDefrost(false),backupHeatSource(NULL),companionHeatSource(NULL), - followedByHeatSource(NULL),minT(-273.15),maxT(100),hysteresis_dC(0),airflowFreedom(1.0),maxSetpoint_C(100.), - typeOfHeatSource(TYPE_none),extrapolationMethod(EXTRAP_LINEAR),maxOut_at_LowT{100,-273.15},standbyLogic(NULL), - isMultipass(true),mpFlowRate_LPS(0.),externalInletHeight(-1),externalOutletHeight(-1),useBtwxtGrid(false), - secondaryHeatExchanger{0.,0.,0.} -{} +// public HPWH::HeatSource functions +HPWH::HeatSource::HeatSource(HPWH* parentInput) + : hpwh(parentInput) + , isOn(false) + , lockedOut(false) + , doDefrost(false) + , backupHeatSource(NULL) + , companionHeatSource(NULL) + , followedByHeatSource(NULL) + , minT(-273.15) + , maxT(100) + , hysteresis_dC(0) + , airflowFreedom(1.0) + , maxSetpoint_C(100.) + , typeOfHeatSource(TYPE_none) + , extrapolationMethod(EXTRAP_LINEAR) + , maxOut_at_LowT {100, -273.15} + , standbyLogic(NULL) + , isMultipass(true) + , mpFlowRate_LPS(0.) + , externalInletHeight(-1) + , externalOutletHeight(-1) + , useBtwxtGrid(false) + , secondaryHeatExchanger {0., 0., 0.} +{ +} + +HPWH::HeatSource::HeatSource(const HeatSource& hSource) { *this = hSource; } + +HPWH::HeatSource& HPWH::HeatSource::operator=(const HeatSource& hSource) +{ + if (this == &hSource) + { + return *this; + } + + hpwh = hSource.hpwh; + isOn = hSource.isOn; + lockedOut = hSource.lockedOut; + doDefrost = hSource.doDefrost; + + 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; + } + + condensity = hSource.condensity; + + Tshrinkage_C = hSource.Tshrinkage_C; + + perfMap = hSource.perfMap; + + perfGrid = hSource.perfGrid; + perfGridValues = hSource.perfGridValues; + perfRGI = hSource.perfRGI; + useBtwxtGrid = hSource.useBtwxtGrid; + + defrostMap = hSource.defrostMap; + resDefrost = hSource.resDefrost; + + // i think vector assignment works correctly here + turnOnLogicSet = hSource.turnOnLogicSet; + shutOffLogicSet = hSource.shutOffLogicSet; + standbyLogic = hSource.standbyLogic; + + minT = hSource.minT; + maxT = hSource.maxT; + maxOut_at_LowT = hSource.maxOut_at_LowT; + hysteresis_dC = hSource.hysteresis_dC; + maxSetpoint_C = hSource.maxSetpoint_C; + + depressesTemperature = hSource.depressesTemperature; + airflowFreedom = hSource.airflowFreedom; + + configuration = hSource.configuration; + typeOfHeatSource = hSource.typeOfHeatSource; + isMultipass = hSource.isMultipass; + mpFlowRate_LPS = hSource.mpFlowRate_LPS; + + externalInletHeight = hSource.externalInletHeight; + externalOutletHeight = hSource.externalOutletHeight; + + lowestNode = hSource.lowestNode; + extrapolationMethod = hSource.extrapolationMethod; + secondaryHeatExchanger = hSource.secondaryHeatExchanger; + + return *this; +} + +void HPWH::HeatSource::setCondensity(const std::vector& condensity_in) +{ + condensity = condensity_in; +} + +int HPWH::HeatSource::getCondensitySize() const { return static_cast(condensity.size()); } + +int HPWH::HeatSource::findParent() const +{ + for (int i = 0; i < hpwh->getNumHeatSources(); ++i) + { + if (this == hpwh->heatSources[i].backupHeatSource) + { + return i; + } + } + return -1; +} -HPWH::HeatSource::HeatSource(const HeatSource &hSource) { - *this = hSource; +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 (maxedOut()) + { + lock = true; + if (hpwh->hpwhVerbosity >= HPWH::VRB_emetic) + { + hpwh->msg("\tlock-out: condenser water temperature above max: %.2f", maxSetpoint_C); + } + } + // 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; + } } -HPWH::HeatSource& HPWH::HeatSource::operator=(const HeatSource &hSource) { - if(this == &hSource) { - return *this; - } +bool HPWH::HeatSource::shouldUnlock(double heatSourceAmbientT_C) const +{ + + // if it's already unlocked, keep it unlocked + if (isLockedOut() == false) + { + return true; + } + // if it the heat source is capped and can't produce hotter water + else if (maxedOut()) + { + return false; + } + 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; + } +} - hpwh = hSource.hpwh; - isOn = hSource.isOn; - lockedOut = hSource.lockedOut; - doDefrost = hSource.doDefrost; +bool HPWH::HeatSource::toLockOrUnlock(double heatSourceAmbientT_C) +{ - runtime_min = hSource.runtime_min; - energyInput_kWh = hSource.energyInput_kWh; - energyOutput_kWh = hSource.energyOutput_kWh; + if (shouldLockOut(heatSourceAmbientT_C)) + { + lockOutHeatSource(); + } + if (shouldUnlock(heatSourceAmbientT_C)) + { + unlockHeatSource(); + } - isVIP = hSource.isVIP; + return isLockedOut(); +} - 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; - } +void HPWH::HeatSource::engageHeatSource(DRMODES DR_signal) +{ + isOn = true; + hpwh->isHeating = true; + if (companionHeatSource != NULL && companionHeatSource->shutsOff() != true && + companionHeatSource->isEngaged() == false && + hpwh->shouldDRLockOut(companionHeatSource->typeOfHeatSource, DR_signal) == false) + { + companionHeatSource->engageHeatSource(DR_signal); + } +} - condensity = hSource.condensity; +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 = turnOnLogicSet[i]->getTankValue(); + double comparison = turnOnLogicSet[i]->getComparisonValue(); + + if (turnOnLogicSet[i]->compare(average, comparison)) + { + if (turnOnLogicSet[i]->description == "standby" && standbyLogic != NULL) + { + double comparisonStandby = standbyLogic->getComparisonValue(); + double avgStandby = standbyLogic->getTankValue(); + + if (turnOnLogicSet[i]->compare(avgStandby, comparisonStandby)) + { + shouldEngage = true; + } + } + else + { + shouldEngage = true; + } + } + + // quit searching the logics if one of them turns it on + if (shouldEngage) + { + // 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]->getDecisionPoint(), + comparison); + } + 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; +} - Tshrinkage_C = hSource.Tshrinkage_C; +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 = shutOffLogicSet[i]->getTankValue(); + double comparison = shutOffLogicSet[i]->getComparisonValue(); + + 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; +} - perfMap = hSource.perfMap; +bool HPWH::HeatSource::maxedOut() const +{ + bool maxed = false; + + // If the heat source can't produce water at the setpoint and the control logics are saying to + // shut off + if (hpwh->setpoint_C > maxSetpoint_C) + { + if (hpwh->tankTemps_C[0] >= maxSetpoint_C || shutsOff()) + { + maxed = true; + } + } + return maxed; +} - perfGrid = hSource.perfGrid; - perfGridValues = hSource.perfGridValues; - perfRGI = hSource.perfRGI; - useBtwxtGrid = hSource.useBtwxtGrid; +double HPWH::HeatSource::fractToMeetComparisonExternal() const +{ + double fracTemp; + double frac = 1.; - defrostMap = hSource.defrostMap; - resDefrost = hSource.resDefrost; + for (int i = 0; i < (int)shutOffLogicSet.size(); i++) + { + if (hpwh->hpwhVerbosity >= VRB_emetic) + { + hpwh->msg("\tshutsOff logic: %s ", shutOffLogicSet[i]->description.c_str()); + } - //i think vector assignment works correctly here - turnOnLogicSet = hSource.turnOnLogicSet; - shutOffLogicSet = hSource.shutOffLogicSet; - standbyLogic = hSource.standbyLogic; - - minT = hSource.minT; - maxT = hSource.maxT; - maxOut_at_LowT = hSource.maxOut_at_LowT; - hysteresis_dC = hSource.hysteresis_dC; - maxSetpoint_C = hSource.maxSetpoint_C; - - depressesTemperature = hSource.depressesTemperature; - airflowFreedom = hSource.airflowFreedom; - - configuration = hSource.configuration; - typeOfHeatSource = hSource.typeOfHeatSource; - isMultipass = hSource.isMultipass; - mpFlowRate_LPS = hSource.mpFlowRate_LPS; - - externalInletHeight = hSource.externalInletHeight; - externalOutletHeight = hSource.externalOutletHeight; - - lowestNode = hSource.lowestNode; - extrapolationMethod = hSource.extrapolationMethod; - secondaryHeatExchanger = hSource.secondaryHeatExchanger; - - return *this; -} - -void HPWH::HeatSource::setCondensity(const std::vector &condensity_in) { - condensity = condensity_in; -} - -int HPWH::HeatSource::getCondensitySize() const { - return static_cast(condensity.size()); -} - -int HPWH::HeatSource::findParent() const { - for(int i = 0; i < hpwh->getNumHeatSources(); ++i) { - if(this == hpwh->heatSources[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(maxedOut()) { - lock = true; - if(hpwh->hpwhVerbosity >= HPWH::VRB_emetic) { - hpwh->msg("\tlock-out: condenser water temperature above max: %.2f",maxSetpoint_C); - } - } - // 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; - } - // if it the heat source is capped and can't produce hotter water - else if(maxedOut()) { - return false; - } 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; - } -} - -bool HPWH::HeatSource::toLockOrUnlock(double heatSourceAmbientT_C) { - - if(shouldLockOut(heatSourceAmbientT_C)) { - lockOutHeatSource(); - } - if(shouldUnlock(heatSourceAmbientT_C)) { - unlockHeatSource(); - } - - return isLockedOut(); -} - -void HPWH::HeatSource::engageHeatSource(DRMODES DR_signal) { - isOn = true; - hpwh->isHeating = true; - if(companionHeatSource != NULL && - companionHeatSource->shutsOff() != true && - companionHeatSource->isEngaged() == false && - hpwh->shouldDRLockOut(companionHeatSource->typeOfHeatSource,DR_signal) == false) - { - companionHeatSource->engageHeatSource(DR_signal); - } -} - -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 = turnOnLogicSet[i]->getTankValue(); - double comparison = turnOnLogicSet[i]->getComparisonValue(); - - if(turnOnLogicSet[i]->compare(average,comparison)) { - if(turnOnLogicSet[i]->description == "standby" && standbyLogic != NULL) { - double comparisonStandby = standbyLogic->getComparisonValue(); - double avgStandby = standbyLogic->getTankValue(); - - if(turnOnLogicSet[i]->compare(avgStandby,comparisonStandby)) { - shouldEngage = true; - } - } else{ - shouldEngage = true; - } - } - - //quit searching the logics if one of them turns it on - if(shouldEngage) { - //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]->getDecisionPoint(),comparison); - } - 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 = shutOffLogicSet[i]->getTankValue(); - double comparison = shutOffLogicSet[i]->getComparisonValue(); - - 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; -} - -bool HPWH::HeatSource::maxedOut() const { - bool maxed = false; - - // If the heat source can't produce water at the setpoint and the control logics are saying to shut off - if(hpwh->setpoint_C > maxSetpoint_C){ - if(hpwh->tankTemps_C[0] >= maxSetpoint_C || shutsOff()) { - maxed = true; - } - } - return maxed; -} - -double HPWH::HeatSource::fractToMeetComparisonExternal() const { - double fracTemp; - double frac = 1.; - - for(int i = 0; i < (int)shutOffLogicSet.size(); i++) { - if(hpwh->hpwhVerbosity >= VRB_emetic) { - hpwh->msg("\tshutsOff logic: %s ",shutOffLogicSet[i]->description.c_str()); - } - - fracTemp = shutOffLogicSet[i]->getFractToMeetComparisonExternal(); - - frac = fracTemp < frac ? fracTemp : frac; - } - - return frac; -} - -void HPWH::HeatSource::addHeat(double externalT_C,double minutesToRun) { - double input_BTUperHr = 0.,cap_BTUperHr = 0.,cop = 0.; - - switch(configuration) { - case CONFIG_SUBMERGED: - case CONFIG_WRAPPED: - { - std::vector heatDistribution; - - // calcHeatDist takes care of the swooping for wrapped configurations - calcHeatDist(heatDistribution); - - // calculate capacity btu/hr, input btu/hr, and cop - if(isACompressor()) { - hpwh->condenserInlet_C = getTankTemp(); - getCapacity(externalT_C,getTankTemp(),input_BTUperHr,cap_BTUperHr,cop); - } - else { - getCapacity(externalT_C,getTankTemp(),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) / min_per_hr,cap_BTUperHr); - } - - //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 - - // set the leftover capacity to 0 - double leftoverCap_kJ = 0.; - for(int i = hpwh->getNumNodes() - 1; i >= 0; i--) { - //for(int i = 0; i < hpwh->numNodes; i++){ - double nodeCap_kJ = BTU_TO_KJ(cap_BTUperHr * minutesToRun / min_per_hr * heatDistribution[i]); - if(nodeCap_kJ != 0.) { - //add leftoverCap to the next run, and keep passing it on - leftoverCap_kJ = hpwh->addHeatAboveNode(nodeCap_kJ + leftoverCap_kJ,i,maxSetpoint_C); - } - } - - if(isACompressor()) { // outlet temperature is the condenser temperature after heat has been added - hpwh->condenserOutlet_C = getTankTemp(); - } - - //after you've done everything, any leftover capacity is time that didn't run - double cap_kJ = BTU_TO_KJ(cap_BTUperHr * minutesToRun / min_per_hr); - runtime_min = (1. - (leftoverCap_kJ / cap_kJ)) * minutesToRun; -#if 1 // error check, 1-22-2017; updated 12-6-2023 - if(runtime_min < -TOL_MINVALUE) - if(hpwh->hpwhVerbosity >= VRB_reluctant) - hpwh->msg("Internal error: Negative runtime = %0.3f min\n",runtime_min); -#endif - } - break; + fracTemp = shutOffLogicSet[i]->getFractToMeetComparisonExternal(); - case CONFIG_EXTERNAL: - //Else the heat source is external. SANCO2 system is only current example - //capacity is calculated internal to this functio - // n, and cap/input_BTUperHr, cop are outputs - runtime_min = addHeatExternal(externalT_C,minutesToRun,cap_BTUperHr,input_BTUperHr,cop); - break; - } + frac = fracTemp < frac ? fracTemp : frac; + } - // Write the input & output energy - energyInput_kWh += BTU_TO_KWH(input_BTUperHr * runtime_min / min_per_hr); - energyOutput_kWh += BTU_TO_KWH(cap_BTUperHr * runtime_min / min_per_hr); + return frac; +} + +void HPWH::HeatSource::addHeat(double externalT_C, double minutesToRun) +{ + double input_BTUperHr = 0., cap_BTUperHr = 0., cop = 0.; + + switch (configuration) + { + case CONFIG_SUBMERGED: + case CONFIG_WRAPPED: + { + std::vector heatDistribution; + + // calcHeatDist takes care of the swooping for wrapped configurations + calcHeatDist(heatDistribution); + + // calculate capacity btu/hr, input btu/hr, and cop + if (isACompressor()) + { + hpwh->condenserInlet_C = getTankTemp(); + getCapacity(externalT_C, getTankTemp(), input_BTUperHr, cap_BTUperHr, cop); + } + else + { + getCapacity(externalT_C, getTankTemp(), 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) / min_per_hr, + cap_BTUperHr); + } + + // 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 + + // set the leftover capacity to 0 + double leftoverCap_kJ = 0.; + for (int i = hpwh->getNumNodes() - 1; i >= 0; i--) + { + // for(int i = 0; i < hpwh->numNodes; i++){ + double nodeCap_kJ = + BTU_TO_KJ(cap_BTUperHr * minutesToRun / min_per_hr * heatDistribution[i]); + if (nodeCap_kJ != 0.) + { + // add leftoverCap to the next run, and keep passing it on + leftoverCap_kJ = + hpwh->addHeatAboveNode(nodeCap_kJ + leftoverCap_kJ, i, maxSetpoint_C); + } + } + + if (isACompressor()) + { // outlet temperature is the condenser temperature after heat has been added + hpwh->condenserOutlet_C = getTankTemp(); + } + + // after you've done everything, any leftover capacity is time that didn't run + double cap_kJ = BTU_TO_KJ(cap_BTUperHr * minutesToRun / min_per_hr); + runtime_min = (1. - (leftoverCap_kJ / cap_kJ)) * minutesToRun; +#if 1 // error check, 1-22-2017; updated 12-6-2023 + if (runtime_min < -TOL_MINVALUE) + 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. SANCO2 system is only current example + // capacity is calculated internal to this functio + // n, and cap/input_BTUperHr, cop are outputs + 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 / min_per_hr); + energyOutput_kWh += BTU_TO_KWH(cap_BTUperHr * runtime_min / min_per_hr); } // private HPWH::HeatSource 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::getTankTemp() const{ - - std::vector resampledTankTemps(getCondensitySize()); - resample(resampledTankTemps, hpwh->tankTemps_C); - - double tankTemp_C = 0.; - - std::size_t j = 0; - for(auto &resampledNodeTemp: resampledTankTemps) { - tankTemp_C += condensity[j] * resampledNodeTemp; - // Note that condensity is normalized. - ++j; - } - if(hpwh->hpwhVerbosity >= VRB_typical) { - hpwh->msg("tank temp %.2lf \n",tankTemp_C); - } - return tankTemp_C; -} - -void HPWH::HeatSource::getCapacity(double externalT_C,double condenserTemp_C,double setpointTemp_C,double &input_BTUperHr,double &cap_BTUperHr,double &cop) { - double externalT_F,condenserTemp_F; - - // Add an offset to the condenser temperature (or incoming coldwater temperature) to approximate a secondary heat exchange in line with the compressor - condenserTemp_F = C_TO_F(condenserTemp_C + secondaryHeatExchanger.coldSideTemperatureOffest_dC); - 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; - double Tout_F = C_TO_F(setpointTemp_C + secondaryHeatExchanger.hotSideTemperatureOffset_dC); - - if(useBtwxtGrid) { - std::vector target{externalT_F,Tout_F,condenserTemp_F}; - btwxtInterp(input_BTUperHr,cop,target); - } else { - if(perfMap.empty()) { // Avoid using empty perfMap - input_BTUperHr = 0.; - cop = 0.; - } else if(perfMap.size() > 1) { - 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 - - 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 - linearInterp(cop,externalT_F,perfMap[i_prev].T_F,perfMap[i_next].T_F,COP_T1,COP_T2); - linearInterp(input_BTUperHr,externalT_F,perfMap[i_prev].T_F,perfMap[i_next].T_F,inputPower_T1_Watts,inputPower_T2_Watts); - input_BTUperHr = KWH_TO_BTU(input_BTUperHr / 1000.0);//1000 converts w to kw); - - } else { //perfMap.size() == 1 or we've got an issue. - if(externalT_F > perfMap[0].T_F) { - extrapolate = true; - if(extrapolationMethod == EXTRAP_NEAREST) { - externalT_F = perfMap[0].T_F; - } - } - - regressedMethod(input_BTUperHr,perfMap[0].inputPower_coeffs,externalT_F,Tout_F,condenserTemp_F); - input_BTUperHr = KWH_TO_BTU(input_BTUperHr); - - regressedMethod(cop,perfMap[0].COP_coeffs,externalT_F,Tout_F,condenserTemp_F); - } - } - - if(doDefrost) { - //adjust COP by the defrost factor - defrostDerate(cop,externalT_F); - } - - cap_BTUperHr = cop * input_BTUperHr; - - if(hpwh->hpwhVerbosity >= VRB_emetic) { - hpwh->msg("externalT_F: %.2lf, Tout_F: %.2lf, condenserTemp_F: %.2lf\n",externalT_F,Tout_F,condenserTemp_F); - hpwh->msg("input_BTUperHr: %.2lf , cop: %.2lf, cap_BTUperHr: %.2lf \n",input_BTUperHr,cop,cap_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); - if(cop < 0.) { - hpwh->msg(" Warning: COP is Negative! \n"); - } - if(cop < 1.) { - hpwh->msg(" Warning: COP is Less than 1! \n"); - } - } -} - -void HPWH::HeatSource::getCapacityMP(double externalT_C,double condenserTemp_C,double &input_BTUperHr,double &cap_BTUperHr,double &cop) { - double externalT_F,condenserTemp_F; - bool resDefrostHeatingOn = false; - // Convert Celsius to Fahrenheit for the curve fits - condenserTemp_F = C_TO_F(condenserTemp_C + secondaryHeatExchanger.coldSideTemperatureOffest_dC); - externalT_F = C_TO_F(externalT_C); - - // Check if we have resistance elements to turn on for defrost and add the constant lift. - if(resDefrost.inputPwr_kW > 0) { - if(externalT_F < resDefrost.onBelowT_F) { - externalT_F += resDefrost.constTempLift_dF; - resDefrostHeatingOn = true; - } - } - - if(useBtwxtGrid) { - std::vector target{externalT_F,condenserTemp_F}; - btwxtInterp(input_BTUperHr,cop,target); - } else { - // Get bounding performance map points for interpolation/extrapolation - bool extrapolate = false; - if(externalT_F > perfMap[0].T_F) { - extrapolate = true; - if(extrapolationMethod == EXTRAP_NEAREST) { - externalT_F = perfMap[0].T_F; - } - } - - //Const Tair Tin Tair2 Tin2 TairTin - regressedMethodMP(input_BTUperHr,perfMap[0].inputPower_coeffs,externalT_F,condenserTemp_F); - regressedMethodMP(cop,perfMap[0].COP_coeffs,externalT_F,condenserTemp_F); - - } - input_BTUperHr = KWH_TO_BTU(input_BTUperHr); - - if(doDefrost) { - //adjust COP by the defrost factor - defrostDerate(cop,externalT_F); - } - - cap_BTUperHr = cop * input_BTUperHr; - - //For accounting add the resistance defrost to the input energy - if(resDefrostHeatingOn){ - input_BTUperHr += KW_TO_BTUperH(resDefrost.inputPwr_kW); - } - if(hpwh->hpwhVerbosity >= VRB_emetic) { - hpwh->msg("externalT_F: %.2lf, condenserTemp_F: %.2lf\n",externalT_F,condenserTemp_F); - hpwh->msg("input_BTUperHr: %.2lf , cop: %.2lf, cap_BTUperHr: %.2lf \n",input_BTUperHr,cop,cap_BTUperHr); - } -} - -double HPWH::HeatSource::calcMPOutletTemperature(double heatingCapacity_KW) { - return hpwh->tankTemps_C[externalOutletHeight] + heatingCapacity_KW / (mpFlowRate_LPS * DENSITYWATER_kgperL * CPWATER_kJperkgC); -} - -void HPWH::HeatSource::setupDefrostMap(double derate35/*=0.8865*/) { - doDefrost = true; - defrostMap.reserve(3); - defrostMap.push_back({17.,1.}); - defrostMap.push_back({35.,derate35}); - defrostMap.push_back({47.,1.}); -} - -void HPWH::HeatSource::defrostDerate(double &to_derate,double airT_F) { - if(airT_F <= defrostMap[0].T_F || airT_F >= defrostMap[defrostMap.size() - 1].T_F) { - return; // Air temperature outside bounds of the defrost map. There is no extrapolation here. - } - double derate_factor = 1.; - size_t i_prev = 0; - for(size_t i = 1; i < defrostMap.size(); ++i) { - if(airT_F <= defrostMap[i].T_F) { - i_prev = i - 1; - break; - } - } - linearInterp(derate_factor,airT_F, - defrostMap[i_prev].T_F,defrostMap[i_prev + 1].T_F, - defrostMap[i_prev].derate_fraction,defrostMap[i_prev + 1].derate_fraction); - to_derate *= derate_factor; -} - -void HPWH::HeatSource::linearInterp(double &ynew,double xnew,double x0,double x1,double y0,double y1) { - ynew = y0 + (xnew - x0) * (y1 - y0) / (x1 - x0); -} - -void HPWH::HeatSource::regressedMethod(double &ynew,std::vector &coefficents,double x1,double x2,double x3) { - ynew = coefficents[0] + - coefficents[1] * x1 + - coefficents[2] * x2 + - coefficents[3] * x3 + - coefficents[4] * x1 * x1 + - coefficents[5] * x2 * x2 + - coefficents[6] * x3 * x3 + - coefficents[7] * x1 * x2 + - coefficents[8] * x1 * x3 + - coefficents[9] * x2 * x3 + - coefficents[10] * x1 * x2 * x3; -} - -void HPWH::HeatSource::regressedMethodMP(double &ynew,std::vector &coefficents,double x1,double x2) { - //Const Tair Tin Tair2 Tin2 TairTin - ynew = coefficents[0] + - coefficents[1] * x1 + - coefficents[2] * x2 + - coefficents[3] * x1 * x1 + - coefficents[4] * x2 * x2 + - coefficents[5] * x1 * x2; -} - -void HPWH::HeatSource::btwxtInterp(double& input_BTUperHr,double& cop,std::vector &target) { - - std::vector result = perfRGI->get_values_at_target(target); - - input_BTUperHr = result[0]; - cop = result[1]; -} - -void HPWH::HeatSource::calcHeatDist(std::vector &heatDistribution) { - - // Populate the vector of heat distribution - if(configuration == CONFIG_SUBMERGED) { - heatDistribution.resize(hpwh->getNumNodes()); - resampleExtensive(heatDistribution, condensity); - } - else if(configuration == CONFIG_WRAPPED) { // Wrapped around the tank, send through the logistic function - calcThermalDist(heatDistribution,Tshrinkage_C,lowestNode,hpwh->tankTemps_C,hpwh->setpoint_C); - } -} - -bool HPWH::HeatSource::isACompressor() const { - return this->typeOfHeatSource == TYPE_compressor; -} - -bool HPWH::HeatSource::isAResistance() const { - return this->typeOfHeatSource == TYPE_resistance; -} -bool HPWH::HeatSource::isExternalMultipass() const { - return isMultipass && configuration == HeatSource::CONFIG_EXTERNAL; -} - -double HPWH::HeatSource::addHeatExternal(double externalT_C,double minutesToRun,double &cap_BTUperHr,double &input_BTUperHr,double &cop) { - double heatingCapacity_kJ,heatingCapacityNeeded_kJ,deltaT_C,timeUsed_min,nodeHeat_kJperNode,nodeFrac,fractToShutOff; - double inputTemp_BTUperHr = 0,capTemp_BTUperHr = 0,copTemp = 0; - double volumePerNode_LperNode = hpwh->tankVolume_L / hpwh->getNumNodes(); - double timeRemaining_min = minutesToRun; - double maxTargetTemp_C = std::min(maxSetpoint_C,hpwh->setpoint_C); - double targetTemp_C = 0.; - 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]); - } - - if(this->isMultipass) { - // if multipass evenly mix the tank up - hpwh->mixTankNodes(0,hpwh->getNumNodes(),1.0); // 1.0 will give even mixing, so all temperatures mixed end at average temperature. - - //how much heat is added this timestep - getCapacityMP(externalT_C,hpwh->tankTemps_C[externalOutletHeight],inputTemp_BTUperHr,capTemp_BTUperHr,copTemp); - double heatingCapacity_KW = BTUperH_TO_KW(capTemp_BTUperHr); - - heatingCapacity_kJ = heatingCapacity_KW * (timeRemaining_min * sec_per_min); - - targetTemp_C = calcMPOutletTemperature(heatingCapacity_KW); - deltaT_C = targetTemp_C - hpwh->tankTemps_C[externalOutletHeight]; - } else { - //how much heat is available this timestep - getCapacity(externalT_C,hpwh->tankTemps_C[externalOutletHeight],inputTemp_BTUperHr,capTemp_BTUperHr,copTemp); - heatingCapacity_kJ = BTU_TO_KJ(capTemp_BTUperHr * (minutesToRun / min_per_hr)); - 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 *= (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 - targetTemp_C = maxTargetTemp_C; - deltaT_C = targetTemp_C - hpwh->tankTemps_C[externalOutletHeight]; - - } - - nodeHeat_kJperNode = volumePerNode_LperNode * DENSITYWATER_kgperL * CPWATER_kJperkgC * deltaT_C; - - // Caclulate fraction of node to move - if(nodeHeat_kJperNode <= 0.) { // protect against dividing by zero - if bottom node is at (or above) setpoint, add no heat - 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); - } - - fractToShutOff = fractToMeetComparisonExternal(); - if(fractToShutOff < 1. && fractToShutOff < nodeFrac && !this->isMultipass) { // circle back and check on this for multipass - nodeFrac = fractToShutOff; - heatingCapacityNeeded_kJ = nodeFrac * nodeHeat_kJperNode; - - timeUsed_min = (heatingCapacityNeeded_kJ / heatingCapacity_kJ) * timeRemaining_min; - timeRemaining_min -= timeUsed_min; - } - //if more than one, round down to 1 and subtract the amount of time it would - //take to heat that node from the timeRemaining - else 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.; - } - - // Track the condenser temperature if this is a compressor before moving the nodes - if(isACompressor()) { - hpwh->condenserInlet_C += hpwh->tankTemps_C[externalOutletHeight] * timeUsed_min; - hpwh->condenserOutlet_C += targetTemp_C * timeUsed_min; - } - - // Moving the nodes down - // move all nodes down, mixing if less than a full node - for(int n = externalOutletHeight; n < externalInletHeight; 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[externalInletHeight] = hpwh->tankTemps_C[externalInletHeight] * (1. - nodeFrac) + targetTemp_C * nodeFrac; - - hpwh->mixTankInversions(); - hpwh->updateSoCIfNecessary(); - - // track outputs - weight by the time ran - // Add in pump power to approximate a secondary heat exchange in line with the compressor - input_BTUperHr += (inputTemp_BTUperHr + W_TO_BTUperH(secondaryHeatExchanger.extraPumpPower_W)) * timeUsed_min; - cap_BTUperHr += capTemp_BTUperHr * timeUsed_min; - cop += copTemp * timeUsed_min; - - hpwh->externalVolumeHeated_L += nodeFrac * volumePerNode_LperNode; - - //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 - // not timeRemaining_min == minutesToRun is possible - // must prevent divide by 0 (added 4-11-2023) - double timeRun = minutesToRun - timeRemaining_min; - if(timeRun > 0.) - { - input_BTUperHr /= timeRun; - cap_BTUperHr /= timeRun; - cop /= timeRun; - hpwh->condenserInlet_C /= timeRun; - hpwh->condenserOutlet_C /= timeRun; - } - - if(hpwh->hpwhVerbosity >= VRB_emetic) { - hpwh->msg("final remaining time: %.2lf \n",timeRemaining_min); - } - // return the time left - return timeRun; -} - -void HPWH::HeatSource::setupAsResistiveElement(int node,double Watts,int condensitySize/* = CONDENSITY_SIZE*/) { - - isOn = false; - isVIP = false; - condensity = std::vector(condensitySize, 0.); - condensity[node] = 1; - - perfMap.reserve(2); +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; }); +} - 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; +double HPWH::HeatSource::getTankTemp() const +{ + + std::vector resampledTankTemps(getCondensitySize()); + resample(resampledTankTemps, hpwh->tankTemps_C); + + double tankTemp_C = 0.; + + std::size_t j = 0; + for (auto& resampledNodeTemp : resampledTankTemps) + { + tankTemp_C += condensity[j] * resampledNodeTemp; + // Note that condensity is normalized. + ++j; + } + if (hpwh->hpwhVerbosity >= VRB_typical) + { + hpwh->msg("tank temp %.2lf \n", tankTemp_C); + } + return tankTemp_C; } - -void HPWH::HeatSource::addTurnOnLogic(std::shared_ptr logic) { - this->turnOnLogicSet.push_back(logic); + +void HPWH::HeatSource::getCapacity(double externalT_C, + double condenserTemp_C, + double setpointTemp_C, + double& input_BTUperHr, + double& cap_BTUperHr, + double& cop) +{ + double externalT_F, condenserTemp_F; + + // Add an offset to the condenser temperature (or incoming coldwater temperature) to approximate + // a secondary heat exchange in line with the compressor + condenserTemp_F = C_TO_F(condenserTemp_C + secondaryHeatExchanger.coldSideTemperatureOffest_dC); + 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; + double Tout_F = C_TO_F(setpointTemp_C + secondaryHeatExchanger.hotSideTemperatureOffset_dC); + + if (useBtwxtGrid) + { + std::vector target {externalT_F, Tout_F, condenserTemp_F}; + btwxtInterp(input_BTUperHr, cop, target); + } + else + { + if (perfMap.empty()) + { // Avoid using empty perfMap + input_BTUperHr = 0.; + cop = 0.; + } + else if (perfMap.size() > 1) + { + 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 + + 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 + linearInterp( + cop, externalT_F, perfMap[i_prev].T_F, perfMap[i_next].T_F, COP_T1, COP_T2); + linearInterp(input_BTUperHr, + externalT_F, + perfMap[i_prev].T_F, + perfMap[i_next].T_F, + inputPower_T1_Watts, + inputPower_T2_Watts); + input_BTUperHr = KWH_TO_BTU(input_BTUperHr / 1000.0); // 1000 converts w to kw); + } + else + { // perfMap.size() == 1 or we've got an issue. + if (externalT_F > perfMap[0].T_F) + { + extrapolate = true; + if (extrapolationMethod == EXTRAP_NEAREST) + { + externalT_F = perfMap[0].T_F; + } + } + + regressedMethod( + input_BTUperHr, perfMap[0].inputPower_coeffs, externalT_F, Tout_F, condenserTemp_F); + input_BTUperHr = KWH_TO_BTU(input_BTUperHr); + + regressedMethod(cop, perfMap[0].COP_coeffs, externalT_F, Tout_F, condenserTemp_F); + } + } + + if (doDefrost) + { + // adjust COP by the defrost factor + defrostDerate(cop, externalT_F); + } + + cap_BTUperHr = cop * input_BTUperHr; + + if (hpwh->hpwhVerbosity >= VRB_emetic) + { + hpwh->msg("externalT_F: %.2lf, Tout_F: %.2lf, condenserTemp_F: %.2lf\n", + externalT_F, + Tout_F, + condenserTemp_F); + hpwh->msg("input_BTUperHr: %.2lf , cop: %.2lf, cap_BTUperHr: %.2lf \n", + input_BTUperHr, + cop, + cap_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); + if (cop < 0.) + { + hpwh->msg(" Warning: COP is Negative! \n"); + } + if (cop < 1.) + { + hpwh->msg(" Warning: COP is Less than 1! \n"); + } + } } - -void HPWH::HeatSource::addShutOffLogic(std::shared_ptr logic) { - this->shutOffLogicSet.push_back(logic); + +void HPWH::HeatSource::getCapacityMP(double externalT_C, + double condenserTemp_C, + double& input_BTUperHr, + double& cap_BTUperHr, + double& cop) +{ + double externalT_F, condenserTemp_F; + bool resDefrostHeatingOn = false; + // Convert Celsius to Fahrenheit for the curve fits + condenserTemp_F = C_TO_F(condenserTemp_C + secondaryHeatExchanger.coldSideTemperatureOffest_dC); + externalT_F = C_TO_F(externalT_C); + + // Check if we have resistance elements to turn on for defrost and add the constant lift. + if (resDefrost.inputPwr_kW > 0) + { + if (externalT_F < resDefrost.onBelowT_F) + { + externalT_F += resDefrost.constTempLift_dF; + resDefrostHeatingOn = true; + } + } + + if (useBtwxtGrid) + { + std::vector target {externalT_F, condenserTemp_F}; + btwxtInterp(input_BTUperHr, cop, target); + } + else + { + // Get bounding performance map points for interpolation/extrapolation + bool extrapolate = false; + if (externalT_F > perfMap[0].T_F) + { + extrapolate = true; + if (extrapolationMethod == EXTRAP_NEAREST) + { + externalT_F = perfMap[0].T_F; + } + } + + // Const Tair Tin Tair2 Tin2 TairTin + regressedMethodMP( + input_BTUperHr, perfMap[0].inputPower_coeffs, externalT_F, condenserTemp_F); + regressedMethodMP(cop, perfMap[0].COP_coeffs, externalT_F, condenserTemp_F); + } + input_BTUperHr = KWH_TO_BTU(input_BTUperHr); + + if (doDefrost) + { + // adjust COP by the defrost factor + defrostDerate(cop, externalT_F); + } + + cap_BTUperHr = cop * input_BTUperHr; + + // For accounting add the resistance defrost to the input energy + if (resDefrostHeatingOn) + { + input_BTUperHr += KW_TO_BTUperH(resDefrost.inputPwr_kW); + } + if (hpwh->hpwhVerbosity >= VRB_emetic) + { + hpwh->msg("externalT_F: %.2lf, condenserTemp_F: %.2lf\n", externalT_F, condenserTemp_F); + hpwh->msg("input_BTUperHr: %.2lf , cop: %.2lf, cap_BTUperHr: %.2lf \n", + input_BTUperHr, + cop, + cap_BTUperHr); + } } -void HPWH::HeatSource::clearAllTurnOnLogic() { - this->turnOnLogicSet.clear(); +double HPWH::HeatSource::calcMPOutletTemperature(double heatingCapacity_KW) +{ + return hpwh->tankTemps_C[externalOutletHeight] + + heatingCapacity_KW / (mpFlowRate_LPS * DENSITYWATER_kgperL * CPWATER_kJperkgC); } -void HPWH::HeatSource::clearAllShutOffLogic() { - this->shutOffLogicSet.clear(); +void HPWH::HeatSource::setupDefrostMap(double derate35 /*=0.8865*/) +{ + doDefrost = true; + defrostMap.reserve(3); + defrostMap.push_back({17., 1.}); + defrostMap.push_back({35., derate35}); + defrostMap.push_back({47., 1.}); } -void HPWH::HeatSource::clearAllLogic() { - this->clearAllTurnOnLogic(); - this->clearAllShutOffLogic(); +void HPWH::HeatSource::defrostDerate(double& to_derate, double airT_F) +{ + if (airT_F <= defrostMap[0].T_F || airT_F >= defrostMap[defrostMap.size() - 1].T_F) + { + return; // Air temperature outside bounds of the defrost map. There is no extrapolation + // here. + } + double derate_factor = 1.; + size_t i_prev = 0; + for (size_t i = 1; i < defrostMap.size(); ++i) + { + if (airT_F <= defrostMap[i].T_F) + { + i_prev = i - 1; + break; + } + } + linearInterp(derate_factor, + airT_F, + defrostMap[i_prev].T_F, + defrostMap[i_prev + 1].T_F, + defrostMap[i_prev].derate_fraction, + defrostMap[i_prev + 1].derate_fraction); + to_derate *= derate_factor; +} + +void HPWH::HeatSource::linearInterp( + double& ynew, double xnew, double x0, double x1, double y0, double y1) +{ + ynew = y0 + (xnew - x0) * (y1 - y0) / (x1 - x0); +} + +void HPWH::HeatSource::regressedMethod( + double& ynew, std::vector& coefficents, double x1, double x2, double x3) +{ + ynew = coefficents[0] + coefficents[1] * x1 + coefficents[2] * x2 + coefficents[3] * x3 + + coefficents[4] * x1 * x1 + coefficents[5] * x2 * x2 + coefficents[6] * x3 * x3 + + coefficents[7] * x1 * x2 + coefficents[8] * x1 * x3 + coefficents[9] * x2 * x3 + + coefficents[10] * x1 * x2 * x3; +} + +void HPWH::HeatSource::regressedMethodMP(double& ynew, + std::vector& coefficents, + double x1, + double x2) +{ + // Const Tair Tin Tair2 Tin2 TairTin + ynew = coefficents[0] + coefficents[1] * x1 + coefficents[2] * x2 + coefficents[3] * x1 * x1 + + coefficents[4] * x2 * x2 + coefficents[5] * x1 * x2; +} + +void HPWH::HeatSource::btwxtInterp(double& input_BTUperHr, double& cop, std::vector& target) +{ + + std::vector result = perfRGI->get_values_at_target(target); + + input_BTUperHr = result[0]; + cop = result[1]; +} + +void HPWH::HeatSource::calcHeatDist(std::vector& heatDistribution) +{ + + // Populate the vector of heat distribution + if (configuration == CONFIG_SUBMERGED) + { + heatDistribution.resize(hpwh->getNumNodes()); + resampleExtensive(heatDistribution, condensity); + } + else if (configuration == CONFIG_WRAPPED) + { // Wrapped around the tank, send through the logistic function + calcThermalDist( + heatDistribution, Tshrinkage_C, lowestNode, hpwh->tankTemps_C, hpwh->setpoint_C); + } +} + +bool HPWH::HeatSource::isACompressor() const { return this->typeOfHeatSource == TYPE_compressor; } + +bool HPWH::HeatSource::isAResistance() const { return this->typeOfHeatSource == TYPE_resistance; } +bool HPWH::HeatSource::isExternalMultipass() const +{ + return isMultipass && configuration == HeatSource::CONFIG_EXTERNAL; +} + +double HPWH::HeatSource::addHeatExternal(double externalT_C, + double minutesToRun, + double& cap_BTUperHr, + double& input_BTUperHr, + double& cop) +{ + double heatingCapacity_kJ, heatingCapacityNeeded_kJ, deltaT_C, timeUsed_min, nodeHeat_kJperNode, + nodeFrac, fractToShutOff; + double inputTemp_BTUperHr = 0, capTemp_BTUperHr = 0, copTemp = 0; + double volumePerNode_LperNode = hpwh->tankVolume_L / hpwh->getNumNodes(); + double timeRemaining_min = minutesToRun; + double maxTargetTemp_C = std::min(maxSetpoint_C, hpwh->setpoint_C); + double targetTemp_C = 0.; + 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]); + } + + if (this->isMultipass) + { + // if multipass evenly mix the tank up + hpwh->mixTankNodes( + 0, hpwh->getNumNodes(), 1.0); // 1.0 will give even mixing, so all temperatures + // mixed end at average temperature. + + // how much heat is added this timestep + getCapacityMP(externalT_C, + hpwh->tankTemps_C[externalOutletHeight], + inputTemp_BTUperHr, + capTemp_BTUperHr, + copTemp); + double heatingCapacity_KW = BTUperH_TO_KW(capTemp_BTUperHr); + + heatingCapacity_kJ = heatingCapacity_KW * (timeRemaining_min * sec_per_min); + + targetTemp_C = calcMPOutletTemperature(heatingCapacity_KW); + deltaT_C = targetTemp_C - hpwh->tankTemps_C[externalOutletHeight]; + } + else + { + // how much heat is available this timestep + getCapacity(externalT_C, + hpwh->tankTemps_C[externalOutletHeight], + inputTemp_BTUperHr, + capTemp_BTUperHr, + copTemp); + heatingCapacity_kJ = BTU_TO_KJ(capTemp_BTUperHr * (minutesToRun / min_per_hr)); + 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 *= (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 + targetTemp_C = maxTargetTemp_C; + deltaT_C = targetTemp_C - hpwh->tankTemps_C[externalOutletHeight]; + } + + nodeHeat_kJperNode = + volumePerNode_LperNode * DENSITYWATER_kgperL * CPWATER_kJperkgC * deltaT_C; + + // Caclulate fraction of node to move + if (nodeHeat_kJperNode <= 0.) + { // protect against dividing by zero - if bottom node is at (or above) setpoint, add no + // heat + 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); + } + + fractToShutOff = fractToMeetComparisonExternal(); + if (fractToShutOff < 1. && fractToShutOff < nodeFrac && !this->isMultipass) + { // circle back and check on this for multipass + nodeFrac = fractToShutOff; + heatingCapacityNeeded_kJ = nodeFrac * nodeHeat_kJperNode; + + timeUsed_min = (heatingCapacityNeeded_kJ / heatingCapacity_kJ) * timeRemaining_min; + timeRemaining_min -= timeUsed_min; + } + // if more than one, round down to 1 and subtract the amount of time it would + // take to heat that node from the timeRemaining + else 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.; + } + + // Track the condenser temperature if this is a compressor before moving the nodes + if (isACompressor()) + { + hpwh->condenserInlet_C += hpwh->tankTemps_C[externalOutletHeight] * timeUsed_min; + hpwh->condenserOutlet_C += targetTemp_C * timeUsed_min; + } + + // Moving the nodes down + // move all nodes down, mixing if less than a full node + for (int n = externalOutletHeight; n < externalInletHeight; 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[externalInletHeight] = + hpwh->tankTemps_C[externalInletHeight] * (1. - nodeFrac) + targetTemp_C * nodeFrac; + + hpwh->mixTankInversions(); + hpwh->updateSoCIfNecessary(); + + // track outputs - weight by the time ran + // Add in pump power to approximate a secondary heat exchange in line with the compressor + input_BTUperHr += + (inputTemp_BTUperHr + W_TO_BTUperH(secondaryHeatExchanger.extraPumpPower_W)) * + timeUsed_min; + cap_BTUperHr += capTemp_BTUperHr * timeUsed_min; + cop += copTemp * timeUsed_min; + + hpwh->externalVolumeHeated_L += nodeFrac * volumePerNode_LperNode; + + // 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 + // not timeRemaining_min == minutesToRun is possible + // must prevent divide by 0 (added 4-11-2023) + double timeRun = minutesToRun - timeRemaining_min; + if (timeRun > 0.) + { + input_BTUperHr /= timeRun; + cap_BTUperHr /= timeRun; + cop /= timeRun; + hpwh->condenserInlet_C /= timeRun; + hpwh->condenserOutlet_C /= timeRun; + } + + if (hpwh->hpwhVerbosity >= VRB_emetic) + { + hpwh->msg("final remaining time: %.2lf \n", timeRemaining_min); + } + // return the time left + return timeRun; +} + +void HPWH::HeatSource::setupAsResistiveElement(int node, + double Watts, + int condensitySize /* = CONDENSITY_SIZE*/) +{ + + isOn = false; + isVIP = false; + condensity = std::vector(condensitySize, 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::addTurnOnLogic(std::shared_ptr logic) +{ + this->turnOnLogicSet.push_back(logic); +} + +void HPWH::HeatSource::addShutOffLogic(std::shared_ptr logic) +{ + this->shutOffLogicSet.push_back(logic); +} + +void HPWH::HeatSource::clearAllTurnOnLogic() { this->turnOnLogicSet.clear(); } + +void HPWH::HeatSource::clearAllShutOffLogic() { this->shutOffLogicSet.clear(); } + +void HPWH::HeatSource::clearAllLogic() +{ + this->clearAllTurnOnLogic(); + this->clearAllShutOffLogic(); } -void HPWH::HeatSource::changeResistanceWatts(double watts) { - for(auto &perfP : perfMap) { - perfP.inputPower_coeffs[0] = watts; - } +void HPWH::HeatSource::changeResistanceWatts(double watts) +{ + for (auto& perfP : perfMap) + { + perfP.inputPower_coeffs[0] = watts; + } } diff --git a/src/HPWHHeatingLogics.cc b/src/HPWHHeatingLogics.cc index 174d75f1..e2f1838d 100644 --- a/src/HPWHHeatingLogics.cc +++ b/src/HPWHHeatingLogics.cc @@ -5,226 +5,285 @@ File of the presets heating logics available HPWHsim #include "HPWH.hh" /* State of Charge Based Logic*/ -const bool HPWH::SoCBasedHeatingLogic::isValid() { - bool isValid = true; - if(decisionPoint < 0) { - isValid = false; - } - return isValid; +const bool HPWH::SoCBasedHeatingLogic::isValid() +{ + bool isValid = true; + if (decisionPoint < 0) + { + isValid = false; + } + return isValid; } -const double HPWH::SoCBasedHeatingLogic::getComparisonValue() { - return decisionPoint + hysteresisFraction; +const double HPWH::SoCBasedHeatingLogic::getComparisonValue() +{ + return decisionPoint + hysteresisFraction; } -const double HPWH::SoCBasedHeatingLogic::getTankValue() { - double soCFraction; - if(hpwh->member_inletT_C == HPWH_ABORT && !useCostantMains) { - soCFraction = HPWH_ABORT; - } else { - soCFraction = hpwh->getSoCFraction(); - } - return soCFraction; +const double HPWH::SoCBasedHeatingLogic::getTankValue() +{ + double soCFraction; + if (hpwh->member_inletT_C == HPWH_ABORT && !useCostantMains) + { + soCFraction = HPWH_ABORT; + } + else + { + soCFraction = hpwh->getSoCFraction(); + } + return soCFraction; } -const double HPWH::SoCBasedHeatingLogic::getMainsT_C() { - if(useCostantMains) { - return constantMains_C; - } else { - return hpwh->member_inletT_C; - } +const double HPWH::SoCBasedHeatingLogic::getMainsT_C() +{ + if (useCostantMains) + { + return constantMains_C; + } + else + { + return hpwh->member_inletT_C; + } } -const double HPWH::SoCBasedHeatingLogic::getTempMinUseful_C() { - return tempMinUseful_C; -} - -int HPWH::SoCBasedHeatingLogic::setDecisionPoint(double value) { - decisionPoint = value; - return 0; -} +const double HPWH::SoCBasedHeatingLogic::getTempMinUseful_C() { return tempMinUseful_C; } -int HPWH::SoCBasedHeatingLogic::setConstantMainsTemperature(double mains_C) { - constantMains_C = mains_C; - useCostantMains = true; - return 0; +int HPWH::SoCBasedHeatingLogic::setDecisionPoint(double value) +{ + decisionPoint = value; + return 0; } -const double HPWH::SoCBasedHeatingLogic::nodeWeightAvgFract() { - return getComparisonValue(); +int HPWH::SoCBasedHeatingLogic::setConstantMainsTemperature(double mains_C) +{ + constantMains_C = mains_C; + useCostantMains = true; + return 0; } -const double HPWH::SoCBasedHeatingLogic::getFractToMeetComparisonExternal() { - double deltaSoCFraction = (getComparisonValue() + HPWH::TOL_MINVALUE) - getTankValue(); - - // Check how much of a change in the SoC fraction occurs if one full node at set point is added. If this is less than the change needed move on. - double fullNodeSoC = 1. / hpwh->getNumNodes(); - if(deltaSoCFraction >= fullNodeSoC) { - return 1.; - } - - // Find the last node greater the min use temp - int calcNode = 0; - for(int i = hpwh->getNumNodes() - 1; i >= 0; i--) { - if(hpwh->tankTemps_C[i] < tempMinUseful_C) { - calcNode = i + 1; - break; - } - } - if(calcNode == hpwh->getNumNodes()) { // if the whole tank is cold - return 1.; - } - - // Find the fraction to heat the calc node to meet the target SoC fraction without heating the node below up to tempMinUseful. - double maxSoC = hpwh->getNumNodes() * hpwh->getChargePerNode(getMainsT_C(),tempMinUseful_C,hpwh->setpoint_C); - double targetTemp = deltaSoCFraction * maxSoC + (hpwh->tankTemps_C[calcNode] - getMainsT_C()) / (tempMinUseful_C - getMainsT_C()); - targetTemp = targetTemp * (tempMinUseful_C - getMainsT_C()) + getMainsT_C(); - - //Catch case where node temperature == setpoint - double fractCalcNode; - if(hpwh->tankTemps_C[calcNode] >= hpwh->setpoint_C) { - fractCalcNode = 1; - } else { - fractCalcNode = (targetTemp - hpwh->tankTemps_C[calcNode]) / (hpwh->setpoint_C - hpwh->tankTemps_C[calcNode]); - } - - // If we're at the bottom node there's not another node to heat so case 2 doesn't apply. - if(calcNode == 0) { - return fractCalcNode; - } - - // Fraction to heat next node, where the step change occurs - double fractNextNode = (tempMinUseful_C - hpwh->tankTemps_C[calcNode - 1]) / (hpwh->tankTemps_C[calcNode] - hpwh->tankTemps_C[calcNode - 1]); - fractNextNode += HPWH::TOL_MINVALUE; - - if(hpwh->hpwhVerbosity >= VRB_emetic) { - double smallestSoCChangeWhenHeatingNextNode = 1. / maxSoC * (1. + fractNextNode * (hpwh->setpoint_C - hpwh->tankTemps_C[calcNode]) / - (tempMinUseful_C - getMainsT_C())); - hpwh->msg("fractThisNode %.6f, fractNextNode %.6f, smallestSoCChangeWithNextNode: %.6f, deltaSoCFraction: %.6f\n", - fractCalcNode,fractNextNode,smallestSoCChangeWhenHeatingNextNode,deltaSoCFraction); - } - - // if the fraction is enough to heat up the next node, do that minimum and handle the heating of the next node next iteration. - return std::min(fractCalcNode,fractNextNode); +const double HPWH::SoCBasedHeatingLogic::nodeWeightAvgFract() { return getComparisonValue(); } + +const double HPWH::SoCBasedHeatingLogic::getFractToMeetComparisonExternal() +{ + double deltaSoCFraction = (getComparisonValue() + HPWH::TOL_MINVALUE) - getTankValue(); + + // Check how much of a change in the SoC fraction occurs if one full node at set point is added. + // If this is less than the change needed move on. + double fullNodeSoC = 1. / hpwh->getNumNodes(); + if (deltaSoCFraction >= fullNodeSoC) + { + return 1.; + } + + // Find the last node greater the min use temp + int calcNode = 0; + for (int i = hpwh->getNumNodes() - 1; i >= 0; i--) + { + if (hpwh->tankTemps_C[i] < tempMinUseful_C) + { + calcNode = i + 1; + break; + } + } + if (calcNode == hpwh->getNumNodes()) + { // if the whole tank is cold + return 1.; + } + + // Find the fraction to heat the calc node to meet the target SoC fraction without heating the + // node below up to tempMinUseful. + double maxSoC = hpwh->getNumNodes() * + hpwh->getChargePerNode(getMainsT_C(), tempMinUseful_C, hpwh->setpoint_C); + double targetTemp = deltaSoCFraction * maxSoC + (hpwh->tankTemps_C[calcNode] - getMainsT_C()) / + (tempMinUseful_C - getMainsT_C()); + targetTemp = targetTemp * (tempMinUseful_C - getMainsT_C()) + getMainsT_C(); + + // Catch case where node temperature == setpoint + double fractCalcNode; + if (hpwh->tankTemps_C[calcNode] >= hpwh->setpoint_C) + { + fractCalcNode = 1; + } + else + { + fractCalcNode = (targetTemp - hpwh->tankTemps_C[calcNode]) / + (hpwh->setpoint_C - hpwh->tankTemps_C[calcNode]); + } + + // If we're at the bottom node there's not another node to heat so case 2 doesn't apply. + if (calcNode == 0) + { + return fractCalcNode; + } + + // Fraction to heat next node, where the step change occurs + double fractNextNode = (tempMinUseful_C - hpwh->tankTemps_C[calcNode - 1]) / + (hpwh->tankTemps_C[calcNode] - hpwh->tankTemps_C[calcNode - 1]); + fractNextNode += HPWH::TOL_MINVALUE; + + if (hpwh->hpwhVerbosity >= VRB_emetic) + { + double smallestSoCChangeWhenHeatingNextNode = + 1. / maxSoC * + (1. + fractNextNode * (hpwh->setpoint_C - hpwh->tankTemps_C[calcNode]) / + (tempMinUseful_C - getMainsT_C())); + hpwh->msg("fractThisNode %.6f, fractNextNode %.6f, smallestSoCChangeWithNextNode: %.6f, " + "deltaSoCFraction: %.6f\n", + fractCalcNode, + fractNextNode, + smallestSoCChangeWhenHeatingNextNode, + deltaSoCFraction); + } + + // if the fraction is enough to heat up the next node, do that minimum and handle the heating of + // the next node next iteration. + return std::min(fractCalcNode, fractNextNode); } /* Temperature Based Heating Logic*/ -const bool HPWH::TempBasedHeatingLogic::isValid() { - bool isValid = true; - if(!areNodeWeightsValid()) { - isValid = false; - } - return isValid; +const bool HPWH::TempBasedHeatingLogic::isValid() +{ + bool isValid = true; + if (!areNodeWeightsValid()) + { + isValid = false; + } + return isValid; } -const bool HPWH::TempBasedHeatingLogic::areNodeWeightsValid() { - for(auto nodeWeight : nodeWeights) { - if(nodeWeight.nodeNum > 13 || nodeWeight.nodeNum < 0) { - return false; - } - } - return true; +const bool HPWH::TempBasedHeatingLogic::areNodeWeightsValid() +{ + for (auto nodeWeight : nodeWeights) + { + if (nodeWeight.nodeNum > 13 || nodeWeight.nodeNum < 0) + { + return false; + } + } + return true; } -const double HPWH::TempBasedHeatingLogic::getComparisonValue() { - double value = decisionPoint; - if(isAbsolute) { - return value; - } else { - return hpwh->getSetpoint() - value; - } +const double HPWH::TempBasedHeatingLogic::getComparisonValue() +{ + double value = decisionPoint; + if (isAbsolute) + { + return value; + } + else + { + return hpwh->getSetpoint() - value; + } } -const double HPWH::TempBasedHeatingLogic::getTankValue() { - return hpwh->tankAvg_C(nodeWeights); -} +const double HPWH::TempBasedHeatingLogic::getTankValue() { return hpwh->tankAvg_C(nodeWeights); } -int HPWH::TempBasedHeatingLogic::setDecisionPoint(double value) { - decisionPoint = value; - return 0; +int HPWH::TempBasedHeatingLogic::setDecisionPoint(double value) +{ + decisionPoint = value; + return 0; } -int HPWH::TempBasedHeatingLogic::setDecisionPoint(double value,bool absolute) { - isAbsolute = absolute; - return setDecisionPoint(value); +int HPWH::TempBasedHeatingLogic::setDecisionPoint(double value, bool absolute) +{ + isAbsolute = absolute; + return setDecisionPoint(value); } -const double HPWH::TempBasedHeatingLogic::nodeWeightAvgFract() { - double logicNode; - double calcNodes = 0,totWeight = 0; - - for(auto nodeWeight : nodeWeights) { - // bottom calc node only - if(nodeWeight.nodeNum == 0) { // simple equation - return 1. / (double)hpwh->getNumNodes(); - } - // top calc node only - else if(nodeWeight.nodeNum == LOGIC_NODE_SIZE + 1) { - return 1.; - } else { // have to tally up the nodes - calcNodes += nodeWeight.nodeNum * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - } - - logicNode = calcNodes / totWeight; - - return logicNode / static_cast(LOGIC_NODE_SIZE); +const double HPWH::TempBasedHeatingLogic::nodeWeightAvgFract() +{ + double logicNode; + double calcNodes = 0, totWeight = 0; + + for (auto nodeWeight : nodeWeights) + { + // bottom calc node only + if (nodeWeight.nodeNum == 0) + { // simple equation + return 1. / (double)hpwh->getNumNodes(); + } + // top calc node only + else if (nodeWeight.nodeNum == LOGIC_NODE_SIZE + 1) + { + return 1.; + } + else + { // have to tally up the nodes + calcNodes += nodeWeight.nodeNum * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + } + + logicNode = calcNodes / totWeight; + + return logicNode / static_cast(LOGIC_NODE_SIZE); } -const double HPWH::TempBasedHeatingLogic::getFractToMeetComparisonExternal() { - double fracTemp; - double diff; - int calcNode = 0; - int firstNode = -1; - double sum = 0; - double totWeight = 0; - - std::vector resampledTankTemps(LOGIC_NODE_SIZE); - resample(resampledTankTemps,hpwh->tankTemps_C); - double comparison = getComparisonValue(); - comparison += HPWH::TOL_MINVALUE; // Make this possible so we do slightly over heat - - double nodeDensity = static_cast(hpwh->getNumNodes()) / LOGIC_NODE_SIZE; - for(auto nodeWeight : nodeWeights) { - - // bottom calc node only - if(nodeWeight.nodeNum == 0) { // bottom-most tank node only - firstNode = calcNode = 0; - double nodeTemp = hpwh->tankTemps_C.front(); - sum = nodeTemp * nodeWeight.weight; - totWeight = nodeWeight.weight; - } - // top calc node only - else if(nodeWeight.nodeNum == LOGIC_NODE_SIZE + 1) { // top-most tank node only - calcNode = firstNode = hpwh->getNumNodes() - 1; - double nodeTemp = hpwh->tankTemps_C.back(); - sum = nodeTemp * nodeWeight.weight; - totWeight = nodeWeight.weight; - } else { // all tank nodes corresponding to logical node - firstNode = static_cast(nodeDensity * (nodeWeight.nodeNum - 1)); - calcNode = static_cast(nodeDensity * (nodeWeight.nodeNum))- 1; - double nodeTemp = resampledTankTemps[static_cast(nodeWeight.nodeNum - 1)]; - sum += nodeTemp * nodeWeight.weight; - totWeight += nodeWeight.weight; - } - } - - if(calcNode == hpwh->getNumNodes() - 1) { // top node calc - diff = hpwh->getSetpoint() - hpwh->tankTemps_C[firstNode]; - } else { - diff = hpwh->tankTemps_C[calcNode + 1] - hpwh->tankTemps_C[firstNode]; - } - // if totWeight * comparison - sum < 0 then the shutoff condition is already true and you shouldn't - // be here. Will revaluate shut off condition at the end the do while loop of addHeatExternal, in the - // mean time lets not shift anything around. - if(compare(sum,totWeight * comparison)) { // Then should shut off - fracTemp = 0.; // 0 means shift no nodes - } else { - // if the difference in denominator is <= 0 then we aren't adding heat to the nodes we care about, so - // shift a whole node. - // factor of hpwh->nodeDensity included below to reproduce original algorithm - fracTemp = diff > 0. ? (totWeight * comparison - sum) * nodeDensity / diff : 1.; - } - - return fracTemp; +const double HPWH::TempBasedHeatingLogic::getFractToMeetComparisonExternal() +{ + double fracTemp; + double diff; + int calcNode = 0; + int firstNode = -1; + double sum = 0; + double totWeight = 0; + + std::vector resampledTankTemps(LOGIC_NODE_SIZE); + resample(resampledTankTemps, hpwh->tankTemps_C); + double comparison = getComparisonValue(); + comparison += HPWH::TOL_MINVALUE; // Make this possible so we do slightly over heat + + double nodeDensity = static_cast(hpwh->getNumNodes()) / LOGIC_NODE_SIZE; + for (auto nodeWeight : nodeWeights) + { + + // bottom calc node only + if (nodeWeight.nodeNum == 0) + { // bottom-most tank node only + firstNode = calcNode = 0; + double nodeTemp = hpwh->tankTemps_C.front(); + sum = nodeTemp * nodeWeight.weight; + totWeight = nodeWeight.weight; + } + // top calc node only + else if (nodeWeight.nodeNum == LOGIC_NODE_SIZE + 1) + { // top-most tank node only + calcNode = firstNode = hpwh->getNumNodes() - 1; + double nodeTemp = hpwh->tankTemps_C.back(); + sum = nodeTemp * nodeWeight.weight; + totWeight = nodeWeight.weight; + } + else + { // all tank nodes corresponding to logical node + firstNode = static_cast(nodeDensity * (nodeWeight.nodeNum - 1)); + calcNode = static_cast(nodeDensity * (nodeWeight.nodeNum)) - 1; + double nodeTemp = resampledTankTemps[static_cast(nodeWeight.nodeNum - 1)]; + sum += nodeTemp * nodeWeight.weight; + totWeight += nodeWeight.weight; + } + } + + if (calcNode == hpwh->getNumNodes() - 1) + { // top node calc + diff = hpwh->getSetpoint() - hpwh->tankTemps_C[firstNode]; + } + else + { + diff = hpwh->tankTemps_C[calcNode + 1] - hpwh->tankTemps_C[firstNode]; + } + // if totWeight * comparison - sum < 0 then the shutoff condition is already true and you + // shouldn't be here. Will revaluate shut off condition at the end the do while loop of + // addHeatExternal, in the mean time lets not shift anything around. + if (compare(sum, totWeight * comparison)) + { // Then should shut off + fracTemp = 0.; // 0 means shift no nodes + } + else + { + // if the difference in denominator is <= 0 then we aren't adding heat to the nodes we care + // about, so shift a whole node. factor of hpwh->nodeDensity included below to reproduce + // original algorithm + fracTemp = diff > 0. ? (totWeight * comparison - sum) * nodeDensity / diff : 1.; + } + + return fracTemp; } diff --git a/src/HPWHpresets.cc b/src/HPWHpresets.cc index a39bb2f3..cf6e7625 100644 --- a/src/HPWHpresets.cc +++ b/src/HPWHpresets.cc @@ -7,3914 +7,4594 @@ File Containing all of the presets available in HPWHsim #include -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() +{ + // 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) { - - setAllDefaults(); // reset all defaults if you're re-initilizing - // sets simHasFailed = true; this gets cleared on successful completion of init - // return 0 on success, HPWH_ABORT for failure - - heatSources.clear(); - - //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 lower element wattage below 550 W. DOES NOT COMPUTE\n"); - } - return HPWH_ABORT; - } - if (upperPower_W < 0.) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Upper resistance tank wattage below 0 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; - } - - setNumNodes(12); - - //use tank size setting function since it has bounds checking - tankSizeFixed = false; - int failure = this->setTankSize(tankVol_L); - if (failure == HPWH_ABORT) { - return failure; - } - - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource resistiveElementBottom(this); - resistiveElementBottom.setupAsResistiveElement(0, lowerPower_W); - - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - - if (upperPower_W > 0.) { - // Only add an upper element when the upperPower_W > 0 otherwise ignore this. - // If the element is added this can mess with the intended logic. - HeatSource resistiveElementTop(this); - resistiveElementTop.setupAsResistiveElement(8, upperPower_W); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.isVIP = true; - - // set everything in it's correct place - heatSources.resize(2); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - - heatSources[0].followedByHeatSource = &heatSources[1]; - } - else { - heatSources.resize(1); - heatSources[0] = resistiveElementBottom; - } - - // (1/EnFac - 1/RecovEff) / (67.5 * ((24/41094) - 1/(RecovEff * Power_btuperHr))) - // Previous comment and the equation source said (1/EnFac + 1/RecovEff) however this was - // determined to be a typo from the source that was kept in the comment. On 8/12/2021 - // the comment was changed to reflect the correct mathematical formulation which is performed - // below. - 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 < getNumHeatSources(); i++) { - if (heatSources[i].isOn) { - isHeating = true; - } - heatSources[i].sortPerformanceMap(); - } - - if (hpwhVerbosity >= VRB_emetic) { - for (int i = 0; i < getNumHeatSources(); i++) { - msg("heat source %d: %p \n", i, &heatSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - return 0; //successful init returns 0 +int HPWH::HPWHinit_resTank(double tankVol_L, + double energyFactor, + double upperPower_W, + double lowerPower_W) +{ + + setAllDefaults(); // reset all defaults if you're re-initilizing + // sets simHasFailed = true; this gets cleared on successful completion of init + // return 0 on success, HPWH_ABORT for failure + + heatSources.clear(); + + // 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 lower element wattage below 550 W. DOES NOT COMPUTE\n"); + } + return HPWH_ABORT; + } + if (upperPower_W < 0.) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Upper resistance tank wattage below 0 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; + } + + setNumNodes(12); + + // use tank size setting function since it has bounds checking + tankSizeFixed = false; + int failure = this->setTankSize(tankVol_L); + if (failure == HPWH_ABORT) + { + return failure; + } + + setpoint_C = F_TO_C(127.0); + + // start tank off at setpoint + resetTankToSetpoint(); + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource resistiveElementBottom(this); + resistiveElementBottom.setupAsResistiveElement(0, lowerPower_W); + + // standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); + + if (upperPower_W > 0.) + { + // Only add an upper element when the upperPower_W > 0 otherwise ignore this. + // If the element is added this can mess with the intended logic. + HeatSource resistiveElementTop(this); + resistiveElementTop.setupAsResistiveElement(8, upperPower_W); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.isVIP = true; + + // set everything in it's correct place + heatSources.resize(2); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + + heatSources[0].followedByHeatSource = &heatSources[1]; + } + else + { + heatSources.resize(1); + heatSources[0] = resistiveElementBottom; + } + + // (1/EnFac - 1/RecovEff) / (67.5 * ((24/41094) - 1/(RecovEff * Power_btuperHr))) + // Previous comment and the equation source said (1/EnFac + 1/RecovEff) however this was + // determined to be a typo from the source that was kept in the comment. On 8/12/2021 + // the comment was changed to reflect the correct mathematical formulation which is performed + // below. + 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 < getNumHeatSources(); i++) + { + if (heatSources[i].isOn) + { + isHeating = true; + } + heatSources[i].sortPerformanceMap(); + } + + if (hpwhVerbosity >= VRB_emetic) + { + for (int i = 0; i < getNumHeatSources(); i++) + { + msg("heat source %d: %p \n", i, &heatSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + return 0; // successful init returns 0 } - -int HPWH::HPWHinit_resTankGeneric(double tankVol_L, double rValue_M2KperW, double upperPower_W, double lowerPower_W) { - - setAllDefaults(); // reset all defaults if you're re-initilizing - // sets simHasFailed = true; this gets cleared on successful completion of init - // return 0 on success, HPWH_ABORT for failure - heatSources.clear(); - - //low power element will cause divide by zero/negative UA in EF -> UA conversion - if (lowerPower_W < 0) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Lower resistance tank wattage below 0 W. DOES NOT COMPUTE\n"); - } - return HPWH_ABORT; - } - if (upperPower_W < 0.) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Upper resistance tank wattage below 0 W. DOES NOT COMPUTE\n"); - } - return HPWH_ABORT; - } - if (rValue_M2KperW <= 0.) { - if (hpwhVerbosity >= VRB_reluctant) { - msg("R-Value is equal to or below 0. DOES NOT COMPUTE\n"); - } - return HPWH_ABORT; - } - - setNumNodes(12); - - //set tank size function has bounds checking - tankSizeFixed = false; - if (this->setTankSize(tankVol_L) == HPWH_ABORT) { - return HPWH_ABORT; - } - canScale = true; - - setpoint_C = F_TO_C(127.0); - resetTankToSetpoint(); //start tank off at setpoint - - doTempDepression = false; - tankMixesOnDraw = true; - - // Deal with upper element - if (upperPower_W > 0.) { - // Only add an upper element when the upperPower_W > 0 otherwise ignore this. - // If the element is added this can mess with the intended logic. - HeatSource resistiveElementTop(this); - resistiveElementTop.setupAsResistiveElement(8, upperPower_W); - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); - resistiveElementTop.isVIP = true; - - // Upper should always be first in heatSources if it exists. - heatSources.push_back(resistiveElementTop); - } - - // Deal with bottom element - if (lowerPower_W > 0.) { - HeatSource resistiveElementBottom(this); - resistiveElementBottom.setupAsResistiveElement(0, lowerPower_W); - - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40.))); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10.))); - - // set everything in it's correct place - heatSources.push_back(resistiveElementBottom); - } - - if (getNumHeatSources() == 2) { - heatSources[0].followedByHeatSource = &heatSources[1]; - } - - // Calc UA - double SA_M2 = getTankSurfaceArea(tankVol_L, HPWH::UNITS_L, HPWH::UNITS_M2); - double tankUA_WperK = SA_M2 / rValue_M2KperW; - tankUA_kJperHrC = tankUA_WperK * 3.6; // 3.6 = 3600 S/Hr and 1/1000 kJ/J - - 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 = HPWH::MODELS_CustomResTankGeneric; - - //calculate oft-used derived values - calcDerivedValues(); - - if (checkInputs() == HPWH_ABORT) return HPWH_ABORT; - - isHeating = false; - for (auto &source: heatSources) { - if (source.isOn) { - isHeating = true; - } - source.sortPerformanceMap(); - } - - if (hpwhVerbosity >= VRB_emetic) { - for (int i = 0; i < getNumHeatSources(); i++) { - msg("heat source %d: %p \n", i, &heatSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - return 0; //successful init returns 0 +int HPWH::HPWHinit_resTankGeneric(double tankVol_L, + double rValue_M2KperW, + double upperPower_W, + double lowerPower_W) +{ + + setAllDefaults(); // reset all defaults if you're re-initilizing + // sets simHasFailed = true; this gets cleared on successful completion of init + // return 0 on success, HPWH_ABORT for failure + heatSources.clear(); + + // low power element will cause divide by zero/negative UA in EF -> UA conversion + if (lowerPower_W < 0) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Lower resistance tank wattage below 0 W. DOES NOT COMPUTE\n"); + } + return HPWH_ABORT; + } + if (upperPower_W < 0.) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Upper resistance tank wattage below 0 W. DOES NOT COMPUTE\n"); + } + return HPWH_ABORT; + } + if (rValue_M2KperW <= 0.) + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("R-Value is equal to or below 0. DOES NOT COMPUTE\n"); + } + return HPWH_ABORT; + } + + setNumNodes(12); + + // set tank size function has bounds checking + tankSizeFixed = false; + if (this->setTankSize(tankVol_L) == HPWH_ABORT) + { + return HPWH_ABORT; + } + canScale = true; + + setpoint_C = F_TO_C(127.0); + resetTankToSetpoint(); // start tank off at setpoint + + doTempDepression = false; + tankMixesOnDraw = true; + + // Deal with upper element + if (upperPower_W > 0.) + { + // Only add an upper element when the upperPower_W > 0 otherwise ignore this. + // If the element is added this can mess with the intended logic. + HeatSource resistiveElementTop(this); + resistiveElementTop.setupAsResistiveElement(8, upperPower_W); + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.isVIP = true; + + // Upper should always be first in heatSources if it exists. + heatSources.push_back(resistiveElementTop); + } + + // Deal with bottom element + if (lowerPower_W > 0.) + { + HeatSource resistiveElementBottom(this); + resistiveElementBottom.setupAsResistiveElement(0, lowerPower_W); + + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40.))); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10.))); + + // set everything in it's correct place + heatSources.push_back(resistiveElementBottom); + } + + if (getNumHeatSources() == 2) + { + heatSources[0].followedByHeatSource = &heatSources[1]; + } + + // Calc UA + double SA_M2 = getTankSurfaceArea(tankVol_L, HPWH::UNITS_L, HPWH::UNITS_M2); + double tankUA_WperK = SA_M2 / rValue_M2KperW; + tankUA_kJperHrC = tankUA_WperK * 3.6; // 3.6 = 3600 S/Hr and 1/1000 kJ/J + + 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 = HPWH::MODELS_CustomResTankGeneric; + + // calculate oft-used derived values + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) + return HPWH_ABORT; + + isHeating = false; + for (auto& source : heatSources) + { + if (source.isOn) + { + isHeating = true; + } + source.sortPerformanceMap(); + } + + if (hpwhVerbosity >= VRB_emetic) + { + for (int i = 0; i < getNumHeatSources(); i++) + { + msg("heat source %d: %p \n", i, &heatSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + return 0; // successful init returns 0 } -int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C) { - - setAllDefaults(); // reset all defaults if you're re-initilizing - // sets simHasFailed = true; this gets cleared on successful completion of init - // return 0 on success, HPWH_ABORT for failure - heatSources.clear(); - - //except where noted, these values are taken from MODELS_GE2014STDMode on 5/17/16 - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - //start tank off at setpoint - resetTankToSetpoint(); - - tankSizeFixed = false; - - //custom settings - these are set later - //tankVolume_L = GAL_TO_L(45); - //tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(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 place - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[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 < getNumHeatSources(); i++) { - if (heatSources[i].isOn) { - isHeating = true; - } - heatSources[i].sortPerformanceMap(); - } - - if (hpwhVerbosity >= VRB_emetic) { - for (int i = 0; i < getNumHeatSources(); i++) { - msg("heat source %d: %p \n", i, &heatSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - - return 0; +int HPWH::HPWHinit_genericHPWH(double tankVol_L, double energyFactor, double resUse_C) +{ + + setAllDefaults(); // reset all defaults if you're re-initilizing + // sets simHasFailed = true; this gets cleared on successful completion of init + // return 0 on success, HPWH_ABORT for failure + heatSources.clear(); + + // except where noted, these values are taken from MODELS_GE2014STDMode on 5/17/16 + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + // start tank off at setpoint + resetTankToSetpoint(); + + tankSizeFixed = false; + + // custom settings - these are set later + // tankVolume_L = GAL_TO_L(45); + // tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(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 place + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[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 < getNumHeatSources(); i++) + { + if (heatSources[i].isOn) + { + isHeating = true; + } + heatSources[i].sortPerformanceMap(); + } + + if (hpwhVerbosity >= VRB_emetic) + { + for (int i = 0; i < getNumHeatSources(); i++) + { + msg("heat source %d: %p \n", i, &heatSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + + return 0; } +int HPWH::HPWHinit_presets(MODELS presetNum) +{ -int HPWH::HPWHinit_presets(MODELS presetNum) { - - setAllDefaults(); // reset all defaults if you're re-initilizing - // sets simHasFailed = true; this gets cleared on successful completion of init - // return 0 on success, HPWH_ABORT for failure - - heatSources.clear(); - - bool hasInitialTankTemp = false; - double initialTankT_C = F_TO_C(120.); - - //resistive with no UA losses for testing - if (presetNum == MODELS_restankNoUA) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankSizeFixed = false; - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 0; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = true; - - 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 - //set everything in its places - heatSources.resize(2); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - - heatSources[0].followedByHeatSource = &heatSources[1]; - } - - //resistive tank with massive UA loss for testing - else if (presetNum == MODELS_restankHugeUA) { - setNumNodes(12); - setpoint_C = 50; - - tankSizeFixed = false; - tankVolume_L = 120; - tankUA_kJperHrC = 500; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - - //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 - heatSources.resize(2); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - - heatSources[0].followedByHeatSource = &heatSources[1]; - } - - //realistic resistive tank - else if (presetNum == MODELS_restankRealistic) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankSizeFixed = false; - 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; - - 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; - - //set everything in its place - heatSources.resize(2); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - - heatSources[0].followedByHeatSource = &heatSources[1]; - } - - else if (presetNum == MODELS_StorageTank) { - setNumNodes(12); - setpoint_C = 800.; - - initialTankT_C = F_TO_C(127.); - hasInitialTankTemp = true; - - tankSizeFixed = false; - tankVolume_L = GAL_TO_L(80); - tankUA_kJperHrC = 10; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - } + setAllDefaults(); // reset all defaults if you're re-initilizing + // sets simHasFailed = true; this gets cleared on successful completion of init + // return 0 on success, HPWH_ABORT for failure - //basic compressor tank for testing - else if (presetNum == MODELS_basicIntegrated) { - setNumNodes(12); - setpoint_C = 50; + heatSources.clear(); - tankSizeFixed = false; - tankVolume_L = 120; - tankUA_kJperHrC = 10; //0 to turn off - //tankUA_kJperHrC = 0; //0 to turn off + bool hasInitialTankTemp = false; + double initialTankT_C = F_TO_C(120.); - doTempDepression = false; - tankMixesOnDraw = false; + // resistive with no UA losses for testing + if (presetNum == MODELS_restankNoUA) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - HeatSource compressor(this); + tankSizeFixed = false; + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 0; // 0 to turn off - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementTop.setupAsResistiveElement(9, 4500); + doTempDepression = false; + tankMixesOnDraw = true; - resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); - //standard logic conditions - resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); - resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(8, 4500); - resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); - resistiveElementTop.isVIP = true; + // standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(dF_TO_dC(40))); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(dF_TO_dC(10))); - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(20))); + resistiveElementTop.isVIP = true; - //double oneSixth = 1.0 / 6.0; - compressor.setCondensity({1., 0.}); + // assign heat sources into array in order of priority + // set everything in its places + heatSources.resize(2); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; - //GE tier 1 values - compressor.perfMap.reserve(2); + heatSources[0].followedByHeatSource = &heatSources[1]; + } - 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) - }); + // resistive tank with massive UA loss for testing + else if (presetNum == MODELS_restankHugeUA) + { + setNumNodes(12); + setpoint_C = 50; - 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) - }); + tankSizeFixed = false; + tankVolume_L = 120; + tankUA_kJperHrC = 500; // 0 to turn off - 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.maxSetpoint_C = MAXOUTLET_R134A; + doTempDepression = false; + tankMixesOnDraw = false; - compressor.addTurnOnLogic(HPWH::bottomThird(20)); - compressor.addTurnOnLogic(HPWH::standby(15)); + // set up a resistive element at the bottom, 4500 kW + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); - //set everything in its place - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = compressor; - heatSources[2] = resistiveElementBottom; + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(9, 4500); - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - - //simple external style for testing - else if (presetNum == MODELS_externalTest) { - setNumNodes(96); - setpoint_C = 50; - - tankSizeFixed = false; - tankVolume_L = 120; - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 0; //0 to turn off - - doTempDepression = false; - tankMixesOnDraw = false; - - 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.isMultipass = false; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - compressor.addTurnOnLogic(HPWH::bottomThird(20)); - compressor.addTurnOnLogic(HPWH::standby(15)); - - //lowT cutoff - compressor.addShutOffLogic(HPWH::bottomNodeMaxTemp(20, true)); - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - //voltex 60 gallon - else if (presetNum == MODELS_AOSmithPHPT60) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = 215.8; - tankUA_kJperHrC = 7.31; - - doTempDepression = false; - tankMixesOnDraw = true; - - 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = compressor; - heatSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_AOSmithPHPT80) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = 283.9; - tankUA_kJperHrC = 8.8; - - doTempDepression = false; - tankMixesOnDraw = true; - - 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = compressor; - heatSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_GE2012) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = 172; - tankUA_kJperHrC = 6.8; - - doTempDepression = false; - tankMixesOnDraw = true; - - 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = compressor; - heatSources[2] = resistiveElementBottom; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - } - // If a Colmac single pass preset cold weather or not - else if (MODELS_ColmacCxV_5_SP <= presetNum && presetNum <= MODELS_ColmacCxA_30_SP) { - setNumNodes(96); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - - doTempDepression = false; - tankMixesOnDraw = false; - - tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important - tankUA_kJperHrC = 7; - - 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.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.isMultipass = false; - compressor.perfMap.reserve(1); - compressor.hysteresis_dC = 0; - - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = getNumNodes() - 1; - - //logic conditions - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(15), this)); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(std::make_shared("bottom node", nodeWeights1, dF_TO_dC(15.), - this, false, std::greater(), true)); - compressor.depressesTemperature = false; //no temp depression - - //Defrost Derate - compressor.setupDefrostMap(); - - if (presetNum == MODELS_ColmacCxV_5_SP) { - setTankSize_adjustUA(200., UNITS_GAL); - //logic conditions - compressor.minT = F_TO_C(-4.0); - compressor.maxSetpoint_C = MAXOUTLET_R410A; - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 4.9621645063, -0.0096084144, -0.0095647009, -0.0115911960, -0.0000788517, 0.0000886176, - 0.0001114142, 0.0001832377, -0.0000451308, 0.0000411975, 0.0000003535}, // Input Power Coefficients (inputPower_coeffs) - - { 3.8189922420, 0.0569412237, -0.0320101962, -0.0012859036, 0.0000576439, 0.0001101241, - -0.0000352368, -0.0002630301, -0.0000509365, 0.0000369655, -0.0000000606} // COP Coefficients (COP_coeffs) - }); - } - else { - //logic conditions - compressor.minT = F_TO_C(40.); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - if (presetNum == MODELS_ColmacCxA_10_SP) { - setTankSize_adjustUA(500., UNITS_GAL); - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 5.9786974243, 0.0194445115, -0.0077802278, 0.0053809029, -0.0000334832, 0.0001864310, 0.0001190540, - 0.0000040405, -0.0002538279, -0.0000477652, 0.0000014101}, // Input Power Coefficients (inputPower_coeffs) - - { 3.6128563086, 0.0527064498, -0.0278198945, -0.0070529748, 0.0000934705, 0.0000781711, -0.0000359215, - -0.0002223206, 0.0000359239, 0.0000727189, -0.0000005037} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_15_SP) { - setTankSize_adjustUA(600., UNITS_GAL); - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 15.5869846555, -0.0044503761, -0.0577941202, -0.0286911185, -0.0000803325, 0.0003399817, - 0.0002009576, 0.0002494761, -0.0000595773, 0.0001401800, 0.0000004312}, // Input Power Coefficients (inputPower_coeffs) - - { 1.6643120405, 0.0515623393, -0.0110239930, 0.0041514430, 0.0000481544, 0.0000493424, - -0.0000262721, -0.0002356218, -0.0000989625, -0.0000070572, 0.0000004108} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_20_SP) { - setTankSize_adjustUA(800., UNITS_GAL); - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 23.0746692231, 0.0248584608, -0.1417927282, -0.0253733303, -0.0004882754, 0.0006508079, - 0.0002139934, 0.0005552752, -0.0002026772, 0.0000607338, 0.0000021571}, // Input Power Coefficients (inputPower_coeffs) - - { 1.7692660120, 0.0525134783, -0.0081102040, -0.0008715405, 0.0001274956, 0.0000369489, - -0.0000293775, -0.0002778086, -0.0000095067, 0.0000381186, -0.0000003135} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_25_SP) { - setTankSize_adjustUA(1000., UNITS_GAL); - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 20.4185336541, -0.0236920615, -0.0736219119, -0.0260385082, -0.0005048074, 0.0004940510, - 0.0002632660, 0.0009820050, -0.0000223587, 0.0000885101, 0.0000005649}, // Input Power Coefficients (inputPower_coeffs) - - { 0.8942843854, 0.0677641611, -0.0001582927, 0.0048083998, 0.0001196407, 0.0000334921, - -0.0000378740, -0.0004146401, -0.0001213363, -0.0000031856, 0.0000006306} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_30_SP) { - setTankSize_adjustUA(1200., UNITS_GAL); - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 11.3687485772, -0.0207292362, 0.0496254077, -0.0038394967, -0.0005991041, 0.0001304318, - 0.0003099774, 0.0012092717, -0.0001455509, -0.0000893889, 0.0000018221}, // Input Power Coefficients (inputPower_coeffs) - - { 4.4170108542, 0.0596384263, -0.0416104579, -0.0017199887, 0.0000774664, 0.0001521934, - -0.0000251665, -0.0003289731, -0.0000801823, 0.0000325972, 0.0000002705} // COP Coefficients (COP_coeffs) - }); - } - } //End if MODELS_ColmacCxV_5_SP - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - - - // if colmac multipass - else if (MODELS_ColmacCxV_5_MP <= presetNum && presetNum <= MODELS_ColmacCxA_30_MP) { - setNumNodes(24); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - - doTempDepression = false; - tankMixesOnDraw = false; - - tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important - tankUA_kJperHrC = 7; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.perfMap.reserve(1); - compressor.hysteresis_dC = 0; - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = static_cast(getNumNodes() / 3) - 1; - - //logic conditions - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(5.), this)); - - std::vector nodeWeights1; - nodeWeights1.emplace_back(4); - compressor.addShutOffLogic(std::make_shared("fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); - - compressor.depressesTemperature = false; //no temp depression - - //Defrost Derate - compressor.setupDefrostMap(); - - if (presetNum == MODELS_ColmacCxV_5_MP) { - setTankSize_adjustUA(200., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(9.); //https://colmacwaterheat.com/wp-content/uploads/2020/10/Technical-Datasheet-Air-Source.pdf - - //logic conditions - compressor.minT = F_TO_C(-4.0); - compressor.maxT = F_TO_C(105.); - compressor.maxSetpoint_C = MAXOUTLET_R410A; - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 5.8438525529, 0.0003288231, -0.0494255840, -0.0000386642, 0.0004385362, 0.0000647268}, // Input Power Coefficients (inputPower_coeffs) - - { 0.6679056901, 0.0499777846, 0.0251828292, 0.0000699764, -0.0001552229, -0.0002911167} // COP Coefficients (COP_coeffs) - }); - } - else { - //logic conditions - compressor.minT = F_TO_C(40.); - compressor.maxT = F_TO_C(105.); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - if (presetNum == MODELS_ColmacCxA_10_MP) { - setTankSize_adjustUA(500., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(18.); - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 8.6918824405, 0.0136666667, -0.0548348214, -0.0000208333, 0.0005301339, -0.0000250000}, // Input Power Coefficients (inputPower_coeffs) - - { 0.6944181117, 0.0445926666, 0.0213188804, 0.0001172913, -0.0001387694, -0.0002365885} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_15_MP) { - setTankSize_adjustUA(600., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(26.); - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 12.4908723958, 0.0073988095, -0.0411417411, 0.0000000000, 0.0005789621, 0.0000696429}, // Input Power Coefficients (inputPower_coeffs) - - { 1.2846349520, 0.0334658309, 0.0019121906, 0.0002840970, 0.0000497136, -0.0004401737} // COP Coefficients (COP_coeffs) - - }); - - } - else if (presetNum == MODELS_ColmacCxA_20_MP) { - setTankSize_adjustUA(800., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(36.); //https://colmacwaterheat.com/wp-content/uploads/2020/10/Technical-Datasheet-Air-Source.pdf - - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 14.4893345424, 0.0355357143, -0.0476593192, -0.0002916667, 0.0006120954, 0.0003607143}, // Input Power Coefficients (inputPower_coeffs) - - { 1.2421582831, 0.0450256569, 0.0051234755, 0.0001271296, -0.0000299981, -0.0002910606} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_25_MP) { - setTankSize_adjustUA(1000., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(32.); - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 14.5805808222, 0.0081934524, -0.0216169085, -0.0001979167, 0.0007376535, 0.0004955357}, // Input Power Coefficients (inputPower_coeffs) - - { 2.0013175767, 0.0576617432, -0.0130480870, 0.0000856818, 0.0000610760, -0.0003684106} // COP Coefficients (COP_coeffs) - }); - - } - else if (presetNum == MODELS_ColmacCxA_30_MP) { - setTankSize_adjustUA(1200., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(41.); - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 14.5824911644, 0.0072083333, -0.0278055246, -0.0002916667, 0.0008841378, 0.0008125000}, // Input Power Coefficients (inputPower_coeffs) - - { 2.6996807527, 0.0617507969, -0.0220966420, 0.0000336149, 0.0000890989, -0.0003682431} // COP Coefficients (COP_coeffs) - }); - } - } - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - // If Nyle single pass preset - else if (MODELS_NyleC25A_SP <= presetNum && presetNum <= MODELS_NyleC250A_C_SP) { - setNumNodes(96); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - - tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important - tankUA_kJperHrC = 7; - - doTempDepression = false; - tankMixesOnDraw = false; - - 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.extrapolationMethod = EXTRAP_NEAREST; - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.isMultipass = false; - compressor.perfMap.reserve(1); - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = getNumNodes() - 1; - - //logic conditions - if (MODELS_NyleC25A_SP <= presetNum && presetNum <= MODELS_NyleC250A_SP) {// If not cold weather package - compressor.minT = F_TO_C(40.); // Min air temperature sans Cold Weather Package - } - else { - compressor.minT = F_TO_C(35.);// Min air temperature WITH Cold Weather Package - } - compressor.maxT = F_TO_C(120.0); // Max air temperature - compressor.hysteresis_dC = 0; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - // Defines the maximum outlet temperature at the a low air temperature - compressor.maxOut_at_LowT.outT_C = F_TO_C(140.); - compressor.maxOut_at_LowT.airT_C = F_TO_C(40.); - - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(15), this, false)); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(std::make_shared("bottom node", nodeWeights1, dF_TO_dC(15.), this, false, - std::greater(), true)); - compressor.depressesTemperature = false; //no temp depression - - //Defrost Derate - compressor.setupDefrostMap(); - - //Perfmaps for each compressor size - if (presetNum == MODELS_NyleC25A_SP) { - setTankSize_adjustUA(200., UNITS_GAL); - compressor.perfMap.push_back({ - 90, // Temperature (T_F) - - {4.060120364 ,-0.020584279,-0.024201054,-0.007023945,0.000017461,0.000110366,0.000060338, - 0.000120015,0.000111068,0.000138907,-0.000001569}, // Input Power Coefficients (inputPower_coeffs) - - {0.462979529,0.065656840,0.001077377,0.003428059,0.000243692,0.000021522,0.000005143, - -0.000384778,-0.000404744,-0.000036277, 0.000001900 } // COP Coefficients (COP_coeffs) - }); - } - else if (presetNum == MODELS_NyleC60A_SP || presetNum == MODELS_NyleC60A_C_SP) { - setTankSize_adjustUA(300., UNITS_GAL); - compressor.perfMap.push_back({ - 90, // Temperature (T_F) - - {-0.1180905709,0.0045354306,0.0314990479,-0.0406839757,0.0002355294,0.0000818684,0.0001943834, - -0.0002160871,0.0003053633,0.0003612413,-0.0000035912}, // Input Power Coefficients (inputPower_coeffs) - - {6.8205043418,0.0860385185,-0.0748330699,-0.0172447955,0.0000510842,0.0002187441,-0.0000321036, - -0.0003311463,-0.0002154270,0.0001307922,0.0000005568} // COP Coefficients (COP_coeffs) - }); - } - else if (presetNum == MODELS_NyleC90A_SP || presetNum == MODELS_NyleC90A_C_SP) { - setTankSize_adjustUA(400., UNITS_GAL); - compressor.perfMap.push_back({ - 90, // Temperature (T_F) - - {13.27612215047,-0.01014009337,-0.13401028549,-0.02325705976,-0.00032515646,0.00040270625, - 0.00001988733,0.00069451670,0.00069067890,0.00071091372,-0.00000854352}, // Input Power Coefficients (inputPower_coeffs) - - {1.49112327987,0.06616282153,0.00715307252,-0.01269458185,0.00031448571,0.00001765313, - 0.00006002498,-0.00045661397,-0.00034003896,-0.00004327766,0.00000176015} // COP Coefficients (COP_coeffs) - }); - } - else if (presetNum == MODELS_NyleC125A_SP || presetNum == MODELS_NyleC125A_C_SP) { - setTankSize_adjustUA(500., UNITS_GAL); - compressor.perfMap.push_back({ - 90, // Temperature (T_F) - - {-3.558277209, -0.038590968, 0.136307181, -0.016945699,0.000983753, -5.18201E-05, - 0.000476904, -0.000514211, -0.000359172, 0.000266509, -1.58646E-07}, // Input Power Coefficients (inputPower_coeffs) - - {4.889555031, 0.117102769, -0.060005795, -0.011871234, -1.79926E-05, 0.000207293, - -1.4452E-05, -0.000492486, -0.000376814, 7.85911E-05, 1.47884E-06}// COP Coefficients (COP_coeffs) - }); - } - else if (presetNum == MODELS_NyleC185A_SP || presetNum == MODELS_NyleC185A_C_SP) { - setTankSize_adjustUA(800., UNITS_GAL); - compressor.perfMap.push_back({ - 90, // Temperature (T_F) - - {18.58007733, -0.215324777, -0.089782421, 0.01503161, 0.000332503, 0.000274216, - 2.70498E-05, 0.001387914, 0.000449199, 0.000829578, -5.28641E-06}, // Input Power Coefficients (inputPower_coeffs) - - {-0.629432348, 0.181466663, 0.00044047, 0.012104957, -6.61515E-05, 9.29975E-05, - 9.78042E-05, -0.000872708,-0.001013945, -0.00021852, 5.55444E-06}// COP Coefficients (COP_coeffs) - }); - } - else if (presetNum == MODELS_NyleC250A_SP || presetNum == MODELS_NyleC250A_C_SP) { - setTankSize_adjustUA(800., UNITS_GAL); - - compressor.perfMap.push_back({ - 90, // Temperature (T_F) - - {-13.89057656,0.025902417,0.304250541,0.061695153,-0.001474249,-0.001126845,-0.000220192, - 0.001241026,0.000571009,-0.000479282,9.04063E-06},// Input Power Coefficients (inputPower_coeffs) - - {7.443904067,0.185978755,-0.098481635,-0.002500073,0.000127658,0.000444321,0.000139547, - -0.001000195,-0.001140199,-8.77557E-05,4.87405E-06} // COP Coefficients (COP_coeffs) - }); - } - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - - // If Nyle multipass presets - else if (MODELS_NyleC60A_MP <= presetNum && presetNum <= MODELS_NyleC250A_C_MP) { - setNumNodes(24); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - - doTempDepression = false; - tankMixesOnDraw = false; - - tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important - tankUA_kJperHrC = 7; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); - compressor.extrapolationMethod = EXTRAP_NEAREST; - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.hysteresis_dC = 0; - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = static_cast(getNumNodes() / 3.) - 1; - - //logic conditions//logic conditions - if (MODELS_NyleC60A_MP <= presetNum && presetNum <= MODELS_NyleC250A_MP) {// If not cold weather package - compressor.minT = F_TO_C(40.); // Min air temperature sans Cold Weather Package - } - else { - compressor.minT = F_TO_C(35.);// Min air temperature WITH Cold Weather Package - } - compressor.maxT = F_TO_C(130.0); // Max air temperature - compressor.maxSetpoint_C = F_TO_C(160.); - - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(5.), this)); - - std::vector nodeWeights1; - nodeWeights1.emplace_back(4); - compressor.addShutOffLogic(std::make_shared("fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); - compressor.depressesTemperature = false; //no temp depression - - //Defrost Derate - compressor.setupDefrostMap(); - - // Performance grid - compressor.perfGrid.reserve(2); - compressor.perfGridValues.reserve(2); - - // Nyle MP models are all on the same grid axes - compressor.perfGrid.push_back({ 40., 60., 80., 90. }); // Grid Axis 1 Tair (F) - compressor.perfGrid.push_back({ 40., 60., 80., 100., 130., 150. }); // Grid Axis 2 Tin (F) - - if (presetNum == MODELS_NyleC60A_MP || presetNum == MODELS_NyleC60A_C_MP) { - setTankSize_adjustUA(360., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(13.); - if (presetNum == MODELS_NyleC60A_C_MP) { - compressor.resDefrost = { - 4.5, // inputPwr_kW; - 5.0, // constTempLift_dF; - 40.0 // onBelowT_F - }; - } - // Grid values in long format, table 1, input power (W) - compressor.perfGridValues.push_back({ 3.64, 4.11, 4.86, 5.97, 8.68, 9.95, 3.72, 4.27, 4.99, 6.03, 8.55, 10.02, 3.98, 4.53, - 5.24, 6.24, 8.54, 9.55, 4.45, 4.68, 5.37, 6.34, 8.59, 9.55 - }); - // Grid values in long format, table 2, COP - compressor.perfGridValues.push_back({ 3.362637363, 2.917274939, 2.407407407, 1.907872697, 1.296082949, 1.095477387, 4.438172043, - 3.772833724, 3.132264529, 2.505804312, 1.678362573, 1.386227545, 5.467336683, 4.708609272, 3.921755725, 3.169871795, 2.165105386, - 1.860732984, 5.512359551, 5.153846154, 4.290502793, 3.417981073, 2.272409779, 1.927748691 - }); - } - else if (presetNum == MODELS_NyleC90A_MP || presetNum == MODELS_NyleC90A_C_MP) { - setTankSize_adjustUA(480., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(20.); - if (presetNum == MODELS_NyleC90A_C_MP) { - compressor.resDefrost = { - 5.4, // inputPwr_kW; - 5.0, // constTempLift_dF; - 40.0 // onBelowT_F - }; - } - // Grid values in long format, table 1, input power (W) - compressor.perfGridValues.push_back({ 4.41, 6.04, 7.24, 9.14, 12.23, 14.73, 4.78, 6.61, 7.74, 9.40, 12.47, 14.75, - 5.51, 6.66, 8.44, 9.95, 13.06, 15.35, 6.78, 7.79, 8.81, 10.01, 11.91, 13.35 - }); - // Grid values in long format, table 2, COP - compressor.perfGridValues.push_back({ 4.79138322, 3.473509934, 2.801104972, 2.177242888, 1.569910057, 1.272233537, 6.071129707, - 4.264750378, 3.536175711, 2.827659574, 2.036086608, 1.666440678, 7.150635209, 5.659159159, 4.305687204, 3.493467337, - 2.487748851, 2.018241042, 6.750737463, 5.604621309, 4.734392736, 3.94005994, 3.04534005, 2.558801498 - }); - - } - else if (presetNum == MODELS_NyleC125A_MP || presetNum == MODELS_NyleC125A_C_MP) { - setTankSize_adjustUA(600., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(28.); - if (presetNum == MODELS_NyleC125A_C_MP) { - compressor.resDefrost = { - 9.0, // inputPwr_kW; - 5.0, // constTempLift_dF; - 40.0 // onBelowT_F - }; - } - // Grid values in long format, table 1, input power (W) - compressor.perfGridValues.push_back({ 6.4, 7.72, 9.65, 12.54, 20.54, 24.69, 6.89, 8.28, 10.13, 12.85, 19.75, 24.39, - 7.69, 9.07, 10.87, 13.44, 19.68, 22.35, 8.58, 9.5, 11.27, 13.69, 19.72, 22.4 - }); - // Grid values in long format, table 2, COP - compressor.perfGridValues.push_back({ 4.2390625, 3.465025907, 2.718134715, 2.060606061, 1.247809153, 1.016605913, - 5.374455733, 4.352657005, 3.453109576, 2.645136187, 1.66278481, 1.307093071, 6.503250975, 5.276736494, 4.229070837, - 3.27827381, 2.113821138, 1.770469799, 6.657342657, 5.749473684, 4.612244898, 3.542731921, 2.221095335, 1.816964286 - }); - } - else if (presetNum == MODELS_NyleC185A_MP || presetNum == MODELS_NyleC185A_C_MP) { - setTankSize_adjustUA(960., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(40.); - if (presetNum == MODELS_NyleC185A_C_MP) { - compressor.resDefrost = { - 7.2, // inputPwr_kW; - 5.0, // constTempLift_dF; - 40.0 // onBelowTemp_F - }; - } - // Grid values in long format, table 1, input power (W) - compressor.perfGridValues.push_back({ 7.57, 11.66, 14.05, 18.3, 25.04, 30.48, 6.99, 10.46, 14.28, 18.19, 26.24, 32.32, - 7.87, 12.04, 15.02, 18.81, 25.99, 31.26, 8.15, 12.46, 15.17, 18.95, 26.23, 31.62 - }); - // Grid values in long format, table 2, COP - compressor.perfGridValues.push_back({ 5.531043593, 3.556603774, 2.918149466, 2.214754098, 1.590255591, 1.291010499, - 8.010014306, 5.258126195, 3.778711485, 2.916437603, 1.964176829, 1.56404703, 9.65819568, 6.200166113, 4.792276964, - 3.705475811, 2.561369758, 2.05950096, 10.26993865, 6.350722311, 5.04218853, 3.841688654, 2.574151735, 2.025616698 - }); - } - else if (presetNum == MODELS_NyleC250A_MP || presetNum == MODELS_NyleC250A_C_MP) { - setTankSize_adjustUA(960., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(50.); - if (presetNum == MODELS_NyleC250A_C_MP) { - compressor.resDefrost = { - 18.0, // inputPwr_kW; - 5.0, // constTempLift_dF; - 40.0 // onBelowT_F - }; - } - // Grid values in long format, table 1, input power (W) - compressor.perfGridValues.push_back({ 10.89, 12.23, 13.55, 14.58, 15.74, 16.72, 11.46, 13.76, 15.97, 17.79, - 20.56, 22.50, 10.36, 14.66, 18.07, 21.23, 25.81, 29.01, 8.67, 15.05, 18.76, 21.87, 26.63, 30.02 - }); - - // Grid values in long format, table 2, COP - compressor.perfGridValues.push_back({ 5.81818181, 4.50040883, 3.69667896, 3.12414266, 2.38500635, 1.93540669, - 7.24520069, 5.50145348, 4.39323732, 3.67734682, 2.73249027, 2.23911111, 10.6196911, 7.05320600, 5.41228555, - 4.28638718, 3.04804339, 2.46053085, 14.7831603, 7.77903268, 5.71801705, 4.40237768, 2.92489673, 2.21419054 - }); - - } - - // Set up regular grid interpolator. - compressor.perfRGI = new Btwxt::RegularGridInterpolator(compressor.perfGrid, compressor.perfGridValues); - compressor.useBtwxtGrid = true; - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - // if rheem multipass - else if (MODELS_RHEEM_HPHD60HNU_201_MP <= presetNum && presetNum <= MODELS_RHEEM_HPHD135VNU_483_MP) { - setNumNodes(24); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - - doTempDepression = false; - tankMixesOnDraw = false; - - tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important - tankUA_kJperHrC = 7; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.perfMap.reserve(1); - compressor.hysteresis_dC = 0; - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = static_cast(getNumNodes() / 3.) - 1; - - //logic conditions - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(5.), this)); - - std::vector nodeWeights1; - nodeWeights1.emplace_back(4); - compressor.addShutOffLogic(std::make_shared("fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); - compressor.depressesTemperature = false; //no temp depression - - //Defrost Derate - compressor.setupDefrostMap(); - - //logic conditions - compressor.minT = F_TO_C(45.); - compressor.maxT = F_TO_C(110.); - compressor.maxSetpoint_C = MAXOUTLET_R134A; // data says 150... - - if (presetNum == MODELS_RHEEM_HPHD60HNU_201_MP || presetNum == MODELS_RHEEM_HPHD60VNU_201_MP) { - setTankSize_adjustUA(250., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(17.4); - compressor.perfMap.push_back({ - 110, // Temperature (T_F) - - { 1.8558438453, 0.0120796155, -0.0135443327, 0.0000059621, 0.0003010506, -0.0000463525}, // Input Power Coefficients (inputPower_coeffs) - - { 3.6840046360, 0.0995685071, -0.0398107723, -0.0001903160, 0.0000980361, -0.0003469814} // COP Coefficients (COP_coeffs) - }); - } - else if (presetNum == MODELS_RHEEM_HPHD135HNU_483_MP || presetNum == MODELS_RHEEM_HPHD135VNU_483_MP) { - setTankSize_adjustUA(500., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(34.87); - compressor.perfMap.push_back({ - 110, // Temperature (T_F) - - { 5.1838201136, 0.0247312962, -0.0120766440, 0.0000493862, 0.0005422089, -0.0001385078}, // Input Power Coefficients (inputPower_coeffs) - - { 5.0207181209, 0.0442525790, -0.0418284882, 0.0000793531, 0.0001132421, -0.0002491563} // COP Coefficients (COP_coeffs) - }); - } - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - - else if (presetNum == MODELS_MITSUBISHI_QAHV_N136TAU_HPB_SP) { - setNumNodes(96); - setpoint_C = 65; - - tankVolume_L = GAL_TO_L(500); - tankUA_kJperHrC = 12; - tankSizeFixed = false; - - doTempDepression = false; - tankMixesOnDraw = false; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.minT = F_TO_C(-13.); - compressor.setCondensity({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = getNumNodes() - 1; - - // What to do about these?! - compressor.hysteresis_dC = 4; - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.isMultipass = false; - compressor.maxSetpoint_C = F_TO_C(176.1); - - // Turn on - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("eighth node absolute", nodeWeights, F_TO_C(110.), this, true)); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(std::make_shared("bottom node", nodeWeights1, dF_TO_dC(15.), this, false, - std::greater(), true)); - compressor.depressesTemperature = false; - - // Performance grid: externalT_F, Tout_F, condenserTemp_F - compressor.perfGrid.reserve(2); - compressor.perfGridValues.reserve(2); - compressor.perfGrid.push_back({ -13, -11.2, -7.6, -4, -0.4, 3.2, 6.8, 10.4, 14, 17.6, 21.2, 24.8, 28.4, 32, 35.6, 39.2, - 42.8, 46.4, 50, 53.6, 57.2, 60.8, 64.4, 68, 71.6, 75.2, 78.8, 82.4, 86, 89.6, 93.2, 96.8, 100.4, 104}); // Grid Axis 1 Tair (F) - compressor.perfGrid.push_back({ 140., 158., 176. }); // Grid Axis 2 Tout (F) - compressor.perfGrid.push_back({ 41, 48.2, 62.6, 75.2, 84.2}); // Grid Axis 3 Tin (F) - - // Grid values in long format, table 1, input power (Btu/hr) - compressor.perfGridValues.push_back({ 56518.565328, 57130.739544, 57094.73612, 57166.756616, 57238.777112, 56518.565328, 57130.739544, 57094.73612, 57166.756616, 57238.777112, - 58061.348896, 58360.24692, 58591.39286, 58870.368216, 59093.547136, 56626.5960719999, 57202.763452, 57310.794196, 57310.794196, 57490.848848, 56626.5960719999, 57202.763452, - 57310.794196, 57310.794196, 57490.848848, 58280.539188, 58519.65556, 58786.67868, 59093.547136, 59372.5190799999, 56950.695128, 57850.95474, 57814.947904, 57814.947904, - 57922.978648, 56950.695128, 57850.95474, 57814.947904, 57814.947904, 57922.978648, 58679.067612, 58977.969048, 59372.5190799999, 59651.494436, 59930.46638, 57418.831764, - 58175.053796, 58283.08454, 58247.07088, 58427.12212, 57418.831764, 58175.053796, 58283.08454, 58247.07088, 58427.12212, 59137.384512, 59376.504296, 59902.566456, 60265.23476, - 60516.313604, 57778.930832, 58643.190432, 58823.23826, 58643.190432, 58967.282664, 57778.930832, 58643.190432, 58823.23826, 58643.190432, 58967.282664, 59515.99368, 59954.377676, - 60321.031196, 60934.77152, 61213.743464, 58103.029888, 59075.313408, 59219.357812, 59291.374896, 59579.45688, 58103.029888, 59075.313408, 59219.357812, 59291.374896, 59579.45688, - 60014.159328, 60273.205192, 60934.77152, 61436.922384, 61799.587276, 58535.152864, 59399.412464, 59795.525192, 59903.555936, 60299.672076, 58535.152864, 59399.412464, 59795.525192, - 59903.555936, 60299.672076, 60512.321564, 60751.448172, 61409.02246, 62022.769608, 62329.641476, 58931.275828, 59651.4842, 60011.58668, 60263.66524, 60551.747224, 58931.275828, - 59651.4842, 60011.58668, 60263.66524, 60551.747224, 60910.860224, 61369.1703, 62022.769608, 62580.713496, 62803.895828, 59219.357812, 60155.634496, 60443.71648, 60659.777968, - 61163.92144, 59219.357812, 60155.634496, 60443.71648, 60659.777968, 61163.92144, 61329.321552, 61687.997816, 62441.224112, 63166.56072, 63585.015224, 59651.4842, 60479.726728, - 61019.88386, 61163.92144, 61920.146884, 59651.4842, 60479.726728, 61019.88386, 61163.92144, 61920.146884, 61707.923896, 62186.166876, 63027.071336, 63724.504608, 64170.862448, - 59723.504696, 60839.83262, 61379.993164, 61524.030744, 62352.273272, 59723.504696, 60839.83262, 61379.993164, 61524.030744, 62352.273272, 61966.96976, 62445.21274, 63361.839716, - 64142.969348, 64505.63424, 59759.511532, 60695.788216, 61524.030744, 61884.136636, 62712.382576, 59759.511532, 60695.788216, 61524.030744, 61884.136636, 62712.382576, 62166.240796, - 62744.114176, 63668.714996, 64282.451908, 64589.323776, 59759.511532, 61019.88386, 61848.1298, 62064.191288, 62964.4509, 59759.511532, 61019.88386, 61848.1298, 62064.191288, - 62964.4509, 62425.28666, 63003.16004, 63975.58004, 64533.523928, 64617.2237, 59471.429548, 61055.894108, 61956.160544, 62460.304016, 63216.526048, 59471.429548, 61055.894108, - 61956.160544, 62460.304016, 63216.526048, 62624.550872, 63202.424252, 64310.351832, 64756.70626, 64728.806336, 61632.061488, 63036.474808, 63828.7105, 64296.847136, 65053.069168, - 61632.061488, 63036.474808, 63828.7105, 64296.847136, 65053.069168, 63561.10734, 64099.125148, 65258.8605359999, 65593.625504, 65621.525428, 51189.004268, 52917.506408, 54826.070024, - 57094.73612, 59327.388556, 51189.004268, 52917.506408, 54826.070024, 57094.73612, 59327.388556, 55391.172288, 58240.683616, 62106.459144, 65733.114888, 65705.214964, 41286.100768, - 42726.524336, 45931.460292, 49784.590948, 53457.6669519999, 41286.100768, 42726.524336, 45931.460292, 49784.590948, 53457.6669519999, 49433.093532, 51067.083272, 57921.8561, - 61604.3082799999, 64477.734316, 35632.444064, 37108.87788, 41394.134924, 45139.228012, 49568.526048, 35632.444064, 37108.87788, 41394.134924, 45139.228012, 49568.526048, 47540.06134, - 47221.233824, 56080.631716, 59539.904976, 61883.276812, 34192.023908, 35596.430404, 39809.6669519999, 43410.72246, 47659.969256, 34192.023908, 35596.430404, 39809.6669519999, - 43410.72246, 47659.969256, 47559.98742, 47061.821772, 54490.482764, 57893.959588, 60153.648712, 32895.641332, 34192.023908, 38225.202392, 41646.20666, 45751.409052, 32895.641332, - 34192.023908, 38225.202392, 41646.20666, 45751.409052, 47659.621232, 46842.624656, 52956.13366, 56220.1211, 58312.420916, 31563.25192, 32715.58668, 36532.707088, 39881.687448, - 43914.869344, 31563.25192, 32715.58668, 36532.707088, 39881.687448, 43914.869344, 47719.402884, 46742.994256, 51393.877808, 54518.382688, 56554.886068, 30194.85226, 31347.18702, - 35092.286932, 38117.171648, 42006.312552, 30194.85226, 31347.18702, 35092.286932, 38117.171648, 42006.312552, 47799.1072039999, 46324.532928, 49692.139396, 52732.951328, 54769.45812, - 30446.923996, 31383.197268, 34300.054652, 37144.884716, 40781.953884, 30446.923996, 31383.197268, 34300.054652, 37144.884716, 40781.953884, 45547.391924, 44212.307032, 47320.867636, - 50389.57608, 52202.90054, 30698.995732, 31311.176772, 33723.88386, 36316.6456, 39701.636208, 30698.995732, 31311.176772, 33723.88386, 36316.6456, 39701.636208, 43295.680056, - 41980.517832, 45089.078436, 47795.121988, 49720.03932, 30987.081128, 31311.176772, 33183.726728, 35344.358668, 38477.27754, 30987.081128, 31311.176772, 33183.726728, 35344.358668, - 38477.27754, 41562.059916, 40486.017476, 43415.236536, 46037.590552, 47795.121988, 31203.146028, 31311.176772, 32571.545688, 34552.126388, 37360.949616, 31203.146028, 31311.176772, - 32571.545688, 34552.126388, 37360.949616, 40486.017476, 39310.3446359999, 42159.859376, 44782.20998, 46511.844904, 31167.132368, 31311.176772, 32355.4842, 34156.010248, 36712.758328, - 31167.132368, 31311.176772, 32355.4842, 34156.010248, 36712.758328, 39449.830608, 38353.862088, 41127.657724, 43666.31538, 45284.360844, 31059.101624, 31131.12212, 32319.473952, - 34156.010248, 36748.771988, 31059.101624, 31131.12212, 32319.473952, 34156.010248, 36748.771988, 38592.985284, 37457.161192, 40151.249096, 42801.496212, 44419.541676, 30879.050384, - 31023.091376, 32319.473952, 34192.023908, 36820.789072, 30879.050384, 31023.091376, 32319.473952, 34192.023908, 36820.789072, 37756.062628, 36680.0236, 39258.536828, 41797.194484, - 43387.343436, 30807.029888, 30951.07088, 32427.504696, 34156.010248, 36748.771988, 30807.029888, 30951.07088, 32427.504696, 34156.010248, 36748.771988, 37795.9182, 36779.657412, - 39258.536828, 41769.29456, 43331.547, 30626.976942, 30735.007686, 32319.473952, 34156.010248, 36748.771988, 30626.976942, 30735.007686, 32319.473952, 34156.010248, 36748.771988, - 37815.84428, 36779.657412, 39286.431634, 41825.090996, 43359.446924, 30446.923996, 30518.944492, 32355.4842, 34264.040992, 36820.789072, 30446.923996, 30518.944492, 32355.4842, - 34264.040992, 36820.789072, 37775.988708, 36759.731332, 39286.431634, 41825.090996, 43359.446924, 30230.859096, 30410.913748, 32319.473952, 34192.023908, 36748.771988, 30230.859096, - 30410.913748, 32319.473952, 34192.023908, 36748.771988, 37775.988708, 36759.731332, 39342.226364, 41825.090996, 43303.650488, 30194.85226, 30194.85226, 32391.491036, 34156.010248, - 36748.771988, 30194.85226, 30194.85226, 32391.491036, 34156.010248, 36748.771988, 37756.062628, 36779.657412, 39342.226364, 41825.090996, 43359.446924 - }); - - // Grid values in long format, table 2, COP - compressor.perfGridValues.push_back({ 1.177126, 1.1393, 1.091664, 1.033858, 0.981755, 1.177126, 1.1393, 1.091664, 1.033858, 0.981755, 1.134534, 1.106615, 1.04928, 0.989101, 0.946182, 1.228935, - 1.190326, 1.136507, 1.075244, 1.023802, 1.228935, 1.190326, 1.136507, 1.075244, 1.023802, 1.174944, 1.147101, 1.087318, 1.026909, 0.980265, 1.324165, 1.27451, 1.218468, 1.156182, 1.103823, - 1.324165, 1.27451, 1.218468, 1.156182, 1.103823, 1.267595, 1.236929, 1.165864, 1.102888, 1.052601, 1.415804, 1.365212, 1.301359, 1.238022, 1.180586, 1.415804, 1.365212, 1.301359, 1.238022, - 1.180586, 1.358408, 1.324943, 1.249272, 1.179609, 1.128155, 1.510855, 1.453792, 1.381237, 1.31747, 1.253435, 1.510855, 1.453792, 1.381237, 1.31747, 1.253435, 1.446639, 1.404653, 1.331368, - 1.249056, 1.195511, 1.60469, 1.540079, 1.462451, 1.39417, 1.326987, 1.60469, 1.540079, 1.462451, 1.39417, 1.326987, 1.529924, 1.492107, 1.404944, 1.325122, 1.265433, 1.690249, 1.630494, - 1.539446, 1.464082, 1.395939, 1.690249, 1.630494, 1.539446, 1.464082, 1.395939, 1.610302, 1.56761, 1.479273, 1.393118, 1.33076, 1.776046, 1.715967, 1.624662, 1.540783, 1.469819, 1.776046, - 1.715967, 1.624662, 1.540783, 1.469819, 1.693465, 1.646725, 1.550545, 1.459601, 1.400666, 1.865309, 1.797965, 1.703902, 1.612645, 1.539888, 1.865309, 1.797965, 1.703902, 1.612645, 1.539888, - 1.776866, 1.728095, 1.626829, 1.531743, 1.461555, 1.956837, 1.881215, 1.777073, 1.690021, 1.603082, 1.956837, 1.881215, 1.777073, 1.690021, 1.603082, 1.857512, 1.807899, 1.699347, 1.599321, - 1.526899, 2.038891, 1.956496, 1.848049, 1.757975, 1.668207, 2.038891, 1.956496, 1.848049, 1.757975, 1.668207, 1.934721, 1.878022, 1.764777, 1.657606, 1.583414, 2.110576, 2.028182, 1.922739, - 1.818155, 1.725237, 2.110576, 2.028182, 1.922739, 1.818155, 1.725237, 1.997516, 1.937435, 1.824625, 1.712596, 1.630169, 2.185899, 2.091178, 1.989083, 1.883087, 1.786388, 2.185899, 2.091178, - 1.989083, 1.883087, 1.786388, 2.06464, 2.003084, 1.88041, 1.763428, 1.68041, 2.29337, 2.155411, 2.063354, 1.942635, 1.844204, 2.29337, 2.155411, 2.063354, 1.942635, 1.844204, 2.125448, 2.061323, - 1.933955, 1.810339, 1.720612, 2.213555, 2.111682, 2.027503, 1.91515, 1.819817, 2.213555, 2.111682, 2.027503, 1.91515, 1.819817, 2.126499, 2.064584, 1.935343, 1.818713, 1.728239, 2.665142, - 2.578088, 2.488506, 2.388206, 2.29651, 2.665142, 2.578088, 2.488506, 2.388206, 2.29651, 2.46407, 2.344111, 2.198428, 2.057188, 1.96338, 3.304405, 3.191319, 2.971384, 2.741049, 2.550691, 3.304405, - 3.191319, 2.971384, 2.741049, 2.550691, 2.763177, 2.673398, 2.356773, 2.216348, 2.116712, 3.827691, 3.676371, 3.297085, 3.022338, 2.752997, 3.827691, 3.676371, 3.297085, 3.022338, 2.752997, - 2.871739, 2.890389, 2.434648, 2.292726, 2.205906, 3.989995, 3.834598, 3.428313, 3.14185, 2.862486, 3.989995, 3.834598, 3.428313, 3.14185, 2.862486, 2.871269, 2.900921, 2.506208, 2.358391, - 2.270261, 4.147236, 3.992101, 3.570419, 3.274967, 2.982684, 4.147236, 3.992101, 3.570419, 3.274967, 2.982684, 2.865266, 2.913751, 2.578296, 2.428607, 2.341945, 4.322305, 4.172262, 3.73583, - 3.419865, 3.107421, 4.322305, 4.172262, 3.73583, 3.419865, 3.107421, 2.861677, 2.919962, 2.65667, 2.504413, 2.414725, 4.518187, 4.354394, 3.889174, 3.578177, 3.248607, 4.518187, 4.354394, 3.889174, - 3.578177, 3.248607, 2.856905, 2.946338, 2.747649, 2.589208, 2.493442, 4.481963, 4.349398, 3.979002, 3.671837, 3.346137, 4.481963, 4.349398, 3.979002, 3.671837, 3.346137, 2.998141, 3.087098, 2.885335, - 2.709619, 2.616032, 4.445162, 4.359402, 4.046983, 3.755577, 3.437188, 4.445162, 4.359402, 4.046983, 3.755577, 3.437188, 3.154067, 3.251216, 3.028152, 2.856705, 2.746669, 4.402673, 4.359402, 4.112859, - 3.858889, 3.546561, 4.402673, 4.359402, 4.112859, 3.858889, 3.546561, 3.285629, 3.371232, 3.1449, 2.965763, 2.857289, 4.373341, 4.359402, 4.19016, 3.947368, 3.65253, 4.373341, 4.359402, 4.19016, - 3.947368, 3.65253, 3.372955, 3.472057, 3.238544, 3.048902, 2.936122, 4.378394, 4.359402, 4.218141, 3.993147, 3.717018, 4.378394, 4.359402, 4.218141, 3.993147, 3.717018, 3.461548, 3.558644, 3.319824, - 3.126817, 3.015709, 4.391304, 4.384616, 4.222841, 3.993147, 3.713376, 4.391304, 4.384616, 4.222841, 3.993147, 3.713376, 3.538402, 3.643836, 3.400556, 3.189995, 3.074423, 4.419242, 4.399884, 4.222841, - 3.988941, 3.706113, 4.419242, 4.399884, 4.222841, 3.988941, 3.706113, 3.616836, 3.721038, 3.477882, 3.266644, 3.147565, 4.429573, 4.410122, 4.208773, 3.993147, 3.713376, 4.429573, 4.410122, 4.208773, - 3.993147, 3.713376, 3.613022, 3.710957, 3.477882, 3.268826, 3.151618, 4.45679, 4.441125, 4.222841, 3.993147, 3.713376, 4.45679, 4.441125, 4.222841, 3.993147, 3.713376, 3.611119, 3.710957, 3.475413, - 3.264466, 3.14959, 4.483146, 4.472566, 4.218141, 3.980557, 3.706113, 4.483146, 4.472566, 4.218141, 3.980557, 3.706113, 3.614928, 3.712969, 3.475413, 3.264466, 3.14959, 4.515188, 4.488454, 4.222841, - 3.988941, 3.713376, 4.515188, 4.488454, 4.222841, 3.988941, 3.713376, 3.614928, 3.712969, 3.470484, 3.264466, 3.153648, 4.522957, 4.520572, 4.213452, 3.993147, 3.713376, 4.522957, 4.520572, 4.213452, - 3.993147, 3.713376, 3.616836, 3.710957, 3.470484, 3.264466, 3.14959 - }); - - // Set up regular grid interpolator. - Btwxt::GridAxis g0(compressor.perfGrid[0], "TAir", Btwxt::InterpolationMethod::linear, Btwxt::ExtrapolationMethod::constant); - Btwxt::GridAxis g1(compressor.perfGrid[1], "TOut", Btwxt::InterpolationMethod::linear, Btwxt::ExtrapolationMethod::constant); - Btwxt::GridAxis g2(compressor.perfGrid[2], "TIn", Btwxt::InterpolationMethod::linear, Btwxt::ExtrapolationMethod::linear); - - std::vector gx{ g0, g1, g2 }; - - compressor.perfRGI = new Btwxt::RegularGridInterpolator(gx, compressor.perfGridValues); - compressor.useBtwxtGrid = true; - - compressor.secondaryHeatExchanger = { dF_TO_dC(10.), dF_TO_dC(15.), 27. }; - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - - else if (presetNum == MODELS_SANCO2_83 || presetNum == MODELS_SANCO2_GS3_45HPA_US_SP || presetNum == MODELS_SANCO2_119) { - setNumNodes(96); - setpoint_C = 65; - setpointFixed = true; - - if (presetNum == MODELS_SANCO2_119) { - tankVolume_L = GAL_TO_L(119); - tankUA_kJperHrC = 9; - } - else { - tankVolume_L = 315; - tankUA_kJperHrC = 7; - if (presetNum == MODELS_SANCO2_GS3_45HPA_US_SP) { - tankSizeFixed = false; - } - } - - doTempDepression = false; - tankMixesOnDraw = false; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.minT = F_TO_C(-25.); - compressor.setCondensity({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = getNumNodes() - 1; - - 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; - compressor.isMultipass = false; - compressor.maxSetpoint_C = MAXOUTLET_R744; - - std::vector nodeWeights; - nodeWeights.emplace_back(8); - compressor.addTurnOnLogic(std::make_shared("eighth node absolute", nodeWeights, F_TO_C(113), this, true)); - if (presetNum == MODELS_SANCO2_83 || presetNum == MODELS_SANCO2_119) { - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); - // Adds a bonus standby logic so the external heater does not cycle, recommended for any external heater with standby - std::vector nodeWeightStandby; - nodeWeightStandby.emplace_back(0); - compressor.standbyLogic = std::make_shared("bottom node absolute", nodeWeightStandby, F_TO_C(113), this, true, std::greater()); - } - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(std::make_shared("bottom node absolute", nodeWeights1, F_TO_C(135), this, true, - std::greater(), true)); - compressor.depressesTemperature = false; //no temp depression - - //set everything in its place - heatSources.resize(1); - heatSources[0] = compressor; - } - else if (presetNum == MODELS_SANCO2_43) { - setNumNodes(96); - setpoint_C = 65; - setpointFixed = true; - - tankVolume_L = 160; - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 5; - - doTempDepression = false; - tankMixesOnDraw = false; - - HeatSource compressor(this); - - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.minT = F_TO_C(-25.); - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = getIndexTopNode(); - - 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; - compressor.isMultipass = false; - compressor.maxSetpoint_C = MAXOUTLET_R744; - - std::vector nodeWeights; - nodeWeights.emplace_back(4); - std::vector nodeWeightStandby; - nodeWeightStandby.emplace_back(0); - compressor.addTurnOnLogic(std::make_shared("fourth node absolute", nodeWeights, F_TO_C(113), this, true)); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); - compressor.standbyLogic = std::make_shared("bottom node absolute", nodeWeightStandby, F_TO_C(113), this, true, std::greater()); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(std::make_shared("bottom twelfth absolute", nodeWeights1, F_TO_C(135), this, true, - std::greater(), true)); - compressor.depressesTemperature = false; //no temp depression - - //set everything in its place - heatSources.resize(1); - heatSources[0] = compressor; - } - else if (presetNum == MODELS_AOSmithHPTU50 || presetNum == MODELS_RheemHBDR2250 || presetNum == MODELS_RheemHBDR4550) { - setNumNodes(24); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = 171; - tankUA_kJperHrC = 6; - - doTempDepression = false; - tankMixesOnDraw = true; - - 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(100))); - - std::vector nodeWeights; - nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); - resistiveElementTop.addTurnOnLogic(std::make_shared("top sixth absolute", nodeWeights, F_TO_C(105), this, true)); - // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - - - } - else if (presetNum == MODELS_AOSmithHPTU66 || presetNum == MODELS_RheemHBDR2265 || presetNum == MODELS_RheemHBDR4565) { - setNumNodes(24); - setpoint_C = F_TO_C(127.0); - - if (presetNum == MODELS_AOSmithHPTU66) { - tankVolume_L = 244.6; - } - else { - tankVolume_L = 221.4; - } - tankUA_kJperHrC = 8; - - doTempDepression = false; - tankMixesOnDraw = true; - - 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({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(100))); - - std::vector nodeWeights; - nodeWeights.emplace_back(11); nodeWeights.emplace_back(12); - resistiveElementTop.addTurnOnLogic(std::make_shared("top sixth absolute", nodeWeights, F_TO_C(105), this, true)); - // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_AOSmithHPTU80 || presetNum == MODELS_RheemHBDR2280 || presetNum == MODELS_RheemHBDR4580) { - setNumNodes(24); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = 299.5; - tankUA_kJperHrC = 9; - - doTempDepression = false; - tankMixesOnDraw = true; - - 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //double split = 1.0 / 3.0; - compressor.setCondensity({1., 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::bottomTwelfthMaxTemp(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(std::make_shared("top sixth absolute", nodeWeights, F_TO_C(105), this, true)); - // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_AOSmithHPTU80_DR) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = 283.9; - tankUA_kJperHrC = 9; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - compressor.setCondensity({1., 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::bottomTwelfthMaxTemp(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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_AOSmithCAHP120) { - setNumNodes(24); - setpoint_C = F_TO_C(150.0); - - tankVolume_L = GAL_TO_L(111.76); // AOSmith docs say 111.76 - tankUA_kJperHrC = UAf_TO_UAc(3.94); - - doTempDepression = false; - tankMixesOnDraw = false; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0.3, 0.3, 0.2, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}); - - //From CAHP 120 COP Tests - compressor.perfMap.reserve(3); - - // Tuned on the multiple K167 tests - compressor.perfMap.push_back({ - 50., // Temperature (T_F) - {2010.49966, -4.20966, 0.085395}, // Input Power Coefficients (inputPower_coeffs) - {5.91, -0.026299, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67.5, // Temperature (T_F) - {2171.012, -6.936571, 0.1094962}, // Input Power Coefficients (inputPower_coeffs) - {7.26272, -0.034135, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95., // Temperature (T_F) - {2276.0625, -7.106608, 0.119911}, // Input Power Coefficients (inputPower_coeffs) - {8.821262, -0.042059, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(47.0); //Product documentation says 45F doesn't look like it in CMP-T test// - compressor.maxT = F_TO_C(110.0); - compressor.hysteresis_dC = dF_TO_dC(2); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //top resistor values - double wattRE = 6000;//5650.; - resistiveElementTop.setupAsResistiveElement(7, wattRE); - resistiveElementTop.isVIP = true; // VIP is the only source that turns on independently when something else is already heating. - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, wattRE); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - resistiveElementBottom.setCondensity({0.2, 0.8, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}); // Based of CMP test - - //logic conditions for - double compStart = dF_TO_dC(5.25); - double standbyT = dF_TO_dC(5.); //Given CMP_T test - compressor.addTurnOnLogic(HPWH::secondSixth(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - double resistanceStart = 12.; - resistiveElementTop.addTurnOnLogic(HPWH::topThird(resistanceStart)); - resistiveElementBottom.addTurnOnLogic(HPWH::topThird(resistanceStart)); - - resistiveElementTop.addShutOffLogic(HPWH::fifthSixthMaxTemp(F_TO_C(117.))); - resistiveElementBottom.addShutOffLogic(HPWH::secondSixthMaxTemp(F_TO_C(109.))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - //heatSources[2].followedByHeatSource = &heatSources[1];; - - heatSources[0].companionHeatSource = &heatSources[1]; - heatSources[1].companionHeatSource = &heatSources[2]; - - } - else if (MODELS_AOSmithHPTS50 <= presetNum && presetNum <= MODELS_AOSmithHPTS80) - { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - if (presetNum == MODELS_AOSmithHPTS50) { - tankVolume_L = GAL_TO_L(45.6); - tankUA_kJperHrC = 6.403; - } - else if (presetNum == MODELS_AOSmithHPTS66) { - tankVolume_L = GAL_TO_L(67.63); - tankUA_kJperHrC = UAf_TO_UAc(1.5) * 6.403 / UAf_TO_UAc(1.16); - } - else if (presetNum == MODELS_AOSmithHPTS80) { - tankVolume_L = GAL_TO_L(81.94); - tankUA_kJperHrC = UAf_TO_UAc(1.73) * 6.403 / UAf_TO_UAc(1.16); - } - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementTop(this); - HeatSource resistiveElementBottom(this); - - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0, 0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0}); - - // performance map - compressor.perfMap.reserve(3); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {66.82, 2.49, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {8.64, -0.0436, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67.5, // Temperature (T_F) - {85.1, 2.38, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {10.82, -0.0551, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {89, 2.62, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {12.52, -0.0534, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.); - compressor.maxT = F_TO_C(120.); - compressor.hysteresis_dC = dF_TO_dC(1.); - compressor.configuration = HeatSource::CONFIG_WRAPPED; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - resistiveElementTop.setupAsResistiveElement(8, 4500); - resistiveElementTop.isVIP = true; - - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); - - //logic conditions - double compStart = dF_TO_dC(30.2); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(11.87))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[2]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - } - - else if (presetNum == MODELS_GE2014STDMode) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; + // standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; + resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); + resistiveElementTop.isVIP = true; - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_GE2014STDMode_80) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; + // assign heat sources into array in order of priority + heatSources.resize(2); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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::bottomTwelfthMaxTemp(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))); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_GE2014) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(80))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_GE2014_80) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(80))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_GE2014_80DR) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(80))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - // PRESET USING GE2014 DATA - else if (presetNum == MODELS_BWC2020_65) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(64); - tankUA_kJperHrC = 7.6; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(80))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - } - // If Rheem Premium - else if (MODELS_Rheem2020Prem40 <= presetNum && presetNum <= MODELS_Rheem2020Prem80) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - if (presetNum == MODELS_Rheem2020Prem40) { - tankVolume_L = GAL_TO_L(36.1); - tankUA_kJperHrC = 9.5; - } - else if (presetNum == MODELS_Rheem2020Prem50) { - tankVolume_L = GAL_TO_L(45.1); - tankUA_kJperHrC = 8.55; - } - else if (presetNum == MODELS_Rheem2020Prem65) { - tankVolume_L = GAL_TO_L(58.5); - tankUA_kJperHrC = 10.64; - } - else if (presetNum == MODELS_Rheem2020Prem80) { - tankVolume_L = GAL_TO_L(72.0); - tankUA_kJperHrC = 10.83; - } - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0}); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {250, -1.0883, 0.0176}, // Input Power Coefficients (inputPower_coeffs) - {6.7, -0.0087, -0.0002} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {275.0, -0.6631, 0.01571}, // Input Power Coefficients (inputPower_coeffs) - {7.0, -0.0168, -0.0001} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.0); - compressor.hysteresis_dC = dF_TO_dC(1); - compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //top resistor values - resistiveElementTop.setupAsResistiveElement(8, 4500); - resistiveElementTop.isVIP = true; - - //bottom resistor values - resistiveElementBottom.setupAsResistiveElement(0, 4500); - resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); - - //logic conditions - double compStart = dF_TO_dC(32); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(100))); - resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[1].backupHeatSource = &heatSources[2]; - heatSources[2].backupHeatSource = &heatSources[1]; - - heatSources[0].followedByHeatSource = &heatSources[2]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - } - - // If Rheem Build - else if (MODELS_Rheem2020Build40 <= presetNum && presetNum <= MODELS_Rheem2020Build80) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - if (presetNum == MODELS_Rheem2020Build40) { - tankVolume_L = GAL_TO_L(36.1); - tankUA_kJperHrC = 9.5; - } - else if (presetNum == MODELS_Rheem2020Build50) { - tankVolume_L = GAL_TO_L(45.1); - tankUA_kJperHrC = 8.55; - } - else if (presetNum == MODELS_Rheem2020Build65) { - tankVolume_L = GAL_TO_L(58.5); - tankUA_kJperHrC = 10.64; - } - else if (presetNum == MODELS_Rheem2020Build80) { - tankVolume_L = GAL_TO_L(72.0); - tankUA_kJperHrC = 10.83; - } - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0}); - - compressor.perfMap.reserve(2); - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {220.0, 0.8743, 0.00454}, // Input Power Coefficients (inputPower_coeffs) - { 7.96064, -0.0448, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {275.0, -0.6631, 0.01571}, // Input Power Coefficients (inputPower_coeffs) - {8.45936, -0.04539, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.hysteresis_dC = dF_TO_dC(1); - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.0); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - compressor.configuration = HPWH::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(4); - - //logic conditions - double compStart = dF_TO_dC(30); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(100))); - resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[1].backupHeatSource = &heatSources[2]; - heatSources[2].backupHeatSource = &heatSources[1]; - - heatSources[0].followedByHeatSource = &heatSources[2]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - } - else if (MODELS_RheemPlugInShared40 <= presetNum && presetNum <= MODELS_RheemPlugInShared80) { - setNumNodes(12); - - if (presetNum == MODELS_RheemPlugInShared40) { - tankVolume_L = GAL_TO_L(36.0); - tankUA_kJperHrC = 9.5; - setpoint_C = F_TO_C(140.0); - } - else if (presetNum == MODELS_RheemPlugInShared50) { - tankVolume_L = GAL_TO_L(45.0); - tankUA_kJperHrC = 8.55; - setpoint_C = F_TO_C(140.0); - } - else if (presetNum == MODELS_RheemPlugInShared65) { - tankVolume_L = GAL_TO_L(58.5); - tankUA_kJperHrC = 10.64; - setpoint_C = F_TO_C(127.0); - } - else if (presetNum == MODELS_RheemPlugInShared80) { - tankVolume_L = GAL_TO_L(72.0); - tankUA_kJperHrC = 10.83; - setpoint_C = F_TO_C(127.0); - } - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0}); - - compressor.perfMap.reserve(2); - - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {250, -1.0883, 0.0176}, // Input Power Coefficients (inputPower_coeffs) - {6.7, -0.0087, -0.0002} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {275.0, -0.6631, 0.01571}, // Input Power Coefficients (inputPower_coeffs) - {7.0, -0.0168, -0.0001} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.0); - compressor.hysteresis_dC = dF_TO_dC(1); - compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //logic conditions - double compStart = dF_TO_dC(32); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - else if (presetNum == MODELS_RheemPlugInDedicated40 || presetNum == MODELS_RheemPlugInDedicated50) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - if (presetNum == MODELS_RheemPlugInDedicated40) { - tankVolume_L = GAL_TO_L(36); - tankUA_kJperHrC = 5.5; - } - else if (presetNum == MODELS_RheemPlugInDedicated50) { - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.33; - } - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0.5, 0.5, 0.}); - - compressor.perfMap.reserve(2); - compressor.perfMap.push_back({ - 50, // Temperature (T_F) - {528.91, 4.8988, 0.0}, // Input Power Coefficients (inputPower_coeffs) - { 4.3943, -0.012443, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {494.03, 7.7266, 0.0}, // Input Power Coefficients (inputPower_coeffs) - {5.48189, -0.01604, 0.0} // COP Coefficients (COP_coeffs) - }); - - compressor.hysteresis_dC = dF_TO_dC(1); - compressor.minT = F_TO_C(37.0); - compressor.maxT = F_TO_C(120.0); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; - - //logic conditions - double compStart = dF_TO_dC(20); - double standbyT = dF_TO_dC(9); - compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); - compressor.addTurnOnLogic(HPWH::standby(standbyT)); - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - else if (presetNum == MODELS_RheemHB50) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 7; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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.maxSetpoint_C = MAXOUTLET_R134A; - - 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::bottomTwelfthMaxTemp(F_TO_C(76.7747))); - - resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_Stiebel220E) { - setNumNodes(12); - setpoint_C = F_TO_C(127); - - tankVolume_L = GAL_TO_L(56); - //tankUA_kJperHrC = 10; //0 to turn off - tankUA_kJperHrC = 9; - - doTempDepression = false; - tankMixesOnDraw = false; - - 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); + heatSources[0].followedByHeatSource = &heatSources[1]; + } - 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) - }); + // realistic resistive tank + else if (presetNum == MODELS_restankRealistic) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); - 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) - }); + tankSizeFixed = false; + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 10; // 0 to turn off - 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.maxSetpoint_C = MAXOUTLET_R134A; + doTempDepression = false; + // should eventually put tankmixes to true when testing progresses + tankMixesOnDraw = false; - compressor.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(6.5509))); - compressor.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(100))); - - compressor.depressesTemperature = false; //no temp depression + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); - //set everything in its places - heatSources.resize(2); - heatSources[0] = compressor; - heatSources[1] = resistiveElement; + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementTop.setupAsResistiveElement(9, 4500); - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[0].backupHeatSource = &heatSources[1]; + // standard logic conditions + resistiveElementBottom.addTurnOnLogic(HPWH::bottomThird(20)); + resistiveElementBottom.addTurnOnLogic(HPWH::standby(15)); - } - else if (presetNum == MODELS_Generic1) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 9; - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(110))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_Generic2) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 7.5; - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - HeatSource resistiveElementTop(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(F_TO_C(100))); - - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(40))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; + resistiveElementTop.addTurnOnLogic(HPWH::topThird(20)); + resistiveElementTop.isVIP = true; - } - else if (presetNum == MODELS_Generic3) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(50); - tankUA_kJperHrC = 5; - - doTempDepression = false; - tankMixesOnDraw = true; - - //set everything in its places - HeatSource resistiveElementTop(this); - HeatSource compressor(this); - HeatSource resistiveElementBottom(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - compressor.setCondensity({1., 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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (presetNum == MODELS_UEF2generic) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource resistiveElementTop(this); - HeatSource resistiveElementBottom(this); - HeatSource compressor(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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::bottomTwelfthMaxTemp(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 - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - } - else if (MODELS_AWHSTier3Generic40 <= presetNum && presetNum <= MODELS_AWHSTier3Generic80) { - setNumNodes(12); - setpoint_C = F_TO_C(127.0); - - if (presetNum == MODELS_AWHSTier3Generic40) { - tankVolume_L = GAL_TO_L(36.1); - tankUA_kJperHrC = 5; - } - else if (presetNum == MODELS_AWHSTier3Generic50) { - tankVolume_L = GAL_TO_L(45); - tankUA_kJperHrC = 6.5; - } - else if (presetNum == MODELS_AWHSTier3Generic65) { - tankVolume_L = GAL_TO_L(64); - tankUA_kJperHrC = 7.6; - } - else if (presetNum == MODELS_AWHSTier3Generic80) { - tankVolume_L = GAL_TO_L(75.4); - tankUA_kJperHrC = 10.; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("Incorrect model specification. \n"); - } - return HPWH_ABORT; - } - - doTempDepression = false; - tankMixesOnDraw = true; - - HeatSource resistiveElementTop(this); - HeatSource resistiveElementBottom(this); - HeatSource compressor(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1., 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) - {5.22288834, -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) - {6.84694165, -0.0335265, 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; - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - //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))); - resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); - resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(80))); - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - } - // If a the model is the TamOMatic, HotTam, Generic... This model is scalable. - else if (presetNum == MODELS_TamScalable_SP) { - setNumNodes(24); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - canScale = true; // a fully scallable model - - doTempDepression = false; - tankMixesOnDraw = false; - - tankVolume_L = 315; - tankUA_kJperHrC = 7; - setTankSize_adjustUA(600., UNITS_GAL); - - HeatSource resistiveElementTop(this); - HeatSource resistiveElementBottom(this); - 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.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.isMultipass = false; - compressor.perfMap.reserve(1); - compressor.hysteresis_dC = 0; - - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = getIndexTopNode(); - - //Defrost Derate - compressor.setupDefrostMap(); - - //Perfmap for input power and COP made from data for poor preforming modeled to be scalled for this model - std::vector inputPwr_coeffs = { 13.6, 0.00995, -0.0342, -0.014, -0.000110, 0.00026, 0.000232, 0.000195, -0.00034, 5.30E-06, 2.3600E-06}; - std::vector COP_coeffs = { 1.945, 0.0412, -0.0112, -0.00161, 0.0000492, 0.0000348, -0.0000323, -0.000166, 0.0000112, 0.0000392, -3.52E-07}; - - compressor.perfMap.push_back({ - 105, // Temperature (T_F) - inputPwr_coeffs, // Input Power Coefficients (inputPower_coeffs - COP_coeffs // COP Coefficients (COP_coeffs) - }); - - //logic conditions - compressor.minT = F_TO_C(40.); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(15), this)); - - //lowT cutoff - std::vector nodeWeights1; - nodeWeights1.emplace_back(1); - compressor.addShutOffLogic(std::make_shared("bottom node", nodeWeights1, dF_TO_dC(15.), this, false, - std::greater(), true)); - compressor.depressesTemperature = false; //no temp depression - - resistiveElementBottom.setupAsResistiveElement(0, 30000); - resistiveElementTop.setupAsResistiveElement(9, 30000); - - //top resistor values - //standard logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(15))); - resistiveElementTop.isVIP = true; - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - } - else if (presetNum == MODELS_Scalable_MP) { - setNumNodes(24); - setpoint_C = F_TO_C(135.0); - tankSizeFixed = false; - canScale = true; // a fully scallable model - - doTempDepression = false; - tankMixesOnDraw = false; - - tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important - tankUA_kJperHrC = 7; - - HeatSource resistiveElementTop(this); - HeatSource resistiveElementBottom(this); - HeatSource compressor(this); - compressor.isOn = false; - compressor.isVIP = true; - compressor.typeOfHeatSource = TYPE_compressor; - compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); - compressor.configuration = HeatSource::CONFIG_EXTERNAL; - compressor.perfMap.reserve(1); - compressor.hysteresis_dC = 0; - compressor.externalOutletHeight = 0; - compressor.externalInletHeight = static_cast(getNumNodes() / 3.) - 1; - - //logic conditions - std::vector nodeWeights; - nodeWeights.emplace_back(4); - compressor.addTurnOnLogic(std::make_shared("fourth node", nodeWeights, dF_TO_dC(5.), this, false)); - - std::vector nodeWeights1; - nodeWeights1.emplace_back(4); - compressor.addShutOffLogic(std::make_shared("fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); - compressor.depressesTemperature = false; //no temp depression - - //Defrost Derate - compressor.setupDefrostMap(); - - //logic conditions - compressor.minT = F_TO_C(40.); - compressor.maxT = F_TO_C(105.); - compressor.maxSetpoint_C = MAXOUTLET_R134A; - - setTankSize_adjustUA(600., UNITS_GAL); - compressor.mpFlowRate_LPS = GPM_TO_LPS(25.); - compressor.perfMap.push_back({ - 100, // Temperature (T_F) - - { 12.4, 0.00739, -0.0410, 0.0, 0.000578, 0.0000696}, // Input Power Coefficients (inputPower_coeffs) - - { 1.20, 0.0333, 0.00191, 0.000283, 0.0000496, -0.000440} // COP Coefficients (COP_coeffs) - - }); - - resistiveElementBottom.setupAsResistiveElement(0, 30000); - resistiveElementTop.setupAsResistiveElement(9, 30000); - - //top resistor values - //standard logic conditions - resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(30))); - resistiveElementTop.isVIP = true; - - //set everything in its places - heatSources.resize(3); - heatSources[0] = resistiveElementTop; - heatSources[1] = resistiveElementBottom; - heatSources[2] = compressor; - - //and you have to do this after putting them into heatSources, otherwise - //you don't get the right pointers - heatSources[2].backupHeatSource = &heatSources[1]; - heatSources[1].backupHeatSource = &heatSources[2]; - - heatSources[0].followedByHeatSource = &heatSources[1]; - heatSources[1].followedByHeatSource = &heatSources[2]; - - heatSources[0].companionHeatSource = &heatSources[2]; - } - else if (presetNum == MODELS_AquaThermAire) { // AquaThermAire - setNumNodes(12); - setpoint_C = 50.; - - initialTankT_C = 49.32; - hasInitialTankTemp = true; - - tankVolume_L = GAL_TO_L(54.4); - tankUA_kJperHrC = 10.35; - - doTempDepression = false; - tankMixesOnDraw = false; - - // heat exchangers only - hasHeatExchanger = true; - heatExchangerEffectiveness = 0.93; - - HeatSource compressor(this); - - //compressor values - compressor.isOn = false; - compressor.isVIP = false; - compressor.typeOfHeatSource = TYPE_compressor; - - compressor.setCondensity({1.}); - - //AOSmithPHPT60 values - compressor.perfMap.reserve(4); - - compressor.perfMap.push_back({ - 5, // Temperature (T_F) - {-1423, 38.70 ,0.}, // Input Power Coefficients (inputPower_coeffs) - {-0.13839, 0.012319, 0.} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 34, // Temperature (T_F) - {-1558, 42.40, 0.}, // Input Power Coefficients (inputPower_coeffs) - {-0.19375, 0.017247, 0.} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 67, // Temperature (T_F) - {-1713, 46.60, 0.}, // Input Power Coefficients (inputPower_coeffs) - {-0.28156, 0.025064, 0.} // COP Coefficients (COP_coeffs) - }); - - compressor.perfMap.push_back({ - 95, // Temperature (T_F) - {-1844, 50.17, 0.}, // Input Power Coefficients (inputPower_coeffs) - {-0.47273, 0.042082, 0.} // COP Coefficients (COP_coeffs) - }); - - compressor.minT = F_TO_C(-25); - compressor.maxT = F_TO_C(125.); - compressor.hysteresis_dC = dF_TO_dC(1); - compressor.configuration = HeatSource::CONFIG_SUBMERGED; - - //logic conditions - compressor.addTurnOnLogic(HPWH::wholeTank(111,UNITS_F,true)); - compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(14))); - - //set everything in its places - heatSources.resize(1); - heatSources[0] = compressor; - } - else { - if (hpwhVerbosity >= VRB_reluctant) { - msg("You have tried to select a preset model which does not exist. \n"); - } - return HPWH_ABORT; - } - - if (hasInitialTankTemp) - setTankToTemperature(initialTankT_C); - else //start tank off at setpoint - resetTankToSetpoint(); - - hpwhModel = presetNum; - - //calculate oft-used derived values - calcDerivedValues(); - - if (checkInputs() == HPWH_ABORT) { - return HPWH_ABORT; - } - - isHeating = false; - for (int i = 0; i < getNumHeatSources(); i++) { - if (heatSources[i].isOn) { - isHeating = true; - } - heatSources[i].sortPerformanceMap(); - } - - if (hpwhVerbosity >= VRB_emetic) { - for (int i = 0; i < getNumHeatSources(); i++) { - msg("heat source %d: %p \n", i, &heatSources[i]); - } - msg("\n\n"); - } - - simHasFailed = false; - return 0; //successful init returns 0 -} //end HPWHinit_presets \ No newline at end of file + // set everything in its place + heatSources.resize(2); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + + heatSources[0].followedByHeatSource = &heatSources[1]; + } + + else if (presetNum == MODELS_StorageTank) + { + setNumNodes(12); + setpoint_C = 800.; + + initialTankT_C = F_TO_C(127.); + hasInitialTankTemp = true; + + tankSizeFixed = false; + tankVolume_L = GAL_TO_L(80); + tankUA_kJperHrC = 10; // 0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + } + + // basic compressor tank for testing + else if (presetNum == MODELS_basicIntegrated) + { + setNumNodes(12); + setpoint_C = 50; + + tankSizeFixed = false; + tankVolume_L = 120; + tankUA_kJperHrC = 10; // 0 to turn off + // tankUA_kJperHrC = 0; //0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + + 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({1., 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.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.addTurnOnLogic(HPWH::bottomThird(20)); + compressor.addTurnOnLogic(HPWH::standby(15)); + + // set everything in its place + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = compressor; + heatSources[2] = resistiveElementBottom; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + + // simple external style for testing + else if (presetNum == MODELS_externalTest) + { + setNumNodes(96); + setpoint_C = 50; + + tankSizeFixed = false; + tankVolume_L = 120; + // tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 0; // 0 to turn off + + doTempDepression = false; + tankMixesOnDraw = false; + + 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.isMultipass = false; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.addTurnOnLogic(HPWH::bottomThird(20)); + compressor.addTurnOnLogic(HPWH::standby(15)); + + // lowT cutoff + compressor.addShutOffLogic(HPWH::bottomNodeMaxTemp(20, true)); + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + // voltex 60 gallon + else if (presetNum == MODELS_AOSmithPHPT60) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = 215.8; + tankUA_kJperHrC = 7.31; + + doTempDepression = false; + tankMixesOnDraw = true; + + 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = compressor; + heatSources[2] = resistiveElementBottom; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_AOSmithPHPT80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = 283.9; + tankUA_kJperHrC = 8.8; + + doTempDepression = false; + tankMixesOnDraw = true; + + 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = compressor; + heatSources[2] = resistiveElementBottom; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_GE2012) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = 172; + tankUA_kJperHrC = 6.8; + + doTempDepression = false; + tankMixesOnDraw = true; + + 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = compressor; + heatSources[2] = resistiveElementBottom; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + // If a Colmac single pass preset cold weather or not + else if (MODELS_ColmacCxV_5_SP <= presetNum && presetNum <= MODELS_ColmacCxA_30_SP) + { + setNumNodes(96); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + + doTempDepression = false; + tankMixesOnDraw = false; + + tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important + tankUA_kJperHrC = 7; + + 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.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.isMultipass = false; + compressor.perfMap.reserve(1); + compressor.hysteresis_dC = 0; + + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = getNumNodes() - 1; + + // logic conditions + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(15), this)); + + // lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic(std::make_shared( + "bottom node", nodeWeights1, dF_TO_dC(15.), this, false, std::greater(), true)); + compressor.depressesTemperature = false; // no temp depression + + // Defrost Derate + compressor.setupDefrostMap(); + + if (presetNum == MODELS_ColmacCxV_5_SP) + { + setTankSize_adjustUA(200., UNITS_GAL); + // logic conditions + compressor.minT = F_TO_C(-4.0); + compressor.maxSetpoint_C = MAXOUTLET_R410A; + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {4.9621645063, + -0.0096084144, + -0.0095647009, + -0.0115911960, + -0.0000788517, + 0.0000886176, + 0.0001114142, + 0.0001832377, + -0.0000451308, + 0.0000411975, + 0.0000003535}, // Input Power Coefficients (inputPower_coeffs) + + {3.8189922420, + 0.0569412237, + -0.0320101962, + -0.0012859036, + 0.0000576439, + 0.0001101241, + -0.0000352368, + -0.0002630301, + -0.0000509365, + 0.0000369655, + -0.0000000606} // COP Coefficients (COP_coeffs) + }); + } + else + { + // logic conditions + compressor.minT = F_TO_C(40.); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + if (presetNum == MODELS_ColmacCxA_10_SP) + { + setTankSize_adjustUA(500., UNITS_GAL); + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {5.9786974243, + 0.0194445115, + -0.0077802278, + 0.0053809029, + -0.0000334832, + 0.0001864310, + 0.0001190540, + 0.0000040405, + -0.0002538279, + -0.0000477652, + 0.0000014101}, // Input Power Coefficients (inputPower_coeffs) + + {3.6128563086, + 0.0527064498, + -0.0278198945, + -0.0070529748, + 0.0000934705, + 0.0000781711, + -0.0000359215, + -0.0002223206, + 0.0000359239, + 0.0000727189, + -0.0000005037} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_15_SP) + { + setTankSize_adjustUA(600., UNITS_GAL); + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {15.5869846555, + -0.0044503761, + -0.0577941202, + -0.0286911185, + -0.0000803325, + 0.0003399817, + 0.0002009576, + 0.0002494761, + -0.0000595773, + 0.0001401800, + 0.0000004312}, // Input Power Coefficients (inputPower_coeffs) + + {1.6643120405, + 0.0515623393, + -0.0110239930, + 0.0041514430, + 0.0000481544, + 0.0000493424, + -0.0000262721, + -0.0002356218, + -0.0000989625, + -0.0000070572, + 0.0000004108} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_20_SP) + { + setTankSize_adjustUA(800., UNITS_GAL); + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {23.0746692231, + 0.0248584608, + -0.1417927282, + -0.0253733303, + -0.0004882754, + 0.0006508079, + 0.0002139934, + 0.0005552752, + -0.0002026772, + 0.0000607338, + 0.0000021571}, // Input Power Coefficients (inputPower_coeffs) + + {1.7692660120, + 0.0525134783, + -0.0081102040, + -0.0008715405, + 0.0001274956, + 0.0000369489, + -0.0000293775, + -0.0002778086, + -0.0000095067, + 0.0000381186, + -0.0000003135} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_25_SP) + { + setTankSize_adjustUA(1000., UNITS_GAL); + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {20.4185336541, + -0.0236920615, + -0.0736219119, + -0.0260385082, + -0.0005048074, + 0.0004940510, + 0.0002632660, + 0.0009820050, + -0.0000223587, + 0.0000885101, + 0.0000005649}, // Input Power Coefficients (inputPower_coeffs) + + {0.8942843854, + 0.0677641611, + -0.0001582927, + 0.0048083998, + 0.0001196407, + 0.0000334921, + -0.0000378740, + -0.0004146401, + -0.0001213363, + -0.0000031856, + 0.0000006306} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_30_SP) + { + setTankSize_adjustUA(1200., UNITS_GAL); + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {11.3687485772, + -0.0207292362, + 0.0496254077, + -0.0038394967, + -0.0005991041, + 0.0001304318, + 0.0003099774, + 0.0012092717, + -0.0001455509, + -0.0000893889, + 0.0000018221}, // Input Power Coefficients (inputPower_coeffs) + + {4.4170108542, + 0.0596384263, + -0.0416104579, + -0.0017199887, + 0.0000774664, + 0.0001521934, + -0.0000251665, + -0.0003289731, + -0.0000801823, + 0.0000325972, + 0.0000002705} // COP Coefficients (COP_coeffs) + }); + } + } // End if MODELS_ColmacCxV_5_SP + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + + // if colmac multipass + else if (MODELS_ColmacCxV_5_MP <= presetNum && presetNum <= MODELS_ColmacCxA_30_MP) + { + setNumNodes(24); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + + doTempDepression = false; + tankMixesOnDraw = false; + + tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important + tankUA_kJperHrC = 7; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.perfMap.reserve(1); + compressor.hysteresis_dC = 0; + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = static_cast(getNumNodes() / 3) - 1; + + // logic conditions + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(5.), this)); + + std::vector nodeWeights1; + nodeWeights1.emplace_back(4); + compressor.addShutOffLogic(std::make_shared( + "fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); + + compressor.depressesTemperature = false; // no temp depression + + // Defrost Derate + compressor.setupDefrostMap(); + + if (presetNum == MODELS_ColmacCxV_5_MP) + { + setTankSize_adjustUA(200., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS( + 9.); // https://colmacwaterheat.com/wp-content/uploads/2020/10/Technical-Datasheet-Air-Source.pdf + + // logic conditions + compressor.minT = F_TO_C(-4.0); + compressor.maxT = F_TO_C(105.); + compressor.maxSetpoint_C = MAXOUTLET_R410A; + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {5.8438525529, + 0.0003288231, + -0.0494255840, + -0.0000386642, + 0.0004385362, + 0.0000647268}, // Input Power Coefficients (inputPower_coeffs) + + {0.6679056901, + 0.0499777846, + 0.0251828292, + 0.0000699764, + -0.0001552229, + -0.0002911167} // COP Coefficients (COP_coeffs) + }); + } + else + { + // logic conditions + compressor.minT = F_TO_C(40.); + compressor.maxT = F_TO_C(105.); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + if (presetNum == MODELS_ColmacCxA_10_MP) + { + setTankSize_adjustUA(500., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(18.); + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {8.6918824405, + 0.0136666667, + -0.0548348214, + -0.0000208333, + 0.0005301339, + -0.0000250000}, // Input Power Coefficients (inputPower_coeffs) + + {0.6944181117, + 0.0445926666, + 0.0213188804, + 0.0001172913, + -0.0001387694, + -0.0002365885} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_15_MP) + { + setTankSize_adjustUA(600., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(26.); + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {12.4908723958, + 0.0073988095, + -0.0411417411, + 0.0000000000, + 0.0005789621, + 0.0000696429}, // Input Power Coefficients (inputPower_coeffs) + + {1.2846349520, + 0.0334658309, + 0.0019121906, + 0.0002840970, + 0.0000497136, + -0.0004401737} // COP Coefficients (COP_coeffs) + + }); + } + else if (presetNum == MODELS_ColmacCxA_20_MP) + { + setTankSize_adjustUA(800., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS( + 36.); // https://colmacwaterheat.com/wp-content/uploads/2020/10/Technical-Datasheet-Air-Source.pdf + + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {14.4893345424, + 0.0355357143, + -0.0476593192, + -0.0002916667, + 0.0006120954, + 0.0003607143}, // Input Power Coefficients (inputPower_coeffs) + + {1.2421582831, + 0.0450256569, + 0.0051234755, + 0.0001271296, + -0.0000299981, + -0.0002910606} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_25_MP) + { + setTankSize_adjustUA(1000., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(32.); + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {14.5805808222, + 0.0081934524, + -0.0216169085, + -0.0001979167, + 0.0007376535, + 0.0004955357}, // Input Power Coefficients (inputPower_coeffs) + + {2.0013175767, + 0.0576617432, + -0.0130480870, + 0.0000856818, + 0.0000610760, + -0.0003684106} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_ColmacCxA_30_MP) + { + setTankSize_adjustUA(1200., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(41.); + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {14.5824911644, + 0.0072083333, + -0.0278055246, + -0.0002916667, + 0.0008841378, + 0.0008125000}, // Input Power Coefficients (inputPower_coeffs) + + {2.6996807527, + 0.0617507969, + -0.0220966420, + 0.0000336149, + 0.0000890989, + -0.0003682431} // COP Coefficients (COP_coeffs) + }); + } + } + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + // If Nyle single pass preset + else if (MODELS_NyleC25A_SP <= presetNum && presetNum <= MODELS_NyleC250A_C_SP) + { + setNumNodes(96); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + + tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important + tankUA_kJperHrC = 7; + + doTempDepression = false; + tankMixesOnDraw = false; + + 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.extrapolationMethod = EXTRAP_NEAREST; + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.isMultipass = false; + compressor.perfMap.reserve(1); + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = getNumNodes() - 1; + + // logic conditions + if (MODELS_NyleC25A_SP <= presetNum && presetNum <= MODELS_NyleC250A_SP) + { // If not cold weather package + compressor.minT = F_TO_C(40.); // Min air temperature sans Cold Weather Package + } + else + { + compressor.minT = F_TO_C(35.); // Min air temperature WITH Cold Weather Package + } + compressor.maxT = F_TO_C(120.0); // Max air temperature + compressor.hysteresis_dC = 0; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // Defines the maximum outlet temperature at the a low air temperature + compressor.maxOut_at_LowT.outT_C = F_TO_C(140.); + compressor.maxOut_at_LowT.airT_C = F_TO_C(40.); + + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(15), this, false)); + + // lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic(std::make_shared( + "bottom node", nodeWeights1, dF_TO_dC(15.), this, false, std::greater(), true)); + compressor.depressesTemperature = false; // no temp depression + + // Defrost Derate + compressor.setupDefrostMap(); + + // Perfmaps for each compressor size + if (presetNum == MODELS_NyleC25A_SP) + { + setTankSize_adjustUA(200., UNITS_GAL); + compressor.perfMap.push_back({ + 90, // Temperature (T_F) + + {4.060120364, + -0.020584279, + -0.024201054, + -0.007023945, + 0.000017461, + 0.000110366, + 0.000060338, + 0.000120015, + 0.000111068, + 0.000138907, + -0.000001569}, // Input Power Coefficients (inputPower_coeffs) + + {0.462979529, + 0.065656840, + 0.001077377, + 0.003428059, + 0.000243692, + 0.000021522, + 0.000005143, + -0.000384778, + -0.000404744, + -0.000036277, + 0.000001900} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_NyleC60A_SP || presetNum == MODELS_NyleC60A_C_SP) + { + setTankSize_adjustUA(300., UNITS_GAL); + compressor.perfMap.push_back({ + 90, // Temperature (T_F) + + {-0.1180905709, + 0.0045354306, + 0.0314990479, + -0.0406839757, + 0.0002355294, + 0.0000818684, + 0.0001943834, + -0.0002160871, + 0.0003053633, + 0.0003612413, + -0.0000035912}, // Input Power Coefficients (inputPower_coeffs) + + {6.8205043418, + 0.0860385185, + -0.0748330699, + -0.0172447955, + 0.0000510842, + 0.0002187441, + -0.0000321036, + -0.0003311463, + -0.0002154270, + 0.0001307922, + 0.0000005568} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_NyleC90A_SP || presetNum == MODELS_NyleC90A_C_SP) + { + setTankSize_adjustUA(400., UNITS_GAL); + compressor.perfMap.push_back({ + 90, // Temperature (T_F) + + {13.27612215047, + -0.01014009337, + -0.13401028549, + -0.02325705976, + -0.00032515646, + 0.00040270625, + 0.00001988733, + 0.00069451670, + 0.00069067890, + 0.00071091372, + -0.00000854352}, // Input Power Coefficients (inputPower_coeffs) + + {1.49112327987, + 0.06616282153, + 0.00715307252, + -0.01269458185, + 0.00031448571, + 0.00001765313, + 0.00006002498, + -0.00045661397, + -0.00034003896, + -0.00004327766, + 0.00000176015} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_NyleC125A_SP || presetNum == MODELS_NyleC125A_C_SP) + { + setTankSize_adjustUA(500., UNITS_GAL); + compressor.perfMap.push_back({ + 90, // Temperature (T_F) + + {-3.558277209, + -0.038590968, + 0.136307181, + -0.016945699, + 0.000983753, + -5.18201E-05, + 0.000476904, + -0.000514211, + -0.000359172, + 0.000266509, + -1.58646E-07}, // Input Power Coefficients (inputPower_coeffs) + + {4.889555031, + 0.117102769, + -0.060005795, + -0.011871234, + -1.79926E-05, + 0.000207293, + -1.4452E-05, + -0.000492486, + -0.000376814, + 7.85911E-05, + 1.47884E-06} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_NyleC185A_SP || presetNum == MODELS_NyleC185A_C_SP) + { + setTankSize_adjustUA(800., UNITS_GAL); + compressor.perfMap.push_back({ + 90, // Temperature (T_F) + + {18.58007733, + -0.215324777, + -0.089782421, + 0.01503161, + 0.000332503, + 0.000274216, + 2.70498E-05, + 0.001387914, + 0.000449199, + 0.000829578, + -5.28641E-06}, // Input Power Coefficients (inputPower_coeffs) + + {-0.629432348, + 0.181466663, + 0.00044047, + 0.012104957, + -6.61515E-05, + 9.29975E-05, + 9.78042E-05, + -0.000872708, + -0.001013945, + -0.00021852, + 5.55444E-06} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_NyleC250A_SP || presetNum == MODELS_NyleC250A_C_SP) + { + setTankSize_adjustUA(800., UNITS_GAL); + + compressor.perfMap.push_back({ + 90, // Temperature (T_F) + + {-13.89057656, + 0.025902417, + 0.304250541, + 0.061695153, + -0.001474249, + -0.001126845, + -0.000220192, + 0.001241026, + 0.000571009, + -0.000479282, + 9.04063E-06}, // Input Power Coefficients (inputPower_coeffs) + + {7.443904067, + 0.185978755, + -0.098481635, + -0.002500073, + 0.000127658, + 0.000444321, + 0.000139547, + -0.001000195, + -0.001140199, + -8.77557E-05, + 4.87405E-06} // COP Coefficients (COP_coeffs) + }); + } + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + + // If Nyle multipass presets + else if (MODELS_NyleC60A_MP <= presetNum && presetNum <= MODELS_NyleC250A_C_MP) + { + setNumNodes(24); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + + doTempDepression = false; + tankMixesOnDraw = false; + + tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important + tankUA_kJperHrC = 7; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); + compressor.extrapolationMethod = EXTRAP_NEAREST; + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.hysteresis_dC = 0; + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = static_cast(getNumNodes() / 3.) - 1; + + // logic conditions//logic conditions + if (MODELS_NyleC60A_MP <= presetNum && presetNum <= MODELS_NyleC250A_MP) + { // If not cold weather package + compressor.minT = F_TO_C(40.); // Min air temperature sans Cold Weather Package + } + else + { + compressor.minT = F_TO_C(35.); // Min air temperature WITH Cold Weather Package + } + compressor.maxT = F_TO_C(130.0); // Max air temperature + compressor.maxSetpoint_C = F_TO_C(160.); + + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(5.), this)); + + std::vector nodeWeights1; + nodeWeights1.emplace_back(4); + compressor.addShutOffLogic(std::make_shared( + "fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); + compressor.depressesTemperature = false; // no temp depression + + // Defrost Derate + compressor.setupDefrostMap(); + + // Performance grid + compressor.perfGrid.reserve(2); + compressor.perfGridValues.reserve(2); + + // Nyle MP models are all on the same grid axes + compressor.perfGrid.push_back({40., 60., 80., 90.}); // Grid Axis 1 Tair (F) + compressor.perfGrid.push_back({40., 60., 80., 100., 130., 150.}); // Grid Axis 2 Tin (F) + + if (presetNum == MODELS_NyleC60A_MP || presetNum == MODELS_NyleC60A_C_MP) + { + setTankSize_adjustUA(360., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(13.); + if (presetNum == MODELS_NyleC60A_C_MP) + { + compressor.resDefrost = { + 4.5, // inputPwr_kW; + 5.0, // constTempLift_dF; + 40.0 // onBelowT_F + }; + } + // Grid values in long format, table 1, input power (W) + compressor.perfGridValues.push_back({3.64, 4.11, 4.86, 5.97, 8.68, 9.95, 3.72, 4.27, + 4.99, 6.03, 8.55, 10.02, 3.98, 4.53, 5.24, 6.24, + 8.54, 9.55, 4.45, 4.68, 5.37, 6.34, 8.59, 9.55}); + // Grid values in long format, table 2, COP + compressor.perfGridValues.push_back( + {3.362637363, 2.917274939, 2.407407407, 1.907872697, 1.296082949, 1.095477387, + 4.438172043, 3.772833724, 3.132264529, 2.505804312, 1.678362573, 1.386227545, + 5.467336683, 4.708609272, 3.921755725, 3.169871795, 2.165105386, 1.860732984, + 5.512359551, 5.153846154, 4.290502793, 3.417981073, 2.272409779, 1.927748691}); + } + else if (presetNum == MODELS_NyleC90A_MP || presetNum == MODELS_NyleC90A_C_MP) + { + setTankSize_adjustUA(480., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(20.); + if (presetNum == MODELS_NyleC90A_C_MP) + { + compressor.resDefrost = { + 5.4, // inputPwr_kW; + 5.0, // constTempLift_dF; + 40.0 // onBelowT_F + }; + } + // Grid values in long format, table 1, input power (W) + compressor.perfGridValues.push_back( + {4.41, 6.04, 7.24, 9.14, 12.23, 14.73, 4.78, 6.61, 7.74, 9.40, 12.47, 14.75, + 5.51, 6.66, 8.44, 9.95, 13.06, 15.35, 6.78, 7.79, 8.81, 10.01, 11.91, 13.35}); + // Grid values in long format, table 2, COP + compressor.perfGridValues.push_back( + {4.79138322, 3.473509934, 2.801104972, 2.177242888, 1.569910057, 1.272233537, + 6.071129707, 4.264750378, 3.536175711, 2.827659574, 2.036086608, 1.666440678, + 7.150635209, 5.659159159, 4.305687204, 3.493467337, 2.487748851, 2.018241042, + 6.750737463, 5.604621309, 4.734392736, 3.94005994, 3.04534005, 2.558801498}); + } + else if (presetNum == MODELS_NyleC125A_MP || presetNum == MODELS_NyleC125A_C_MP) + { + setTankSize_adjustUA(600., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(28.); + if (presetNum == MODELS_NyleC125A_C_MP) + { + compressor.resDefrost = { + 9.0, // inputPwr_kW; + 5.0, // constTempLift_dF; + 40.0 // onBelowT_F + }; + } + // Grid values in long format, table 1, input power (W) + compressor.perfGridValues.push_back( + {6.4, 7.72, 9.65, 12.54, 20.54, 24.69, 6.89, 8.28, 10.13, 12.85, 19.75, 24.39, + 7.69, 9.07, 10.87, 13.44, 19.68, 22.35, 8.58, 9.5, 11.27, 13.69, 19.72, 22.4}); + // Grid values in long format, table 2, COP + compressor.perfGridValues.push_back( + {4.2390625, 3.465025907, 2.718134715, 2.060606061, 1.247809153, 1.016605913, + 5.374455733, 4.352657005, 3.453109576, 2.645136187, 1.66278481, 1.307093071, + 6.503250975, 5.276736494, 4.229070837, 3.27827381, 2.113821138, 1.770469799, + 6.657342657, 5.749473684, 4.612244898, 3.542731921, 2.221095335, 1.816964286}); + } + else if (presetNum == MODELS_NyleC185A_MP || presetNum == MODELS_NyleC185A_C_MP) + { + setTankSize_adjustUA(960., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(40.); + if (presetNum == MODELS_NyleC185A_C_MP) + { + compressor.resDefrost = { + 7.2, // inputPwr_kW; + 5.0, // constTempLift_dF; + 40.0 // onBelowTemp_F + }; + } + // Grid values in long format, table 1, input power (W) + compressor.perfGridValues.push_back( + {7.57, 11.66, 14.05, 18.3, 25.04, 30.48, 6.99, 10.46, 14.28, 18.19, 26.24, 32.32, + 7.87, 12.04, 15.02, 18.81, 25.99, 31.26, 8.15, 12.46, 15.17, 18.95, 26.23, 31.62}); + // Grid values in long format, table 2, COP + compressor.perfGridValues.push_back( + {5.531043593, 3.556603774, 2.918149466, 2.214754098, 1.590255591, 1.291010499, + 8.010014306, 5.258126195, 3.778711485, 2.916437603, 1.964176829, 1.56404703, + 9.65819568, 6.200166113, 4.792276964, 3.705475811, 2.561369758, 2.05950096, + 10.26993865, 6.350722311, 5.04218853, 3.841688654, 2.574151735, 2.025616698}); + } + else if (presetNum == MODELS_NyleC250A_MP || presetNum == MODELS_NyleC250A_C_MP) + { + setTankSize_adjustUA(960., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(50.); + if (presetNum == MODELS_NyleC250A_C_MP) + { + compressor.resDefrost = { + 18.0, // inputPwr_kW; + 5.0, // constTempLift_dF; + 40.0 // onBelowT_F + }; + } + // Grid values in long format, table 1, input power (W) + compressor.perfGridValues.push_back({10.89, 12.23, 13.55, 14.58, 15.74, 16.72, + 11.46, 13.76, 15.97, 17.79, 20.56, 22.50, + 10.36, 14.66, 18.07, 21.23, 25.81, 29.01, + 8.67, 15.05, 18.76, 21.87, 26.63, 30.02}); + + // Grid values in long format, table 2, COP + compressor.perfGridValues.push_back( + {5.81818181, 4.50040883, 3.69667896, 3.12414266, 2.38500635, 1.93540669, + 7.24520069, 5.50145348, 4.39323732, 3.67734682, 2.73249027, 2.23911111, + 10.6196911, 7.05320600, 5.41228555, 4.28638718, 3.04804339, 2.46053085, + 14.7831603, 7.77903268, 5.71801705, 4.40237768, 2.92489673, 2.21419054}); + } + + // Set up regular grid interpolator. + compressor.perfRGI = + new Btwxt::RegularGridInterpolator(compressor.perfGrid, compressor.perfGridValues); + compressor.useBtwxtGrid = true; + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + // if rheem multipass + else if (MODELS_RHEEM_HPHD60HNU_201_MP <= presetNum && + presetNum <= MODELS_RHEEM_HPHD135VNU_483_MP) + { + setNumNodes(24); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + + doTempDepression = false; + tankMixesOnDraw = false; + + tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important + tankUA_kJperHrC = 7; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.perfMap.reserve(1); + compressor.hysteresis_dC = 0; + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = static_cast(getNumNodes() / 3.) - 1; + + // logic conditions + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(5.), this)); + + std::vector nodeWeights1; + nodeWeights1.emplace_back(4); + compressor.addShutOffLogic(std::make_shared( + "fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); + compressor.depressesTemperature = false; // no temp depression + + // Defrost Derate + compressor.setupDefrostMap(); + + // logic conditions + compressor.minT = F_TO_C(45.); + compressor.maxT = F_TO_C(110.); + compressor.maxSetpoint_C = MAXOUTLET_R134A; // data says 150... + + if (presetNum == MODELS_RHEEM_HPHD60HNU_201_MP || + presetNum == MODELS_RHEEM_HPHD60VNU_201_MP) + { + setTankSize_adjustUA(250., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(17.4); + compressor.perfMap.push_back({ + 110, // Temperature (T_F) + + {1.8558438453, + 0.0120796155, + -0.0135443327, + 0.0000059621, + 0.0003010506, + -0.0000463525}, // Input Power Coefficients (inputPower_coeffs) + + {3.6840046360, + 0.0995685071, + -0.0398107723, + -0.0001903160, + 0.0000980361, + -0.0003469814} // COP Coefficients (COP_coeffs) + }); + } + else if (presetNum == MODELS_RHEEM_HPHD135HNU_483_MP || + presetNum == MODELS_RHEEM_HPHD135VNU_483_MP) + { + setTankSize_adjustUA(500., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(34.87); + compressor.perfMap.push_back({ + 110, // Temperature (T_F) + + {5.1838201136, + 0.0247312962, + -0.0120766440, + 0.0000493862, + 0.0005422089, + -0.0001385078}, // Input Power Coefficients (inputPower_coeffs) + + {5.0207181209, + 0.0442525790, + -0.0418284882, + 0.0000793531, + 0.0001132421, + -0.0002491563} // COP Coefficients (COP_coeffs) + }); + } + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + + else if (presetNum == MODELS_MITSUBISHI_QAHV_N136TAU_HPB_SP) + { + setNumNodes(96); + setpoint_C = 65; + + tankVolume_L = GAL_TO_L(500); + tankUA_kJperHrC = 12; + tankSizeFixed = false; + + doTempDepression = false; + tankMixesOnDraw = false; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.minT = F_TO_C(-13.); + compressor.setCondensity({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = getNumNodes() - 1; + + // What to do about these?! + compressor.hysteresis_dC = 4; + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.isMultipass = false; + compressor.maxSetpoint_C = F_TO_C(176.1); + + // Turn on + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "eighth node absolute", nodeWeights, F_TO_C(110.), this, true)); + + // lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic(std::make_shared( + "bottom node", nodeWeights1, dF_TO_dC(15.), this, false, std::greater(), true)); + compressor.depressesTemperature = false; + + // Performance grid: externalT_F, Tout_F, condenserTemp_F + compressor.perfGrid.reserve(2); + compressor.perfGridValues.reserve(2); + compressor.perfGrid.push_back( + {-13, -11.2, -7.6, -4, -0.4, 3.2, 6.8, 10.4, 14, 17.6, 21.2, 24.8, + 28.4, 32, 35.6, 39.2, 42.8, 46.4, 50, 53.6, 57.2, 60.8, 64.4, 68, + 71.6, 75.2, 78.8, 82.4, 86, 89.6, 93.2, 96.8, 100.4, 104}); // Grid Axis 1 Tair (F) + compressor.perfGrid.push_back({140., 158., 176.}); // Grid Axis 2 Tout (F) + compressor.perfGrid.push_back({41, 48.2, 62.6, 75.2, 84.2}); // Grid Axis 3 Tin (F) + + // Grid values in long format, table 1, input power (Btu/hr) + compressor.perfGridValues.push_back( + {56518.565328, 57130.739544, 57094.73612, 57166.756616, + 57238.777112, 56518.565328, 57130.739544, 57094.73612, + 57166.756616, 57238.777112, 58061.348896, 58360.24692, + 58591.39286, 58870.368216, 59093.547136, 56626.5960719999, + 57202.763452, 57310.794196, 57310.794196, 57490.848848, + 56626.5960719999, 57202.763452, 57310.794196, 57310.794196, + 57490.848848, 58280.539188, 58519.65556, 58786.67868, + 59093.547136, 59372.5190799999, 56950.695128, 57850.95474, + 57814.947904, 57814.947904, 57922.978648, 56950.695128, + 57850.95474, 57814.947904, 57814.947904, 57922.978648, + 58679.067612, 58977.969048, 59372.5190799999, 59651.494436, + 59930.46638, 57418.831764, 58175.053796, 58283.08454, + 58247.07088, 58427.12212, 57418.831764, 58175.053796, + 58283.08454, 58247.07088, 58427.12212, 59137.384512, + 59376.504296, 59902.566456, 60265.23476, 60516.313604, + 57778.930832, 58643.190432, 58823.23826, 58643.190432, + 58967.282664, 57778.930832, 58643.190432, 58823.23826, + 58643.190432, 58967.282664, 59515.99368, 59954.377676, + 60321.031196, 60934.77152, 61213.743464, 58103.029888, + 59075.313408, 59219.357812, 59291.374896, 59579.45688, + 58103.029888, 59075.313408, 59219.357812, 59291.374896, + 59579.45688, 60014.159328, 60273.205192, 60934.77152, + 61436.922384, 61799.587276, 58535.152864, 59399.412464, + 59795.525192, 59903.555936, 60299.672076, 58535.152864, + 59399.412464, 59795.525192, 59903.555936, 60299.672076, + 60512.321564, 60751.448172, 61409.02246, 62022.769608, + 62329.641476, 58931.275828, 59651.4842, 60011.58668, + 60263.66524, 60551.747224, 58931.275828, 59651.4842, + 60011.58668, 60263.66524, 60551.747224, 60910.860224, + 61369.1703, 62022.769608, 62580.713496, 62803.895828, + 59219.357812, 60155.634496, 60443.71648, 60659.777968, + 61163.92144, 59219.357812, 60155.634496, 60443.71648, + 60659.777968, 61163.92144, 61329.321552, 61687.997816, + 62441.224112, 63166.56072, 63585.015224, 59651.4842, + 60479.726728, 61019.88386, 61163.92144, 61920.146884, + 59651.4842, 60479.726728, 61019.88386, 61163.92144, + 61920.146884, 61707.923896, 62186.166876, 63027.071336, + 63724.504608, 64170.862448, 59723.504696, 60839.83262, + 61379.993164, 61524.030744, 62352.273272, 59723.504696, + 60839.83262, 61379.993164, 61524.030744, 62352.273272, + 61966.96976, 62445.21274, 63361.839716, 64142.969348, + 64505.63424, 59759.511532, 60695.788216, 61524.030744, + 61884.136636, 62712.382576, 59759.511532, 60695.788216, + 61524.030744, 61884.136636, 62712.382576, 62166.240796, + 62744.114176, 63668.714996, 64282.451908, 64589.323776, + 59759.511532, 61019.88386, 61848.1298, 62064.191288, + 62964.4509, 59759.511532, 61019.88386, 61848.1298, + 62064.191288, 62964.4509, 62425.28666, 63003.16004, + 63975.58004, 64533.523928, 64617.2237, 59471.429548, + 61055.894108, 61956.160544, 62460.304016, 63216.526048, + 59471.429548, 61055.894108, 61956.160544, 62460.304016, + 63216.526048, 62624.550872, 63202.424252, 64310.351832, + 64756.70626, 64728.806336, 61632.061488, 63036.474808, + 63828.7105, 64296.847136, 65053.069168, 61632.061488, + 63036.474808, 63828.7105, 64296.847136, 65053.069168, + 63561.10734, 64099.125148, 65258.8605359999, 65593.625504, + 65621.525428, 51189.004268, 52917.506408, 54826.070024, + 57094.73612, 59327.388556, 51189.004268, 52917.506408, + 54826.070024, 57094.73612, 59327.388556, 55391.172288, + 58240.683616, 62106.459144, 65733.114888, 65705.214964, + 41286.100768, 42726.524336, 45931.460292, 49784.590948, + 53457.6669519999, 41286.100768, 42726.524336, 45931.460292, + 49784.590948, 53457.6669519999, 49433.093532, 51067.083272, + 57921.8561, 61604.3082799999, 64477.734316, 35632.444064, + 37108.87788, 41394.134924, 45139.228012, 49568.526048, + 35632.444064, 37108.87788, 41394.134924, 45139.228012, + 49568.526048, 47540.06134, 47221.233824, 56080.631716, + 59539.904976, 61883.276812, 34192.023908, 35596.430404, + 39809.6669519999, 43410.72246, 47659.969256, 34192.023908, + 35596.430404, 39809.6669519999, 43410.72246, 47659.969256, + 47559.98742, 47061.821772, 54490.482764, 57893.959588, + 60153.648712, 32895.641332, 34192.023908, 38225.202392, + 41646.20666, 45751.409052, 32895.641332, 34192.023908, + 38225.202392, 41646.20666, 45751.409052, 47659.621232, + 46842.624656, 52956.13366, 56220.1211, 58312.420916, + 31563.25192, 32715.58668, 36532.707088, 39881.687448, + 43914.869344, 31563.25192, 32715.58668, 36532.707088, + 39881.687448, 43914.869344, 47719.402884, 46742.994256, + 51393.877808, 54518.382688, 56554.886068, 30194.85226, + 31347.18702, 35092.286932, 38117.171648, 42006.312552, + 30194.85226, 31347.18702, 35092.286932, 38117.171648, + 42006.312552, 47799.1072039999, 46324.532928, 49692.139396, + 52732.951328, 54769.45812, 30446.923996, 31383.197268, + 34300.054652, 37144.884716, 40781.953884, 30446.923996, + 31383.197268, 34300.054652, 37144.884716, 40781.953884, + 45547.391924, 44212.307032, 47320.867636, 50389.57608, + 52202.90054, 30698.995732, 31311.176772, 33723.88386, + 36316.6456, 39701.636208, 30698.995732, 31311.176772, + 33723.88386, 36316.6456, 39701.636208, 43295.680056, + 41980.517832, 45089.078436, 47795.121988, 49720.03932, + 30987.081128, 31311.176772, 33183.726728, 35344.358668, + 38477.27754, 30987.081128, 31311.176772, 33183.726728, + 35344.358668, 38477.27754, 41562.059916, 40486.017476, + 43415.236536, 46037.590552, 47795.121988, 31203.146028, + 31311.176772, 32571.545688, 34552.126388, 37360.949616, + 31203.146028, 31311.176772, 32571.545688, 34552.126388, + 37360.949616, 40486.017476, 39310.3446359999, 42159.859376, + 44782.20998, 46511.844904, 31167.132368, 31311.176772, + 32355.4842, 34156.010248, 36712.758328, 31167.132368, + 31311.176772, 32355.4842, 34156.010248, 36712.758328, + 39449.830608, 38353.862088, 41127.657724, 43666.31538, + 45284.360844, 31059.101624, 31131.12212, 32319.473952, + 34156.010248, 36748.771988, 31059.101624, 31131.12212, + 32319.473952, 34156.010248, 36748.771988, 38592.985284, + 37457.161192, 40151.249096, 42801.496212, 44419.541676, + 30879.050384, 31023.091376, 32319.473952, 34192.023908, + 36820.789072, 30879.050384, 31023.091376, 32319.473952, + 34192.023908, 36820.789072, 37756.062628, 36680.0236, + 39258.536828, 41797.194484, 43387.343436, 30807.029888, + 30951.07088, 32427.504696, 34156.010248, 36748.771988, + 30807.029888, 30951.07088, 32427.504696, 34156.010248, + 36748.771988, 37795.9182, 36779.657412, 39258.536828, + 41769.29456, 43331.547, 30626.976942, 30735.007686, + 32319.473952, 34156.010248, 36748.771988, 30626.976942, + 30735.007686, 32319.473952, 34156.010248, 36748.771988, + 37815.84428, 36779.657412, 39286.431634, 41825.090996, + 43359.446924, 30446.923996, 30518.944492, 32355.4842, + 34264.040992, 36820.789072, 30446.923996, 30518.944492, + 32355.4842, 34264.040992, 36820.789072, 37775.988708, + 36759.731332, 39286.431634, 41825.090996, 43359.446924, + 30230.859096, 30410.913748, 32319.473952, 34192.023908, + 36748.771988, 30230.859096, 30410.913748, 32319.473952, + 34192.023908, 36748.771988, 37775.988708, 36759.731332, + 39342.226364, 41825.090996, 43303.650488, 30194.85226, + 30194.85226, 32391.491036, 34156.010248, 36748.771988, + 30194.85226, 30194.85226, 32391.491036, 34156.010248, + 36748.771988, 37756.062628, 36779.657412, 39342.226364, + 41825.090996, 43359.446924}); + + // Grid values in long format, table 2, COP + compressor.perfGridValues.push_back( + {1.177126, 1.1393, 1.091664, 1.033858, 0.981755, 1.177126, 1.1393, 1.091664, + 1.033858, 0.981755, 1.134534, 1.106615, 1.04928, 0.989101, 0.946182, 1.228935, + 1.190326, 1.136507, 1.075244, 1.023802, 1.228935, 1.190326, 1.136507, 1.075244, + 1.023802, 1.174944, 1.147101, 1.087318, 1.026909, 0.980265, 1.324165, 1.27451, + 1.218468, 1.156182, 1.103823, 1.324165, 1.27451, 1.218468, 1.156182, 1.103823, + 1.267595, 1.236929, 1.165864, 1.102888, 1.052601, 1.415804, 1.365212, 1.301359, + 1.238022, 1.180586, 1.415804, 1.365212, 1.301359, 1.238022, 1.180586, 1.358408, + 1.324943, 1.249272, 1.179609, 1.128155, 1.510855, 1.453792, 1.381237, 1.31747, + 1.253435, 1.510855, 1.453792, 1.381237, 1.31747, 1.253435, 1.446639, 1.404653, + 1.331368, 1.249056, 1.195511, 1.60469, 1.540079, 1.462451, 1.39417, 1.326987, + 1.60469, 1.540079, 1.462451, 1.39417, 1.326987, 1.529924, 1.492107, 1.404944, + 1.325122, 1.265433, 1.690249, 1.630494, 1.539446, 1.464082, 1.395939, 1.690249, + 1.630494, 1.539446, 1.464082, 1.395939, 1.610302, 1.56761, 1.479273, 1.393118, + 1.33076, 1.776046, 1.715967, 1.624662, 1.540783, 1.469819, 1.776046, 1.715967, + 1.624662, 1.540783, 1.469819, 1.693465, 1.646725, 1.550545, 1.459601, 1.400666, + 1.865309, 1.797965, 1.703902, 1.612645, 1.539888, 1.865309, 1.797965, 1.703902, + 1.612645, 1.539888, 1.776866, 1.728095, 1.626829, 1.531743, 1.461555, 1.956837, + 1.881215, 1.777073, 1.690021, 1.603082, 1.956837, 1.881215, 1.777073, 1.690021, + 1.603082, 1.857512, 1.807899, 1.699347, 1.599321, 1.526899, 2.038891, 1.956496, + 1.848049, 1.757975, 1.668207, 2.038891, 1.956496, 1.848049, 1.757975, 1.668207, + 1.934721, 1.878022, 1.764777, 1.657606, 1.583414, 2.110576, 2.028182, 1.922739, + 1.818155, 1.725237, 2.110576, 2.028182, 1.922739, 1.818155, 1.725237, 1.997516, + 1.937435, 1.824625, 1.712596, 1.630169, 2.185899, 2.091178, 1.989083, 1.883087, + 1.786388, 2.185899, 2.091178, 1.989083, 1.883087, 1.786388, 2.06464, 2.003084, + 1.88041, 1.763428, 1.68041, 2.29337, 2.155411, 2.063354, 1.942635, 1.844204, + 2.29337, 2.155411, 2.063354, 1.942635, 1.844204, 2.125448, 2.061323, 1.933955, + 1.810339, 1.720612, 2.213555, 2.111682, 2.027503, 1.91515, 1.819817, 2.213555, + 2.111682, 2.027503, 1.91515, 1.819817, 2.126499, 2.064584, 1.935343, 1.818713, + 1.728239, 2.665142, 2.578088, 2.488506, 2.388206, 2.29651, 2.665142, 2.578088, + 2.488506, 2.388206, 2.29651, 2.46407, 2.344111, 2.198428, 2.057188, 1.96338, + 3.304405, 3.191319, 2.971384, 2.741049, 2.550691, 3.304405, 3.191319, 2.971384, + 2.741049, 2.550691, 2.763177, 2.673398, 2.356773, 2.216348, 2.116712, 3.827691, + 3.676371, 3.297085, 3.022338, 2.752997, 3.827691, 3.676371, 3.297085, 3.022338, + 2.752997, 2.871739, 2.890389, 2.434648, 2.292726, 2.205906, 3.989995, 3.834598, + 3.428313, 3.14185, 2.862486, 3.989995, 3.834598, 3.428313, 3.14185, 2.862486, + 2.871269, 2.900921, 2.506208, 2.358391, 2.270261, 4.147236, 3.992101, 3.570419, + 3.274967, 2.982684, 4.147236, 3.992101, 3.570419, 3.274967, 2.982684, 2.865266, + 2.913751, 2.578296, 2.428607, 2.341945, 4.322305, 4.172262, 3.73583, 3.419865, + 3.107421, 4.322305, 4.172262, 3.73583, 3.419865, 3.107421, 2.861677, 2.919962, + 2.65667, 2.504413, 2.414725, 4.518187, 4.354394, 3.889174, 3.578177, 3.248607, + 4.518187, 4.354394, 3.889174, 3.578177, 3.248607, 2.856905, 2.946338, 2.747649, + 2.589208, 2.493442, 4.481963, 4.349398, 3.979002, 3.671837, 3.346137, 4.481963, + 4.349398, 3.979002, 3.671837, 3.346137, 2.998141, 3.087098, 2.885335, 2.709619, + 2.616032, 4.445162, 4.359402, 4.046983, 3.755577, 3.437188, 4.445162, 4.359402, + 4.046983, 3.755577, 3.437188, 3.154067, 3.251216, 3.028152, 2.856705, 2.746669, + 4.402673, 4.359402, 4.112859, 3.858889, 3.546561, 4.402673, 4.359402, 4.112859, + 3.858889, 3.546561, 3.285629, 3.371232, 3.1449, 2.965763, 2.857289, 4.373341, + 4.359402, 4.19016, 3.947368, 3.65253, 4.373341, 4.359402, 4.19016, 3.947368, + 3.65253, 3.372955, 3.472057, 3.238544, 3.048902, 2.936122, 4.378394, 4.359402, + 4.218141, 3.993147, 3.717018, 4.378394, 4.359402, 4.218141, 3.993147, 3.717018, + 3.461548, 3.558644, 3.319824, 3.126817, 3.015709, 4.391304, 4.384616, 4.222841, + 3.993147, 3.713376, 4.391304, 4.384616, 4.222841, 3.993147, 3.713376, 3.538402, + 3.643836, 3.400556, 3.189995, 3.074423, 4.419242, 4.399884, 4.222841, 3.988941, + 3.706113, 4.419242, 4.399884, 4.222841, 3.988941, 3.706113, 3.616836, 3.721038, + 3.477882, 3.266644, 3.147565, 4.429573, 4.410122, 4.208773, 3.993147, 3.713376, + 4.429573, 4.410122, 4.208773, 3.993147, 3.713376, 3.613022, 3.710957, 3.477882, + 3.268826, 3.151618, 4.45679, 4.441125, 4.222841, 3.993147, 3.713376, 4.45679, + 4.441125, 4.222841, 3.993147, 3.713376, 3.611119, 3.710957, 3.475413, 3.264466, + 3.14959, 4.483146, 4.472566, 4.218141, 3.980557, 3.706113, 4.483146, 4.472566, + 4.218141, 3.980557, 3.706113, 3.614928, 3.712969, 3.475413, 3.264466, 3.14959, + 4.515188, 4.488454, 4.222841, 3.988941, 3.713376, 4.515188, 4.488454, 4.222841, + 3.988941, 3.713376, 3.614928, 3.712969, 3.470484, 3.264466, 3.153648, 4.522957, + 4.520572, 4.213452, 3.993147, 3.713376, 4.522957, 4.520572, 4.213452, 3.993147, + 3.713376, 3.616836, 3.710957, 3.470484, 3.264466, 3.14959}); + + // Set up regular grid interpolator. + Btwxt::GridAxis g0(compressor.perfGrid[0], + "TAir", + Btwxt::InterpolationMethod::linear, + Btwxt::ExtrapolationMethod::constant); + Btwxt::GridAxis g1(compressor.perfGrid[1], + "TOut", + Btwxt::InterpolationMethod::linear, + Btwxt::ExtrapolationMethod::constant); + Btwxt::GridAxis g2(compressor.perfGrid[2], + "TIn", + Btwxt::InterpolationMethod::linear, + Btwxt::ExtrapolationMethod::linear); + + std::vector gx {g0, g1, g2}; + + compressor.perfRGI = new Btwxt::RegularGridInterpolator(gx, compressor.perfGridValues); + compressor.useBtwxtGrid = true; + + compressor.secondaryHeatExchanger = {dF_TO_dC(10.), dF_TO_dC(15.), 27.}; + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + + else if (presetNum == MODELS_SANCO2_83 || presetNum == MODELS_SANCO2_GS3_45HPA_US_SP || + presetNum == MODELS_SANCO2_119) + { + setNumNodes(96); + setpoint_C = 65; + setpointFixed = true; + + if (presetNum == MODELS_SANCO2_119) + { + tankVolume_L = GAL_TO_L(119); + tankUA_kJperHrC = 9; + } + else + { + tankVolume_L = 315; + tankUA_kJperHrC = 7; + if (presetNum == MODELS_SANCO2_GS3_45HPA_US_SP) + { + tankSizeFixed = false; + } + } + + doTempDepression = false; + tankMixesOnDraw = false; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.minT = F_TO_C(-25.); + compressor.setCondensity({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = getNumNodes() - 1; + + 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; + compressor.isMultipass = false; + compressor.maxSetpoint_C = MAXOUTLET_R744; + + std::vector nodeWeights; + nodeWeights.emplace_back(8); + compressor.addTurnOnLogic(std::make_shared( + "eighth node absolute", nodeWeights, F_TO_C(113), this, true)); + if (presetNum == MODELS_SANCO2_83 || presetNum == MODELS_SANCO2_119) + { + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); + // Adds a bonus standby logic so the external heater does not cycle, recommended for any + // external heater with standby + std::vector nodeWeightStandby; + nodeWeightStandby.emplace_back(0); + compressor.standbyLogic = + std::make_shared("bottom node absolute", + nodeWeightStandby, + F_TO_C(113), + this, + true, + std::greater()); + } + // lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic( + std::make_shared("bottom node absolute", + nodeWeights1, + F_TO_C(135), + this, + true, + std::greater(), + true)); + compressor.depressesTemperature = false; // no temp depression + + // set everything in its place + heatSources.resize(1); + heatSources[0] = compressor; + } + else if (presetNum == MODELS_SANCO2_43) + { + setNumNodes(96); + setpoint_C = 65; + setpointFixed = true; + + tankVolume_L = 160; + // tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 5; + + doTempDepression = false; + tankMixesOnDraw = false; + + HeatSource compressor(this); + + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.minT = F_TO_C(-25.); + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = getIndexTopNode(); + + 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; + compressor.isMultipass = false; + compressor.maxSetpoint_C = MAXOUTLET_R744; + + std::vector nodeWeights; + nodeWeights.emplace_back(4); + std::vector nodeWeightStandby; + nodeWeightStandby.emplace_back(0); + compressor.addTurnOnLogic(std::make_shared( + "fourth node absolute", nodeWeights, F_TO_C(113), this, true)); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(8.2639))); + compressor.standbyLogic = + std::make_shared("bottom node absolute", + nodeWeightStandby, + F_TO_C(113), + this, + true, + std::greater()); + + // lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic( + std::make_shared("bottom twelfth absolute", + nodeWeights1, + F_TO_C(135), + this, + true, + std::greater(), + true)); + compressor.depressesTemperature = false; // no temp depression + + // set everything in its place + heatSources.resize(1); + heatSources[0] = compressor; + } + else if (presetNum == MODELS_AOSmithHPTU50 || presetNum == MODELS_RheemHBDR2250 || + presetNum == MODELS_RheemHBDR4550) + { + setNumNodes(24); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = 171; + tankUA_kJperHrC = 6; + + doTempDepression = false; + tankMixesOnDraw = true; + + 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(100))); + + std::vector nodeWeights; + nodeWeights.emplace_back(11); + nodeWeights.emplace_back(12); + resistiveElementTop.addTurnOnLogic(std::make_shared( + "top sixth absolute", nodeWeights, F_TO_C(105), this, true)); + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(28))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_AOSmithHPTU66 || presetNum == MODELS_RheemHBDR2265 || + presetNum == MODELS_RheemHBDR4565) + { + setNumNodes(24); + setpoint_C = F_TO_C(127.0); + + if (presetNum == MODELS_AOSmithHPTU66) + { + tankVolume_L = 244.6; + } + else + { + tankVolume_L = 221.4; + } + tankUA_kJperHrC = 8; + + doTempDepression = false; + tankMixesOnDraw = true; + + 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({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(100))); + + std::vector nodeWeights; + nodeWeights.emplace_back(11); + nodeWeights.emplace_back(12); + resistiveElementTop.addTurnOnLogic(std::make_shared( + "top sixth absolute", nodeWeights, F_TO_C(105), this, true)); + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(31))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_AOSmithHPTU80 || presetNum == MODELS_RheemHBDR2280 || + presetNum == MODELS_RheemHBDR4580) + { + setNumNodes(24); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = 299.5; + tankUA_kJperHrC = 9; + + doTempDepression = false; + tankMixesOnDraw = true; + + 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // double split = 1.0 / 3.0; + compressor.setCondensity({1., 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::bottomTwelfthMaxTemp(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(std::make_shared( + "top sixth absolute", nodeWeights, F_TO_C(105), this, true)); + // resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_AOSmithHPTU80_DR) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = 283.9; + tankUA_kJperHrC = 9; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.setCondensity({1., 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::bottomTwelfthMaxTemp(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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_AOSmithCAHP120) + { + setNumNodes(24); + setpoint_C = F_TO_C(150.0); + + tankVolume_L = GAL_TO_L(111.76); // AOSmith docs say 111.76 + tankUA_kJperHrC = UAf_TO_UAc(3.94); + + doTempDepression = false; + tankMixesOnDraw = false; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0.3, 0.3, 0.2, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}); + + // From CAHP 120 COP Tests + compressor.perfMap.reserve(3); + + // Tuned on the multiple K167 tests + compressor.perfMap.push_back({ + 50., // Temperature (T_F) + {2010.49966, -4.20966, 0.085395}, // Input Power Coefficients (inputPower_coeffs) + {5.91, -0.026299, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67.5, // Temperature (T_F) + {2171.012, -6.936571, 0.1094962}, // Input Power Coefficients (inputPower_coeffs) + {7.26272, -0.034135, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95., // Temperature (T_F) + {2276.0625, -7.106608, 0.119911}, // Input Power Coefficients (inputPower_coeffs) + {8.821262, -0.042059, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = + F_TO_C(47.0); // Product documentation says 45F doesn't look like it in CMP-T test// + compressor.maxT = F_TO_C(110.0); + compressor.hysteresis_dC = dF_TO_dC(2); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // top resistor values + double wattRE = 6000; // 5650.; + resistiveElementTop.setupAsResistiveElement(7, wattRE); + resistiveElementTop.isVIP = true; // VIP is the only source that turns on independently when + // something else is already heating. + + // bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, wattRE); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + resistiveElementBottom.setCondensity( + {0.2, 0.8, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}); // Based of CMP test + + // logic conditions for + double compStart = dF_TO_dC(5.25); + double standbyT = dF_TO_dC(5.); // Given CMP_T test + compressor.addTurnOnLogic(HPWH::secondSixth(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + double resistanceStart = 12.; + resistiveElementTop.addTurnOnLogic(HPWH::topThird(resistanceStart)); + resistiveElementBottom.addTurnOnLogic(HPWH::topThird(resistanceStart)); + + resistiveElementTop.addShutOffLogic(HPWH::fifthSixthMaxTemp(F_TO_C(117.))); + resistiveElementBottom.addShutOffLogic(HPWH::secondSixthMaxTemp(F_TO_C(109.))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + // heatSources[2].followedByHeatSource = &heatSources[1];; + + heatSources[0].companionHeatSource = &heatSources[1]; + heatSources[1].companionHeatSource = &heatSources[2]; + } + else if (MODELS_AOSmithHPTS50 <= presetNum && presetNum <= MODELS_AOSmithHPTS80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + if (presetNum == MODELS_AOSmithHPTS50) + { + tankVolume_L = GAL_TO_L(45.6); + tankUA_kJperHrC = 6.403; + } + else if (presetNum == MODELS_AOSmithHPTS66) + { + tankVolume_L = GAL_TO_L(67.63); + tankUA_kJperHrC = UAf_TO_UAc(1.5) * 6.403 / UAf_TO_UAc(1.16); + } + else if (presetNum == MODELS_AOSmithHPTS80) + { + tankVolume_L = GAL_TO_L(81.94); + tankUA_kJperHrC = UAf_TO_UAc(1.73) * 6.403 / UAf_TO_UAc(1.16); + } + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementTop(this); + HeatSource resistiveElementBottom(this); + + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0, 0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0}); + + // performance map + compressor.perfMap.reserve(3); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {66.82, 2.49, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {8.64, -0.0436, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67.5, // Temperature (T_F) + {85.1, 2.38, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {10.82, -0.0551, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {89, 2.62, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {12.52, -0.0534, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.); + compressor.maxT = F_TO_C(120.); + compressor.hysteresis_dC = dF_TO_dC(1.); + compressor.configuration = HeatSource::CONFIG_WRAPPED; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + resistiveElementTop.setupAsResistiveElement(8, 4500); + resistiveElementTop.isVIP = true; + + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(2); + + // logic conditions + double compStart = dF_TO_dC(30.2); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(11.87))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[2]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + + else if (presetNum == MODELS_GE2014STDMode) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_GE2014STDMode_80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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::bottomTwelfthMaxTemp(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))); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_GE2014) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(80))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_GE2014_80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(80))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_GE2014_80DR) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(80))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + // PRESET USING GE2014 DATA + else if (presetNum == MODELS_BWC2020_65) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(64); + tankUA_kJperHrC = 7.6; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(80))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + } + // If Rheem Premium + else if (MODELS_Rheem2020Prem40 <= presetNum && presetNum <= MODELS_Rheem2020Prem80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + if (presetNum == MODELS_Rheem2020Prem40) + { + tankVolume_L = GAL_TO_L(36.1); + tankUA_kJperHrC = 9.5; + } + else if (presetNum == MODELS_Rheem2020Prem50) + { + tankVolume_L = GAL_TO_L(45.1); + tankUA_kJperHrC = 8.55; + } + else if (presetNum == MODELS_Rheem2020Prem65) + { + tankVolume_L = GAL_TO_L(58.5); + tankUA_kJperHrC = 10.64; + } + else if (presetNum == MODELS_Rheem2020Prem80) + { + tankVolume_L = GAL_TO_L(72.0); + tankUA_kJperHrC = 10.83; + } + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0}); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {250, -1.0883, 0.0176}, // Input Power Coefficients (inputPower_coeffs) + {6.7, -0.0087, -0.0002} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {275.0, -0.6631, 0.01571}, // Input Power Coefficients (inputPower_coeffs) + {7.0, -0.0168, -0.0001} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.0); + compressor.hysteresis_dC = dF_TO_dC(1); + compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // top resistor values + resistiveElementTop.setupAsResistiveElement(8, 4500); + resistiveElementTop.isVIP = true; + + // bottom resistor values + resistiveElementBottom.setupAsResistiveElement(0, 4500); + resistiveElementBottom.hysteresis_dC = dF_TO_dC(4); + + // logic conditions + double compStart = dF_TO_dC(32); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(100))); + resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[1].backupHeatSource = &heatSources[2]; + heatSources[2].backupHeatSource = &heatSources[1]; + + heatSources[0].followedByHeatSource = &heatSources[2]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + + // If Rheem Build + else if (MODELS_Rheem2020Build40 <= presetNum && presetNum <= MODELS_Rheem2020Build80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + if (presetNum == MODELS_Rheem2020Build40) + { + tankVolume_L = GAL_TO_L(36.1); + tankUA_kJperHrC = 9.5; + } + else if (presetNum == MODELS_Rheem2020Build50) + { + tankVolume_L = GAL_TO_L(45.1); + tankUA_kJperHrC = 8.55; + } + else if (presetNum == MODELS_Rheem2020Build65) + { + tankVolume_L = GAL_TO_L(58.5); + tankUA_kJperHrC = 10.64; + } + else if (presetNum == MODELS_Rheem2020Build80) + { + tankVolume_L = GAL_TO_L(72.0); + tankUA_kJperHrC = 10.83; + } + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0}); + + compressor.perfMap.reserve(2); + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {220.0, 0.8743, 0.00454}, // Input Power Coefficients (inputPower_coeffs) + {7.96064, -0.0448, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {275.0, -0.6631, 0.01571}, // Input Power Coefficients (inputPower_coeffs) + {8.45936, -0.04539, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.hysteresis_dC = dF_TO_dC(1); + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.0); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.configuration = HPWH::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(4); + + // logic conditions + double compStart = dF_TO_dC(30); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(100))); + resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[1].backupHeatSource = &heatSources[2]; + heatSources[2].backupHeatSource = &heatSources[1]; + + heatSources[0].followedByHeatSource = &heatSources[2]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + else if (MODELS_RheemPlugInShared40 <= presetNum && presetNum <= MODELS_RheemPlugInShared80) + { + setNumNodes(12); + + if (presetNum == MODELS_RheemPlugInShared40) + { + tankVolume_L = GAL_TO_L(36.0); + tankUA_kJperHrC = 9.5; + setpoint_C = F_TO_C(140.0); + } + else if (presetNum == MODELS_RheemPlugInShared50) + { + tankVolume_L = GAL_TO_L(45.0); + tankUA_kJperHrC = 8.55; + setpoint_C = F_TO_C(140.0); + } + else if (presetNum == MODELS_RheemPlugInShared65) + { + tankVolume_L = GAL_TO_L(58.5); + tankUA_kJperHrC = 10.64; + setpoint_C = F_TO_C(127.0); + } + else if (presetNum == MODELS_RheemPlugInShared80) + { + tankVolume_L = GAL_TO_L(72.0); + tankUA_kJperHrC = 10.83; + setpoint_C = F_TO_C(127.0); + } + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0.2, 0.2, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 0, 0}); + + compressor.perfMap.reserve(2); + + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {250, -1.0883, 0.0176}, // Input Power Coefficients (inputPower_coeffs) + {6.7, -0.0087, -0.0002} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {275.0, -0.6631, 0.01571}, // Input Power Coefficients (inputPower_coeffs) + {7.0, -0.0168, -0.0001} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.0); + compressor.hysteresis_dC = dF_TO_dC(1); + compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // logic conditions + double compStart = dF_TO_dC(32); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + else if (presetNum == MODELS_RheemPlugInDedicated40 || + presetNum == MODELS_RheemPlugInDedicated50) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + if (presetNum == MODELS_RheemPlugInDedicated40) + { + tankVolume_L = GAL_TO_L(36); + tankUA_kJperHrC = 5.5; + } + else if (presetNum == MODELS_RheemPlugInDedicated50) + { + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.33; + } + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0.5, 0.5, 0.}); + + compressor.perfMap.reserve(2); + compressor.perfMap.push_back({ + 50, // Temperature (T_F) + {528.91, 4.8988, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {4.3943, -0.012443, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {494.03, 7.7266, 0.0}, // Input Power Coefficients (inputPower_coeffs) + {5.48189, -0.01604, 0.0} // COP Coefficients (COP_coeffs) + }); + + compressor.hysteresis_dC = dF_TO_dC(1); + compressor.minT = F_TO_C(37.0); + compressor.maxT = F_TO_C(120.0); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.configuration = HPWH::HeatSource::CONFIG_WRAPPED; + + // logic conditions + double compStart = dF_TO_dC(20); + double standbyT = dF_TO_dC(9); + compressor.addTurnOnLogic(HPWH::bottomThird(compStart)); + compressor.addTurnOnLogic(HPWH::standby(standbyT)); + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + else if (presetNum == MODELS_RheemHB50) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 7; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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.maxSetpoint_C = MAXOUTLET_R134A; + + 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::bottomTwelfthMaxTemp(F_TO_C(76.7747))); + + resistiveElementTop.addTurnOnLogic(HPWH::topSixth(dF_TO_dC(20.4167))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_Stiebel220E) + { + setNumNodes(12); + setpoint_C = F_TO_C(127); + + tankVolume_L = GAL_TO_L(56); + // tankUA_kJperHrC = 10; //0 to turn off + tankUA_kJperHrC = 9; + + doTempDepression = false; + tankMixesOnDraw = false; + + 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.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(6.5509))); + compressor.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(100))); + + compressor.depressesTemperature = false; // no temp depression + + // set everything in its places + heatSources.resize(2); + heatSources[0] = compressor; + heatSources[1] = resistiveElement; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[0].backupHeatSource = &heatSources[1]; + } + else if (presetNum == MODELS_Generic1) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 9; + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(110))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(35))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_Generic2) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 7.5; + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + HeatSource resistiveElementTop(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(F_TO_C(100))); + + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(40))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_Generic3) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(50); + tankUA_kJperHrC = 5; + + doTempDepression = false; + tankMixesOnDraw = true; + + // set everything in its places + HeatSource resistiveElementTop(this); + HeatSource compressor(this); + HeatSource resistiveElementBottom(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + compressor.setCondensity({1., 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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_UEF2generic) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource resistiveElementTop(this); + HeatSource resistiveElementBottom(this); + HeatSource compressor(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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::bottomTwelfthMaxTemp(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 + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + else if (MODELS_AWHSTier3Generic40 <= presetNum && presetNum <= MODELS_AWHSTier3Generic80) + { + setNumNodes(12); + setpoint_C = F_TO_C(127.0); + + if (presetNum == MODELS_AWHSTier3Generic40) + { + tankVolume_L = GAL_TO_L(36.1); + tankUA_kJperHrC = 5; + } + else if (presetNum == MODELS_AWHSTier3Generic50) + { + tankVolume_L = GAL_TO_L(45); + tankUA_kJperHrC = 6.5; + } + else if (presetNum == MODELS_AWHSTier3Generic65) + { + tankVolume_L = GAL_TO_L(64); + tankUA_kJperHrC = 7.6; + } + else if (presetNum == MODELS_AWHSTier3Generic80) + { + tankVolume_L = GAL_TO_L(75.4); + tankUA_kJperHrC = 10.; + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("Incorrect model specification. \n"); + } + return HPWH_ABORT; + } + + doTempDepression = false; + tankMixesOnDraw = true; + + HeatSource resistiveElementTop(this); + HeatSource resistiveElementBottom(this); + HeatSource compressor(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1., 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) + {5.22288834, -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) + {6.84694165, -0.0335265, 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; + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + // 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))); + resistiveElementBottom.addTurnOnLogic(HPWH::thirdSixth(dF_TO_dC(60))); + resistiveElementBottom.addShutOffLogic(HPWH::bottomTwelfthMaxTemp(F_TO_C(80))); + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + } + // If a the model is the TamOMatic, HotTam, Generic... This model is scalable. + else if (presetNum == MODELS_TamScalable_SP) + { + setNumNodes(24); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + canScale = true; // a fully scallable model + + doTempDepression = false; + tankMixesOnDraw = false; + + tankVolume_L = 315; + tankUA_kJperHrC = 7; + setTankSize_adjustUA(600., UNITS_GAL); + + HeatSource resistiveElementTop(this); + HeatSource resistiveElementBottom(this); + 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.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.isMultipass = false; + compressor.perfMap.reserve(1); + compressor.hysteresis_dC = 0; + + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = getIndexTopNode(); + + // Defrost Derate + compressor.setupDefrostMap(); + + // Perfmap for input power and COP made from data for poor preforming modeled to be scalled + // for this model + std::vector inputPwr_coeffs = {13.6, + 0.00995, + -0.0342, + -0.014, + -0.000110, + 0.00026, + 0.000232, + 0.000195, + -0.00034, + 5.30E-06, + 2.3600E-06}; + std::vector COP_coeffs = {1.945, + 0.0412, + -0.0112, + -0.00161, + 0.0000492, + 0.0000348, + -0.0000323, + -0.000166, + 0.0000112, + 0.0000392, + -3.52E-07}; + + compressor.perfMap.push_back({ + 105, // Temperature (T_F) + inputPwr_coeffs, // Input Power Coefficients (inputPower_coeffs + COP_coeffs // COP Coefficients (COP_coeffs) + }); + + // logic conditions + compressor.minT = F_TO_C(40.); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(15), this)); + + // lowT cutoff + std::vector nodeWeights1; + nodeWeights1.emplace_back(1); + compressor.addShutOffLogic(std::make_shared( + "bottom node", nodeWeights1, dF_TO_dC(15.), this, false, std::greater(), true)); + compressor.depressesTemperature = false; // no temp depression + + resistiveElementBottom.setupAsResistiveElement(0, 30000); + resistiveElementTop.setupAsResistiveElement(9, 30000); + + // top resistor values + // standard logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(15))); + resistiveElementTop.isVIP = true; + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_Scalable_MP) + { + setNumNodes(24); + setpoint_C = F_TO_C(135.0); + tankSizeFixed = false; + canScale = true; // a fully scallable model + + doTempDepression = false; + tankMixesOnDraw = false; + + tankVolume_L = 315; // Gets adjust per model but ratio between vol and UA is important + tankUA_kJperHrC = 7; + + HeatSource resistiveElementTop(this); + HeatSource resistiveElementBottom(this); + HeatSource compressor(this); + compressor.isOn = false; + compressor.isVIP = true; + compressor.typeOfHeatSource = TYPE_compressor; + compressor.setCondensity({0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}); + compressor.configuration = HeatSource::CONFIG_EXTERNAL; + compressor.perfMap.reserve(1); + compressor.hysteresis_dC = 0; + compressor.externalOutletHeight = 0; + compressor.externalInletHeight = static_cast(getNumNodes() / 3.) - 1; + + // logic conditions + std::vector nodeWeights; + nodeWeights.emplace_back(4); + compressor.addTurnOnLogic(std::make_shared( + "fourth node", nodeWeights, dF_TO_dC(5.), this, false)); + + std::vector nodeWeights1; + nodeWeights1.emplace_back(4); + compressor.addShutOffLogic(std::make_shared( + "fourth node", nodeWeights1, dF_TO_dC(0.), this, false, std::greater())); + compressor.depressesTemperature = false; // no temp depression + + // Defrost Derate + compressor.setupDefrostMap(); + + // logic conditions + compressor.minT = F_TO_C(40.); + compressor.maxT = F_TO_C(105.); + compressor.maxSetpoint_C = MAXOUTLET_R134A; + + setTankSize_adjustUA(600., UNITS_GAL); + compressor.mpFlowRate_LPS = GPM_TO_LPS(25.); + compressor.perfMap.push_back({ + 100, // Temperature (T_F) + + {12.4, 0.00739, -0.0410, 0.0, 0.000578, 0.0000696}, // Input Power Coefficients + // (inputPower_coeffs) + + {1.20, 0.0333, 0.00191, 0.000283, 0.0000496, -0.000440} // COP Coefficients (COP_coeffs) + + }); + + resistiveElementBottom.setupAsResistiveElement(0, 30000); + resistiveElementTop.setupAsResistiveElement(9, 30000); + + // top resistor values + // standard logic conditions + resistiveElementTop.addTurnOnLogic(HPWH::topThird(dF_TO_dC(30))); + resistiveElementTop.isVIP = true; + + // set everything in its places + heatSources.resize(3); + heatSources[0] = resistiveElementTop; + heatSources[1] = resistiveElementBottom; + heatSources[2] = compressor; + + // and you have to do this after putting them into heatSources, otherwise + // you don't get the right pointers + heatSources[2].backupHeatSource = &heatSources[1]; + heatSources[1].backupHeatSource = &heatSources[2]; + + heatSources[0].followedByHeatSource = &heatSources[1]; + heatSources[1].followedByHeatSource = &heatSources[2]; + + heatSources[0].companionHeatSource = &heatSources[2]; + } + else if (presetNum == MODELS_AquaThermAire) + { // AquaThermAire + setNumNodes(12); + setpoint_C = 50.; + + initialTankT_C = 49.32; + hasInitialTankTemp = true; + + tankVolume_L = GAL_TO_L(54.4); + tankUA_kJperHrC = 10.35; + + doTempDepression = false; + tankMixesOnDraw = false; + + // heat exchangers only + hasHeatExchanger = true; + heatExchangerEffectiveness = 0.93; + + HeatSource compressor(this); + + // compressor values + compressor.isOn = false; + compressor.isVIP = false; + compressor.typeOfHeatSource = TYPE_compressor; + + compressor.setCondensity({1.}); + + // AOSmithPHPT60 values + compressor.perfMap.reserve(4); + + compressor.perfMap.push_back({ + 5, // Temperature (T_F) + {-1423, 38.70, 0.}, // Input Power Coefficients (inputPower_coeffs) + {-0.13839, 0.012319, 0.} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 34, // Temperature (T_F) + {-1558, 42.40, 0.}, // Input Power Coefficients (inputPower_coeffs) + {-0.19375, 0.017247, 0.} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 67, // Temperature (T_F) + {-1713, 46.60, 0.}, // Input Power Coefficients (inputPower_coeffs) + {-0.28156, 0.025064, 0.} // COP Coefficients (COP_coeffs) + }); + + compressor.perfMap.push_back({ + 95, // Temperature (T_F) + {-1844, 50.17, 0.}, // Input Power Coefficients (inputPower_coeffs) + {-0.47273, 0.042082, 0.} // COP Coefficients (COP_coeffs) + }); + + compressor.minT = F_TO_C(-25); + compressor.maxT = F_TO_C(125.); + compressor.hysteresis_dC = dF_TO_dC(1); + compressor.configuration = HeatSource::CONFIG_SUBMERGED; + + // logic conditions + compressor.addTurnOnLogic(HPWH::wholeTank(111, UNITS_F, true)); + compressor.addTurnOnLogic(HPWH::standby(dF_TO_dC(14))); + + // set everything in its places + heatSources.resize(1); + heatSources[0] = compressor; + } + else + { + if (hpwhVerbosity >= VRB_reluctant) + { + msg("You have tried to select a preset model which does not exist. \n"); + } + return HPWH_ABORT; + } + + if (hasInitialTankTemp) + setTankToTemperature(initialTankT_C); + else // start tank off at setpoint + resetTankToSetpoint(); + + hpwhModel = presetNum; + + // calculate oft-used derived values + calcDerivedValues(); + + if (checkInputs() == HPWH_ABORT) + { + return HPWH_ABORT; + } + + isHeating = false; + for (int i = 0; i < getNumHeatSources(); i++) + { + if (heatSources[i].isOn) + { + isHeating = true; + } + heatSources[i].sortPerformanceMap(); + } + + if (hpwhVerbosity >= VRB_emetic) + { + for (int i = 0; i < getNumHeatSources(); i++) + { + msg("heat source %d: %p \n", i, &heatSources[i]); + } + msg("\n\n"); + } + + simHasFailed = false; + return 0; // successful init returns 0 +} // end HPWHinit_presets diff --git a/src/HPWHversion.cc b/src/HPWHversion.cc index 7bba6197..50d30efc 100644 --- a/src/HPWHversion.cc +++ b/src/HPWHversion.cc @@ -1,17 +1,18 @@ /* -* Version control -*/ + * Version control + */ #include "HPWHVersion.hh" #include "HPWH.hh" -//ugh, this should be in the header +// ugh, this should be in the header const std::string HPWH::version_maint = HPWHVRSN_META; -std::string HPWH::getVersion() { - std::stringstream version; +std::string HPWH::getVersion() +{ + std::stringstream version; - version << version_major << '.' << version_minor << '.' << version_patch << version_maint; + version << version_major << '.' << version_minor << '.' << version_patch << version_maint; - return version.str(); + return version.str(); } diff --git a/src/HPWHversion.in.hh b/src/HPWHversion.in.hh index 7adffdc4..b7a093d4 100644 --- a/src/HPWHversion.in.hh +++ b/src/HPWHversion.in.hh @@ -1,13 +1,15 @@ /* -* Version control -*/ + * Version control + */ #ifndef HPWHversion_hh #define HPWHversion_hh +// clang-format off #define HPWHVRSN_MAJOR @HPWHsim_VRSN_MAJOR@ #define HPWHVRSN_MINOR @HPWHsim_VRSN_MINOR@ #define HPWHVRSN_PATCH @HPWHsim_VRSN_PATCH@ #define HPWHVRSN_META "@HPWHsim_VRSN_META@" +// clang-format on #endif diff --git a/test/main.cc b/test/main.cc index 2444f3eb..057a4c0b 100644 --- a/test/main.cc +++ b/test/main.cc @@ -13,488 +13,586 @@ #include #include #include -#include -#include // std::max +#include +#include // std::max #define MAX_DIR_LENGTH 255 using std::cout; using std::endl; -using std::string; using std::ifstream; -//using std::ofstream; +using std::string; +// using std::ofstream; typedef std::vector schedule; -int readSchedule(schedule &scheduleArray, string scheduleFileName, long minutesOfTest); +int readSchedule(schedule& scheduleArray, string scheduleFileName, long minutesOfTest); -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { - HPWH hpwh; + HPWH hpwh; - HPWH::DRMODES drStatus = HPWH::DR_ALLOW; - HPWH::MODELS model; - //HPWH::CSVOPTIONS IP = HPWH::CSVOPT_IPUNITS; // CSVOPT_NONE or CSVOPT_IPUNITS - // HPWH::UNITS units = HPWH::UNITS_F; + HPWH::DRMODES drStatus = HPWH::DR_ALLOW; + HPWH::MODELS model; + // HPWH::CSVOPTIONS IP = HPWH::CSVOPT_IPUNITS; // CSVOPT_NONE or CSVOPT_IPUNITS + // HPWH::UNITS units = HPWH::UNITS_F; - const double EBALTHRESHOLD = 0.005; + const double EBALTHRESHOLD = 0.005; - const int nTestTCouples = 6; + const int nTestTCouples = 6; - const double soCMinTUse_C = F_TO_C(110.); - const double soCMains_C = F_TO_C(65.); + const double soCMinTUse_C = F_TO_C(110.); + const double soCMains_C = F_TO_C(65.); - // Schedule stuff - std::vector scheduleNames; - std::vector allSchedules(7); + // Schedule stuff + std::vector scheduleNames; + std::vector allSchedules(7); - string testDirectory, fileToOpen, fileToOpen2, scheduleName, var1, input1, input2, input3, inputFile, outputDirectory; - string inputVariableName, firstCol; - double testVal, newSetpoint, airTemp, airTemp2, tempDepressThresh, inletH, newTankSize, tot_limit, initialTankT_C; - bool useSoC; - int i, outputCode; - long minutesToRun; + string testDirectory, fileToOpen, fileToOpen2, scheduleName, var1, input1, input2, input3, + inputFile, outputDirectory; + string inputVariableName, firstCol; + double testVal, newSetpoint, airTemp, airTemp2, tempDepressThresh, inletH, newTankSize, + tot_limit, initialTankT_C; + bool useSoC; + int i, outputCode; + long minutesToRun; - double cumHeatIn[3] = { 0,0,0 }; - double cumHeatOut[3] = { 0,0,0 }; + double cumHeatIn[3] = {0, 0, 0}; + double cumHeatOut[3] = {0, 0, 0}; - bool HPWH_doTempDepress; - int doInvMix, doCondu; + bool HPWH_doTempDepress; + int doInvMix, doCondu; - FILE * outputFile = NULL; - FILE * yearOutFile = NULL; - ifstream controlFile; + FILE* outputFile = NULL; + FILE* yearOutFile = NULL; + ifstream controlFile; - string strPreamble; - string strHead = "minutes,Ta,Tsetpoint,inletT,draw,"; - string strHeadMP = "condenserInletT,condenserOutletT,externalVolGPM,"; - string strHeadSoC = "targetSoCFract,soCFract,"; + string strPreamble; + string strHead = "minutes,Ta,Tsetpoint,inletT,draw,"; + string strHeadMP = "condenserInletT,condenserOutletT,externalVolGPM,"; + string strHeadSoC = "targetSoCFract,soCFract,"; #if defined _DEBUG - hpwh.setVerbosity(HPWH::VRB_reluctant); + hpwh.setVerbosity(HPWH::VRB_reluctant); #endif - //....................................... - //process command line arguments - //....................................... - - cout << "Testing HPWHsim version " << HPWH::getVersion() << endl; - - //Obvious wrong number of command line arguments - if ((argc > 6)) { - cout << "Invalid input. This program takes FOUR arguments: model specification type (ie. Preset or File), model specification (ie. Sanden80), test name (ie. test50) and output directory\n"; - exit(1); - } - //Help message - if(argc > 1) { - input1 = argv[1]; - input2 = argv[2]; - input3 = argv[3]; - outputDirectory = argv[4]; - } else { - input1 = "asdf"; // Makes the next conditional not crash... a little clumsy but whatever - input2 = "def"; - input3 = "ghi"; - outputDirectory = "."; - } - if (argc < 5 || (argc > 6) || (input1 == "?") || (input1 == "help")) { - cout << "Standard usage: \"hpwhTestTool.x [model spec type Preset/File] [model spec Name] [testName] [airtemp override F (optional)]\"\n"; - cout << "All input files should be located in the test directory, with these names:\n"; - cout << "drawschedule.csv DRschedule.csv ambientTschedule.csv evaporatorTschedule.csv inletTschedule.csv hpwhProperties.csv\n"; - cout << "An output file, `modelname'Output.csv, will be written in the test directory\n"; - exit(1); - } - - if(argc == 6) { - airTemp = std::stoi(argv[5]); - HPWH_doTempDepress = true; - } else { - airTemp = 0; - HPWH_doTempDepress = false; - } - - //Only input file specified -- don't suffix with .csv - testDirectory = input3; - - // Parse the model - newSetpoint = 0; - if(input1 == "Preset") { - inputFile = ""; - - if (getHPWHObject(hpwh, input2) == HPWH::HPWH_ABORT) { - cout << "Error, preset model did not initialize.\n"; - exit(1); - } - - model = static_cast (hpwh.getHPWHModel()); - - if(model == HPWH::MODELS_Sanden80 || model == HPWH::MODELS_Sanden40) { - newSetpoint = (149 - 32) / 1.8; - } - } else if (input1 == "File") { - inputFile = input2 + ".txt"; - if (hpwh.HPWHinit_file(inputFile) != 0) exit(1); - } - else { - cout << "Invalid argument, received '"<< input1 << "', expected 'Preset' or 'File'.\n"; - exit(1); - } - -// hpwh.HPWHinit_resSwingTank(80., .95, 0., 10000., F_TO_C(125.)); - - // Use the built-in temperature depression for the lockout test. Set the temp depression of 4C to better - // try and trigger the lockout and hysteresis conditions - tempDepressThresh = 4; - hpwh.setMaxTempDepression(tempDepressThresh); - hpwh.setDoTempDepression(HPWH_doTempDepress); - - // Read the test control file - fileToOpen = testDirectory + "/" + "testInfo.txt"; - controlFile.open(fileToOpen.c_str()); - if(!controlFile.is_open()) { - cout << "Could not open control file " << fileToOpen << "\n"; - exit(1); - } - outputCode = 0; - minutesToRun = 0; - newSetpoint = 0.; - initialTankT_C = 0.; - doCondu = 1; - doInvMix = 1; - inletH = 0.; - newTankSize = 0.; - tot_limit = 0.; - useSoC = false; - bool hasInitialTankTemp = false; - cout << "Running: " << input2 << ", " << input1 << ", " << input3 << endl; - - while(controlFile >> var1 >> testVal) { - if(var1 == "setpoint") { // If a setpoint was specified then override the default - newSetpoint = testVal; + //....................................... + // process command line arguments + //....................................... + + cout << "Testing HPWHsim version " << HPWH::getVersion() << endl; + + // Obvious wrong number of command line arguments + if ((argc > 6)) + { + cout << "Invalid input. This program takes FOUR arguments: model specification type (ie. " + "Preset or File), model specification (ie. Sanden80), test name (ie. test50) and " + "output directory\n"; + exit(1); } - else if(var1 == "length_of_test") { - minutesToRun = (int) testVal; + // Help message + if (argc > 1) + { + input1 = argv[1]; + input2 = argv[2]; + input3 = argv[3]; + outputDirectory = argv[4]; + } + else + { + input1 = "asdf"; // Makes the next conditional not crash... a little clumsy but whatever + input2 = "def"; + input3 = "ghi"; + outputDirectory = "."; + } + if (argc < 5 || (argc > 6) || (input1 == "?") || (input1 == "help")) + { + cout << "Standard usage: \"hpwhTestTool.x [model spec type Preset/File] [model spec Name] " + "[testName] [airtemp override F (optional)]\"\n"; + cout << "All input files should be located in the test directory, with these names:\n"; + cout << "drawschedule.csv DRschedule.csv ambientTschedule.csv evaporatorTschedule.csv " + "inletTschedule.csv hpwhProperties.csv\n"; + cout << "An output file, `modelname'Output.csv, will be written in the test directory\n"; + exit(1); + } + + if (argc == 6) + { + airTemp = std::stoi(argv[5]); + HPWH_doTempDepress = true; + } + else + { + airTemp = 0; + HPWH_doTempDepress = false; + } + + // Only input file specified -- don't suffix with .csv + testDirectory = input3; + + // Parse the model + newSetpoint = 0; + if (input1 == "Preset") + { + inputFile = ""; + + if (getHPWHObject(hpwh, input2) == HPWH::HPWH_ABORT) + { + cout << "Error, preset model did not initialize.\n"; + exit(1); + } + + model = static_cast(hpwh.getHPWHModel()); + + if (model == HPWH::MODELS_Sanden80 || model == HPWH::MODELS_Sanden40) + { + newSetpoint = (149 - 32) / 1.8; + } } - else if(var1 == "doInversionMixing") { - doInvMix = (testVal > 0.0) ? 1 : 0; - } - else if(var1 == "doConduction") { - doCondu = (testVal > 0.0) ? 1 : 0; - } - else if(var1 == "inletH") { - inletH = testVal; - } - else if(var1 == "tanksize") { - newTankSize = testVal; - } - else if(var1 == "tot_limit") { - tot_limit = testVal; - } - else if(var1 == "useSoC") { - useSoC = (bool)testVal; - } - else if(var1 == "initialTankT_C") { // Initialize at this temperature instead of setpoint - initialTankT_C = testVal; - hasInitialTankTemp = true; + else if (input1 == "File") + { + inputFile = input2 + ".txt"; + if (hpwh.HPWHinit_file(inputFile) != 0) + exit(1); } - else { - cout << var1 << " in testInfo.txt is an unrecogized key.\n"; - } - } - - if(minutesToRun == 0) { - cout << "Error, must record length_of_test in testInfo.txt file\n"; - exit(1); - } - - // ------------------------------------- Read Schedules--------------------------------------- // - scheduleNames.push_back("inletT"); - scheduleNames.push_back("draw"); - scheduleNames.push_back("ambientT"); - scheduleNames.push_back("evaporatorT"); - scheduleNames.push_back("DR"); - scheduleNames.push_back("setpoint"); - scheduleNames.push_back("SoC"); - - for(i = 0; (unsigned)i < scheduleNames.size(); i++) { - fileToOpen = testDirectory + "/" + scheduleNames[i] + "schedule.csv"; - outputCode = readSchedule(allSchedules[i], fileToOpen, minutesToRun); - if(outputCode != 0) { - if (scheduleNames[i] != "setpoint" && scheduleNames[i] != "SoC") { - cout << "readSchedule returns an error on " << scheduleNames[i] << " schedule!\n"; - exit(1); - } - else { - outputCode = 0; - } + else + { + cout << "Invalid argument, received '" << input1 << "', expected 'Preset' or 'File'.\n"; + exit(1); } - } - - - if (doInvMix == 0) { - outputCode += hpwh.setDoInversionMixing(false); - } - - if (doCondu == 0) { - outputCode += hpwh.setDoConduction(false); - } - if (newSetpoint > 0) { - if (!allSchedules[5].empty()) { - hpwh.setSetpoint(allSchedules[5][0]); //expect this to fail sometimes - if (hasInitialTankTemp) - hpwh.setTankToTemperature(initialTankT_C); - else - hpwh.resetTankToSetpoint(); - } - else { - hpwh.setSetpoint(newSetpoint); - if (hasInitialTankTemp) - hpwh.setTankToTemperature(initialTankT_C); - else - hpwh.resetTankToSetpoint(); - } - } - if (inletH > 0) { - outputCode += hpwh.setInletByFraction(inletH); - } - if (newTankSize > 0) { - hpwh.setTankSize(newTankSize, HPWH::UNITS_GAL); - } - if (tot_limit > 0) { - outputCode += hpwh.setTimerLimitTOT(tot_limit); - } - if (useSoC) { - if (allSchedules[6].empty()) { - cout << "If useSoC is true need an SoCschedule.csv file \n"; - } - outputCode += hpwh.switchToSoCControls(1., .05, soCMinTUse_C, true, soCMains_C); - } - - if (outputCode != 0) { - cout << "Control file testInfo.txt has unsettable specifics in it. \n"; - exit(1); - } - - // ----------------------Open the Output Files and Print the Header---------------------------- // - - if (minutesToRun > 500000.) { - fileToOpen = outputDirectory + "/DHW_YRLY.csv"; - - if (fopen_s(&yearOutFile, fileToOpen.c_str(), "a+") != 0) { - cout << "Could not open output file " << fileToOpen << "\n"; - exit(1); - } - } - else { - fileToOpen = outputDirectory + "/" + input3 + "_" + input1 + "_" + input2 + ".csv"; - - if (fopen_s(&outputFile, fileToOpen.c_str(), "w+") != 0) { - cout << "Could not open output file " << fileToOpen << "\n"; - exit(1); - } - - string header = strHead; - if (hpwh.isCompressoExternalMultipass()) { - header += strHeadMP; - } - if(useSoC){ - header += strHeadSoC; - } - int csvOptions = HPWH::CSVOPT_NONE; - hpwh.WriteCSVHeading(outputFile, header.c_str(), nTestTCouples, csvOptions); - } - - // ------------------------------------- Simulate --------------------------------------- // - cout << "Now Simulating " << minutesToRun << " Minutes of the Test\n"; - - std::vector nodeExtraHeat_W; - std::vector* vectptr = NULL; - // Loop over the minutes in the test - for (i = 0; i < minutesToRun; i++) { + + // hpwh.HPWHinit_resSwingTank(80., .95, 0., 10000., F_TO_C(125.)); + + // Use the built-in temperature depression for the lockout test. Set the temp depression of 4C + // to better try and trigger the lockout and hysteresis conditions + tempDepressThresh = 4; + hpwh.setMaxTempDepression(tempDepressThresh); + hpwh.setDoTempDepression(HPWH_doTempDepress); + + // Read the test control file + fileToOpen = testDirectory + "/" + "testInfo.txt"; + controlFile.open(fileToOpen.c_str()); + if (!controlFile.is_open()) + { + cout << "Could not open control file " << fileToOpen << "\n"; + exit(1); + } + outputCode = 0; + minutesToRun = 0; + newSetpoint = 0.; + initialTankT_C = 0.; + doCondu = 1; + doInvMix = 1; + inletH = 0.; + newTankSize = 0.; + tot_limit = 0.; + useSoC = false; + bool hasInitialTankTemp = false; + cout << "Running: " << input2 << ", " << input1 << ", " << input3 << endl; + + while (controlFile >> var1 >> testVal) + { + if (var1 == "setpoint") + { // If a setpoint was specified then override the default + newSetpoint = testVal; + } + else if (var1 == "length_of_test") + { + minutesToRun = (int)testVal; + } + else if (var1 == "doInversionMixing") + { + doInvMix = (testVal > 0.0) ? 1 : 0; + } + else if (var1 == "doConduction") + { + doCondu = (testVal > 0.0) ? 1 : 0; + } + else if (var1 == "inletH") + { + inletH = testVal; + } + else if (var1 == "tanksize") + { + newTankSize = testVal; + } + else if (var1 == "tot_limit") + { + tot_limit = testVal; + } + else if (var1 == "useSoC") + { + useSoC = (bool)testVal; + } + else if (var1 == "initialTankT_C") + { // Initialize at this temperature instead of setpoint + initialTankT_C = testVal; + hasInitialTankTemp = true; + } + else + { + cout << var1 << " in testInfo.txt is an unrecogized key.\n"; + } + } + + if (minutesToRun == 0) + { + cout << "Error, must record length_of_test in testInfo.txt file\n"; + exit(1); + } + + // ------------------------------------- Read Schedules--------------------------------------- + // // + scheduleNames.push_back("inletT"); + scheduleNames.push_back("draw"); + scheduleNames.push_back("ambientT"); + scheduleNames.push_back("evaporatorT"); + scheduleNames.push_back("DR"); + scheduleNames.push_back("setpoint"); + scheduleNames.push_back("SoC"); + + for (i = 0; (unsigned)i < scheduleNames.size(); i++) + { + fileToOpen = testDirectory + "/" + scheduleNames[i] + "schedule.csv"; + outputCode = readSchedule(allSchedules[i], fileToOpen, minutesToRun); + if (outputCode != 0) + { + if (scheduleNames[i] != "setpoint" && scheduleNames[i] != "SoC") + { + cout << "readSchedule returns an error on " << scheduleNames[i] << " schedule!\n"; + exit(1); + } + else + { + outputCode = 0; + } + } + } + + if (doInvMix == 0) + { + outputCode += hpwh.setDoInversionMixing(false); + } + + if (doCondu == 0) + { + outputCode += hpwh.setDoConduction(false); + } + if (newSetpoint > 0) + { + if (!allSchedules[5].empty()) + { + hpwh.setSetpoint(allSchedules[5][0]); // expect this to fail sometimes + if (hasInitialTankTemp) + hpwh.setTankToTemperature(initialTankT_C); + else + hpwh.resetTankToSetpoint(); + } + else + { + hpwh.setSetpoint(newSetpoint); + if (hasInitialTankTemp) + hpwh.setTankToTemperature(initialTankT_C); + else + hpwh.resetTankToSetpoint(); + } + } + if (inletH > 0) + { + outputCode += hpwh.setInletByFraction(inletH); + } + if (newTankSize > 0) + { + hpwh.setTankSize(newTankSize, HPWH::UNITS_GAL); + } + if (tot_limit > 0) + { + outputCode += hpwh.setTimerLimitTOT(tot_limit); + } + if (useSoC) + { + if (allSchedules[6].empty()) + { + cout << "If useSoC is true need an SoCschedule.csv file \n"; + } + outputCode += hpwh.switchToSoCControls(1., .05, soCMinTUse_C, true, soCMains_C); + } + + if (outputCode != 0) + { + cout << "Control file testInfo.txt has unsettable specifics in it. \n"; + exit(1); + } + + // ----------------------Open the Output Files and Print the Header---------------------------- + // // + + if (minutesToRun > 500000.) + { + fileToOpen = outputDirectory + "/DHW_YRLY.csv"; + + if (fopen_s(&yearOutFile, fileToOpen.c_str(), "a+") != 0) + { + cout << "Could not open output file " << fileToOpen << "\n"; + exit(1); + } + } + else + { + fileToOpen = outputDirectory + "/" + input3 + "_" + input1 + "_" + input2 + ".csv"; + + if (fopen_s(&outputFile, fileToOpen.c_str(), "w+") != 0) + { + cout << "Could not open output file " << fileToOpen << "\n"; + exit(1); + } + + string header = strHead; + if (hpwh.isCompressoExternalMultipass()) + { + header += strHeadMP; + } + if (useSoC) + { + header += strHeadSoC; + } + int csvOptions = HPWH::CSVOPT_NONE; + hpwh.WriteCSVHeading(outputFile, header.c_str(), nTestTCouples, csvOptions); + } + + // ------------------------------------- Simulate --------------------------------------- // + cout << "Now Simulating " << minutesToRun << " Minutes of the Test\n"; + + std::vector nodeExtraHeat_W; + std::vector* vectptr = NULL; + // Loop over the minutes in the test + for (i = 0; i < minutesToRun; i++) + { #if defined _DEBUG && 0 - cout << "Now on minute: " << i << "\n"; + cout << "Now on minute: " << i << "\n"; #endif - if (HPWH_doTempDepress) { - airTemp2 = F_TO_C(airTemp); - } - else { - airTemp2 = allSchedules[2][i]; - } - - double tankHCStart = hpwh.getTankHeatContent_kJ(); - - // Process the dr status - drStatus = static_cast(int(allSchedules[4][i])); - - // Change setpoint if there is a setpoint schedule. - if (!allSchedules[5].empty() && !hpwh.isSetpointFixed()) { - hpwh.setSetpoint(allSchedules[5][i]); //expect this to fail sometimes - } - - // Change SoC schedule - if (useSoC) { - if (hpwh.setTargetSoCFraction(allSchedules[6][i]) != 0) { - cout << "ERROR: Can not set the target state of charge fraction. \n"; - exit(1); - } - } - - // Mix down for yearly tests with large compressors - if (hpwh.getHPWHModel() >= 210 && minutesToRun > 500000.) { - //Do a simple mix down of the draw for the cold water temperature - if (hpwh.getSetpoint() <= 125.) { - allSchedules[1][i] *= (125. - allSchedules[0][i]) / (hpwh.getTankNodeTemp(hpwh.getNumNodes() - 1, HPWH::UNITS_F) - allSchedules[0][i]); - } - } - - // 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. * GAL_TO_L(allSchedules[1][i]), allSchedules[0][i], - vectptr); - - if (!hpwh.isEnergyBalanced(GAL_TO_L(allSchedules[1][i]),allSchedules[0][i],tankHCStart,EBALTHRESHOLD)) { - cout << "WARNING: On minute " << i << " HPWH has an energy balance error.\n"; - } - - // Check timing - for (int iHS = 0; iHS < hpwh.getNumHeatSources(); iHS++) { - if (hpwh.getNthHeatSourceRunTime(iHS) > 1) { - cout << "ERROR: On minute " << i << " heat source " << iHS << " ran for " << hpwh.getNthHeatSourceRunTime(iHS) << "minutes" << "\n"; - exit(1); - } - } - // Check flow for external MP - if (hpwh.isCompressoExternalMultipass()) { - double volumeHeated_Gal = hpwh.getExternalVolumeHeated(HPWH::UNITS_GAL); - double mpFlowVolume_Gal = hpwh.getExternalMPFlowRate(HPWH::UNITS_GPM)*hpwh.getNthHeatSourceRunTime(hpwh.getCompressorIndex()); - if (fabs(volumeHeated_Gal - mpFlowVolume_Gal) > 0.000001) { - cout << "ERROR: Externally heated volumes are inconsistent! Volume Heated [Gal]: " << volumeHeated_Gal << ", mpFlowRate in 1 minute [Gal]: " - << mpFlowVolume_Gal << "\n"; - exit(1); - } - } - // Recording - if (minutesToRun < 500000.) { - // Copy current status into the output file - if (HPWH_doTempDepress) { - airTemp2 = hpwh.getLocationTemp_C(); - } - strPreamble = std::to_string(i) + ", " + std::to_string(airTemp2) + ", " + std::to_string(hpwh.getSetpoint()) + ", " + - std::to_string(allSchedules[0][i]) + ", " + std::to_string(allSchedules[1][i]) + ", "; - // Add some more outputs for mp tests - if (hpwh.isCompressoExternalMultipass()) { - strPreamble += std::to_string(hpwh.getCondenserWaterInletTemp()) + ", " + std::to_string(hpwh.getCondenserWaterOutletTemp()) + ", " + - std::to_string(hpwh.getExternalVolumeHeated(HPWH::UNITS_GAL)) + ", "; - } - if (useSoC) { - strPreamble += std::to_string(allSchedules[6][i]) + ", " + std::to_string(hpwh.getSoCFraction()) + ", "; - } - int csvOptions = HPWH::CSVOPT_NONE; - if (allSchedules[1][i] > 0.) { - csvOptions |= HPWH::CSVOPT_IS_DRAWING; - } - hpwh.WriteCSVRow(outputFile, strPreamble.c_str(), nTestTCouples, csvOptions); - } - else { - for (int iHS = 0; iHS < hpwh.getNumHeatSources(); iHS++) { - cumHeatIn[iHS] += hpwh.getNthHeatSourceEnergyInput(iHS, HPWH::UNITS_KWH)*1000.; - cumHeatOut[iHS] += hpwh.getNthHeatSourceEnergyOutput(iHS, HPWH::UNITS_KWH)*1000.; - } - } - } - - if (minutesToRun > 500000.) { - firstCol = input3 + "," + input1 + "," + input2; - fprintf(yearOutFile, "%s", firstCol.c_str()); - double totalIn = 0, totalOut = 0; - for (int iHS = 0; iHS < 3; iHS++) { - fprintf(yearOutFile, ",%0.0f,%0.0f", cumHeatIn[iHS], cumHeatOut[iHS]); - totalIn += cumHeatIn[iHS]; - totalOut += cumHeatOut[iHS]; - } - fprintf(yearOutFile, ",%0.0f,%0.0f", totalIn, totalOut); - for (int iHS = 0; iHS < 3; iHS++) { - fprintf(yearOutFile, ",%0.2f", cumHeatOut[iHS] /cumHeatIn[iHS]); - } - fprintf(yearOutFile, ",%0.2f", totalOut/totalIn); - fprintf(yearOutFile, "\n"); - fclose(yearOutFile); - } - else { - fclose(outputFile); - } - controlFile.close(); - - return 0; + if (HPWH_doTempDepress) + { + airTemp2 = F_TO_C(airTemp); + } + else + { + airTemp2 = allSchedules[2][i]; + } + + double tankHCStart = hpwh.getTankHeatContent_kJ(); + + // Process the dr status + drStatus = static_cast(int(allSchedules[4][i])); + + // Change setpoint if there is a setpoint schedule. + if (!allSchedules[5].empty() && !hpwh.isSetpointFixed()) + { + hpwh.setSetpoint(allSchedules[5][i]); // expect this to fail sometimes + } + + // Change SoC schedule + if (useSoC) + { + if (hpwh.setTargetSoCFraction(allSchedules[6][i]) != 0) + { + cout << "ERROR: Can not set the target state of charge fraction. \n"; + exit(1); + } + } + + // Mix down for yearly tests with large compressors + if (hpwh.getHPWHModel() >= 210 && minutesToRun > 500000.) + { + // Do a simple mix down of the draw for the cold water temperature + if (hpwh.getSetpoint() <= 125.) + { + allSchedules[1][i] *= (125. - allSchedules[0][i]) / + (hpwh.getTankNodeTemp(hpwh.getNumNodes() - 1, HPWH::UNITS_F) - + allSchedules[0][i]); + } + } + + // 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. * GAL_TO_L(allSchedules[1][i]), + allSchedules[0][i], + vectptr); + + if (!hpwh.isEnergyBalanced( + GAL_TO_L(allSchedules[1][i]), allSchedules[0][i], tankHCStart, EBALTHRESHOLD)) + { + cout << "WARNING: On minute " << i << " HPWH has an energy balance error.\n"; + } + + // Check timing + for (int iHS = 0; iHS < hpwh.getNumHeatSources(); iHS++) + { + if (hpwh.getNthHeatSourceRunTime(iHS) > 1) + { + cout << "ERROR: On minute " << i << " heat source " << iHS << " ran for " + << hpwh.getNthHeatSourceRunTime(iHS) << "minutes" + << "\n"; + exit(1); + } + } + // Check flow for external MP + if (hpwh.isCompressoExternalMultipass()) + { + double volumeHeated_Gal = hpwh.getExternalVolumeHeated(HPWH::UNITS_GAL); + double mpFlowVolume_Gal = hpwh.getExternalMPFlowRate(HPWH::UNITS_GPM) * + hpwh.getNthHeatSourceRunTime(hpwh.getCompressorIndex()); + if (fabs(volumeHeated_Gal - mpFlowVolume_Gal) > 0.000001) + { + cout << "ERROR: Externally heated volumes are inconsistent! Volume Heated [Gal]: " + << volumeHeated_Gal << ", mpFlowRate in 1 minute [Gal]: " << mpFlowVolume_Gal + << "\n"; + exit(1); + } + } + // Recording + if (minutesToRun < 500000.) + { + // Copy current status into the output file + if (HPWH_doTempDepress) + { + airTemp2 = hpwh.getLocationTemp_C(); + } + strPreamble = std::to_string(i) + ", " + std::to_string(airTemp2) + ", " + + std::to_string(hpwh.getSetpoint()) + ", " + + std::to_string(allSchedules[0][i]) + ", " + + std::to_string(allSchedules[1][i]) + ", "; + // Add some more outputs for mp tests + if (hpwh.isCompressoExternalMultipass()) + { + strPreamble += std::to_string(hpwh.getCondenserWaterInletTemp()) + ", " + + std::to_string(hpwh.getCondenserWaterOutletTemp()) + ", " + + std::to_string(hpwh.getExternalVolumeHeated(HPWH::UNITS_GAL)) + ", "; + } + if (useSoC) + { + strPreamble += std::to_string(allSchedules[6][i]) + ", " + + std::to_string(hpwh.getSoCFraction()) + ", "; + } + int csvOptions = HPWH::CSVOPT_NONE; + if (allSchedules[1][i] > 0.) + { + csvOptions |= HPWH::CSVOPT_IS_DRAWING; + } + hpwh.WriteCSVRow(outputFile, strPreamble.c_str(), nTestTCouples, csvOptions); + } + else + { + for (int iHS = 0; iHS < hpwh.getNumHeatSources(); iHS++) + { + cumHeatIn[iHS] += hpwh.getNthHeatSourceEnergyInput(iHS, HPWH::UNITS_KWH) * 1000.; + cumHeatOut[iHS] += hpwh.getNthHeatSourceEnergyOutput(iHS, HPWH::UNITS_KWH) * 1000.; + } + } + } + + if (minutesToRun > 500000.) + { + firstCol = input3 + "," + input1 + "," + input2; + fprintf(yearOutFile, "%s", firstCol.c_str()); + double totalIn = 0, totalOut = 0; + for (int iHS = 0; iHS < 3; iHS++) + { + fprintf(yearOutFile, ",%0.0f,%0.0f", cumHeatIn[iHS], cumHeatOut[iHS]); + totalIn += cumHeatIn[iHS]; + totalOut += cumHeatOut[iHS]; + } + fprintf(yearOutFile, ",%0.0f,%0.0f", totalIn, totalOut); + for (int iHS = 0; iHS < 3; iHS++) + { + fprintf(yearOutFile, ",%0.2f", cumHeatOut[iHS] / cumHeatIn[iHS]); + } + fprintf(yearOutFile, ",%0.2f", totalOut / totalIn); + fprintf(yearOutFile, "\n"); + fclose(yearOutFile); + } + else + { + fclose(outputFile); + } + controlFile.close(); + return 0; } +// this function reads the named schedule into the provided array +int readSchedule(schedule& scheduleArray, string scheduleFileName, long minutesOfTest) +{ + int minuteHrTmp; + bool hourInput; + string line, snippet, s, minORhr; + double valTmp; + ifstream inputFile(scheduleFileName.c_str()); + // open the schedule file provided + cout << "Opening " << scheduleFileName << '\n'; + + if (!inputFile.is_open()) + { + return 1; + } + inputFile >> snippet >> valTmp; + // cout << "snippet " << snippet << " valTmp"<< valTmp<<'\n'; -// this function reads the named schedule into the provided array -int readSchedule(schedule &scheduleArray, string scheduleFileName, long minutesOfTest) { - int minuteHrTmp; - bool hourInput; - string line, snippet, s, minORhr; - double valTmp; - ifstream inputFile(scheduleFileName.c_str()); - //open the schedule file provided - cout << "Opening " << scheduleFileName << '\n'; - - if(!inputFile.is_open()) { - return 1; - } - - inputFile >> snippet >> valTmp; - // cout << "snippet " << snippet << " valTmp"<< valTmp<<'\n'; - - if(snippet != "default") { - cout << "First line of " << scheduleFileName << " must specify default\n"; - return 1; - } - // cout << valTmp << " minutes = " << minutesOfTest << "\n"; - - // Fill with the default value - scheduleArray.assign(minutesOfTest, valTmp); - - // Burn the first two lines - std::getline(inputFile, line); - std::getline(inputFile, line); - - std::stringstream ss(line); // Will parse with a stringstream - // Grab the first token, which is the minute or hour marker - ss >> minORhr; - if (minORhr.empty() ) { // If nothing left in the file - return 0; - } - hourInput = tolower(minORhr.at(0)) == 'h'; - char c; // to eat the commas nom nom - // Read all the exceptions to the default value - while (inputFile >> minuteHrTmp >> c >> valTmp) { - - if (minuteHrTmp >= (int)scheduleArray.size()) { - cout << "In " << scheduleFileName << " the input file has more minutes than the test was defined with\n"; - return 1; - } - // Update the value - if (!hourInput) { - scheduleArray[minuteHrTmp] = valTmp; - } - else if (hourInput) { - for (int j = minuteHrTmp * 60; j < (minuteHrTmp+1) * 60; j++) { - scheduleArray[j] = valTmp; - //cout << "minute " << j-(minuteHrTmp) * 60 << " of hour" << (minuteHrTmp)<<"\n"; - } - } - } - - inputFile.close(); - - return 0; - -} \ No newline at end of file + if (snippet != "default") + { + cout << "First line of " << scheduleFileName << " must specify default\n"; + return 1; + } + // cout << valTmp << " minutes = " << minutesOfTest << "\n"; + + // Fill with the default value + scheduleArray.assign(minutesOfTest, valTmp); + + // Burn the first two lines + std::getline(inputFile, line); + std::getline(inputFile, line); + + std::stringstream ss(line); // Will parse with a stringstream + // Grab the first token, which is the minute or hour marker + ss >> minORhr; + if (minORhr.empty()) + { // If nothing left in the file + return 0; + } + hourInput = tolower(minORhr.at(0)) == 'h'; + char c; // to eat the commas nom nom + // Read all the exceptions to the default value + while (inputFile >> minuteHrTmp >> c >> valTmp) + { + + if (minuteHrTmp >= (int)scheduleArray.size()) + { + cout << "In " << scheduleFileName + << " the input file has more minutes than the test was defined with\n"; + return 1; + } + // Update the value + if (!hourInput) + { + scheduleArray[minuteHrTmp] = valTmp; + } + else if (hourInput) + { + for (int j = minuteHrTmp * 60; j < (minuteHrTmp + 1) * 60; j++) + { + scheduleArray[j] = valTmp; + // cout << "minute " << j-(minuteHrTmp) * 60 << " of hour" << (minuteHrTmp)<<"\n"; + } + } + } + + inputFile.close(); + + return 0; +} diff --git a/test/testCompressorFcts.cc b/test/testCompressorFcts.cc index ab13578b..c3aff1e1 100644 --- a/test/testCompressorFcts.cc +++ b/test/testCompressorFcts.cc @@ -14,75 +14,141 @@ using std::cout; using std::string; - - -void testHasACompressor(string input, bool expected) { - HPWH hpwh; - getHPWHObject(hpwh, input); // get preset model - ASSERTTRUE(hpwh.hasACompressor() == expected); +void testHasACompressor(string input, bool expected) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); // get preset model + ASSERTTRUE(hpwh.hasACompressor() == expected); } -void testGetCompCoilConfig(string input, int expected) { - HPWH hpwh; - getHPWHObject(hpwh, input); // get preset model - ASSERTTRUE(hpwh.getCompressorCoilConfig() == expected); +void testGetCompCoilConfig(string input, int expected) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); // get preset model + ASSERTTRUE(hpwh.getCompressorCoilConfig() == expected); } -void testIsCompressorMultipass(string input, bool expected) { - HPWH hpwh; - getHPWHObject(hpwh, input); // get preset model - ASSERTTRUE(hpwh.isCompressorMultipass() == expected); +void testIsCompressorMultipass(string input, bool expected) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); // get preset model + ASSERTTRUE(hpwh.isCompressorMultipass() == expected); } -void testIsCompressoExternalMultipass(string input, bool expected) { - HPWH hpwh; - getHPWHObject(hpwh, input); // get preset model - ASSERTTRUE(hpwh.isCompressoExternalMultipass() == expected); +void testIsCompressoExternalMultipass(string input, bool expected) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); // get preset model + ASSERTTRUE(hpwh.isCompressoExternalMultipass() == expected); } -void testGetMaxCompressorSetpoint(string input, double expected) { - HPWH hpwh; - getHPWHObject(hpwh, input); // get preset model - ASSERTTRUE(hpwh.getMaxCompressorSetpoint() == expected); +void testGetMaxCompressorSetpoint(string input, double expected) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); // get preset model + ASSERTTRUE(hpwh.getMaxCompressorSetpoint() == expected); } -void testGetMinOperatingTemp(string input, double expected) { - HPWH hpwh; - getHPWHObject(hpwh, input); // get preset model - ASSERTTRUE(hpwh.getMinOperatingTemp(HPWH::UNITS_F) == expected); +void testGetMinOperatingTemp(string input, double expected) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); // get preset model + ASSERTTRUE(hpwh.getMinOperatingTemp(HPWH::UNITS_F) == expected); } int main(int, char*) { - const int length = 14; - const string models [length] = {"AOSmithHPTU50", "Stiebel220e","AOSmithCAHP120", \ - "Sanden80", "ColmacCxV_5_SP", "ColmacCxA_20_SP", \ - "TamScalable_SP", "restankRealistic", "StorageTank", \ - "ColmacCxA_20_MP", "Scalable_MP", "NyleC90A_MP", "NyleC90A_C_MP", \ - "QAHV_N136TAU_HPB_SP"}; - const int hasComp[length] = { true, true, true, true, true, true, true, false, false, true, true, true, true, true }; - const int coilConfig[length] = { 1, 1, 1, 2, 2, 2, 2, HPWH::HPWH_ABORT, HPWH::HPWH_ABORT, 2, 2, 2, 2, 2}; // 1 Wrapped 2 External - const int heatCycle[length] = { true, true, true, false, false, false, false, HPWH::HPWH_ABORT, HPWH::HPWH_ABORT, true, true, true, true, false }; //true single, false singlepass - const int isExtMP[length] = { false, false, false, false, false, false, false, HPWH::HPWH_ABORT, HPWH::HPWH_ABORT, true, true, true, true, false }; - - const double maxStpt[length] = { HPWH::MAXOUTLET_R134A, HPWH::MAXOUTLET_R134A,HPWH::MAXOUTLET_R134A, - HPWH::MAXOUTLET_R744, HPWH::MAXOUTLET_R410A, HPWH::MAXOUTLET_R134A, - HPWH::MAXOUTLET_R134A, HPWH::HPWH_ABORT, HPWH::HPWH_ABORT, - HPWH::MAXOUTLET_R134A, HPWH::MAXOUTLET_R134A, F_TO_C(160.), F_TO_C(160.), F_TO_C(176.1) }; // deg C - - const double minTemp[length] = { 42., 32., 47., -25, -4., 40., 40., HPWH::HPWH_ABORT, HPWH::HPWH_ABORT, 40., 40., 40., 35., -13.}; //deg F - - for (int i = 0; i < length; i++) { - testHasACompressor(models[i], hasComp[i]); - testGetCompCoilConfig(models[i], coilConfig[i]); - testIsCompressorMultipass(models[i], heatCycle[i]); - testIsCompressoExternalMultipass(models[i], isExtMP[i]); - - testGetMaxCompressorSetpoint(models[i], maxStpt[i]); - testGetMinOperatingTemp(models[i], minTemp[i]); - } - - //Made it through the gauntlet - return 0; + const int length = 14; + const string models[length] = {"AOSmithHPTU50", + "Stiebel220e", + "AOSmithCAHP120", + "Sanden80", + "ColmacCxV_5_SP", + "ColmacCxA_20_SP", + "TamScalable_SP", + "restankRealistic", + "StorageTank", + "ColmacCxA_20_MP", + "Scalable_MP", + "NyleC90A_MP", + "NyleC90A_C_MP", + "QAHV_N136TAU_HPB_SP"}; + const int hasComp[length] = { + true, true, true, true, true, true, true, false, false, true, true, true, true, true}; + const int coilConfig[length] = { + 1, 1, 1, 2, 2, 2, 2, HPWH::HPWH_ABORT, HPWH::HPWH_ABORT, 2, 2, 2, 2, 2}; // 1 Wrapped 2 + // External + const int heatCycle[length] = {true, + true, + true, + false, + false, + false, + false, + HPWH::HPWH_ABORT, + HPWH::HPWH_ABORT, + true, + true, + true, + true, + false}; // true single, false singlepass + const int isExtMP[length] = {false, + false, + false, + false, + false, + false, + false, + HPWH::HPWH_ABORT, + HPWH::HPWH_ABORT, + true, + true, + true, + true, + false}; + + const double maxStpt[length] = {HPWH::MAXOUTLET_R134A, + HPWH::MAXOUTLET_R134A, + HPWH::MAXOUTLET_R134A, + HPWH::MAXOUTLET_R744, + HPWH::MAXOUTLET_R410A, + HPWH::MAXOUTLET_R134A, + HPWH::MAXOUTLET_R134A, + HPWH::HPWH_ABORT, + HPWH::HPWH_ABORT, + HPWH::MAXOUTLET_R134A, + HPWH::MAXOUTLET_R134A, + F_TO_C(160.), + F_TO_C(160.), + F_TO_C(176.1)}; // deg C + + const double minTemp[length] = {42., + 32., + 47., + -25, + -4., + 40., + 40., + HPWH::HPWH_ABORT, + HPWH::HPWH_ABORT, + 40., + 40., + 40., + 35., + -13.}; // deg F + + for (int i = 0; i < length; i++) + { + testHasACompressor(models[i], hasComp[i]); + testGetCompCoilConfig(models[i], coilConfig[i]); + testIsCompressorMultipass(models[i], heatCycle[i]); + testIsCompressoExternalMultipass(models[i], isExtMP[i]); + + testGetMaxCompressorSetpoint(models[i], maxStpt[i]); + testGetMinOperatingTemp(models[i], minTemp[i]); + } + + // Made it through the gauntlet + return 0; } diff --git a/test/testEnergyBalance.cc b/test/testEnergyBalance.cc index 9257ef1d..0ddc1669 100644 --- a/test/testEnergyBalance.cc +++ b/test/testEnergyBalance.cc @@ -5,74 +5,77 @@ #include "testUtilityFcts.cc" #include -#include +#include /* Test energy balance for AOSmithHPTS50*/ -void testEnergyBalanceAOSmithHPTS50() { +void testEnergyBalanceAOSmithHPTS50() +{ - HPWH hpwh; - getHPWHObject(hpwh, "AOSmithHPTS50"); + HPWH hpwh; + getHPWHObject(hpwh, "AOSmithHPTS50"); - double maxDrawVol_L = 1.; - const double ambientT_C = 20.; - const double externalT_C = 20.; + double maxDrawVol_L = 1.; + const double ambientT_C = 20.; + const double externalT_C = 20.; - // - hpwh.setTankToTemperature(20.); - hpwh.setInletT(5.); + // + hpwh.setTankToTemperature(20.); + hpwh.setInletT(5.); - const double Pi = 4. * atan(1.); - double testDuration_min = 60.; - for(int i_min = 0; i_min < testDuration_min; ++i_min) - { - double t_min = static_cast(i_min); + const double Pi = 4. * atan(1.); + double testDuration_min = 60.; + for (int i_min = 0; i_min < testDuration_min; ++i_min) + { + double t_min = static_cast(i_min); - double flowFac = sin(Pi * t_min / testDuration_min) - 0.5; - flowFac += fabs(flowFac); // semi-sinusoidal flow profile - double drawVol_L = flowFac * maxDrawVol_L; + double flowFac = sin(Pi * t_min / testDuration_min) - 0.5; + flowFac += fabs(flowFac); // semi-sinusoidal flow profile + double drawVol_L = flowFac * maxDrawVol_L; - double prevHeatContent_kJ = hpwh.getTankHeatContent_kJ(); - hpwh.runOneStep(drawVol_L, ambientT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(hpwh.isEnergyBalanced(drawVol_L,prevHeatContent_kJ,1.e-6)); - } + double prevHeatContent_kJ = hpwh.getTankHeatContent_kJ(); + hpwh.runOneStep(drawVol_L, ambientT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(hpwh.isEnergyBalanced(drawVol_L, prevHeatContent_kJ, 1.e-6)); + } } /* Test energy balance for storage tank with extra heat (solar)*/ -void testEnergyBalanceSolar() { - - HPWH hpwh; - getHPWHObject(hpwh, "StorageTank"); - - double maxDrawVol_L = 1.; - const double ambientT_C = 20.; - const double externalT_C = 20.; - - std::vector nodePowerExtra_W = {1000.}; - // - hpwh.setUA(0.); - hpwh.setTankToTemperature(20.); - hpwh.setInletT(5.); - - const double Pi = 4. * atan(1.); - double testDuration_min = 60.; - for(int i_min = 0; i_min < testDuration_min; ++i_min) - { - double t_min = static_cast(i_min); - - double flowFac = sin(Pi * t_min / testDuration_min) - 0.5; - flowFac += fabs(flowFac); // semi-sinusoidal flow profile - double drawVol_L = flowFac * maxDrawVol_L; - - double prevHeatContent_kJ = hpwh.getTankHeatContent_kJ(); - hpwh.runOneStep(drawVol_L, ambientT_C, externalT_C, HPWH::DR_ALLOW,0.,0.,&nodePowerExtra_W); - ASSERTTRUE(hpwh.isEnergyBalanced(drawVol_L,prevHeatContent_kJ,1.e-6)); - } +void testEnergyBalanceSolar() +{ + + HPWH hpwh; + getHPWHObject(hpwh, "StorageTank"); + + double maxDrawVol_L = 1.; + const double ambientT_C = 20.; + const double externalT_C = 20.; + + std::vector nodePowerExtra_W = {1000.}; + // + hpwh.setUA(0.); + hpwh.setTankToTemperature(20.); + hpwh.setInletT(5.); + + const double Pi = 4. * atan(1.); + double testDuration_min = 60.; + for (int i_min = 0; i_min < testDuration_min; ++i_min) + { + double t_min = static_cast(i_min); + + double flowFac = sin(Pi * t_min / testDuration_min) - 0.5; + flowFac += fabs(flowFac); // semi-sinusoidal flow profile + double drawVol_L = flowFac * maxDrawVol_L; + + double prevHeatContent_kJ = hpwh.getTankHeatContent_kJ(); + hpwh.runOneStep( + drawVol_L, ambientT_C, externalT_C, HPWH::DR_ALLOW, 0., 0., &nodePowerExtra_W); + ASSERTTRUE(hpwh.isEnergyBalanced(drawVol_L, prevHeatContent_kJ, 1.e-6)); + } } int main(int, char*) { - testEnergyBalanceAOSmithHPTS50(); - testEnergyBalanceSolar(); - - return 0; + testEnergyBalanceAOSmithHPTS50(); + testEnergyBalanceSolar(); + + return 0; } diff --git a/test/testHeatingLogics.cc b/test/testHeatingLogics.cc index 24705fd0..2603675f 100644 --- a/test/testHeatingLogics.cc +++ b/test/testHeatingLogics.cc @@ -2,7 +2,7 @@ #include "testUtilityFcts.cc" #include -#include +#include #include using std::cout; @@ -22,275 +22,331 @@ void testSetStateOfCharge(string& input); void testSetStateOfCharge(string& input, double coldWater_F, double minTUse_F, double tankTAt76SoC); void testExtraHeat(); -const std::vector hasHighShuttOffVectSP = { "Sanden80", "QAHV_N136TAU_HPB_SP", \ - "ColmacCxA_20_SP", "ColmacCxV_5_SP", "NyleC60A_SP", "NyleC60A_C_SP", "NyleC185A_C_SP", "TamScalable_SP" }; - -const std::vector noHighShuttOffVectMPExternal = {"Scalable_MP", "ColmacCxA_20_MP", "ColmacCxA_15_MP", \ - "ColmacCxV_5_MP", "NyleC90A_MP", "NyleC90A_C_MP" , "NyleC250A_MP", "NyleC250A_C_MP" }; - -const std::vector noHighShuttOffVectIntegrated = { "AOSmithHPTU80", "Rheem2020Build80", \ - "Stiebel220e", "AOSmithCAHP120", "AWHSTier3Generic80", "Generic1", "RheemPlugInDedicated50", \ - "RheemPlugInShared65", "restankRealistic", "StorageTank"}; - -int main(int, char*) { - double tempsForSetSoC[5][3] = { {49, 99, 125}, {65, 110, 129}, {32, 120, 121}, {32, 33, 121}, {80, 81, 132.5} }; - - for (string hpwhStr : hasHighShuttOffVectSP) { - testHasEnteringWaterShutOff(hpwhStr); - testSetEnteringWaterShuffOffOutOfBoundsIndex(hpwhStr); - testSetEnteringWaterShuffOffDeadbandToSmall(hpwhStr); - testSetEnteringWaterHighTempShutOffAbsolute(hpwhStr); - testSetEnteringWaterHighTempShutOffRelative(hpwhStr); - - testChangeToStateofChargeControlled(hpwhStr); - for (int i = 0; i < 5; i++) { - testSetStateOfCharge(hpwhStr, tempsForSetSoC[i][0], tempsForSetSoC[i][1], tempsForSetSoC[i][2]); - } - } - - for (string hpwhStr : noHighShuttOffVectMPExternal) { - testDoesNotHaveEnteringWaterShutOff(hpwhStr); - testCanNotSetEnteringWaterShutOff(hpwhStr); - - testChangeToStateofChargeControlled(hpwhStr); - for (int i = 0; i < 5; i++) { - testSetStateOfCharge(hpwhStr, tempsForSetSoC[i][0], tempsForSetSoC[i][1], tempsForSetSoC[i][2]); - } - } - - for (string hpwhStr : noHighShuttOffVectIntegrated) { - testDoesNotHaveEnteringWaterShutOff(hpwhStr); - testCanNotSetEnteringWaterShutOff(hpwhStr); - } - - testExtraHeat(); - return 0; +const std::vector hasHighShuttOffVectSP = {"Sanden80", + "QAHV_N136TAU_HPB_SP", + "ColmacCxA_20_SP", + "ColmacCxV_5_SP", + "NyleC60A_SP", + "NyleC60A_C_SP", + "NyleC185A_C_SP", + "TamScalable_SP"}; + +const std::vector noHighShuttOffVectMPExternal = {"Scalable_MP", + "ColmacCxA_20_MP", + "ColmacCxA_15_MP", + "ColmacCxV_5_MP", + "NyleC90A_MP", + "NyleC90A_C_MP", + "NyleC250A_MP", + "NyleC250A_C_MP"}; + +const std::vector noHighShuttOffVectIntegrated = {"AOSmithHPTU80", + "Rheem2020Build80", + "Stiebel220e", + "AOSmithCAHP120", + "AWHSTier3Generic80", + "Generic1", + "RheemPlugInDedicated50", + "RheemPlugInShared65", + "restankRealistic", + "StorageTank"}; + +int main(int, char*) +{ + double tempsForSetSoC[5][3] = { + {49, 99, 125}, {65, 110, 129}, {32, 120, 121}, {32, 33, 121}, {80, 81, 132.5}}; + + for (string hpwhStr : hasHighShuttOffVectSP) + { + testHasEnteringWaterShutOff(hpwhStr); + testSetEnteringWaterShuffOffOutOfBoundsIndex(hpwhStr); + testSetEnteringWaterShuffOffDeadbandToSmall(hpwhStr); + testSetEnteringWaterHighTempShutOffAbsolute(hpwhStr); + testSetEnteringWaterHighTempShutOffRelative(hpwhStr); + + testChangeToStateofChargeControlled(hpwhStr); + for (int i = 0; i < 5; i++) + { + testSetStateOfCharge( + hpwhStr, tempsForSetSoC[i][0], tempsForSetSoC[i][1], tempsForSetSoC[i][2]); + } + } + + for (string hpwhStr : noHighShuttOffVectMPExternal) + { + testDoesNotHaveEnteringWaterShutOff(hpwhStr); + testCanNotSetEnteringWaterShutOff(hpwhStr); + + testChangeToStateofChargeControlled(hpwhStr); + for (int i = 0; i < 5; i++) + { + testSetStateOfCharge( + hpwhStr, tempsForSetSoC[i][0], tempsForSetSoC[i][1], tempsForSetSoC[i][2]); + } + } + + for (string hpwhStr : noHighShuttOffVectIntegrated) + { + testDoesNotHaveEnteringWaterShutOff(hpwhStr); + testCanNotSetEnteringWaterShutOff(hpwhStr); + } + + testExtraHeat(); + return 0; } -void testHasEnteringWaterShutOff(string& input) { - HPWH hpwh; - getHPWHObject(hpwh, input); - int index = hpwh.getCompressorIndex() == -1 ? 0 : hpwh.getCompressorIndex(); - ASSERTTRUE(hpwh.hasEnteringWaterHighTempShutOff(index) == true); +void testHasEnteringWaterShutOff(string& input) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); + int index = hpwh.getCompressorIndex() == -1 ? 0 : hpwh.getCompressorIndex(); + ASSERTTRUE(hpwh.hasEnteringWaterHighTempShutOff(index) == true); } -void testDoesNotHaveEnteringWaterShutOff(string& input) { - HPWH hpwh; - getHPWHObject(hpwh, input); - int index = hpwh.getCompressorIndex() == -1 ? 0 : hpwh.getCompressorIndex(); - ASSERTTRUE(hpwh.hasEnteringWaterHighTempShutOff(index) == false); +void testDoesNotHaveEnteringWaterShutOff(string& input) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); + int index = hpwh.getCompressorIndex() == -1 ? 0 : hpwh.getCompressorIndex(); + ASSERTTRUE(hpwh.hasEnteringWaterHighTempShutOff(index) == false); } -void testCanNotSetEnteringWaterShutOff(string& input) { - HPWH hpwh; - getHPWHObject(hpwh, input); - int index = hpwh.getCompressorIndex() == -1 ? 0 : hpwh.getCompressorIndex(); - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(10., false, index) == HPWH::HPWH_ABORT); +void testCanNotSetEnteringWaterShutOff(string& input) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); + int index = hpwh.getCompressorIndex() == -1 ? 0 : hpwh.getCompressorIndex(); + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(10., false, index) == HPWH::HPWH_ABORT); } -void testSetEnteringWaterShuffOffOutOfBoundsIndex(string& input) { - HPWH hpwh; - getHPWHObject(hpwh, input); - int index = -1; - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(10., false, index) == HPWH::HPWH_ABORT); - index = 15; - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(10., false, index) == HPWH::HPWH_ABORT); +void testSetEnteringWaterShuffOffOutOfBoundsIndex(string& input) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); + int index = -1; + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(10., false, index) == HPWH::HPWH_ABORT); + index = 15; + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(10., false, index) == HPWH::HPWH_ABORT); } -void testSetEnteringWaterShuffOffDeadbandToSmall(string& input) { - HPWH hpwh; - getHPWHObject(hpwh, input); +void testSetEnteringWaterShuffOffDeadbandToSmall(string& input) +{ + HPWH hpwh; + getHPWHObject(hpwh, input); - double value = HPWH::MINSINGLEPASSLIFT - 1.; - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, false, hpwh.getCompressorIndex()) == HPWH::HPWH_ABORT); - - value = HPWH::MINSINGLEPASSLIFT; - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, false, hpwh.getCompressorIndex()) == 0); + double value = HPWH::MINSINGLEPASSLIFT - 1.; + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, false, hpwh.getCompressorIndex()) == + HPWH::HPWH_ABORT); - value = hpwh.getSetpoint() - (HPWH::MINSINGLEPASSLIFT-1); - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, true, hpwh.getCompressorIndex()) == HPWH::HPWH_ABORT); + value = HPWH::MINSINGLEPASSLIFT; + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, false, hpwh.getCompressorIndex()) == 0); - value = hpwh.getSetpoint() - HPWH::MINSINGLEPASSLIFT; - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, true, hpwh.getCompressorIndex()) == 0); + value = hpwh.getSetpoint() - (HPWH::MINSINGLEPASSLIFT - 1); + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, true, hpwh.getCompressorIndex()) == + HPWH::HPWH_ABORT); + + value = hpwh.getSetpoint() - HPWH::MINSINGLEPASSLIFT; + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(value, true, hpwh.getCompressorIndex()) == 0); } -/* Tests we can set the entering water high temp shutt off with aboslute values and it works turns off and on as expected. */ -void testSetEnteringWaterHighTempShutOffAbsolute(string& input) { - const double drawVolume_L = 0.; - const double externalT_C = 20.; - const double delta = 2.; - const double highT_C = 20.; - const bool doAbsolute = true; - HPWH hpwh; - getHPWHObject(hpwh, input); - - // make tank cold to force on - hpwh.setTankToTemperature(highT_C); - - // run a step and check we're heating - hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(compressorIsRunning(hpwh)); - - // change entering water temp to below temp - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(highT_C - delta, doAbsolute, hpwh.getCompressorIndex()) == 0); - - // run a step and check we're not heating. - hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTFALSE(compressorIsRunning(hpwh)); - - // and reverse it - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(highT_C + delta, doAbsolute, hpwh.getCompressorIndex()) == 0); - hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(compressorIsRunning(hpwh)); +/* Tests we can set the entering water high temp shutt off with aboslute values and it works turns + * off and on as expected. */ +void testSetEnteringWaterHighTempShutOffAbsolute(string& input) +{ + const double drawVolume_L = 0.; + const double externalT_C = 20.; + const double delta = 2.; + const double highT_C = 20.; + const bool doAbsolute = true; + HPWH hpwh; + getHPWHObject(hpwh, input); + + // make tank cold to force on + hpwh.setTankToTemperature(highT_C); + + // run a step and check we're heating + hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(compressorIsRunning(hpwh)); + + // change entering water temp to below temp + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff( + highT_C - delta, doAbsolute, hpwh.getCompressorIndex()) == 0); + + // run a step and check we're not heating. + hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTFALSE(compressorIsRunning(hpwh)); + + // and reverse it + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff( + highT_C + delta, doAbsolute, hpwh.getCompressorIndex()) == 0); + hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(compressorIsRunning(hpwh)); } -/* Tests we can set the entering water high temp shutt off with relative values and it works turns off and on as expected. */ -void testSetEnteringWaterHighTempShutOffRelative(string& input) { - const double drawVolume_L = 0.; - const double externalT_C = 20.; - const double delta = 2.; - const double highT_C = 20.; - const bool doAbsolute = false; - HPWH hpwh; - getHPWHObject(hpwh, input); - - const double relativeHighT_C = hpwh.getSetpoint() - highT_C; - // make tank cold to force on - hpwh.setTankToTemperature(highT_C); - - // run a step and check we're heating - hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()) == 1); - - // change entering water temp to below temp - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(relativeHighT_C + delta, doAbsolute, hpwh.getCompressorIndex()) == 0); - - // run a step and check we're not heating. - hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()) == 0); - - // and reverse it - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(relativeHighT_C - delta, doAbsolute, hpwh.getCompressorIndex()) == 0); - hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()) == 1); +/* Tests we can set the entering water high temp shutt off with relative values and it works turns + * off and on as expected. */ +void testSetEnteringWaterHighTempShutOffRelative(string& input) +{ + const double drawVolume_L = 0.; + const double externalT_C = 20.; + const double delta = 2.; + const double highT_C = 20.; + const bool doAbsolute = false; + HPWH hpwh; + getHPWHObject(hpwh, input); + + const double relativeHighT_C = hpwh.getSetpoint() - highT_C; + // make tank cold to force on + hpwh.setTankToTemperature(highT_C); + + // run a step and check we're heating + hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()) == 1); + + // change entering water temp to below temp + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff( + relativeHighT_C + delta, doAbsolute, hpwh.getCompressorIndex()) == 0); + + // run a step and check we're not heating. + hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()) == 0); + + // and reverse it + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff( + relativeHighT_C - delta, doAbsolute, hpwh.getCompressorIndex()) == 0); + hpwh.runOneStep(highT_C, drawVolume_L, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()) == 1); } /* Test we can switch and the controls change as expected*/ -void testChangeToStateofChargeControlled(string& input) { - HPWH hpwh; - const double externalT_C = 20.; - const double setpointT_C = F_TO_C(149.); - - getHPWHObject(hpwh, input); - const bool originalHasHighTempShutOff = hpwh.hasEnteringWaterHighTempShutOff(hpwh.getCompressorIndex()); - if (!hpwh.isSetpointFixed()) { - double temp; - string tempStr; - if (!hpwh.isNewSetpointPossible(setpointT_C, temp, tempStr)) { - return; // Numbers don't aline for this type - } - hpwh.setSetpoint(setpointT_C); - } - - //Just created so should be fasle - ASSERTFALSE(hpwh.isSoCControlled()); - - // change to SOC control; - hpwh.switchToSoCControls(.76, .05, 99, true, 49, HPWH::UNITS_F); - ASSERTTRUE(hpwh.isSoCControlled()); - - // check entering water high temp shut off controll unchanged - ASSERTTRUE(hpwh.hasEnteringWaterHighTempShutOff(hpwh.getCompressorIndex()) == originalHasHighTempShutOff); - - // Test we can change the SoC and run a step and check we're heating - if (hpwh.hasEnteringWaterHighTempShutOff(hpwh.getCompressorIndex())) { - ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff(setpointT_C-HPWH::MINSINGLEPASSLIFT, true, hpwh.getCompressorIndex()) == 0); // Force to ignore this part. - } - ASSERTTRUE(hpwh.setTankToTemperature(F_TO_C(100.)) == 0); // .51 - hpwh.runOneStep( 0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(compressorIsRunning(hpwh)); - - // Test if we're on and in band stay on - hpwh.setTankToTemperature(F_TO_C(125)); // .76 (current target) - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(compressorIsRunning(hpwh)); - - // Test we can change the SoC and turn off - hpwh.setTankToTemperature(F_TO_C(133)); // .84 - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTFALSE(compressorIsRunning(hpwh)); - - // Test if off and in band stay off - hpwh.setTankToTemperature(F_TO_C(125)); // .76 (current target) - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTFALSE(compressorIsRunning(hpwh)); +void testChangeToStateofChargeControlled(string& input) +{ + HPWH hpwh; + const double externalT_C = 20.; + const double setpointT_C = F_TO_C(149.); + + getHPWHObject(hpwh, input); + const bool originalHasHighTempShutOff = + hpwh.hasEnteringWaterHighTempShutOff(hpwh.getCompressorIndex()); + if (!hpwh.isSetpointFixed()) + { + double temp; + string tempStr; + if (!hpwh.isNewSetpointPossible(setpointT_C, temp, tempStr)) + { + return; // Numbers don't aline for this type + } + hpwh.setSetpoint(setpointT_C); + } + + // Just created so should be fasle + ASSERTFALSE(hpwh.isSoCControlled()); + + // change to SOC control; + hpwh.switchToSoCControls(.76, .05, 99, true, 49, HPWH::UNITS_F); + ASSERTTRUE(hpwh.isSoCControlled()); + + // check entering water high temp shut off controll unchanged + ASSERTTRUE(hpwh.hasEnteringWaterHighTempShutOff(hpwh.getCompressorIndex()) == + originalHasHighTempShutOff); + + // Test we can change the SoC and run a step and check we're heating + if (hpwh.hasEnteringWaterHighTempShutOff(hpwh.getCompressorIndex())) + { + ASSERTTRUE(hpwh.setEnteringWaterHighTempShutOff( + setpointT_C - HPWH::MINSINGLEPASSLIFT, true, hpwh.getCompressorIndex()) == + 0); // Force to ignore this part. + } + ASSERTTRUE(hpwh.setTankToTemperature(F_TO_C(100.)) == 0); // .51 + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(compressorIsRunning(hpwh)); + + // Test if we're on and in band stay on + hpwh.setTankToTemperature(F_TO_C(125)); // .76 (current target) + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(compressorIsRunning(hpwh)); + + // Test we can change the SoC and turn off + hpwh.setTankToTemperature(F_TO_C(133)); // .84 + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTFALSE(compressorIsRunning(hpwh)); + + // Test if off and in band stay off + hpwh.setTankToTemperature(F_TO_C(125)); // .76 (current target) + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTFALSE(compressorIsRunning(hpwh)); } /*Test we can change the target and turn off and on*/ -void testSetStateOfCharge(string& input, double coldWater_F, double minTUse_F, double tankTAt76SoC) { - HPWH hpwh; - const double externalT_C = 20.; - const double setpointT_C = F_TO_C(149.); - - getHPWHObject(hpwh, input); - if (!hpwh.isSetpointFixed()) { - double temp; - string tempStr; - if (!hpwh.isNewSetpointPossible(setpointT_C, temp, tempStr)) { - return; // Numbers don't aline for this type - } - hpwh.setSetpoint(setpointT_C); - } - - // change to SOC control; - hpwh.switchToSoCControls(.85, .05, minTUse_F, true, coldWater_F, HPWH::UNITS_F); - ASSERTTRUE(hpwh.isSoCControlled()); - - // Test if we're on and in band stay on - hpwh.setTankToTemperature(F_TO_C(tankTAt76SoC)); // .76 - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTTRUE(compressorIsRunning(hpwh)); - - // Test we can change the target SoC and turn off - hpwh.setTargetSoCFraction(0.5); - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTFALSE(compressorIsRunning(hpwh)); - - // Change back in the band and stay turned off - hpwh.setTargetSoCFraction(0.72); - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTFALSE(compressorIsRunning(hpwh)); - - // Drop below the band and turn on - hpwh.setTargetSoCFraction(0.70); - hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); - ASSERTFALSE(compressorIsRunning(hpwh)); +void testSetStateOfCharge(string& input, double coldWater_F, double minTUse_F, double tankTAt76SoC) +{ + HPWH hpwh; + const double externalT_C = 20.; + const double setpointT_C = F_TO_C(149.); + + getHPWHObject(hpwh, input); + if (!hpwh.isSetpointFixed()) + { + double temp; + string tempStr; + if (!hpwh.isNewSetpointPossible(setpointT_C, temp, tempStr)) + { + return; // Numbers don't aline for this type + } + hpwh.setSetpoint(setpointT_C); + } + + // change to SOC control; + hpwh.switchToSoCControls(.85, .05, minTUse_F, true, coldWater_F, HPWH::UNITS_F); + ASSERTTRUE(hpwh.isSoCControlled()); + + // Test if we're on and in band stay on + hpwh.setTankToTemperature(F_TO_C(tankTAt76SoC)); // .76 + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTTRUE(compressorIsRunning(hpwh)); + + // Test we can change the target SoC and turn off + hpwh.setTargetSoCFraction(0.5); + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTFALSE(compressorIsRunning(hpwh)); + + // Change back in the band and stay turned off + hpwh.setTargetSoCFraction(0.72); + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTFALSE(compressorIsRunning(hpwh)); + + // Drop below the band and turn on + hpwh.setTargetSoCFraction(0.70); + hpwh.runOneStep(0, externalT_C, externalT_C, HPWH::DR_ALLOW); + ASSERTFALSE(compressorIsRunning(hpwh)); } /*Test adding extra heat to a tank for one minute*/ -void testExtraHeat() { - HPWH hpwh; - getHPWHObject(hpwh, "StorageTank"); +void testExtraHeat() +{ + HPWH hpwh; + getHPWHObject(hpwh, "StorageTank"); - const double ambientT_C = 20.; - const double externalT_C = 20.; - const double inletVol2_L = 0.; - const double inletT2_C = 0.; + const double ambientT_C = 20.; + const double externalT_C = 20.; + const double inletVol2_L = 0.; + const double inletT2_C = 0.; - double extraPower_W = 1000.; - std::vector nodePowerExtra_W = {extraPower_W}; + double extraPower_W = 1000.; + std::vector nodePowerExtra_W = {extraPower_W}; - // - hpwh.setUA(0.); - hpwh.setTankToTemperature(20.); + // + hpwh.setUA(0.); + hpwh.setTankToTemperature(20.); - double Q_init = hpwh.getTankHeatContent_kJ(); - hpwh.runOneStep(0, ambientT_C, externalT_C, HPWH::DR_LOC, inletVol2_L, inletT2_C, &nodePowerExtra_W); - double Q_final = hpwh.getTankHeatContent_kJ(); + double Q_init = hpwh.getTankHeatContent_kJ(); + hpwh.runOneStep( + 0, ambientT_C, externalT_C, HPWH::DR_LOC, inletVol2_L, inletT2_C, &nodePowerExtra_W); + double Q_final = hpwh.getTankHeatContent_kJ(); - double dQ_actual_kJ = (Q_final - Q_init); + double dQ_actual_kJ = (Q_final - Q_init); - double dQ_expected_kJ = extraPower_W * 60. / 1.e3; // 1 min + double dQ_expected_kJ = extraPower_W * 60. / 1.e3; // 1 min - ASSERTTRUE(cmpd(dQ_actual_kJ, dQ_expected_kJ)); -} \ No newline at end of file + ASSERTTRUE(cmpd(dQ_actual_kJ, dQ_expected_kJ)); +} diff --git a/test/testMaxSetpoint.cc b/test/testMaxSetpoint.cc index 2414387d..2ec8c138 100644 --- a/test/testMaxSetpoint.cc +++ b/test/testMaxSetpoint.cc @@ -9,13 +9,11 @@ #include "testUtilityFcts.cc" #include -#include - +#include using std::cout; using std::string; - void testMaxSetpointResistanceTank(); void testScalableCompressor(); void testJustR134ACompressor(); @@ -31,263 +29,276 @@ const double REMaxShouldBe = 100.; int main(int, char*) { - testMaxSetpointResistanceTank(); - testScalableCompressor(); - testJustR134ACompressor(); - testJustR410ACompressor(); - testJustQAHVCompressor(); - testHybridModel(); - testStorageTankSetpoint(); - testSetpointFixed(); - testResampling(); - testSetTankTemps(); - - //Made it through the gauntlet - return 0; + testMaxSetpointResistanceTank(); + testScalableCompressor(); + testJustR134ACompressor(); + testJustR410ACompressor(); + testJustQAHVCompressor(); + testHybridModel(); + testStorageTankSetpoint(); + testSetpointFixed(); + testResampling(); + testSetTankTemps(); + + // Made it through the gauntlet + return 0; } -void testMaxSetpointResistanceTank() { - HPWH hpwh; - double num; - string why; - hpwh.HPWHinit_resTank(); - - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(100., num, why)); // Can go to boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(10., num, why)); // Can go low, albiet dumb - ASSERTTRUE(REMaxShouldBe == num); - - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(99.) == 0) +void testMaxSetpointResistanceTank() +{ + HPWH hpwh; + double num; + string why; + hpwh.HPWHinit_resTank(); + + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(100., num, why)); // Can go to boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(10., num, why)); // Can go low, albiet dumb + ASSERTTRUE(REMaxShouldBe == num); + + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(99.) == 0) } -void testScalableCompressor() { - HPWH hpwh; +void testScalableCompressor() +{ + HPWH hpwh; - string input = "TamScalable_SP"; // Just a compressor with R134A - double num; - string why; + string input = "TamScalable_SP"; // Just a compressor with R134A + double num; + string why; - getHPWHObject(hpwh, input); + getHPWHObject(hpwh, input); - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(60., num, why)); // Can go to normal - ASSERTTRUE(hpwh.isNewSetpointPossible(100, num, why)); // Can go to programed max + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(60., num, why)); // Can go to normal + ASSERTTRUE(hpwh.isNewSetpointPossible(100, num, why)); // Can go to programed max - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(50.) == 0) + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(50.) == 0) } -void testJustR134ACompressor() { - HPWH hpwh; +void testJustR134ACompressor() +{ + HPWH hpwh; - string input = "NyleC90A_SP"; // Just a compressor with R134A - double num; - string why; + string input = "NyleC90A_SP"; // Just a compressor with R134A + double num; + string why; - getHPWHObject(hpwh, input); + getHPWHObject(hpwh, input); - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling - ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling - ASSERTTRUE(HPWH::MAXOUTLET_R134A == num); //Assert we're getting the right number - ASSERTTRUE(hpwh.isNewSetpointPossible(60., num, why)); // Can go to normal - ASSERTTRUE(hpwh.isNewSetpointPossible(HPWH::MAXOUTLET_R134A, num, why)); // Can go to programed max + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling + ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling + ASSERTTRUE(HPWH::MAXOUTLET_R134A == num); // Assert we're getting the right number + ASSERTTRUE(hpwh.isNewSetpointPossible(60., num, why)); // Can go to normal + ASSERTTRUE( + hpwh.isNewSetpointPossible(HPWH::MAXOUTLET_R134A, num, why)); // Can go to programed max - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(50.) == 0) + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(50.) == 0) } -void testJustR410ACompressor() { - HPWH hpwh; - string input = "ColmacCxV_5_SP"; // Just a compressor with R410A - double num; - string why; - - getHPWHObject(hpwh, input); - - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling - ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling - ASSERTTRUE(HPWH::MAXOUTLET_R410A == num); //Assert we're getting the right number - ASSERTTRUE(hpwh.isNewSetpointPossible(50., num, why)); // Can go to normal - ASSERTTRUE(hpwh.isNewSetpointPossible(HPWH::MAXOUTLET_R410A, num, why)); // Can go to programed max - - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(50.) == 0) +void testJustR410ACompressor() +{ + HPWH hpwh; + string input = "ColmacCxV_5_SP"; // Just a compressor with R410A + double num; + string why; + + getHPWHObject(hpwh, input); + + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling + ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling + ASSERTTRUE(HPWH::MAXOUTLET_R410A == num); // Assert we're getting the right number + ASSERTTRUE(hpwh.isNewSetpointPossible(50., num, why)); // Can go to normal + ASSERTTRUE( + hpwh.isNewSetpointPossible(HPWH::MAXOUTLET_R410A, num, why)); // Can go to programed max + + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(50.) == 0) } -void testJustQAHVCompressor() { - HPWH hpwh; - string input = "QAHV_N136TAU_HPB_SP"; - double num; - string why; +void testJustQAHVCompressor() +{ + HPWH hpwh; + string input = "QAHV_N136TAU_HPB_SP"; + double num; + string why; - getHPWHObject(hpwh, input); + getHPWHObject(hpwh, input); - const double maxQAHVSetpoint = F_TO_C(176.1); - const double qAHVHotSideTemepratureOffset = dF_TO_dC(15.); + const double maxQAHVSetpoint = F_TO_C(176.1); + const double qAHVHotSideTemepratureOffset = dF_TO_dC(15.); - // isNewSetpointPossible should be fine, we aren't changing the setpoint of the Sanden. - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling - ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling + // isNewSetpointPossible should be fine, we aren't changing the setpoint of the Sanden. + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling + ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(60., num, why)); // Can go to normal + ASSERTTRUE(hpwh.isNewSetpointPossible(60., num, why)); // Can go to normal - ASSERTFALSE(hpwh.isNewSetpointPossible(maxQAHVSetpoint, num, why)); - ASSERTTRUE(hpwh.isNewSetpointPossible(maxQAHVSetpoint-qAHVHotSideTemepratureOffset, num, why)); + ASSERTFALSE(hpwh.isNewSetpointPossible(maxQAHVSetpoint, num, why)); + ASSERTTRUE( + hpwh.isNewSetpointPossible(maxQAHVSetpoint - qAHVHotSideTemepratureOffset, num, why)); - // Check this carries over into setting the setpoint. - ASSERTTRUE(hpwh.setSetpoint(101) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwh.setSetpoint(maxQAHVSetpoint) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwh.setSetpoint(maxQAHVSetpoint - qAHVHotSideTemepratureOffset) == 0); + // Check this carries over into setting the setpoint. + ASSERTTRUE(hpwh.setSetpoint(101) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwh.setSetpoint(maxQAHVSetpoint) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwh.setSetpoint(maxQAHVSetpoint - qAHVHotSideTemepratureOffset) == 0); } -void testHybridModel() { - HPWH hpwh; - string input = "AOSmithCAHP120"; //Hybrid unit with a compressor with R134A - double num; - string why; - - getHPWHObject(hpwh, input); - - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(100., num, why)); // Can go to boiling - ASSERTTRUE(hpwh.isNewSetpointPossible(10., num, why)); // Can go low, albiet dumb - ASSERTTRUE(REMaxShouldBe == num); // Max is boiling - - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(99.) == 0) // Can go lower than boiling though +void testHybridModel() +{ + HPWH hpwh; + string input = "AOSmithCAHP120"; // Hybrid unit with a compressor with R134A + double num; + string why; + + getHPWHObject(hpwh, input); + + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(100., num, why)); // Can go to boiling + ASSERTTRUE(hpwh.isNewSetpointPossible(10., num, why)); // Can go low, albiet dumb + ASSERTTRUE(REMaxShouldBe == num); // Max is boiling + + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(99.) == 0) // Can go lower than boiling though } -void testStorageTankSetpoint() { - HPWH hpwh; - string input = "StorageTank"; //Hybrid unit with a compressor with R134A - double num; - string why; - - getHPWHObject(hpwh, input); +void testStorageTankSetpoint() +{ + HPWH hpwh; + string input = "StorageTank"; // Hybrid unit with a compressor with R134A + double num; + string why; - // Storage tanks have free reign! - ASSERTTRUE(hpwh.isNewSetpointPossible(101., num, why)); // Can go above boiling! - ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling! - ASSERTTRUE(hpwh.isNewSetpointPossible(10., num, why)); // Can go low, albiet dumb + getHPWHObject(hpwh, input); - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == 0); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(99.) == 0) // Can go lower than boiling though + // Storage tanks have free reign! + ASSERTTRUE(hpwh.isNewSetpointPossible(101., num, why)); // Can go above boiling! + ASSERTTRUE(hpwh.isNewSetpointPossible(99., num, why)); // Can go to near boiling! + ASSERTTRUE(hpwh.isNewSetpointPossible(10., num, why)); // Can go low, albiet dumb + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == 0); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(99.) == 0) // Can go lower than boiling though } -void testSetpointFixed() { - HPWH hpwh; - string input = "Sanden80"; //Fixed setpoint model - double num, num1; - string why; - - getHPWHObject(hpwh, input); - - // Storage tanks have free reign! - ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling! - ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling! - ASSERTFALSE(hpwh.isNewSetpointPossible(60., num, why)); // Can't go to normalish - ASSERTFALSE(hpwh.isNewSetpointPossible(10., num, why)); // Can't go low, albiet dumb - - ASSERTTRUE(num == hpwh.getSetpoint()); // Make sure it thinks the max is the setpoint - ASSERTTRUE(hpwh.isNewSetpointPossible(num, num1, why)) // Check that the setpoint can be set to the setpoint. - - // Check this carries over into setting the setpoint - ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling - ASSERTTRUE(hpwh.setSetpoint(99.) == HPWH::HPWH_ABORT) // Can go lower than boiling though - ASSERTTRUE(hpwh.setSetpoint(60.) == HPWH::HPWH_ABORT); // Can't go to normalish - ASSERTTRUE(hpwh.setSetpoint(10.) == HPWH::HPWH_ABORT); // Can't go low, albiet dumb +void testSetpointFixed() +{ + HPWH hpwh; + string input = "Sanden80"; // Fixed setpoint model + double num, num1; + string why; + + getHPWHObject(hpwh, input); + + // Storage tanks have free reign! + ASSERTFALSE(hpwh.isNewSetpointPossible(101., num, why)); // Can't go above boiling! + ASSERTFALSE(hpwh.isNewSetpointPossible(99., num, why)); // Can't go to near boiling! + ASSERTFALSE(hpwh.isNewSetpointPossible(60., num, why)); // Can't go to normalish + ASSERTFALSE(hpwh.isNewSetpointPossible(10., num, why)); // Can't go low, albiet dumb + + ASSERTTRUE(num == hpwh.getSetpoint()); // Make sure it thinks the max is the setpoint + ASSERTTRUE(hpwh.isNewSetpointPossible( + num, num1, why)) // Check that the setpoint can be set to the setpoint. + + // Check this carries over into setting the setpoint + ASSERTTRUE(hpwh.setSetpoint(101.) == HPWH::HPWH_ABORT); // Can't go above boiling + ASSERTTRUE(hpwh.setSetpoint(99.) == HPWH::HPWH_ABORT) // Can go lower than boiling though + ASSERTTRUE(hpwh.setSetpoint(60.) == HPWH::HPWH_ABORT); // Can't go to normalish + ASSERTTRUE(hpwh.setSetpoint(10.) == HPWH::HPWH_ABORT); // Can't go low, albiet dumb } -void testResampling() { +void testResampling() +{ -// test extensive resampling + // test extensive resampling std::vector values(10); - std::vector sampleValues{20., 40., 60., 40., 20.}; - ASSERTTRUE(resampleExtensive(values, sampleValues)); + std::vector sampleValues {20., 40., 60., 40., 20.}; + ASSERTTRUE(resampleExtensive(values, sampleValues)); - // Check some expected values. - ASSERTTRUE(relcmpd(values[1], 10.)); // - ASSERTTRUE(relcmpd(values[5], 30.)); // + // Check some expected values. + ASSERTTRUE(relcmpd(values[1], 10.)); // + ASSERTTRUE(relcmpd(values[5], 30.)); // -// test intensive resampling - ASSERTTRUE(resampleIntensive(values, sampleValues)); + // test intensive resampling + ASSERTTRUE(resampleIntensive(values, sampleValues)); - // Check some expected values. - ASSERTTRUE(relcmpd(values[1], 20.)); // - ASSERTTRUE(relcmpd(values[5], 60.)); // + // Check some expected values. + ASSERTTRUE(relcmpd(values[1], 20.)); // + ASSERTTRUE(relcmpd(values[5], 60.)); // } -void testSetTankTemps() { - HPWH hpwh; - getHPWHObject(hpwh, "Rheem2020Prem50"); // 12-node model - - std::vector newTemps; - -// test 1 - std::vector setTemps{10., 60.}; - hpwh.setTankLayerTemperatures(setTemps); - hpwh.getTankTemps(newTemps); - - // Check some expected values. - ASSERTTRUE(relcmpd(newTemps[0], 10.0)); // - ASSERTTRUE(relcmpd(newTemps[11], 60.0)); // - -// test 2 - setTemps = {10., 20., 30., 40., 50., 60.}; - hpwh.setTankLayerTemperatures(setTemps); - hpwh.getTankTemps(newTemps); - - // Check some expected values. - ASSERTTRUE(relcmpd(newTemps[0], 10.)); // - ASSERTTRUE(relcmpd(newTemps[5], 30.)); // - ASSERTTRUE(relcmpd(newTemps[6], 40.)); // - ASSERTTRUE(relcmpd(newTemps[11], 60.)); // - -// test 3 - setTemps = {10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65., 70., 75., 80., 85., 90.}; - hpwh.setTankLayerTemperatures(setTemps); - hpwh.getTankTemps(newTemps); - - // Check some expected values. - ASSERTTRUE(relcmpd(newTemps[2], 25.3, 0.1)); // - ASSERTTRUE(relcmpd(newTemps[8], 67.6, 0.1)); // - -// test 4 - int nSet = 24; - setTemps.resize(nSet); - double Ti = 20., Tf = 66.; - for (std::size_t i = 0; i < nSet; ++i) - setTemps[i] = Ti + (Tf - Ti) * i / (nSet - 1); - hpwh.setTankLayerTemperatures(setTemps); - hpwh.getTankTemps(newTemps); - - // Check some expected values. - ASSERTTRUE(relcmpd(newTemps[4], 37.)); // - ASSERTTRUE(relcmpd(newTemps[10], 61.)); // - -// test 5 - nSet = 12; - setTemps.resize(nSet); - Ti = 20.; Tf = 64.; - for (std::size_t i = 0; i < nSet; ++i) - setTemps[i] = Ti + (Tf - Ti) * i / (nSet - 1); - hpwh.setTankLayerTemperatures(setTemps); - hpwh.getTankTemps(newTemps); - - // Check some expected values. - ASSERTTRUE(fabs(newTemps[3] - 32.) < 0.1); // - ASSERTTRUE(fabs(newTemps[11] - 64.) < 0.1); // - +void testSetTankTemps() +{ + HPWH hpwh; + getHPWHObject(hpwh, "Rheem2020Prem50"); // 12-node model + + std::vector newTemps; + + // test 1 + std::vector setTemps {10., 60.}; + hpwh.setTankLayerTemperatures(setTemps); + hpwh.getTankTemps(newTemps); + + // Check some expected values. + ASSERTTRUE(relcmpd(newTemps[0], 10.0)); // + ASSERTTRUE(relcmpd(newTemps[11], 60.0)); // + + // test 2 + setTemps = {10., 20., 30., 40., 50., 60.}; + hpwh.setTankLayerTemperatures(setTemps); + hpwh.getTankTemps(newTemps); + + // Check some expected values. + ASSERTTRUE(relcmpd(newTemps[0], 10.)); // + ASSERTTRUE(relcmpd(newTemps[5], 30.)); // + ASSERTTRUE(relcmpd(newTemps[6], 40.)); // + ASSERTTRUE(relcmpd(newTemps[11], 60.)); // + + // test 3 + setTemps = { + 10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65., 70., 75., 80., 85., 90.}; + hpwh.setTankLayerTemperatures(setTemps); + hpwh.getTankTemps(newTemps); + + // Check some expected values. + ASSERTTRUE(relcmpd(newTemps[2], 25.3, 0.1)); // + ASSERTTRUE(relcmpd(newTemps[8], 67.6, 0.1)); // + + // test 4 + int nSet = 24; + setTemps.resize(nSet); + double Ti = 20., Tf = 66.; + for (std::size_t i = 0; i < nSet; ++i) + setTemps[i] = Ti + (Tf - Ti) * i / (nSet - 1); + hpwh.setTankLayerTemperatures(setTemps); + hpwh.getTankTemps(newTemps); + + // Check some expected values. + ASSERTTRUE(relcmpd(newTemps[4], 37.)); // + ASSERTTRUE(relcmpd(newTemps[10], 61.)); // + + // test 5 + nSet = 12; + setTemps.resize(nSet); + Ti = 20.; + Tf = 64.; + for (std::size_t i = 0; i < nSet; ++i) + setTemps[i] = Ti + (Tf - Ti) * i / (nSet - 1); + hpwh.setTankLayerTemperatures(setTemps); + hpwh.getTankTemps(newTemps); + + // Check some expected values. + ASSERTTRUE(fabs(newTemps[3] - 32.) < 0.1); // + ASSERTTRUE(fabs(newTemps[11] - 64.) < 0.1); // } - diff --git a/test/testPerformanceMaps.cc b/test/testPerformanceMaps.cc index 5f90aa00..67f642fb 100644 --- a/test/testPerformanceMaps.cc +++ b/test/testPerformanceMaps.cc @@ -6,7 +6,7 @@ #include "testUtilityFcts.cc" #include -#include +#include using std::cout; using std::string; @@ -14,562 +14,669 @@ using std::string; const double tInOffsetQAHV_dF = 10.; const double tOutOffsetQAHV_dF = 15.; -struct performancePointMP { - double tairF; - double tinF; - double outputBTUH; +struct performancePointMP +{ + double tairF; + double tinF; + double outputBTUH; }; -double getCapacityMP_F_KW(HPWH& hpwh, performancePointMP& point) { - return hpwh.getCompressorCapacity(point.tairF, point.tinF, point.tinF, HPWH::UNITS_KW, HPWH::UNITS_F); +double getCapacityMP_F_KW(HPWH& hpwh, performancePointMP& point) +{ + return hpwh.getCompressorCapacity( + point.tairF, point.tinF, point.tinF, HPWH::UNITS_KW, HPWH::UNITS_F); } -struct performancePointSP { - double tairF; - double toutF; - double tinF; - double outputBTUH; +struct performancePointSP +{ + double tairF; + double toutF; + double tinF; + double outputBTUH; }; -double getCapacitySP_F_BTUHR(HPWH& hpwh, performancePointSP& point) { - return hpwh.getCompressorCapacity(point.tairF, point.tinF, point.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); +double getCapacitySP_F_BTUHR(HPWH& hpwh, performancePointSP& point) +{ + return hpwh.getCompressorCapacity( + point.tairF, point.tinF, point.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); } -double getCapacitySP_F_BTUHR(HPWH& hpwh, performancePointSP& point, double tInOffSet_dF, double tOutOffSet_dF) { - return hpwh.getCompressorCapacity(point.tairF, point.tinF-tInOffSet_dF, point.toutF-tOutOffSet_dF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); +double getCapacitySP_F_BTUHR(HPWH& hpwh, + performancePointSP& point, + double tInOffSet_dF, + double tOutOffSet_dF) +{ + return hpwh.getCompressorCapacity(point.tairF, + point.tinF - tInOffSet_dF, + point.toutF - tOutOffSet_dF, + HPWH::UNITS_BTUperHr, + HPWH::UNITS_F); } -void testCXA15MatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_15_SP"; - double capacity_kW, capacity_BTUperHr; - // get preset model - getHPWHObject(hpwh, input); - - // test hot ////////////////////////// - double airTempF = 100.; - double waterTempF = 125.; - double setpointF = 150.; - double capacityData_kW = 52.779317; - - capacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - capacity_kW = hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); - - ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); - ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); - - // test middle //////////////// - airTempF = 80.; - waterTempF = 40.; - setpointF = 150.; - capacityData_kW = 44.962957379; - - capacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - capacity_kW = hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); - - ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); - ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); - - // test cold //////////////// - airTempF = 60.; - waterTempF = 40.; - setpointF = 125.; - capacityData_kW = 37.5978306881; - - capacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - capacity_kW = hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); - - ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); - ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); +void testCXA15MatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_15_SP"; + double capacity_kW, capacity_BTUperHr; + // get preset model + getHPWHObject(hpwh, input); + + // test hot ////////////////////////// + double airTempF = 100.; + double waterTempF = 125.; + double setpointF = 150.; + double capacityData_kW = 52.779317; + + capacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + capacity_kW = + hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); + + ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); + ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); + + // test middle //////////////// + airTempF = 80.; + waterTempF = 40.; + setpointF = 150.; + capacityData_kW = 44.962957379; + + capacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + capacity_kW = + hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); + + ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); + ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); + + // test cold //////////////// + airTempF = 60.; + waterTempF = 40.; + setpointF = 125.; + capacityData_kW = 37.5978306881; + + capacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + capacity_kW = + hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); + + ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); + ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); } -void testCXA30MatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_30_SP"; - double capacity_kW, capacity_BTUperHr; - // get preset model - getHPWHObject(hpwh, input); - - // test hot ////////////////////////// - double airTempF = 100.; - double waterTempF = 125.; - double setpointF = 150.; - double capacityData_kW = 105.12836804; - - capacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - capacity_kW = hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); - - ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); - ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); - - // test middle //////////////// - airTempF = 80.; - waterTempF = 40.; - setpointF = 150.; - capacityData_kW = 89.186101453; - - capacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - capacity_kW = hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); - - ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); - ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); - - // test cold //////////////// - airTempF = 60.; - waterTempF = 40.; - setpointF = 125.; - capacityData_kW = 74.2437689948; - - capacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - capacity_kW = hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); - - ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); - ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); +void testCXA30MatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_30_SP"; + double capacity_kW, capacity_BTUperHr; + // get preset model + getHPWHObject(hpwh, input); + + // test hot ////////////////////////// + double airTempF = 100.; + double waterTempF = 125.; + double setpointF = 150.; + double capacityData_kW = 105.12836804; + + capacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + capacity_kW = + hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); + + ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); + ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); + + // test middle //////////////// + airTempF = 80.; + waterTempF = 40.; + setpointF = 150.; + capacityData_kW = 89.186101453; + + capacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + capacity_kW = + hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); + + ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); + ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); + + // test cold //////////////// + airTempF = 60.; + waterTempF = 40.; + setpointF = 125.; + capacityData_kW = 74.2437689948; + + capacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + capacity_kW = + hpwh.getCompressorCapacity(F_TO_C(airTempF), F_TO_C(waterTempF), F_TO_C(setpointF)); + + ASSERTTRUE(relcmpd(KWH_TO_BTU(capacityData_kW), capacity_BTUperHr)); + ASSERTTRUE(relcmpd(capacityData_kW, capacity_kW)); } // Colmac MP perfmaps tests -void testCXV5MPMatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxV_5_MP"; - performancePointMP checkPoint; +void testCXV5MPMatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxV_5_MP"; + performancePointMP checkPoint; - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - // test some points outside of defrost //////////////// - checkPoint = { 10.0, 60.0, 8.7756391 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + // test some points outside of defrost //////////////// + checkPoint = {10.0, 60.0, 8.7756391}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 10.0, 102.0, 9.945545 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {10.0, 102.0, 9.945545}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 70.0, 74.0, 19.09682784 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {70.0, 74.0, 19.09682784}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 70.0, 116.0, 18.97090763 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {70.0, 116.0, 18.97090763}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 116.0, 24.48703111 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 116.0, 24.48703111}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testCXA10MPMatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_10_MP"; - performancePointMP checkPoint; +void testCXA10MPMatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_10_MP"; + performancePointMP checkPoint; - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 66.0, 29.36581754 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + // test some points outside of defrost //////////////// + checkPoint = {60.0, 66.0, 29.36581754}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 60.0, 114.0, 27.7407144 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {60.0, 114.0, 27.7407144}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 66.0, 37.4860496 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 66.0, 37.4860496}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 114.0, 35.03416199 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 114.0, 35.03416199}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 66.0, 46.63144 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 66.0, 46.63144}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 114.0, 43.4308219 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 114.0, 43.4308219}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testCXA15MPMatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_15_MP"; - performancePointMP checkPoint; +void testCXA15MPMatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_15_MP"; + performancePointMP checkPoint; - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 66.0, 37.94515042 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + // test some points outside of defrost //////////////// + checkPoint = {60.0, 66.0, 37.94515042}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 60.0, 114.0, 35.2295393 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {60.0, 114.0, 35.2295393}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 66.0, 50.360549115 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 66.0, 50.360549115}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 114.0, 43.528417017 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 114.0, 43.528417017}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 66.0, 66.2675493 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 66.0, 66.2675493}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 114.0, 55.941855 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 114.0, 55.941855}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testCXA20MPMatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_20_MP"; - performancePointMP checkPoint; +void testCXA20MPMatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_20_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); - // get preset model - getHPWHObject(hpwh, input); + // test some points outside of defrost //////////////// + checkPoint = {60.0, 66.0, 57.0994624}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 66.0, 57.0994624 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {60.0, 114.0, 53.554293}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 60.0, 114.0, 53.554293 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 66.0, 73.11242842}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 66.0, 73.11242842 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 114.0, 68.034677}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 114.0, 68.034677 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - - checkPoint = { 100.0, 66.0, 90.289474295 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - - checkPoint = { 100.0, 114.0, 84.69323 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 66.0, 90.289474295}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + + checkPoint = {100.0, 114.0, 84.69323}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testCXA25MPMatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_25_MP"; - performancePointMP checkPoint; +void testCXA25MPMatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_25_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); - // get preset model - getHPWHObject(hpwh, input); + // test some points outside of defrost //////////////// + checkPoint = {60.0, 66.0, 67.28116620}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 66.0, 67.28116620 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {60.0, 114.0, 63.5665037}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 60.0, 114.0, 63.5665037 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 66.0, 84.9221285742}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 66.0, 84.9221285742 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 114.0, 79.6237088}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 114.0, 79.6237088 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 66.0, 103.43268186}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 66.0, 103.43268186 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - - checkPoint = { 100.0, 114.0, 97.71458413 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 114.0, 97.71458413}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testCXA30MPMatchesDataMap() { - HPWH hpwh; - string input = "ColmacCxA_30_MP"; - performancePointMP checkPoint; +void testCXA30MPMatchesDataMap() +{ + HPWH hpwh; + string input = "ColmacCxA_30_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); - // get preset model - getHPWHObject(hpwh, input); + // test some points outside of defrost //////////////// + checkPoint = {60.0, 66.0, 76.741462845}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 66.0, 76.741462845 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - - checkPoint = { 60.0, 114.0, 73.66879620 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {60.0, 114.0, 73.66879620}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 66.0, 94.863116775 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 66.0, 94.863116775}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 114.0, 90.998904269 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 114.0, 90.998904269}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 66.0, 112.864628 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 66.0, 112.864628}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 100.0, 114.0, 109.444451 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {100.0, 114.0, 109.444451}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testRheemHPHD60() { - //MODELS_RHEEM_HPHD60HNU_201_MP - //MODELS_RHEEM_HPHD60VNU_201_MP - HPWH hpwh; - string input = "RheemHPHD60"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 66.6666666, 60.0, 16.785535996 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 66.6666666, 120.0, 16.76198953 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 88.3333333, 80.0, 20.8571194294 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 110.0, 100.0, 24.287512 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testRheemHPHD60() +{ + // MODELS_RHEEM_HPHD60HNU_201_MP + // MODELS_RHEEM_HPHD60VNU_201_MP + HPWH hpwh; + string input = "RheemHPHD60"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {66.6666666, 60.0, 16.785535996}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {66.6666666, 120.0, 16.76198953}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {88.3333333, 80.0, 20.8571194294}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {110.0, 100.0, 24.287512}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testRheemHPHD135() { - //MODELS_RHEEM_HPHD135HNU_483_MP - //MODELS_RHEEM_HPHD135VNU_483_MP - HPWH hpwh; - string input = "RheemHPHD135"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 66.6666666, 80.0, 38.560161199 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 66.6666666, 140.0, 34.70681846 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 88.3333333, 140.0, 42.40407101 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 110.0, 120.0, 54.3580927 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testRheemHPHD135() +{ + // MODELS_RHEEM_HPHD135HNU_483_MP + // MODELS_RHEEM_HPHD135VNU_483_MP + HPWH hpwh; + string input = "RheemHPHD135"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {66.6666666, 80.0, 38.560161199}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {66.6666666, 140.0, 34.70681846}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {88.3333333, 140.0, 42.40407101}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {110.0, 120.0, 54.3580927}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testNyleC60AMP() { - HPWH hpwh; - string input = "NyleC60A_MP"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 60.0, 16.11 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 60.0, 21.33 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 90.0, 130.0, 19.52 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testNyleC60AMP() +{ + HPWH hpwh; + string input = "NyleC60A_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {60.0, 60.0, 16.11}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 60.0, 21.33}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {90.0, 130.0, 19.52}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testNyleC90AMP() { - HPWH hpwh; - string input = "NyleC90A_MP"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 60.0, 28.19 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 60.0, 37.69 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 90.0, 130.0, 36.27 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testNyleC90AMP() +{ + HPWH hpwh; + string input = "NyleC90A_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {60.0, 60.0, 28.19}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 60.0, 37.69}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {90.0, 130.0, 36.27}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testNyleC125AMP() { - HPWH hpwh; - string input = "NyleC125A_MP"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 60.0, 36.04}; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 60.0, 47.86 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 90.0, 130.0, 43.80}; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testNyleC125AMP() +{ + HPWH hpwh; + string input = "NyleC125A_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {60.0, 60.0, 36.04}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 60.0, 47.86}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {90.0, 130.0, 43.80}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testNyleC185AMP() { - HPWH hpwh; - string input = "NyleC185A_MP"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 60.0, 55.00}; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 60.0, 74.65 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 90.0, 130.0, 67.52 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testNyleC185AMP() +{ + HPWH hpwh; + string input = "NyleC185A_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {60.0, 60.0, 55.00}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 60.0, 74.65}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {90.0, 130.0, 67.52}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testNyleC250AMP() { - HPWH hpwh; - string input = "NyleC250A_MP"; - performancePointMP checkPoint; - - // get preset model - getHPWHObject(hpwh, input); - - // test some points outside of defrost //////////////// - checkPoint = { 60.0, 60.0, 75.70 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 80.0, 60.0, 103.40 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); - checkPoint = { 90.0, 130.0, 77.89 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); +void testNyleC250AMP() +{ + HPWH hpwh; + string input = "NyleC250A_MP"; + performancePointMP checkPoint; + + // get preset model + getHPWHObject(hpwh, input); + + // test some points outside of defrost //////////////// + checkPoint = {60.0, 60.0, 75.70}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {80.0, 60.0, 103.40}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); + checkPoint = {90.0, 130.0, 77.89}; + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacityMP_F_KW(hpwh, checkPoint))); } -void testQAHVMatchesDataMap() { - HPWH hpwh; - string input = "QAHV_N136TAU_HPB_SP"; - performancePointSP checkPoint; // tairF, toutF, tinF, outputW - double outputBTUH; - getHPWHObject(hpwh, input); - - // test - checkPoint = { -13.0, 140.0, 41.0, 66529.49616 }; - outputBTUH = getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF); - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, outputBTUH)); - // test - checkPoint = { -13.0, 176.0, 41.0, 65872.597448 }; - ASSERTTRUE(getCapacitySP_F_BTUHR(hpwh, checkPoint) == HPWH::HPWH_ABORT); // max setpoint without adjustment forces error - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { -13.0, 176.0, 84.2, 55913.249232 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 14.0, 176.0, 84.2, 92933.01932 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 42.8, 140.0, 41.0, 136425.98804}; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 50.0, 140.0, 41.0, 136425.98804 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 50.0, 176.0, 84.2, 136564.470884}; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 60.8, 158.0, 84.2, 136461.998288 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 71.6, 158.0, 48.2, 136498.001712 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - - // test constant with setpoint between 140 and 158 - checkPoint = { 71.6, 149.0, 48.2, 136498.001712 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 82.4, 176.0, 41.0, 136557.496756 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 104.0, 140.0, 62.6, 136480 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 104.0, 158.0, 62.6, 136480 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - // test - checkPoint = { 104.0, 176.0, 84.2, 136564.470884 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); +void testQAHVMatchesDataMap() +{ + HPWH hpwh; + string input = "QAHV_N136TAU_HPB_SP"; + performancePointSP checkPoint; // tairF, toutF, tinF, outputW + double outputBTUH; + getHPWHObject(hpwh, input); + + // test + checkPoint = {-13.0, 140.0, 41.0, 66529.49616}; + outputBTUH = getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF); + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, outputBTUH)); + // test + checkPoint = {-13.0, 176.0, 41.0, 65872.597448}; + ASSERTTRUE(getCapacitySP_F_BTUHR(hpwh, checkPoint) == + HPWH::HPWH_ABORT); // max setpoint without adjustment forces error + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {-13.0, 176.0, 84.2, 55913.249232}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {14.0, 176.0, 84.2, 92933.01932}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {42.8, 140.0, 41.0, 136425.98804}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {50.0, 140.0, 41.0, 136425.98804}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {50.0, 176.0, 84.2, 136564.470884}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {60.8, 158.0, 84.2, 136461.998288}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {71.6, 158.0, 48.2, 136498.001712}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + + // test constant with setpoint between 140 and 158 + checkPoint = {71.6, 149.0, 48.2, 136498.001712}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {82.4, 176.0, 41.0, 136557.496756}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {104.0, 140.0, 62.6, 136480}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {104.0, 158.0, 62.6, 136480}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + // test + checkPoint = {104.0, 176.0, 84.2, 136564.470884}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); } -// Also test QAHV constant extrpolation at high air temperatures AND linear extrapolation to hot water temperatures! -void testQAHVExtrapolates() { - HPWH hpwh; - string input = "QAHV_N136TAU_HPB_SP"; - performancePointSP checkPoint; // tairF, toutF, tinF, outputW - getHPWHObject(hpwh, input); - - // test linear along Tin - checkPoint = { -13.0, 140.0, 36.0, 66529.49616 }; - ASSERTTRUE(checkPoint.outputBTUH < getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has increased - // test linear along Tin - checkPoint = { -13.0, 140.0, 100.0, 66529.49616 }; - ASSERTTRUE(checkPoint.outputBTUH > getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased - // test linear along Tin - checkPoint = { -13.0, 176.0, 36.0, 65872.597448 }; - ASSERTTRUE(checkPoint.outputBTUH < getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has increased - // test linear along Tin - checkPoint = { -13.0, 176.0, 100.0, 55913.249232 }; - ASSERTTRUE(checkPoint.outputBTUH > getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased at high Tin - // test linear along Tin - checkPoint = { 10.4, 140.0, 111., 89000.085396 }; - ASSERTTRUE(checkPoint.outputBTUH > getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased at high Tin - // test linear along Tin - checkPoint = { 64.4, 158.0, 100, 136461.998288 }; - ASSERTTRUE(checkPoint.outputBTUH > getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased at high Tin - // test linear along Tin - checkPoint = { 86.0, 158.0, 100, 136461.998288 }; - ASSERTTRUE(checkPoint.outputBTUH > getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased at high Tin - // test linear along Tin - checkPoint = { 104.0, 176.0, 100., 136564.470884 }; - ASSERTTRUE(checkPoint.outputBTUH > getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased at high Tin - - // test const along Tair - checkPoint = { 110.0, 140.0, 62.6, 136480 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - - // test const along Tair - checkPoint = { 114.0, 176.0, 84.2, 136564.470884 }; - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); - - checkPoint = { 114.0, 200.0, 84.2, 136564.470884 }; - double output = getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF); - ASSERTTRUE(output == HPWH::HPWH_ABORT); +// Also test QAHV constant extrpolation at high air temperatures AND linear extrapolation to hot +// water temperatures! +void testQAHVExtrapolates() +{ + HPWH hpwh; + string input = "QAHV_N136TAU_HPB_SP"; + performancePointSP checkPoint; // tairF, toutF, tinF, outputW + getHPWHObject(hpwh, input); + + // test linear along Tin + checkPoint = {-13.0, 140.0, 36.0, 66529.49616}; + ASSERTTRUE( + checkPoint.outputBTUH < + getCapacitySP_F_BTUHR( + hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has increased + // test linear along Tin + checkPoint = {-13.0, 140.0, 100.0, 66529.49616}; + ASSERTTRUE( + checkPoint.outputBTUH > + getCapacitySP_F_BTUHR( + hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has decreased + // test linear along Tin + checkPoint = {-13.0, 176.0, 36.0, 65872.597448}; + ASSERTTRUE( + checkPoint.outputBTUH < + getCapacitySP_F_BTUHR( + hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF)); // Check output has increased + // test linear along Tin + checkPoint = {-13.0, 176.0, 100.0, 55913.249232}; + ASSERTTRUE(checkPoint.outputBTUH > + getCapacitySP_F_BTUHR(hpwh, + checkPoint, + tInOffsetQAHV_dF, + tOutOffsetQAHV_dF)); // Check output has decreased at high Tin + // test linear along Tin + checkPoint = {10.4, 140.0, 111., 89000.085396}; + ASSERTTRUE(checkPoint.outputBTUH > + getCapacitySP_F_BTUHR(hpwh, + checkPoint, + tInOffsetQAHV_dF, + tOutOffsetQAHV_dF)); // Check output has decreased at high Tin + // test linear along Tin + checkPoint = {64.4, 158.0, 100, 136461.998288}; + ASSERTTRUE(checkPoint.outputBTUH > + getCapacitySP_F_BTUHR(hpwh, + checkPoint, + tInOffsetQAHV_dF, + tOutOffsetQAHV_dF)); // Check output has decreased at high Tin + // test linear along Tin + checkPoint = {86.0, 158.0, 100, 136461.998288}; + ASSERTTRUE(checkPoint.outputBTUH > + getCapacitySP_F_BTUHR(hpwh, + checkPoint, + tInOffsetQAHV_dF, + tOutOffsetQAHV_dF)); // Check output has decreased at high Tin + // test linear along Tin + checkPoint = {104.0, 176.0, 100., 136564.470884}; + ASSERTTRUE(checkPoint.outputBTUH > + getCapacitySP_F_BTUHR(hpwh, + checkPoint, + tInOffsetQAHV_dF, + tOutOffsetQAHV_dF)); // Check output has decreased at high Tin + + // test const along Tair + checkPoint = {110.0, 140.0, 62.6, 136480}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + + // test const along Tair + checkPoint = {114.0, 176.0, 84.2, 136564.470884}; + ASSERTTRUE( + relcmpd(checkPoint.outputBTUH, + getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF))); + + checkPoint = {114.0, 200.0, 84.2, 136564.470884}; + double output = getCapacitySP_F_BTUHR(hpwh, checkPoint, tInOffsetQAHV_dF, tOutOffsetQAHV_dF); + ASSERTTRUE(output == HPWH::HPWH_ABORT); } void testSanden() { - HPWH hpwh; - string input = "Sanden120"; - performancePointSP checkPoint; // tairF, toutF, tinF, outputW - double outputBTUH; - getHPWHObject(hpwh, input); - - // nominal - checkPoint = { 60, 149.0, 41.0, 15059.59167 }; - outputBTUH = hpwh.getCompressorCapacity(checkPoint.tairF, checkPoint.tinF, checkPoint.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, outputBTUH)); - - // Cold outlet temperature - checkPoint = { 60, 125.0, 41.0, 15059.59167 }; - outputBTUH = hpwh.getCompressorCapacity(checkPoint.tairF, checkPoint.tinF, checkPoint.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - ASSERTTRUE(relcmpd(checkPoint.outputBTUH, outputBTUH)); - - // tests fails when output high - checkPoint = { 60, 200, 41.0, 15059.59167 }; - outputBTUH = hpwh.getCompressorCapacity(checkPoint.tairF, checkPoint.tinF, checkPoint.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - ASSERTTRUE(outputBTUH == HPWH::HPWH_ABORT); + HPWH hpwh; + string input = "Sanden120"; + performancePointSP checkPoint; // tairF, toutF, tinF, outputW + double outputBTUH; + getHPWHObject(hpwh, input); + + // nominal + checkPoint = {60, 149.0, 41.0, 15059.59167}; + outputBTUH = hpwh.getCompressorCapacity( + checkPoint.tairF, checkPoint.tinF, checkPoint.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, outputBTUH)); + + // Cold outlet temperature + checkPoint = {60, 125.0, 41.0, 15059.59167}; + outputBTUH = hpwh.getCompressorCapacity( + checkPoint.tairF, checkPoint.tinF, checkPoint.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + ASSERTTRUE(relcmpd(checkPoint.outputBTUH, outputBTUH)); + + // tests fails when output high + checkPoint = {60, 200, 41.0, 15059.59167}; + outputBTUH = hpwh.getCompressorCapacity( + checkPoint.tairF, checkPoint.tinF, checkPoint.toutF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + ASSERTTRUE(outputBTUH == HPWH::HPWH_ABORT); } int main(int, char*) { - testSanden(); // check can still work with HPWH::getCapacity() as expected - - testQAHVMatchesDataMap(); // Test QAHV grid data input correctly - testQAHVExtrapolates(); // Test QAHV grid data extrapolate correctly - - // Tests that the correct capacity is returned for specific equipement that matches the data set - testCXA15MatchesDataMap(); - testCXA30MatchesDataMap(); - - testCXV5MPMatchesDataMap(); - testCXA10MPMatchesDataMap(); - testCXA15MPMatchesDataMap(); - testCXA20MPMatchesDataMap(); - testCXA25MPMatchesDataMap(); - testCXA30MPMatchesDataMap(); - - testRheemHPHD135(); - testRheemHPHD60(); - - testNyleC60AMP(); - testNyleC90AMP(); - testNyleC125AMP(); - testNyleC185AMP(); - testNyleC250AMP(); - - //Made it through the gauntlet - return 0; -} \ No newline at end of file + testSanden(); // check can still work with HPWH::getCapacity() as expected + + testQAHVMatchesDataMap(); // Test QAHV grid data input correctly + testQAHVExtrapolates(); // Test QAHV grid data extrapolate correctly + + // Tests that the correct capacity is returned for specific equipement that matches the data set + testCXA15MatchesDataMap(); + testCXA30MatchesDataMap(); + + testCXV5MPMatchesDataMap(); + testCXA10MPMatchesDataMap(); + testCXA15MPMatchesDataMap(); + testCXA20MPMatchesDataMap(); + testCXA25MPMatchesDataMap(); + testCXA30MPMatchesDataMap(); + + testRheemHPHD135(); + testRheemHPHD60(); + + testNyleC60AMP(); + testNyleC90AMP(); + testNyleC125AMP(); + testNyleC185AMP(); + testNyleC250AMP(); + + // Made it through the gauntlet + return 0; +} diff --git a/test/testResistanceFcts.cc b/test/testResistanceFcts.cc index efe556c4..480a32d6 100644 --- a/test/testResistanceFcts.cc +++ b/test/testResistanceFcts.cc @@ -9,225 +9,265 @@ #include "testUtilityFcts.cc" #include -#include +#include using std::cout; using std::string; - -void testSetResistanceCapacityErrorChecks() { - HPWH hpwhHP1, hpwhR; - string input = "ColmacCxA_30_SP"; - - // get preset model - getHPWHObject(hpwhHP1, input); - - ASSERTTRUE(hpwhHP1.setResistanceCapacity(100.) == HPWH::HPWH_ABORT); // Need's to be scalable - - input = "restankRealistic"; - // get preset model - getHPWHObject(hpwhR, input); - ASSERTTRUE(hpwhR.setResistanceCapacity(-100.) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwhR.setResistanceCapacity(100., 3) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwhR.setResistanceCapacity(100., 30000) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwhR.setResistanceCapacity(100., -3) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwhR.setResistanceCapacity(100., 0, HPWH::UNITS_F) == HPWH::HPWH_ABORT); +void testSetResistanceCapacityErrorChecks() +{ + HPWH hpwhHP1, hpwhR; + string input = "ColmacCxA_30_SP"; + + // get preset model + getHPWHObject(hpwhHP1, input); + + ASSERTTRUE(hpwhHP1.setResistanceCapacity(100.) == HPWH::HPWH_ABORT); // Need's to be scalable + + input = "restankRealistic"; + // get preset model + getHPWHObject(hpwhR, input); + ASSERTTRUE(hpwhR.setResistanceCapacity(-100.) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwhR.setResistanceCapacity(100., 3) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwhR.setResistanceCapacity(100., 30000) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwhR.setResistanceCapacity(100., -3) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwhR.setResistanceCapacity(100., 0, HPWH::UNITS_F) == HPWH::HPWH_ABORT); } -void testGetSetResistanceErrors() { - HPWH hpwh; - double lowerElementPower_W = 1000; - double lowerElementPower = lowerElementPower_W / 1000; - hpwh.HPWHinit_resTank(100., 0.95, 0., lowerElementPower_W); - - double returnVal; - returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); // lower - ASSERTTRUE(relcmpd(returnVal, lowerElementPower)); - returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); // both, - ASSERTTRUE(relcmpd(returnVal, lowerElementPower)); - returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); // higher doesn't exist - ASSERTTRUE(returnVal == HPWH::HPWH_ABORT); +void testGetSetResistanceErrors() +{ + HPWH hpwh; + double lowerElementPower_W = 1000; + double lowerElementPower = lowerElementPower_W / 1000; + hpwh.HPWHinit_resTank(100., 0.95, 0., lowerElementPower_W); + + double returnVal; + returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); // lower + ASSERTTRUE(relcmpd(returnVal, lowerElementPower)); + returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); // both, + ASSERTTRUE(relcmpd(returnVal, lowerElementPower)); + returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); // higher doesn't exist + ASSERTTRUE(returnVal == HPWH::HPWH_ABORT); } -void testCommercialTankInitErrors() { - HPWH hpwh; - // init model - ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(-800., 10., 100., 100.) == HPWH::HPWH_ABORT); // negative volume - ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 10., -100., 100.) == HPWH::HPWH_ABORT); // negative element - ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 10., 100., -100.) == HPWH::HPWH_ABORT); // negative element - ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., -10., 100., 100.) == HPWH::HPWH_ABORT); // negative r value - ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 0., 100., 100.) == HPWH::HPWH_ABORT); // 0 r value - ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 10., 0., 0.) == HPWH::HPWH_ABORT); // Check needs one element +void testCommercialTankInitErrors() +{ + HPWH hpwh; + // init model + ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(-800., 10., 100., 100.) == + HPWH::HPWH_ABORT); // negative volume + ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 10., -100., 100.) == + HPWH::HPWH_ABORT); // negative element + ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 10., 100., -100.) == + HPWH::HPWH_ABORT); // negative element + ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., -10., 100., 100.) == + HPWH::HPWH_ABORT); // negative r value + ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 0., 100., 100.) == HPWH::HPWH_ABORT); // 0 r value + ASSERTTRUE(hpwh.HPWHinit_resTankGeneric(800., 10., 0., 0.) == + HPWH::HPWH_ABORT); // Check needs one element } -void testGetNumResistanceElements() { - HPWH hpwh; - - hpwh.HPWHinit_resTankGeneric(800., 10., 0., 1000.); - ASSERTTRUE(hpwh.getNumResistanceElements() == 1); // Check 1 elements - hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 0.); - ASSERTTRUE(hpwh.getNumResistanceElements() == 1); // Check 1 elements - hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 1000.); - ASSERTTRUE(hpwh.getNumResistanceElements() == 2); // Check 2 elements +void testGetNumResistanceElements() +{ + HPWH hpwh; + + hpwh.HPWHinit_resTankGeneric(800., 10., 0., 1000.); + ASSERTTRUE(hpwh.getNumResistanceElements() == 1); // Check 1 elements + hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 0.); + ASSERTTRUE(hpwh.getNumResistanceElements() == 1); // Check 1 elements + hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 1000.); + ASSERTTRUE(hpwh.getNumResistanceElements() == 2); // Check 2 elements } -void testGetResistancePositionInRETank() { - HPWH hpwh; - - hpwh.HPWHinit_resTankGeneric(800., 10., 0., 1000.); - ASSERTTRUE(hpwh.getResistancePosition(0) == 0); // Check lower element is there - ASSERTTRUE(hpwh.getResistancePosition(1) == HPWH::HPWH_ABORT); // Check no element - ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check no element - - hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 0.); - ASSERTTRUE(hpwh.getResistancePosition(0) == 8); // Check upper element there - ASSERTTRUE(hpwh.getResistancePosition(1) == HPWH::HPWH_ABORT); // Check no elements - ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check no elements - - hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 1000.); - ASSERTTRUE(hpwh.getResistancePosition(0) == 8); // Check upper element there - ASSERTTRUE(hpwh.getResistancePosition(1) == 0); // Check lower element is there - ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check 0 elements} +void testGetResistancePositionInRETank() +{ + HPWH hpwh; + + hpwh.HPWHinit_resTankGeneric(800., 10., 0., 1000.); + ASSERTTRUE(hpwh.getResistancePosition(0) == 0); // Check lower element is there + ASSERTTRUE(hpwh.getResistancePosition(1) == HPWH::HPWH_ABORT); // Check no element + ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check no element + + hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 0.); + ASSERTTRUE(hpwh.getResistancePosition(0) == 8); // Check upper element there + ASSERTTRUE(hpwh.getResistancePosition(1) == HPWH::HPWH_ABORT); // Check no elements + ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check no elements + + hpwh.HPWHinit_resTankGeneric(800., 10., 1000., 1000.); + ASSERTTRUE(hpwh.getResistancePosition(0) == 8); // Check upper element there + ASSERTTRUE(hpwh.getResistancePosition(1) == 0); // Check lower element is there + ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check 0 elements} } -void testGetResistancePositionInCompressorTank() { - HPWH hpwh; +void testGetResistancePositionInCompressorTank() +{ + HPWH hpwh; - string input = "TamScalable_SP"; - // get preset model - getHPWHObject(hpwh, input); + string input = "TamScalable_SP"; + // get preset model + getHPWHObject(hpwh, input); - ASSERTTRUE(hpwh.getResistancePosition(0) == 9); // Check top elements - ASSERTTRUE(hpwh.getResistancePosition(1) == 0); // Check bottom elements - ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check compressor isn't RE - ASSERTTRUE(hpwh.getResistancePosition(-1) == HPWH::HPWH_ABORT); // Check out of bounds - ASSERTTRUE(hpwh.getResistancePosition(1000) == HPWH::HPWH_ABORT); // Check out of bounds + ASSERTTRUE(hpwh.getResistancePosition(0) == 9); // Check top elements + ASSERTTRUE(hpwh.getResistancePosition(1) == 0); // Check bottom elements + ASSERTTRUE(hpwh.getResistancePosition(2) == HPWH::HPWH_ABORT); // Check compressor isn't RE + ASSERTTRUE(hpwh.getResistancePosition(-1) == HPWH::HPWH_ABORT); // Check out of bounds + ASSERTTRUE(hpwh.getResistancePosition(1000) == HPWH::HPWH_ABORT); // Check out of bounds } -void testCommercialTankErrorsWithBottomElement() { - HPWH hpwh; - double elementPower_kW = 10.; //KW - // init model - hpwh.HPWHinit_resTankGeneric(800., 10., 0., elementPower_kW * 1000.); - - // Check only lowest setting works - double factor = 3.; - // set both, but really only one - ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, -1, HPWH::UNITS_KW) == 0); // Check sets - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW), factor * elementPower_kW)); // Check gets just bottom with both - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), factor * elementPower_kW)); // Check gets bottom with bottom - ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == HPWH::HPWH_ABORT); // only have one element - - // set lowest - factor = 4.; - ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 0, HPWH::UNITS_KW) == 0); // Set just bottom - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW), factor * elementPower_kW)); // Check gets just bottom with both - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), factor * elementPower_kW)); // Check gets bottom with bottom - ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == HPWH::HPWH_ABORT); // only have one element - - ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 1, HPWH::UNITS_KW) == HPWH::HPWH_ABORT); // set top returns error +void testCommercialTankErrorsWithBottomElement() +{ + HPWH hpwh; + double elementPower_kW = 10.; // KW + // init model + hpwh.HPWHinit_resTankGeneric(800., 10., 0., elementPower_kW * 1000.); + + // Check only lowest setting works + double factor = 3.; + // set both, but really only one + ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, -1, HPWH::UNITS_KW) == + 0); // Check sets + ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check gets just bottom with both + ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check gets bottom with bottom + ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == + HPWH::HPWH_ABORT); // only have one element + + // set lowest + factor = 4.; + ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 0, HPWH::UNITS_KW) == + 0); // Set just bottom + ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check gets just bottom with both + ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check gets bottom with bottom + ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == + HPWH::HPWH_ABORT); // only have one element + + ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 1, HPWH::UNITS_KW) == + HPWH::HPWH_ABORT); // set top returns error } -void testCommercialTankErrorsWithTopElement() { - HPWH hpwh; - double elementPower_kW = 10.; //KW - // init model - hpwh.HPWHinit_resTankGeneric(800., 10., elementPower_kW * 1000., 0.); - - // Check only bottom setting works - double factor = 3.; - // set both, but only bottom really. - ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, -1, HPWH::UNITS_KW) == 0); // Check sets - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW), factor * elementPower_kW)); // Check gets just bottom which is now top with both - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), factor * elementPower_kW)); // Check the lower and only element - ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == HPWH::HPWH_ABORT); // error on non existant element - - // set top - factor = 4.; - ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 0, HPWH::UNITS_KW) == 0); // only one element to set - ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), factor * elementPower_kW)); // Check gets just bottom which is now top with both - ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == HPWH::HPWH_ABORT); // error on non existant bottom - - // set bottom returns error - ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 2, HPWH::UNITS_KW) == HPWH::HPWH_ABORT); +void testCommercialTankErrorsWithTopElement() +{ + HPWH hpwh; + double elementPower_kW = 10.; // KW + // init model + hpwh.HPWHinit_resTankGeneric(800., 10., elementPower_kW * 1000., 0.); + + // Check only bottom setting works + double factor = 3.; + // set both, but only bottom really. + ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, -1, HPWH::UNITS_KW) == + 0); // Check sets + ASSERTTRUE( + relcmpd(hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check gets just bottom which is now top with both + ASSERTTRUE(relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check the lower and only element + ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == + HPWH::HPWH_ABORT); // error on non existant element + + // set top + factor = 4.; + ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 0, HPWH::UNITS_KW) == + 0); // only one element to set + ASSERTTRUE( + relcmpd(hpwh.getResistanceCapacity(0, HPWH::UNITS_KW), + factor * elementPower_kW)); // Check gets just bottom which is now top with both + ASSERTTRUE(hpwh.getResistanceCapacity(1, HPWH::UNITS_KW) == + HPWH::HPWH_ABORT); // error on non existant bottom + + // set bottom returns error + ASSERTTRUE(hpwh.setResistanceCapacity(factor * elementPower_kW, 2, HPWH::UNITS_KW) == + HPWH::HPWH_ABORT); } -struct InsulationPoint { - double volume_L; - double rValue_IP; - double expectedUA_SI; +struct InsulationPoint +{ + double volume_L; + double rValue_IP; + double expectedUA_SI; }; #define R_TO_RSI(rvalue) rvalue * 0.176110 -#define INITGEN(point) hpwh.HPWHinit_resTankGeneric(point.volume_L, R_TO_RSI(point.rValue_IP), elementPower_kW * 1000., elementPower_kW * 1000.) -void testCommercialTankInit() { - HPWH hpwh; - double elementPower_kW = 10.; //KW - double UA; - const InsulationPoint testPoint800 = { 800., 10., 10.500366 }; - const InsulationPoint testPoint2 = { 2., 6., 0.322364 }; - const InsulationPoint testPoint50 = { 50., 12., 1.37808 }; - const InsulationPoint testPoint200 = { 200., 16., 2.604420 }; - const InsulationPoint testPoint200B = { 200., 6., 6.94512163 }; - const InsulationPoint testPoint2000 = { 2000., 16., 12.0886496 }; - const InsulationPoint testPoint20000 = { 20000., 6., 149.628109 }; - - - // Check UA is as expected at 800 gal - INITGEN(testPoint800); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint800.expectedUA_SI, 0.0001)); - // Check UA independent of elements - hpwh.HPWHinit_resTankGeneric(testPoint800.volume_L, R_TO_RSI(testPoint800.rValue_IP), elementPower_kW, elementPower_kW); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint800.expectedUA_SI, 0.0001)); - - // Check UA is as expected at 2 gal - INITGEN(testPoint2); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint2.expectedUA_SI, 0.0001)); - - // Check UA is as expected at 50 gal - INITGEN(testPoint50); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint50.expectedUA_SI, 0.0001)); - - // Check UA is as expected at 200 gal - INITGEN(testPoint200); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint200.expectedUA_SI, 0.0001)); - - INITGEN(testPoint200B); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint200B.expectedUA_SI, 0.0001)); - - // Check UA is as expected at 2000 gal - INITGEN(testPoint2000); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint2000.expectedUA_SI, 0.0001)); - - // Check UA is as expected at 20000 gal - INITGEN(testPoint20000); - hpwh.getUA(UA); - ASSERTTRUE(relcmpd(UA, testPoint20000.expectedUA_SI, 0.0001)); +#define INITGEN(point) \ + hpwh.HPWHinit_resTankGeneric(point.volume_L, \ + R_TO_RSI(point.rValue_IP), \ + elementPower_kW * 1000., \ + elementPower_kW * 1000.) +void testCommercialTankInit() +{ + HPWH hpwh; + double elementPower_kW = 10.; // KW + double UA; + const InsulationPoint testPoint800 = {800., 10., 10.500366}; + const InsulationPoint testPoint2 = {2., 6., 0.322364}; + const InsulationPoint testPoint50 = {50., 12., 1.37808}; + const InsulationPoint testPoint200 = {200., 16., 2.604420}; + const InsulationPoint testPoint200B = {200., 6., 6.94512163}; + const InsulationPoint testPoint2000 = {2000., 16., 12.0886496}; + const InsulationPoint testPoint20000 = {20000., 6., 149.628109}; + + // Check UA is as expected at 800 gal + INITGEN(testPoint800); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint800.expectedUA_SI, 0.0001)); + // Check UA independent of elements + hpwh.HPWHinit_resTankGeneric( + testPoint800.volume_L, R_TO_RSI(testPoint800.rValue_IP), elementPower_kW, elementPower_kW); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint800.expectedUA_SI, 0.0001)); + + // Check UA is as expected at 2 gal + INITGEN(testPoint2); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint2.expectedUA_SI, 0.0001)); + + // Check UA is as expected at 50 gal + INITGEN(testPoint50); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint50.expectedUA_SI, 0.0001)); + + // Check UA is as expected at 200 gal + INITGEN(testPoint200); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint200.expectedUA_SI, 0.0001)); + + INITGEN(testPoint200B); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint200B.expectedUA_SI, 0.0001)); + + // Check UA is as expected at 2000 gal + INITGEN(testPoint2000); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint2000.expectedUA_SI, 0.0001)); + + // Check UA is as expected at 20000 gal + INITGEN(testPoint20000); + hpwh.getUA(UA); + ASSERTTRUE(relcmpd(UA, testPoint20000.expectedUA_SI, 0.0001)); } #undef INITGEN #undef R_TO_RSI int main(int, char*) { - testSetResistanceCapacityErrorChecks(); // Check the resistance reset throws errors when expected. + testSetResistanceCapacityErrorChecks(); // Check the resistance reset throws errors when + // expected. - testGetSetResistanceErrors(); // Check can make ER residential tank with one lower element, and can't set/get upper + testGetSetResistanceErrors(); // Check can make ER residential tank with one lower element, and + // can't set/get upper - testCommercialTankInitErrors(); // test it inits as expected - testGetNumResistanceElements(); // unit test on getNumResistanceElements() - testGetResistancePositionInRETank(); // unit test on getResistancePosition() for an RE tank - testGetResistancePositionInCompressorTank(); // unit test on getResistancePosition() for a compressor + testCommercialTankInitErrors(); // test it inits as expected + testGetNumResistanceElements(); // unit test on getNumResistanceElements() + testGetResistancePositionInRETank(); // unit test on getResistancePosition() for an RE tank + testGetResistancePositionInCompressorTank(); // unit test on getResistancePosition() for a + // compressor - testCommercialTankErrorsWithBottomElement(); // - testCommercialTankErrorsWithTopElement(); - testCommercialTankInit(); // + testCommercialTankErrorsWithBottomElement(); // + testCommercialTankErrorsWithTopElement(); + testCommercialTankInit(); // - //Made it through the gauntlet - return 0; + // Made it through the gauntlet + return 0; } diff --git a/test/testScaleHPWH.cc b/test/testScaleHPWH.cc index e1f83390..f199c633 100644 --- a/test/testScaleHPWH.cc +++ b/test/testScaleHPWH.cc @@ -9,522 +9,570 @@ #include "testUtilityFcts.cc" #include -#include +#include using std::cout; using std::string; -struct performance { - double input, output, cop; +struct performance +{ + double input, output, cop; }; -void getCompressorPerformance(HPWH &hpwh, performance &point, double waterTempC, double airTempC, double setpointC) { - if (hpwh.isCompressorMultipass()) { // Multipass capacity looks at the average of the temperature of the lift - hpwh.setSetpoint((waterTempC + setpointC) / 2.); - } - else { - hpwh.setSetpoint(waterTempC); - } - hpwh.resetTankToSetpoint(); //Force tank cold - hpwh.setSetpoint(setpointC); - - // Run the step - hpwh.runOneStep(waterTempC, // Inlet water temperature (C ) - 0., // Flow in gallons - airTempC, // Ambient Temp (C) - airTempC, // External Temp (C) - //HPWH::DR_TOO // DR Status (now an enum. Fixed for now as allow) - (HPWH::DR_TOO | HPWH::DR_LOR) // DR Status (now an enum. Fixed for now as allow) - ); - - // Check the heat in and out of the compressor - point.input = hpwh.getNthHeatSourceEnergyInput(hpwh.getCompressorIndex()); - point.output = hpwh.getNthHeatSourceEnergyOutput(hpwh.getCompressorIndex()); - point.cop = point.output / point.input ; +void getCompressorPerformance( + HPWH& hpwh, performance& point, double waterTempC, double airTempC, double setpointC) +{ + if (hpwh.isCompressorMultipass()) + { // Multipass capacity looks at the average of the + // temperature of the lift + hpwh.setSetpoint((waterTempC + setpointC) / 2.); + } + else + { + hpwh.setSetpoint(waterTempC); + } + hpwh.resetTankToSetpoint(); // Force tank cold + hpwh.setSetpoint(setpointC); + + // Run the step + hpwh.runOneStep(waterTempC, // Inlet water temperature (C ) + 0., // Flow in gallons + airTempC, // Ambient Temp (C) + airTempC, // External Temp (C) + // HPWH::DR_TOO // DR Status (now an enum. Fixed for now as allow) + (HPWH::DR_TOO | HPWH::DR_LOR) // DR Status (now an enum. Fixed for now as allow) + ); + + // Check the heat in and out of the compressor + point.input = hpwh.getNthHeatSourceEnergyInput(hpwh.getCompressorIndex()); + point.output = hpwh.getNthHeatSourceEnergyOutput(hpwh.getCompressorIndex()); + point.cop = point.output / point.input; } -void scaleCapacityCOP(HPWH &hpwh, double scaleInput, double scaleCOP, performance &point0, performance &point1, - double waterTempC = F_TO_C(63), double airTempC = F_TO_C(77), double setpointC = F_TO_C(135)) { - // Get peformance unscalled - getCompressorPerformance(hpwh, point0, waterTempC, airTempC, setpointC); +void scaleCapacityCOP(HPWH& hpwh, + double scaleInput, + double scaleCOP, + performance& point0, + performance& point1, + double waterTempC = F_TO_C(63), + double airTempC = F_TO_C(77), + double setpointC = F_TO_C(135)) +{ + // Get peformance unscalled + getCompressorPerformance(hpwh, point0, waterTempC, airTempC, setpointC); - // Scale the compressor - int val = hpwh.setScaleHPWHCapacityCOP(scaleInput, scaleCOP); - ASSERTTRUE(val == 0); + // Scale the compressor + int val = hpwh.setScaleHPWHCapacityCOP(scaleInput, scaleCOP); + ASSERTTRUE(val == 0); - // Get the scaled performance - getCompressorPerformance(hpwh, point1, waterTempC, airTempC, setpointC); + // Get the scaled performance + getCompressorPerformance(hpwh, point1, waterTempC, airTempC, setpointC); } -void testNoScaleOutOfBounds() { // Test that we can NOT scale the scalable model with scale <= 0 - HPWH hpwh; +void testNoScaleOutOfBounds() +{ // Test that we can NOT scale the scalable model with scale <= 0 + HPWH hpwh; - string input = "TamScalable_SP"; + string input = "TamScalable_SP"; - double num; + double num; - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - num = 0; - ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(num, 1.) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., num) == HPWH::HPWH_ABORT); + num = 0; + ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(num, 1.) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., num) == HPWH::HPWH_ABORT); - num = -1; - ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(num, 1.) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., num) == HPWH::HPWH_ABORT); + num = -1; + ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(num, 1.) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., num) == HPWH::HPWH_ABORT); } -void testNoneScalable() { // Test a model that is not scalable - HPWH hpwh; +void testNoneScalable() +{ // Test a model that is not scalable + HPWH hpwh; - string input = "AOSmithCAHP120"; + string input = "AOSmithCAHP120"; - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., 1.) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., 1.) == HPWH::HPWH_ABORT); } -void testScalableHPWHScales() { // Test the scalable hpwh can scale - HPWH hpwh; - - string input = "TamScalable_SP"; - - performance point0, point1; - double num, anotherNum; - - // get preset model - getHPWHObject(hpwh, input); - - num = 0.001; // Very low - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 0.2; // normal but bad low - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 3.; // normal but pretty high - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 1000; // really high - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 10; - anotherNum = 0.1; // weird high and low - scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); - - scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 1.5; - anotherNum = 0.9; // real high and low - scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); - - scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); +void testScalableHPWHScales() +{ // Test the scalable hpwh can scale + HPWH hpwh; + + string input = "TamScalable_SP"; + + performance point0, point1; + double num, anotherNum; + + // get preset model + getHPWHObject(hpwh, input); + + num = 0.001; // Very low + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 0.2; // normal but bad low + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 3.; // normal but pretty high + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 1000; // really high + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 10; + anotherNum = 0.1; // weird high and low + scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); + + scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 1.5; + anotherNum = 0.9; // real high and low + scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); + + scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); } -void testScalableMPHPWHScales() { // Test the scalable MP hpwh can scale - HPWH hpwh; - - string input = "Scalable_MP"; - - performance point0, point1; - double num, anotherNum; - - // get preset model - getHPWHObject(hpwh, input); - - num = 0.001; // Very low - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 0.2; // normal but bad low - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 3.; // normal but pretty high - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 1000; // really high - scaleCapacityCOP(hpwh, num, 1., point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop)); - - scaleCapacityCOP(hpwh, 1., num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input)); - ASSERTTRUE(cmpd(point0.output, point1.output / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - scaleCapacityCOP(hpwh, num, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 10; - anotherNum = 0.1; // weird high and low - scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); - - scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); - - num = 1.5; - anotherNum = 0.9; // real high and low - scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / num)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); - - scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); - ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); - ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); - ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); +void testScalableMPHPWHScales() +{ // Test the scalable MP hpwh can scale + HPWH hpwh; + + string input = "Scalable_MP"; + + performance point0, point1; + double num, anotherNum; + + // get preset model + getHPWHObject(hpwh, input); + + num = 0.001; // Very low + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 0.2; // normal but bad low + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 3.; // normal but pretty high + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 1000; // really high + scaleCapacityCOP(hpwh, num, 1., point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop)); + + scaleCapacityCOP(hpwh, 1., num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input)); + ASSERTTRUE(cmpd(point0.output, point1.output / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + scaleCapacityCOP(hpwh, num, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / num)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 10; + anotherNum = 0.1; // weird high and low + scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); + + scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); + + num = 1.5; + anotherNum = 0.9; // real high and low + scaleCapacityCOP(hpwh, num, anotherNum, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / num)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / anotherNum)); + + scaleCapacityCOP(hpwh, anotherNum, num, point0, point1); + ASSERTTRUE(cmpd(point0.input, point1.input / anotherNum)); + ASSERTTRUE(cmpd(point0.output, point1.output / num / anotherNum)); + ASSERTTRUE(cmpd(point0.cop, point1.cop / num)); } -void testSPGetCompressorCapacity() { - HPWH hpwh; +void testSPGetCompressorCapacity() +{ + HPWH hpwh; - string input = "ColmacCxA_20_SP"; + string input = "ColmacCxA_20_SP"; - performance point0; + performance point0; - double capacity_kWH, capacity_BTU; - double waterTempC = F_TO_C(63); - double airTempC = F_TO_C(77); - double setpointC = F_TO_C(135); + double capacity_kWH, capacity_BTU; + double waterTempC = F_TO_C(63); + double airTempC = F_TO_C(77); + double setpointC = F_TO_C(135); - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - getCompressorPerformance(hpwh, point0, waterTempC, airTempC, setpointC); // gives kWH - capacity_kWH = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC) / 60; // div 60 to kWh because I know above only runs 1 minute + getCompressorPerformance(hpwh, point0, waterTempC, airTempC, setpointC); // gives kWH + capacity_kWH = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC) / + 60; // div 60 to kWh because I know above only runs 1 minute - ASSERTTRUE(cmpd(point0.output, capacity_kWH)); + ASSERTTRUE(cmpd(point0.output, capacity_kWH)); - //Test with IP units - capacity_BTU = hpwh.getCompressorCapacity(C_TO_F(airTempC), C_TO_F(waterTempC), C_TO_F(setpointC), HPWH::UNITS_BTUperHr, HPWH::UNITS_F) / 60; // div 60 to BTU because I know above only runs 1 minute - ASSERTTRUE(relcmpd(KWH_TO_BTU(point0.output), capacity_BTU)); // relative cmp since in btu's these will be large numbers + // Test with IP units + capacity_BTU = hpwh.getCompressorCapacity(C_TO_F(airTempC), + C_TO_F(waterTempC), + C_TO_F(setpointC), + HPWH::UNITS_BTUperHr, + HPWH::UNITS_F) / + 60; // div 60 to BTU because I know above only runs 1 minute + ASSERTTRUE(relcmpd(KWH_TO_BTU(point0.output), + capacity_BTU)); // relative cmp since in btu's these will be large numbers } -void testMPGetCompressorCapacity() { - HPWH hpwh; - - string input = "ColmacCxA_20_MP"; - - performance point0; - - double capacity_kWH, capacity_BTU; - double waterTempC = F_TO_C(50); // 50 and 126 to make average 88 - double airTempC = F_TO_C(61.7); - double setpointC = F_TO_C(126); - - // get preset model - getHPWHObject(hpwh, input); - capacity_kWH = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC) / 60.; // div 60 to kWh because I know above only runs 1 minute - getCompressorPerformance(hpwh, point0, waterTempC, airTempC, setpointC); // gives kWH - ASSERTTRUE(cmpd(point0.output, capacity_kWH)); - - //Test with IP units - capacity_BTU = hpwh.getCompressorCapacity(C_TO_F(airTempC), C_TO_F(waterTempC), C_TO_F(setpointC), HPWH::UNITS_BTUperHr, HPWH::UNITS_F) / 60; // div 60 to BTU because I know above only runs 1 minute - ASSERTTRUE(relcmpd(KWH_TO_BTU(point0.output), capacity_BTU, 0.0001)); // relative cmp since in btu's these will be large numbers +void testMPGetCompressorCapacity() +{ + HPWH hpwh; + + string input = "ColmacCxA_20_MP"; + + performance point0; + + double capacity_kWH, capacity_BTU; + double waterTempC = F_TO_C(50); // 50 and 126 to make average 88 + double airTempC = F_TO_C(61.7); + double setpointC = F_TO_C(126); + + // get preset model + getHPWHObject(hpwh, input); + capacity_kWH = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC) / + 60.; // div 60 to kWh because I know above only runs 1 minute + getCompressorPerformance(hpwh, point0, waterTempC, airTempC, setpointC); // gives kWH + ASSERTTRUE(cmpd(point0.output, capacity_kWH)); + + // Test with IP units + capacity_BTU = hpwh.getCompressorCapacity(C_TO_F(airTempC), + C_TO_F(waterTempC), + C_TO_F(setpointC), + HPWH::UNITS_BTUperHr, + HPWH::UNITS_F) / + 60; // div 60 to BTU because I know above only runs 1 minute + ASSERTTRUE(relcmpd(KWH_TO_BTU(point0.output), + capacity_BTU, + 0.0001)); // relative cmp since in btu's these will be large numbers } - -void testSetMPCompressorOutputCapacity() { - HPWH hpwh; - - string input = "Scalable_MP"; - - double newCapacity_kW, num; - double waterTempC = F_TO_C(44); - double airTempC = F_TO_C(98); - double setpointC = F_TO_C(150); - - // get preset model - getHPWHObject(hpwh, input); - - //Scale output to 1 kW - num = 1.; - hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTTRUE(cmpd(num, newCapacity_kW)); - - //Scale output to .01 kW - num = .01; - hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTTRUE(cmpd(num, newCapacity_kW)); - - //Scale output to 1000 kW - num = 1000.; - hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTTRUE(cmpd(num, newCapacity_kW)); - - // Check again with changed setpoint, for MP it should affect output capacity since it looks at the mean temperature for the cycle. - setpointC = F_TO_C(100); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTFALSE(cmpd(num, newCapacity_kW)); +void testSetMPCompressorOutputCapacity() +{ + HPWH hpwh; + + string input = "Scalable_MP"; + + double newCapacity_kW, num; + double waterTempC = F_TO_C(44); + double airTempC = F_TO_C(98); + double setpointC = F_TO_C(150); + + // get preset model + getHPWHObject(hpwh, input); + + // Scale output to 1 kW + num = 1.; + hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTTRUE(cmpd(num, newCapacity_kW)); + + // Scale output to .01 kW + num = .01; + hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTTRUE(cmpd(num, newCapacity_kW)); + + // Scale output to 1000 kW + num = 1000.; + hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTTRUE(cmpd(num, newCapacity_kW)); + + // Check again with changed setpoint, for MP it should affect output capacity since it looks at + // the mean temperature for the cycle. + setpointC = F_TO_C(100); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTFALSE(cmpd(num, newCapacity_kW)); } -void testSetCompressorOutputCapacity() { - HPWH hpwh; - - string input = "TamScalable_SP"; - - double newCapacity_kW, num; - double waterTempC = F_TO_C(44); - double airTempC = F_TO_C(98); - double setpointC = F_TO_C(145); - - // get preset model - getHPWHObject(hpwh, input); - - //Scale output to 1 kW - num = 1.; - hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTTRUE(cmpd(num, newCapacity_kW)); - - //Scale output to .01 kW - num = .01; - hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTTRUE(cmpd(num, newCapacity_kW)); - - //Scale output to 1000 kW - num = 1000.; - hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); - newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); - ASSERTTRUE(cmpd(num, newCapacity_kW)); - - //Scale output to 1000 kW but let's use do the calc in other units - num = KW_TO_BTUperHR(num); - hpwh.setCompressorOutputCapacity(num, C_TO_F(airTempC), C_TO_F(waterTempC), C_TO_F(setpointC), HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - double newCapacity_BTUperHr = hpwh.getCompressorCapacity(C_TO_F(airTempC), C_TO_F(waterTempC), C_TO_F(setpointC), HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - ASSERTTRUE(relcmpd(num, newCapacity_BTUperHr)); +void testSetCompressorOutputCapacity() +{ + HPWH hpwh; + + string input = "TamScalable_SP"; + + double newCapacity_kW, num; + double waterTempC = F_TO_C(44); + double airTempC = F_TO_C(98); + double setpointC = F_TO_C(145); + + // get preset model + getHPWHObject(hpwh, input); + + // Scale output to 1 kW + num = 1.; + hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTTRUE(cmpd(num, newCapacity_kW)); + + // Scale output to .01 kW + num = .01; + hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTTRUE(cmpd(num, newCapacity_kW)); + + // Scale output to 1000 kW + num = 1000.; + hpwh.setCompressorOutputCapacity(num, airTempC, waterTempC, setpointC); + newCapacity_kW = hpwh.getCompressorCapacity(airTempC, waterTempC, setpointC); + ASSERTTRUE(cmpd(num, newCapacity_kW)); + + // Scale output to 1000 kW but let's use do the calc in other units + num = KW_TO_BTUperHR(num); + hpwh.setCompressorOutputCapacity(num, + C_TO_F(airTempC), + C_TO_F(waterTempC), + C_TO_F(setpointC), + HPWH::UNITS_BTUperHr, + HPWH::UNITS_F); + double newCapacity_BTUperHr = hpwh.getCompressorCapacity(C_TO_F(airTempC), + C_TO_F(waterTempC), + C_TO_F(setpointC), + HPWH::UNITS_BTUperHr, + HPWH::UNITS_F); + ASSERTTRUE(relcmpd(num, newCapacity_BTUperHr)); } -void testChipsCaseWithIPUnits() { - HPWH hpwh; +void testChipsCaseWithIPUnits() +{ + HPWH hpwh; - string input = "TamScalable_SP"; + string input = "TamScalable_SP"; - double waterTempF = 50; - double airTempF = 50; - double setpointF = 120; - double wh_heatingCap = 20000.; + double waterTempF = 50; + double airTempF = 50; + double setpointF = 120; + double wh_heatingCap = 20000.; - // get preset model - getHPWHObject(hpwh, input); + // get preset model + getHPWHObject(hpwh, input); - //Scale output to 20000 btu/hr but let's use do the calc in other units - hpwh.setCompressorOutputCapacity(wh_heatingCap, airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - double newCapacity_BTUperHr = hpwh.getCompressorCapacity(airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); - ASSERTTRUE(relcmpd(wh_heatingCap, newCapacity_BTUperHr)); + // Scale output to 20000 btu/hr but let's use do the calc in other units + hpwh.setCompressorOutputCapacity( + wh_heatingCap, airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + double newCapacity_BTUperHr = hpwh.getCompressorCapacity( + airTempF, waterTempF, setpointF, HPWH::UNITS_BTUperHr, HPWH::UNITS_F); + ASSERTTRUE(relcmpd(wh_heatingCap, newCapacity_BTUperHr)); } -void testScaleRestank() { - HPWH hpwh; +void testScaleRestank() +{ + HPWH hpwh; - string input = "restankRealistic"; - // get preset model - getHPWHObject(hpwh, input); + string input = "restankRealistic"; + // get preset model + getHPWHObject(hpwh, input); - //Scale COP for restank fails. - int val = hpwh.setScaleHPWHCapacityCOP(2., 2.); - ASSERTTRUE(val == HPWH::HPWH_ABORT); + // Scale COP for restank fails. + int val = hpwh.setScaleHPWHCapacityCOP(2., 2.); + ASSERTTRUE(val == HPWH::HPWH_ABORT); } -void testResistanceScales() { - HPWH hpwh; - - string input = "TamScalable_SP"; - double elementPower = 30.0; //KW - - //hpwh.HPWHinit_resTank(); - getHPWHObject(hpwh, input); - - double returnVal; - returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, elementPower)); - returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, elementPower)); - returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, 2.*elementPower)); - - // check units convert - returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_BTUperHr); - ASSERTTRUE(relcmpd(returnVal, 2.*elementPower * 3412.14)); - - // Check setting bottom works - double factor = 2.0; - hpwh.setResistanceCapacity(factor * elementPower, 0); - returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, factor * elementPower)); - returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, elementPower)); - returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, factor * elementPower + elementPower)); - - // Check setting both works - factor = 3.; - hpwh.setResistanceCapacity(factor * elementPower, -1); - returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, factor * elementPower)); - returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, factor * elementPower)); - returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); - ASSERTTRUE(relcmpd(returnVal, 2.*factor * elementPower)); +void testResistanceScales() +{ + HPWH hpwh; + + string input = "TamScalable_SP"; + double elementPower = 30.0; // KW + + // hpwh.HPWHinit_resTank(); + getHPWHObject(hpwh, input); + + double returnVal; + returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, elementPower)); + returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, elementPower)); + returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, 2. * elementPower)); + + // check units convert + returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_BTUperHr); + ASSERTTRUE(relcmpd(returnVal, 2. * elementPower * 3412.14)); + + // Check setting bottom works + double factor = 2.0; + hpwh.setResistanceCapacity(factor * elementPower, 0); + returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, factor * elementPower)); + returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, elementPower)); + returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, factor * elementPower + elementPower)); + + // Check setting both works + factor = 3.; + hpwh.setResistanceCapacity(factor * elementPower, -1); + returnVal = hpwh.getResistanceCapacity(0, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, factor * elementPower)); + returnVal = hpwh.getResistanceCapacity(1, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, factor * elementPower)); + returnVal = hpwh.getResistanceCapacity(-1, HPWH::UNITS_KW); + ASSERTTRUE(relcmpd(returnVal, 2. * factor * elementPower)); } -void testStorageTankErrors() { - HPWH hpwh; - string input = "StorageTank"; - // get preset model - getHPWHObject(hpwh, input); - - ASSERTTRUE(hpwh.setResistanceCapacity(1000.) == HPWH::HPWH_ABORT); - ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., 1.) == HPWH::HPWH_ABORT); +void testStorageTankErrors() +{ + HPWH hpwh; + string input = "StorageTank"; + // get preset model + getHPWHObject(hpwh, input); + ASSERTTRUE(hpwh.setResistanceCapacity(1000.) == HPWH::HPWH_ABORT); + ASSERTTRUE(hpwh.setScaleHPWHCapacityCOP(1., 1.) == HPWH::HPWH_ABORT); } int main(int, char*) { - testSetMPCompressorOutputCapacity(); + testSetMPCompressorOutputCapacity(); + testScalableHPWHScales(); // Test the scalable model scales properly + testNoScaleOutOfBounds(); // Test that models can't scale with invalid inputs - testScalableHPWHScales(); // Test the scalable model scales properly + testNoneScalable(); // Test that models can't scale that are non scalable presets - testNoScaleOutOfBounds(); // Test that models can't scale with invalid inputs - - testNoneScalable(); // Test that models can't scale that are non scalable presets - - testScaleRestank(); // Test the resistance tank can't scale the compressor + testScaleRestank(); // Test the resistance tank can't scale the compressor - testResistanceScales(); // Test the resistance tank scales the resistance elements + testResistanceScales(); // Test the resistance tank scales the resistance elements - testSPGetCompressorCapacity(); //Test we can get the capacity + testSPGetCompressorCapacity(); // Test we can get the capacity - testSetCompressorOutputCapacity(); //Test we can set the capacity with a specific number + testSetCompressorOutputCapacity(); // Test we can set the capacity with a specific number - testChipsCaseWithIPUnits(); //Debuging Chip's case + testChipsCaseWithIPUnits(); // Debuging Chip's case - testStorageTankErrors(); // Make sure we can't scale the storage tank. + testStorageTankErrors(); // Make sure we can't scale the storage tank. - testScalableMPHPWHScales(); // Test for proper scaling in the MP scalable model + testScalableMPHPWHScales(); // Test for proper scaling in the MP scalable model - testMPGetCompressorCapacity(); // Test MP capacity in and out correct. + testMPGetCompressorCapacity(); // Test MP capacity in and out correct. - //testSetMPCompressorOutputCapacity(); // Tets MP capacity can be set correctly. + // testSetMPCompressorOutputCapacity(); // Tets MP capacity can be set correctly. - //Made it through the gauntlet - return 0; + // Made it through the gauntlet + return 0; } diff --git a/test/testSizingFractions.cc b/test/testSizingFractions.cc index b3af85eb..9a3664d6 100644 --- a/test/testSizingFractions.cc +++ b/test/testSizingFractions.cc @@ -9,7 +9,7 @@ #include "testUtilityFcts.cc" #include -#include +#include #define SIZE (double)HPWH::CONDENSITY_SIZE @@ -28,144 +28,149 @@ void testGetCompressorMinRuntime(); int main(int, char*) { - testScalableSizingFract(); - testSandenSizingFract(); - testColmacSizingFract(); - testHPTU50SizingFract(); - test220eSizingFract(); - testGESizingFract(); - testResTankSizingFract(); - testStoTankSizingFract(); - - //Made it through the gauntlet - return 0; + testScalableSizingFract(); + testSandenSizingFract(); + testColmacSizingFract(); + testHPTU50SizingFract(); + test220eSizingFract(); + testGESizingFract(); + testResTankSizingFract(); + testStoTankSizingFract(); + + // Made it through the gauntlet + return 0; } +void testScalableSizingFract() +{ + HPWH hpwh; + double AF, pU; + double AF_answer = 4. / SIZE; -void testScalableSizingFract() { - HPWH hpwh; - double AF, pU; - double AF_answer = 4. / SIZE; - - string input = "TamScalable_SP"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "TamScalable_SP"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions( AF, pU); - ASSERTTRUE(val == 0); - ASSERTTRUE(cmpd(AF, AF_answer)); - ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == 0); + ASSERTTRUE(cmpd(AF, AF_answer)); + ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); } -void testSandenSizingFract() { - HPWH hpwh; - double AF, pU; - double AF_answer = 8. / SIZE; +void testSandenSizingFract() +{ + HPWH hpwh; + double AF, pU; + double AF_answer = 8. / SIZE; - string input = "Sanden80"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "Sanden80"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == 0); - ASSERTTRUE(cmpd(AF, AF_answer)); - ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == 0); + ASSERTTRUE(cmpd(AF, AF_answer)); + ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); } -void testColmacSizingFract() { - HPWH hpwh; - double AF, pU; - double AF_answer = 4. / SIZE; +void testColmacSizingFract() +{ + HPWH hpwh; + double AF, pU; + double AF_answer = 4. / SIZE; - string input = "ColmacCxV_5_SP"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "ColmacCxV_5_SP"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == 0); - ASSERTTRUE(cmpd(AF, AF_answer)); - ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == 0); + ASSERTTRUE(cmpd(AF, AF_answer)); + ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); } -void testHPTU50SizingFract() { - HPWH hpwh; - double AF, pU; - double AF_answer = (1. + 2. + 3. + 4.) / 4. / SIZE; +void testHPTU50SizingFract() +{ + HPWH hpwh; + double AF, pU; + double AF_answer = (1. + 2. + 3. + 4.) / 4. / SIZE; - string input = "AOSmithHPTU50"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "AOSmithHPTU50"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == 0); - ASSERTTRUE(cmpd(AF, AF_answer)); - ASSERTTRUE(cmpd(pU, 1.)); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == 0); + ASSERTTRUE(cmpd(AF, AF_answer)); + ASSERTTRUE(cmpd(pU, 1.)); } -void testGESizingFract() { - HPWH hpwh; - double AF, pU; - double AF_answer = (1. + 2. + 3. + 4.) / 4. / SIZE; +void testGESizingFract() +{ + HPWH hpwh; + double AF, pU; + double AF_answer = (1. + 2. + 3. + 4.) / 4. / SIZE; - string input = "GE"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "GE"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == 0); - ASSERTTRUE(cmpd(AF, AF_answer)); - ASSERTTRUE(cmpd(pU, 1.)); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == 0); + ASSERTTRUE(cmpd(AF, AF_answer)); + ASSERTTRUE(cmpd(pU, 1.)); } -void test220eSizingFract() { - HPWH hpwh; - double AF, pU; - double AF_answer = (5. + 6.) / 2. / SIZE; +void test220eSizingFract() +{ + HPWH hpwh; + double AF, pU; + double AF_answer = (5. + 6.) / 2. / SIZE; - string input = "Stiebel220e"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "Stiebel220e"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == 0); - ASSERTTRUE(cmpd(AF, AF_answer)); - ASSERTTRUE(cmpd(pU, 1 - 1./SIZE)); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == 0); + ASSERTTRUE(cmpd(AF, AF_answer)); + ASSERTTRUE(cmpd(pU, 1 - 1. / SIZE)); } +void testResTankSizingFract() +{ + HPWH hpwh; + double AF, pU; -void testResTankSizingFract() { - HPWH hpwh; - double AF, pU; - - string input = "restankRealistic"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "restankRealistic"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == HPWH::HPWH_ABORT); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == HPWH::HPWH_ABORT); } -void testStoTankSizingFract() { - HPWH hpwh; - double AF, pU; +void testStoTankSizingFract() +{ + HPWH hpwh; + double AF, pU; - string input = "StorageTank"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model + string input = "StorageTank"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model - int val = hpwh.getSizingFractions(AF, pU); - ASSERTTRUE(val == HPWH::HPWH_ABORT); + int val = hpwh.getSizingFractions(AF, pU); + ASSERTTRUE(val == HPWH::HPWH_ABORT); } +void testGetCompressorMinRuntime() +{ + HPWH hpwh; + string input = "TamScalable_SP"; // Just a compressor with R134A + getHPWHObject(hpwh, input); // get preset model -void testGetCompressorMinRuntime() { - HPWH hpwh; - string input = "TamScalable_SP"; // Just a compressor with R134A - getHPWHObject(hpwh, input); // get preset model - - double expected_mins = 10.; - double expected_secs = expected_mins * 60.; - double expected_hrs = expected_mins / 60.; - - double mins = hpwh.getCompressorMinRuntime(); - ASSERTTRUE(mins == expected_mins); + double expected_mins = 10.; + double expected_secs = expected_mins * 60.; + double expected_hrs = expected_mins / 60.; - double secs = hpwh.getCompressorMinRuntime(HPWH::UNITS_SEC); - ASSERTTRUE(secs == expected_secs); + double mins = hpwh.getCompressorMinRuntime(); + ASSERTTRUE(mins == expected_mins); - double hrs = hpwh.getCompressorMinRuntime(HPWH::UNITS_HR); - ASSERTTRUE(hrs == expected_hrs); + double secs = hpwh.getCompressorMinRuntime(HPWH::UNITS_SEC); + ASSERTTRUE(secs == expected_secs); -} \ No newline at end of file + double hrs = hpwh.getCompressorMinRuntime(HPWH::UNITS_HR); + ASSERTTRUE(hrs == expected_hrs); +} diff --git a/test/testStateOfChargeFcts.cc b/test/testStateOfChargeFcts.cc index c3cb90fa..a4a93892 100644 --- a/test/testStateOfChargeFcts.cc +++ b/test/testStateOfChargeFcts.cc @@ -2,78 +2,79 @@ #include "testUtilityFcts.cc" #include -#include +#include void testGetStateOfCharge(); void testChargeBelowSetpoint(); - int main() { - testGetStateOfCharge(); - testChargeBelowSetpoint(); + testGetStateOfCharge(); + testChargeBelowSetpoint(); } -void testGetStateOfCharge() { - HPWH hpwh; - string input = "Sanden80"; - getHPWHObject(hpwh, input); - double tMains_C = F_TO_C(55.); - double tMinUseful_C = F_TO_C(110.); - double chargeFraction; +void testGetStateOfCharge() +{ + HPWH hpwh; + string input = "Sanden80"; + getHPWHObject(hpwh, input); + double tMains_C = F_TO_C(55.); + double tMinUseful_C = F_TO_C(110.); + double chargeFraction; - // Check for errors - chargeFraction = hpwh.calcSoCFraction(F_TO_C(125.), tMinUseful_C); - ASSERTTRUE(chargeFraction == HPWH::HPWH_ABORT); - chargeFraction = hpwh.calcSoCFraction(tMains_C, F_TO_C(155.)); - ASSERTTRUE(chargeFraction == HPWH::HPWH_ABORT); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(100.)); - ASSERTTRUE(chargeFraction == HPWH::HPWH_ABORT); + // Check for errors + chargeFraction = hpwh.calcSoCFraction(F_TO_C(125.), tMinUseful_C); + ASSERTTRUE(chargeFraction == HPWH::HPWH_ABORT); + chargeFraction = hpwh.calcSoCFraction(tMains_C, F_TO_C(155.)); + ASSERTTRUE(chargeFraction == HPWH::HPWH_ABORT); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(100.)); + ASSERTTRUE(chargeFraction == HPWH::HPWH_ABORT); - // Check state of charge returns 1 at setpoint - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C); - ASSERTTRUE(cmpd(chargeFraction, 1.)); - chargeFraction = hpwh.calcSoCFraction(tMains_C + 5., tMinUseful_C); - ASSERTTRUE(cmpd(chargeFraction, 1.)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C + 5.); - ASSERTTRUE(cmpd(chargeFraction, 1.)); + // Check state of charge returns 1 at setpoint + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C); + ASSERTTRUE(cmpd(chargeFraction, 1.)); + chargeFraction = hpwh.calcSoCFraction(tMains_C + 5., tMinUseful_C); + ASSERTTRUE(cmpd(chargeFraction, 1.)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C + 5.); + ASSERTTRUE(cmpd(chargeFraction, 1.)); - // Varying max temp - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(110.)); - ASSERTTRUE(cmpd(chargeFraction, 1.70909)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(120.)); - ASSERTTRUE(cmpd(chargeFraction, 1.4461)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(135.)); - ASSERTTRUE(cmpd(chargeFraction, 1.175)); + // Varying max temp + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(110.)); + ASSERTTRUE(cmpd(chargeFraction, 1.70909)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(120.)); + ASSERTTRUE(cmpd(chargeFraction, 1.4461)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(135.)); + ASSERTTRUE(cmpd(chargeFraction, 1.175)); } -void testChargeBelowSetpoint() { - HPWH hpwh; - string input = "ColmacCxV_5_SP"; - getHPWHObject(hpwh, input); - double tMains_C = F_TO_C(60.); - double tMinUseful_C = F_TO_C(110.); - double chargeFraction; +void testChargeBelowSetpoint() +{ + HPWH hpwh; + string input = "ColmacCxV_5_SP"; + getHPWHObject(hpwh, input); + double tMains_C = F_TO_C(60.); + double tMinUseful_C = F_TO_C(110.); + double chargeFraction; - // Check state of charge returns 1 at setpoint - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C); - ASSERTTRUE(cmpd(chargeFraction, 1.)); + // Check state of charge returns 1 at setpoint + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C); + ASSERTTRUE(cmpd(chargeFraction, 1.)); - // Check state of charge returns 0 when tank below useful - hpwh.setTankToTemperature(F_TO_C(109.)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); - ASSERTTRUE(cmpd(chargeFraction, 0.)); + // Check state of charge returns 0 when tank below useful + hpwh.setTankToTemperature(F_TO_C(109.)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); + ASSERTTRUE(cmpd(chargeFraction, 0.)); - // Check some lower values with tank set at constant temperatures - hpwh.setTankToTemperature(F_TO_C(110.)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); - ASSERTTRUE(cmpd(chargeFraction, 0.625)); + // Check some lower values with tank set at constant temperatures + hpwh.setTankToTemperature(F_TO_C(110.)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); + ASSERTTRUE(cmpd(chargeFraction, 0.625)); - hpwh.setTankToTemperature(F_TO_C(120.)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); - ASSERTTRUE(cmpd(chargeFraction, 0.75)); + hpwh.setTankToTemperature(F_TO_C(120.)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); + ASSERTTRUE(cmpd(chargeFraction, 0.75)); - hpwh.setTankToTemperature(F_TO_C(130.)); - chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); - ASSERTTRUE(cmpd(chargeFraction, 0.875)); + hpwh.setTankToTemperature(F_TO_C(130.)); + chargeFraction = hpwh.calcSoCFraction(tMains_C, tMinUseful_C, F_TO_C(140.)); + ASSERTTRUE(cmpd(chargeFraction, 0.875)); } diff --git a/test/testTankSizeFixed.cc b/test/testTankSizeFixed.cc index be4886d2..af286fcc 100644 --- a/test/testTankSizeFixed.cc +++ b/test/testTankSizeFixed.cc @@ -9,8 +9,7 @@ #include "testUtilityFcts.cc" #include -#include - +#include using std::cout; using std::string; @@ -18,97 +17,119 @@ using std::string; int testForceChangeTankSize(HPWH::MODELS model); int testIsTankSizeFixed(HPWH::MODELS model); -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { - HPWH::MODELS presetModel; - - string input;// = "AOSmithCAHP120"; - - if (argc != 2) { - cout << "Invalid input. This program takes One arguments: preset model specification (ie. Sanden80). Recieved input: \n"; - for (int ii = 0; ii < argc; ii++) { - cout << argv[ii] << " "; - } - exit(1); - } - else { - input = argv[1]; - } - - // get model number - presetModel = mapStringToPreset(input); - - ASSERTTRUE(testIsTankSizeFixed(presetModel) == 0, "Tank size is not fixed"); - ASSERTTRUE(testForceChangeTankSize(presetModel) == 0, "Tank size was not forced to change"); - - //Made it through the gauntlet - return 0; + HPWH::MODELS presetModel; + + string input; // = "AOSmithCAHP120"; + + if (argc != 2) + { + cout << "Invalid input. This program takes One arguments: preset model specification (ie. " + "Sanden80). Recieved input: \n"; + for (int ii = 0; ii < argc; ii++) + { + cout << argv[ii] << " "; + } + exit(1); + } + else + { + input = argv[1]; + } + + // get model number + presetModel = mapStringToPreset(input); + + ASSERTTRUE(testIsTankSizeFixed(presetModel) == 0, "Tank size is not fixed"); + ASSERTTRUE(testForceChangeTankSize(presetModel) == 0, "Tank size was not forced to change"); + + // Made it through the gauntlet + return 0; } -int testIsTankSizeFixed(HPWH::MODELS model) { - HPWH hpwh; - - // set preset - if (hpwh.HPWHinit_presets(model) != 0) return 1; - - double originalTankSize = hpwh.getTankSize(); - double newTankSize = originalTankSize + 100; - - // change the tank size - int result = hpwh.setTankSize(newTankSize); - - if (result != 0 && result != HPWH::HPWH_ABORT) { - cout << "Error, setTankSize() returned an invalid result: " << result << "\n"; - return 1; - } - - if (hpwh.isTankSizeFixed()) { // better not have change! - if (result == 0) { - cout << "Error, setTankSize() returned 0 when should be HPWH_ABORT\n"; - return 1; - } - if (originalTankSize != hpwh.getTankSize()) { - cout << "Error, the tank size has changed when isTankSizeFixed is true\n"; - return 1; - } - } - else { // it better have changed - if (result != 0) { - cout << "Error, setTankSize() returned HPWH_ABORT when it should be 0\n"; - return 1; - } - if (newTankSize != hpwh.getTankSize()) { - cout << "Error, the tank size hasn't changed to the new tank size when it should have. New Size: " << newTankSize << ". Returned Value: " << hpwh.getTankSize() << "\n"; - return 1; - } - } - return 0; +int testIsTankSizeFixed(HPWH::MODELS model) +{ + HPWH hpwh; + + // set preset + if (hpwh.HPWHinit_presets(model) != 0) + return 1; + + double originalTankSize = hpwh.getTankSize(); + double newTankSize = originalTankSize + 100; + + // change the tank size + int result = hpwh.setTankSize(newTankSize); + + if (result != 0 && result != HPWH::HPWH_ABORT) + { + cout << "Error, setTankSize() returned an invalid result: " << result << "\n"; + return 1; + } + + if (hpwh.isTankSizeFixed()) + { // better not have change! + if (result == 0) + { + cout << "Error, setTankSize() returned 0 when should be HPWH_ABORT\n"; + return 1; + } + if (originalTankSize != hpwh.getTankSize()) + { + cout << "Error, the tank size has changed when isTankSizeFixed is true\n"; + return 1; + } + } + else + { // it better have changed + if (result != 0) + { + cout << "Error, setTankSize() returned HPWH_ABORT when it should be 0\n"; + return 1; + } + if (newTankSize != hpwh.getTankSize()) + { + cout << "Error, the tank size hasn't changed to the new tank size when it should have. " + "New Size: " + << newTankSize << ". Returned Value: " << hpwh.getTankSize() << "\n"; + return 1; + } + } + return 0; } -int testForceChangeTankSize(HPWH::MODELS model) { - HPWH hpwh; - - // set preset - if (hpwh.HPWHinit_presets(model) != 0) return 1; - - double newTankSize = 133.312; //No way a tank has this size originally - - // change the tank size - int result = hpwh.setTankSize(newTankSize, HPWH::UNITS_GAL, true); - - if (result != 0 && result != HPWH::HPWH_ABORT) { - cout << "Error, setTankSize() returned an invalid result: " << result << "\n"; - return 1; - } - - // it better have changed - if (result != 0) { - cout << "Error, setTankSize() returned HPWH_ABORT when it should be 0\n"; - return 1; - } - if (newTankSize != hpwh.getTankSize(HPWH::UNITS_GAL)) { - cout << "Error, the tank size hasn't changed to the new tank size when it should have. New Size: " << newTankSize << ". Returned Value: " << hpwh.getTankSize(HPWH::UNITS_GAL) << "\n"; - return 1; - } - return 0; +int testForceChangeTankSize(HPWH::MODELS model) +{ + HPWH hpwh; + + // set preset + if (hpwh.HPWHinit_presets(model) != 0) + return 1; + + double newTankSize = 133.312; // No way a tank has this size originally + + // change the tank size + int result = hpwh.setTankSize(newTankSize, HPWH::UNITS_GAL, true); + + if (result != 0 && result != HPWH::HPWH_ABORT) + { + cout << "Error, setTankSize() returned an invalid result: " << result << "\n"; + return 1; + } + + // it better have changed + if (result != 0) + { + cout << "Error, setTankSize() returned HPWH_ABORT when it should be 0\n"; + return 1; + } + if (newTankSize != hpwh.getTankSize(HPWH::UNITS_GAL)) + { + cout << "Error, the tank size hasn't changed to the new tank size when it should have. New " + "Size: " + << newTankSize << ". Returned Value: " << hpwh.getTankSize(HPWH::UNITS_GAL) << "\n"; + return 1; + } + return 0; } diff --git a/test/testUtilityFcts.cc b/test/testUtilityFcts.cc index f1a85566..94e2a76e 100644 --- a/test/testUtilityFcts.cc +++ b/test/testUtilityFcts.cc @@ -10,319 +10,414 @@ */ #include "HPWH.hh" #include -#include +#include using std::cout; using std::string; -#define F_TO_C(T) ((T-32.0)*5.0/9.0) -#define C_TO_F(T) (((9.0/5.0)*T) + 32.0) -#define dF_TO_dC(T) (T*5.0/9.0) -#define GAL_TO_L(GAL) (GAL*3.78541) +#define F_TO_C(T) ((T - 32.0) * 5.0 / 9.0) +#define C_TO_F(T) (((9.0 / 5.0) * T) + 32.0) +#define dF_TO_dC(T) (T * 5.0 / 9.0) +#define GAL_TO_L(GAL) (GAL * 3.78541) #define KW_TO_BTUperHR(KW) (KW * BTUperKWH) #define KWH_TO_BTU(KW) (KW * BTUperKWH) +#define ASSERTTRUE(input, ...) \ + if (!(input)) \ + { \ + cout << "Assertation failed at " << __FILE__ << ", line: " << __LINE__ << ".\n"; \ + exit(1); \ + } +#define ASSERTFALSE(input, ...) \ + if ((input)) \ + { \ + cout << "Assertation failed at " << __FILE__ << ", line: " << __LINE__ << ".\n"; \ + exit(1); \ + } -#define ASSERTTRUE(input, ...) if(! (input)) {cout<< "Assertation failed at " <<__FILE__ << ", line: " << __LINE__ << ".\n"; exit(1);} -#define ASSERTFALSE(input, ...) if( (input)) {cout<< "Assertation failed at " <<__FILE__ << ", line: " << __LINE__ << ".\n"; exit(1);} - - -//Compare doubles -bool cmpd(double A, double B, double epsilon = 0.0001) { - return (fabs(A - B) < epsilon); -} -//Relative Compare doubles -bool relcmpd(double A, double B, double epsilon = 0.00001) { - return fabs(A - B) < (epsilon *(fabs(A) < fabs(B) ? fabs(B) : fabs(A))); +// Compare doubles +bool cmpd(double A, double B, double epsilon = 0.0001) { return (fabs(A - B) < epsilon); } +// Relative Compare doubles +bool relcmpd(double A, double B, double epsilon = 0.00001) +{ + return fabs(A - B) < (epsilon * (fabs(A) < fabs(B) ? fabs(B) : fabs(A))); } -bool compressorIsRunning(HPWH& hpwh) { - return (bool)hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()); +bool compressorIsRunning(HPWH& hpwh) +{ + return (bool)hpwh.isNthHeatSourceRunning(hpwh.getCompressorIndex()); } -HPWH::MODELS mapStringToPreset(string modelName) { +HPWH::MODELS mapStringToPreset(string modelName) +{ - HPWH::MODELS hpwhModel; + HPWH::MODELS hpwhModel; - if(modelName == "Voltex60" || modelName == "AOSmithPHPT60") { - hpwhModel = HPWH::MODELS_AOSmithPHPT60; - } - else if (modelName == "Voltex80" || modelName == "AOSmith80") { - hpwhModel = HPWH::MODELS_AOSmithPHPT80; - } - else if (modelName == "GEred" || modelName == "GE") { - hpwhModel = HPWH::MODELS_GE2012; - } - else if (modelName == "SandenGAU" || modelName == "Sanden80" || modelName == "SandenGen3") { - hpwhModel = HPWH::MODELS_Sanden80; - } - else if (modelName == "Sanden120") { - hpwhModel = HPWH::MODELS_Sanden120; - } - else if (modelName == "SandenGES" || modelName == "Sanden40") { - hpwhModel = HPWH::MODELS_Sanden40; - } - else if (modelName == "AOSmithHPTU50") { - hpwhModel = HPWH::MODELS_AOSmithHPTU50; - } - else if (modelName == "AOSmithHPTU66") { - hpwhModel = HPWH::MODELS_AOSmithHPTU66; - } - else if (modelName == "AOSmithHPTU80") { - hpwhModel = HPWH::MODELS_AOSmithHPTU80; - } - else if (modelName == "AOSmithHPTS50") { - hpwhModel = HPWH::MODELS_AOSmithHPTS50; - } - else if (modelName == "AOSmithHPTS66") { - hpwhModel = HPWH::MODELS_AOSmithHPTS66; - } - else if (modelName == "AOSmithHPTS80") { - hpwhModel = HPWH::MODELS_AOSmithHPTS80; - } - else if (modelName == "AOSmithHPTU80DR") { - hpwhModel = HPWH::MODELS_AOSmithHPTU80_DR; - } - else if (modelName == "GE502014STDMode" || modelName == "GE2014STDMode") { - hpwhModel = HPWH::MODELS_GE2014STDMode; - } - else if (modelName == "GE502014" || modelName == "GE2014") { - hpwhModel = HPWH::MODELS_GE2014; - } - else if (modelName == "GE802014") { - hpwhModel = HPWH::MODELS_GE2014_80DR; - } - else if (modelName == "RheemHB50") { - hpwhModel = HPWH::MODELS_RheemHB50; - } - else if (modelName == "Stiebel220e" || modelName == "Stiebel220E") { - hpwhModel = HPWH::MODELS_Stiebel220E; - } - else if (modelName == "Generic1") { - hpwhModel = HPWH::MODELS_Generic1; - } - else if (modelName == "Generic2") { - hpwhModel = HPWH::MODELS_Generic2; - } - else if (modelName == "Generic3") { - hpwhModel = HPWH::MODELS_Generic3; - } - else if (modelName == "custom") { - hpwhModel = HPWH::MODELS_CustomFile; - } - else if (modelName == "restankRealistic") { - hpwhModel = HPWH::MODELS_restankRealistic; - } - else if (modelName == "StorageTank") { - hpwhModel = HPWH::MODELS_StorageTank; - } - else if (modelName == "BWC2020_65") { - hpwhModel = HPWH::MODELS_BWC2020_65; - } - // New Rheems - else if (modelName == "Rheem2020Prem40") { - hpwhModel = HPWH::MODELS_Rheem2020Prem40; - } - else if (modelName == "Rheem2020Prem50") { - hpwhModel = HPWH::MODELS_Rheem2020Prem50; - } - else if (modelName == "Rheem2020Prem65") { - hpwhModel = HPWH::MODELS_Rheem2020Prem65; - } - else if (modelName == "Rheem2020Prem80") { - hpwhModel = HPWH::MODELS_Rheem2020Prem80; - } - else if (modelName == "Rheem2020Build40") { - hpwhModel = HPWH::MODELS_Rheem2020Build40; - } - else if (modelName == "Rheem2020Build50") { - hpwhModel = HPWH::MODELS_Rheem2020Build50; - } - else if (modelName == "Rheem2020Build65") { - hpwhModel = HPWH::MODELS_Rheem2020Build65; - } - else if (modelName == "Rheem2020Build80") { - hpwhModel = HPWH::MODELS_Rheem2020Build80; - } - else if (modelName == "RheemPlugInDedicated40") { - hpwhModel = HPWH::MODELS_RheemPlugInDedicated40; - } - else if (modelName == "RheemPlugInDedicated50") { - hpwhModel = HPWH::MODELS_RheemPlugInDedicated50; - } - else if (modelName == "RheemPlugInShared40") { - hpwhModel = HPWH::MODELS_RheemPlugInShared40; - } - else if (modelName == "RheemPlugInShared50") { - hpwhModel = HPWH::MODELS_RheemPlugInShared50; - } - else if (modelName == "RheemPlugInShared65") { - hpwhModel = HPWH::MODELS_RheemPlugInShared65; - } - else if (modelName == "RheemPlugInShared80") { - hpwhModel = HPWH::MODELS_RheemPlugInShared80; - } - // Large HPWH's - else if (modelName == "AOSmithCAHP120") { - hpwhModel = HPWH::MODELS_AOSmithCAHP120; - } - else if (modelName == "ColmacCxV_5_SP") { - hpwhModel = HPWH::MODELS_ColmacCxV_5_SP; - } - else if (modelName == "ColmacCxA_10_SP") { - hpwhModel = HPWH::MODELS_ColmacCxA_10_SP; - } - else if (modelName == "ColmacCxA_15_SP") { - hpwhModel = HPWH::MODELS_ColmacCxA_15_SP; - } - else if (modelName == "ColmacCxA_20_SP") { - hpwhModel = HPWH::MODELS_ColmacCxA_20_SP; - } - else if (modelName == "ColmacCxA_25_SP") { - hpwhModel = HPWH::MODELS_ColmacCxA_25_SP; - } - else if (modelName == "ColmacCxA_30_SP") { - hpwhModel = HPWH::MODELS_ColmacCxA_30_SP; - } + if (modelName == "Voltex60" || modelName == "AOSmithPHPT60") + { + hpwhModel = HPWH::MODELS_AOSmithPHPT60; + } + else if (modelName == "Voltex80" || modelName == "AOSmith80") + { + hpwhModel = HPWH::MODELS_AOSmithPHPT80; + } + else if (modelName == "GEred" || modelName == "GE") + { + hpwhModel = HPWH::MODELS_GE2012; + } + else if (modelName == "SandenGAU" || modelName == "Sanden80" || modelName == "SandenGen3") + { + hpwhModel = HPWH::MODELS_Sanden80; + } + else if (modelName == "Sanden120") + { + hpwhModel = HPWH::MODELS_Sanden120; + } + else if (modelName == "SandenGES" || modelName == "Sanden40") + { + hpwhModel = HPWH::MODELS_Sanden40; + } + else if (modelName == "AOSmithHPTU50") + { + hpwhModel = HPWH::MODELS_AOSmithHPTU50; + } + else if (modelName == "AOSmithHPTU66") + { + hpwhModel = HPWH::MODELS_AOSmithHPTU66; + } + else if (modelName == "AOSmithHPTU80") + { + hpwhModel = HPWH::MODELS_AOSmithHPTU80; + } + else if (modelName == "AOSmithHPTS50") + { + hpwhModel = HPWH::MODELS_AOSmithHPTS50; + } + else if (modelName == "AOSmithHPTS66") + { + hpwhModel = HPWH::MODELS_AOSmithHPTS66; + } + else if (modelName == "AOSmithHPTS80") + { + hpwhModel = HPWH::MODELS_AOSmithHPTS80; + } + else if (modelName == "AOSmithHPTU80DR") + { + hpwhModel = HPWH::MODELS_AOSmithHPTU80_DR; + } + else if (modelName == "GE502014STDMode" || modelName == "GE2014STDMode") + { + hpwhModel = HPWH::MODELS_GE2014STDMode; + } + else if (modelName == "GE502014" || modelName == "GE2014") + { + hpwhModel = HPWH::MODELS_GE2014; + } + else if (modelName == "GE802014") + { + hpwhModel = HPWH::MODELS_GE2014_80DR; + } + else if (modelName == "RheemHB50") + { + hpwhModel = HPWH::MODELS_RheemHB50; + } + else if (modelName == "Stiebel220e" || modelName == "Stiebel220E") + { + hpwhModel = HPWH::MODELS_Stiebel220E; + } + else if (modelName == "Generic1") + { + hpwhModel = HPWH::MODELS_Generic1; + } + else if (modelName == "Generic2") + { + hpwhModel = HPWH::MODELS_Generic2; + } + else if (modelName == "Generic3") + { + hpwhModel = HPWH::MODELS_Generic3; + } + else if (modelName == "custom") + { + hpwhModel = HPWH::MODELS_CustomFile; + } + else if (modelName == "restankRealistic") + { + hpwhModel = HPWH::MODELS_restankRealistic; + } + else if (modelName == "StorageTank") + { + hpwhModel = HPWH::MODELS_StorageTank; + } + else if (modelName == "BWC2020_65") + { + hpwhModel = HPWH::MODELS_BWC2020_65; + } + // New Rheems + else if (modelName == "Rheem2020Prem40") + { + hpwhModel = HPWH::MODELS_Rheem2020Prem40; + } + else if (modelName == "Rheem2020Prem50") + { + hpwhModel = HPWH::MODELS_Rheem2020Prem50; + } + else if (modelName == "Rheem2020Prem65") + { + hpwhModel = HPWH::MODELS_Rheem2020Prem65; + } + else if (modelName == "Rheem2020Prem80") + { + hpwhModel = HPWH::MODELS_Rheem2020Prem80; + } + else if (modelName == "Rheem2020Build40") + { + hpwhModel = HPWH::MODELS_Rheem2020Build40; + } + else if (modelName == "Rheem2020Build50") + { + hpwhModel = HPWH::MODELS_Rheem2020Build50; + } + else if (modelName == "Rheem2020Build65") + { + hpwhModel = HPWH::MODELS_Rheem2020Build65; + } + else if (modelName == "Rheem2020Build80") + { + hpwhModel = HPWH::MODELS_Rheem2020Build80; + } + else if (modelName == "RheemPlugInDedicated40") + { + hpwhModel = HPWH::MODELS_RheemPlugInDedicated40; + } + else if (modelName == "RheemPlugInDedicated50") + { + hpwhModel = HPWH::MODELS_RheemPlugInDedicated50; + } + else if (modelName == "RheemPlugInShared40") + { + hpwhModel = HPWH::MODELS_RheemPlugInShared40; + } + else if (modelName == "RheemPlugInShared50") + { + hpwhModel = HPWH::MODELS_RheemPlugInShared50; + } + else if (modelName == "RheemPlugInShared65") + { + hpwhModel = HPWH::MODELS_RheemPlugInShared65; + } + else if (modelName == "RheemPlugInShared80") + { + hpwhModel = HPWH::MODELS_RheemPlugInShared80; + } + // Large HPWH's + else if (modelName == "AOSmithCAHP120") + { + hpwhModel = HPWH::MODELS_AOSmithCAHP120; + } + else if (modelName == "ColmacCxV_5_SP") + { + hpwhModel = HPWH::MODELS_ColmacCxV_5_SP; + } + else if (modelName == "ColmacCxA_10_SP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_10_SP; + } + else if (modelName == "ColmacCxA_15_SP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_15_SP; + } + else if (modelName == "ColmacCxA_20_SP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_20_SP; + } + else if (modelName == "ColmacCxA_25_SP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_25_SP; + } + else if (modelName == "ColmacCxA_30_SP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_30_SP; + } - else if (modelName == "ColmacCxV_5_MP") { - hpwhModel = HPWH::MODELS_ColmacCxV_5_MP; - } - else if (modelName == "ColmacCxA_10_MP") { - hpwhModel = HPWH::MODELS_ColmacCxA_10_MP; - } - else if (modelName == "ColmacCxA_15_MP") { - hpwhModel = HPWH::MODELS_ColmacCxA_15_MP; - } - else if (modelName == "ColmacCxA_20_MP") { - hpwhModel = HPWH::MODELS_ColmacCxA_20_MP; - } - else if (modelName == "ColmacCxA_25_MP") { - hpwhModel = HPWH::MODELS_ColmacCxA_25_MP; - } - else if (modelName == "ColmacCxA_30_MP") { - hpwhModel = HPWH::MODELS_ColmacCxA_30_MP; - } - - else if (modelName == "RheemHPHD60") { - hpwhModel = HPWH::MODELS_RHEEM_HPHD60VNU_201_MP; - } - else if (modelName == "RheemHPHD135") { - hpwhModel = HPWH::MODELS_RHEEM_HPHD135VNU_483_MP; - } - //Nyle Single pass models - else if (modelName == "NyleC25A_SP") { - hpwhModel = HPWH::MODELS_NyleC25A_SP; - } - else if (modelName == "NyleC60A_SP") { - hpwhModel = HPWH::MODELS_NyleC60A_SP; - } - else if (modelName == "NyleC90A_SP") { - hpwhModel = HPWH::MODELS_NyleC90A_SP; - } - else if (modelName == "NyleC185A_SP") { - hpwhModel = HPWH::MODELS_NyleC185A_SP; - } - else if (modelName == "NyleC250A_SP") { - hpwhModel = HPWH::MODELS_NyleC250A_SP; - } - else if (modelName == "NyleC60A_C_SP") { - hpwhModel = HPWH::MODELS_NyleC60A_C_SP; - } - else if (modelName == "NyleC90A_C_SP") { - hpwhModel = HPWH::MODELS_NyleC90A_C_SP; - } - else if (modelName == "NyleC185A_C_SP") { - hpwhModel = HPWH::MODELS_NyleC185A_C_SP; - } - else if (modelName == "NyleC250A_C_SP") { - hpwhModel = HPWH::MODELS_NyleC250A_C_SP; - } - // Nyle MP models - else if (modelName == "NyleC60A_MP") { - hpwhModel = HPWH::MODELS_NyleC60A_MP; - } - else if (modelName == "NyleC90A_MP") { - hpwhModel = HPWH::MODELS_NyleC90A_MP; - } - else if (modelName == "NyleC125A_MP") { - hpwhModel = HPWH::MODELS_NyleC125A_MP; - } - else if (modelName == "NyleC185A_MP") { - hpwhModel = HPWH::MODELS_NyleC185A_MP; - } - else if (modelName == "NyleC250A_MP") { - hpwhModel = HPWH::MODELS_NyleC250A_MP; - } - else if (modelName == "NyleC60A_C_MP") { - hpwhModel = HPWH::MODELS_NyleC60A_C_MP; - } - else if (modelName == "NyleC90A_C_MP") { - hpwhModel = HPWH::MODELS_NyleC90A_C_MP; - } - else if (modelName == "NyleC125A_C_MP") { - hpwhModel = HPWH::MODELS_NyleC125A_C_MP; - } - else if (modelName == "NyleC185A_C_MP") { - hpwhModel = HPWH::MODELS_NyleC185A_C_MP; - } - else if (modelName == "NyleC250A_C_MP") { - hpwhModel = HPWH::MODELS_NyleC250A_C_MP; - } - else if (modelName == "QAHV_N136TAU_HPB_SP") { - hpwhModel = HPWH::MODELS_MITSUBISHI_QAHV_N136TAU_HPB_SP; - } - // Stack in a couple scalable models - else if (modelName == "TamScalable_SP") { - hpwhModel = HPWH::MODELS_TamScalable_SP; - } - else if (modelName == "TamScalable_SP_2X") { - hpwhModel = HPWH::MODELS_TamScalable_SP; - } - else if (modelName == "TamScalable_SP_Half") { - hpwhModel = HPWH::MODELS_TamScalable_SP; - } - else if (modelName == "Scalable_MP") { - hpwhModel = HPWH::MODELS_Scalable_MP; - } - else if (modelName == "AWHSTier3Generic40") { - hpwhModel = HPWH::MODELS_AWHSTier3Generic40; - } - else if (modelName == "AWHSTier3Generic50") { - hpwhModel = HPWH::MODELS_AWHSTier3Generic50; - } - else if (modelName == "AWHSTier3Generic65") { - hpwhModel = HPWH::MODELS_AWHSTier3Generic65; - } - else if (modelName == "AWHSTier3Generic80") { - hpwhModel = HPWH::MODELS_AWHSTier3Generic80; - } - else if (modelName == "AquaThermAire") { - hpwhModel = HPWH::MODELS_AquaThermAire; - } - else { - hpwhModel = HPWH::MODELS_basicIntegrated; - cout << "Couldn't find model " << modelName << ". Exiting...\n"; - exit(1); - } - return hpwhModel; -} + else if (modelName == "ColmacCxV_5_MP") + { + hpwhModel = HPWH::MODELS_ColmacCxV_5_MP; + } + else if (modelName == "ColmacCxA_10_MP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_10_MP; + } + else if (modelName == "ColmacCxA_15_MP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_15_MP; + } + else if (modelName == "ColmacCxA_20_MP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_20_MP; + } + else if (modelName == "ColmacCxA_25_MP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_25_MP; + } + else if (modelName == "ColmacCxA_30_MP") + { + hpwhModel = HPWH::MODELS_ColmacCxA_30_MP; + } + else if (modelName == "RheemHPHD60") + { + hpwhModel = HPWH::MODELS_RHEEM_HPHD60VNU_201_MP; + } + else if (modelName == "RheemHPHD135") + { + hpwhModel = HPWH::MODELS_RHEEM_HPHD135VNU_483_MP; + } + // Nyle Single pass models + else if (modelName == "NyleC25A_SP") + { + hpwhModel = HPWH::MODELS_NyleC25A_SP; + } + else if (modelName == "NyleC60A_SP") + { + hpwhModel = HPWH::MODELS_NyleC60A_SP; + } + else if (modelName == "NyleC90A_SP") + { + hpwhModel = HPWH::MODELS_NyleC90A_SP; + } + else if (modelName == "NyleC185A_SP") + { + hpwhModel = HPWH::MODELS_NyleC185A_SP; + } + else if (modelName == "NyleC250A_SP") + { + hpwhModel = HPWH::MODELS_NyleC250A_SP; + } + else if (modelName == "NyleC60A_C_SP") + { + hpwhModel = HPWH::MODELS_NyleC60A_C_SP; + } + else if (modelName == "NyleC90A_C_SP") + { + hpwhModel = HPWH::MODELS_NyleC90A_C_SP; + } + else if (modelName == "NyleC185A_C_SP") + { + hpwhModel = HPWH::MODELS_NyleC185A_C_SP; + } + else if (modelName == "NyleC250A_C_SP") + { + hpwhModel = HPWH::MODELS_NyleC250A_C_SP; + } + // Nyle MP models + else if (modelName == "NyleC60A_MP") + { + hpwhModel = HPWH::MODELS_NyleC60A_MP; + } + else if (modelName == "NyleC90A_MP") + { + hpwhModel = HPWH::MODELS_NyleC90A_MP; + } + else if (modelName == "NyleC125A_MP") + { + hpwhModel = HPWH::MODELS_NyleC125A_MP; + } + else if (modelName == "NyleC185A_MP") + { + hpwhModel = HPWH::MODELS_NyleC185A_MP; + } + else if (modelName == "NyleC250A_MP") + { + hpwhModel = HPWH::MODELS_NyleC250A_MP; + } + else if (modelName == "NyleC60A_C_MP") + { + hpwhModel = HPWH::MODELS_NyleC60A_C_MP; + } + else if (modelName == "NyleC90A_C_MP") + { + hpwhModel = HPWH::MODELS_NyleC90A_C_MP; + } + else if (modelName == "NyleC125A_C_MP") + { + hpwhModel = HPWH::MODELS_NyleC125A_C_MP; + } + else if (modelName == "NyleC185A_C_MP") + { + hpwhModel = HPWH::MODELS_NyleC185A_C_MP; + } + else if (modelName == "NyleC250A_C_MP") + { + hpwhModel = HPWH::MODELS_NyleC250A_C_MP; + } + else if (modelName == "QAHV_N136TAU_HPB_SP") + { + hpwhModel = HPWH::MODELS_MITSUBISHI_QAHV_N136TAU_HPB_SP; + } + // Stack in a couple scalable models + else if (modelName == "TamScalable_SP") + { + hpwhModel = HPWH::MODELS_TamScalable_SP; + } + else if (modelName == "TamScalable_SP_2X") + { + hpwhModel = HPWH::MODELS_TamScalable_SP; + } + else if (modelName == "TamScalable_SP_Half") + { + hpwhModel = HPWH::MODELS_TamScalable_SP; + } + else if (modelName == "Scalable_MP") + { + hpwhModel = HPWH::MODELS_Scalable_MP; + } + else if (modelName == "AWHSTier3Generic40") + { + hpwhModel = HPWH::MODELS_AWHSTier3Generic40; + } + else if (modelName == "AWHSTier3Generic50") + { + hpwhModel = HPWH::MODELS_AWHSTier3Generic50; + } + else if (modelName == "AWHSTier3Generic65") + { + hpwhModel = HPWH::MODELS_AWHSTier3Generic65; + } + else if (modelName == "AWHSTier3Generic80") + { + hpwhModel = HPWH::MODELS_AWHSTier3Generic80; + } + else if (modelName == "AquaThermAire") + { + hpwhModel = HPWH::MODELS_AquaThermAire; + } + else + { + hpwhModel = HPWH::MODELS_basicIntegrated; + cout << "Couldn't find model " << modelName << ". Exiting...\n"; + exit(1); + } + return hpwhModel; +} -int getHPWHObject(HPWH &hpwh, string modelName) { - /**Sets up the preset HPWH object with modelName */ - int returnVal = 1; - HPWH::MODELS model = mapStringToPreset(modelName); +int getHPWHObject(HPWH& hpwh, string modelName) +{ + /**Sets up the preset HPWH object with modelName */ + int returnVal = 1; + HPWH::MODELS model = mapStringToPreset(modelName); - returnVal = hpwh.HPWHinit_presets(model); + returnVal = hpwh.HPWHinit_presets(model); - if (modelName == "TamScalable_SP_2X") { - hpwh.setScaleHPWHCapacityCOP(2., 1.); // Scale the compressor - hpwh.setResistanceCapacity(60.); // Reset resistance elements in kW - } - else if (modelName == "TamScalable_SP_Half") { - hpwh.setScaleHPWHCapacityCOP(1/2., 1.); // Scale the compressor - hpwh.setResistanceCapacity(15.); // Reset resistance elements in kW - } - return returnVal; -} \ No newline at end of file + if (modelName == "TamScalable_SP_2X") + { + hpwh.setScaleHPWHCapacityCOP(2., 1.); // Scale the compressor + hpwh.setResistanceCapacity(60.); // Reset resistance elements in kW + } + else if (modelName == "TamScalable_SP_Half") + { + hpwh.setScaleHPWHCapacityCOP(1 / 2., 1.); // Scale the compressor + hpwh.setResistanceCapacity(15.); // Reset resistance elements in kW + } + return returnVal; +}