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

Surf heat balance arrays refactoring #8744

Merged
merged 40 commits into from
May 29, 2021
Merged

Conversation

xuanluo113
Copy link
Contributor

@xuanluo113 xuanluo113 commented May 3, 2021

Pull request overview

This branch including the refactoring of moving run time heat balance variables from SurfaceData object to surface arrays. The following refactorings are also made along with the restructuring.

  1. Put GetMovableInsulationData and GetShadingSurfReflectanceData after Surface is allocated and sorted. The related property arrays do not need to be assigned to SurfaceTmp array and copied to Surface later.
  2. Move Encl arrays to DataHeatBal namespaces.
  3. Remove dataHeatBalSurf->ZoneMRT arrays withthe same meaning as dataHeatBal->ZoneMRT arrays to reduce data copies at each time step.
  4. Naming convention of surface and zone arrays in DataSurface and DataHeatBal namespaces (e.g. QS -> EnclSolSWRad, QL -> EnclRadQLWRad).
  5. Reduce run time allocation checking such as state.dataSolarShading->MustAllocSolarShading or if (!allocated(state.dataHeatBal->EnclSolQSWRad)) and (conditionally) allocate the arrays once during simulation setup. These conditions do not have to be checked at every time step.
  6. Convert the third party function call sum_product_sub(state.dataSurface->Surface, &SurfaceData::Area, &SurfaceData::OutDryBulbTemp, SurfPtrARR) to manual calc of sum product (as SurfOutDryBulbTemp field is not in SurfaceData domain).
  7. Some function calls to pass Surface Object pointer (&surface) -> pass SurfNum (index only).

The CI performance is not green. Other than items 6 & 7 I don't see what other changes may possibly reduce the performance.

There are two parts of text diffs causing by:

  1. Reporting state.dataSurface->SurfExtConvCoeff(SurfLoop) instead of Surface(SurfLoop).ExtConvCoeff changes the eio printout from 0.0 to 0 for zero values.
 print(state.files.eio,
      Format_901,
      Surface(SurfLoop).Name,
-     Surface(SurfLoop).ExtConvCoeff,
+     state.dataSurface->SurfExtConvCoeff(SurfLoop),
  1. Setup of output variable "Zone Mean Radiant Temperature" is moved from DataHeatBalSurf to DataHeatBal, causing the reordering of the RDD files.

There are still quite a few related array reordering, renaming and initialization issue to fix including:

  1. The todos marked in DataHeatBalance and DataHeatBalanceSurf
  2. Avoid using dimension for initialization.
  3. Naming of NumOfZones, NumOfSolarEnclosures, etc. (in comparing to TotSurfaces?)
    that I plan to do in some other branch later.

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

state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataLoopNodes->Node(state.dataSurface->SurfLinkedOutAirNode(SurfNum)).OutAirWindSpeed;
state.dataSurface->SurfOutWindDir(SurfNum) = state.dataLoopNodes->Node(state.dataSurface->SurfLinkedOutAirNode(SurfNum)).OutAirWindDir;
state.dataSurface->SurfOutDryBulbTemp(SurfNum) =
state.dataLoopNodes->Node(state.dataSurface->SurfLinkedOutAirNode(SurfNum)).OutAirDryBulb;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we should do this type of formatting at this time. IMO, it would be better to leave the lines as one and to shorten some of the variable and field names, .e.g.,

s.surface->OutDryBulbTemp(SurfNum) = s.loopNodes->Node(s.surface->LinkedOutAirNode(SurfNum)).OutAirDryBulb;

@@ -2137,7 +2156,7 @@ void InitThermalAndFluxHistories(EnergyPlusData &state)
auto &Surface(state.dataSurface->Surface);

// First do the "bulk" initializations of arrays sized to NumOfZones
state.dataHeatBal->ZoneMRT = 23.0; // module level array
state.dataHeatBal->ZoneMRT = 23.0; // module level array
state.dataHeatBalFanSys->MAT = 23.0; // DataHeatBalFanSys array
state.dataHeatBalFanSys->ZT = 23.0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should avoid using these array initializers and initialize explicitly using a loop. This should also allow us to combine these different initialization loops into a single loop.

@@ -2137,7 +2156,7 @@ void InitThermalAndFluxHistories(EnergyPlusData &state)
auto &Surface(state.dataSurface->Surface);

// First do the "bulk" initializations of arrays sized to NumOfZones
state.dataHeatBal->ZoneMRT = 23.0; // module level array
state.dataHeatBal->ZoneMRT = 23.0; // module level array
state.dataHeatBalFanSys->MAT = 23.0; // DataHeatBalFanSys array
state.dataHeatBalFanSys->ZT = 23.0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, should use a symbolic constexpr rather than a literal 23.0.

@@ -2328,7 +2347,7 @@ void EvalOutsideMovableInsulation(EnergyPlusData &state)
state.dataMaterial->Material(state.dataConstruction->Construct(ConstrNum).LayerPoint(1)).Roughness;
continue;
}
auto const MaterialIndex(state.dataSurface->Surface(SurfNum).MaterialMovInsulExt);
auto const MaterialIndex(state.dataSurface->SurfMaterialMovInsulExt(SurfNum));
auto const MaterialGroupNum(state.dataMaterial->Material(MaterialIndex).Group);
state.dataHeatBalSurf->SurfMovInsulExtPresent(SurfNum) = true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's shorter and clearer to write int than auto.

@@ -4970,7 +4970,7 @@ void FigureSolarBeamAtTimestep(EnergyPlusData &state, int const iHour, int const

for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {

if (!state.dataSurface->Surface(SurfNum).ShadowingSurf &&
if (!state.dataSurface->SurfIsShadowing(SurfNum) &&
(!state.dataSurface->Surface(SurfNum).HeatTransSurf || !state.dataSurface->Surface(SurfNum).ExtSolar))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is IsShadowing a surface-level array while HeatTransSurf and ExtSolar are fields in the Surface array? It seems like these fields have similar usages.

Copy link
Contributor Author

@xuanluo113 xuanluo113 May 16, 2021

Choose a reason for hiding this comment

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

@amirroth HeatTransSurf and ExtSolar are assigned during input processing and saved in SurfaceTmp (e.g.ExtSolar = UtilityRoutines::SameString(state.dataIPShortCut->cAlphaArgs(ArgPointer), "SunExposed")). With all surface data being read, SurfaceTmp arrays are sorted and copied to a new Surface array. So these fields remain as fields not arrays because otherwise, I'll have to sort the arrays. IsShadowing is evaluated and assigned afterward. I don't know if it's worthwhile or good practice to move it out from a field to an array.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not suggesting removing it from the class, I was suggesting replicating it in its own array if the use cases warrants it. If not (maybe surfaces are sorted so that you don't have to explicitly check for these conditions or maybe we are using explicit surface lists that only include surfaces with these conditions) then it doesn't matter.

@@ -771,7 +771,7 @@ void AllocateModuleArrays(EnergyPlusData &state)
state.dataHeatBal->CosIncAngHR.dimension(24, state.dataSurface->TotSurfaces, 0.0);
state.dataHeatBal->CosIncAng.dimension(state.dataGlobal->NumOfTimeStepInHour, 24, state.dataSurface->TotSurfaces, 0.0);
state.dataHeatBal->SurfAnisoSkyMult.dimension(state.dataSurface->TotSurfaces,
Copy link
Collaborator

Choose a reason for hiding this comment

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

FWIW I would also like us to stop using dimension and replace with allocate and explicit loop initialization. This will allow us to combine the initializations multiple arrays into a single loop, which improves performance.

@xuanluo113 xuanluo113 changed the title Surf hb struct Surf heat balance arrays refactoring May 15, 2021
@xuanluo113 xuanluo113 added the Performance Includes code changes that are directed at improving the runtime performance of EnergyPlus label May 15, 2021
@xuanluo113 xuanluo113 added this to the EnergyPlus 10.0 BugFix Freeze milestone May 15, 2021
@xuanluo113
Copy link
Contributor Author

@mjwitte This branch is ready for review. The PR overview lists a walkthrough of the changes and diffs. The CI performance is not green... but tested locally 10x_incr_100zones100surfaces.idf shows a 8% run time reduction comparing to the current develop head. For other models smaller than this, the run time difference is marginal.
I've addressed some of @amirroth 's comments above. There are some remaining related issues either Amir mentioned or I plotted, and I plan to start another branch to continue working on (as listed in the overview).
Thanks very much!

@amirroth
Copy link
Collaborator

Hmmm. Surprising that you are not seeing any improvements on other models, although 8% on a big model is good. Maybe some other things like data-dependent branches are getting in the way of the speedups. Which functions are you expecting to see speedup on?

Comment on lines 5907 to 5909
if (!surface.ShadowSurfPossibleObstruction) return false; // Do Consider separate octree without filtered surfaces
int const ObsSurfNum = surface.Index;
if (!state.dataSurface->SurfShadowPossibleObstruction(ObsSurfNum)) return false; // Do Consider separate octree without filtered surfaces
Copy link
Contributor

Choose a reason for hiding this comment

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

Since a single instance of Surface is a parameter to this lambda function, is it possible that the lookup of state.dataSurface->SurfShadowPossibleObstruction(ObsSurfNum) is more work than fetching surface.ShadowSurfPossibleObstruction?

Copy link
Collaborator

@amirroth amirroth May 18, 2021

Choose a reason for hiding this comment

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

It is. However, given that this function was written when all data fields were part of the SurfaceData struct, it is also possible that this lambda needs to be written to take a SurfIdx argument rather than a Surface & argument. Either way, I don't think this is a huge immediate problem.

Copy link
Contributor

Choose a reason for hiding this comment

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

The reason I asked about this is because the performance test for 15zonePSZ shows about 38 million more "cost" on this branch than develop (0.24% more). About 10m is coming from daylighting functions. Small overall, but just curious why these code changes would result in any increase at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. I found this piece suspicious when I rewrote this part to fit the new array structure into the lambda function. I actually had to add an Index to Surface to find the SurfNum. (Potentially Index should be useful as there are still places we find Surface Index by Name using FindItemInList)
I didn't realize it would create such performance reduction. I actually thought about rewriting the lambda function, but the syntax at this part looked quite confusing to me. Since it's potentially a performance bottleneck I'll take another look.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. This one change shouldn't be that much, unless daylighting functions are called much more frequently than they need to be. Overall, the increase in instruction count probably has to do with the fact that we have to call many more array constructors & initializers. Some of that will go away if we initialize explicitly using one or several large loops as opposed to using dimension. Definitely things to keep markers on.

Copy link
Collaborator

Choose a reason for hiding this comment

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

A few things.

  1. It's fine to have both a SurfShadowPossibleObstruction array and a SurfShadowPossibleObstruction field. If they don't change over the life of the model, then there is no overhead associated with keeping them in sync.
  2. It's also probably fine to leave SurfShadowPossibleObstruction as a field since I don't imagine it is used in the kinds of loops that would be amenable to vectorization anyway.
  3. You could change the PierceSurface/SurfaceOctree functions to take SurfIndex instead of Surface & as an argument. If we are worried about the performance of those functions there are possibly other things we can do to make them faster, not sure if these mods belong in this branch/PR.

@xuanluo113
Copy link
Contributor Author

@mjwitte So I reverted the changes of two shadowing fields to arrays. Testing locally before and after using 15zonePSZ, comparing develop and this branch, I can't see any meaningful and consistent performance difference. I also used local timers to test the daylighting functions, and the run time difference is marginal (<0.5%). I'll wait for CI to tell the updated performance difference then.

Comment on lines 1304 to 1305
Array1D<Real64> SurfIntConvCoeff; // Interior Convection Coefficient pointer (different data structure) when being overridden
Array1D<Real64> SurfExtConvCoeff; // Exterior Convection Coefficient pointer (different data structure) when being overridden
Copy link
Contributor

Choose a reason for hiding this comment

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

These should be integers. That should reduce the eio diffs. Without seeing the declarations, I had assumed SurfIntConvCoeff was the actual convection coefficient value. Perhaps these should be renamed, e.g. SurfIntConvCoeffIndex ?

@mjwitte mjwitte marked this pull request as ready for review May 28, 2021 17:28
Copy link
Contributor

@mjwitte mjwitte left a comment

Choose a reason for hiding this comment

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

@xuanluo113 I pushed up a few minor doc edits to clean up font and equation width issues (that were present before but were more pronounced with the longer variable names). Once CI comes back clean, I will merge this.

@mjwitte mjwitte merged commit b8f984a into NREL:develop May 29, 2021
@mjwitte mjwitte deleted the surf-hb-struct branch May 29, 2021 03:06
@Myoldmopar Myoldmopar modified the milestones: EnergyPlus 9.6 BugFix Freeze, EnergyPlus 9.6 Release Aug 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Performance Includes code changes that are directed at improving the runtime performance of EnergyPlus
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants