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

#5145 - HeatExchangerAirToAirSensibleAndLatent normalization divisor error #5193

Merged
merged 3 commits into from
May 14, 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
38 changes: 30 additions & 8 deletions src/model/HeatExchangerAirToAirSensibleAndLatent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
// Remove when deprecated methods are removed
#include "../utilities/core/DeprecatedHelpers.hpp"

#include <limits>

namespace openstudio {

namespace model {
Expand Down Expand Up @@ -1026,8 +1028,13 @@ namespace model {
return setCurveAt75(*curve_, sensibleEffectivenessat75HeatingAirFlow);
}

auto tableLookup = makeTable(sensibleEffectivenessat75HeatingAirFlow, sensibleEffectivenessat100HeatingAirFlow(), model(),
fmt::format("{}_SensHeatEff", nameString()));
const auto val100 = sensibleEffectivenessat100HeatingAirFlow();
if (std::abs(val100) < std::numeric_limits<double>::min()) {
LOG_AND_THROW("Unable to set " << briefDescription()
<< "'s Sensible Effectiveness at 75 Heating Air Flow because the value at 100 is zero and would lead to a "
"Normalization divisor equal to zero.");
}
auto tableLookup = makeTable(sensibleEffectivenessat75HeatingAirFlow, val100, model(), fmt::format("{}_SensHeatEff", nameString()));
Comment on lines +1031 to +1037
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HX: we actually throw if we get to such a condition.

return setSensibleEffectivenessofHeatingAirFlowCurve(tableLookup);
}

Expand All @@ -1038,8 +1045,13 @@ namespace model {
return setCurveAt75(*curve_, latentEffectivenessat75HeatingAirFlow);
}

auto tableLookup =
makeTable(latentEffectivenessat75HeatingAirFlow, latentEffectivenessat100HeatingAirFlow(), model(), fmt::format("{}_LatHeatEff", nameString()));
const auto val100 = latentEffectivenessat100HeatingAirFlow();
if (std::abs(val100) < std::numeric_limits<double>::min()) {
LOG_AND_THROW("Unable to set " << briefDescription()
<< "'s Latent Effectiveness at 75 Heating Air Flow because the value at 100 is zero and would lead to a "
"Normalization divisor equal to zero.");
}
auto tableLookup = makeTable(latentEffectivenessat75HeatingAirFlow, val100, model(), fmt::format("{}_LatHeatEff", nameString()));
return setLatentEffectivenessofHeatingAirFlowCurve(tableLookup);
}

Expand All @@ -1050,8 +1062,13 @@ namespace model {
return setCurveAt75(*curve_, sensibleEffectivenessat75CoolingAirFlow);
}

auto tableLookup = makeTable(sensibleEffectivenessat75CoolingAirFlow, sensibleEffectivenessat100CoolingAirFlow(), model(),
fmt::format("{}_SensCoolEff", nameString()));
const auto val100 = sensibleEffectivenessat100CoolingAirFlow();
if (std::abs(val100) < std::numeric_limits<double>::min()) {
LOG_AND_THROW("Unable to set " << briefDescription()
<< "'s Sensible Effectiveness at 75 Cooling Air Flow because the value at 100 is zero and would lead to a "
"Normalization divisor equal to zero.");
}
auto tableLookup = makeTable(sensibleEffectivenessat75CoolingAirFlow, val100, model(), fmt::format("{}_SensCoolEff", nameString()));
return setSensibleEffectivenessofCoolingAirFlowCurve(tableLookup);
}

Expand All @@ -1062,8 +1079,13 @@ namespace model {
return setCurveAt75(*curve_, latentEffectivenessat75CoolingAirFlow);
}

auto tableLookup =
makeTable(latentEffectivenessat75CoolingAirFlow, latentEffectivenessat100CoolingAirFlow(), model(), fmt::format("{}_LatCoolEff", nameString()));
const auto val100 = latentEffectivenessat100CoolingAirFlow();
if (std::abs(val100) < std::numeric_limits<double>::min()) {
LOG_AND_THROW("Unable to set " << briefDescription()
<< "'s Latent Effectiveness at 75 Cooling Air Flow because the value at 100 is zero and would lead to a "
"Normalization divisor equal to zero.");
}
auto tableLookup = makeTable(latentEffectivenessat75CoolingAirFlow, val100, model(), fmt::format("{}_LatCoolEff", nameString()));
return setLatentEffectivenessofCoolingAirFlowCurve(tableLookup);
}

Expand Down
14 changes: 10 additions & 4 deletions src/model/TableLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <algorithm>
#include <iomanip>
#include <limits>

namespace openstudio {
namespace model {
Expand Down Expand Up @@ -165,8 +166,13 @@ namespace model {
return result;
}

bool TableLookup_Impl::setNormalizationDivisor(double normalizationDivisior) {
bool result = setDouble(OS_Table_LookupFields::NormalizationDivisor, normalizationDivisior);
bool TableLookup_Impl::setNormalizationDivisor(double normalizationDivisor) {
if (std::abs(normalizationDivisor) < std::numeric_limits<double>::min()) {
LOG(Warn, "Unable to set " << briefDescription() << "'s Normalization divisor to zero.");
return false;
}

bool result = setDouble(OS_Table_LookupFields::NormalizationDivisor, normalizationDivisor);
Comment on lines +169 to +175
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TableLookup will reject 0.0 now, and return false + a warn

return result;
}

Expand Down Expand Up @@ -374,8 +380,8 @@ namespace model {
return getImpl<detail::TableLookup_Impl>()->setNormalizationMethod(normalizationMethod);
}

bool TableLookup::setNormalizationDivisor(double normalizationDivisior) {
return getImpl<detail::TableLookup_Impl>()->setNormalizationDivisor(normalizationDivisior);
bool TableLookup::setNormalizationDivisor(double normalizationDivisor) {
return getImpl<detail::TableLookup_Impl>()->setNormalizationDivisor(normalizationDivisor);
}

bool TableLookup::setMinimumOutput(double minimumOutput) {
Expand Down
2 changes: 1 addition & 1 deletion src/model/TableLookup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace model {

bool setNormalizationMethod(const std::string& normalizationMethod);

bool setNormalizationDivisor(double normalizationDivisior);
bool setNormalizationDivisor(double normalizationDivisor);

bool setMinimumOutput(double minimumOutput);
void resetMinimumOutput();
Expand Down
2 changes: 1 addition & 1 deletion src/model/TableLookup_Impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ namespace model {

bool setNormalizationMethod(const std::string& normalizationMethod);

bool setNormalizationDivisor(double normalizationDivisior);
bool setNormalizationDivisor(double normalizationDivisor);

bool setMinimumOutput(double minimumOutput);
void resetMinimumOutput();
Expand Down
20 changes: 20 additions & 0 deletions src/model/test/HeatExchangerAirToAirSensibleAndLatent_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,26 @@ TEST_F(ModelFixture, HeatExchangerAirToAirSensibleAndLatent_Deprecated75Effectiv
EXPECT_TRUE(hx.setLatentEffectivenessat75CoolingAirFlow(val2));
EXPECT_DOUBLE_EQ(val2, hx.latentEffectivenessat75CoolingAirFlow());
}

// Ensure we protect users against dumb issues that lead to a normalization divisor of zero
{
// Ensure no curves assigned for now
hx.resetSensibleEffectivenessofHeatingAirFlowCurve();
hx.resetLatentEffectivenessofHeatingAirFlowCurve();
hx.resetSensibleEffectivenessofCoolingAirFlowCurve();
hx.resetLatentEffectivenessofCoolingAirFlowCurve();

// IMHO this doesn't make sense (IDD should be `\minimum> 0.0`), but the E+ IDD and OS IDD have always allowed it, so leaving it untouched
EXPECT_TRUE(hx.setSensibleEffectivenessat100HeatingAirFlow(0.0));
EXPECT_TRUE(hx.setLatentEffectivenessat100HeatingAirFlow(0.0));
EXPECT_TRUE(hx.setSensibleEffectivenessat100CoolingAirFlow(0.0));
EXPECT_TRUE(hx.setLatentEffectivenessat100CoolingAirFlow(0.0));

EXPECT_ANY_THROW(hx.setSensibleEffectivenessat75HeatingAirFlow(0.5));
EXPECT_ANY_THROW(hx.setLatentEffectivenessat75HeatingAirFlow(0.5));
EXPECT_ANY_THROW(hx.setSensibleEffectivenessat75CoolingAirFlow(0.5));
EXPECT_ANY_THROW(hx.setLatentEffectivenessat75CoolingAirFlow(0.5));
}
Comment on lines +405 to +423
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test that it throws with HX

}

TEST_F(ModelFixture, HeatExchangerAirToAirSensibleAndLatent_assignHistoricalEffectivenessCurves) {
Expand Down
4 changes: 4 additions & 0 deletions src/model/test/TableLookup_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ TEST_F(ModelFixture, TableLookup_GettersSetters) {
EXPECT_TRUE(tableLookup.setNormalizationDivisor(0.5));
EXPECT_EQ(0.5, tableLookup.normalizationDivisor());

// #5145 - We reject 0 as a normalization divisor
EXPECT_FALSE(tableLookup.setNormalizationDivisor(0.0));
EXPECT_EQ(0.5, tableLookup.normalizationDivisor());
Comment on lines +54 to +56
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test that TableLookup just returns false and leaves it untouched


// Minimum Output: Optional Double
EXPECT_TRUE(tableLookup.setMinimumOutput(0.6));
ASSERT_TRUE(tableLookup.minimumOutput());
Expand Down
Loading