diff --git a/src/EnergyPlus/OutputReportTabular.cc b/src/EnergyPlus/OutputReportTabular.cc index 26ac646e96f..d58a7080766 100644 --- a/src/EnergyPlus/OutputReportTabular.cc +++ b/src/EnergyPlus/OutputReportTabular.cc @@ -8464,8 +8464,7 @@ namespace OutputReportTabular { columnHead.allocate(7); columnWidth.allocate(7); columnWidth = 14; // array assignment - same for all columns - tableBody.allocate(7, numRows); - + tableBody.allocate(7, numRows); // TODO: this appears to be (column, row)... rowHead = ""; tableBody = ""; @@ -8541,14 +8540,31 @@ namespace OutputReportTabular { if (displayTabularBEPS) { WriteSubtitle("End Uses By Subcategory"); WriteTable(tableBody, rowHead, columnHead, columnWidth); + + Array1D_string rowHeadTemp(rowHead); + // Before outputing to SQL, we forward fill the End use column (rowHead) (cf #7481) + // for better sql queries + FillRowHead(rowHeadTemp); + + for (int i = 1; i <= numRows; ++i) { + rowHeadTemp(i) = rowHeadTemp(i) + ":" + tableBody(1, i); + } + + // Erase the SubCategory (first column), using slicing + Array2D_string tableBodyTemp(tableBody({2, _, _ }, {_, _, _})); + Array1D_string columnHeadTemp(columnHead({2, _, _ })); + if (sqlite) { sqlite->createSQLiteTabularDataRecords( - tableBody, rowHead, columnHead, "AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "End Uses By Subcategory"); + tableBodyTemp, rowHeadTemp, columnHeadTemp, "AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "End Uses By Subcategory"); } if (ResultsFramework::OutputSchema->timeSeriesAndTabularEnabled()) { ResultsFramework::OutputSchema->TabularReportsCollection.addReportTable( - tableBody, rowHead, columnHead, "Annual Building Utility Performance Summary", "Entire Facility", "End Uses By Subcategory"); + tableBodyTemp, rowHeadTemp, columnHeadTemp, "Annual Building Utility Performance Summary", "Entire Facility", "End Uses By Subcategory"); } + rowHeadTemp.deallocate(); + tableBodyTemp.deallocate(); + columnHeadTemp.deallocate(); } // EAp2-4/5. Performance Rating Method Compliance @@ -9869,14 +9885,31 @@ namespace OutputReportTabular { // heading for the entire sub-table WriteSubtitle("End Uses By Subcategory"); WriteTable(tableBody, rowHead, columnHead, columnWidth, false, footnote); + + Array1D_string rowHeadTemp(rowHead); + // Before outputing to SQL, we forward fill the End use column (rowHead) (cf #7481) + // for better sql queries + FillRowHead(rowHeadTemp); + + for (int i = 1; i <= numRows; ++i) { + rowHeadTemp(i) = rowHeadTemp(i) + ":" + tableBody(1, i); + } + + // Erase the SubCategory (first column), using slicing + Array2D_string tableBodyTemp(tableBody({2, _, _ }, {_, _, _})); + Array1D_string columnHeadTemp(columnHead({2, _, _ })); + if (sqlite) { sqlite->createSQLiteTabularDataRecords( - tableBody, rowHead, columnHead, "DemandEndUseComponentsSummary", "Entire Facility", "End Uses By Subcategory"); + tableBodyTemp, rowHeadTemp, columnHeadTemp, "DemandEndUseComponentsSummary", "Entire Facility", "End Uses By Subcategory"); } if (ResultsFramework::OutputSchema->timeSeriesAndTabularEnabled()) { ResultsFramework::OutputSchema->TabularReportsCollection.addReportTable( - tableBody, rowHead, columnHead, "Demand End Use Components Summary", "Entire Facility", "End Uses By Subcategory"); + tableBodyTemp, rowHeadTemp, columnHeadTemp, "Demand End Use Components Summary", "Entire Facility", "End Uses By Subcategory"); } + rowHeadTemp.deallocate(); + tableBodyTemp.deallocate(); + columnHeadTemp.deallocate(); // EAp2-4/5. Performance Rating Method Compliance for (iResource = 1; iResource <= 6; ++iResource) { @@ -15127,6 +15160,21 @@ namespace OutputReportTabular { } } + void FillRowHead(Array1D_string & rowHead) + { + // Forward fill the blanks in rowHead (eg End use column) + std::string currentEndUseName; + for (size_t i = 1; i <= rowHead.size(); ++i) { + std::string thisEndUseName = rowHead(i); + if (thisEndUseName.empty()) { + rowHead(i) = currentEndUseName; + } else { + currentEndUseName = thisEndUseName; + } + } + } + + //====================================================================================================================== //====================================================================================================================== diff --git a/src/EnergyPlus/OutputReportTabular.hh b/src/EnergyPlus/OutputReportTabular.hh index 1a6a21efa56..3c1ee2acede 100644 --- a/src/EnergyPlus/OutputReportTabular.hh +++ b/src/EnergyPlus/OutputReportTabular.hh @@ -890,6 +890,10 @@ namespace OutputReportTabular { void DetermineBuildingFloorArea(); + /* Tables with Subcategories in particular have a blank for rowHead for display in the HTML output. + * This routine will fill up the blanks for output to Sql in particular */ + void FillRowHead(Array1D_string & rowHead); + //====================================================================================================================== //====================================================================================================================== diff --git a/src/Transition/OutputRulesFiles/OutputChanges9-2-0-to-9-3-0.md b/src/Transition/OutputRulesFiles/OutputChanges9-2-0-to-9-3-0.md index 0b1760693f8..35f8be61b1a 100644 --- a/src/Transition/OutputRulesFiles/OutputChanges9-2-0-to-9-3-0.md +++ b/src/Transition/OutputRulesFiles/OutputChanges9-2-0-to-9-3-0.md @@ -77,3 +77,56 @@ Environment:Design Day Data,33.00,6.60,DefaultMultipliers,Enthalpy,90500.00,{J/k ``` See [#PR7577](https://github.com/NREL/EnergyPlus/pull/7577) + +### End Use By Subcategory in SQL + +In the SQL Output file, for `ReportName = "AnnualBuildingUtilityPerformanceSummary"` and `ReportName = "DemandEndUseComponentsSummary"`, +the tables `TableName = "End Uses by Subcategory"` have been refactored. `RowName` is now in the format `:`. +This will allow querying a specific End Use Subcategory in the SQL file more easily. + +Example SQL Queries: + +* Get the Value corresponding to a specific Fuel Type "Electricity", End Use "Interior Lighting", Subcategory "GeneralLights": + +```sql +SELECT Value FROM TabularDataWithStrings + WHERE TableName = 'End Uses By Subcategory' + AND ReportName = 'AnnualBuildingUtilityPerformanceSummary' + AND ColumnName = 'Electricity' + AND RowName = 'Interior Lighting:GeneralLights' +``` + +* Return all rows (one row per fuel type) for End Use "Interior Lighting", Subcategory "GeneralLights": + +```sql +SELECT ColumnName as FuelType, Value FROM TabularDataWithStrings + WHERE TableName = 'End Uses By Subcategory' + AND ReportName = 'AnnualBuildingUtilityPerformanceSummary' + AND RowName = 'Interior Lighting:GeneralLights' +``` + +| FuelType | Value | +|------------------|-------| +| Electricity | 83.33 | +| Natural Gas | 0.00 | +| Additional Fuel | 0.00 | +| District Cooling | 0.00 | +| District Heating | 0.00 | +| Water | 0.00 | + +* Get all rows related to Electricity usage by Interior Lighting: + +```sql +SELECT RowName as "End Use&Subcategory", Value FROM TabularDataWithStrings + WHERE TableName = 'End Uses By Subcategory' + AND ReportName = 'AnnualBuildingUtilityPerformanceSummary' + AND ColumnName = 'Electricity' + AND RowName LIKE 'Interior Lighting:%' +``` + +| End Use&Subcategory | Value | +|---------------------------------------|--------| +| Interior Lighting:GeneralLights | 166.67 | +| Interior Lighting:AnotherEndUseSubCat | 83.33 | + +See [PR#7584](https://github.com/NREL/EnergyPlus/pull/7584). diff --git a/tst/EnergyPlus/unit/OutputReportTabular.unit.cc b/tst/EnergyPlus/unit/OutputReportTabular.unit.cc index 918a741fb6a..ac64abf4d39 100644 --- a/tst/EnergyPlus/unit/OutputReportTabular.unit.cc +++ b/tst/EnergyPlus/unit/OutputReportTabular.unit.cc @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -7653,3 +7654,168 @@ TEST_F(SQLiteFixture, WriteSourceEnergyEndUseSummary_TestPerArea) { EnergyPlus::sqlite->sqliteCommit(); } + +TEST_F(SQLiteFixture, OutputReportTabular_EndUseBySubcategorySQL) +{ + EnergyPlus::sqlite->sqliteBegin(); + EnergyPlus::sqlite->createSQLiteSimulationsRecord(1, "EnergyPlus Version", "Current Time"); + + OutputReportTabular::displayTabularBEPS = true; + OutputReportTabular::displayDemandEndUse = true; + OutputReportTabular::displayLEEDSummary = true; + + OutputReportTabular::WriteTabularFiles = true; + + SetupUnitConversions(); + OutputReportTabular::unitsStyle = OutputReportTabular::unitsStyleJtoKWH; + + // Needed to avoid crash (from ElectricPowerServiceManager.hh) + createFacilityElectricPowerServiceObject(); + + SetPredefinedTables(); + + Real64 extLitUse = 1e8; + + SetupOutputVariable("Exterior Lights Electric Energy", + OutputProcessor::Unit::J, + extLitUse, + "Zone", + "Sum", + "Lite1", + _, + "Electricity", + "Exterior Lights", + "General"); + SetupOutputVariable("Exterior Lights Electric Energy", + OutputProcessor::Unit::J, + extLitUse, + "Zone", + "Sum", + "Lite2", + _, + "Electricity", + "Exterior Lights", + "AnotherEndUseSubCat"); + SetupOutputVariable("Exterior Lights Electric Energy", + OutputProcessor::Unit::J, + extLitUse, + "Zone", + "Sum", + "Lite3", + _, + "Electricity", + "Exterior Lights", + "General"); + + DataGlobals::DoWeathSim = true; + DataGlobals::TimeStepZone = 1.0; + displayTabularBEPS = true; + // OutputProcessor::TimeValue.allocate(2); + + auto timeStep = 1.0; + + SetupTimePointers("Zone", timeStep); + SetupTimePointers("HVAC", timeStep); + + TimeValue.at(OutputProcessor::TimeStepType::TimeStepZone).TimeStep = 60; + TimeValue.at(OutputProcessor::TimeStepType::TimeStepSystem).TimeStep = 60; + + GetInputOutputTableSummaryReports(); + + DataEnvironment::Month = 12; + + UpdateMeterReporting(); + UpdateDataandReport(OutputProcessor::TimeStepType::TimeStepZone); + GatherBEPSResultsForTimestep(OutputProcessor::TimeStepType::TimeStepZone); + GatherPeakDemandForTimestep(OutputProcessor::TimeStepType::TimeStepZone); + EXPECT_NEAR(extLitUse * 3, gatherEndUseBEPS(1, DataGlobalConstants::endUseExteriorLights), 1.); + // General + EXPECT_NEAR(extLitUse * 2, gatherEndUseSubBEPS(1, DataGlobalConstants::endUseExteriorLights, 1), 1.); + // AnotherEndUseSubCat + EXPECT_NEAR(extLitUse * 1, gatherEndUseSubBEPS(2, DataGlobalConstants::endUseExteriorLights, 1), 1.); + + UpdateMeterReporting(); + UpdateDataandReport(OutputProcessor::TimeStepType::TimeStepZone); + GatherBEPSResultsForTimestep(OutputProcessor::TimeStepType::TimeStepZone); + GatherPeakDemandForTimestep(OutputProcessor::TimeStepType::TimeStepZone); + EXPECT_NEAR(extLitUse * 6, gatherEndUseBEPS(1, DataGlobalConstants::endUseExteriorLights), 1.); + // General + EXPECT_NEAR(extLitUse * 4, gatherEndUseSubBEPS(1, DataGlobalConstants::endUseExteriorLights, 1), 1.); + // AnotherEndUseSubCat + EXPECT_NEAR(extLitUse * 2, gatherEndUseSubBEPS(2, DataGlobalConstants::endUseExteriorLights, 1), 1.); + + UpdateMeterReporting(); + UpdateDataandReport(OutputProcessor::TimeStepType::TimeStepZone); + GatherBEPSResultsForTimestep(OutputProcessor::TimeStepType::TimeStepZone); + GatherPeakDemandForTimestep(OutputProcessor::TimeStepType::TimeStepZone); + EXPECT_NEAR(extLitUse * 9, gatherEndUseBEPS(1, DataGlobalConstants::endUseExteriorLights), 1.); + // General + EXPECT_NEAR(extLitUse * 6, gatherEndUseSubBEPS(1, DataGlobalConstants::endUseExteriorLights, 1), 1.); + // AnotherEndUseSubCat + EXPECT_NEAR(extLitUse * 3, gatherEndUseSubBEPS(2, DataGlobalConstants::endUseExteriorLights, 1), 1.); + + OutputReportTabular::WriteBEPSTable(); + OutputReportTabular::WriteDemandEndUseSummary(); + + EnergyPlus::sqlite->sqliteCommit(); + + // We test for Heating and Total, since they should be the same + std::vector testReportNames = {"AnnualBuildingUtilityPerformanceSummary", "DemandEndUseComponentsSummary"}; + std::vector endUseSubCategoryNames = {"General", "AnotherEndUseSubCat"}; + + std::string endUseName = "Exterior Lighting"; + std::string endUseSubCategoryName = "AnotherEndUseSubCat"; + std::string rowName = endUseName + ":" + endUseSubCategoryName; + std::string columnName = "Electricity"; + + for (auto& endUseSubCategoryName: endUseSubCategoryNames) { + for (auto& reportName: testReportNames) { + + std::string query("SELECT Value From TabularDataWithStrings" + " WHERE TableName = 'End Uses By Subcategory'" + " AND ColumnName = 'Electricity'" + " AND ReportName = '" + reportName + "'" + " AND RowName = '" + endUseName + ":" + endUseSubCategoryName + "'"); // Now Like 'Exterior Lighting:General' + + auto result = queryResult(query, "TabularDataWithStrings"); + + ASSERT_EQ(1ul, result.size()) << "Query crashed for reportName=" << reportName; + } + } + + + // Specifically get the electricity usage for End Use = Exterior Lighting, and End Use Subcat = AnotherEndUseSubCat, + // and make sure it's the right number that's returned + std::string query("SELECT Value From TabularDataWithStrings" + " WHERE TableName = 'End Uses By Subcategory'" + " AND ReportName = 'AnnualBuildingUtilityPerformanceSummary'" + " AND ColumnName = 'Electricity'" + " AND RowName = 'Exterior Lighting:AnotherEndUseSubCat'"); + Real64 return_val = execAndReturnFirstDouble(query); + + EXPECT_NEAR(extLitUse * 3 / 3.6e6, return_val, 0.01) << "Failed for query: " << query; + + + // Get all Interior Lighting End Uses (all subcats) for Electricity + { + std::string query("SELECT Value From TabularDataWithStrings" + " WHERE TableName = 'End Uses By Subcategory'" + " AND ReportName = 'AnnualBuildingUtilityPerformanceSummary'" + " AND ColumnName = 'Electricity'" + " AND RowName LIKE 'Exterior Lighting:%'"); + auto result = queryResult(query, "TabularDataWithStrings"); + + ASSERT_EQ(2u, result.size()) << "Failed for query: " << query; + } + + // Get all subcat usage for all fuels (6) + { + std::string query("SELECT Value From TabularDataWithStrings" + " WHERE TableName = 'End Uses By Subcategory'" + " AND ReportName = 'AnnualBuildingUtilityPerformanceSummary'" + " AND RowName = 'Exterior Lighting:AnotherEndUseSubCat'"); + auto result = queryResult(query, "TabularDataWithStrings"); + + ASSERT_EQ(6u, result.size()) << "Failed for query: " << query; + } +}