urbanopt-reporting 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/nightly_build.yml +1 -1
- data/CHANGELOG.md +9 -0
- data/Gemfile +5 -5
- data/lib/measures/default_feature_reports/measure.rb +111 -24
- data/lib/urbanopt/reporting/default_reports/distributed_generation.rb +1 -1
- data/lib/urbanopt/reporting/default_reports/feature_report.rb +5 -1
- data/lib/urbanopt/reporting/default_reports/logger.rb +2 -0
- data/lib/urbanopt/reporting/default_reports/qaqc_flags.rb +182 -0
- data/lib/urbanopt/reporting/default_reports/scenario_power_distribution_cost.rb +212 -0
- data/lib/urbanopt/reporting/default_reports/scenario_report.rb +34 -15
- data/lib/urbanopt/reporting/default_reports/schema/scenario_schema.json +372 -31
- data/lib/urbanopt/reporting/version.rb +1 -1
- data/urbanopt-reporting-gem.gemspec +1 -1
- metadata +6 -4
@@ -0,0 +1,212 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt™, Copyright (c) 2019-2022, 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
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as “URBANopt”. Except to comply with the foregoing,
|
24
|
+
# the term “URBANopt”, or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
29
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
30
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
31
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
32
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
33
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
34
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
35
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
36
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
37
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
38
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
39
|
+
# *********************************************************************************
|
40
|
+
|
41
|
+
require_relative 'validator'
|
42
|
+
|
43
|
+
require 'json'
|
44
|
+
require 'json-schema'
|
45
|
+
|
46
|
+
module URBANopt
|
47
|
+
module Reporting
|
48
|
+
module DefaultReports
|
49
|
+
##
|
50
|
+
# scenario_power_distribution_cost include eletrical power distribution system violation and
|
51
|
+
# upgrade cost information.
|
52
|
+
##
|
53
|
+
class ScenarioPowerDistributionCost
|
54
|
+
attr_accessor :results, :outputs, :violation_summary, :costs_per_equipment, :equipment
|
55
|
+
|
56
|
+
##
|
57
|
+
# ScenarioPowerDistributionCost class initializes all
|
58
|
+
# scenario_power_distribution_cost attributes:
|
59
|
+
# +:results+, +:outputs+, +:violation_summary+, +:costs_per_equipment+, +:equipment+
|
60
|
+
##
|
61
|
+
def initialize(hash = {})
|
62
|
+
hash.delete_if { |k, v| v.nil? }
|
63
|
+
hash = defaults.merge(hash)
|
64
|
+
|
65
|
+
@results = hash[:results]
|
66
|
+
@outputs = hash[:outputs]
|
67
|
+
@violation_summary = hash[:violation_summary]
|
68
|
+
@costs_per_equipment = hash[:costs_per_equipment]
|
69
|
+
@equipment = hash[:equipment]
|
70
|
+
|
71
|
+
# initialize class variables @@validator and @@schema
|
72
|
+
@@validator ||= Validator.new
|
73
|
+
@@schema ||= @@validator.schema
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Assigns default values if attribute values do not exist.##
|
78
|
+
def defaults
|
79
|
+
hash = {}
|
80
|
+
hash[:results] = []
|
81
|
+
hash[:outputs] = {}
|
82
|
+
hash[:violation_summary] = []
|
83
|
+
hash[:costs_per_equipment] = []
|
84
|
+
hash[:equipment] = []
|
85
|
+
|
86
|
+
return hash
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Converts to a Hash equivalent for JSON serialization.
|
91
|
+
##
|
92
|
+
# - Exclude attributes with nil values.
|
93
|
+
# - Validate power_distribution_cost hash properties against schema.
|
94
|
+
##
|
95
|
+
def to_hash
|
96
|
+
result = {}
|
97
|
+
result[:results] = @results if @results
|
98
|
+
result[:outputs] = @outputs if !@outputs.empty?
|
99
|
+
result[:violation_summary] = @violation_summary if @violation_summary
|
100
|
+
result[:costs_per_equipment] = @costs_per_equipment if @costs_per_equipment
|
101
|
+
result[:equipment] = @equipment if @equipment
|
102
|
+
|
103
|
+
# validate power_distribution_cost properties against schema
|
104
|
+
if @@validator.validate(@@schema[:definitions][:ScenarioPowerDistributionCost][:properties], result).any?
|
105
|
+
raise "scenario_power_distribution_cost properties does not match schema: #{@@validator.validate(@@schema[:definitions][:ScenarioPowerDistributionCost][:properties], result)}"
|
106
|
+
end
|
107
|
+
|
108
|
+
return result
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Add a result
|
113
|
+
##
|
114
|
+
def add_result(hash = {})
|
115
|
+
hash.delete_if { |k, v| v.nil? }
|
116
|
+
hash = defaults.merge(hash)
|
117
|
+
result = {}
|
118
|
+
result['num_violations'] = hash[:num_violations]
|
119
|
+
@results << result
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
## Add outputs
|
124
|
+
##
|
125
|
+
def add_outputs(hash = {})
|
126
|
+
hash.delete_if { |k, v| v.nil? }
|
127
|
+
hash = defaults.merge(hash)
|
128
|
+
output = {}
|
129
|
+
output['log_file'] = hash[:log_file]
|
130
|
+
output['jobs'] = []
|
131
|
+
hash[:jobs].each do |job|
|
132
|
+
output['jobs'] << job
|
133
|
+
end
|
134
|
+
@outputs = output
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
## Add a violation summary
|
139
|
+
##
|
140
|
+
def add_violation_summary(hash = {})
|
141
|
+
hash.delete_if { |k, v| v.nil? }
|
142
|
+
hash = defaults.merge(hash)
|
143
|
+
violation_summary = {}
|
144
|
+
violation_summary['scenario'] = hash[:scenario]
|
145
|
+
violation_summary['stage'] = hash[:stage]
|
146
|
+
violation_summary['upgrade_type'] = hash[:upgrade_type]
|
147
|
+
violation_summary['simulation_time_s'] = hash[:simulation_time_s]
|
148
|
+
violation_summary['thermal_violations_present'] = hash[:thermal_violations_present]
|
149
|
+
violation_summary['voltage_violations_present'] = hash[:voltage_violations_present]
|
150
|
+
violation_summary['max_bus_voltage'] = hash[:max_bus_voltage]
|
151
|
+
violation_summary['min_bus_voltage'] = hash[:min_bus_voltage]
|
152
|
+
violation_summary['num_voltage_violation_buses'] = hash[:num_voltage_violation_buses]
|
153
|
+
violation_summary['num_overvoltage_violation_buses'] = hash[:num_overvoltage_violation_buses]
|
154
|
+
violation_summary['voltage_upper_limit'] = hash[:voltage_upper_limit]
|
155
|
+
violation_summary['num_undervoltage_violation_buses'] = hash[:num_undervoltage_violation_buses]
|
156
|
+
violation_summary['voltage_lower_limit'] = hash[:voltage_lower_limit]
|
157
|
+
violation_summary['max_line_loading'] = hash[:max_line_loading]
|
158
|
+
violation_summary['max_transformer_loading'] = hash[:max_transformer_loading]
|
159
|
+
violation_summary['num_line_violations'] = hash[:num_line_violations]
|
160
|
+
violation_summary['line_upper_limit'] = hash[:line_upper_limit]
|
161
|
+
violation_summary['num_transformer_violations'] = hash[:num_transformer_violations]
|
162
|
+
violation_summary['transformer_upper_limit'] = hash[:transformer_upper_limit]
|
163
|
+
|
164
|
+
@violation_summary << violation_summary
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# Add costs per equipment
|
169
|
+
##
|
170
|
+
def add_costs_per_equipment
|
171
|
+
hash.delete_if { |k, v| v.nil? }
|
172
|
+
hash = defaults.merge(hash)
|
173
|
+
costs_per_equipment = {}
|
174
|
+
costs_per_equipment['name'] = hash[:name]
|
175
|
+
costs_per_equipment['type'] = hash[:type]
|
176
|
+
costs_per_equipment['count'] = hash[:count]
|
177
|
+
costs_per_equipment['total_cost_usd'] = hash[:costs_per_equipment]
|
178
|
+
|
179
|
+
@costs_per_equipment << costs_per_equipment
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Add equipment
|
184
|
+
##
|
185
|
+
def add_equipment
|
186
|
+
hash.delete_if { |k, v| v.nil? }
|
187
|
+
hash = defaults.merge(hash)
|
188
|
+
equipment = {}
|
189
|
+
equipment['equipment_type'] = hash[:equipment_type]
|
190
|
+
equipment['equipment_name'] = hash[:equipment_name]
|
191
|
+
equipment['status'] = hash[:status]
|
192
|
+
equipment['parameter1_name'] = hash[:parameter1_name]
|
193
|
+
equipment['parameter1_original'] = hash[:parameter1_original]
|
194
|
+
equipment['parameter1_upgraded'] = hash[:parameter1_upgraded]
|
195
|
+
equipment['parameter2_name'] = hash[:parameter2_name]
|
196
|
+
equipment['parameter2_original'] = hash[:parameter2_original]
|
197
|
+
equipment['parameter2_upgraded'] = hash[:parameter2_upgraded]
|
198
|
+
equipment['parameter3_name'] = hash[:parameter3_name]
|
199
|
+
equipment['parameter3_original'] = hash[:parameter3_original]
|
200
|
+
equipment['parameter3_upgraded'] = hash[:parameter3_upgraded]
|
201
|
+
equipment['name'] = hash[:name]
|
202
|
+
|
203
|
+
@equipment << equipment
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
end # ScenarioPowerDistributionCost
|
209
|
+
|
210
|
+
end # DefaultReports
|
211
|
+
end # Reporting
|
212
|
+
end # URBANopt
|
@@ -47,6 +47,8 @@ require_relative 'timeseries_csv'
|
|
47
47
|
require_relative 'distributed_generation'
|
48
48
|
require_relative 'validator'
|
49
49
|
require_relative 'scenario_power_distribution'
|
50
|
+
require_relative 'scenario_power_distribution_cost'
|
51
|
+
require_relative 'qaqc_flags'
|
50
52
|
|
51
53
|
require 'json'
|
52
54
|
require 'json-schema'
|
@@ -64,12 +66,13 @@ module URBANopt
|
|
64
66
|
attr_accessor :id, :name, :directory_name, :timesteps_per_hour, :number_of_not_started_simulations,
|
65
67
|
:number_of_started_simulations, :number_of_complete_simulations, :number_of_failed_simulations,
|
66
68
|
:timeseries_csv, :location, :program, :construction_costs, :reporting_periods, :feature_reports, :distributed_generation,
|
67
|
-
:scenario_power_distribution # :nodoc:
|
69
|
+
:scenario_power_distribution, :scenario_power_distribution_cost, :qaqc_flags # :nodoc:
|
68
70
|
|
69
71
|
# ScenarioReport class intializes the scenario report attributes:
|
70
72
|
# +:id+ , +:name+ , +:directory_name+, +:timesteps_per_hour+ , +:number_of_not_started_simulations+ ,
|
71
73
|
# +:number_of_started_simulations+ , +:number_of_complete_simulations+ , +:number_of_failed_simulations+ ,
|
72
|
-
# +:timeseries_csv+ , +:location+ , +:program+ , +:construction_costs+ , +:reporting_periods+ , +:feature_reports
|
74
|
+
# +:timeseries_csv+ , +:location+ , +:program+ , +:construction_costs+ , +:reporting_periods+ , +:feature_reports+,
|
75
|
+
# +:distributed_generation+, +:scenario_power_distribution+, +:qaqc_flags+
|
73
76
|
##
|
74
77
|
# Each ScenarioReport object corresponds to a single Scenario.
|
75
78
|
##
|
@@ -93,6 +96,8 @@ module URBANopt
|
|
93
96
|
@program = Program.new(hash[:program])
|
94
97
|
@distributed_generation = DistributedGeneration.new(hash[:distributed_generation] || {})
|
95
98
|
@scenario_power_distribution = ScenarioPowerDistribution.new(hash[:scenario_power_distribution] || {})
|
99
|
+
@scenario_power_distribution_cost = ScenarioPowerDistributionCost.new(hash[:scenario_power_distribution_cost] || {})
|
100
|
+
@qaqc_flags = QAQC.new(hash[:qaqc_flags])
|
96
101
|
|
97
102
|
@construction_costs = []
|
98
103
|
hash[:construction_costs].each do |cc|
|
@@ -136,6 +141,7 @@ module URBANopt
|
|
136
141
|
hash[:timeseries_csv] = TimeseriesCSV.new.to_hash
|
137
142
|
hash[:location] = Location.new.defaults
|
138
143
|
hash[:program] = Program.new.to_hash
|
144
|
+
hash[:qaqc_flags] = QAQC.new.to_hash
|
139
145
|
hash[:construction_costs] = []
|
140
146
|
hash[:reporting_periods] = []
|
141
147
|
hash[:feature_reports] = []
|
@@ -161,18 +167,20 @@ module URBANopt
|
|
161
167
|
##
|
162
168
|
# [parameters]:
|
163
169
|
# +file_name+ - _String_ - Assign a name to the saved scenario results file without an extension
|
164
|
-
def save(file_name = 'default_scenario_report', save_feature_reports = true)
|
170
|
+
def save(file_name = 'default_scenario_report', save_feature_reports = true, save_csv_reports = true)
|
165
171
|
# reassign the initialize local variable @file_name to the file name input.
|
166
172
|
@file_name = file_name
|
167
173
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
174
|
+
if save_csv_reports == true
|
175
|
+
# save the scenario reports csv and json data
|
176
|
+
old_timeseries_path = nil
|
177
|
+
if !@timeseries_csv.path.nil?
|
178
|
+
old_timeseries_path = @timeseries_csv.path
|
179
|
+
end
|
173
180
|
|
174
|
-
|
175
|
-
|
181
|
+
@timeseries_csv.path = File.join(@directory_name, "#{file_name}.csv")
|
182
|
+
@timeseries_csv.save_data
|
183
|
+
end
|
176
184
|
|
177
185
|
hash = {}
|
178
186
|
hash[:scenario_report] = to_hash
|
@@ -193,10 +201,12 @@ module URBANopt
|
|
193
201
|
end
|
194
202
|
end
|
195
203
|
|
196
|
-
if
|
197
|
-
|
198
|
-
|
199
|
-
|
204
|
+
if save_csv_reports == true
|
205
|
+
if !old_timeseries_path.nil?
|
206
|
+
@timeseries_csv.path = old_timeseries_path
|
207
|
+
else
|
208
|
+
@timeseries_csv.path = File.join(@directory_name, "#{file_name}.csv")
|
209
|
+
end
|
200
210
|
end
|
201
211
|
|
202
212
|
if save_feature_reports
|
@@ -233,6 +243,8 @@ module URBANopt
|
|
233
243
|
result[:program] = @program.to_hash if @program
|
234
244
|
result[:distributed_generation] = @distributed_generation.to_hash if @distributed_generation
|
235
245
|
result[:scenario_power_distribution] = @scenario_power_distribution.to_hash if @scenario_power_distribution
|
246
|
+
result[:scenario_power_distribution_cost] = @scenario_power_distribution_cost.to_hash if @scenario_power_distribution_cost
|
247
|
+
result[:qaqc_flags] = @qaqc_flags.to_hash if @qaqc_flags
|
236
248
|
|
237
249
|
result[:construction_costs] = []
|
238
250
|
@construction_costs&.each { |cc| result[:construction_costs] << cc.to_hash }
|
@@ -249,7 +261,9 @@ module URBANopt
|
|
249
261
|
end
|
250
262
|
|
251
263
|
# have to use the module method because we have not yet initialized the class one
|
252
|
-
|
264
|
+
unless @name == '' || @name.nil?
|
265
|
+
@@logger.info("Scenario name: #{@name}")
|
266
|
+
end
|
253
267
|
|
254
268
|
return result
|
255
269
|
end
|
@@ -261,6 +275,7 @@ module URBANopt
|
|
261
275
|
# - check feature simulation status
|
262
276
|
# - merge timeseries_csv information
|
263
277
|
# - merge program information
|
278
|
+
# - merge qaqc_flags information
|
264
279
|
# - merge construction_cost information
|
265
280
|
# - merge reporting_periods information
|
266
281
|
# - add the array of feature_reports
|
@@ -324,8 +339,12 @@ module URBANopt
|
|
324
339
|
# merge reporting_periods information
|
325
340
|
@reporting_periods = ReportingPeriod.merge_reporting_periods(@reporting_periods, feature_report.reporting_periods)
|
326
341
|
|
342
|
+
# merge distributed_generation information
|
327
343
|
@distributed_generation = DistributedGeneration.merge_distributed_generation(@distributed_generation, feature_report.distributed_generation)
|
328
344
|
|
345
|
+
# merge qaqc_flags information
|
346
|
+
@qaqc_flags.add_qaqc_flags(feature_report.qaqc_flags)
|
347
|
+
|
329
348
|
# add feature_report
|
330
349
|
@feature_reports << feature_report
|
331
350
|
|