-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Heat exchange models #183
Merged
Merged
Heat exchange models #183
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
99efbed
Restart heat exchange model.
8618861
Add heat exchange params.
508b544
AquaThermAire fails file and regression tests.
6e78b76
Fails villara test.
4681954
Adjust villara time.
7a6b418
Aquatherm added.
ad7efa0
Merge branch 'master' into heat-exchange-models
spahrenk 539ef53
Merge branch 'master' into heat-exchange-models
638a51e
Merge branch 'master' into heat-exchange-models
ccdc7d2
accept master
098874d
Remove specific AquaTerm refs.
9bb19c7
No AquaTherm refs.
2fa7021
AquaTherm back in.
3611502
Review changes; unlock condenser.
92d5edf
Merge branch 'master' into heat-exchange-models
19ae202
Merge branch 'master' into heat-exchange-models
63b453a
Use 800 C storage tank to pass solar test.
e7bb180
Merge master.
592b89e
Fix heating logic issues.
e2d006f
Set AquaTherm to 12 tank nodes.
b8e569c
Use whole-tank logic.
9348872
Merge branch 'master' into heat-exchange-models
5043494
Improve unit conversion.
0dcc923
Conform case.
7553fdd
Merge branch 'master' into heat-exchange-models
4f64c7a
Merge branch 'master' into heat-exchange-models
533ec62
Change heat-exch-eff coeff.
4de5ef1
Define node-heat-exch-effec.
c5e0c99
Check validity.
e3f4a7c
Add 2nd third function.
2e25a71
Review comments; remove outletT space.
4a3e3f9
Remove unused fnc.
31cec6c
Change coil config.
ad4f931
Merge branch 'heat-exchange-models' of https://github.com/bigladder/H…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,10 @@ const double HPWH::MAXOUTLET_R410A = F_TO_C(140.); | |
const double HPWH::MAXOUTLET_R744 = F_TO_C(190.); | ||
const double HPWH::MINSINGLEPASSLIFT = dF_TO_dC(15.); | ||
|
||
double makeC(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)); | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
/// @brief Samples a std::vector to extract a single value spanning the fractional | ||
/// coordinate range from frac_begin to frac_end. | ||
|
@@ -315,6 +319,8 @@ void HPWH::setAllDefaults() { | |
usesSoCLogic = false; | ||
setMinutesPerStep(1.0); | ||
hpwhVerbosity = VRB_minuteOut; | ||
hasHeatExchanger = false; | ||
heatExchangerEffectiveness = 0.9; | ||
} | ||
|
||
HPWH::HPWH(const HPWH &hpwh) { | ||
|
@@ -1738,14 +1744,20 @@ std::vector<HPWH::NodeWeight> HPWH::getNodeWeightRange(double bottomFraction, do | |
return nodeWeights; | ||
} | ||
|
||
std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::wholeTank(double decisionPoint,const UNITS units /* = UNITS_C */, const bool absolute /* = false */) { | ||
std::vector<NodeWeight> nodeWeights = getNodeWeightRange(0., 1.); | ||
double decisionPoint_C = makeC(decisionPoint,units,absolute); | ||
return std::make_shared<HPWH::TempBasedHeatingLogic>("whole tank",nodeWeights,decisionPoint_C,this,absolute); | ||
} | ||
|
||
std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::topThird(double decisionPoint) { | ||
std::vector<NodeWeight> nodeWeights = getNodeWeightRange(2./3., 1.); | ||
return std::make_shared<HPWH::TempBasedHeatingLogic>("top third",nodeWeights,decisionPoint,this); | ||
} | ||
|
||
std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::topThird_absolute(double decisionPoint) { | ||
std::vector<NodeWeight> nodeWeights = getNodeWeightRange(2./3., 1.); | ||
return std::make_shared<HPWH::TempBasedHeatingLogic>("top third absolute",nodeWeights,decisionPoint,this); | ||
return std::make_shared<HPWH::TempBasedHeatingLogic>("top third absolute",nodeWeights,decisionPoint,this,true); | ||
} | ||
|
||
std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::bottomThird(double decisionPoint) { | ||
|
@@ -1798,6 +1810,11 @@ std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::bottomTwelfth(double decision | |
return std::make_shared<HPWH::TempBasedHeatingLogic>("bottom twelfth",nodeWeights,decisionPoint,this); | ||
} | ||
|
||
std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::bottomTwelfth_absolute(double decisionPoint) { | ||
std::vector<NodeWeight> nodeWeights = getNodeWeightRange(0., 1./12.); | ||
return std::make_shared<HPWH::TempBasedHeatingLogic>("bottom twelfth",nodeWeights,decisionPoint,this,true); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is needed anymore. |
||
|
||
std::shared_ptr<HPWH::TempBasedHeatingLogic> HPWH::standby(double decisionPoint) { | ||
std::vector<NodeWeight> nodeWeights; | ||
nodeWeights.emplace_back(LOGIC_NODE_SIZE + 1); // uses very top computation node | ||
|
@@ -2581,77 +2598,93 @@ void HPWH::updateTankTemps(double drawVolume_L,double inletT_C,double tankAmbien | |
lowInletT = inletT_C; | ||
lowInletV = drawVolume_L - inletVol2_L; | ||
} | ||
//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.; | ||
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) { | ||
|
||
while(drawVolume_N > 0) { | ||
// Draw one node at a time | ||
double drawFraction = drawVolume_N > 1. ? 1. : drawVolume_N; | ||
double nodeInletTV = 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]; | ||
|
||
//add temperature for outletT average | ||
outletTemp_C += drawFraction * tankTemps_C[getNumNodes() - 1]; | ||
double cumInletFraction = 0.; | ||
for(int i = getNumNodes() - 1; i >= lowInletH; i--) { | ||
|
||
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; | ||
nodeInletTV += highInletV * drawFraction / drawVolume_L * highInletT; | ||
nodeInletFraction += highInletV * drawFraction / drawVolume_L; | ||
} | ||
if(i == lowInletH) { | ||
nodeInletTV += lowInletV * drawFraction / drawVolume_L * lowInletT; | ||
nodeInletFraction += lowInletV * drawFraction / drawVolume_L; | ||
nodeInletTV += lowInletV * drawFraction / drawVolume_L * lowInletT; | ||
nodeInletFraction += lowInletV * drawFraction / drawVolume_L; | ||
|
||
break; // if this is the bottom inlet break out of the four loop and use the boundary condition equation. | ||
} | ||
|
||
// 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; | ||
|
||
} | ||
// 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]; | ||
|
||
// 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; | ||
cumInletFraction += nodeInletFraction; | ||
|
||
drawVolume_N -= drawFraction; | ||
} | ||
|
||
mixTankInversions(); | ||
} | ||
// 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; | ||
|
||
//fill in average outlet T - it is a weighted averaged, with weights == nodes drawn | ||
this->outletTemp_C /= (drawVolume_L / nodeVolume_L); | ||
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.) { | ||
|
@@ -3024,6 +3057,9 @@ void HPWH::calcSizeConstants() { | |
|
||
// 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<double>(getNumNodes())); | ||
} | ||
|
||
void HPWH::calcDerivedValues() { | ||
|
@@ -3141,7 +3177,6 @@ void HPWH::mapResRelativePosToHeatSources() { | |
}); | ||
} | ||
|
||
|
||
// 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; | ||
|
@@ -3194,7 +3229,8 @@ int HPWH::checkInputs() { | |
|
||
//check is condensity sums to 1 | ||
condensitySum = 0; | ||
for(int j = 0; j < heatSources[i].getCondensitySize(); ++j) condensitySum += heatSources[i].condensity[j]; | ||
|
||
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); | ||
|
@@ -3328,6 +3364,14 @@ int HPWH::checkInputs() { | |
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; | ||
} | ||
|
@@ -3515,7 +3559,21 @@ int HPWH::HPWHinit_file(string configFile) { | |
} | ||
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") { | ||
|
@@ -3668,16 +3726,43 @@ int HPWH::HPWHinit_file(string configFile) { | |
heatSources[heatsource].standbyLogic = std::make_shared<HPWH::TempBasedHeatingLogic>("standby logic",nodeWeights,tempDouble,this,absolute,compare); | ||
} | ||
} else if(token == "onlogic") { | ||
line_ss >> tempDouble >> units; | ||
if(units == "F") tempDouble = dF_TO_dC(tempDouble); | ||
else if(units == "C"); //do nothing, lol | ||
std::string nextToken; | ||
line_ss >> nextToken; | ||
bool absolute = (nextToken == "absolute"); | ||
if(absolute) { | ||
std::string compareStr; | ||
line_ss >> compareStr >> tempDouble >> units; | ||
std::function<bool(double,double)> compare; | ||
if(compareStr == "<") compare = std::less<double>(); | ||
else if(compareStr == ">") compare = std::greater<double>(); | ||
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 == "topThird") { | ||
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)); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can probably be inline, no?
The name is also not very descriptive. Maybe
convertTempToC
?