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,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