urbanopt-scenario 0.2.0.pre1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.github/pull_request_template.md +2 -2
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +1 -1
  5. data/CHANGELOG.md +77 -1
  6. data/CONTRIBUTING.md +1 -1
  7. data/Gemfile +28 -18
  8. data/Jenkinsfile +1 -1
  9. data/LICENSE.md +1 -1
  10. data/RDOC_MAIN.md +1 -1
  11. data/README.md +1 -1
  12. data/Rakefile +2 -2
  13. data/docs/README.md +1 -1
  14. data/docs/package-lock.json +2499 -2322
  15. data/docs/package.json +13 -9
  16. data/lib/urbanopt-scenario.rb +1 -1
  17. data/lib/urbanopt/scenario.rb +3 -1
  18. data/lib/urbanopt/scenario/default_reports.rb +3 -8
  19. data/lib/urbanopt/scenario/extension.rb +1 -1
  20. data/lib/urbanopt/scenario/logger.rb +1 -1
  21. data/lib/urbanopt/scenario/scenario_base.rb +1 -1
  22. data/lib/urbanopt/scenario/scenario_csv.rb +22 -9
  23. data/lib/urbanopt/scenario/scenario_datapoint_base.rb +8 -1
  24. data/lib/urbanopt/scenario/scenario_post_processor_base.rb +1 -1
  25. data/lib/urbanopt/scenario/scenario_post_processor_default.rb +81 -8
  26. data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +275 -0
  27. data/lib/urbanopt/scenario/scenario_runner_base.rb +2 -2
  28. data/lib/urbanopt/scenario/scenario_runner_osw.rb +23 -9
  29. data/lib/urbanopt/scenario/scenario_visualization.rb +236 -0
  30. data/lib/urbanopt/scenario/simulation_dir_base.rb +1 -1
  31. data/lib/urbanopt/scenario/simulation_dir_osw.rb +2 -9
  32. data/lib/urbanopt/scenario/simulation_mapper_base.rb +1 -1
  33. data/lib/urbanopt/scenario/version.rb +2 -2
  34. data/package-lock.json +3 -0
  35. data/urbanopt-scenario-gem.gemspec +15 -15
  36. metadata +78 -82
  37. data/doc_templates/LICENSE.md +0 -27
  38. data/doc_templates/README.md.erb +0 -42
  39. data/doc_templates/copyright_erb.txt +0 -31
  40. data/doc_templates/copyright_js.txt +0 -4
  41. data/doc_templates/copyright_ruby.txt +0 -29
  42. data/lib/change_log.rb +0 -147
  43. data/lib/measures/.rubocop.yml +0 -5
  44. data/lib/measures/default_feature_reports/LICENSE.md +0 -27
  45. data/lib/measures/default_feature_reports/README.md +0 -56
  46. data/lib/measures/default_feature_reports/README.md.erb +0 -42
  47. data/lib/measures/default_feature_reports/measure.rb +0 -957
  48. data/lib/measures/default_feature_reports/measure.xml +0 -143
  49. data/lib/measures/default_feature_reports/tests/USA_CO_Golden-NREL.724666_TMY3.epw +0 -8768
  50. data/lib/measures/default_feature_reports/tests/default_feature_reports_test.rb +0 -238
  51. data/lib/measures/default_feature_reports/tests/example_model.osm +0 -4378
  52. data/lib/urbanopt/scenario/default_reports/construction_cost.rb +0 -169
  53. data/lib/urbanopt/scenario/default_reports/date.rb +0 -97
  54. data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +0 -374
  55. data/lib/urbanopt/scenario/default_reports/end_use.rb +0 -159
  56. data/lib/urbanopt/scenario/default_reports/end_uses.rb +0 -140
  57. data/lib/urbanopt/scenario/default_reports/feature_report.rb +0 -260
  58. data/lib/urbanopt/scenario/default_reports/generator.rb +0 -92
  59. data/lib/urbanopt/scenario/default_reports/location.rb +0 -99
  60. data/lib/urbanopt/scenario/default_reports/logger.rb +0 -44
  61. data/lib/urbanopt/scenario/default_reports/program.rb +0 -266
  62. data/lib/urbanopt/scenario/default_reports/reporting_period.rb +0 -301
  63. data/lib/urbanopt/scenario/default_reports/scenario_report.rb +0 -311
  64. data/lib/urbanopt/scenario/default_reports/schema/README.md +0 -33
  65. data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +0 -21
  66. data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +0 -839
  67. data/lib/urbanopt/scenario/default_reports/solar_pv.rb +0 -93
  68. data/lib/urbanopt/scenario/default_reports/storage.rb +0 -105
  69. data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +0 -284
  70. data/lib/urbanopt/scenario/default_reports/validator.rb +0 -97
  71. data/lib/urbanopt/scenario/default_reports/wind.rb +0 -92
@@ -1,301 +0,0 @@
1
- # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
- # contributors. All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without modification,
6
- # are permitted provided that the following conditions are met:
7
- #
8
- # Redistributions of source code must retain the above copyright notice, this list
9
- # of conditions and the following disclaimer.
10
- #
11
- # Redistributions in binary form must reproduce the above copyright notice, this
12
- # list of conditions and the following disclaimer in the documentation and/or other
13
- # materials provided with the distribution.
14
- #
15
- # Neither the name of the copyright holder nor the names of its contributors may be
16
- # used to endorse or promote products derived from this software without specific
17
- # prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
- # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23
- # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
- # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28
- # OF THE POSSIBILITY OF SUCH DAMAGE.
29
- # *********************************************************************************
30
-
31
- require 'json'
32
- require 'urbanopt/scenario/default_reports/end_uses'
33
- require 'urbanopt/scenario/default_reports/end_use'
34
- require 'urbanopt/scenario/default_reports/date'
35
- require 'urbanopt/scenario/default_reports/validator'
36
- require 'json-schema'
37
-
38
- module URBANopt
39
- module Scenario
40
- module DefaultReports
41
- ##
42
- # ReportingPeriod includes all the results of a specific reporting period.
43
- ##
44
- class ReportingPeriod
45
- attr_accessor :id, :name, :multiplier, :start_date, :end_date, :month, :day_of_month, :year, :total_site_energy, :total_source_energy,
46
- :net_site_energy, :net_source_energy, :net_utility_cost, :electricity, :natural_gas, :additional_fuel, :district_cooling,
47
- :district_heating, :water, :electricity_produced, :end_uses, :energy_production, :photovoltaic, :utility_costs,
48
- :fuel_type, :total_cost, :usage_cost, :demand_cost, :comfort_result, :time_setpoint_not_met_during_occupied_cooling,
49
- :time_setpoint_not_met_during_occupied_heating, :time_setpoint_not_met_during_occupied_hours, :hours_out_of_comfort_bounds_PMV, :hours_out_of_comfort_bounds_PPD #:nodoc:
50
- # ReportingPeriod class initializes the reporting period attributes:
51
- # +:id+ , +:name+ , +:multiplier+ , +:start_date+ , +:end_date+ , +:month+ , +:day_of_month+ , +:year+ , +:total_site_energy+ , +:total_source_energy+ ,
52
- # +:net_site_energy+ , +:net_source_energy+ , +:net_utility_cost+ , +:electricity+ , +:natural_gas+ , +:additional_fuel+ , +:district_cooling+ ,
53
- # +:district_heating+ , +:water+ , +:electricity_produced+ , +:end_uses+ , +:energy_production+ , +:photovoltaic+ , +:utility_costs+ ,
54
- # +:fuel_type+ , +:total_cost+ , +:usage_cost+ , +:demand_cost+ , +:comfort_result+ , +:time_setpoint_not_met_during_occupied_cooling+ ,
55
- # +:time_setpoint_not_met_during_occupied_heating+ , +:time_setpoint_not_met_during_occupied_hours+
56
- ##
57
- # [parameters:]
58
- # +hash+ - _Hash_ - A hash which may contain a deserialized reporting_period.
59
- ##
60
- def initialize(hash = {})
61
- hash.delete_if { |k, v| v.nil? }
62
- hash = defaults.merge(hash)
63
-
64
- @id = hash[:id]
65
- @name = hash[:name]
66
- @multiplier = hash[:multiplier]
67
- @start_date = Date.new(hash[:start_date])
68
- @end_date = Date.new(hash[:end_date])
69
-
70
- @total_site_energy = hash[:total_site_energy]
71
- @total_source_energy = hash[:total_source_energy]
72
- @net_site_energy = hash [:net_site_energy]
73
- @net_source_energy = hash [:net_source_energy]
74
- @net_utility_cost = hash [:net_utility_cost]
75
- @electricity = hash [:electricity]
76
- @natural_gas = hash [:natural_gas]
77
- @additional_fuel = hash [:additional_fuel]
78
- @district_cooling = hash [:district_cooling]
79
- @district_heating = hash[:district_heating]
80
- @water = hash[:water]
81
- @electricity_produced = hash[:electricity_produced]
82
- @end_uses = EndUses.new(hash[:end_uses])
83
-
84
- @energy_production = hash[:energy_production]
85
-
86
- @utility_costs = hash[:utility_costs]
87
-
88
- @comfort_result = hash[:comfort_result]
89
-
90
- # initialize class variables @@validator and @@schema
91
- @@validator ||= Validator.new
92
- @@schema ||= @@validator.schema
93
- end
94
-
95
- ##
96
- # Assigns default values if values do not exist.
97
- ##
98
- def defaults
99
- hash = {}
100
-
101
- hash[:id] = nil
102
- hash[:name] = nil
103
- hash[:multiplier] = nil
104
- hash[:start_date] = Date.new.to_hash
105
- hash[:end_date] = Date.new.to_hash
106
-
107
- hash[:total_site_energy] = nil
108
- hash[:total_source_energy] = nil
109
- hash [:net_site_energy] = nil
110
- hash [:net_source_energy] = nil
111
- hash [:net_utility_cost] = nil
112
- hash [:electricity] = nil
113
- hash [:natural_gas] = nil
114
- hash [:additional_fuel] = nil
115
- hash [:district_cooling] = nil
116
- hash[:district_heating] = nil
117
-
118
- hash[:electricity_produced] = nil
119
- hash[:end_uses] = EndUses.new.to_hash
120
- hash[:energy_production] = { electricity_produced: { photovoltaic: nil } }
121
- hash[:utility_costs] = [{ fuel_type: nil, total_cost: nil, usage_cost: nil, demand_cost: nil }]
122
- hash[:comfort_result] = { time_setpoint_not_met_during_occupied_cooling: nil, time_setpoint_not_met_during_occupied_heating: nil,
123
- time_setpoint_not_met_during_occupied_hours: nil, hours_out_of_comfort_bounds_PMV: nil, hours_out_of_comfort_bounds_PPD: nil }
124
-
125
- return hash
126
- end
127
-
128
- ##
129
- # Converts to a Hash equivalent for JSON serialization.
130
- ##
131
- # - Exclude attributes with nil values.
132
- # - Validate reporting_period hash properties against schema.
133
- #
134
- def to_hash
135
- result = {}
136
-
137
- result[:id] = @id if @id
138
- result[:name] = @name if @name
139
- result[:multiplier] = @multiplier if @multiplier
140
- result[:start_date] = @start_date.to_hash if @start_date
141
- result[:end_date] = @end_date.to_hash if @end_date
142
- result[:total_site_energy] = @total_site_energy if @total_site_energy
143
- result[:total_source_energy] = @total_source_energy if @total_source_energy
144
- result[:net_site_energy] = @net_site_energy if @net_site_energy
145
- result[:net_source_energy] = @net_source_energy if @net_source_energy
146
- result[:net_utility_cost] = @net_utility_cost if @net_utility_cost
147
- result[:electricity] = @electricity if @electricity
148
- result[:natural_gas] = @natural_gas if @natural_gas
149
- result[:additional_fuel] = @additional_fuel if @additional_fuel
150
- result[:district_cooling] = @district_cooling if @district_cooling
151
- result[:district_heating] = @district_heating if @district_heating
152
- result[:water] = @water if @water
153
- result[:electricity_produced] = @electricity_produced if @electricity_produced
154
- result[:end_uses] = @end_uses.to_hash if @end_uses
155
-
156
- energy_production_hash = @energy_production if @energy_production
157
- energy_production_hash.delete_if { |k, v| v.nil? }
158
- energy_production_hash.each do |eph|
159
- eph.delete_if { |k, v| v.nil? }
160
- end
161
-
162
- result[:energy_production] = energy_production_hash if @energy_production
163
-
164
- if @utility_costs.any?
165
- result[:utility_costs] = @utility_costs
166
- @utility_costs.each do |uc|
167
- uc.delete_if { |k, v| v.nil? } if uc
168
- end
169
- end
170
-
171
- comfort_result_hash = @comfort_result if @comfort_result
172
- comfort_result_hash.delete_if { |k, v| v.nil? }
173
- result[:comfort_result] = comfort_result_hash if @comfort_result
174
-
175
- # validates +reporting_period+ properties against schema for reporting period.
176
- if @@validator.validate(@@schema[:definitions][:ReportingPeriod][:properties], result).any?
177
- raise "feature_report properties does not match schema: #{@@validator.validate(@@schema[:definitions][:ReportingPeriod][:properties], result)}"
178
- end
179
-
180
- return result
181
- end
182
-
183
- ##
184
- # Adds up +existing_value+ and +new_values+ if not nill.
185
- ##
186
- # [parameter:]
187
- # +existing_value+ - _Float_ - A value corresponding to a ReportingPeriod attribute.
188
- ##
189
- # +new_value+ - _Float_ - A value corresponding to a ReportingPeriod attribute.
190
- ##
191
- def self.add_values(existing_value, new_value)
192
- if existing_value && new_value
193
- existing_value += new_value
194
- elsif new_value
195
- existing_value = new_value
196
- end
197
- return existing_value
198
- end
199
-
200
- ##
201
- # Merges an +existing_period+ with a +new_period+ if not nil.
202
- ##
203
- # [Parameters:]
204
- # +existing_period+ - _ReportingPeriod_ - An object of ReportingPeriod class.
205
- ##
206
- # +new_period+ - _ReportingPeriod_ - An object of ReportingPeriod class.
207
- ##
208
- # rubocop: disable Metrics/AbcSize
209
- def self.merge_reporting_period(existing_period, new_period)
210
- # modify the existing_period by summing up the results
211
- existing_period.total_site_energy = add_values(existing_period.total_site_energy, new_period.total_site_energy)
212
- existing_period.total_source_energy = add_values(existing_period.total_source_energy, new_period.total_source_energy)
213
- existing_period.net_source_energy = add_values(existing_period.net_source_energy, new_period.net_source_energy)
214
- existing_period.net_utility_cost = add_values(existing_period.net_utility_cost, new_period.net_utility_cost)
215
- existing_period.electricity = add_values(existing_period.electricity, new_period.electricity)
216
- existing_period.natural_gas = add_values(existing_period.natural_gas, new_period.natural_gas)
217
- existing_period.additional_fuel = add_values(existing_period.additional_fuel, new_period.additional_fuel)
218
- existing_period.district_cooling = add_values(existing_period.district_cooling, new_period.district_cooling)
219
- existing_period.district_heating = add_values(existing_period.district_heating, new_period.district_heating)
220
- existing_period.water = add_values(existing_period.water, new_period.water)
221
- existing_period.electricity_produced = add_values(existing_period.electricity_produced, new_period.electricity_produced)
222
-
223
- # merge end uses
224
- new_end_uses = new_period.end_uses
225
- existing_period.end_uses.merge_end_uses!(new_end_uses) if existing_period.end_uses
226
-
227
- if existing_period.energy_production
228
- if existing_period.energy_production[:electricity_produced]
229
- existing_period.energy_production[:electricity_produced][:photovoltaic] = add_values(existing_period.energy_production[:electricity_produced][:photovoltaic], new_period.energy_production[:electricity_produced][:photovoltaic])
230
- end
231
- end
232
-
233
- if existing_period.utility_costs
234
-
235
- existing_period.utility_costs.each_with_index do |item, i|
236
- existing_period.utility_costs[i][:fuel_type] = add_values(existing_period.utility_costs[i][:fuel_type], new_period.utility_costs[i][:fuel_type])
237
- existing_period.utility_costs[i][:total_cost] = add_values(existing_period.utility_costs[i][:total_cost], new_period.utility_costs[i][:total_cost])
238
- existing_period.utility_costs[i][:usage_cost] = add_values(existing_period.utility_costs[i][:usage_cost], new_period.utility_costs[i][:usage_cost])
239
- existing_period.utility_costs[i][:demand_cost] = add_values(existing_period.utility_costs[i][:demand_cost], new_period.utility_costs[i][:demand_cost])
240
- end
241
-
242
- end
243
-
244
- if existing_period.comfort_result
245
- existing_period.comfort_result[:time_setpoint_not_met_during_occupied_cooling] = add_values(existing_period.comfort_result[:time_setpoint_not_met_during_occupied_cooling], new_period.comfort_result[:time_setpoint_not_met_during_occupied_cooling])
246
- existing_period.comfort_result[:time_setpoint_not_met_during_occupied_heating] = add_values(existing_period.comfort_result[:time_setpoint_not_met_during_occupied_heating], new_period.comfort_result[:time_setpoint_not_met_during_occupied_heating])
247
- existing_period.comfort_result[:time_setpoint_not_met_during_occupied_hours] = add_values(existing_period.comfort_result[:time_setpoint_not_met_during_occupied_hours], new_period.comfort_result[:time_setpoint_not_met_during_occupied_hours])
248
- existing_period.comfort_result[:hours_out_of_comfort_bounds_PMV] = add_values(existing_period.comfort_result[:hours_out_of_comfort_bounds_PMV], new_period.comfort_result[:hours_out_of_comfort_bounds_PMV])
249
- existing_period.comfort_result[:hours_out_of_comfort_bounds_PPD] = add_values(existing_period.comfort_result[:hours_out_of_comfort_bounds_PPD], new_period.comfort_result[:hours_out_of_comfort_bounds_PPD])
250
- end
251
-
252
- return existing_period
253
- end
254
- # rubocop: enable Metrics/AbcSize # :nodoc:
255
-
256
- ##
257
- # Merges multiple reporting periods together.
258
- # - If +existing_periods+ and +new_periods+ ids are equal,
259
- # modify the existing_periods by merging the new periods results
260
- # - If existing periods are empty, initialize with new_periods.
261
- # - Raise an error if the existing periods are not identical with new periods (cannot have different reporting period ids).
262
- ##
263
- # [parameters:]
264
- ##
265
- # +existing_periods+ - _Array_ - An array of ReportingPeriod objects.
266
- ##
267
- # +new_periods+ - _Array_ - An array of ReportingPeriod objects.
268
- ##
269
- def self.merge_reporting_periods(existing_periods, new_periods)
270
- id_list_existing = []
271
- id_list_new = []
272
- id_list_existing = existing_periods.collect(&:id)
273
- id_list_new = new_periods.collect(&:id)
274
-
275
- if id_list_existing == id_list_new
276
-
277
- existing_periods.each_index do |index|
278
- # if +existing_periods+ and +new_periods+ ids are equal,
279
- # modify the existing_periods by merging the new periods results
280
- existing_periods[index] = merge_reporting_period(existing_periods[index], new_periods[index])
281
- end
282
-
283
- elsif existing_periods.empty?
284
-
285
- # if existing periods are empty, initialize with new_periods
286
- # the = operator would link existing_periods and new_periods to the same object in memory
287
- # we want to initialize with a deep clone of new_periods
288
- existing_periods = Marshal.load(Marshal.dump(new_periods))
289
-
290
- else
291
- # raise an error if the existing periods are not identical with new periods (cannot have different reporting period ids)
292
- raise 'cannot merge different reporting periods'
293
-
294
- end
295
-
296
- return existing_periods
297
- end
298
- end
299
- end
300
- end
301
- end
@@ -1,311 +0,0 @@
1
- # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
- # contributors. All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without modification,
6
- # are permitted provided that the following conditions are met:
7
- #
8
- # Redistributions of source code must retain the above copyright notice, this list
9
- # of conditions and the following disclaimer.
10
- #
11
- # Redistributions in binary form must reproduce the above copyright notice, this
12
- # list of conditions and the following disclaimer in the documentation and/or other
13
- # materials provided with the distribution.
14
- #
15
- # Neither the name of the copyright holder nor the names of its contributors may be
16
- # used to endorse or promote products derived from this software without specific
17
- # prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
- # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23
- # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
- # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28
- # OF THE POSSIBILITY OF SUCH DAMAGE.
29
- # *********************************************************************************
30
-
31
- require 'urbanopt/scenario/default_reports/construction_cost'
32
- require 'urbanopt/scenario/default_reports/feature_report'
33
- require 'urbanopt/scenario/default_reports/logger'
34
- require 'urbanopt/scenario/default_reports/program'
35
- require 'urbanopt/scenario/default_reports/reporting_period'
36
- require 'urbanopt/scenario/default_reports/timeseries_csv'
37
- require 'urbanopt/scenario/default_reports/distributed_generation'
38
- require 'urbanopt/scenario/default_reports/validator'
39
- require 'json-schema'
40
-
41
- require 'json'
42
- require 'pathname'
43
-
44
- module URBANopt
45
- module Scenario
46
- module DefaultReports
47
- ##
48
- # ScenarioReport can generate two types of reports from a scenario.
49
- # The first is a JSON format saved to 'default_scenario_report.json'.
50
- # The second is a CSV format saved to 'default_scenario_report.csv'.
51
- ##
52
- class ScenarioReport
53
- attr_accessor :id, :name, :directory_name, :timesteps_per_hour, :number_of_not_started_simulations,
54
- :number_of_started_simulations, :number_of_complete_simulations, :number_of_failed_simulations,
55
- :timeseries_csv, :location, :program, :construction_costs, :reporting_periods, :feature_reports, :distributed_generation # :nodoc:
56
- # ScenarioReport class intializes the scenario report attributes:
57
- # +:id+ , +:name+ , +:directory_name+, +:timesteps_per_hour+ , +:number_of_not_started_simulations+ ,
58
- # +:number_of_started_simulations+ , +:number_of_complete_simulations+ , +:number_of_failed_simulations+ ,
59
- # +:timeseries_csv+ , +:location+ , +:program+ , +:construction_costs+ , +:reporting_periods+ , +:feature_reports+
60
- ##
61
- # Each ScenarioReport object corresponds to a single Scenario.
62
- ##
63
- # [parameters:]
64
- # +hash+ - _Hash_ - A hash of a previously serialized ScenarioReport.
65
- ##
66
- def initialize(hash = {})
67
- hash.delete_if { |k, v| v.nil? }
68
- hash = defaults.merge(hash)
69
-
70
- @id = hash[:id]
71
- @name = hash[:name]
72
- @directory_name = hash[:directory_name]
73
- @timesteps_per_hour = hash[:timesteps_per_hour]
74
- @number_of_not_started_simulations = hash[:number_of_not_started_simulations]
75
- @number_of_started_simulations = hash[:number_of_started_simulations]
76
- @number_of_complete_simulations = hash[:number_of_complete_simulations]
77
- @number_of_failed_simulations = hash[:number_of_failed_simulations]
78
- @timeseries_csv = TimeseriesCSV.new(hash[:timeseries_csv])
79
- @location = Location.new(hash[:location])
80
- @program = Program.new(hash[:program])
81
- @distributed_generation = DistributedGeneration.new(hash[:distributed_generation] || {})
82
-
83
- @construction_costs = []
84
- hash[:construction_costs].each do |cc|
85
- @constructiion_costs << ConstructionCost.new(cc)
86
- end
87
-
88
- @reporting_periods = []
89
- hash[:reporting_periods].each do |rp|
90
- @reporting_periods << ReportingPeriod.new(rp)
91
- end
92
-
93
- # feature_report is intialized here to be used in the add_feature_report method
94
- @feature_reports = []
95
- hash[:feature_reports].each do |fr|
96
- @feature_reports << FeatureReport.new(fr)
97
- end
98
-
99
- @file_name = 'default_scenario_report'
100
-
101
- # initialize class variables @@validator and @@schema
102
- @@validator ||= Validator.new
103
- @@schema ||= @@validator.schema
104
-
105
- # initialize @@logger
106
- @@logger ||= URBANopt::Scenario::DefaultReports.logger
107
- end
108
-
109
- ##
110
- # Assigns default values if values do not exist.
111
- ##
112
- def defaults
113
- hash = {}
114
- hash[:id] = nil.to_s
115
- hash[:name] = nil.to_s
116
- hash[:directory_name] = nil.to_s
117
- hash[:timesteps_per_hour] = nil # unknown
118
- hash[:number_of_not_started_simulations] = 0
119
- hash[:number_of_started_simulations] = 0
120
- hash[:number_of_complete_simulations] = 0
121
- hash[:number_of_failed_simulations] = 0
122
- hash[:timeseries_csv] = TimeseriesCSV.new.to_hash
123
- hash[:location] = Location.new.defaults
124
- hash[:program] = Program.new.to_hash
125
- hash[:construction_costs] = []
126
- hash[:reporting_periods] = []
127
- hash[:feature_reports] = []
128
- return hash
129
- end
130
-
131
- ##
132
- # Gets the saved JSON file path.
133
- ##
134
- def json_path
135
- File.join(@directory_name, @file_name + '.json')
136
- end
137
-
138
- ##
139
- # Gets the saved CSV file path.
140
- ##
141
- def csv_path
142
- File.join(@directory_name, @file_name + '.csv')
143
- end
144
-
145
- ##
146
- # Saves the 'default_scenario_report.json' and 'default_scenario_report.csv' files
147
- ##
148
- # [parameters]:
149
- # +file_name+ - _String_ - Assign a name to the saved scenario results file without an extension
150
- def save(file_name = 'default_scenario_report')
151
- # reassign the initialize local variable @file_name to the file name input.
152
- @file_name = file_name
153
-
154
- # save the csv data
155
- old_timeseries_path = nil
156
- if !@timeseries_csv.path.nil?
157
- old_timeseries_path = @timeseries_csv.path
158
- end
159
-
160
- @timeseries_csv.path = File.join(@directory_name, file_name + '.csv')
161
- @timeseries_csv.save_data
162
-
163
- hash = {}
164
- hash[:scenario_report] = to_hash
165
- hash[:feature_reports] = []
166
- @feature_reports.each do |feature_report|
167
- hash[:feature_reports] << feature_report.to_hash
168
- end
169
-
170
- json_name_path = File.join(@directory_name, file_name + '.json')
171
-
172
- File.open(json_name_path, 'w') do |f|
173
- f.puts JSON.pretty_generate(hash)
174
- # make sure data is written to the disk one way or the other
175
- begin
176
- f.fsync
177
- rescue StandardError
178
- f.flush
179
- end
180
- end
181
-
182
- if !old_timeseries_path.nil?
183
- @timeseries_csv.path = old_timeseries_path
184
- else
185
- @timeseries_csv.path = File.join(@directory_name, file_name + '.csv')
186
- end
187
- return true
188
- end
189
-
190
- ##
191
- # Converts to a Hash equivalent for JSON serialization.
192
- ##
193
- # - Exclude attributes with nil values.
194
- # - Validate reporting_period hash properties against schema.
195
- ##
196
- def to_hash
197
- result = {}
198
- result[:id] = @id if @id
199
- result[:name] = @name if @name
200
- result[:directory_name] = @directory_name if @directory_name
201
- result[:timesteps_per_hour] = @timesteps_per_hour if @timesteps_per_hour
202
- result[:number_of_not_started_simulations] = @number_of_not_started_simulations if @number_of_not_started_simulations
203
- result[:number_of_started_simulations] = @number_of_started_simulations if @number_of_started_simulations
204
- result[:number_of_complete_simulations] = @number_of_complete_simulations if @number_of_complete_simulations
205
- result[:number_of_failed_simulations] = @number_of_failed_simulations if @number_of_failed_simulations
206
- result[:timeseries_csv] = @timeseries_csv.to_hash if @timeseries_csv
207
- result[:location] = @location.to_hash if @location
208
- result[:program] = @program.to_hash if @program
209
- result[:distributed_generation] = @distributed_generation.to_hash if @distributed_generation
210
-
211
- result[:construction_costs] = []
212
- @construction_costs.each { |cc| result[:construction_costs] << cc.to_hash } if @construction_costs
213
-
214
- result[:reporting_periods] = []
215
- @reporting_periods.each { |rp| result[:reporting_periods] << rp.to_hash } if @reporting_periods
216
-
217
- # result[:feature_reports] = []
218
- # @feature_reports.each { |fr| result[:feature_reports] << fr.to_hash } if @feature_reports
219
-
220
- # validate scenario_report properties against schema
221
- if @@validator.validate(@@schema[:definitions][:ScenarioReport][:properties], result).any?
222
- raise "scenario_report properties does not match schema: #{@@validator.validate(@@schema[:definitions][:ScenarioReport][:properties], result)}"
223
- end
224
-
225
- # have to use the module method because we have not yet initialized the class one
226
- @@logger.info("Scenario name: #{@name}")
227
-
228
- return result
229
- end
230
-
231
- ##
232
- # Add feature reports to each other.
233
- ##
234
- # - check if a feature report have been already added.
235
- # - check feature simulation status
236
- # - merge timeseries_csv information
237
- # - merge program information
238
- # - merge construction_cost information
239
- # - merge reporting_periods information
240
- # - add the array of feature_reports
241
- # - scenario report location takes the location of the first feature in the list
242
- ##
243
- # [parmeters:]
244
- # +feature_report+ - _FeatureReport_ - An object of FeatureReport class.
245
- ##
246
- def add_feature_report(feature_report)
247
- # check if the timesteps_per_hour are identical
248
- if @timesteps_per_hour.nil? || @timesteps_per_hour == ''
249
- @timesteps_per_hour = feature_report.timesteps_per_hour
250
- else
251
- if feature_report.timesteps_per_hour.is_a?(Integer) && feature_report.timesteps_per_hour != @timesteps_per_hour
252
- raise "FeatureReport timesteps_per_hour = '#{feature_report.timesteps_per_hour}' does not match scenario timesteps_per_hour '#{@timesteps_per_hour}'"
253
- end
254
- end
255
-
256
- # check if first report_report_datetime are identical.
257
- if @timeseries_csv.first_report_datetime.nil? || @timeseries_csv.first_report_datetime == ''
258
- @timeseries_csv.first_report_datetime = feature_report.timeseries_csv.first_report_datetime
259
- else
260
- if feature_report.timeseries_csv.first_report_datetime != @timeseries_csv.first_report_datetime
261
- raise "first_report_datetime '#{@first_report_datetime}' does not match other.first_report_datetime '#{feature_report.timeseries_csv.first_report_datetime}'"
262
- end
263
- end
264
-
265
- # check that we have not already added this feature
266
- id = feature_report.id
267
- @feature_reports.each do |existing_feature_report|
268
- if existing_feature_report.id == id
269
- raise "FeatureReport with id = '#{id}' has already been added"
270
- end
271
- end
272
-
273
- # check feature simulation status
274
- if feature_report.simulation_status == 'Not Started'
275
- @number_of_not_started_simulations += 1
276
- elsif feature_report.simulation_status == 'Started'
277
- @number_of_started_simulations += 1
278
- elsif feature_report.simulation_status == 'Complete'
279
- @number_of_complete_simulations += 1
280
- elsif feature_report.simulation_status == 'Failed'
281
- @number_of_failed_simulations += 1
282
- else
283
- raise "Unknown feature_report simulation_status = '#{feature_report.simulation_status}'"
284
- end
285
-
286
- # merge timeseries_csv information
287
- @timeseries_csv.add_timeseries_csv(feature_report.timeseries_csv)
288
-
289
- @timeseries_csv.run_dir_name(@directory_name)
290
-
291
- # merge program information
292
- @program.add_program(feature_report.program)
293
-
294
- # merge construction_cost information
295
- @construction_costs = ConstructionCost.merge_construction_costs(@construction_costs, feature_report.construction_costs)
296
-
297
- # merge reporting_periods information
298
- @reporting_periods = ReportingPeriod.merge_reporting_periods(@reporting_periods, feature_report.reporting_periods)
299
-
300
- @distributed_generation = DistributedGeneration.merge_distributed_generation(@distributed_generation, feature_report.distributed_generation)
301
-
302
- # add feature_report
303
- @feature_reports << feature_report
304
-
305
- # scenario report location takes the location of the first feature in the list
306
- @location = feature_reports[0].location
307
- end
308
- end
309
- end
310
- end
311
- end