-
Notifications
You must be signed in to change notification settings - Fork 379
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
Convert int constexpr
to enum
: EconomicLifeCycleCost.*
#9170
Conversation
for (iInObj = 0; iInObj < elcc->numRecurringCosts; ++iInObj) { | ||
state.dataInputProcessing->inputProcessor->getObjectItem(state, | ||
CurrentModuleObject, | ||
iInObj, | ||
iInObj+1, | ||
AlphaArray, | ||
NumAlphas, | ||
NumArray, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to use +1 for the third argument in the getObjectItem() function so the vector does not go out of bounds.
elcc->CashFlow[CostCategory::TotEnergy].mnAmount(jMonth) = elcc->CashFlow[CostCategory::Energy].mnAmount(jMonth); | ||
elcc->CashFlow[CostCategory::TotOper].mnAmount(jMonth) = | ||
elcc->CashFlow[CostCategory::Maintenance].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Repair].mnAmount(jMonth) + | ||
elcc->CashFlow[CostCategory::Operation].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Replacement].mnAmount(jMonth) + | ||
elcc->CashFlow[CostCategory::MinorOverhaul].mnAmount(jMonth) + elcc->CashFlow[CostCategory::MajorOverhaul].mnAmount(jMonth) + | ||
elcc->CashFlow[CostCategory::OtherOperational].mnAmount(jMonth) + elcc->CashFlow[CostCategory::Water].mnAmount(jMonth) + | ||
elcc->CashFlow[CostCategory::Energy].mnAmount(jMonth); | ||
elcc->CashFlow[CostCategory::TotCaptl].mnAmount(jMonth) = elcc->CashFlow[CostCategory::Construction].mnAmount(jMonth) + | ||
elcc->CashFlow[CostCategory::Salvage].mnAmount(jMonth) + | ||
elcc->CashFlow[CostCategory::OtherCapital].mnAmount(jMonth); | ||
elcc->CashFlow[CostCategory::TotGrand].mnAmount(jMonth) = | ||
elcc->CashFlow[CostCategory::TotOper].mnAmount(jMonth) + elcc->CashFlow[CostCategory::TotCaptl].mnAmount(jMonth); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enums start at 0
so converted elcc->CashFlow
from EPVector
to std::vector
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If CashFlow
is really indexed by an enum
then you can convert it to std::array
. This applies to all arrays indexed by enum
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only part of CashFlow
is indexed by an enum... It is more of a std::vector
/Array1D
like container that can change size based on the number of items in the IDF.
More details:
As shown in this line, numCashFlow is the size of CashFlow, :
elcc->numCashFlow = countOfCostCat + elcc->numRecurringCosts + elcc->numNonrecurringCost + elcc->numResourcesUsed;
.
In that equation, countOfCostCat
is always constant and equal to the number of enum values, and that part of CashFlow
is indexed by the enum. The other terms (numRecurringCosts
, numNonrecurringCost
, numResourcesUsed
) depend on the IDF.
Should I pull out the countOfCostCat
part and convert that to std::array
, and then continue down that path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks for that clarification. As you were.
Is it worthwhile making comments on this module since it is tagged for deprecation? |
I'm pretty sure we discussed deprecating ComponentCost:*, but not LifeCycleCost:* |
Energy, | ||
NonEnergy, | ||
NotComputed, | ||
Num | ||
}; | ||
|
||
constexpr const char *MonthNames(int const &i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This thing can be a constexpr std::array<std::string view, 12>
lookup, it shouldn't be a function. It should also probably be in a central utility
or calendar
module.
@@ -223,13 +223,13 @@ void GetInputLifeCycleCostParameters(EnergyPlusData &state) | |||
// \key BeginningOfYear | |||
// \default EndOfYear | |||
if (UtilityRoutines::SameString(AlphaArray(2), "EndOfYear")) { | |||
elcc->discountConvention = iDiscConv::EndOfYear; | |||
elcc->discountConvention = DiscConv::EndOfYear; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getEnumerationValue
@@ -390,33 +390,33 @@ void GetInputLifeCycleCostParameters(EnergyPlusData &state) | |||
// \key None | |||
// \default None | |||
if (UtilityRoutines::SameString(AlphaArray(6), "ModifiedAcceleratedCostRecoverySystem-3year")) { | |||
elcc->depreciationMethod = iDeprMethod::MACRS3; | |||
elcc->depreciationMethod = DeprMethod::MACRS3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getEnumerationValue
@@ -498,39 +498,39 @@ void GetInputLifeCycleCostRecurringCosts(EnergyPlusData &state) | |||
// \key OtherOperational | |||
// \default Maintenance | |||
if (UtilityRoutines::SameString(AlphaArray(2), "Maintenance")) { | |||
elcc->RecurringCosts(iInObj).category = costCatMaintenance; | |||
elcc->RecurringCosts[iInObj].category = CostCategory::Maintenance; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getEnumerationValue
// A3, \field Start of Costs | ||
// \type choice | ||
// \key ServicePeriod | ||
// \key BasePeriod | ||
// \default ServicePeriod | ||
if (UtilityRoutines::SameString(AlphaArray(3), "ServicePeriod")) { | ||
elcc->RecurringCosts(iInObj).startOfCosts = iStartCosts::ServicePeriod; | ||
elcc->RecurringCosts[iInObj].startOfCosts = iStartCosts::ServicePeriod; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same.
@@ -690,39 +690,39 @@ void GetInputLifeCycleCostNonrecurringCost(EnergyPlusData &state) | |||
// A1, \field Name | |||
// \required-field | |||
// \type alpha | |||
elcc->NonrecurringCost(iInObj).name = AlphaArray(1); | |||
elcc->NonrecurringCost[iInObj].name = AlphaArray(1); | |||
// A2, \field Category | |||
// \type choice | |||
// \key Construction | |||
// \key Salvage | |||
// \key OtherCapital | |||
// \default Construction | |||
if (UtilityRoutines::SameString(AlphaArray(2), "Construction")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same.
ShowWarningError(state, | ||
CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(2) + "=\"" + AlphaArray(2) + | ||
"\". The category of Construction will be used."); | ||
} | ||
// N1, \field Cost | ||
// \type real | ||
elcc->NonrecurringCost(iInObj).cost = NumArray(1); | ||
elcc->NonrecurringCost[iInObj].cost = NumArray(1); | ||
// A3, \field Start of Costs | ||
// \type choice | ||
// \key ServicePeriod | ||
// \key BasePeriod | ||
// \default ServicePeriod | ||
if (UtilityRoutines::SameString(AlphaArray(3), "ServicePeriod")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same.
…ng table diffs from formatting
…ithout Escalation)
constexpr std::string_view Total{"Total"}; | ||
constexpr std::string_view TotalUC{"TOTAL"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better way to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, but I'm not sure if it is worth it or not. C++ has a toupper
function that operates on individual characters, but not on a string. So you'd have to iterate over the characters, and you'd need to construct a string while doing it. Maybe @lefticus has a fancy idea for how to do this most efficiently, but unless this is a widespread issue on this PR, I'd just put a pin in it. Getting it into constexpr string_view
is already such an improvement over the past...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the fastest and cheapest way to do this unless the MakeUpper
function can itself be made constexpr
. I don't think it can, but it may be worth a quick test. Even then the implementation wouldn't be cheaper, it would have the same cost but maybe the code would be a bit clearer. Either way, it's not worth holding up this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the inputs and insights.
No, it's not a widespread issue, just felt that it felt a bit weird pulling these out as individual variables.
Will investigate changing MakeUpper
into a constexpr
function separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://godbolt.org/z/nKcEf5eev I've given it 10 minutes and haven't figured out a path forward on it. I assume that means it is not possible. 😆 🤣 JK, but for real that's a starting point if anyone wants to try to make a clean solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did you learn how to program?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More seriously, makeUpperCase
cannot be made constexpr
because it has to allocate memory for the target string so this path is closed. Just leave as is for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great. I don't see any red flags here, this is just tons of really great cleanup. I will do some final testing locally but as far as I can tell this is good to go in. If anyone else wants any specific tests/demos/changes, speak up soon.
ShowWarningError(state, | ||
CurrentModuleObject + ": Invalid " + state.dataIPShortCut->cAlphaFieldNames(6) + "=\"" + AlphaArray(6) + | ||
R"(". "None" will be used.)"); | ||
elcc->depreciationMethod = static_cast<DeprMethod>(getEnumerationValue(DeprMethodNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(6)))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjwitte maybe you can remind me here...can we not count on the input being all caps at this point? Assuming we stick with a case-insensitive input format, even for epJSON values, we should just make sure the input processor upper cases everything, even the epJSON blob. (Of course not the fields with \retaincase
).
It would be really nice to just know for sure that the AlphaArray was already upper case then we could eliminate so many MakeUPPERCase function calls and clean up so many chunks of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At present, there's no global pass to push the input strings to UPPERcase. getObjectItem
and getAlphaFieldValue
return UPPERcase strings. Direct access of the JSON data does not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that is strictly necessary. There is nothing wrong with makeUpperCase
as long as you only do it once per string. The most important thing is to avoid case-insensitive comparisons and there are other ways to do that. See this issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I was afraid of, but I also see a good possibility there. If the IP just makes one quick pass to upper case all the (non-retain-case) fields in the IDF or epJSON, then we would get rid of a huge amount of these MakeUPPERCase
calls spread out all over the code. And also it would make it perfectly clear to the developers that the data will be upper case as soon as the input processor is done with it. No ambiguity that leads to extraneous string operations during input processing because the dev isn't sure if it will be upper case or not.
I think this would be a great discussion to have, not here, but soon. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @amirroth, I think I am in full agreement that there is nothing wrong with MakeUPPERCase, as long as it is only done once, but the problem is that we aren't quite sure if it's been done or not, so it ends up being done extra times. If the input processor just made it upper case one time then we could be absolutely sure it's upper case and eliminate many MakeUPPERCase calls spread out of the code. Anyway, nothing for this PR. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. If MakeUpperCase
is done in input processor by default that's even better, but what I was saying is that we can eliminate a lot of the expensive case-insensitive comparisons even with no changes to the input processor or JSON module, because the problem is not MakeUpperCase
inherently, it's the fact that SameString essentially does a MakeUpperCase
on both inputs and it is called by many search routines.
elcc->NonrecurringCost(iInObj).category = costCatOtherCapital; | ||
} else { | ||
elcc->NonrecurringCost(iInObj).category = costCatConstruction; | ||
elcc->NonrecurringCost[iInObj].category = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these changes seem fine really. I definitely look forward to the BITF stuff becoming constexpr functions instead of macros.
@@ -303,7 +295,13 @@ void GetInputLifeCycleCostParameters(EnergyPlusData &state) | |||
// \key November | |||
// \key December | |||
// \default January | |||
elcc->baseDateMonth = MonthToMonthNumber(AlphaArray(4), 1); | |||
elcc->baseDateMonth = getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase(AlphaArray(4))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! So much cleaner, and another function gone.
EXPECT_EQ(9, getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase("October"))); | ||
EXPECT_EQ(10, getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase("November"))); | ||
EXPECT_EQ(11, getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase("December"))); | ||
EXPECT_EQ(-1, getEnumerationValue(UtilityRoutines::MonthNamesUC, UtilityRoutines::MakeUPPERCase("Hexember"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably would have just removed this unit test entirely. But it's fine, it's extra checking of the getEnumerationValue function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, will remember next time!
offset = CostCategory::Num; | ||
rowHead(jObj + 1) = elcc->CashFlow[offset + jObj].name; | ||
switch (elcc->CashFlow[offset + jObj].Category) { | ||
case (CostCategory::Maintenance): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to put parentheses around the case labels. No need to hold it up, but in the future don't add them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
int constexpr SizeDepr(41); | ||
|
||
constexpr std::array<std::array<Real64, SizeDepr>, static_cast<int>(DeprMethod::Num)> DepreciationPercentTable{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't the easiest thing to read/maintain, but I think it's a net gain. The huge IF block, non-constexpr, assignments weren't great either. Maybe there is a better way to format the tabular data ... I dunno. No need to address it now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to split stuff up to improve readability? Something like,
constexpr std::array<string_view> smallTable1;
constexpr std::array<string_view> smallTable2;
constexpr std::array<std:array> bigTable {smallTable1, smallTable2};
rowHead(14) = "Total Operation"; | ||
rowHead(15) = "Total Capital"; | ||
rowHead(16) = "Grand Total"; | ||
for (int CashFlowCostCategory = CostCategory::Maintenance; CashFlowCostCategory <= CostCategory::TotGrand; ++CashFlowCostCategory) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, so this is why the table diffs appear. I think that is acceptable and these loops are much nicer than the manually listed out chunks of code.
Merging in 10 minutes unless someone speaks up otherwise. |
Thanks so much! |
Convert `int constexpr` to `enum` : EconomicLifeCycleCost.*
Pull request overview
Convert economic life cycle cost categories from
int constexpr
toenum
.I used plain enums in this PR so that I did not have to use
static_cast
.Explaination for Table diffs:
![image](https://user-images.githubusercontent.com/45446967/142038796-26c3a0b9-72da-4da4-b1fe-3ced62a9ee37.png)
The rows just got rearranged when I rearranged the enums. Rearranged the enums so I could use loops instead of unrolled code for generating the tables. Screenshot from the ABUPS (.HTML) file generated when using the 5ZoneEconomicsTariffAndLifeCycleCosts.idf file:
Pull Request Author
Add to this list or remove from it as applicable. This is a simple templated set of guidelines.
Reviewer
This will not be exhaustively relevant to every PR.