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

Clean headers from A-C #9365

Merged
merged 80 commits into from
Apr 26, 2022
Merged

Clean headers from A-C #9365

merged 80 commits into from
Apr 26, 2022

Conversation

brianlball
Copy link
Contributor

Clean Headers from A-C

@brianlball brianlball added the Refactoring Includes code changes that don't change the functionality of the program, just perform refactoring label Apr 6, 2022
@brianlball brianlball assigned Myoldmopar and brianlball and unassigned brianlball Apr 6, 2022
bool getBoilerInputFlag = true;
Array1D<Boilers::BoilerSpecs> Boiler;

void clear_state() override
{
this->numBoilers = 0;
this->getBoilerInputFlag = true;
this->Boiler.deallocate();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we no longer have to call deallocate()? Did we not have to call it to begin with?

Copy link
Member

Choose a reason for hiding this comment

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

We don't need the deallocate anymore because we are creating a new instance. For the same reason, we don't need to manually go member-by-member resetting them to their original values anymore. It's so much nicer. Once we get all these clear_states cleaned out to the minimal new code, I think we will have a very rapid change where we don't really need the individual clear_state functions anymore. But we'll see...

Copy link
Collaborator

Choose a reason for hiding this comment

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

So ... what is happening to the old instance? Does it get properly destroyed or are we leaking insane amounts of memory here? I admit I don't have a lot of experience with the *this = idiom. It looks very strange and wrong to me.

Copy link
Member

Choose a reason for hiding this comment

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

This should not be leaking anything. I made a prototype of this and called clear_state 50bn times and the memory usage on the process never went up, and I could see the destructor being called every time, so the assignment seems to be doing everything we need here. This paradigm was suggested by @lefticus, so I feel pretty good about it.

Now, that said, I think once we have all the clear_state functions cleaned up to this level, we can just go back to where we are calling clear_state, and change the call so that we don't need the intermediary clear_state function, if in fact all it is doing is deleting the last one and creating a new one.

(As a final aside, just note that this function is only called during serial unit test runs, and in case someone is calling clear_state during serial runs of E+ via API)

MixedWindowsUserCurveNum(0)
{
}
int SimpleBuoyVertWallEqNum = ConvectionConstants::HcInt_FohannoPolidoriVerticalWall; // InConvClass_A3_VertWalls
Copy link
Collaborator

Choose a reason for hiding this comment

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

Straight up one of the most ridiculous data structures in EnergyPlus.

Real64 CoeffA23 = 0.0;
Real64 CoeffA24 = 0.0;
Real64 CoeffA25 = 0.0;
Real64 CoeffA26 = 0.0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What programming was like before arrays were invented.

Copy link
Member

Choose a reason for hiding this comment

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

😄

PressErrIndex(0), FluidIndex(0), myFlag(true), myEnvrnFlag(true), FuelUsed(0.0), BoilerLoad(0.0), BoilerEff(0.0),
BoilerMassFlowRate(0.0), BoilerOutletTemp(0.0), BoilerEnergy(0.0), FuelConsumed(0.0), BoilerInletTemp(0.0),
BoilerFuelTypeForOutputVariable("")
BoilerSpecs() : plantLoc{}
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 this necessary? Why is plantLoc default constructor not called automatically?

Copy link
Member

Choose a reason for hiding this comment

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

This is not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was part of a diff debug and has been removed

@@ -171,15 +164,12 @@ namespace BoilerSteam {

struct BoilerSteamData : BaseGlobalStruct
{
int numBoilers = 0;
bool getSteamBoilerInput = true;
Array1D<BoilerSteam::BoilerSpecs> Boiler;
Copy link
Collaborator

@amirroth amirroth Apr 17, 2022

Choose a reason for hiding this comment

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

There is no reason for a 1D array of anything that isn't int, Real64, or bool to be an Array1D rather than an EPVector.

Copy link
Member

Choose a reason for hiding this comment

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

I agree, we are definitely moving in that direction. We can go back through these few files and add an extra effort to make sure all the Array1Ds are transitioned. We were trying to balance the header file cleanup with how many lines we had to change in the cc files. Changing this to a vector will cause quite a lot of changes, but I'm OK adding that here if you want.

state.dataBranchInputManager->Branch.allocate(state.dataBranchInputManager->NumOfBranches);
int NumOfBranches = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
if (NumOfBranches > 0) {
state.dataBranchInputManager->Branch.allocate(NumOfBranches);
for (auto &e : state.dataBranchInputManager->Branch)
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 this loop necessary?

Copy link
Member

Choose a reason for hiding this comment

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

It doesn't look like it should be now. But it's been in there since the C++ translation. Back in Fortran, right after Branch was allocated, the AssignedLoopName was set to a blank. I think ....maybe.... it was because in Fortran you couldn't rely on the fixed-length strings to be filled with blank spaces? Maybe...?

state.dataBranchInputManager->NumOfBranchLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
state.dataBranchInputManager->BranchList.allocate(state.dataBranchInputManager->NumOfBranchLists);
int NumOfBranchLists = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, CurrentModuleObject);
state.dataBranchInputManager->BranchList.allocate(NumOfBranchLists);
for (auto &e : state.dataBranchInputManager->BranchList) {
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 this loop necessary?

@@ -2743,7 +2743,7 @@ namespace BranchInputManager {
}

// Build node names in branches
for (Count = 1; Count <= state.dataBranchInputManager->NumOfBranches; ++Count) {
for (Count = 1; Count <= (int)state.dataBranchInputManager->Branch.size(); ++Count) {
BranchNodes(Count).UniqueNodeNames.allocate(state.dataBranchInputManager->Branch(Count).NumOfComponents * 2);
BranchNodes(Count).UniqueNodeNames = std::string();
Copy link
Collaborator

Choose a reason for hiding this comment

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

You just did an allocate on this and now you assign it to a blank string because ... ?

Copy link
Member

Choose a reason for hiding this comment

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

I think these all go back to the original Fortran code, I'm not sure if there is a way we can automatically detect these unnecessary things now...but maybe. Anytime we reset a string to a blank seems like a good starting point, since that seems like an odd thing to do inside the simulation. Then we could add some intelligence to snoop just a couple lines before that assignment to see if it detects an allocation of that variable (or any variable?). Could be a fun excursion.

state.dataChilledCeilingPanelSimple->CheckEquipName.allocate(state.dataChilledCeilingPanelSimple->NumCoolingPanels);
state.dataChilledCeilingPanelSimple->CoolingPanel.allocate(NumCoolingPanels);
state.dataChilledCeilingPanelSimple->CoolingPanelSysNumericFields.allocate(NumCoolingPanels);
state.dataChilledCeilingPanelSimple->CheckEquipName.allocate(NumCoolingPanels);
state.dataChilledCeilingPanelSimple->CheckEquipName = true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this assignment operator set all elements of the array to true? That is a terrible idiom. Is this a Fortran legacy thing?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I suppose, and yes, this is a Fortran legacy thing. Really, if the CheckEquipName needs to stay, it has no need to be its own separate array. It should be a simple boolean on the chilled ceiling struct. That's one better pattern we can propagate in this effort for sure.

@Myoldmopar
Copy link
Member

GPU diff, otherwise CI is clean here. I think it's time to start pushing this toward merge. I need to digest the comments above next.

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.

OK, so a net of 2400 lines removed. No diffs. Awesome. This is making a bunch of big improvements in the headers and state classes. I think it could use another pass based on my comments and some comments from @amirroth. And then we can take the outcomes of this branch and apply them to the next group of files shortly. Thanks @brianlball !

@@ -161,10 +161,9 @@ namespace BoilerSteam {

SteamFluidIndex = 0;
state.dataIPShortCut->cCurrentModuleObject = "Boiler:Steam";
state.dataBoilerSteam->numBoilers =
state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
int numBoilers = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, state.dataIPShortCut->cCurrentModuleObject);
Copy link
Member

Choose a reason for hiding this comment

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

Yay, every time an unnecessary variable is removed from state and replaced with a local variable, an angel smiles.

thisBoiler.FullLoadCoef(1) = state.dataIPShortCut->rNumericArgs(8);
thisBoiler.FullLoadCoef(2) = state.dataIPShortCut->rNumericArgs(9);
thisBoiler.FullLoadCoef(3) = state.dataIPShortCut->rNumericArgs(10);
thisBoiler.FullLoadCoef[0] = state.dataIPShortCut->rNumericArgs(8);
Copy link
Member

Choose a reason for hiding this comment

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

Ahhh, zero based indexing and square brackets, the way arrays were meant to be in C++ 😄

PressErrIndex(0), FluidIndex(0), myFlag(true), myEnvrnFlag(true), FuelUsed(0.0), BoilerLoad(0.0), BoilerEff(0.0),
BoilerMassFlowRate(0.0), BoilerOutletTemp(0.0), BoilerEnergy(0.0), FuelConsumed(0.0), BoilerInletTemp(0.0),
BoilerFuelTypeForOutputVariable("")
BoilerSpecs() : plantLoc{}
Copy link
Member

Choose a reason for hiding this comment

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

This is not necessary.

@@ -171,15 +164,12 @@ namespace BoilerSteam {

struct BoilerSteamData : BaseGlobalStruct
{
int numBoilers = 0;
bool getSteamBoilerInput = true;
Array1D<BoilerSteam::BoilerSpecs> Boiler;
Copy link
Member

Choose a reason for hiding this comment

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

I agree, we are definitely moving in that direction. We can go back through these few files and add an extra effort to make sure all the Array1Ds are transitioned. We were trying to balance the header file cleanup with how many lines we had to change in the cc files. Changing this to a vector will cause quite a lot of changes, but I'm OK adding that here if you want.

this->numBoilers = 0;
this->getSteamBoilerInput = true;
this->Boiler.deallocate();
*this = BoilerSteamData();
Copy link
Member

Choose a reason for hiding this comment

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

Yay! So much DRYer!

this->DoCostEstimate = false;
this->numMonetaryUnit = 0;
this->selectedMonetaryUnit = 0;
this->CurntBldg = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0};
Copy link
Member

Choose a reason for hiding this comment

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

You didn't do anything wrong here, but I dislike that the default values on the struct are all 0.0, but in both cases where we instantiate them, we pass a 1.0 as one of the arguments. I think we should clean that up and just default assign it to 1.0 in the struct and then we don't have to have this initializer list anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think the default values have the same value as the reset now

Real64 CoeffA23 = 0.0;
Real64 CoeffA24 = 0.0;
Real64 CoeffA25 = 0.0;
Real64 CoeffA26 = 0.0;
Copy link
Member

Choose a reason for hiding this comment

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

😄

this->NumNodeConnectionErrors = 0;
this->NumOfNodeConnections = 0;
this->MaxNumOfNodeConnections = 0;
this->NodeConnectionAlloc = 1000;
Copy link
Member

Choose a reason for hiding this comment

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

This variable smells funny.

Copy link
Member

Choose a reason for hiding this comment

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

It smells funny for two reasons:

  1. We are carefully allocating out nodes during initialization in chunks, and I think we can rely on C++ to be smart enough to do this behind-the-scenes reservation. Anyway, the second reason is that
  2. This variable is only used in one function, so I'm moving it to a constexpr int inside that function.

this->varyingOrientationSchedIndex = 0;
this->forceBeginEnvResetSuppress = false;
this->oneTimeCompRptHeaderFlag = true;
*this = EnvironmentData();
Copy link
Member

Choose a reason for hiding this comment

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

Yay!!!!!!!!

this->printConsoleOutput = true;
this->installRootOverride = false;
this->numThread = 1;
*this = DataGlobal();
Copy link
Member

Choose a reason for hiding this comment

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

Woo!!

@@ -288,8 +231,7 @@ struct ContaminantBalanceData : BaseGlobalStruct

Array1D<Real64> CONTRAT; // Zone CO2 at the previous time step used in Exact and Euler method
Array1D<Real64> MixingMassFlowCO2; // Mixing MASS FLOW * CO2
int NumContControlledZones = 0;
Real64 OutdoorCO2 = 0.0; // Outdoor CO2 level
Copy link
Member

Choose a reason for hiding this comment

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

I'd love to go through this and clean up all the Array1D's, but I'm not going to add that to this PR.

this->MoreNodeInfo.deallocate();
this->MarkedNode.deallocate();
this->NodeSetpointCheck.deallocate();
*this = LoopNodeData();
Copy link
Member

Choose a reason for hiding this comment

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

😄

@Myoldmopar
Copy link
Member

Just pushed a few commits that should get this ready to merge. This PR cleans up so much, assuming it's clean let's merge this in. If anyone has any remaining comments, speak up soon.

@Myoldmopar
Copy link
Member

@amirroth thanks for commenting on this. I responded, and am happy to keep pushing on this as needed to resolve them, but if some of them are more just notes to 📌 for later, I'll just carry on to get this merged in.

@Myoldmopar
Copy link
Member

Done, I think this should be ready to drop if clean then.

@Myoldmopar
Copy link
Member

All green, merging this one in. Thanks @brianlball for the changes, and @amirroth for the commentary.

@Myoldmopar Myoldmopar merged commit ec07f58 into develop Apr 26, 2022
@Myoldmopar Myoldmopar deleted the CleanHeadersFromAdown branch April 26, 2022 20:43
@amirroth
Copy link
Collaborator

Whatever the runtime implications of initializing in the object declaration, it's much more readable and understandable (given that you have the comments right there too) than having all of those in a giant constructor blob. Definitely a good development in terms of code readability and developer/reviewer sanity! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Refactoring Includes code changes that don't change the functionality of the program, just perform refactoring
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants