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

Modify End Use by Subcategory tables in SQL so they can be queried #7584

Merged
merged 9 commits into from
Jan 8, 2020
60 changes: 54 additions & 6 deletions src/EnergyPlus/OutputReportTabular.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
}
}


//======================================================================================================================
//======================================================================================================================

Expand Down
4 changes: 4 additions & 0 deletions src/EnergyPlus/OutputReportTabular.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);

//======================================================================================================================
//======================================================================================================================

Expand Down
53 changes: 53 additions & 0 deletions src/Transition/OutputRulesFiles/OutputChanges9-2-0-to-9-3-0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<End Use Category>:<End Use Subcategory>`.
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).
166 changes: 166 additions & 0 deletions tst/EnergyPlus/unit/OutputReportTabular.unit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
#include <EnergyPlus/DataSurfaces.hh>
#include <EnergyPlus/DataZoneEnergyDemands.hh>
#include <EnergyPlus/DataZoneEquipment.hh>
#include <EnergyPlus/ElectricPowerServiceManager.hh>
#include <EnergyPlus/General.hh>
#include <EnergyPlus/HeatBalanceManager.hh>
#include <EnergyPlus/HeatBalanceSurfaceManager.hh>
Expand Down Expand Up @@ -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<std::string> testReportNames = {"AnnualBuildingUtilityPerformanceSummary", "DemandEndUseComponentsSummary"};
std::vector<std::string> 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;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check that the "RowName" isn't empty anymore



// 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;
}
}