Skip to content

Commit

Permalink
Merge main.
Browse files Browse the repository at this point in the history
  • Loading branch information
Phil Ahrenkiel authored and Phil Ahrenkiel committed Jan 11, 2024
2 parents f3bf0fe + b3e2507 commit 1fea557
Show file tree
Hide file tree
Showing 116 changed files with 7,970 additions and 7,992 deletions.
181 changes: 80 additions & 101 deletions src/HPWH.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
Copyright (c) 2014-2016 Ecotope Inc.
All rights reserved.
Expand Down Expand Up @@ -52,10 +52,11 @@ using std::cout;
using std::endl;
using std::string;

const float HPWH::DENSITYWATER_kgperL = 0.995f;
const float HPWH::KWATER_WpermC = 0.62f;
const float HPWH::CPWATER_kJperkgC = 4.180f;
const float HPWH::TOL_MINVALUE = 0.0001f;
const double HPWH::DENSITYWATER_kgperL = 0.995; /// mass density of water
const double HPWH::KWATER_WpermC = 0.62; /// thermal conductivity of water
const double HPWH::CPWATER_kJperkgC = 4.180; /// specific heat capcity of water

const double HPWH::TOL_MINVALUE = 0.0001;
const float HPWH::UNINITIALIZED_LOCATIONTEMP = -500.f;
const float HPWH::ASPECTRATIO = 4.75f;

Expand Down Expand Up @@ -3391,13 +3392,10 @@ void HPWH::updateTankTemps(double drawVolume_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)
Expand All @@ -3408,63 +3406,56 @@ void HPWH::updateTankTemps(double drawVolume_L,
return;
}

// Check which inlet is higher;
int highInletH;
double highInletV, highInletT;
int lowInletH;
double lowInletT, lowInletV;
// sort the inlets by height
int highInletNodeIndex;
double highInletT_C;
double highInletFraction; // fraction of draw from high inlet

int lowInletNodeIndex;
double lowInletT_C;
double lowInletFraction; // fraction of draw from low inlet

if (inletHeight > inlet2Height)
{
highInletH = inletHeight;
highInletV = drawVolume_L - inletVol2_L;
highInletT = inletT_C;
lowInletH = inlet2Height;
lowInletT = inletT2_C;
lowInletV = inletVol2_L;
highInletNodeIndex = inletHeight;
highInletFraction = 1. - inletVol2_L / drawVolume_L;
highInletT_C = inletT_C;
lowInletNodeIndex = inlet2Height;
lowInletT_C = inletT2_C;
lowInletFraction = inletVol2_L / drawVolume_L;
}
else
{
highInletH = inlet2Height;
highInletV = inletVol2_L;
highInletT = inletT2_C;
lowInletH = inletHeight;
lowInletT = inletT_C;
lowInletV = drawVolume_L - inletVol2_L;
highInletNodeIndex = inlet2Height;
highInletFraction = inletVol2_L / drawVolume_L;
highInletT_C = inletT2_C;
lowInletNodeIndex = inletHeight;
lowInletT_C = inletT_C;
lowInletFraction = 1. - inletVol2_L / drawVolume_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;
// calculate number of nodes to draw
double drawVolume_N = drawVolume_L / nodeVolume_L;
double drawCp_kJperC = CPWATER_kJperkgC * DENSITYWATER_kgperL * drawVolume_L;

// heat-exchange models
if (hasHeatExchanger)
{
outletTemp_C = inletT_C;
for (auto& nodeTemp : tankTemps_C)
for (auto& nodeT_C : tankTemps_C)
{
double maxHeatExchange_kJ = C_draw_kJperC * (nodeTemp - outletTemp_C);
double maxHeatExchange_kJ = drawCp_kJperC * (nodeT_C - outletTemp_C);
double heatExchange_kJ = nodeHeatExchangerEffectiveness * maxHeatExchange_kJ;

nodeTemp -= heatExchange_kJ / C_Node_kJperC;
outletTemp_C += heatExchange_kJ / C_draw_kJperC;
nodeT_C -= heatExchange_kJ / nodeCp_kJperC;
outletTemp_C += heatExchange_kJ / drawCp_kJperC;
}
}
else
{
// calculate how many nodes to draw (drawVolume_N)
double drawVolume_N = drawVolume_L / nodeVolume_L;
double remainingDrawVolume_N = drawVolume_N;
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];
Expand All @@ -3474,68 +3465,56 @@ void HPWH::updateTankTemps(double drawVolume_L,
}
outletTemp_C = (outletTemp_C / getNumNodes() * tankVolume_L +
tankTemps_C[0] * (drawVolume_L - tankVolume_L)) /
drawVolume_L * drawVolume_N;
drawVolume_L * remainingDrawVolume_N;

drawVolume_N = 0.;
remainingDrawVolume_N = 0.;
}

while (drawVolume_N > 0)
double totalExpelledHeat_kJ = 0.;
while (remainingDrawVolume_N > 0.)
{

// Draw one node at a time
double drawFraction = drawVolume_N > 1. ? 1. : drawVolume_N;
double nodeInletTV = 0.;
// draw no more than one node at a time
double incrementalDrawVolume_N =
remainingDrawVolume_N > 1. ? 1. : remainingDrawVolume_N;

// add temperature for outletT average
outletTemp_C += drawFraction * tankTemps_C[getNumNodes() - 1];
double outputHeat_kJ = nodeCp_kJperC * incrementalDrawVolume_N * tankTemps_C.back();
totalExpelledHeat_kJ += outputHeat_kJ;
tankTemps_C.back() -= outputHeat_kJ / nodeCp_kJperC;

double cumInletFraction = 0.;
for (int i = getNumNodes() - 1; i >= lowInletH; i--)
for (int i = getNumNodes() - 1; i >= 0; --i)
{

// Reset inlet inputs at this node.
double nodeInletFraction = 0.;
nodeInletTV = 0.;

// Sum of all inlets Vi*Ti at this node
if (i == highInletH)
// combine all inlet contributions at this node
double inletFraction = 0.;
if (i == highInletNodeIndex)
{
nodeInletTV += highInletV * drawFraction / drawVolume_L * highInletT;
nodeInletFraction += highInletV * drawFraction / drawVolume_L;
inletFraction += highInletFraction;
tankTemps_C[i] +=
incrementalDrawVolume_N * highInletFraction * highInletT_C;
}
if (i == lowInletH)
if (i == lowInletNodeIndex)
{
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.
inletFraction += lowInletFraction;
tankTemps_C[i] += incrementalDrawVolume_N * lowInletFraction * lowInletT_C;
}

// 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;
if (i > 0)
{
double transferT_C =
incrementalDrawVolume_N * (1. - inletFraction) * tankTemps_C[i - 1];
tankTemps_C[i] += transferT_C;
tankTemps_C[i - 1] -= transferT_C;
}
}

// 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;

remainingDrawVolume_N -= incrementalDrawVolume_N;
mixTankInversions();
}

// fill in average outlet T - it is a weighted averaged, with weights == nodes drawn
outletTemp_C /= (drawVolume_L / nodeVolume_L);
outletTemp_C = totalExpelledHeat_kJ / drawCp_kJperC;
}

// Account for mixing at the bottom of the tank
// account for mixing at the bottom of the tank
if (tankMixesOnDraw && drawVolume_L > 0.)
{
int mixedBelowNode = (int)(getNumNodes() * mixBelowFractionOnDraw);
Expand Down Expand Up @@ -3583,11 +3562,11 @@ void HPWH::updateTankTemps(double drawVolume_L,
{

// Get the "constant" tau for the stability condition and the conduction calculation
const double tau = KWATER_WpermC /
const double tau = 2. * KWATER_WpermC /
((CPWATER_kJperkgC * 1000.0) * (DENSITYWATER_kgperL * 1000.0) *
(nodeHeight_m * nodeHeight_m)) *
secondsPerStep;
if (tau > 0.5)
if (tau > 1.)
{
if (hpwhVerbosity >= VRB_reluctant)
{
Expand All @@ -3601,16 +3580,15 @@ void HPWH::updateTankTemps(double drawVolume_L,
// 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());
nextTankTemps_C.front() += tau * (tankTemps_C[1] - tankTemps_C.front());
nextTankTemps_C.back() += 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]);
tau * (tankTemps_C[i + 1] - 2. * tankTemps_C[i] + tankTemps_C[i - 1]);
}
}

Expand Down Expand Up @@ -3734,7 +3712,7 @@ double HPWH::addHeatAboveNode(double qAdd_kJ, int nodeNum, const double maxT_C)
}

// heat needed to bring all equal-temp nodes up to heatToT_C
double qIncrement_kJ = nodeCp_kJperC * numNodesToHeat * (heatToT_C - tankTemps_C[nodeNum]);
double qIncrement_kJ = numNodesToHeat * nodeCp_kJperC * (heatToT_C - tankTemps_C[nodeNum]);

if (qIncrement_kJ > qAdd_kJ)
{
Expand Down Expand Up @@ -3966,8 +3944,7 @@ void HPWH::calcSizeConstants()
const double tankHeight_m = ASPECTRATIO * tankRad_m;

nodeVolume_L = tankVolume_L / getNumNodes();
nodeMass_kg = nodeVolume_L * DENSITYWATER_kgperL;
nodeCp_kJperC = nodeMass_kg * CPWATER_kJperkgC;
nodeCp_kJperC = CPWATER_kJperkgC * DENSITYWATER_kgperL * nodeVolume_L;
nodeHeight_m = tankHeight_m / getNumNodes();

// The fraction of UA that is on the top or the bottom of the tank. So 2 * fracAreaTop +
Expand Down Expand Up @@ -4431,6 +4408,9 @@ bool HPWH::isEnergyBalanced(const double drawVol_L,
const double prevHeatContent_kJ,
const double fracEnergyTolerance /* = 0.001 */)
{
double drawCp_kJperC =
CPWATER_kJperkgC * DENSITYWATER_kgperL * drawVol_L; // heat capacity of draw

// Check energy balancing.
double qInElectrical_kJ = 0.;
for (int iHS = 0; iHS < getNumHeatSources(); iHS++)
Expand All @@ -4441,8 +4421,8 @@ bool HPWH::isEnergyBalanced(const double drawVol_L,
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 qOutWater_kJ =
drawCp_kJperC * (outletTemp_C - member_inletT_C); // assumes only one inlet
double expectedTankHeatContent_kJ =
prevHeatContent_kJ // previous heat content
+ qInElectrical_kJ // electrical energy delivered to heat sources
Expand All @@ -4452,7 +4432,6 @@ bool HPWH::isEnergyBalanced(const double drawVol_L,
- 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)
{
Expand Down
37 changes: 20 additions & 17 deletions src/HPWH.hh
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,21 @@ class HPWH
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 std::string version_maint;

static const float DENSITYWATER_kgperL;
static const float KWATER_WpermC;
static const float CPWATER_kJperkgC;
static const int CONDENSITY_SIZE =
12; /**<number of condensity nodes associated with each heat source */
static const int LOGIC_NODE_SIZE =
12; /**< number of logic nodes associated with temperature-based heating logic */
static const int MAXOUTSTRING =
200; /**< this is the maximum length for a debuging output string */
static const float TOL_MINVALUE; /**< any amount of heat distribution less than this is reduced
to 0 this saves on computations */
200; /**< this is the maximum length for a debuging output string */

static const double DENSITYWATER_kgperL; /// mass density of water
static const double KWATER_WpermC; /// thermal conductivity of water
static const double CPWATER_kJperkgC; /// specific heat capcity of water
static const double TOL_MINVALUE; /**< any amount of heat distribution less than this is reduced
to 0 this saves on computations */

static const float UNINITIALIZED_LOCATIONTEMP; /**< this is used to tell the
simulation when the location temperature has not been initialized */
static const float
Expand Down Expand Up @@ -1129,10 +1131,8 @@ class HPWH
/**< the volume (L) of a single node */
double nodeVolume_L;

/**< the mass of water (kg) in a single node */
double nodeMass_kg;

/**< the heat capacity of the water (kJ/�C) in a single node */
/**< heat capacity (kJ/degC) of the fluid (water, except for heat-exchange models) in a single
* node */
double nodeCp_kJperC;

/**< the height in meters of the one node */
Expand Down Expand Up @@ -1565,6 +1565,9 @@ constexpr double offsetF = 32.; // degF offset
constexpr double sec_per_min = 60.; // seconds / min
constexpr double min_per_hr = 60.; // min / hr
constexpr double sec_per_hr = sec_per_min * min_per_hr; // seconds / hr
constexpr double L_per_gal = 3.78541; // liters / gal
constexpr double ft_per_m = 3.2808; // feet / meter
constexpr double ft2_per_m2 = ft_per_m * ft_per_m; // feet / meter

// a few extra functions for unit conversion
inline double dF_TO_dC(double temperature) { return (temperature / FperC); }
Expand All @@ -1578,15 +1581,15 @@ inline double KW_TO_BTUperH(double kw) { return (kw * BTUperKWH); }
inline double W_TO_BTUperH(double w) { return (w * BTUperKWH / 1000.); }
inline double KJ_TO_KWH(double kj) { return (kj / sec_per_hr); }
inline double BTU_TO_KJ(double btu) { return (btu * sec_per_hr / BTUperKWH); }
inline double GAL_TO_L(double gallons) { return (gallons * 3.78541); }
inline double L_TO_GAL(double liters) { return (liters / 3.78541); }
inline double GAL_TO_L(double gallons) { return (gallons * L_per_gal); }
inline double L_TO_GAL(double liters) { return (liters / L_per_gal); }
inline double L_TO_FT3(double liters) { return (liters / 28.31685); }
inline double UAf_TO_UAc(double UAf) { return (UAf * 1.8 / 0.9478); }
inline double GPM_TO_LPS(double gpm) { return (gpm * 3.78541 / sec_per_min); }
inline double LPS_TO_GPM(double lps) { return (lps * sec_per_min / 3.78541); }
inline double GPM_TO_LPS(double gpm) { return (gpm * L_per_gal / sec_per_min); }
inline double LPS_TO_GPM(double lps) { return (lps * sec_per_min / L_per_gal); }

inline double FT_TO_M(double feet) { return (feet / 3.2808); }
inline double FT2_TO_M2(double feet2) { return (feet2 / 10.7640); }
inline double FT_TO_M(double feet) { return (feet / ft_per_m); }
inline double FT2_TO_M2(double feet2) { return (feet2 / ft2_per_m2); }

inline double MIN_TO_SEC(double minute) { return minute * sec_per_min; }
inline double MIN_TO_HR(double minute) { return minute / min_per_hr; }
Expand Down
Loading

0 comments on commit 1fea557

Please sign in to comment.