urbanopt-scenario 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -8
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +2 -10
  4. data/.github/pull_request_template.md +5 -15
  5. data/.rubocop.yml +1 -1
  6. data/CHANGELOG.md +45 -1
  7. data/{.github/CONTRIBUTING.md → CONTRIBUTING.md} +0 -0
  8. data/Gemfile +8 -12
  9. data/Jenkinsfile +2 -2
  10. data/Rakefile +2 -2
  11. data/docs/package-lock.json +4607 -6451
  12. data/docs/package.json +1 -1
  13. data/lib/measures/.rubocop.yml +1 -1
  14. data/lib/measures/default_feature_reports/LICENSE.md +1 -1
  15. data/lib/measures/default_feature_reports/README.md +5 -35
  16. data/lib/measures/default_feature_reports/measure.rb +315 -44
  17. data/lib/measures/default_feature_reports/measure.xml +38 -17
  18. data/lib/urbanopt/scenario.rb +1 -0
  19. data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +209 -17
  20. data/lib/urbanopt/scenario/default_reports/feature_report.rb +57 -3
  21. data/lib/urbanopt/scenario/default_reports/power_distribution.rb +102 -0
  22. data/lib/urbanopt/scenario/default_reports/program.rb +6 -2
  23. data/lib/urbanopt/scenario/default_reports/reporting_period.rb +15 -9
  24. data/lib/urbanopt/scenario/default_reports/scenario_report.rb +24 -7
  25. data/lib/urbanopt/scenario/default_reports/schema/README.md +11 -12
  26. data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +33 -12
  27. data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +52 -25
  28. data/lib/urbanopt/scenario/default_reports/solar_pv.rb +1 -0
  29. data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +62 -21
  30. data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +276 -0
  31. data/lib/urbanopt/scenario/scenario_runner_osw.rb +21 -5
  32. data/lib/urbanopt/scenario/simulation_dir_osw.rb +0 -4
  33. data/lib/urbanopt/scenario/version.rb +1 -1
  34. data/urbanopt-scenario-gem.gemspec +10 -12
  35. metadata +31 -48
  36. data/.travis.yml +0 -23
  37. data/lib/change_log.rb +0 -147
  38. data/lib/measures/default_feature_reports/tests/USA_CO_Golden-NREL.724666_TMY3.epw +0 -8768
  39. data/lib/measures/default_feature_reports/tests/default_feature_reports_test.rb +0 -238
  40. data/lib/measures/default_feature_reports/tests/example_model.osm +0 -4378
@@ -13,7 +13,7 @@
13
13
  "highlight.js": "^9.15.6",
14
14
  "json-schema-ref-parser": "^6.1.0",
15
15
  "json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
16
- "vuepress": "^0.14.10",
16
+ "vuepress": "^1.2.0",
17
17
  "webpack-dev-middleware": "^3.6.0"
18
18
  },
19
19
  "devDependencies": {
@@ -2,4 +2,4 @@ AllCops:
2
2
  Exclude:
3
3
  - 'spec/test_measures/**/*'
4
4
  inherit_from:
5
- - http://s3.amazonaws.com/openstudio-resources/styles/rubocop.yml
5
+ - http://s3.amazonaws.com/openstudio-resources/styles/rubocop_v3.yml
@@ -1,4 +1,4 @@
1
- URBANopt, Copyright (c) 2019, Alliance for Sustainable Energy, LLC, and other
1
+ URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
2
  contributors. All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification,
@@ -2,16 +2,16 @@
2
2
 
3
3
  ###### (Automatically generated documentation)
4
4
 
5
- # DefaultFeatureReports
5
+ #
6
6
 
7
7
  ## Description
8
- Writes default_feature_reports.json file used by URBANopt Scenario Default Post Processor
8
+
9
9
 
10
10
  ## Modeler Description
11
- This measure only allows for one feature_report per simulation. If multiple features are simulated in a single simulation, a new measure must be written to disaggregate simulation results to multiple features.
11
+
12
12
 
13
13
  ## Measure Type
14
- ReportingMeasure
14
+ ModelMeasure
15
15
 
16
16
  ## Taxonomy
17
17
 
@@ -19,38 +19,8 @@ ReportingMeasure
19
19
  ## Arguments
20
20
 
21
21
 
22
- ### Feature unique identifier
23
-
24
- **Name:** feature_id,
25
- **Type:** String,
26
- **Units:** ,
27
- **Required:** false,
28
- **Model Dependent:** false
29
-
30
- ### Feature scenario specific name
31
-
32
- **Name:** feature_name,
33
- **Type:** String,
34
- **Units:** ,
35
- **Required:** false,
36
- **Model Dependent:** false
37
-
38
- ### URBANopt Feature Type
39
-
40
- **Name:** feature_type,
41
- **Type:** String,
42
- **Units:** ,
43
- **Required:** false,
44
- **Model Dependent:** false
45
-
46
- ### Reporting Frequency
47
- The frequency at which to report timeseries output data.
48
- **Name:** reporting_frequency,
49
- **Type:** Choice,
50
- **Units:** ,
51
- **Required:** true,
52
- **Model Dependent:** false
53
22
 
54
23
 
24
+ This measure does not have any user arguments
55
25
 
56
26
 
@@ -44,7 +44,7 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
44
44
 
45
45
  # human readable description
46
46
  def description
47
- return 'Writes default_feature_reports.json file used by URBANopt Scenario Default Post Processor'
47
+ return 'Writes default_feature_reports.json and default_feature_reports.csv files used by URBANopt Scenario Default Post Processor'
48
48
  end
49
49
 
50
50
  # human readable description of modeling approach
@@ -77,7 +77,8 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
77
77
  reporting_frequency_chs << 'Timestep'
78
78
  reporting_frequency_chs << 'Hourly'
79
79
  reporting_frequency_chs << 'Daily'
80
- # reporting_frequency_chs << "BillingPeriod" # match it to utility bill object
80
+ # reporting_frequency_chs << 'Zone Timestep'
81
+ reporting_frequency_chs << 'BillingPeriod' # match it to utility bill object
81
82
  ## Utility report here to report the start and end for each fueltype
82
83
  reporting_frequency_chs << 'Monthly'
83
84
  reporting_frequency_chs << 'Runperiod'
@@ -85,17 +86,13 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
85
86
  reporting_frequency = OpenStudio::Measure::OSArgument.makeChoiceArgument('reporting_frequency', reporting_frequency_chs, true)
86
87
  reporting_frequency.setDisplayName('Reporting Frequency')
87
88
  reporting_frequency.setDescription('The frequency at which to report timeseries output data.')
88
- reporting_frequency.setDefaultValue('Hourly')
89
+ reporting_frequency.setDefaultValue('Timestep')
89
90
  args << reporting_frequency
90
91
 
91
- # move this in the run method
92
- if reporting_frequency.defaultValueDisplayName == 'BillingPeriod'
93
- @@logger.error('BillingPeriod frequency is not implemented yet')
94
- end
95
-
96
92
  return args
97
93
  end
98
94
 
95
+ # define fuel types
99
96
  def fuel_types
100
97
  fuel_types = [
101
98
  'Electricity',
@@ -109,6 +106,7 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
109
106
  return fuel_types
110
107
  end
111
108
 
109
+ # define enduses
112
110
  def end_uses
113
111
  end_uses = [
114
112
  'Heating',
@@ -131,6 +129,24 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
131
129
  return end_uses
132
130
  end
133
131
 
132
+ # format datetime
133
+ def format_datetime(date_time)
134
+ date_time.tr!('-', '/')
135
+ date_time.gsub!('Jan', '01')
136
+ date_time.gsub!('Feb', '02')
137
+ date_time.gsub!('Mar', '03')
138
+ date_time.gsub!('Apr', '04')
139
+ date_time.gsub!('May', '05')
140
+ date_time.gsub!('Jun', '06')
141
+ date_time.gsub!('Jul', '07')
142
+ date_time.gsub!('Aug', '08')
143
+ date_time.gsub!('Sep', '09')
144
+ date_time.gsub!('Oct', '10')
145
+ date_time.gsub!('Nov', '11')
146
+ date_time.gsub!('Dec', '12')
147
+ return date_time
148
+ end
149
+
134
150
  # return a vector of IdfObject's to request EnergyPlus objects needed by the run method
135
151
  # rubocop:disable Naming/MethodName
136
152
  def energyPlusOutputRequests(runner, user_arguments)
@@ -152,17 +168,30 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
152
168
  end
153
169
  end
154
170
 
155
- ### Request the output for each end use/fuel type combination
171
+ # Request the output for each end use/fuel type combination
156
172
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Electricity:Facility,#{reporting_frequency};").get
157
173
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ElectricityProduced:Facility,#{reporting_frequency};").get
158
174
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Gas:Facility,#{reporting_frequency};").get
159
175
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictCooling:Facility,#{reporting_frequency};").get
160
176
  result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictHeating:Facility,#{reporting_frequency};").get
177
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Cooling:Electricity,#{reporting_frequency};").get
178
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Electricity,#{reporting_frequency};").get
179
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorLights:Electricity,#{reporting_frequency};").get
180
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ExteriorLights:Electricity,#{reporting_frequency};").get
181
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Electricity,#{reporting_frequency};").get
182
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Fans:Electricity,#{reporting_frequency};").get
183
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Pumps:Electricity,#{reporting_frequency};").get
184
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Electricity,#{reporting_frequency};").get
185
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Gas,#{reporting_frequency};").get
186
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Gas,#{reporting_frequency};").get
187
+ # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Gas,#{reporting_frequency};").get
188
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Heating Coil Heating Rate,hourly; !- HVAC Average [W];').get
161
189
 
162
190
  timeseries_data = ['District Cooling Chilled Water Rate', 'District Cooling Mass Flow Rate',
163
191
  'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
164
192
  'District Heating Hot Water Rate', 'District Heating Mass Flow Rate',
165
- 'District Heating Inlet Temperature', 'District Heating Outlet Temperature']
193
+ 'District Heating Inlet Temperature', 'District Heating Outlet Temperature', 'Cooling Coil Total Cooling Rate',
194
+ 'Heating Coil Heating Rate']
166
195
 
167
196
  timeseries_data.each do |ts|
168
197
  result << OpenStudio::IdfObject.load("Output:Variable,*,#{ts},#{reporting_frequency};").get
@@ -229,6 +258,11 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
229
258
  # Assign the user inputs to variables
230
259
  reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
231
260
 
261
+ # BilingPeriod reporting frequency not implemented yet
262
+ if reporting_frequency == 'BillingPeriod'
263
+ @@logger.error('BillingPeriod frequency is not implemented yet')
264
+ end
265
+
232
266
  # cache runner for this instance of the measure
233
267
  @runner = runner
234
268
 
@@ -271,7 +305,10 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
271
305
  feature_report.name = feature_name
272
306
  feature_report.feature_type = feature_type
273
307
  feature_report.directory_name = workflow.absoluteRunDir
274
- feature_report.timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
308
+
309
+ timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
310
+ feature_report.timesteps_per_hour = timesteps_per_hour
311
+
275
312
  feature_report.simulation_status = 'Complete'
276
313
 
277
314
  feature_report.reporting_periods << URBANopt::Scenario::DefaultReports::ReportingPeriod.new
@@ -434,6 +471,10 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
434
471
  aspect_ratio ||= nil
435
472
  feature_report.program.aspect_ratio = aspect_ratio
436
473
 
474
+ # total_construction_cost
475
+ total_construction_cost = sql_query(runner, sql_file, 'Life-Cycle Cost Report', "TableName='Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)' AND RowName='LCC_MAT - BUILDING - LIFE CYCLE COSTS' AND ColumnName='Cost'")
476
+ feature_report.program.total_construction_cost = total_construction_cost
477
+
437
478
  ############################################################################
438
479
  ##
439
480
  # Get Reporting Periods information and store in the feature_report
@@ -544,6 +585,19 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
544
585
  photovoltaic_power = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Photovoltaic Power' AND ColumnName='Electricity'")
545
586
  feature_report.reporting_periods[0].energy_production[:electricity_produced][:photovoltaic] = convert_units(photovoltaic_power, 'GJ', 'kBtu')
546
587
 
588
+ ## Total utility cost
589
+ total_utility_cost = sql_query(runner, sql_file, 'Economics Results Summary Report', "TableName='Annual Cost' AND RowName='Cost' AND ColumnName='Total'")
590
+ feature_report.reporting_periods[0].total_utility_cost = total_utility_cost
591
+
592
+ ## Utility Costs
593
+ # electricity utility cost
594
+ elec_utility_cost = sql_query(runner, sql_file, 'Economics Results Summary Report', "TableName='Annual Cost' AND RowName='Cost' AND ColumnName='Electric'")
595
+ feature_report.reporting_periods[0].utility_costs[0][:fuel_type] = 'Electricity'
596
+ feature_report.reporting_periods[0].utility_costs[0][:total_cost] = elec_utility_cost
597
+ # gas utility cost
598
+ gas_utility_cost = sql_query(runner, sql_file, 'Economics Results Summary Report', "TableName='Annual Cost' AND RowName='Cost' AND ColumnName='Gas'")
599
+ feature_report.reporting_periods[0].utility_costs << { fuel_type: 'Natural Gas', total_cost: gas_utility_cost }
600
+
547
601
  ## comfort_result
548
602
  # time_setpoint_not_met_during_occupied_cooling
549
603
  time_setpoint_not_met_during_occupied_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Cooling' AND ColumnName='Facility'")
@@ -580,6 +634,19 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
580
634
  'Electricity:Facility',
581
635
  'ElectricityProduced:Facility',
582
636
  'Gas:Facility',
637
+ 'Cooling:Electricity',
638
+ 'Heating:Electricity',
639
+ 'InteriorLights:Electricity',
640
+ 'ExteriorLights:Electricity',
641
+ 'InteriorEquipment:Electricity',
642
+ 'Fans:Electricity',
643
+ 'Pumps:Electricity',
644
+ 'WaterSystems:Electricity',
645
+ 'HeatRejection:Electricity',
646
+ 'HeatRejection:Gas',
647
+ 'Heating:Gas',
648
+ 'WaterSystems:Gas',
649
+ 'InteriorEquipment:Gas',
583
650
  'DistrictCooling:Facility',
584
651
  'DistrictHeating:Facility',
585
652
  'District Cooling Chilled Water Rate',
@@ -589,26 +656,58 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
589
656
  'District Heating Hot Water Rate',
590
657
  'District Heating Mass Flow Rate',
591
658
  'District Heating Inlet Temperature',
592
- 'District Heating Outlet Temperature'
659
+ 'District Heating Outlet Temperature',
660
+ 'Cooling Coil Total Cooling Rate',
661
+ 'Heating Coil Heating Rate'
593
662
  ]
594
663
 
664
+ # add thermal comfort timeseries
665
+ comfortTimeseries = ['Zone Thermal Comfort Fanger Model PMV', 'Zone Thermal Comfort Fanger Model PPD']
666
+ requested_timeseries_names += comfortTimeseries
667
+
668
+ # add additional power timeseries (for calculating transformer apparent power to compare to rating ) in VA
669
+ powerTimeseries = ['Net Electric Energy', 'Electricity:Facility Power', 'ElectricityProduced:Facility Power', 'Electricity:Facility Apparent Power', 'ElectricityProduced:Facility Apparent Power', 'Net Power', 'Net Apparent Power']
670
+ requested_timeseries_names += powerTimeseries
671
+
672
+ # register info all timeseries
673
+ runner.registerInfo("All timeseries: #{requested_timeseries_names}")
674
+
675
+ # timeseries variables to keep to calculate power
676
+ tsToKeep = ['Electricity:Facility', 'ElectricityProduced:Facility']
677
+ tsToKeepIndexes = {}
678
+
679
+ ### powerFactor ###
680
+ # use power_factor default: 0.9
681
+ # TODO: Set powerFactor default based on building type
682
+ powerFactor = 0.9
683
+
684
+ ### power_conversion ###
685
+ # divide values by total_seconds to convert J to W (W = J/sec)
686
+ # divide values by total_hours to convert kWh to kW (kW = kWh/hrs)
687
+ total_seconds = (60 / timesteps_per_hour.to_f) * 60 # make sure timesteps_per_hour is a float in the division
688
+ total_hours = 1 / timesteps_per_hour.to_f # make sure timesteps_per_hour is a float in the division
689
+ # set power_conversion
690
+ power_conversion = total_hours # we set the power conversio to total_hours since we want to convert lWh to kW
691
+ puts "Power Converion: to convert kWh to kW values will be divided by #{power_conversion}"
692
+
595
693
  # number of values in each timeseries
596
694
  n = nil
597
-
598
- # all numeric timeseries values, transpose of CSV file (e.g. values[j] is column, values[j][i] is column and row)
695
+ # all numeric timeseries values, transpose of CSV file (e.g. values[key_cnt] is column, values[key_cnt][i] is column and row)
599
696
  values = []
600
-
601
- # Since schedule value will have a bunch of key_values, we need to keep track of these as additional timeseries
697
+ tmpArray = []
698
+ # since schedule value will have a bunch of key_values, we need to keep track of these as additional timeseries
699
+ key_cnt = 0
602
700
  # this is recording the name of these final timeseries to write in the header of the CSV
603
701
  final_timeseries_names = []
604
702
 
605
703
  # loop over requested timeseries
606
- # rubocop: disable Metrics/BlockLength
607
- requested_timeseries_names.each_with_index do |timeseries_name, j|
704
+ requested_timeseries_names.each_index do |i|
705
+ timeseries_name = requested_timeseries_names[i]
706
+ puts " *********timeseries_name = #{timeseries_name}******************"
608
707
  runner.registerInfo("TIMESERIES: #{timeseries_name}")
609
708
 
610
- # get all the key values that this timeseries can be reported for (e.g. if power is requested for each zone)
611
- key_values = sql_file.availableKeyValues(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name)
709
+ # get all the key values that this timeseries can be reported for (e.g. if PMV is requested for each zone)
710
+ key_values = sql_file.availableKeyValues('RUN PERIOD 1', 'Zone Timestep', timeseries_name)
612
711
  runner.registerInfo("KEY VALUES: #{key_values}")
613
712
  if key_values.empty?
614
713
  key_values = ['']
@@ -616,7 +715,7 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
616
715
 
617
716
  # sort keys
618
717
  sorted_keys = key_values.sort
619
- requested_keys = ['SUMMED ELECTRICITY:FACILITY', 'SUMMED ELECTRICITY:FACILITY POWER', 'SUMMED ELECTRICITYPRODUCED:FACILITY', 'SUMMED ELECTRICITYPRODUCED:FACILITY POWER', 'SUMMED NET APPARENT POWER', 'SUMMED NET ELECTRIC ENERGY', 'SUMMED NET POWER', 'TRANSFORMER OUTPUT ELECTRIC ENERGY SCHEDULE']
718
+ requested_keys = requested_timeseries_names
620
719
  final_keys = []
621
720
  # make sure aggregated timeseries are listed in sorted order before all individual feature timeseries
622
721
  sorted_keys.each do |k|
@@ -649,7 +748,7 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
649
748
  new_timeseries_name = key_value
650
749
  end
651
750
  end
652
- final_timeseries_names << new_timeseries_name
751
+ # final_timeseries_names << new_timeseries_name
653
752
 
654
753
  # get the actual timeseries
655
754
  ts = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name, key_value)
@@ -657,38 +756,210 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
657
756
  if n.nil?
658
757
  # first timeseries should always be set
659
758
  runner.registerInfo('First timeseries')
660
- values[j] = ts.get.values
661
- n = values[j].size
759
+ values[key_cnt] = ts.get.values
760
+ n = values[key_cnt].size
662
761
  elsif ts.is_initialized
663
762
  runner.registerInfo('Is Initialized')
664
- values[j] = ts.get.values
763
+ values[key_cnt] = ts.get.values
665
764
  else
666
765
  runner.registerInfo('Is NOT Initialized')
667
- values[j] = Array.new(n, 0)
766
+ values[key_cnt] = Array.new(n, 0)
767
+ end
768
+
769
+ # unit conversion
770
+ old_unit = ts.get.units if ts.is_initialized
771
+
772
+ if timeseries_name.include? 'Gas'
773
+ new_unit = 'kBtu'
774
+ else
775
+ new_unit = case old_unit.to_s
776
+ when 'J'
777
+ 'kWh'
778
+ when 'kBtu'
779
+ 'kWh'
780
+ when 'gal'
781
+ 'm3'
782
+ when 'W'
783
+ 'W'
784
+ end
668
785
  end
669
786
 
670
- # ##Unit conversion
671
- old_units = ts.get.units if ts.is_initialized
672
- new_units = case old_units.to_s
673
- when 'J'
674
- 'kBtu'
675
- when 'kWh'
676
- 'kBtu'
677
- when 'm3'
678
- 'gal'
679
- end
680
-
681
- # Unit conversion here
682
- os_vec = values[j]
683
-
684
- # loop through each value to retrieve it
685
- for i in 0..os_vec.length - 1
686
- unless new_units == old_units
687
- os_vec[i] = OpenStudio.convert(os_vec[i], old_units, new_units).get
787
+ # loop through each value and apply unit conversion
788
+ os_vec = values[key_cnt]
789
+ if !timeseries_name.include? 'Zone Thermal Comfort'
790
+ for i in 0..os_vec.length - 1
791
+
792
+ unless new_unit == old_unit || old_unit.nil? || new_unit.nil? || !ts.is_initialized
793
+ os_vec[i] = OpenStudio.convert(os_vec[i], old_unit, new_unit).get
794
+ end
795
+
796
+ end
797
+ end
798
+
799
+ # keep certain timeseries to calculate power
800
+ if tsToKeep.include? timeseries_name
801
+ tsToKeepIndexes[timeseries_name] = key_cnt
802
+ end
803
+
804
+ # special processing: power
805
+ if powerTimeseries.include? timeseries_name
806
+ # special case: net series (subtract generation from load)
807
+ if timeseries_name.include? 'Net'
808
+
809
+ newVals = Array.new(n, 0)
810
+ # Apparent power calculation
811
+
812
+ if timeseries_name.include?('Apparent')
813
+ (0..n - 1).each do |j|
814
+ newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion / powerFactor
815
+ j += 1
816
+ end
817
+ new_unit = 'kVA'
818
+ elsif timeseries_name.include? 'Net Electric Energy'
819
+ (0..n - 1).each do |j|
820
+ newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f)
821
+ j += 1
822
+ end
823
+ new_unit = 'kWh'
824
+ else
825
+ runner.registerInfo('Power calc')
826
+ # Power calculation
827
+ (0..n - 1).each do |j|
828
+ newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion
829
+ j += 1
830
+ end
831
+ new_unit = 'kW'
832
+ end
833
+
834
+ values[key_cnt] = newVals
835
+ else
836
+ tsToKeepIndexes.each do |key, indexValue|
837
+ if timeseries_name.include? key
838
+ runner.registerInfo("timeseries_name: #{timeseries_name}, key: #{key}")
839
+ # use this timeseries
840
+ newVals = Array.new(n, 0)
841
+ # Apparent power calculation
842
+ if timeseries_name.include?('Apparent')
843
+ (0..n - 1).each do |j|
844
+ newVals[j] = values[indexValue][j].to_f / power_conversion / powerFactor
845
+ j += 1
846
+ end
847
+ new_unit = 'kVA'
848
+ else
849
+ # Power calculation
850
+ (0..n - 1).each do |j|
851
+ newVals[j] = values[indexValue][j].to_f / power_conversion
852
+ j += 1
853
+ end
854
+ new_unit = 'kW'
855
+ end
856
+ values[key_cnt] = newVals
857
+ end
858
+ end
688
859
  end
689
860
  end
861
+
862
+ # append units to headers
863
+ new_timeseries_name += "(#{new_unit})"
864
+ final_timeseries_names << new_timeseries_name
865
+
866
+ # TODO: DELETE PUTS
867
+ # puts " *********timeseries_name = #{timeseries_name}******************"
868
+ # if timeseries_name.include? 'Power'
869
+ # puts "values = #{values[key_cnt]}"
870
+ # puts "units = #{new_unit}"
871
+ # end
872
+
873
+ # comfort results usually have multiple timeseries (per zone), aggregate into a single series with consistent name and use worst value at each timestep
874
+ if comfortTimeseries.include? timeseries_name
875
+
876
+ # set up array if 1st key_value
877
+ if key_i == 0
878
+ runner.registerInfo("SETTING UP NEW ARRAY FOR: #{timeseries_name}")
879
+ tmpArray = Array.new(n, 0)
880
+ end
881
+
882
+ # add to array (keep max value at each timestep)
883
+ (0..(n - 1)).each do |ind|
884
+ # process negative and positive values differently
885
+ tVal = values[key_cnt][ind].to_f
886
+ if tVal < 0
887
+ tmpArray[ind] = [tVal, tmpArray[ind]].min
888
+ else
889
+ tmpArray[ind] = [tVal, tmpArray[ind]].max
890
+ end
891
+ end
892
+
893
+ # aggregate and save when all keyvalues have been processed
894
+ if key_i == final_keys.size - 1
895
+
896
+ hrsOutOfBounds = 0
897
+ if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
898
+ (0..(n - 1)).each do |ind|
899
+ # -0.5 < x < 0.5 is within bounds
900
+ if values[key_cnt][ind].to_f > 0.5 || values[key_cnt][ind].to_f < -0.5
901
+ hrsOutOfBounds += 1
902
+ end
903
+ end
904
+ hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
905
+ elsif timeseries_name === 'Zone Thermal Comfort Fanger Model PPD'
906
+ (0..(n - 1)).each do |ind|
907
+ # > 20 is outside bounds
908
+ if values[key_cnt][ind].to_f > 20
909
+ hrsOutOfBounds += 1
910
+ end
911
+ end
912
+ hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
913
+ else
914
+ # this one is already scaled by timestep, no need to divide total
915
+ (0..(n - 1)).each do |ind|
916
+ hrsOutOfBounds += values[key_cnt][ind].to_f if values[key_cnt][ind].to_f > 0
917
+ end
918
+ end
919
+
920
+ # save variable to feature_reports hash
921
+ runner.registerInfo("timeseries #{timeseries_name}: hours out of bounds: #{hrsOutOfBounds}")
922
+ if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
923
+ feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PMV] = hrsOutOfBounds
924
+ elsif timeseries_name == 'Zone Thermal Comfort Fanger Model PPD'
925
+ feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PPD] = hrsOutOfBounds
926
+ end
927
+
928
+ end
929
+
930
+ end
931
+
932
+ # increment key_cnt in new_keys loop
933
+ key_cnt += 1
690
934
  end
691
935
  end
936
+
937
+ # Add datime column
938
+ datetimes = []
939
+ # check what timeseries is available
940
+ available_ts = sql_file.availableTimeSeries
941
+ puts "####### available_ts = #{available_ts}"
942
+ # get the timeseries for any of available timeseries
943
+ # RK: code enhancement needed
944
+ ts_d_e = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, 'Electricity:Facility', '')
945
+ ts_d_g = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, 'Gas:Facility', '')
946
+
947
+ if ts_d_e.is_initialized
948
+ timeseries_d = ts_d_e.get
949
+ elsif ts_d_g.is_initialized
950
+ timeseries_d = ts_d_g.get
951
+ else
952
+ raise 'ELECTRICITY and GAS results are not initiaized'
953
+ end
954
+ # get formated datetimes
955
+ timeseries_d.dateTimes.each do |datetime|
956
+ datetimes << format_datetime(datetime.to_s)
957
+ end
958
+ # insert datetimes to values
959
+ values.insert(0, datetimes)
960
+ # insert datetime header to names
961
+ final_timeseries_names.insert(0, 'Datetime')
962
+
692
963
  # rubocop: enable Metrics/BlockLength
693
964
  runner.registerInfo("new final_timeseries_names size: #{final_timeseries_names.size}")
694
965