urbanopt-reporting 0.3.0 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -2
  3. data/docs/package-lock.json +3 -3
  4. data/docs/package.json +1 -1
  5. data/lib/measures/default_feature_reports/measure.rb +70 -15
  6. data/lib/measures/default_feature_reports/measure.xml +3 -3
  7. data/lib/measures/export_modelica_loads/LICENSE.md +27 -0
  8. data/lib/measures/export_modelica_loads/README.md +26 -0
  9. data/lib/measures/export_modelica_loads/README.md.erb +42 -0
  10. data/lib/measures/export_modelica_loads/docs/.gitkeep +0 -0
  11. data/lib/measures/export_modelica_loads/measure.rb +375 -0
  12. data/lib/measures/export_modelica_loads/measure.xml +92 -0
  13. data/lib/measures/export_modelica_loads/resources/report.html.in +13 -0
  14. data/lib/measures/export_time_series_modelica/LICENSE.md +1 -0
  15. data/lib/measures/export_time_series_modelica/README.md +59 -0
  16. data/lib/measures/export_time_series_modelica/README.md.erb +42 -0
  17. data/lib/measures/export_time_series_modelica/docs/.gitkeep +0 -0
  18. data/lib/measures/export_time_series_modelica/measure.rb +430 -0
  19. data/lib/measures/export_time_series_modelica/measure.xml +147 -0
  20. data/lib/measures/export_time_series_modelica/resources/os_lib_helper_methods.rb +399 -0
  21. data/lib/measures/export_time_series_modelica/resources/report.html.in +13 -0
  22. data/lib/urbanopt/reporting/default_reports/end_use.rb +39 -17
  23. data/lib/urbanopt/reporting/default_reports/feature_report.rb +62 -5
  24. data/lib/urbanopt/reporting/default_reports/reporting_period.rb +14 -2
  25. data/lib/urbanopt/reporting/default_reports/scenario_report.rb +10 -5
  26. data/lib/urbanopt/reporting/default_reports/schema/scenario_csv_columns.txt +1 -0
  27. data/lib/urbanopt/reporting/default_reports/schema/scenario_schema.json +27 -12
  28. data/lib/urbanopt/reporting/version.rb +1 -1
  29. data/urbanopt-reporting-gem.gemspec +1 -1
  30. metadata +19 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a5d25f304c691b6de042fecc1b03cdbcda6e6cb175609532cf0926116dbce94
4
- data.tar.gz: d805ec996625d83bcc9aff79f9d98edf0cc0618ec94b1c799ed8532f7ce47cf5
3
+ metadata.gz: f3bd0a381b1713f28796ac103c6b193c00a87c0ab75084bb49db62654a6f2906
4
+ data.tar.gz: 8672b851b4a5c906c2954bcb42fad185044f4b89c536e545aba185dffb0afa6b
5
5
  SHA512:
6
- metadata.gz: 5a6cc2110c6ba387e9a9fb23b1dcd9d5bcd56cd342a6448097cce382003caa79aabd2bab83360896c764d888dd41408390c350b5eb25943209c8932f22790c52
7
- data.tar.gz: 0c57334221a85295fde20dae7f7ae4ad23145c6baf82d17398c32efa4fc80acf71d5b2c6eb806780d67eba137a98e653a9e930cf5d4fb27ffd8f8bad40a5435f
6
+ metadata.gz: 57bf6dadcd3d1908de6e406bd0bcdf6fd3cf0141cb6858818dc440a4b3cdf1b22d740f06353112181f8d5b3db53a7bdaeb0d890dc0b4b71c6f3103c251584a74
7
+ data.tar.gz: c3602e94784b2d91499cd7dd439f7ae24131da774241b5a0e685cb60e7a3eb00fe2d9d237431fb4430ce084ba5b678cb2032a79f5734b35c2a4600d3f82f9668
data/CHANGELOG.md CHANGED
@@ -1,9 +1,51 @@
1
1
  # URBANopt Reporting Gem
2
2
 
3
+ ## Version 0.3.5
4
+
5
+ Date Range: 01/16/21 - 02/04/21
6
+
7
+ - Fixed [#58]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/58 ), Feature report saving bug fix.
8
+ - Fixed [#60]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/60 ), Added EV/ ExteriorEquipment results to csv and json reports
9
+ - Fixed [#61]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/61 ), Enhance the aggregation of enduses.
10
+
11
+
12
+ ## Version 0.3.4
13
+
14
+ Date Range: 01/14/21 - 01/15/21
15
+
16
+ - Fixed [#53]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/53 ), Make subfolders in feature saving if necessary
17
+ - Fixed [#55]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/55 ), Fix new measures
18
+
19
+ ## Version 0.3.3
20
+
21
+ Date Range: 12/09/20 - 01/13/21
22
+
23
+ - Fixed [#36]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/36 ), Add reporting measure for district heating/cooling system mass flow rates
24
+ - Fixed [#37]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/37 ), Add EUI to default report
25
+ - Fixed [#38]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/38 ), Add better error handling around convert_units
26
+ - Fixed [#43]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/43 ), Add available_roof_area calculation
27
+ - Fixed [#44]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/44 ), Fix coordinates order
28
+
29
+ ## Version 0.3.2
30
+
31
+ Date Range: 12/07/20 - 12/08/20
32
+
33
+ - Fixed [#27]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/27 ), reporting measure fails when there are no additional fuels in the model
34
+ - Fixed [#29]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/29 ), restore save_feature_report function for backward compatibility
35
+ - Fixed [#32]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/32 ), bump extension-gem dependency
36
+
37
+ ## Version 0.3.1
38
+
39
+ Date Range: 11/26/2020 - 12/07/2020
40
+
41
+ - Fixed [#19]( https://github.com/urbanopt/urbanopt-reporting-gem/pull/19 ), check for nil values to avoid crashing unit conversion
42
+ - Fixed [#24]( https://github.com/urbanopt/urbanopt-reporting-gem/pull/24 ), Support reporting of other fuels
43
+ - Fixed [#28]( https://github.com/urbanopt/urbanopt-reporting-gem/pull/28 ), fix for other_fuels being nil and restore save_feature_report function
44
+
3
45
  ## Version 0.3.0
4
46
 
5
47
  Date Range: 11/12/2020 - 11/25/2020
6
-
48
+
7
49
  - Updating dependencies to support OpenStudio 3.1.0
8
50
 
9
51
  ## Version 0.2.1
@@ -35,4 +77,4 @@ Date Range: 08/27/2020 - 09/21/2020
35
77
 
36
78
  08/17/2020
37
79
 
38
- Initial release of the urbanopt-reporting gem.
80
+ Initial release of the urbanopt-reporting gem.
@@ -4814,9 +4814,9 @@
4814
4814
  "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
4815
4815
  },
4816
4816
  "highlight.js": {
4817
- "version": "10.3.1",
4818
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.1.tgz",
4819
- "integrity": "sha512-jeW8rdPdhshYKObedYg5XGbpVgb1/DT4AHvDFXhkU7UnGSIjy9kkJ7zHG7qplhFHMitTSzh5/iClKQk3Kb2RFQ=="
4817
+ "version": "10.4.1",
4818
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz",
4819
+ "integrity": "sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg=="
4820
4820
  },
4821
4821
  "hmac-drbg": {
4822
4822
  "version": "1.0.1",
data/docs/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "author": "NREL",
12
12
  "dependencies": {
13
- "highlight.js": "^10.3.1",
13
+ "highlight.js": "^10.4.1",
14
14
  "json-schema-ref-parser": "^9.0.6",
15
15
  "json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
16
16
  "vuepress": "^1.7.1",
@@ -165,9 +165,9 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
165
165
 
166
166
  # Request the output for each end use/fuel type combination
167
167
  end_uses.each do |end_use|
168
- end_use, _ = end_use
168
+ end_use, = end_use
169
169
  fuel_types.each do |fuel_type|
170
- fuel_type, _ = fuel_type
170
+ fuel_type, = fuel_type
171
171
  variable_name = if end_use == 'Facility'
172
172
  "#{fuel_type}:#{end_use}"
173
173
  else
@@ -178,17 +178,17 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
178
178
  end
179
179
 
180
180
  # OtherFuels
181
- other_fuels = ["FuelOil#1", "Diesel", "Gasoline", "Coal", "Steam"]
182
- other_fuel_uses = ["HeatRejection", "Heating", "WaterSystems", "InteriorEquipment"]
183
- custom_meter_facility = "Meter:Custom,OtherFuels:Facility,OtherFuel1"
181
+ other_fuels = ['FuelOil#1', 'Diesel', 'Gasoline', 'Coal', 'Steam']
182
+ other_fuel_uses = ['HeatRejection', 'Heating', 'WaterSystems', 'InteriorEquipment']
183
+ custom_meter_facility = 'Meter:Custom,OtherFuels:Facility,OtherFuel1'
184
184
  other_fuel_uses.each do |end_use|
185
185
  custom_meter = "Meter:Custom,#{end_use}:OtherFuels,OtherFuel1"
186
- other_fuels.each do |other_fuel|
186
+ other_fuels.each do |other_fuel|
187
187
  result << OpenStudio::IdfObject.load("Output:Meter,#{end_use}:#{other_fuel},#{reporting_frequency};").get
188
188
  custom_meter_facility += ",,#{end_use}:#{other_fuel}"
189
189
  custom_meter += ",,#{end_use}:#{other_fuel}"
190
190
  end
191
- custom_meter += ";"
191
+ custom_meter += ';'
192
192
  result << OpenStudio::IdfObject.load(custom_meter).get
193
193
  result << OpenStudio::IdfObject.load("Output:Meter,#{end_use}:OtherFuels,#{reporting_frequency};").get
194
194
  end
@@ -201,6 +201,8 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
201
201
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Gas:Facility,#{reporting_frequency};").get
202
202
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictCooling:Facility,#{reporting_frequency};").get
203
203
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictHeating:Facility,#{reporting_frequency};").get
204
+
205
+
204
206
  # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Cooling:Electricity,#{reporting_frequency};").get
205
207
  # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Electricity,#{reporting_frequency};").get
206
208
  # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorLights:Electricity,#{reporting_frequency};").get
@@ -213,14 +215,19 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
213
215
  # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Gas,#{reporting_frequency};").get
214
216
  # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Gas,#{reporting_frequency};").get
215
217
  result << OpenStudio::IdfObject.load('Output:Variable,*,Heating Coil Heating Rate,hourly; !- HVAC Average [W];').get
218
+ #result << OpenStudio::IdfObject.load("Output:Variable,*,Exterior Equipment:Electric Vehicles,#{reporting_frequency};").get
216
219
 
217
220
  timeseries_data = ['District Cooling Chilled Water Rate', 'District Cooling Mass Flow Rate',
218
221
  'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
219
222
  'District Heating Hot Water Rate', 'District Heating Mass Flow Rate',
220
223
  'District Heating Inlet Temperature', 'District Heating Outlet Temperature', 'Cooling Coil Total Cooling Rate',
221
- 'Heating Coil Heating Rate']
224
+ 'Heating Coil Heating Rate', 'ExteriorEquipment:Electricity']
225
+
222
226
 
223
227
  tes_timeseries_data = ['Ice Thermal Storage End Fraction', 'Cooling coil Ice Thermal Storage End Fraction']
228
+
229
+ ev_timeseries_data = ['Exterior Equipment:Electric Vehicles']
230
+
224
231
  timeseries_data += tes_timeseries_data
225
232
 
226
233
  timeseries_data.each do |ts|
@@ -258,6 +265,11 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
258
265
  if value.nil?
259
266
  return nil
260
267
  end
268
+ if from_units.nil? || to_units.nil?
269
+ @runner.registerError("Cannot convert units...from_units: #{from_units} or to_units: #{to_units} left blank.")
270
+ return nil
271
+ end
272
+
261
273
  # apply unit conversion
262
274
  value_converted = OpenStudio.convert(value, from_units, to_units)
263
275
  if value_converted.is_initialized
@@ -353,10 +365,10 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
353
365
  ##
354
366
 
355
367
  if feature_location.include? '['
356
- # get latitude from feature_location
357
- latitude = (feature_location.split(',')[0].delete! '[]').to_f
358
368
  # get longitude from feature_location
359
- longitude = (feature_location.split(',')[1].delete! '[]').to_f
369
+ longitude = (feature_location.split(',')[0].delete! '[]').to_f
370
+ # get latitude from feature_location
371
+ latitude = (feature_location.split(',')[1].delete! '[]').to_f
360
372
  # latitude
361
373
  feature_report.location.latitude_deg = latitude
362
374
  # longitude
@@ -495,7 +507,22 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
495
507
  total_roof_area += surface.netArea
496
508
  end
497
509
  end
498
- feature_report.program.roof_area_sqft[:total_roof_area_sqft] = convert_units(total_roof_area, 'm^2', 'ft^2')
510
+
511
+ total_roof_area_sqft = convert_units(total_roof_area, 'm^2', 'ft^2')
512
+ feature_report.program.roof_area_sqft[:total_roof_area_sqft] = total_roof_area_sqft
513
+
514
+ # available_roof_area_sqft
515
+ # RK: a more robust method should be implemented to find the available_roof_area
516
+ # assign available roof area to be a percentage of the total roof area
517
+
518
+ if building_types[0][:building_type].include? 'Single-Family Detached'
519
+ feature_report.program.roof_area_sqft[:available_roof_area_sqft] = 0.45 * total_roof_area_sqft
520
+ else
521
+ feature_report.program.roof_area_sqft[:available_roof_area_sqft] = 0.75 * total_roof_area_sqft
522
+ end
523
+
524
+ # RK: Temporary solution: assign available roof area to be equal to total roof area
525
+ # feature_report.program.roof_area_sqft[:available_roof_area_sqft] = total_roof_area_sqft
499
526
 
500
527
  # orientation
501
528
  # RK: a more robust method should be implemented to find orientation(finding main axis of the building using aspect ratio)
@@ -581,6 +608,22 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
581
608
  total_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Total Energy'")
582
609
  feature_report.reporting_periods[0].total_source_energy_kwh = convert_units(total_source_energy, 'GJ', 'kWh')
583
610
 
611
+ # EUI is only valid with a full year of energy data
612
+ if begin_month == 1 && begin_day_of_month == 1 && end_month == 12 && end_day_of_month == 31
613
+ # calculate site EUI
614
+ site_EUI_kwh_per_m2 = feature_report.reporting_periods[0].total_site_energy_kwh / floor_area
615
+ site_EUI_kbtu_per_ft2 = convert_units(total_site_energy, 'GJ', 'kBtu') / feature_report.program.floor_area_sqft
616
+ # add site EUI to feature report
617
+ feature_report.reporting_periods[0].site_EUI_kwh_per_m2 = site_EUI_kwh_per_m2
618
+ feature_report.reporting_periods[0].site_EUI_kbtu_per_ft2 = site_EUI_kbtu_per_ft2
619
+ # calculate source EUI
620
+ source_EUI_kwh_per_m2 = feature_report.reporting_periods[0].total_source_energy_kwh / floor_area
621
+ source_EUI_kbtu_per_ft2 = convert_units(total_source_energy, 'GJ', 'kBtu') / feature_report.program.floor_area_sqft
622
+ # add source EUI to feature report
623
+ feature_report.reporting_periods[0].source_EUI_kwh_per_m2 = source_EUI_kwh_per_m2
624
+ feature_report.reporting_periods[0].source_EUI_kbtu_per_ft2 = source_EUI_kbtu_per_ft2
625
+ end
626
+
584
627
  # net_site_energy
585
628
  net_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Site Energy' AND ColumnName='Total Energy'")
586
629
  feature_report.reporting_periods[0].net_site_energy_kwh = convert_units(net_site_energy, 'GJ', 'kWh')
@@ -609,7 +652,9 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
609
652
 
610
653
  # other_fuels
611
654
  additional_fuel = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Additional Fuel'")
612
- feature_report.reporting_periods[0].other_fuels_kwh = convert_units(additional_fuel, 'GJ', 'kWh')
655
+ # ensure additional fuel is not nil
656
+ feature_report.reporting_periods[0].other_fuels_kwh = 0.0
657
+ feature_report.reporting_periods[0].other_fuels_kwh = convert_units(additional_fuel, 'GJ', 'kWh') unless additional_fuel.nil?
613
658
  feature_report.reporting_periods[0].other_fuels_kwh -= feature_report.reporting_periods[0].propane_kwh
614
659
  feature_report.reporting_periods[0].other_fuels_kwh -= feature_report.reporting_periods[0].fuel_oil_kwh
615
660
 
@@ -686,18 +731,27 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
686
731
  m = feature_report.reporting_periods[0].end_uses.send(x_u)
687
732
 
688
733
  y = eu.tr(' ', '_').downcase
734
+ # ensure not nil so the equations below don't error out
735
+ if sql_r.nil?
736
+ sql_r = 0.0
737
+ end
689
738
  sql_r = convert_units(sql_r, 'GJ', 'kWh')
690
739
  if x_u == 'other_fuels_kwh'
691
740
  sql_r -= feature_report.reporting_periods[0].end_uses.propane_kwh.send(y)
692
741
  sql_r -= feature_report.reporting_periods[0].end_uses.fuel_oil_kwh.send(y)
693
742
  end
694
743
  building_types.each do |i|
695
- sql_r = 0.0 if (i[:building_type].include?('Single-Family Detached') && x_u.include?('district'))
744
+ sql_r = 0.0 if i[:building_type].include?('Single-Family Detached') && x_u.include?('district')
696
745
  end
697
746
  m.send("#{y}=", sql_r)
698
747
  end
699
748
  end
700
749
 
750
+ # add enduses subcategories
751
+ electric_vehicles = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses By Subcategory' AND RowName='Exterior Equipment:Electric Vehicles' AND ColumnName='Electricity'")
752
+ puts "electric_vehicle = #{electric_vehicles}"
753
+ feature_report.reporting_periods[0].end_uses.electricity_kwh.electric_vehicles = convert_units(electric_vehicles, 'GJ', 'kWh')
754
+
701
755
  ### energy_production
702
756
  ## electricity_produced
703
757
  # photovoltaic
@@ -761,6 +815,7 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
761
815
  'InteriorLights:Electricity',
762
816
  'ExteriorLights:Electricity',
763
817
  'InteriorEquipment:Electricity',
818
+ 'ExteriorEquipment:Electricity',
764
819
  'Fans:Electricity',
765
820
  'Pumps:Electricity',
766
821
  'WaterSystems:Electricity',
@@ -844,7 +899,7 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
844
899
  puts " *********timeseries_name = #{timeseries_name}******************"
845
900
  runner.registerInfo("TIMESERIES: #{timeseries_name}")
846
901
 
847
- # get all the key values that this timeseries can be reported for (e.g. if PMV is requested for each zone)
902
+ # get all the key values that this timeseries can be reported for (e.g. if PMV is requested for each zone)
848
903
  if timeseries_name.include?('OtherFuels')
849
904
  key_values = sql_file.availableKeyValues('RUN PERIOD 1', 'Zone Timestep', timeseries_name.upcase)
850
905
  else
@@ -3,8 +3,8 @@
3
3
  <schema_version>3.0</schema_version>
4
4
  <name>default_feature_reports</name>
5
5
  <uid>9ee3135a-8070-4408-bfa1-b75fecf9dd4f</uid>
6
- <version_id>f9af7bd8-e9ce-41b4-b892-df29f235a9bd</version_id>
7
- <version_modified>20201201T173130Z</version_modified>
6
+ <version_id>d4f5b2e2-f93d-4ce2-9c68-ed29714fdc0c</version_id>
7
+ <version_modified>20201208T230102Z</version_modified>
8
8
  <xml_checksum>FB304155</xml_checksum>
9
9
  <class_name>DefaultFeatureReports</class_name>
10
10
  <display_name>DefaultFeatureReports</display_name>
@@ -153,7 +153,7 @@
153
153
  <filename>measure.rb</filename>
154
154
  <filetype>rb</filetype>
155
155
  <usage_type>script</usage_type>
156
- <checksum>B1E43E26</checksum>
156
+ <checksum>48AEB753</checksum>
157
157
  </file>
158
158
  </files>
159
159
  </measure>
@@ -0,0 +1,27 @@
1
+ OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted
4
+ provided that the following conditions are met:
5
+
6
+ (1) Redistributions of source code must retain the above copyright notice, this list of conditions
7
+ and the following disclaimer.
8
+
9
+ (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
11
+
12
+ (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse
13
+ or promote products derived from this software without specific prior written permission from the
14
+ respective party.
15
+
16
+ (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other
17
+ derivative works may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar
18
+ designation without specific prior written permission from Alliance for Sustainable Energy, LLC.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
21
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES GOVERNMENT,
23
+ OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,26 @@
1
+
2
+
3
+ ###### (Automatically generated documentation)
4
+
5
+ # Export Modelica Loads
6
+
7
+ ## Description
8
+ Use the results from the EnergyPlus simulation to generate a load file for use in Modelica. This will create a MOS and a CSV file of the heating, cooling, and hot water loads.
9
+
10
+ ## Modeler Description
11
+
12
+
13
+ ## Measure Type
14
+ ReportingMeasure
15
+
16
+ ## Taxonomy
17
+
18
+
19
+ ## Arguments
20
+
21
+
22
+
23
+
24
+ This measure does not have any user arguments
25
+
26
+
@@ -0,0 +1,42 @@
1
+ <%#= README.md.erb is used to auto-generate README.md. %>
2
+ <%#= To manually maintain README.md throw away README.md.erb and manually edit README.md %>
3
+ ###### (Automatically generated documentation)
4
+
5
+ # <%= name %>
6
+
7
+ ## Description
8
+ <%= description %>
9
+
10
+ ## Modeler Description
11
+ <%= modelerDescription %>
12
+
13
+ ## Measure Type
14
+ <%= measureType %>
15
+
16
+ ## Taxonomy
17
+ <%= taxonomy %>
18
+
19
+ ## Arguments
20
+
21
+ <% arguments.each do |argument| %>
22
+ ### <%= argument[:display_name] %>
23
+ <%= argument[:description] %>
24
+ **Name:** <%= argument[:name] %>,
25
+ **Type:** <%= argument[:type] %>,
26
+ **Units:** <%= argument[:units] %>,
27
+ **Required:** <%= argument[:required] %>,
28
+ **Model Dependent:** <%= argument[:model_dependent] %>
29
+ <% end %>
30
+
31
+ <% if arguments.size == 0 %>
32
+ <%= "This measure does not have any user arguments" %>
33
+ <% end %>
34
+
35
+ <% if outputs.size > 0 %>
36
+ ## Outputs
37
+ <% output_names = [] %>
38
+ <% outputs.each do |output| %>
39
+ <% output_names << output[:display_name] %>
40
+ <% end %>
41
+ <%= output_names.join(", ") %>
42
+ <% end %>
File without changes
@@ -0,0 +1,375 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ require 'erb'
37
+
38
+
39
+ # This measure is originally from https://github.com/urbanopt/DES_HVAC
40
+ # start the measure
41
+ class ExportModelicaLoads < OpenStudio::Measure::ReportingMeasure
42
+ # human readable name
43
+ def name
44
+ # Measure name should be the title case of the class name.
45
+ return 'Export Modelica Loads'
46
+ end
47
+
48
+ def description
49
+ return 'Use the results from the EnergyPlus simulation to generate a load file for use in Modelica. This will create a MOS and a CSV file of the heating, cooling, and hot water loads.'
50
+ end
51
+
52
+ def modeler_description
53
+ return ''
54
+ end
55
+
56
+ def log(str)
57
+ puts "#{Time.now}: #{str}"
58
+ end
59
+
60
+ def arguments(_model)
61
+ args = OpenStudio::Measure::OSArgumentVector.new
62
+
63
+ # this measure does not require any user arguments, return an empty list
64
+ return args
65
+ end
66
+
67
+ # return a vector of IdfObject's to request EnergyPlus objects needed by the run method
68
+ def energyPlusOutputRequests(runner, user_arguments)
69
+ super(runner, user_arguments)
70
+
71
+ result = OpenStudio::IdfObjectVector.new
72
+
73
+ # To use the built-in error checking we need the model...
74
+ # get the last model and sql file
75
+ model = runner.lastOpenStudioModel
76
+ if model.empty?
77
+ runner.registerError('Cannot find last model.')
78
+ return false
79
+ end
80
+ model = model.get
81
+
82
+ # use the built-in error checking
83
+ if !runner.validateUserArguments(arguments(model), user_arguments)
84
+ return false
85
+ end
86
+
87
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Mains Water Temperature,timestep;').get
88
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Drybulb Temperature,timestep;').get
89
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Relative Humidity,timestep;').get
90
+ result << OpenStudio::IdfObject.load('Output:Meter,Cooling:Electricity,timestep;').get
91
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:Electricity,timestep;').get
92
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:Gas,timestep;').get
93
+ result << OpenStudio::IdfObject.load('Output:Meter,InteriorLights:Electricity,timestep;').get
94
+ result << OpenStudio::IdfObject.load('Output:Meter,Fans:Electricity,timestep;').get
95
+ result << OpenStudio::IdfObject.load('Output:Meter,InteriorEquipment:Electricity,timestep;').get # Joules
96
+ result << OpenStudio::IdfObject.load('Output:Meter,ExteriorLighting:Electricity,timestep;').get # Joules
97
+ result << OpenStudio::IdfObject.load('Output:Meter,Electricity:Facility,timestep;').get # Joules
98
+ result << OpenStudio::IdfObject.load('Output:Meter,Electricity:Facility,timestep;').get ##Using this for data at timestep interval
99
+ result << OpenStudio::IdfObject.load('Output:Meter,Gas:Facility,timestep;').get # Joules
100
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:EnergyTransfer,timestep;').get # Joules
101
+ result << OpenStudio::IdfObject.load('Output:Meter,WaterSystems:EnergyTransfer,timestep;').get # Joules
102
+ # these variables are used for the modelica export.
103
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Zone Predicted Sensible Load to Setpoint Heat Transfer Rate,timestep;').get # watts according to e+
104
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Water Heater Total Demand Heat Transfer Rate,timestep;').get # Watts
105
+
106
+ return result
107
+ end
108
+
109
+ def extract_timeseries_into_matrix(sqlfile, data, variable_name, key_value = nil, default_if_empty = 0, timestep)
110
+ log "Executing query for #{variable_name}"
111
+ column_name = variable_name
112
+ if key_value
113
+ # ts = sqlfile.timeSeries('RUN PERIOD 1', 'Hourly', variable_name, key_value)
114
+ ts = sqlfile.timeSeries('RUN PERIOD 1', 'Zone Timestep', variable_name, key_value)
115
+ column_name += "_#{key_value}"
116
+ else
117
+ # ts = sqlfile.timeSeries('RUN PERIOD 1', 'Hourly', variable_name)
118
+ ts = sqlfile.timeSeries('RUN PERIOD 1', 'Zone Timestep', variable_name)
119
+ end
120
+ log 'Iterating over timeseries'
121
+ column = [column_name.delete(':').delete(' ')] # Set the header of the data to the variable name, removing : and spaces
122
+
123
+ if ts.empty?
124
+ log "No time series for #{variable_name}:#{key_value}... defaulting to #{default_if_empty}"
125
+ # needs to be data.size-1 since the column name is already stored above (+=)
126
+ column += [default_if_empty] * (data.size - 1)
127
+ else
128
+ ts = ts.get if ts.respond_to?(:get)
129
+ ts = ts.first if ts.respond_to?(:first)
130
+
131
+ start = Time.now
132
+ # Iterating in OpenStudio can take up to 60 seconds with 10min data. The quick_proc takes 0.03 seconds.
133
+ # for i in 0..ts.values.size - 1
134
+ # log "... at #{i}" if i % 10000 == 0
135
+ # column << ts.values[i]
136
+ # end
137
+
138
+ quick_proc = ts.values.to_s.split(',')
139
+
140
+ # the first and last have some cleanup items because of the Vector method
141
+ quick_proc[0] = quick_proc[0].gsub(/^.*\(/, '')
142
+ quick_proc[-1] = quick_proc[-1].delete(')')
143
+ column += quick_proc
144
+
145
+ log "Took #{Time.now - start} to iterate"
146
+ end
147
+
148
+ log 'Appending column to data'
149
+
150
+ # append the data to the end of the rows
151
+ if column.size == data.size
152
+ data.each_index do |index|
153
+ data[index] << column[index]
154
+ end
155
+ end
156
+
157
+ log "Finished extracting #{variable_name}"
158
+ end
159
+
160
+ def create_new_variable_sum(data, new_var_name, include_str, options = nil)
161
+ var_info = {
162
+ name: new_var_name,
163
+ var_indexes: []
164
+ }
165
+ data.each_with_index do |row, index|
166
+ if index.zero?
167
+ # Get the index of the columns to add
168
+ row.each do |c|
169
+ if c.include? include_str
170
+ var_info[:var_indexes] << row.index(c)
171
+ end
172
+ end
173
+
174
+ # add the new var to the header row
175
+ data[0] << var_info[:name]
176
+ else
177
+ # sum the values
178
+ sum = 0
179
+ var_info[:var_indexes].each do |var|
180
+ temp_v = row[var].to_f
181
+ if options.nil?
182
+ sum += temp_v
183
+ elsif options[:positive_only] && temp_v > 0
184
+ sum += temp_v
185
+ elsif options[:negative_only] && temp_v < 0
186
+ sum += temp_v
187
+ end
188
+ end
189
+
190
+ # Also round the data here, because we don't need 10 decimals
191
+ data[index] << sum.round(1)
192
+ end
193
+ end
194
+ end
195
+
196
+ def run(runner, user_arguments)
197
+ super(runner, user_arguments)
198
+
199
+ # get the last model and sql file
200
+ model = runner.lastOpenStudioModel
201
+ if model.empty?
202
+ runner.registerError('Cannot find last model.')
203
+ return false
204
+ end
205
+ model = model.get
206
+
207
+ # use the built-in error checking
208
+ return false unless runner.validateUserArguments(arguments(model), user_arguments)
209
+
210
+ # get the last model and sql file
211
+ model = runner.lastOpenStudioModel
212
+ if model.empty?
213
+ runner.registerError('Cannot find last model.')
214
+ return false
215
+ end
216
+ model = model.get
217
+
218
+ timesteps_per_hour=model.getTimestep.numberOfTimestepsPerHour.to_i
219
+ timestep=60/timesteps_per_hour #timestep in minutes
220
+
221
+ sqlFile = runner.lastEnergyPlusSqlFile
222
+ if sqlFile.empty?
223
+ runner.registerError('Cannot find last sql file.')
224
+ return false
225
+ end
226
+ sqlFile = sqlFile.get
227
+ model.setSqlFile(sqlFile)
228
+
229
+ # create a new csv with the values and save to the reports directory.
230
+ # assumptions:
231
+ # - all the variables exist
232
+ # - data are the same length
233
+
234
+ # initialize the rows with the header
235
+ log 'Starting to process Timeseries data'
236
+ # Initial header row
237
+ rows = [
238
+ ['Date Time', 'Month', 'Day', 'Day of Week', 'Hour', 'Minute', 'SecondsFromStart']
239
+ ]
240
+
241
+ # just grab one of the variables to get the date/time stamps
242
+ attribute_name = 'Electricity:Facility'
243
+ ts = sqlFile.timeSeries('RUN PERIOD 1', 'Zone Timestep', attribute_name)
244
+ if ts.empty?
245
+ runner.registerError("This feature does not have the attribute '#{attribute_name}' to enable this measure to work." \
246
+ "To resolve, simulate a building with electricity or remove this measure from your workflow.")
247
+ else
248
+ ts = ts.first
249
+ dt_base = nil
250
+ # Save off the date time values
251
+ ts.dateTimes.each_with_index do |dt, index|
252
+ dt_base = DateTime.parse(dt.to_s) if dt_base.nil?
253
+ dt_current = DateTime.parse(dt.to_s)
254
+ rows << [
255
+ DateTime.parse(dt.to_s).strftime('%m/%d/%Y %H:%M'),
256
+ dt.date.monthOfYear.value,
257
+ dt.date.dayOfMonth,
258
+ dt.date.dayOfWeek.value,
259
+ dt.time.hours,
260
+ dt.time.minutes,
261
+ dt_current.to_time.to_i - dt_base.to_time.to_i + timestep*60
262
+ ]
263
+ end
264
+ end
265
+
266
+ # add in the other variables by columns -- should really pull this from the report variables defined above
267
+ extract_timeseries_into_matrix(sqlFile, rows, 'Site Outdoor Air Drybulb Temperature', 'Environment', 0, timestep)
268
+ extract_timeseries_into_matrix(sqlFile, rows, 'Site Outdoor Air Relative Humidity', 'Environment', 0, timestep)
269
+ extract_timeseries_into_matrix(sqlFile, rows, 'Heating:Electricity', nil, 0, timestep)
270
+ extract_timeseries_into_matrix(sqlFile, rows, 'Heating:Gas', nil, 0, timestep)
271
+ extract_timeseries_into_matrix(sqlFile, rows, 'Cooling:Electricity', nil, 0, timestep)
272
+ extract_timeseries_into_matrix(sqlFile, rows, 'Electricity:Facility', nil, 0, timestep)
273
+ extract_timeseries_into_matrix(sqlFile, rows, 'Gas:Facility', nil, 0, timestep)
274
+ extract_timeseries_into_matrix(sqlFile, rows, 'Heating:EnergyTransfer', nil, 0, timestep)
275
+ extract_timeseries_into_matrix(sqlFile, rows, 'WaterSystems:EnergyTransfer', nil, 0, timestep)
276
+
277
+ # get all zones and save the names for later use in aggregation.
278
+ tz_names = []
279
+ model.getThermalZones.each do |tz|
280
+ tz_names << tz.name.get if tz.name.is_initialized
281
+ extract_timeseries_into_matrix(sqlFile, rows, 'Zone Predicted Sensible Load to Setpoint Heat Transfer Rate', tz_names.last, 0, timestep)
282
+ extract_timeseries_into_matrix(sqlFile, rows, 'Water Heater Heating Rate', tz_names.last, 0, timestep)
283
+ end
284
+
285
+ # sum up a couple of the columns and create a new columns
286
+ create_new_variable_sum(rows, 'TotalSensibleLoad', 'ZonePredictedSensibleLoadtoSetpointHeatTransferRate')
287
+ create_new_variable_sum(rows, 'TotalCoolingSensibleLoad', 'ZonePredictedSensibleLoadtoSetpointHeatTransferRate', negative_only: true)
288
+ create_new_variable_sum(rows, 'TotalHeatingSensibleLoad', 'ZonePredictedSensibleLoadtoSetpointHeatTransferRate', positive_only: true)
289
+ create_new_variable_sum(rows, 'TotalWaterHeating', 'WaterHeaterHeatingRate')
290
+
291
+ # convert this to CSV object
292
+ File.open('./building_loads.csv', 'w') do |f|
293
+ rows.each do |row|
294
+ f << row.join(',') << "\n"
295
+ end
296
+ end
297
+
298
+ # covert the row data into the format needed by modelica
299
+ modelica_data = [['seconds', 'cooling', 'heating', 'waterheating']]
300
+ seconds_index = nil
301
+ total_water_heating_index = nil
302
+ total_cooling_sensible_index = nil
303
+ total_heating_sensible_index = nil
304
+ peak_cooling = 0
305
+ peak_heating = 0
306
+ peak_water_heating = 0
307
+ rows.each_with_index do |row, index|
308
+ if index.zero?
309
+ seconds_index = row.index('SecondsFromStart')
310
+ total_cooling_sensible_index = row.index('TotalCoolingSensibleLoad')
311
+ total_heating_sensible_index = row.index('TotalHeatingSensibleLoad')
312
+ total_water_heating_index = row.index('TotalWaterHeating')
313
+ else
314
+ new_data = [
315
+ row[seconds_index],
316
+ row[total_cooling_sensible_index],
317
+ row[total_heating_sensible_index],
318
+ row[total_water_heating_index]
319
+ ]
320
+
321
+ modelica_data << new_data
322
+
323
+ # store the peaks
324
+ peak_cooling = row[total_cooling_sensible_index] if row[total_cooling_sensible_index] < peak_cooling
325
+ peak_heating = row[total_heating_sensible_index] if row[total_heating_sensible_index] > peak_heating
326
+ peak_water_heating = row[total_water_heating_index] if row[total_water_heating_index] > peak_water_heating
327
+ end
328
+ end
329
+
330
+ File.open('./modelica.mos', 'w') do |f|
331
+ f << "#1\n"
332
+ f << "#Heating and Cooling Model loads from OpenStudio Prototype Buildings\n"
333
+ f << "# Building Type: {{BUILDINGTYPE}}\n"
334
+ f << "# Climate Zone: {{CLIMATEZONE}}\n"
335
+ f << "# Vintage: {{VINTAGE}}\n"
336
+ f << "# Simulation ID (for debugging): {{SIMID}}\n"
337
+ f << "# URL: https://github.com/urbanopt/openstudio-prototype-loads\n"
338
+ f << "\n"
339
+ f << "#First column: Seconds in the year (loads are hourly)\n"
340
+ f << "#Second column: cooling loads in Watts (as negative numbers).\n"
341
+ f << "#Third column: space heating loads in Watts\n"
342
+ f << "#Fourth column: water heating in Watts\n"
343
+ f << "\n"
344
+ f << "#Peak space cooling load = #{peak_cooling} Watts\n"
345
+ f << "#Peak space heating load = #{peak_heating} Watts\n"
346
+ f << "#Peak water heating load = #{peak_water_heating} Watts\n"
347
+ f << "double tab1(8760,4)\n"
348
+ modelica_data.each_with_index do |row, index|
349
+ next if index.zero?
350
+ f << row.join(';') << "\n"
351
+ end
352
+ end
353
+
354
+ # Find the total runtime for energyplus and save it into a registerValue
355
+ total_time = -999
356
+ location_of_file = ['../eplusout.end', './run/eplusout.end']
357
+ first_index = location_of_file.map { |f| File.exist?(f) }.index(true)
358
+ if first_index
359
+ match = File.read(location_of_file[first_index]).to_s.match(/Elapsed.Time=(.*)hr(.*)min(.*)sec/)
360
+ total_time = match[1].to_i * 3600 + match[2].to_i * 60 + match[3].to_f
361
+ end
362
+
363
+ runner.registerValue('energyplus_runtime', total_time, 'sec')
364
+ runner.registerValue('peak_cooling_load', peak_cooling, 'W')
365
+ runner.registerValue('peak_heating_load', peak_heating, 'W')
366
+ runner.registerValue('peak_water_heating', peak_water_heating, 'W')
367
+
368
+ return true
369
+ ensure
370
+ sqlFile.close if sqlFile
371
+ end
372
+ end
373
+
374
+ # register the measure to be used by the application
375
+ ExportModelicaLoads.new.registerWithApplication