Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion idd/Energy+.idd.in
Original file line number Diff line number Diff line change
Expand Up @@ -108304,13 +108304,18 @@ OutputControl:Table:Style,
\key InchPound
\key InchPoundExceptElectricity
\default None
A3; \field Format Numeric Values
A3, \field Format Numeric Values
\note If No, all digits are shown after the decimal point without any rounding (23.238769213).
\note If Yes, values are rounded for readability (23.24).
\type choice
\key Yes
\key No
\default Yes
A4; \field Sort Option
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Field name and keys borrowed from Output:VariableDictionary.

\type choice
\key Name
\key Unsorted
\default Unsorted


OutputControl:ReportingTolerances,
Expand Down
69 changes: 66 additions & 3 deletions src/EnergyPlus/OutputReportTabular.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,8 @@ void GetInputTabularStyle(EnergyPlusData &state)
AlphArray(2) = "None";
ort->unitsStyle_Tabular = UnitsStyle::None;
ort->formatReals_Tabular = true;
AlphArray(4) = "UNSORTED";
ort->sortOption = SortOption::Unsorted;
} else if (NumTabularStyle == 1) {
state.dataInputProcessing->inputProcessor->getObjectItem(state,
CurrentModuleObject,
Expand Down Expand Up @@ -1363,8 +1365,8 @@ void GetInputTabularStyle(EnergyPlusData &state)
ort->del(1) = DataStringGlobals::CharComma; // comma
AlphArray(1) = "COMMA";
}
// MonthlyUnitConversion
if (NumAlphas >= 2) {
// UnitConversion
ort->unitsStyle_Tabular = SetUnitsStyleFromString(AlphArray(2));
if (ort->unitsStyle_Tabular == UnitsStyle::NotFound) {
ShowWarningError(state,
Expand All @@ -1373,9 +1375,19 @@ void GetInputTabularStyle(EnergyPlusData &state)
state.dataIPShortCut->cAlphaFieldNames(2),
AlphArray(2)));
}
ort->sortOption = SetSortOptionFromString(AlphArray(4));
if (ort->sortOption == SortOption::NotFound) {
Copy link
Copy Markdown
Collaborator

@rraustad rraustad Apr 17, 2026

Choose a reason for hiding this comment

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

sortOption is a key choice, and has a default. It is not possible for sortOption to be NotFound. If the field is missing or blank then the InputProcessor will fill that field with Unsorted. This check can be deleted, same for the new function SetSortOptionalFromString, just read the input here as done in SetSortOptionalFromString.

A4; \field Sort Option  
   \type choice
   \key Name
   \key Unsorted
   \default Unsorted

ShowWarningError(state,
EnergyPlus::format("{}: Invalid {}=\"{}\". No sorting will be performed.",
CurrentModuleObject,
state.dataIPShortCut->cAlphaFieldNames(4),
AlphArray(4)));
}
} else {
ort->unitsStyle_Tabular = UnitsStyle::None;
AlphArray(2) = "None";
AlphArray(4) = "UNSORTED";
ort->sortOption = SortOption::Unsorted;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It doesn't even matter if (NumAlphas < 2), AlphaArray(4) will have one of 2 key values so no need to even set these. I've checked this so many times that it's muscle memory now.

}
} else if (NumTabularStyle > 1) {
ShowWarningError(state, EnergyPlus::format("{}: Only one instance of this object is allowed. Commas will be used.", CurrentModuleObject));
Expand All @@ -1385,10 +1397,12 @@ void GetInputTabularStyle(EnergyPlusData &state)
ort->unitsStyle_Tabular = UnitsStyle::None;
AlphArray(2) = "None";
ort->formatReals_Tabular = getYesNoValue(AlphArray(3)) == BooleanSwitch::Yes;
AlphArray(4) = "UNSORTED";
ort->sortOption = SortOption::Unsorted;
}

print(state.files.eio, "! <Tabular Report>,Style,Unit Conversion, Format Reals\n");
print(state.files.eio, "Tabular Report,{},{},{}\n", AlphArray(1), AlphArray(2), ort->formatReals_Tabular ? "Yes" : "No");
print(state.files.eio, "! <Tabular Report>,Style,Unit Conversion,Format Reals,Sort Option\n");
print(state.files.eio, "Tabular Report,{},{},{},{}\n", AlphArray(1), AlphArray(2), ort->formatReals_Tabular ? "Yes" : "No", AlphArray(4));
}

UnitsStyle SetUnitsStyleFromString(std::string const &unitStringIn)
Expand All @@ -1401,6 +1415,52 @@ UnitsStyle SetUnitsStyleFromString(std::string const &unitStringIn)
return unitsStyleReturn;
}

SortOption SetSortOptionFromString(std::string const &sortStringIn)
{
SortOption sortOptionReturn = static_cast<SortOption>(getEnumValue(SortOptionNamesUC, Util::makeUPPER(sortStringIn)));
if (sortOptionReturn == SortOption::Invalid) {
sortOptionReturn = SortOption::NotFound;
}

return sortOptionReturn;
}

Array2D_string SortTableByName(EnergyPlusData &state, Array2D_string body, const Array1D_string &cLabels, int rowsBody, int colsBody)
{
// starting from the left most column, find the first one that contains the Name substring
std::string sCol = "Name";
auto it = std::find_if(cLabels.begin(), cLabels.end(), [&sCol](const std::string& s) { return s.find(sCol) != std::string::npos; });
if (it != cLabels.end()) {
sCol = *it;
}

int index = 0;
it = std::find(cLabels.begin(), cLabels.end(), sCol);
if (it != cLabels.end()) {
index = std::distance(cLabels.begin(), it) + 1;
}

// if no column with Name found, just return the original unsorted table
if (index > 0) {
Array1D_int rowHeadSorted;
rowHeadSorted.allocate(rowsBody);
for (int jRow = 1; jRow <= rowsBody; ++jRow) {
rowHeadSorted(jRow) = jRow;
}
std::sort(rowHeadSorted.begin(), rowHeadSorted.end(), [&](int a, int b) { return body(index, a) < body(index, b); });

Array2D_string bodySorted;
bodySorted.allocate(colsBody, rowsBody);
for (int jRow = 1; jRow <= rowsBody; ++jRow) {
for (int iCol = 1; iCol <= colsBody; ++iCol) {
bodySorted(iCol, jRow) = body(iCol, rowHeadSorted(jRow));
}
}
body = bodySorted;
}
return body;
}

void GetInputOutputTableSummaryReports(EnergyPlusData &state)
{
// SUBROUTINE INFORMATION:
Expand Down Expand Up @@ -14731,6 +14791,9 @@ void WriteEioTables(EnergyPlusData &state)
}

if (currentStyle.produceTabular) {
if (ort->sortOption == SortOption::Name) { // optionally sort
tableBody = SortTableByName(state, tableBody, columnHead, numRows, numCols);
}
WriteSubtitle(state, tableName);
std::string footnote;
WriteTable(state, tableBody, rowHead, columnHead, columnWidth, false, footnote);
Expand Down
16 changes: 16 additions & 0 deletions src/EnergyPlus/OutputReportTabular.hh
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ namespace OutputReportTabular {
constexpr std::array<std::string_view, static_cast<int>(UnitsStyle::Num) - 1> UnitsStyleNamesUC{
"NONE", "JTOKWH", "JTOMJ", "JTOGJ", "INCHPOUND", "INCHPOUNDEXCEPTELECTRICITY"};

enum class SortOption
{
Invalid = -1,
Name,
Unsorted,
NotFound,
Num
};
constexpr std::array<std::string_view, static_cast<int>(SortOption::Num) - 1> SortOptionNamesUC{"NAME", "UNSORTED"};

enum class EndUseSubTableType
{
Invalid = -1,
Expand Down Expand Up @@ -612,6 +622,10 @@ namespace OutputReportTabular {

UnitsStyle SetUnitsStyleFromString(std::string const &unitStringIn);

SortOption SetSortOptionFromString(std::string const &sortStringIn);

Array2D_string SortTableByName(EnergyPlusData &state, Array2D_string body, const Array1D_string &cLabels, int rowsBody, int colsBody);

void GetInputOutputTableSummaryReports(EnergyPlusData &state);

bool isCompLoadRepReq(EnergyPlusData &state);
Expand Down Expand Up @@ -1089,6 +1103,7 @@ struct OutputReportTabularData : BaseGlobalStruct
OutputReportTabular::UnitsStyle unitsStyle_Tabular = OutputReportTabular::UnitsStyle::None;
OutputReportTabular::UnitsStyle unitsStyle_SQLite = OutputReportTabular::UnitsStyle::NotFound;
OutputReportTabular::UnitsStyle unitsStyle_JSON = OutputReportTabular::UnitsStyle::NotFound;
OutputReportTabular::SortOption sortOption = OutputReportTabular::SortOption::Unsorted;

bool ip() const
{
Expand Down Expand Up @@ -1415,6 +1430,7 @@ struct OutputReportTabularData : BaseGlobalStruct
{
this->unitsStyle_Tabular = OutputReportTabular::UnitsStyle::None;
this->unitsStyle_SQLite = OutputReportTabular::UnitsStyle::NotFound;
this->sortOption = OutputReportTabular::SortOption::Unsorted;
this->OutputTableBinnedCount = 0;
this->BinResultsTableCount = 0;
this->BinResultsIntervalCount = 0;
Expand Down
5 changes: 4 additions & 1 deletion testfiles/Supermarket.idf
Original file line number Diff line number Diff line change
Expand Up @@ -2357,7 +2357,10 @@
!- =========== ALL OBJECTS IN CLASS: OUTPUTCONTROL:TABLE:STYLE ===========

OutputControl:Table:Style,
HTML; !- Column Separator
HTML, !- Column Separator
, !- Unit Conversion
, !- Format Numeric Values
Name; !- Sort Option
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We could introduce similar field for Output:JSON and Output:SQLite? I'm not totally sure how these three objects are all related.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For Output:JSON it would be good to have something similar. I'm not so sure if SQLite.


!- =========== ALL OBJECTS IN CLASS: OUTPUT:VARIABLE ===========

Expand Down
Loading