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.
@@ -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
- # save the scenario reports csv and json data
169
- old_timeseries_path = nil
170
- if !@timeseries_csv.path.nil?
171
- old_timeseries_path = @timeseries_csv.path
172
- end
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
- @timeseries_csv.path = File.join(@directory_name, "#{file_name}.csv")
175
- @timeseries_csv.save_data
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 !old_timeseries_path.nil?
197
- @timeseries_csv.path = old_timeseries_path
198
- else
199
- @timeseries_csv.path = File.join(@directory_name, "#{file_name}.csv")
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
- @@logger.info("Scenario name: #{@name}")
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