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

Fix #10574 - Deal with BIPVT for WaterHeater sizing and throw error if Total Collector area found is zero #10577

Merged
merged 4 commits into from
Jul 25, 2024

Conversation

jmarrec
Copy link
Contributor

@jmarrec jmarrec commented Jun 19, 2024

Pull request overview

The issue is specific to the Solar Loop Water Heater. It's really a very specific combination of factors that lead to the issue:

  • Tank sizing uses the PerSolarCollectorArea
  • The solar collectors in the model are PhotovoltaicThermal ones (BIPVT), not the regular FlatPlatWater ones
    • This is currently not handled by the routine for calculating the area
  • Tank has "Ambient Temperature Indicator" set to "Zone"

As a result the collector area is found to be 0.0, there are zero warnings/errors reported.

The tank volume becomes zero, so the mass of the fluid is also 0, and in CalcTankTemp, you end up with a NaN due to a divide by zero.

This tank has "Ambient Temperature Indicator" set to "Zone", and that NaN propagates to the zone internal gains, and you end up with a DualSetPointWithDeadBand error due to that.

Tested that I can reproduce the crash with the Defect File (I put it on DevSupport) and it runs to completion after fix

Pull Request Author

Add to this list or remove from it as applicable. This is a simple templated set of guidelines.

  • Title of PR should be user-synopsis style (clearly understandable in a standalone changelog context)
  • Label the PR with at least one of: Defect, Refactoring, NewFeature, Performance, and/or DoNoPublish
  • Pull requests that impact EnergyPlus code must also include unit tests to cover enhancement or defect repair
  • Author should provide a "walkthrough" of relevant code changes using a GitHub code review comment process
  • If any diffs are expected, author must demonstrate they are justified using plots and descriptions
  • If changes fix a defect, the fix should be demonstrated in plots and descriptions
  • If any defect files are updated to a more recent version, upload new versions here or on DevSupport
  • If IDD requires transition, transition source, rules, ExpandObjects, and IDFs must be updated, and add IDDChange label
  • If structural output changes, add to output rules file and add OutputChange label
  • If adding/removing any LaTeX docs or figures, update that document's CMakeLists file dependencies

Reviewer

This will not be exhaustively relevant to every PR.

  • Perform a Code Review on GitHub
  • If branch is behind develop, merge develop and build locally to check for side effects of the merge
  • If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If feature, test running new feature, try creative ways to break it
  • CI status: all green or justified
  • Check that performance is not impacted (CI Linux results include performance check)
  • Run Unit Test(s) locally
  • Check any new function arguments for performance impacts
  • Verify IDF naming conventions and styles, memos and notes and defaults
  • If new idf included, locally check the err file and other outputs

```
[ RUN      ] EnergyPlusFixture.WH_Sizing
/Users/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/WaterThermalTanks.unit.cc:5690: Failure
Expected equality of these values:
  5.00
    Which is: 5
  Tank.Sizing.TotalSolarCollectorArea
    Which is: 0
/Users/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/WaterThermalTanks.unit.cc:5692: Failure
Expected equality of these values:
  1.0
    Which is: 1
  Tank.Volume
    Which is: 0
/Users/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/WaterThermalTanks.unit.cc:5711: Failure
Value of: std::isnan(Tank.AmbientZoneGain)
  Actual: true
Expected: false
/Users/julien/Software/Others/EnergyPlus2/tst/EnergyPlus/unit/WaterThermalTanks.unit.cc:5712: Failure
Expected equality of these values:
  0.0
    Which is: 0
  Tank.AmbientZoneGain
    Which is: nan
```
@jmarrec jmarrec added the Defect Includes code to repair a defect in EnergyPlus label Jun 19, 2024
@jmarrec jmarrec self-assigned this Jun 19, 2024
…eed to check if we need to trigger the getinput for them
@jmarrec jmarrec force-pushed the 10574_WaterHeater_Autosizing_NaN branch from 7567778 to 4ee8bcb Compare June 19, 2024 23:50
Comment on lines +11268 to +11270
for (int CollectorNum = 1; CollectorNum <= state.dataPhotovoltaicThermalCollector->NumPVT; ++CollectorNum) {
auto const &collector = state.dataPhotovoltaicThermalCollector->PVT(CollectorNum);
this->Sizing.TotalSolarCollectorArea += collector.AreaCol;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also check the BIPVT collectors

Comment on lines +11273 to +11283
if (this->VolumeWasAutoSized) {
if (this->Sizing.TotalSolarCollectorArea > 0) {
tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
} else {
ShowFatalError(state,
format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
"area of Collectors is zero.",
RoutineName,
this->Name));
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If VolumeWasAutosized, and the collector area found is 0, we throw.

Otherwise we'd end up with the same issue, the volume is 0, the Mass of tank fluid (kg) m is therefore 0 when CalcTankTemp is called, and you end up with a divide by zero.

Copy link
Member

Choose a reason for hiding this comment

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

Looks good.

@@ -11201,9 +11202,6 @@ void WaterThermalTankData::SizeTankForSupplySide(EnergyPlusData &state)

static constexpr std::string_view RoutineName("SizeTankForSupplySide");

Real64 Tstart = 14.44;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

  • TODO: should this return early?
if (!this->VolumeWasAutoSized && !this->MaxCapacityWasAutoSized) {
    return;
}

SizeStandAloneWaterHeater does avoid doing work

if (this->VolumeWasAutoSized || this->MaxCapacityWasAutoSized) {

Copy link
Member

Choose a reason for hiding this comment

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

We could, did you check if it triggered any failures or diffs when you return early? If it causes any issues, then I say we push that to a later PR. If it is a no diff/issue and we just gain a little efficiency, then that's great.

Comment on lines +11264 to +11265
auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
Copy link
Contributor Author

@jmarrec jmarrec Jun 19, 2024

Choose a reason for hiding this comment

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

  • TODO: Should we make this should only count the collectors that serve this WH?!
const int thisPlantNum = this->SrcSidePlantLoc.loopNum;
const int plantNum = collector.plantLoc.loopNum;
if (plantNum == thisPlantNum) {
  this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
}

The documentation does say that it uses all of the collectors in the model, but I think that is weird.

https://bigladdersoftware.com/epx/docs/24-1/input-output-reference/group-water-heaters.html#field-design-mode

PerSolarCollectorArea
This design method scales tank volume based on the collector area for a solar hot water collector. The collector area is summed for all the collectors in the model and each tank is sized for the total. The collector area is determined from input for Solar Collectors defined elsewhere in the input file.

Copy link
Member

Choose a reason for hiding this comment

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

That is an interesting idea. Are there any corner cases we would need to consider further? Multiple WH on the same plant loop or something? I don't think any corner cases would cause problems, but it may just continue to use non-ideal sizing values or something. Either way, I'm not sure we should worry about that in this scope.

@@ -5427,3 +5435,279 @@ TEST_F(EnergyPlusFixture, setBackupElementCapacityTest)
expectedAnswer = -456.0;
EXPECT_NEAR(DSup.BackupElementCapacity, expectedAnswer, allowedTolerance);
}

TEST_F(EnergyPlusFixture, MixedTank_PVT_Per_VolumeSizing_PerSolarCollectorArea)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New unit test.

A PlantLoop with a WHMixed on the supply, a BIPVT collector on the demand.

WHMixed was Autosized Volume, Sizing is PerCollectorArea

Comment on lines +5679 to +5692
state->dataPlnt->PlantFirstSizesOkayToFinalize = true;
state->dataPlnt->PlantFinalSizesOkayToReport = true;
Tank.onInitLoopEquip(*state, plantLocSupply);
EXPECT_ENUM_EQ(DataPlant::LoopSideLocation::Supply, Tank.SrcSidePlantLoc.loopSideNum);
EXPECT_EQ(1, Tank.SrcSidePlantLoc.loopNum);

// compare_eio_stream(delimited_string({
// "Water Heater Information,WaterHeater:Mixed,SOLAR LOOP WATER HEATER,-99998.9999,0.0,0.000,0.0000",
// "! <Component Sizing Information>, Component Type, Component Name, Input Field Description, Value",
// " Component Sizing Information, WaterHeater:Mixed, SOLAR LOOP WATER HEATER, Tank Volume [m3], 1.00000",
// }));
EXPECT_DOUBLE_EQ(5.00, Tank.Sizing.TotalSolarCollectorArea);
EXPECT_DOUBLE_EQ(0.2, Tank.Sizing.TankCapacityPerCollectorArea);
EXPECT_DOUBLE_EQ(1.0, Tank.Volume);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Autosizing works with BIPVT

Comment on lines +5694 to +5712
state->dataGlobal->HourOfDay = 0;
state->dataGlobal->TimeStep = 1;
state->dataGlobal->TimeStepZone = 1.0 / 60.0; // one-minute system time step
state->dataHVACGlobal->TimeStepSys = state->dataGlobal->TimeStepZone;
state->dataHVACGlobal->TimeStepSysSec = state->dataHVACGlobal->TimeStepSys * Constant::SecInHour;
Tank.SavedTankTemp = 60.0;
Tank.TankTemp = 60.0;
Tank.AmbientTempZone = 20.0;
Tank.AmbientTemp = 20.0;
Tank.UseInletTemp = 20.0;
Tank.SetPointTemp = 55.0;
Tank.SetPointTemp2 = Tank.SetPointTemp;
Tank.TimeElapsed = 0.0;

Tank.SourceMassFlowRate = 0.0;

Tank.CalcWaterThermalTankMixed(*state);
EXPECT_FALSE(std::isnan(Tank.AmbientZoneGain));
EXPECT_DOUBLE_EQ(0.0, Tank.AmbientZoneGain); // Didn't define on/off cycle losses
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Specifically test for the NaN encountered.

@Myoldmopar
Copy link
Member

There are still several TODO's so I'm putting this as waiting on developer for now.

@jmarrec
Copy link
Contributor Author

jmarrec commented Jul 8, 2024

@Myoldmopar These TODOs are meant for discussion with you/the reviewer.

it's stuff I noticed, that is extra, and that maybe should be addressed, maybe here (in the case of early returns), maybe into another (counting per zone)

@Myoldmopar
Copy link
Member

@Myoldmopar These TODOs are meant for discussion with you/the reviewer.

I see, I should have looked closer. Back when the PR list was like 60 I was looking quickly for PRs that appeared 'not done', and the TODO's were a red flag to me. :) I'll check it out in more detail now.

Copy link
Member

@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.

My feeling on the TODO's is to just wait until a later PR. This seems good to me. Anyone have any issues?

Comment on lines +11264 to +11265
auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
Copy link
Member

Choose a reason for hiding this comment

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

That is an interesting idea. Are there any corner cases we would need to consider further? Multiple WH on the same plant loop or something? I don't think any corner cases would cause problems, but it may just continue to use non-ideal sizing values or something. Either way, I'm not sure we should worry about that in this scope.

@@ -11201,9 +11202,6 @@ void WaterThermalTankData::SizeTankForSupplySide(EnergyPlusData &state)

static constexpr std::string_view RoutineName("SizeTankForSupplySide");

Real64 Tstart = 14.44;
Copy link
Member

Choose a reason for hiding this comment

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

We could, did you check if it triggered any failures or diffs when you return early? If it causes any issues, then I say we push that to a later PR. If it is a no diff/issue and we just gain a little efficiency, then that's great.

Comment on lines +11273 to +11283
if (this->VolumeWasAutoSized) {
if (this->Sizing.TotalSolarCollectorArea > 0) {
tmpTankVolume = this->Sizing.TotalSolarCollectorArea * this->Sizing.TankCapacityPerCollectorArea;
} else {
ShowFatalError(state,
format("{}: Tank=\"{}\", requested sizing for volume with PerSolarCollectorArea but total found "
"area of Collectors is zero.",
RoutineName,
this->Name));
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Looks good.

this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(state.dataSolarCollectors->Collector(CollectorNum).Surface).Area;
auto const &collector = state.dataSolarCollectors->Collector(CollectorNum);
this->Sizing.TotalSolarCollectorArea += state.dataSurface->Surface(collector.Surface).Area;
}
Copy link
Member

@Myoldmopar Myoldmopar Jul 15, 2024

Choose a reason for hiding this comment

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

I'm so tempted to say things like "range-based loop" and "std::accumulate", both of which could help remove the 1-based array dependence, but I won't for now. https://godbolt.org/z/35jEcv1E5

@Myoldmopar
Copy link
Member

This is still fine, just let me pull develop and test locally.

@Myoldmopar
Copy link
Member

Yep, still good here. Merging, thanks @jmarrec

@Myoldmopar Myoldmopar merged commit 047575a into develop Jul 25, 2024
15 checks passed
@Myoldmopar Myoldmopar deleted the 10574_WaterHeater_Autosizing_NaN branch July 25, 2024 20:41
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 for autosizing when using WaterHeater:Mixed
6 participants