Skip to content
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

Correction of Hybrid Model Reporting Issues #10581

Merged
merged 3 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ \subsubsection{Inverse algorithm for zone capacitance multiplier}\label{Inverse-
\label{eq:InternalMassMultiplierforEachTimestep}
\end{equation}

The default value is 1.0. Ideally the zone heat capacity shall remain constant for the same condition of the interior environment in the zone heat balance equation. An underlying assumption is that the zone heat capacity is treated as constant for the equilibrium of the inversed heat balance model. However the measured temperatures are not the same as the simulated zone air temperatures which is the result of the energy simulation in Equations~\ref{eq:ZoneAirHeatCapacityforEachTimestep} and~\ref{eq:InternalMassMultiplierforEachTimestep}. This causes the internal mass multiplier, $C_T^t$, the result from the inverse model is not constant during the course of the simulation period. The hybrid model will determine a time span when $|T_z^t - T_z^{t-\delta t}| > 0.05^{\circ}C$ that $C_z^t$ remains more constant. Internal mass multiplier calculations are only done when the zone air temperature difference between timesteps meets the condition. This filter is needed for more reliable inverse calculation to avoid the anomaly conditions due to the use of the inverse model.
The default value is 1.0. Ideally the zone heat capacity shall remain constant for the same condition of the interior environment in the zone heat balance equation. An underlying assumption is that the zone heat capacity is treated as constant for the equilibrium of the inversed heat balance model. However the measured temperatures are not the same as the simulated zone air temperatures which is the result of the energy simulation in Equations~\ref{eq:ZoneAirHeatCapacityforEachTimestep} and~\ref{eq:InternalMassMultiplierforEachTimestep}. This causes the internal mass multiplier, $C_T^t$, the result from the inverse model is not constant during the course of the simulation period. The hybrid model will determine a time span when $|T_z^t - T_z^{t-\delta t}| > 0.05^{\circ}C$ that $C_z^t$ remains more constant. Internal mass multiplier calculations are only done when the zone air temperature difference between timesteps meets the condition. This filter is needed for more reliable inverse calculation to avoid the anomaly conditions due to the use of the inverse model. So, when the value for this parameter is 1.0, this means that any of the following conditions is met: $|T_z^t - T_z^{t-\delta t}| > 0.05^{\circ}C$, the simulation is not currently during the time period for which hybrid modeling is set to run, or the value calculated for this parameter using the above methodology results in a value less than 1.0. The value for this multiplier can be obtained as output as described in the Input/Output Reference (Object: HybridModel:Zone).

\subsection{Infiltration hybrid modeling method}\label{Infiltration hybrid modeling method}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ \subsubsection{Outputs}\label{outputs-030}
Zone,Average,Zone Infiltration Hybrid Model Air Change Rate [ACH]
Zone,Average,Zone Infiltration Hybrid Model Mass Flow Rate [kg/s]
Zone,Average,Zone Hybrid Model People Count
Zone,Average,Zone Hybrid Model Thermal Mass Multiplier
\end{lstlisting}

\paragraph{Zone Infiltration Hybrid Model Air Change Rate {[}ACH{]}}\label{zone-infiltration-hybrid-model-air-change-rate}
Expand All @@ -210,4 +211,8 @@ \subsubsection{Outputs}\label{outputs-030}

\paragraph{Zone Hybrid Model People Count}\label{zone-infiltration-hybrid-model-people-count}

The zone people count calculated by the hybrid model.
The zone people count calculated by the hybrid model.

\paragraph{Zone Hybrid Model Thermal Mass Multiplier}\label{zone-infiltration-hybrid-model-thermal-mass-multiplier}

This is the value of the thermal mass multiplier that is the ratio of the zone air heat capacity calculated for current conditions using a heat balance equation to the heat capacity of the volume of air contained in the zone. This value is 1.0 when one of the following conditions is met: $|T_z^t - T_z^{t-\delta t}| > 0.05^{\circ}C$, the simulation is not currently during the time period for which hybrid modeling is set to run, or the value calculated for this parameter using the above methodology results in a value less than 1.0. The first time that the value that is calculated for this parameter is greater than 30.0 for a particular zone a warning message is produced in the EnergyPlus Error File (*.err) to inform the user of this occurrence. A summary of the times for when this parameter is greater than 30.0 for each zone is produced at the end of the standard error file. Note: when the value does exceed this warning threshold, the value is NOT reset.
10 changes: 9 additions & 1 deletion src/EnergyPlus/HybridModel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,15 @@ namespace HybridModel {
OutputProcessor::StoreType::Average,
state.dataHeatBal->Zone(ZonePtr).Name);
}

if (state.dataHybridModel->HybridModelZone(ZonePtr).InternalThermalMassCalc_T) {
SetupOutputVariable(state,
"Zone Hybrid Model Thermal Mass Multiplier",
Constant::Units::None,
state.dataHeatBal->Zone(ZonePtr).ZoneVolCapMultpSensHM,
OutputProcessor::TimeStepType::Zone,
OutputProcessor::StoreType::Average,
state.dataHeatBal->Zone(ZonePtr).Name);
}
} else {
ShowSevereError(
state,
Expand Down
62 changes: 41 additions & 21 deletions src/EnergyPlus/ZoneTempPredictorCorrector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5146,10 +5146,10 @@ void InverseModelTemperature(EnergyPlusData &state,

int ZoneMult = zone.Multiplier * zone.ListMultiplier;
zone.ZoneMeasuredTemperature = ScheduleManager::GetCurrentScheduleValue(state, hybridModelZone.ZoneMeasuredTemperatureSchedulePtr);
zone.ZoneVolCapMultpSensHM = 1.0; // Initialize to 1.0 in case hybrid not active

// HM calculation only HM calculation period start
if (state.dataEnvrn->DayOfYear >= hybridModelZone.HybridStartDayOfYear && state.dataEnvrn->DayOfYear <= hybridModelZone.HybridEndDayOfYear) {
Real64 HMMultiplierAverage(1.0);
Real64 MultpHM(1.0);

thisZoneHB.ZT = zone.ZoneMeasuredTemperature; // Array1D<Real64> ZT -- Zone
Expand Down Expand Up @@ -5248,31 +5248,14 @@ void InverseModelTemperature(EnergyPlusData &state,
thisZoneHB.airHumRat) *
Psychrometrics::PsyCpAirFnW(thisZoneHB.airHumRat)) *
(state.dataGlobal->TimeStepZone * Constant::SecInHour); // Inverse equation
if ((MultpHM < 1.0) || (MultpHM > 30.0)) { // Temperature capacity multiplier greater than
// 1 and less than 30
MultpHM = 1.0; // Default value 1.0
}
} else {
MultpHM = 1.0; // Default value 1.0
}

zone.ZoneVolCapMultpSensHM = MultpHM; // For timestep output

// Calculate the average multiplier of the zone for the whole running period
{
// count for hybrid model calculations
if (MultpHM > 1.0) {
zone.ZoneVolCapMultpSensHMSum += MultpHM;
zone.ZoneVolCapMultpSensHMCountSum++;
}
processInverseModelMultpHM(
state, MultpHM, zone.ZoneVolCapMultpSensHMSum, zone.ZoneVolCapMultpSensHMCountSum, zone.ZoneVolCapMultpSensHMAverage, ZoneNum);
zone.ZoneVolCapMultpSensHM = MultpHM;

// Calculate and store the multiplier average at the end of HM
// simulations
if (state.dataEnvrn->DayOfYear == hybridModelZone.HybridEndDayOfYear && state.dataGlobal->EndDayFlag) {
HMMultiplierAverage = zone.ZoneVolCapMultpSensHMSum / zone.ZoneVolCapMultpSensHMCountSum;
zone.ZoneVolCapMultpSensHMAverage = HMMultiplierAverage;
}
}
} // Hybrid model internal thermal mass calcualtion end

// Hybrid model people count calculation
Expand Down Expand Up @@ -5346,6 +5329,43 @@ void InverseModelTemperature(EnergyPlusData &state,
state.dataHeatBalFanSys->PreviousMeasuredZT1(ZoneNum) = thisZoneHB.ZT;
}

void processInverseModelMultpHM(EnergyPlusData &state,
Real64 &multiplierHM, // Hybrid model thermal mass multiplier
Real64 &multSumHM, // Sum of Hybrid model thermal mass multipliers
Real64 &countSumHM, // Count of number of points in sum
Real64 &multAvgHM, // Average of hybrid model mass multipier
int zoneNum // Zone number for the hybrid model
)
{
Real64 constexpr minHMMultValue = 1.0;
Real64 constexpr maxHMMultValue = 30.0;

auto &zone = state.dataHeatBal->Zone(zoneNum);
auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(zoneNum);

// Apply limits and generate warnings as needed
if (multiplierHM < minHMMultValue) { // don't allow this to be less than minimum (potential for instability)
multiplierHM = minHMMultValue;
} else if (multiplierHM > maxHMMultValue) { // as per suggestions in Defect #10508, only warn if greater than the max
if (thisZoneHB.hmThermalMassMultErrIndex == 0) {
ShowWarningMessage(state, format("Hybrid model thermal mass multiplier higher than the limit for {}", zone.Name));
ShowContinueError(state, "This means that the ratio of the zone air heat capacity for the current time step to the");
ShowContinueError(state, format("zone air heat storage is higher than the maximum limit of {:.1R}.", maxHMMultValue));
}
ShowRecurringWarningErrorAtEnd(
state, "Hybrid model thermal mass multiplier limit exceeded in zone " + zone.Name, thisZoneHB.hmThermalMassMultErrIndex);
}

// Update running totals (but only when there is a valid multiplier, i.e. multiplier is greater than min but not higher than the max)
if (multiplierHM > minHMMultValue) {
multSumHM += multiplierHM;
countSumHM++;
}

// Calculate average (always so that it does get calculated)
if (countSumHM >= 1) multAvgHM = multSumHM / countSumHM;
}

void InverseModelHumidity(EnergyPlusData &state,
int const ZoneNum, // Zone number
Real64 const LatentGain, // Zone sum of latent gain
Expand Down
9 changes: 9 additions & 0 deletions src/EnergyPlus/ZoneTempPredictorCorrector.hh
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ namespace ZoneTempPredictorCorrector {
Real64 tempDepLoad = 0.0;
Real64 airRelHum = 0.0; // Zone relative humidity in percent
Real64 AirPowerCap = 0.0; // "air power capacity" Vol*VolMult*rho*Cp/timestep [W/degK]
int hmThermalMassMultErrIndex = 0;

virtual ~ZoneSpaceHeatBalanceData() = default;

Expand Down Expand Up @@ -338,6 +339,14 @@ namespace ZoneTempPredictorCorrector {
Real64 AirCap // Formerly CoefAirrat, coef in zone temp eqn with dim of "air power capacity"rd
);

void processInverseModelMultpHM(EnergyPlusData &state,
Real64 &multiplierHM, // Hybrid model thermal mass multiplier
Real64 &multSumHM, // Sum of Hybrid model thermal mass multipliers
Real64 &countSumHM, // Count of number of points in sum
Real64 &multAvgHM, // Average of hybrid model mass multipier
int zoneNum // Zone number for the hybrid model
);

void InverseModelHumidity(EnergyPlusData &state,
int ZoneNum, // Zone number
Real64 LatentGain, // Zone sum of latent gain
Expand Down
90 changes: 90 additions & 0 deletions tst/EnergyPlus/unit/ZoneTempPredictorCorrector.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1695,3 +1695,93 @@ TEST_F(EnergyPlusFixture, DownInterpolate4HistoryValues_Test)
EXPECT_NEAR(oldValue[2], DSHistoryValue3, 0.000001);
EXPECT_NEAR(oldValue[3], DSHistoryValue4, 0.000001);
}

TEST_F(EnergyPlusFixture, HybridModel_processInverseModelMultpHMTest)
{
// Test added for fix to GitHub Issue #10508
Real64 calcHMmult;
Real64 calcHMsum = 0.0;
Real64 calcHMcount = 0.0;
Real64 calcHMavg = 0.0;
Real64 expectedHMmult;
Real64 expectedHMsum;
Real64 expectedHMcount;
Real64 expectedHMavg;
int numZones = 1;
Real64 constexpr allowableTolerance = 0.001;

state->dataHeatBal->Zone.allocate(numZones);
state->dataHeatBal->Zone(numZones).Name = "Hybrid Zone";
state->dataZoneTempPredictorCorrector->zoneHeatBalance.allocate(numZones);

// Test 1: Multiplier is less than the minimum. Reset to the minimum. Nothing added to averages.
calcHMmult = 0.5;
expectedHMmult = 1.0;
expectedHMsum = 0.0;
expectedHMcount = 0;
expectedHMavg = 0.0;
processInverseModelMultpHM(*state, calcHMmult, calcHMsum, calcHMcount, calcHMavg, numZones);
EXPECT_NEAR(calcHMmult, expectedHMmult, allowableTolerance);
EXPECT_NEAR(calcHMsum, expectedHMsum, allowableTolerance);
EXPECT_NEAR(calcHMcount, expectedHMcount, allowableTolerance);
EXPECT_NEAR(calcHMavg, expectedHMavg, allowableTolerance);
EXPECT_EQ(state->dataZoneTempPredictorCorrector->zoneHeatBalance(numZones).hmThermalMassMultErrIndex, 0);

// Test 2: Multiplier is equal to minimum. Reset to the minimum. Nothing added to averages.
calcHMmult = 1.0;
expectedHMmult = 1.0;
expectedHMsum = 0.0;
expectedHMcount = 0;
expectedHMavg = 0.0;
processInverseModelMultpHM(*state, calcHMmult, calcHMsum, calcHMcount, calcHMavg, numZones);
EXPECT_NEAR(calcHMmult, expectedHMmult, allowableTolerance);
EXPECT_NEAR(calcHMsum, expectedHMsum, allowableTolerance);
EXPECT_NEAR(calcHMcount, expectedHMcount, allowableTolerance);
EXPECT_NEAR(calcHMavg, expectedHMavg, allowableTolerance);
EXPECT_EQ(state->dataZoneTempPredictorCorrector->zoneHeatBalance(numZones).hmThermalMassMultErrIndex, 0);

// Test 3: Multiplier is greater than minimum but less than maximum. Set the statistical variables accordingly.
calcHMmult = 10.0;
expectedHMmult = 10.0;
expectedHMsum = 10.0;
expectedHMcount = 1;
expectedHMavg = 10.0;
processInverseModelMultpHM(*state, calcHMmult, calcHMsum, calcHMcount, calcHMavg, numZones);
EXPECT_NEAR(calcHMmult, expectedHMmult, allowableTolerance);
EXPECT_NEAR(calcHMsum, expectedHMsum, allowableTolerance);
EXPECT_NEAR(calcHMcount, expectedHMcount, allowableTolerance);
EXPECT_NEAR(calcHMavg, expectedHMavg, allowableTolerance);
EXPECT_EQ(state->dataZoneTempPredictorCorrector->zoneHeatBalance(numZones).hmThermalMassMultErrIndex, 0);

// Test 4: Multiplier is greater than maximum. Produce an error message but still set the statistical variables accordingly.
calcHMmult = 50.0;
expectedHMmult = 50.0;
expectedHMsum = 60.0;
expectedHMcount = 2;
expectedHMavg = 30.0;
processInverseModelMultpHM(*state, calcHMmult, calcHMsum, calcHMcount, calcHMavg, numZones);
EXPECT_NEAR(calcHMmult, expectedHMmult, allowableTolerance);
EXPECT_NEAR(calcHMsum, expectedHMsum, allowableTolerance);
EXPECT_NEAR(calcHMcount, expectedHMcount, allowableTolerance);
EXPECT_NEAR(calcHMavg, expectedHMavg, allowableTolerance);
EXPECT_NE(state->dataZoneTempPredictorCorrector->zoneHeatBalance(numZones).hmThermalMassMultErrIndex,
0); // This is now set, won't be zero anymore
std::string const error_string =
delimited_string({" ** Warning ** Hybrid model thermal mass multiplier higher than the limit for Hybrid Zone",
" ** ~~~ ** This means that the ratio of the zone air heat capacity for the current time step to the",
" ** ~~~ ** zone air heat storage is higher than the maximum limit of 30.0."});
EXPECT_TRUE(compare_err_stream(error_string, true));

// Test 5: Repeat of Test 1--verifying that it won't impact the statistical variables. No error message.
calcHMmult = 0.5;
expectedHMmult = 1.0;
expectedHMsum = 60.0;
expectedHMcount = 2;
expectedHMavg = 30.0;
processInverseModelMultpHM(*state, calcHMmult, calcHMsum, calcHMcount, calcHMavg, numZones);
EXPECT_NEAR(calcHMmult, expectedHMmult, allowableTolerance);
EXPECT_NEAR(calcHMsum, expectedHMsum, allowableTolerance);
EXPECT_NEAR(calcHMcount, expectedHMcount, allowableTolerance);
EXPECT_NEAR(calcHMavg, expectedHMavg, allowableTolerance);
EXPECT_NE(state->dataZoneTempPredictorCorrector->zoneHeatBalance(numZones).hmThermalMassMultErrIndex, 0);
}
Loading