urbanopt-reporting 0.1.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rubocop.yml +10 -0
  4. data/CHANGELOG.md +7 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/Gemfile +18 -0
  7. data/Jenkinsfile +10 -0
  8. data/LICENSE.md +27 -0
  9. data/README.md +40 -0
  10. data/Rakefile +45 -0
  11. data/doc_templates/LICENSE.md +27 -0
  12. data/doc_templates/README.md.erb +42 -0
  13. data/doc_templates/copyright_erb.txt +31 -0
  14. data/doc_templates/copyright_js.txt +4 -0
  15. data/doc_templates/copyright_ruby.txt +29 -0
  16. data/lib/measures/.rubocop.yml +5 -0
  17. data/lib/measures/default_feature_reports/LICENSE.md +27 -0
  18. data/lib/measures/default_feature_reports/README.md +26 -0
  19. data/lib/measures/default_feature_reports/README.md.erb +42 -0
  20. data/lib/measures/default_feature_reports/measure.rb +1012 -0
  21. data/lib/measures/default_feature_reports/measure.xml +160 -0
  22. data/lib/urbanopt/reporting.rb +37 -0
  23. data/lib/urbanopt/reporting/default_reports.rb +44 -0
  24. data/lib/urbanopt/reporting/default_reports/construction_cost.rb +169 -0
  25. data/lib/urbanopt/reporting/default_reports/date.rb +97 -0
  26. data/lib/urbanopt/reporting/default_reports/distributed_generation.rb +379 -0
  27. data/lib/urbanopt/reporting/default_reports/end_use.rb +159 -0
  28. data/lib/urbanopt/reporting/default_reports/end_uses.rb +140 -0
  29. data/lib/urbanopt/reporting/default_reports/extension.rb +15 -0
  30. data/lib/urbanopt/reporting/default_reports/feature_report.rb +266 -0
  31. data/lib/urbanopt/reporting/default_reports/generator.rb +92 -0
  32. data/lib/urbanopt/reporting/default_reports/location.rb +99 -0
  33. data/lib/urbanopt/reporting/default_reports/logger.rb +44 -0
  34. data/lib/urbanopt/reporting/default_reports/power_distribution.rb +103 -0
  35. data/lib/urbanopt/reporting/default_reports/program.rb +265 -0
  36. data/lib/urbanopt/reporting/default_reports/reporting_period.rb +300 -0
  37. data/lib/urbanopt/reporting/default_reports/scenario_report.rb +317 -0
  38. data/lib/urbanopt/reporting/default_reports/schema/README.md +33 -0
  39. data/lib/urbanopt/reporting/default_reports/schema/scenario_csv_columns.txt +34 -0
  40. data/lib/urbanopt/reporting/default_reports/schema/scenario_schema.json +857 -0
  41. data/lib/urbanopt/reporting/default_reports/solar_pv.rb +93 -0
  42. data/lib/urbanopt/reporting/default_reports/storage.rb +105 -0
  43. data/lib/urbanopt/reporting/default_reports/timeseries_csv.rb +300 -0
  44. data/lib/urbanopt/reporting/default_reports/validator.rb +112 -0
  45. data/lib/urbanopt/reporting/default_reports/wind.rb +92 -0
  46. data/lib/urbanopt/reporting/derived_extension.rb +63 -0
  47. data/lib/urbanopt/reporting/version.rb +35 -0
  48. data/urbanopt-reporting-gem.gemspec +33 -0
  49. metadata +176 -0
@@ -0,0 +1,379 @@
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_relative 'solar_pv'
33
+ require_relative 'wind'
34
+ require_relative 'generator'
35
+ require_relative 'storage'
36
+ require 'json-schema'
37
+
38
+ module URBANopt
39
+ module Reporting
40
+ module DefaultReports
41
+ ##
42
+ # Onsite distributed generation system (i.e. SolarPV, Wind, Storage, Generator) design attributes and financial metrics.
43
+ ##
44
+ class DistributedGeneration
45
+ ##
46
+ # _Float_ - Lifecycle costs for the complete distributed generation system in US Dollars
47
+ #
48
+ attr_accessor :lcc_us_dollars
49
+
50
+ ##
51
+ # _Float_ - Net present value of the complete distributed generation system in US Dollars
52
+ #
53
+ attr_accessor :npv_us_dollars
54
+
55
+ ##
56
+ # _Float_ - Total amount paid for utility energy in US Dollars in the first year of operation
57
+ #
58
+ attr_accessor :year_one_energy_cost_us_dollars
59
+
60
+ ##
61
+ # _Float_ - Total amount paid in utility demand charges in US Dollars in the first year of operation
62
+ #
63
+ attr_accessor :year_one_demand_cost_us_dollars
64
+
65
+ ##
66
+ # _Float_ - Total amount paid to the utility in US Dollars in the first year of operation
67
+ #
68
+ attr_accessor :year_one_bill_us_dollars
69
+
70
+ ##
71
+ # _Float_ - Total amount paid to the utility in US Dollars over the life of the system
72
+ #
73
+ attr_accessor :total_energy_cost_us_dollars
74
+
75
+ ##
76
+ # _Array_ - List of _SolarPV_ systems
77
+ #
78
+ attr_accessor :solar_pv
79
+
80
+ ##
81
+ # _Array_ - List of _Wind_ systems
82
+ #
83
+ attr_accessor :wind
84
+
85
+ ##
86
+ # _Array_ - List of _Generator_ systems
87
+ #
88
+ attr_accessor :generator
89
+
90
+ ##
91
+ # _Array_ - List of _Storage_ systems
92
+ #
93
+ attr_accessor :storage
94
+
95
+ ##
96
+ # _Float_ - Installed solar PV capacity
97
+ #
98
+ attr_accessor :total_solar_pv_kw
99
+
100
+ ##
101
+ # _Float_ - Installed wind capacity
102
+ #
103
+ attr_accessor :total_wind_kw
104
+
105
+ ##
106
+ # _Float_ - Installed storage capacity
107
+ #
108
+ attr_accessor :total_storage_kw
109
+
110
+ ##
111
+ # _Float_ - Installed storage capacity
112
+ #
113
+ attr_accessor :total_storage_kwh
114
+
115
+ ##
116
+ # _Float_ - Installed generator capacity
117
+ #
118
+ attr_accessor :total_generator_kw
119
+
120
+ ##
121
+ # Initialize distributed generation system design and financial metrics.
122
+ #
123
+ # * Technologies include +:solar_pv+, +:wind+, +:generator+, and +:storage+.
124
+ # * Financial metrics include +:lcc_us_dollars+, +:npv_us_dollars+, +:year_one_energy_cost_us_dollars+, +:year_one_demand_cost_us_dollars+,
125
+ # +:year_one_bill_us_dollars+, and +:total_energy_cost_us_dollars+
126
+ ##
127
+ # [parameters:]
128
+ #
129
+ # * +hash+ - _Hash_ - A hash containting key/value pairs for the distributed generation system attributes listed above.
130
+ #
131
+ def initialize(hash = {})
132
+ hash.delete_if { |k, v| v.nil? }
133
+
134
+ @lcc_us_dollars = hash[:lcc_us_dollars]
135
+ @npv_us_dollars = hash[:npv_us_dollars]
136
+ @year_one_energy_cost_us_dollars = hash[:year_one_energy_cost_us_dollars]
137
+ @year_one_demand_cost_us_dollars = hash[:year_one_demand_cost_us_dollars]
138
+ @year_one_bill_us_dollars = hash[:year_one_bill_us_dollars]
139
+ @total_energy_cost_us_dollars = hash[:total_energy_cost_us_dollars]
140
+
141
+ @total_solar_pv_kw = nil
142
+ @total_wind_kw = nil
143
+ @total_generator_kw = nil
144
+ @total_storage_kw = nil
145
+ @total_storage_kwh = nil
146
+
147
+ @solar_pv = []
148
+ if hash[:solar_pv].class == Hash
149
+ hash[:solar_pv] = [hash[:solar_pv]]
150
+ elsif hash[:solar_pv].nil?
151
+ hash[:solar_pv] = []
152
+ end
153
+
154
+ hash[:solar_pv].each do |s|
155
+ if !s[:size_kw].nil? && (s[:size_kw] != 0)
156
+ @solar_pv.push SolarPV.new(s)
157
+ if @total_solar_pv_kw.nil?
158
+ @total_solar_pv_kw = @solar_pv[-1].size_kw
159
+ else
160
+ @total_solar_pv_kw += @solar_pv[-1].size_kw
161
+ end
162
+ end
163
+ end
164
+
165
+ @wind = []
166
+ if hash[:wind].class == Hash
167
+ hash[:wind] = [hash[:wind]]
168
+ elsif hash[:wind].nil?
169
+ hash[:wind] = []
170
+ end
171
+
172
+ hash[:wind].each do |s|
173
+ if !s[:size_kw].nil? && (s[:size_kw] != 0)
174
+ @wind.push Wind.new(s)
175
+ if @total_wind_kw.nil?
176
+ @total_wind_kw = @wind[-1].size_kw
177
+ else
178
+ @total_wind_kw += @wind[-1].size_kw
179
+ end
180
+ end
181
+ end
182
+
183
+ @generator = []
184
+ if hash[:generator].class == Hash
185
+ hash[:generator] = [hash[:generator]]
186
+ elsif hash[:generator].nil?
187
+ hash[:generator] = []
188
+ end
189
+
190
+ hash[:generator].each do |s|
191
+ if !s[:size_kw].nil? && (s[:size_kw] != 0)
192
+ @generator.push Generator.new(s)
193
+ if @total_generator_kw.nil?
194
+ @total_generator_kw = @generator[-1].size_kw
195
+ else
196
+ @total_generator_kw += @generator[-1].size_kw
197
+ end
198
+ end
199
+ end
200
+
201
+ @storage = []
202
+ if hash[:storage].class == Hash
203
+ hash[:storage] = [hash[:storage]]
204
+ elsif hash[:storage].nil?
205
+ hash[:storage] = []
206
+ end
207
+
208
+ hash[:storage].each do |s|
209
+ if !s[:size_kw].nil? && (s[:size_kw] != 0)
210
+ @storage.push Storage.new(s)
211
+ if @total_storage_kw.nil?
212
+ @total_storage_kw = @storage[-1].size_kw
213
+ @total_storage_kwh = @storage[-1].size_kwh
214
+ else
215
+ @total_storage_kw += @storage[-1].size_kw
216
+ @total_storage_kwh += @storage[-1].size_kwh
217
+ end
218
+ end
219
+ end
220
+
221
+ # initialize class variables @@validator and @@schema
222
+ @@validator ||= Validator.new
223
+ @@schema ||= @@validator.schema
224
+
225
+ # initialize @@logger
226
+ @@logger ||= URBANopt::Reporting::DefaultReports.logger
227
+ end
228
+
229
+ ##
230
+ # Add a tech
231
+ ##
232
+ def add_tech(name, tech)
233
+ if name == 'solar_pv'
234
+ @solar_pv.push tech
235
+ if @total_solar_pv_kw.nil?
236
+ @total_solar_pv_kw = tech.size_kw
237
+ else
238
+ @total_solar_pv_kw += tech.size_kw
239
+ end
240
+ end
241
+
242
+ if name == 'wind'
243
+ @wind.push tech
244
+ if @total_wind_kw.nil?
245
+ @total_wind_kw = tech.size_kw
246
+ else
247
+ @total_wind_kw += tech.size_kw
248
+ end
249
+ end
250
+
251
+ if name == 'storage'
252
+ @storage.push tech
253
+ if @total_storage_kw.nil?
254
+ @total_storage_kw = tech.size_kw
255
+ @total_storage_kwh = tech.size_kwh
256
+ else
257
+ @total_storage_kw += tech.size_kw
258
+ @total_storage_kwh += tech.size_kwh
259
+ end
260
+ end
261
+
262
+ if name == 'generator'
263
+ @generator.push tech
264
+ if @total_generator_kw.nil?
265
+ @total_generator_kw = tech.size_kw
266
+ else
267
+ @total_generator_kw += tech.size_kw
268
+ end
269
+ end
270
+ end
271
+
272
+ ##
273
+ # Convert to a Hash equivalent for JSON serialization
274
+ ##
275
+ def to_hash
276
+ result = {}
277
+
278
+ result[:lcc_us_dollars] = @lcc_us_dollars if @lcc_us_dollars
279
+ result[:npv_us_dollars] = @npv_us_dollars if @npv_us_dollars
280
+ result[:year_one_energy_cost_us_dollars] = @year_one_energy_cost_us_dollars if @year_one_energy_cost_us_dollars
281
+ result[:year_one_demand_cost_us_dollars] = @year_one_demand_cost_us_dollars if @year_one_demand_cost_us_dollars
282
+ result[:year_one_bill_us_dollars] = @year_one_bill_us_dollars if @year_one_bill_us_dollars
283
+ result[:total_solar_pv_kw] = @total_solar_pv_kw if @total_solar_pv_kw
284
+ result[:total_wind_kw] = @total_wind_kw if @total_wind_kw
285
+ result[:total_generator_kw] = @total_generator_kw if @total_generator_kw
286
+ result[:total_storage_kw] = @total_storage_kw if @total_storage_kw
287
+ result[:total_storage_kwh] = @total_storage_kwh if @total_storage_kwh
288
+
289
+ result[:solar_pv] = []
290
+ @solar_pv.each do |pv|
291
+ result[:solar_pv].push pv.to_hash
292
+ end
293
+ result[:wind] = []
294
+ @wind.each do |wind|
295
+ result[:wind].push wind.to_hash
296
+ end
297
+ result[:generator] = []
298
+ @generator.each do |generator|
299
+ result[:generator].push generator.to_hash
300
+ end
301
+ result[:storage] = []
302
+ @storage.each do |storage|
303
+ result[:storage].push storage.to_hash
304
+ end
305
+ return result
306
+ end
307
+
308
+ ### get keys ...not needed
309
+ # def self.get_all_keys(h)
310
+ # h.each_with_object([]){|(k,v),a| v.is_a?(Hash) ? a.push(k,*get_all_keys(v)) : a << k }
311
+ # end
312
+
313
+ ##
314
+ # Add up old and new values
315
+ ##
316
+ def self.add_values(existing_value, new_value) #:nodoc:
317
+ if existing_value && new_value
318
+ existing_value += new_value
319
+ elsif new_value
320
+ existing_value = new_value
321
+ end
322
+ return existing_value
323
+ end
324
+
325
+ ##
326
+ # Merge a distributed generation system with a new system
327
+ ##
328
+ def self.merge_distributed_generation(existing_dgen, new_dgen)
329
+ existing_dgen.lcc_us_dollars = add_values(existing_dgen.lcc_us_dollars, new_dgen.lcc_us_dollars)
330
+ existing_dgen.npv_us_dollars = add_values(existing_dgen.npv_us_dollars, new_dgen.npv_us_dollars)
331
+ existing_dgen.year_one_energy_cost_us_dollars = add_values(existing_dgen.year_one_energy_cost_us_dollars, new_dgen.year_one_energy_cost_us_dollars)
332
+ existing_dgen.year_one_demand_cost_us_dollars = add_values(existing_dgen.year_one_demand_cost_us_dollars, new_dgen.year_one_demand_cost_us_dollars)
333
+ existing_dgen.year_one_bill_us_dollars = add_values(existing_dgen.year_one_bill_us_dollars, new_dgen.year_one_bill_us_dollars)
334
+ existing_dgen.total_energy_cost_us_dollars = add_values(existing_dgen.total_energy_cost_us_dollars, new_dgen.total_energy_cost_us_dollars)
335
+
336
+ new_dgen.solar_pv.each do |pv|
337
+ existing_dgen.solar_pv.push pv
338
+ if existing_dgen.total_solar_pv_kw.nil?
339
+ existing_dgen.total_solar_pv_kw = pv.size_kw
340
+ else
341
+ existing_dgen.total_solar_pv_kw += pv.size_kw
342
+ end
343
+ end
344
+
345
+ new_dgen.wind.each do |wind|
346
+ existing_dgen.wind.push wind
347
+ if existing_dgen.total_wind_kw.nil?
348
+ existing_dgen.total_wind_kw = wind.size_kw
349
+ else
350
+ existing_dgen.total_wind_kw += wind.size_kw
351
+ end
352
+ end
353
+
354
+ new_dgen.storage.each do |storage|
355
+ existing_dgen.storage.push storage
356
+ if existing_dgen.total_wind_kw.nil?
357
+ existing_dgen.total_storage_kw = storage.size_kw
358
+ existing_dgen.total_storage_kwh = storage.size_kwh
359
+ else
360
+ existing_dgen.total_storage_kw += storage.size_kw
361
+ existing_dgen.total_storage_kwh += storage.size_kwh
362
+ end
363
+ end
364
+
365
+ new_dgen.generator.each do |generator|
366
+ existing_dgen.generator.push generator
367
+ if existing_dgen.total_wind_kw.nil?
368
+ existing_dgen.total_generator_kw = generator.size_kw
369
+ else
370
+ existing_dgen.total_generator_kw += generator.size_kw
371
+ end
372
+ end
373
+
374
+ return existing_dgen
375
+ end
376
+ end
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,159 @@
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_relative 'validator'
32
+ require 'json-schema'
33
+
34
+ module URBANopt
35
+ module Reporting
36
+ module DefaultReports
37
+ ##
38
+ # Enduse class all enduse energy consumption results.
39
+ ##
40
+ class EndUse
41
+ attr_accessor :heating, :cooling, :interior_lighting, :exterior_lighting, :interior_equipment, :exterior_equipment,
42
+ :fans, :pumps, :heat_rejection, :humidification, :heat_recovery, :water_systems, :refrigeration, :generators # :nodoc:
43
+
44
+ ##
45
+ # EndUse class intialize all enduse atributes: +:heating+ , +:cooling+ , +:interior_lighting+ ,
46
+ # +:exterior_lighting+ , +:interior_equipment+ , +:exterior_equipment+ ,
47
+ # +:fans+ , +:pumps+ , +:heat_rejection+ , +:humidification+ , +:heat_recovery+ , +:water_systems+ , +:refrigeration+ , +:generators+
48
+ ##
49
+ # [parameters:]
50
+ # +hash+ - _Hash_ - A hash which may contain a deserialized end_use.
51
+ ##
52
+ def initialize(hash = {})
53
+ hash.delete_if { |k, v| v.nil? }
54
+ hash = defaults.merge(hash)
55
+
56
+ @heating = hash[:heating]
57
+ @cooling = hash[:cooling]
58
+ @interior_lighting = hash[:interior_lighting]
59
+ @exterior_lighting = hash[:exterior_lighting]
60
+ @interior_equipment = hash[:interior_equipment]
61
+ @exterior_equipment = hash[:exterior_equipment]
62
+ @fans = hash[:fans]
63
+ @pumps = hash[:pumps]
64
+ @heat_rejection = hash[:heat_rejection]
65
+ @humidification = hash[:humidification]
66
+ @heat_recovery = hash[:heat_recovery]
67
+ @water_systems = hash[:water_systems]
68
+ @refrigeration = hash[:refrigeration]
69
+ @generators = hash[:generators]
70
+
71
+ # initialize class variables @@validator and @@schema
72
+ @@validator ||= Validator.new
73
+ @@schema ||= @@validator.schema
74
+ end
75
+
76
+ ##
77
+ # Assign default values if values does not exist
78
+ ##
79
+ def defaults
80
+ hash = {}
81
+
82
+ hash[:heating] = nil
83
+ hash[:cooling] = nil
84
+ hash[:interior_lighting] = nil
85
+ hash[:exterior_lighting] = nil
86
+ hash[:interior_equipment] = nil
87
+ hash[:exterior_equipment] = nil
88
+ hash[:fans] = nil
89
+ hash[:pumps] = nil
90
+ hash[:heat_rejection] = nil
91
+ hash[:humidification] = nil
92
+ hash[:heat_recovery] = nil
93
+ hash[:water_systems] = nil
94
+ hash[:refrigeration] = nil
95
+ hash[:generators] = nil
96
+
97
+ return hash
98
+ end
99
+
100
+ ##
101
+ # Convert to a Hash equivalent for JSON serialization.
102
+ ##
103
+ # - Exclude attributes with nil values.
104
+ # - Validate end_use hash properties against schema.
105
+ ##
106
+ def to_hash
107
+ result = {}
108
+
109
+ result[:heating] = @heating
110
+ result[:cooling] = @cooling
111
+ result[:interior_lighting] = @interior_lighting
112
+ result[:exterior_lighting] = @exterior_lighting
113
+ result[:interior_equipment] = @interior_equipment
114
+ result[:exterior_equipment] = @exterior_equipment
115
+ result[:fans] = @fans
116
+ result[:pumps] = @pumps
117
+ result[:heat_rejection] = @heat_rejection
118
+ result[:humidification] = @humidification
119
+ result[:heat_recovery] = @heat_recovery
120
+ result[:water_systems] = @water_systems
121
+ result[:refrigeration] = @refrigeration
122
+ result[:generators] = @generators
123
+
124
+ # validate end_use properties against schema
125
+ if @@validator.validate(@@schema[:definitions][:EndUse][:properties], result).any?
126
+ raise "end_use properties does not match schema: #{@@validator.validate(@@schema[:definitions][:EndUse][:properties], result)}"
127
+ end
128
+
129
+ return result
130
+ end
131
+
132
+ ##
133
+ # Aggregate values of each EndUse attribute.
134
+ ##
135
+ # [Parameters:]
136
+ # +new_end_use+ - _EndUse_ - An object of EndUse class.
137
+ ##
138
+ def merge_end_use!(new_end_use)
139
+ @heating += new_end_use.heating if new_end_use.heating
140
+ @cooling += new_end_use.cooling if new_end_use.cooling
141
+ @interior_lighting += new_end_use.interior_lighting if new_end_use.interior_lighting
142
+ @exterior_lighting += new_end_use.exterior_lighting if new_end_use.exterior_lighting
143
+ @interior_equipment += new_end_use.interior_equipment if new_end_use.interior_equipment
144
+ @exterior_equipment += new_end_use.exterior_equipment if new_end_use.exterior_equipment
145
+ @fans += new_end_use.fans if new_end_use.fans
146
+ @pumps += new_end_use.pumps if new_end_use.pumps
147
+ @heat_rejection += new_end_use.heat_rejection if new_end_use.heat_rejection
148
+ @humidification += new_end_use.humidification if new_end_use.humidification
149
+ @heat_recovery += new_end_use.heat_recovery if new_end_use.heat_recovery
150
+ @water_systems += new_end_use.water_systems if new_end_use.water_systems
151
+ @refrigeration += new_end_use.refrigeration if new_end_use.refrigeration
152
+ @generators += new_end_use.generators if new_end_use.generators
153
+
154
+ return self
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end