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

Protect for table lookup zero divisor #10465

Merged
merged 5 commits into from
Apr 12, 2024

Conversation

Myoldmopar
Copy link
Member

Pull request overview

The actual problem wasn't in the load calculations, it was a NaN being propagated after a divide by zero. In the table lookup objects. users can enter a normalization divisor value, and a zero value was allowed to pass through without being checked. This resulted in a NaN value coming out of the curve evaluation, which was immediately causing problems in other parts of the code.

I added a trap for zero value on the normalization divisor and we now issue a severe error. I added a unit test that ensures the zero value is caught properly.

@Myoldmopar Myoldmopar added the Defect Includes code to repair a defect in EnergyPlus label Apr 12, 2024
@Myoldmopar Myoldmopar added this to the EnergyPlus 24.2 milestone Apr 12, 2024
ShowSevereError(
state, format("Table:Lookup named \"{}\": Normalization divisor entered as zero, which is invalid", thisCurve->Name));
ErrorsFound = true;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Check if the value coming in is zero (or essentially zero). This is only hit if the normalization divisor is going to be used (based on other input fields). I'm open to wording improvement, but it should get the job done.


EXPECT_THROW(Curve::GetCurveInput(*state), std::runtime_error);
EXPECT_TRUE(this->err_stream->str().find("Normalization divisor entered as zero") != std::string::npos);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

New unit test that sets up a simple table lookup with zero valued normalization divisor. It first makes sure the runtime error is thrown during curve get input, and then also makes sure there is a specific mention of the normalization divisor in the error output. Note I'm not comparing the whole error output because I don't care about overall output message structure, I just want to make sure the divisor error is, in fact, emitted.

Copy link
Member Author

@Myoldmopar Myoldmopar left a comment

Choose a reason for hiding this comment

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

OK, I think this is good to go if CI agrees. If anyone wants to review, that's welcomed, but it's pretty minimal.

EXPECT_TRUE(found);
if (reset_stream) this->err_stream->str(std::string());
return found;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Added a new worker function on the unit test fixture. This allows you to compare some specific search phrase in the ERR contents without having to list the entire formatted ERR message. See the unit test above in this PR for an example.

// This calls EXPECT_* within the function as well as returns a boolean so you can call [ASSERT/EXPECT]_[TRUE/FALSE] depending
// if it makes sense for the unit test to continue after returning from function.
// Will return true if string is found in the stream and false if it is not
bool compare_err_stream_substring(std::string const &search_string, bool reset_stream = true);
Copy link
Member Author

Choose a reason for hiding this comment

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

New worker function in fixture header so it can be used by unit tests.

@@ -2494,6 +2494,11 @@ namespace Curve {

if (normalizeMethod != NM_NONE && fields.count("normalization_divisor")) {
normalizationDivisor = fields.at("normalization_divisor").get<Real64>();
if (fabs(normalizationDivisor) < std::numeric_limits<Real64>::min()) {
Copy link
Contributor

@jmarrec jmarrec Apr 12, 2024

Choose a reason for hiding this comment

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

std::abs, or if you insist std::fabs
But std:: namespace it.
@Myoldmopar

Copy link
Contributor

Choose a reason for hiding this comment

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

In c++ you have function overloading, so std::abs is sufficient.

In C, abs takes an int, fabs a double, which is a common cause of problems.

@Myoldmopar
Copy link
Member Author

There you go @jmarrec, thanks! OK, if this is clean again after lunch I'll merge.

@Myoldmopar
Copy link
Member Author

All good, merging.

@Myoldmopar Myoldmopar merged commit a6b0834 into develop Apr 12, 2024
15 checks passed
@Myoldmopar Myoldmopar deleted the ProtectForTableLookupZeroDivisor branch April 12, 2024 20:05
bool EnergyPlusFixture::compare_err_stream_substring(std::string const &search_string, bool reset_stream)
{
auto const stream_str = this->err_stream->str();
bool const found = stream_str.find("Normalization divisor entered as zero") != std::string::npos;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you meant search_string instead of harcoding?

Copy link
Member Author

Choose a reason for hiding this comment

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

No I wanted it to search for this hard coded string.

.

.

.
...

OK, OK, I'll slow down. Thank you an awful lot, @jmarrec ! Fix incoming soon - tomorrow.

Copy link
Contributor

Choose a reason for hiding this comment

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

Reading only the first bit on my phone in the email notification cracked me up ;)

Copy link
Contributor

Choose a reason for hiding this comment

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

Clang-tidy 15 definitely catches it for me. I wonder why the cppcheck didn't? And that's because we only run cppcheck in ./src :)

Myoldmopar added a commit that referenced this pull request Apr 15, 2024
Hotfix for #10465 - correctly use the search_string parameter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Defect Includes code to repair a defect in EnergyPlus
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fatal error in heating and cooling loads during sizing run
8 participants