urbanopt-scenario 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +2 -2
- data/.gitignore +2 -0
- data/CHANGELOG.md +32 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +33 -17
- data/Jenkinsfile +1 -1
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +1 -1
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/docs/README.md +1 -1
- data/docs/package-lock.json +2499 -2322
- data/docs/package.json +13 -9
- data/lib/urbanopt-scenario.rb +1 -1
- data/lib/urbanopt/scenario.rb +2 -1
- data/lib/urbanopt/scenario/default_reports.rb +3 -8
- data/lib/urbanopt/scenario/extension.rb +1 -1
- data/lib/urbanopt/scenario/logger.rb +1 -1
- data/lib/urbanopt/scenario/scenario_base.rb +1 -1
- data/lib/urbanopt/scenario/scenario_csv.rb +22 -9
- data/lib/urbanopt/scenario/scenario_datapoint_base.rb +8 -1
- data/lib/urbanopt/scenario/scenario_post_processor_base.rb +1 -1
- data/lib/urbanopt/scenario/scenario_post_processor_default.rb +81 -8
- data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +6 -7
- data/lib/urbanopt/scenario/scenario_runner_base.rb +2 -2
- data/lib/urbanopt/scenario/scenario_runner_osw.rb +23 -9
- data/lib/urbanopt/scenario/scenario_visualization.rb +236 -0
- data/lib/urbanopt/scenario/simulation_dir_base.rb +1 -1
- data/lib/urbanopt/scenario/simulation_dir_osw.rb +2 -5
- data/lib/urbanopt/scenario/simulation_mapper_base.rb +1 -1
- data/lib/urbanopt/scenario/version.rb +2 -2
- data/package-lock.json +3 -0
- data/urbanopt-scenario-gem.gemspec +10 -6
- metadata +73 -46
- data/doc_templates/LICENSE.md +0 -27
- data/doc_templates/README.md.erb +0 -42
- data/doc_templates/copyright_erb.txt +0 -31
- data/doc_templates/copyright_js.txt +0 -4
- data/doc_templates/copyright_ruby.txt +0 -29
- data/lib/measures/.rubocop.yml +0 -5
- data/lib/measures/default_feature_reports/LICENSE.md +0 -27
- data/lib/measures/default_feature_reports/README.md +0 -26
- data/lib/measures/default_feature_reports/README.md.erb +0 -42
- data/lib/measures/default_feature_reports/measure.rb +0 -1013
- data/lib/measures/default_feature_reports/measure.xml +0 -160
- data/lib/urbanopt/scenario/default_reports/construction_cost.rb +0 -169
- data/lib/urbanopt/scenario/default_reports/date.rb +0 -97
- data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +0 -379
- data/lib/urbanopt/scenario/default_reports/end_use.rb +0 -159
- data/lib/urbanopt/scenario/default_reports/end_uses.rb +0 -140
- data/lib/urbanopt/scenario/default_reports/feature_report.rb +0 -267
- data/lib/urbanopt/scenario/default_reports/generator.rb +0 -92
- data/lib/urbanopt/scenario/default_reports/location.rb +0 -99
- data/lib/urbanopt/scenario/default_reports/logger.rb +0 -44
- data/lib/urbanopt/scenario/default_reports/power_distribution.rb +0 -102
- data/lib/urbanopt/scenario/default_reports/program.rb +0 -265
- data/lib/urbanopt/scenario/default_reports/reporting_period.rb +0 -304
- data/lib/urbanopt/scenario/default_reports/scenario_report.rb +0 -317
- data/lib/urbanopt/scenario/default_reports/schema/README.md +0 -33
- data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +0 -34
- data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +0 -857
- data/lib/urbanopt/scenario/default_reports/solar_pv.rb +0 -93
- data/lib/urbanopt/scenario/default_reports/storage.rb +0 -105
- data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +0 -299
- data/lib/urbanopt/scenario/default_reports/validator.rb +0 -97
- data/lib/urbanopt/scenario/default_reports/wind.rb +0 -92
@@ -1,304 +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, :total_utility_cost, :net_utility_cost, :utility_costs, :electricity, :natural_gas, :additional_fuel, :district_cooling,
|
47
|
-
:district_heating, :water, :electricity_produced, :end_uses, :energy_production, :photovoltaic,
|
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+ , +:total_utility_cost , +:net_utility_cost+ , +:utility_costs+ , +:electricity+ , +:natural_gas+ , +:additional_fuel+ , +:district_cooling+ ,
|
53
|
-
# +:district_heating+ , +:water+ , +:electricity_produced+ , +:end_uses+ , +:energy_production+ , +:photovoltaic+ ,
|
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
|
-
@total_utility_cost = hash [:total_utility_cost]
|
76
|
-
@electricity = hash [:electricity]
|
77
|
-
@natural_gas = hash [:natural_gas]
|
78
|
-
@additional_fuel = hash [:additional_fuel]
|
79
|
-
@district_cooling = hash [:district_cooling]
|
80
|
-
@district_heating = hash[:district_heating]
|
81
|
-
@water = hash[:water]
|
82
|
-
@electricity_produced = hash[:electricity_produced]
|
83
|
-
@end_uses = EndUses.new(hash[:end_uses])
|
84
|
-
|
85
|
-
@energy_production = hash[:energy_production]
|
86
|
-
|
87
|
-
@utility_costs = hash[:utility_costs]
|
88
|
-
|
89
|
-
@comfort_result = hash[:comfort_result]
|
90
|
-
|
91
|
-
# initialize class variables @@validator and @@schema
|
92
|
-
@@validator ||= Validator.new
|
93
|
-
@@schema ||= @@validator.schema
|
94
|
-
end
|
95
|
-
|
96
|
-
##
|
97
|
-
# Assigns default values if values do not exist.
|
98
|
-
##
|
99
|
-
def defaults
|
100
|
-
hash = {}
|
101
|
-
|
102
|
-
hash[:id] = nil
|
103
|
-
hash[:name] = nil
|
104
|
-
hash[:multiplier] = nil
|
105
|
-
hash[:start_date] = Date.new.to_hash
|
106
|
-
hash[:end_date] = Date.new.to_hash
|
107
|
-
|
108
|
-
hash[:total_site_energy] = nil
|
109
|
-
hash[:total_source_energy] = nil
|
110
|
-
hash [:net_site_energy] = nil
|
111
|
-
hash [:net_source_energy] = nil
|
112
|
-
hash [:net_utility_cost] = nil
|
113
|
-
hash [:total_utility_cost] = nil
|
114
|
-
hash [:electricity] = nil
|
115
|
-
hash [:natural_gas] = nil
|
116
|
-
hash [:additional_fuel] = nil
|
117
|
-
hash [:district_cooling] = nil
|
118
|
-
hash[:district_heating] = nil
|
119
|
-
|
120
|
-
hash[:electricity_produced] = nil
|
121
|
-
hash[:end_uses] = EndUses.new.to_hash
|
122
|
-
hash[:energy_production] = { electricity_produced: { photovoltaic: nil } }
|
123
|
-
hash[:utility_costs] = [{ fuel_type: nil, total_cost: nil, usage_cost: nil, demand_cost: nil }]
|
124
|
-
hash[:comfort_result] = { time_setpoint_not_met_during_occupied_cooling: nil, time_setpoint_not_met_during_occupied_heating: nil,
|
125
|
-
time_setpoint_not_met_during_occupied_hours: nil, hours_out_of_comfort_bounds_PMV: nil, hours_out_of_comfort_bounds_PPD: nil }
|
126
|
-
|
127
|
-
return hash
|
128
|
-
end
|
129
|
-
|
130
|
-
##
|
131
|
-
# Converts to a Hash equivalent for JSON serialization.
|
132
|
-
##
|
133
|
-
# - Exclude attributes with nil values.
|
134
|
-
# - Validate reporting_period hash properties against schema.
|
135
|
-
#
|
136
|
-
def to_hash
|
137
|
-
result = {}
|
138
|
-
|
139
|
-
result[:id] = @id if @id
|
140
|
-
result[:name] = @name if @name
|
141
|
-
result[:multiplier] = @multiplier if @multiplier
|
142
|
-
result[:start_date] = @start_date.to_hash if @start_date
|
143
|
-
result[:end_date] = @end_date.to_hash if @end_date
|
144
|
-
result[:total_site_energy] = @total_site_energy if @total_site_energy
|
145
|
-
result[:total_source_energy] = @total_source_energy if @total_source_energy
|
146
|
-
result[:net_site_energy] = @net_site_energy if @net_site_energy
|
147
|
-
result[:net_source_energy] = @net_source_energy if @net_source_energy
|
148
|
-
result[:net_utility_cost] = @net_utility_cost if @net_utility_cost
|
149
|
-
result[:total_utility_cost] = @total_utility_cost if @total_utility_cost
|
150
|
-
result[:electricity] = @electricity if @electricity
|
151
|
-
result[:natural_gas] = @natural_gas if @natural_gas
|
152
|
-
result[:additional_fuel] = @additional_fuel if @additional_fuel
|
153
|
-
result[:district_cooling] = @district_cooling if @district_cooling
|
154
|
-
result[:district_heating] = @district_heating if @district_heating
|
155
|
-
result[:water] = @water if @water
|
156
|
-
result[:electricity_produced] = @electricity_produced if @electricity_produced
|
157
|
-
result[:end_uses] = @end_uses.to_hash if @end_uses
|
158
|
-
|
159
|
-
energy_production_hash = @energy_production if @energy_production
|
160
|
-
energy_production_hash.delete_if { |k, v| v.nil? }
|
161
|
-
energy_production_hash.each do |eph|
|
162
|
-
eph.delete_if { |k, v| v.nil? }
|
163
|
-
end
|
164
|
-
|
165
|
-
result[:energy_production] = energy_production_hash if @energy_production
|
166
|
-
|
167
|
-
if @utility_costs.any?
|
168
|
-
result[:utility_costs] = @utility_costs
|
169
|
-
@utility_costs.each do |uc|
|
170
|
-
uc.delete_if { |k, v| v.nil? } if uc
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
comfort_result_hash = @comfort_result if @comfort_result
|
175
|
-
comfort_result_hash.delete_if { |k, v| v.nil? }
|
176
|
-
result[:comfort_result] = comfort_result_hash if @comfort_result
|
177
|
-
|
178
|
-
# validates +reporting_period+ properties against schema for reporting period.
|
179
|
-
if @@validator.validate(@@schema[:definitions][:ReportingPeriod][:properties], result).any?
|
180
|
-
raise "feature_report properties does not match schema: #{@@validator.validate(@@schema[:definitions][:ReportingPeriod][:properties], result)}"
|
181
|
-
end
|
182
|
-
|
183
|
-
return result
|
184
|
-
end
|
185
|
-
|
186
|
-
##
|
187
|
-
# Adds up +existing_value+ and +new_values+ if not nill.
|
188
|
-
##
|
189
|
-
# [parameter:]
|
190
|
-
# +existing_value+ - _Float_ - A value corresponding to a ReportingPeriod attribute.
|
191
|
-
##
|
192
|
-
# +new_value+ - _Float_ - A value corresponding to a ReportingPeriod attribute.
|
193
|
-
##
|
194
|
-
def self.add_values(existing_value, new_value)
|
195
|
-
if existing_value && new_value
|
196
|
-
existing_value += new_value
|
197
|
-
elsif new_value
|
198
|
-
existing_value = new_value
|
199
|
-
end
|
200
|
-
return existing_value
|
201
|
-
end
|
202
|
-
|
203
|
-
##
|
204
|
-
# Merges an +existing_period+ with a +new_period+ if not nil.
|
205
|
-
##
|
206
|
-
# [Parameters:]
|
207
|
-
# +existing_period+ - _ReportingPeriod_ - An object of ReportingPeriod class.
|
208
|
-
##
|
209
|
-
# +new_period+ - _ReportingPeriod_ - An object of ReportingPeriod class.
|
210
|
-
##
|
211
|
-
def self.merge_reporting_period(existing_period, new_period)
|
212
|
-
# modify the existing_period by summing up the results
|
213
|
-
existing_period.total_site_energy = add_values(existing_period.total_site_energy, new_period.total_site_energy)
|
214
|
-
existing_period.total_source_energy = add_values(existing_period.total_source_energy, new_period.total_source_energy)
|
215
|
-
existing_period.net_source_energy = add_values(existing_period.net_source_energy, new_period.net_source_energy)
|
216
|
-
existing_period.net_utility_cost = add_values(existing_period.net_utility_cost, new_period.net_utility_cost)
|
217
|
-
existing_period.total_utility_cost = add_values(existing_period.total_utility_cost, new_period.total_utility_cost)
|
218
|
-
existing_period.electricity = add_values(existing_period.electricity, new_period.electricity)
|
219
|
-
existing_period.natural_gas = add_values(existing_period.natural_gas, new_period.natural_gas)
|
220
|
-
existing_period.additional_fuel = add_values(existing_period.additional_fuel, new_period.additional_fuel)
|
221
|
-
existing_period.district_cooling = add_values(existing_period.district_cooling, new_period.district_cooling)
|
222
|
-
existing_period.district_heating = add_values(existing_period.district_heating, new_period.district_heating)
|
223
|
-
existing_period.water = add_values(existing_period.water, new_period.water)
|
224
|
-
existing_period.electricity_produced = add_values(existing_period.electricity_produced, new_period.electricity_produced)
|
225
|
-
|
226
|
-
# merge end uses
|
227
|
-
new_end_uses = new_period.end_uses
|
228
|
-
existing_period.end_uses.merge_end_uses!(new_end_uses) if existing_period.end_uses
|
229
|
-
|
230
|
-
if existing_period.energy_production
|
231
|
-
if existing_period.energy_production[:electricity_produced]
|
232
|
-
existing_period.energy_production[:electricity_produced][:photovoltaic] = add_values(existing_period.energy_production[:electricity_produced][:photovoltaic], new_period.energy_production[:electricity_produced][:photovoltaic])
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
if existing_period.utility_costs
|
237
|
-
# RK: this need to be updated
|
238
|
-
existing_period.utility_costs.each_with_index do |item, i|
|
239
|
-
existing_period.utility_costs[i][:fuel_type] = existing_period.utility_costs[i][:fuel_type]
|
240
|
-
existing_period.utility_costs[i][:total_cost] = add_values(existing_period.utility_costs[i][:total_cost], new_period.utility_costs[i][:total_cost])
|
241
|
-
existing_period.utility_costs[i][:usage_cost] = add_values(existing_period.utility_costs[i][:usage_cost], new_period.utility_costs[i][:usage_cost])
|
242
|
-
existing_period.utility_costs[i][:demand_cost] = add_values(existing_period.utility_costs[i][:demand_cost], new_period.utility_costs[i][:demand_cost])
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|
246
|
-
|
247
|
-
if existing_period.comfort_result
|
248
|
-
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])
|
249
|
-
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])
|
250
|
-
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])
|
251
|
-
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])
|
252
|
-
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])
|
253
|
-
end
|
254
|
-
|
255
|
-
return existing_period
|
256
|
-
end
|
257
|
-
# rubocop: enable Metrics/AbcSize # :nodoc:
|
258
|
-
|
259
|
-
##
|
260
|
-
# Merges multiple reporting periods together.
|
261
|
-
# - If +existing_periods+ and +new_periods+ ids are equal,
|
262
|
-
# modify the existing_periods by merging the new periods results
|
263
|
-
# - If existing periods are empty, initialize with new_periods.
|
264
|
-
# - Raise an error if the existing periods are not identical with new periods (cannot have different reporting period ids).
|
265
|
-
##
|
266
|
-
# [parameters:]
|
267
|
-
##
|
268
|
-
# +existing_periods+ - _Array_ - An array of ReportingPeriod objects.
|
269
|
-
##
|
270
|
-
# +new_periods+ - _Array_ - An array of ReportingPeriod objects.
|
271
|
-
##
|
272
|
-
def self.merge_reporting_periods(existing_periods, new_periods)
|
273
|
-
id_list_existing = []
|
274
|
-
id_list_new = []
|
275
|
-
id_list_existing = existing_periods.collect(&:id)
|
276
|
-
id_list_new = new_periods.collect(&:id)
|
277
|
-
|
278
|
-
if id_list_existing == id_list_new
|
279
|
-
|
280
|
-
existing_periods.each_index do |index|
|
281
|
-
# if +existing_periods+ and +new_periods+ ids are equal,
|
282
|
-
# modify the existing_periods by merging the new periods results
|
283
|
-
existing_periods[index] = merge_reporting_period(existing_periods[index], new_periods[index])
|
284
|
-
end
|
285
|
-
|
286
|
-
elsif existing_periods.empty?
|
287
|
-
|
288
|
-
# if existing periods are empty, initialize with new_periods
|
289
|
-
# the = operator would link existing_periods and new_periods to the same object in memory
|
290
|
-
# we want to initialize with a deep clone of new_periods
|
291
|
-
existing_periods = Marshal.load(Marshal.dump(new_periods))
|
292
|
-
|
293
|
-
else
|
294
|
-
# raise an error if the existing periods are not identical with new periods (cannot have different reporting period ids)
|
295
|
-
raise 'cannot merge different reporting periods'
|
296
|
-
|
297
|
-
end
|
298
|
-
|
299
|
-
return existing_periods
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
end
|
@@ -1,317 +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 scenario reports csv and json 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
|
-
|
188
|
-
# save the feature reports csv and json data
|
189
|
-
# @feature_reports.each do |feature_report|
|
190
|
-
# feature_report.save_feature_report()
|
191
|
-
# end
|
192
|
-
|
193
|
-
return true
|
194
|
-
end
|
195
|
-
|
196
|
-
##
|
197
|
-
# Converts to a Hash equivalent for JSON serialization.
|
198
|
-
##
|
199
|
-
# - Exclude attributes with nil values.
|
200
|
-
# - Validate reporting_period hash properties against schema.
|
201
|
-
##
|
202
|
-
def to_hash
|
203
|
-
result = {}
|
204
|
-
result[:id] = @id if @id
|
205
|
-
result[:name] = @name if @name
|
206
|
-
result[:directory_name] = @directory_name if @directory_name
|
207
|
-
result[:timesteps_per_hour] = @timesteps_per_hour if @timesteps_per_hour
|
208
|
-
result[:number_of_not_started_simulations] = @number_of_not_started_simulations if @number_of_not_started_simulations
|
209
|
-
result[:number_of_started_simulations] = @number_of_started_simulations if @number_of_started_simulations
|
210
|
-
result[:number_of_complete_simulations] = @number_of_complete_simulations if @number_of_complete_simulations
|
211
|
-
result[:number_of_failed_simulations] = @number_of_failed_simulations if @number_of_failed_simulations
|
212
|
-
result[:timeseries_csv] = @timeseries_csv.to_hash if @timeseries_csv
|
213
|
-
result[:location] = @location.to_hash if @location
|
214
|
-
result[:program] = @program.to_hash if @program
|
215
|
-
result[:distributed_generation] = @distributed_generation.to_hash if @distributed_generation
|
216
|
-
|
217
|
-
result[:construction_costs] = []
|
218
|
-
@construction_costs.each { |cc| result[:construction_costs] << cc.to_hash } if @construction_costs
|
219
|
-
|
220
|
-
result[:reporting_periods] = []
|
221
|
-
@reporting_periods.each { |rp| result[:reporting_periods] << rp.to_hash } if @reporting_periods
|
222
|
-
|
223
|
-
# result[:feature_reports] = []
|
224
|
-
# @feature_reports.each { |fr| result[:feature_reports] << fr.to_hash } if @feature_reports
|
225
|
-
|
226
|
-
# validate scenario_report properties against schema
|
227
|
-
if @@validator.validate(@@schema[:definitions][:ScenarioReport][:properties], result).any?
|
228
|
-
raise "scenario_report properties does not match schema: #{@@validator.validate(@@schema[:definitions][:ScenarioReport][:properties], result)}"
|
229
|
-
end
|
230
|
-
|
231
|
-
# have to use the module method because we have not yet initialized the class one
|
232
|
-
@@logger.info("Scenario name: #{@name}")
|
233
|
-
|
234
|
-
return result
|
235
|
-
end
|
236
|
-
|
237
|
-
##
|
238
|
-
# Add feature reports to each other.
|
239
|
-
##
|
240
|
-
# - check if a feature report have been already added.
|
241
|
-
# - check feature simulation status
|
242
|
-
# - merge timeseries_csv information
|
243
|
-
# - merge program information
|
244
|
-
# - merge construction_cost information
|
245
|
-
# - merge reporting_periods information
|
246
|
-
# - add the array of feature_reports
|
247
|
-
# - scenario report location takes the location of the first feature in the list
|
248
|
-
##
|
249
|
-
# [parmeters:]
|
250
|
-
# +feature_report+ - _FeatureReport_ - An object of FeatureReport class.
|
251
|
-
##
|
252
|
-
def add_feature_report(feature_report)
|
253
|
-
# check if the timesteps_per_hour are identical
|
254
|
-
if @timesteps_per_hour.nil? || @timesteps_per_hour == ''
|
255
|
-
@timesteps_per_hour = feature_report.timesteps_per_hour
|
256
|
-
else
|
257
|
-
if feature_report.timesteps_per_hour.is_a?(Integer) && feature_report.timesteps_per_hour != @timesteps_per_hour
|
258
|
-
raise "FeatureReport timesteps_per_hour = '#{feature_report.timesteps_per_hour}' does not match scenario timesteps_per_hour '#{@timesteps_per_hour}'"
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
# check if first report_report_datetime are identical.
|
263
|
-
if @timeseries_csv.first_report_datetime.nil? || @timeseries_csv.first_report_datetime == ''
|
264
|
-
@timeseries_csv.first_report_datetime = feature_report.timeseries_csv.first_report_datetime
|
265
|
-
else
|
266
|
-
if feature_report.timeseries_csv.first_report_datetime != @timeseries_csv.first_report_datetime
|
267
|
-
raise "first_report_datetime '#{@first_report_datetime}' does not match other.first_report_datetime '#{feature_report.timeseries_csv.first_report_datetime}'"
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# check that we have not already added this feature
|
272
|
-
id = feature_report.id
|
273
|
-
@feature_reports.each do |existing_feature_report|
|
274
|
-
if existing_feature_report.id == id
|
275
|
-
raise "FeatureReport with id = '#{id}' has already been added"
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
# check feature simulation status
|
280
|
-
if feature_report.simulation_status == 'Not Started'
|
281
|
-
@number_of_not_started_simulations += 1
|
282
|
-
elsif feature_report.simulation_status == 'Started'
|
283
|
-
@number_of_started_simulations += 1
|
284
|
-
elsif feature_report.simulation_status == 'Complete'
|
285
|
-
@number_of_complete_simulations += 1
|
286
|
-
elsif feature_report.simulation_status == 'Failed'
|
287
|
-
@number_of_failed_simulations += 1
|
288
|
-
else
|
289
|
-
raise "Unknown feature_report simulation_status = '#{feature_report.simulation_status}'"
|
290
|
-
end
|
291
|
-
|
292
|
-
# merge timeseries_csv information
|
293
|
-
@timeseries_csv.add_timeseries_csv(feature_report.timeseries_csv)
|
294
|
-
|
295
|
-
@timeseries_csv.run_dir_name(@directory_name)
|
296
|
-
|
297
|
-
# merge program information
|
298
|
-
@program.add_program(feature_report.program)
|
299
|
-
|
300
|
-
# merge construction_cost information
|
301
|
-
@construction_costs = ConstructionCost.merge_construction_costs(@construction_costs, feature_report.construction_costs)
|
302
|
-
|
303
|
-
# merge reporting_periods information
|
304
|
-
@reporting_periods = ReportingPeriod.merge_reporting_periods(@reporting_periods, feature_report.reporting_periods)
|
305
|
-
|
306
|
-
@distributed_generation = DistributedGeneration.merge_distributed_generation(@distributed_generation, feature_report.distributed_generation)
|
307
|
-
|
308
|
-
# add feature_report
|
309
|
-
@feature_reports << feature_report
|
310
|
-
|
311
|
-
# scenario report location takes the location of the first feature in the list
|
312
|
-
@location = feature_reports[0].location
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
end
|
317
|
-
end
|