urbanopt-reporting 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|