From 5e5a8d4bfe738ff1fa2d0c7438778a682989510a Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 13 Apr 2026 16:29:38 -0700 Subject: [PATCH 1/4] Add a new optional choice field to OutputControl:Table:Style for sorting. --- idd/Energy+.idd.in | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/idd/Energy+.idd.in b/idd/Energy+.idd.in index 8f9912ae00a..8874a49b60a 100644 --- a/idd/Energy+.idd.in +++ b/idd/Energy+.idd.in @@ -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 + \type choice + \key Name + \key Unsorted + \default Unsorted OutputControl:ReportingTolerances, From 27d1c41697563fbdb5d1f6b07bc60550fa472487 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 13 Apr 2026 16:30:21 -0700 Subject: [PATCH 2/4] Update OutputReportTabular with functions for handling optional table sort. --- src/EnergyPlus/OutputReportTabular.cc | 66 ++++++++++++++++++++++++++- src/EnergyPlus/OutputReportTabular.hh | 17 +++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/EnergyPlus/OutputReportTabular.cc b/src/EnergyPlus/OutputReportTabular.cc index 6043358ca46..8dafbd848c2 100644 --- a/src/EnergyPlus/OutputReportTabular.cc +++ b/src/EnergyPlus/OutputReportTabular.cc @@ -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, @@ -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, @@ -1373,9 +1375,19 @@ void GetInputTabularStyle(EnergyPlusData &state) state.dataIPShortCut->cAlphaFieldNames(2), AlphArray(2))); } + ort->sortOption = SetSortOptionFromString(AlphArray(4)); + if (ort->sortOption == SortOption::NotFound) { + 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; } } else if (NumTabularStyle > 1) { ShowWarningError(state, EnergyPlus::format("{}: Only one instance of this object is allowed. Commas will be used.", CurrentModuleObject)); @@ -1385,6 +1397,8 @@ 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, "! ,Style,Unit Conversion, Format Reals\n"); @@ -1401,6 +1415,50 @@ UnitsStyle SetUnitsStyleFromString(std::string const &unitStringIn) return unitsStyleReturn; } +SortOption SetSortOptionFromString(std::string const &sortStringIn) +{ + SortOption sortOptionReturn = static_cast(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 &columnLabels, + int rowsBody, + int colsBody) +{ + int index = 0; + auto it = std::find(std::begin(columnLabels), std::end(columnLabels), "Name"); + if (it != std::end(columnLabels)) { + index = std::distance(std::begin(columnLabels), it) + 1; + } + + 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: @@ -14731,6 +14789,12 @@ void WriteEioTables(EnergyPlusData &state) } if (currentStyle.produceTabular) { + if (tableName == "ScheduleTypeLimits") { + // optionally sort + if (ort->sortOption == SortOption::Name) { + tableBody = SortTableByName(state, tableBody, columnHead, numRows, numCols); + } + } WriteSubtitle(state, tableName); std::string footnote; WriteTable(state, tableBody, rowHead, columnHead, columnWidth, false, footnote); diff --git a/src/EnergyPlus/OutputReportTabular.hh b/src/EnergyPlus/OutputReportTabular.hh index 89520cd8a9c..cabbbbd899a 100644 --- a/src/EnergyPlus/OutputReportTabular.hh +++ b/src/EnergyPlus/OutputReportTabular.hh @@ -128,6 +128,17 @@ namespace OutputReportTabular { constexpr std::array(UnitsStyle::Num) - 1> UnitsStyleNamesUC{ "NONE", "JTOKWH", "JTOMJ", "JTOGJ", "INCHPOUND", "INCHPOUNDEXCEPTELECTRICITY"}; + enum class SortOption + { + Invalid = -1, + Name, + Unsorted, + NotFound, + Num + }; + constexpr std::array(SortOption::Num) - 1> SortOptionNamesUC{ + "NAME", "UNSORTED"}; + enum class EndUseSubTableType { Invalid = -1, @@ -612,6 +623,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 &columnLabels, int rowsBody, int colsBody); + void GetInputOutputTableSummaryReports(EnergyPlusData &state); bool isCompLoadRepReq(EnergyPlusData &state); @@ -1089,6 +1104,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 { @@ -1415,6 +1431,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; From 22e105e2b348157c141724be5dd20427278ea319 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 13 Apr 2026 16:30:49 -0700 Subject: [PATCH 3/4] Update a testfile for trying out new sort option field. --- testfiles/Supermarket.idf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testfiles/Supermarket.idf b/testfiles/Supermarket.idf index 3484aa61a20..7fce40c80f6 100644 --- a/testfiles/Supermarket.idf +++ b/testfiles/Supermarket.idf @@ -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 !- =========== ALL OBJECTS IN CLASS: OUTPUT:VARIABLE =========== From 87b882b7c88aa41628d2d5c7387b4d80be72ef49 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Tue, 14 Apr 2026 10:27:58 -0700 Subject: [PATCH 4/4] Try approach of sorting on leftmost column with Name subtring. --- src/EnergyPlus/OutputReportTabular.cc | 35 +++++++++++++-------------- src/EnergyPlus/OutputReportTabular.hh | 5 ++-- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/EnergyPlus/OutputReportTabular.cc b/src/EnergyPlus/OutputReportTabular.cc index 8dafbd848c2..c1dc1709b65 100644 --- a/src/EnergyPlus/OutputReportTabular.cc +++ b/src/EnergyPlus/OutputReportTabular.cc @@ -1401,8 +1401,8 @@ void GetInputTabularStyle(EnergyPlusData &state) ort->sortOption = SortOption::Unsorted; } - print(state.files.eio, "! ,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, "! ,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) @@ -1425,27 +1425,29 @@ SortOption SetSortOptionFromString(std::string const &sortStringIn) return sortOptionReturn; } -Array2D_string SortTableByName(EnergyPlusData &state, - Array2D_string body, - const Array1D_string &columnLabels, - int rowsBody, - int colsBody) +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; - auto it = std::find(std::begin(columnLabels), std::end(columnLabels), "Name"); - if (it != std::end(columnLabels)) { - index = std::distance(std::begin(columnLabels), it) + 1; + 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); - }); + std::sort(rowHeadSorted.begin(), rowHeadSorted.end(), [&](int a, int b) { return body(index, a) < body(index, b); }); Array2D_string bodySorted; bodySorted.allocate(colsBody, rowsBody); @@ -14789,11 +14791,8 @@ void WriteEioTables(EnergyPlusData &state) } if (currentStyle.produceTabular) { - if (tableName == "ScheduleTypeLimits") { - // optionally sort - if (ort->sortOption == SortOption::Name) { - tableBody = SortTableByName(state, tableBody, columnHead, numRows, numCols); - } + if (ort->sortOption == SortOption::Name) { // optionally sort + tableBody = SortTableByName(state, tableBody, columnHead, numRows, numCols); } WriteSubtitle(state, tableName); std::string footnote; diff --git a/src/EnergyPlus/OutputReportTabular.hh b/src/EnergyPlus/OutputReportTabular.hh index cabbbbd899a..4516df35b45 100644 --- a/src/EnergyPlus/OutputReportTabular.hh +++ b/src/EnergyPlus/OutputReportTabular.hh @@ -136,8 +136,7 @@ namespace OutputReportTabular { NotFound, Num }; - constexpr std::array(SortOption::Num) - 1> SortOptionNamesUC{ - "NAME", "UNSORTED"}; + constexpr std::array(SortOption::Num) - 1> SortOptionNamesUC{"NAME", "UNSORTED"}; enum class EndUseSubTableType { @@ -625,7 +624,7 @@ namespace OutputReportTabular { SortOption SetSortOptionFromString(std::string const &sortStringIn); - Array2D_string SortTableByName(EnergyPlusData &state, Array2D_string body, const Array1D_string &columnLabels, int rowsBody, int colsBody); + Array2D_string SortTableByName(EnergyPlusData &state, Array2D_string body, const Array1D_string &cLabels, int rowsBody, int colsBody); void GetInputOutputTableSummaryReports(EnergyPlusData &state);