urbanopt-reporting 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,99 @@
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
+ require 'json'
34
+
35
+ module URBANopt
36
+ module Reporting
37
+ module DefaultReports
38
+ ##
39
+ # Location include all location information.
40
+ ##
41
+ class Location
42
+ attr_accessor :latitude, :longitude, :surface_elevation, :weather_filename #:nodoc:
43
+ ##
44
+ # Location class initialize location attributes: +:latitude+ , +:longitude+ , +:surface_elevation+ , +:weather_filename+
45
+ ##
46
+ # [parameters:]
47
+ # +hash+ - _Hash_ - A hash which may contain a deserialized location.
48
+ ##
49
+ def initialize(hash = {})
50
+ hash.delete_if { |k, v| v.nil? }
51
+ hash = defaults.merge(hash)
52
+
53
+ @latitude = hash[:latitude]
54
+ @longitude = hash[:longitude]
55
+ @surface_elevation = hash[:surface_elevation]
56
+ @weather_filename = hash[:weather_filename]
57
+
58
+ # initialize class variables @@validator and @@schema
59
+ @@validator ||= Validator.new
60
+ @@schema ||= @@validator.schema
61
+ end
62
+
63
+ ##
64
+ # Convert to a Hash equivalent for JSON serialization.
65
+ ##
66
+ # - Exclude attributes with nil values.
67
+ # - Validate location hash properties against schema.
68
+ ##
69
+ def to_hash
70
+ result = {}
71
+ result[:latitude] = @latitude if @latitude
72
+ result[:longitude] = @longitude if @longitude
73
+ result[:surface_elevation] = @surface_elevation if @surface_elevation
74
+ result[:weather_filename] = @weather_filename if @weather_filename
75
+
76
+ # validate location properties against schema
77
+ if @@validator.validate(@@schema[:definitions][:Location][:properties], result).any?
78
+ raise "end_uses properties does not match schema: #{@@validator.validate(@@schema[:definitions][:Location][:properties], result)}"
79
+ end
80
+
81
+ return result
82
+ end
83
+
84
+ ##
85
+ # Assign default values if values does not exist
86
+ ##
87
+ def defaults
88
+ hash = {}
89
+ hash[:latitude] = nil
90
+ hash[:longitude] = nil
91
+ hash[:surface_elevation] = nil
92
+ hash[:weather_filename] = nil
93
+
94
+ return hash
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,44 @@
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 'logger'
32
+
33
+ module URBANopt
34
+ module Reporting
35
+ module DefaultReports
36
+ @@logger = Logger.new(STDOUT)
37
+ ##
38
+ # Definining class variable "@@logger" to log errors, info and warning messages.
39
+ def self.logger
40
+ @@logger
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,103 @@
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
+
33
+ require 'json'
34
+ require 'json-schema'
35
+
36
+ module URBANopt
37
+ module Reporting
38
+ module DefaultReports
39
+ ##
40
+ # power_distributio include eletrical power distribution systems information.
41
+ ##
42
+ class PowerDistribution
43
+ attr_accessor :under_voltage_hours, :over_voltage_hours # :nodoc:
44
+ ##
45
+ # PowerDistrinution class intialize all power_distribution attributes:
46
+ # +:under_voltage_hours+ , +:over_voltage_hours+
47
+ ##
48
+ # [parameters:]
49
+ # +hash+ - _Hash_ - A hash which may contain a deserialized power_distribution.
50
+ ##
51
+ def initialize(hash = {})
52
+ hash.delete_if { |k, v| v.nil? }
53
+ hash = defaults.merge(hash)
54
+
55
+ @under_voltage_hours = hash[:under_voltage_hours]
56
+ @over_voltage_hours = hash[:over_voltage_hours]
57
+
58
+ # initialize class variables @@validator and @@schema
59
+ @@validator ||= Validator.new
60
+ @@schema ||= @@validator.schema
61
+ end
62
+
63
+ ##
64
+ # Assigns default values if attribute values do not exist.
65
+ ##
66
+ def defaults
67
+ hash = {}
68
+ hash[:under_voltage_hours] = nil
69
+ hash[:over_voltage_hours] = nil
70
+
71
+ return hash
72
+ end
73
+
74
+ ##
75
+ # Converts to a Hash equivalent for JSON serialization.
76
+ ##
77
+ # - Exclude attributes with nil values.
78
+ # - Validate power_distribution hash properties against schema.
79
+ ##
80
+ def to_hash
81
+ result = {}
82
+ result[:under_voltage_hours] = @under_voltage_hours if @under_voltage_hours
83
+ result[:over_voltage_hours] = @over_voltage_hours if @over_voltage_hours
84
+
85
+ # validate power_distribution properties against schema
86
+ if @@validator.validate(@@schema[:definitions][:PowerDistribution][:properties], result).any?
87
+ raise "power_distribution properties does not match schema: #{@@validator.validate(@@schema[:definitions][:PowerDistribution][:properties], result)}"
88
+ end
89
+
90
+ return result
91
+ end
92
+
93
+ ##
94
+ # Merges muliple power distribution results together.
95
+ ##
96
+ # +new_costs+ - _Array_ - An array of ConstructionCost objects.
97
+ def merge_power_distribition
98
+ # method to be developed for any attributes to be aggregated or merged
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,265 @@
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
+
33
+ require 'json-schema'
34
+ require 'json'
35
+
36
+ module URBANopt
37
+ module Reporting
38
+ module DefaultReports
39
+ ##
40
+ # Program includes all building program related information.
41
+ ##
42
+ class Program
43
+ attr_accessor :site_area, :floor_area, :conditioned_area, :unconditioned_area, :footprint_area, :maximum_roof_height,
44
+ :maximum_number_of_stories, :maximum_number_of_stories_above_ground, :parking_area, :number_of_parking_spaces,
45
+ :number_of_parking_spaces_charging, :parking_footprint_area, :maximum_parking_height, :maximum_number_of_parking_stories,
46
+ :maximum_number_of_parking_stories_above_ground, :number_of_residential_units, :building_types, :building_type, :maximum_occupancy,
47
+ :area, :window_area, :north_window_area, :south_window_area, :east_window_area, :west_window_area, :wall_area, :roof_area, :equipment_roof_area,
48
+ :photovoltaic_roof_area, :available_roof_area, :total_roof_area, :orientation, :aspect_ratio, :total_construction_cost # :nodoc:
49
+ # Program class initialize building program attributes: +:site_area+ , +:floor_area+ , +:conditioned_area+ , +:unconditioned_area+ ,
50
+ # +:footprint_area+ , +:maximum_roof_height, +:maximum_number_of_stories+ , +:maximum_number_of_stories_above_ground+ , +:parking_area+ ,
51
+ # +:number_of_parking_spaces+ , +:number_of_parking_spaces_charging+ , +:parking_footprint_area+ , +:maximum_parking_height+ , +:maximum_number_of_parking_stories+ ,
52
+ # +:maximum_number_of_parking_stories_above_ground+ , +:number_of_residential_units+ , +:building_types+ , +:building_type+ , +:maximum_occupancy+ ,
53
+ # +:area+ , +:window_area+ , +:north_window_area+ , +:south_window_area+ , +:east_window_area+ , +:west_window_area+ , +:wall_area+ , +:roof_area+ ,
54
+ # +:equipment_roof_area+ , +:photovoltaic_roof_area+ , +:available_roof_area+ , +:total_roof_area+ , +:orientation+ , +:aspect_ratio+
55
+ ##
56
+ # [parameters:]
57
+ # +hash+ - _Hash_ - A hash which may contain a deserialized program.
58
+ ##
59
+ def initialize(hash = {})
60
+ hash.delete_if { |k, v| v.nil? }
61
+ hash = defaults.merge(hash)
62
+
63
+ @site_area = hash[:site_area]
64
+ @floor_area = hash[:floor_area]
65
+ @conditioned_area = hash[:conditioned_area]
66
+ @unconditioned_area = hash[:unconditioned_area]
67
+ @footprint_area = hash[:footprint_area]
68
+ @maximum_roof_height = hash[:maximum_roof_height]
69
+ @maximum_number_of_stories = hash[:maximum_number_of_stories]
70
+ @maximum_number_of_stories_above_ground = hash[:maximum_number_of_stories_above_ground]
71
+ @parking_area = hash[:parking_area]
72
+ @number_of_parking_spaces = hash[:number_of_parking_spaces]
73
+ @number_of_parking_spaces_charging = hash[:number_of_parking_spaces_charging]
74
+ @parking_footprint_area = hash[:parking_footprint_area]
75
+ @maximum_parking_height = hash[:maximum_parking_height]
76
+ @maximum_number_of_parking_stories = hash[:maximum_number_of_parking_stories]
77
+ @maximum_number_of_parking_stories_above_ground = hash[:maximum_number_of_parking_stories_above_ground]
78
+ @number_of_residential_units = hash[:number_of_residential_units]
79
+ @building_types = hash[:building_types]
80
+ @window_area = hash[:window_area]
81
+ @wall_area = hash[:wall_area]
82
+ @roof_area = hash[:roof_area]
83
+ @orientation = hash[:orientation]
84
+ @aspect_ratio = hash[:aspect_ratio]
85
+ @total_construction_cost = hash[:total_construction_cost]
86
+
87
+ # initialize class variables @@validator and @@schema
88
+ @@validator ||= Validator.new
89
+ @@schema ||= @@validator.schema
90
+ end
91
+
92
+ ##
93
+ # Assigns default values if values do not exist.
94
+ ##
95
+ def defaults
96
+ hash = {}
97
+ hash[:site_area] = nil
98
+ hash[:floor_area] = nil
99
+ hash[:conditioned_area] = nil
100
+ hash[:unconditioned_area] = nil
101
+ hash[:footprint_area] = nil
102
+ hash[:maximum_roof_height] = nil
103
+ hash[:maximum_number_of_stories] = nil
104
+ hash[:maximum_number_of_stories_above_ground] = nil
105
+ hash[:parking_area] = nil
106
+ hash[:number_of_parking_spaces] = nil
107
+ hash[:number_of_parking_spaces_charging] = nil
108
+ hash[:parking_footprint_area] = nil
109
+ hash[:maximum_parking_height] = nil
110
+ hash[:maximum_number_of_parking_stories] = nil
111
+ hash[:maximum_number_of_parking_stories_above_ground] = nil
112
+ hash[:number_of_residential_units] = nil
113
+ hash[:building_types] = [{ building_type: nil, maximum_occupancy: nil, floor_area: nil }]
114
+ hash[:window_area] = { north_window_area: nil, south_window_area: nil, east_window_area: nil, west_window_area: nil, total_window_area: nil }
115
+ hash[:wall_area] = { north_wall_area: nil, south_wall_area: nil, east_wall_area: nil, west_wall_area: nil, total_wall_area: nil }
116
+ hash[:roof_area] = { equipment_roof_area: nil, photovoltaic_roof_area: nil, available_roof_area: nil, total_roof_area: nil }
117
+ hash[:orientation] = nil
118
+ hash[:aspect_ratio] = nil
119
+ hash[:total_construction_cost] = nil
120
+ return hash
121
+ end
122
+
123
+ ##
124
+ # Convert to a Hash equivalent for JSON serialization.
125
+ ##
126
+ # - Exclude attributes with nil values.
127
+ # - Validate program hash properties against schema.
128
+ ##
129
+ def to_hash
130
+ result = {}
131
+ result[:site_area] = @site_area if @site_area
132
+ result[:floor_area] = @floor_area if @floor_area
133
+ result[:conditioned_area] = @conditioned_area if @conditioned_area
134
+ result[:unconditioned_area] = @unconditioned_area if @unconditioned_area
135
+ result[:footprint_area] = @footprint_area if @footprint_area
136
+ result[:maximum_roof_height] = @maximum_roof_height if @maximum_roof_height
137
+ result[:maximum_number_of_stories] = @maximum_number_of_stories if @maximum_number_of_stories
138
+ result[:maximum_number_of_stories_above_ground] = @maximum_number_of_stories_above_ground if @maximum_number_of_parking_stories_above_ground
139
+ result[:parking_area] = @parking_area if @parking_area
140
+ result[:number_of_parking_spaces] = @number_of_parking_spaces if @number_of_parking_spaces
141
+ result[:number_of_parking_spaces_charging] = @number_of_parking_spaces_charging if @number_of_parking_spaces_charging
142
+ result[:parking_footprint_area] = @parking_footprint_area if @parking_footprint_area
143
+ result[:maximum_parking_height] = @maximum_parking_height if @maximum_parking_height
144
+ result[:maximum_number_of_parking_stories] = @maximum_number_of_parking_stories if @maximum_number_of_parking_stories
145
+ result[:maximum_number_of_parking_stories_above_ground] = @maximum_number_of_parking_stories_above_ground if @maximum_number_of_parking_stories_above_ground
146
+ result[:number_of_residential_units] = @number_of_residential_units if @number_of_residential_units
147
+
148
+ if @building_types.any?
149
+ result[:building_types] = @building_types
150
+ @building_types.each do |bt|
151
+ bt&.delete_if { |k, v| v.nil? }
152
+ end
153
+ end
154
+
155
+ # result[:window_area] = @window_area if @window_area
156
+ window_area_hash = @window_area if @window_area
157
+ window_area_hash.delete_if { |k, v| v.nil? }
158
+ result[:window_area] = window_area_hash if @window_area
159
+
160
+ # result[:wall_area] = @wall_area if @wall_area
161
+ wall_area_hash = @wall_area if @wall_area
162
+ wall_area_hash.delete_if { |k, v| v.nil? }
163
+ result[:wall_area] = wall_area_hash if @wall_area
164
+
165
+ # result[:roof_area] = @roof_area if @roof_area
166
+ roof_area_hash = @roof_area if @roof_area
167
+ roof_area_hash.delete_if { |k, v| v.nil? }
168
+ result[:roof_area] = roof_area_hash if @roof_area
169
+
170
+ result[:orientation] = @orientation if @orientation
171
+ result[:aspect_ratio] = @aspect_ratio if @aspect_ratio
172
+
173
+ result[:total_construction_cost] = @total_construction_cost if @total_construction_cost
174
+
175
+ # validate program properties against schema
176
+ if @@validator.validate(@@schema[:definitions][:Program][:properties], result).any?
177
+ raise "program properties does not match schema: #{@@validator.validate(@@schema[:definitions][:Program][:properties], result)}"
178
+ end
179
+
180
+ return result
181
+ end
182
+
183
+ ##
184
+ # Return the maximum value from +existing_value+ and +new_value+.
185
+ ##
186
+ # [parameters:]
187
+ # +existing_value+ - _Float_ - A value corresponding to a Program attribute.
188
+ ##
189
+ # +new_value+ - _Float_ - A value corresponding to a Program attribute.
190
+ ##
191
+ def max_value(existing_value, new_value)
192
+ if existing_value && new_value
193
+ [existing_value, new_value].max
194
+ elsif new_value
195
+ existing_value = new_value
196
+ end
197
+ return existing_value
198
+ end
199
+
200
+ ##
201
+ # Adds up +existing_value+ and +new_values+ if not nill.
202
+ ##
203
+ # [parameters:]
204
+ # +existing_value+ - _Float_ - A value corresponding to a Program attribute.
205
+ ##
206
+ # +new_value+ - _Float_ - A value corresponding to a Program attribute.
207
+ ##
208
+ def add_values(existing_value, new_value)
209
+ if existing_value && new_value
210
+ existing_value += new_value
211
+ elsif new_value
212
+ existing_value = new_value
213
+ end
214
+ return existing_value
215
+ end
216
+
217
+ ##
218
+ # Merges program objects to each other by summing up values or taking the maximum value of the attributes.
219
+ ##
220
+ # [parameters:]
221
+ # +other+ - _Program_ - An object of Program class.
222
+ ##
223
+ def add_program(other)
224
+ @site_area = add_values(@site_area, other.site_area)
225
+
226
+ @floor_area = add_values(@floor_area, other.floor_area)
227
+ @conditioned_area = add_values(@conditioned_area, other.conditioned_area)
228
+ @unconditioned_area = add_values(@unconditioned_area, other.unconditioned_area)
229
+ @footprint_area = add_values(@footprint_area, other.footprint_area)
230
+ @maximum_roof_height = max_value(@maximum_roof_height, other.maximum_roof_height)
231
+ @maximum_number_of_stories = max_value(@maximum_number_of_stories, other.maximum_number_of_stories)
232
+ @maximum_number_of_stories_above_ground = max_value(@maximum_number_of_stories_above_ground, other.maximum_number_of_stories_above_ground)
233
+ @parking_area = add_values(@parking_area, other.parking_area)
234
+ @number_of_parking_spaces = add_values(@number_of_parking_spaces, other.number_of_parking_spaces)
235
+ @number_of_parking_spaces_charging = add_values(@number_of_parking_spaces_charging, other.number_of_parking_spaces_charging)
236
+ @parking_footprint_area = add_values(@parkig_footprint_area, other.parking_footprint_area)
237
+ @maximum_parking_height = max_value(@maximum_parking_height, other.maximum_parking_height)
238
+ @maximum_number_of_parking_stories = max_value(@maximum_number_of_parking_stories, other.maximum_number_of_parking_stories)
239
+ @maximum_number_of_parking_stories_above_ground = max_value(maximum_number_of_parking_stories_above_ground, other.maximum_number_of_parking_stories_above_ground)
240
+ @number_of_residential_units = add_values(@number_of_residential_units, other.number_of_residential_units)
241
+ @total_construction_cost = add_values(@total_construction_cost, other.total_construction_cost)
242
+
243
+ @building_types = other.building_types
244
+
245
+ @window_area[:north_window_area] = add_values(@window_area[:north_window_area], other.window_area[:north_window_area])
246
+ @window_area[:south_window_area] = add_values(@window_area[:south_window_area], other.window_area[:south_window_area])
247
+ @window_area[:east_window_area] = add_values(@window_area[:east_window_area], other.window_area[:east_window_area])
248
+ @window_area[:west_window_area] = add_values(@window_area[:west_window_area], other.window_area[:west_window_area])
249
+ @window_area[:total_window_area] = add_values(@window_area[:total_window_area], other.window_area[:total_window_area])
250
+
251
+ @wall_area[:north_wall_area] = add_values(@wall_area[:north_wall_area], other.wall_area[:north_wall_area])
252
+ @wall_area[:south_wall_area] = add_values(@wall_area[:south_wall_area], other.wall_area[:south_wall_area])
253
+ @wall_area[:east_wall_area] = add_values(@wall_area[:east_wall_area], other.wall_area[:east_wall_area])
254
+ @wall_area[:west_wall_area] = add_values(@wall_area[:west_wall_area], other.wall_area[:west_wall_area])
255
+ @wall_area[:total_wall_area] = add_values(@wall_area[:total_wall_area], other.wall_area[:total_wall_area])
256
+
257
+ @roof_area[:equipment_roof_area] = add_values(@roof_area[:equipment_roof_area], other.roof_area[:equipment_roof_area])
258
+ @roof_area[:photovoltaic_roof_area] = add_values(@roof_area[:photovoltaic_roof_area], other.roof_area[:photovoltaic_roof_area])
259
+ @roof_area[:available_roof_area] = add_values(@roof_area[:available_roof_area], other.roof_area[:available_roof_area])
260
+ @roof_area[:total_roof_area] = add_values(@roof_area[:total_roof_area], other.roof_area[:total_roof_area])
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end