urbanopt-scenario 0.1.1

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 (84) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CONTRIBUTING.md +58 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. data/.github/pull_request_template.md +23 -0
  6. data/.gitignore +26 -0
  7. data/.rdoc_options +36 -0
  8. data/.rspec +3 -0
  9. data/.rubocop.yml +10 -0
  10. data/.travis.yml +23 -0
  11. data/CHANGELOG.md +19 -0
  12. data/Gemfile +43 -0
  13. data/Jenkinsfile +10 -0
  14. data/LICENSE.md +27 -0
  15. data/RDOC_MAIN.md +39 -0
  16. data/README.md +39 -0
  17. data/Rakefile +51 -0
  18. data/deploy_docs.sh +5 -0
  19. data/doc_templates/LICENSE.md +27 -0
  20. data/doc_templates/README.md.erb +42 -0
  21. data/doc_templates/copyright_erb.txt +31 -0
  22. data/doc_templates/copyright_js.txt +4 -0
  23. data/doc_templates/copyright_ruby.txt +29 -0
  24. data/docs/.gitignore +3 -0
  25. data/docs/.vuepress/components/InnerJsonSchema.vue +84 -0
  26. data/docs/.vuepress/components/JsonSchema.vue +12 -0
  27. data/docs/.vuepress/components/ScenarioSchema.vue +12 -0
  28. data/docs/.vuepress/components/StaticLink.vue +8 -0
  29. data/docs/.vuepress/config.js +15 -0
  30. data/docs/.vuepress/highlight.js +8 -0
  31. data/docs/.vuepress/public/custom_rdoc_styles.css +74 -0
  32. data/docs/.vuepress/utils.js +17 -0
  33. data/docs/README.md +39 -0
  34. data/docs/package-lock.json +11817 -0
  35. data/docs/package.json +26 -0
  36. data/docs/schemas/scenario-schema.md +3 -0
  37. data/lib/change_log.rb +147 -0
  38. data/lib/measures/.rubocop.yml +5 -0
  39. data/lib/measures/default_feature_reports/LICENSE.md +27 -0
  40. data/lib/measures/default_feature_reports/README.md +56 -0
  41. data/lib/measures/default_feature_reports/README.md.erb +42 -0
  42. data/lib/measures/default_feature_reports/measure.rb +742 -0
  43. data/lib/measures/default_feature_reports/measure.xml +139 -0
  44. data/lib/measures/default_feature_reports/tests/USA_CO_Golden-NREL.724666_TMY3.epw +8768 -0
  45. data/lib/measures/default_feature_reports/tests/default_feature_reports_test.rb +238 -0
  46. data/lib/measures/default_feature_reports/tests/example_model.osm +4378 -0
  47. data/lib/urbanopt-scenario.rb +31 -0
  48. data/lib/urbanopt/scenario.rb +45 -0
  49. data/lib/urbanopt/scenario/default_reports.rb +40 -0
  50. data/lib/urbanopt/scenario/default_reports/construction_cost.rb +169 -0
  51. data/lib/urbanopt/scenario/default_reports/date.rb +97 -0
  52. data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +187 -0
  53. data/lib/urbanopt/scenario/default_reports/end_use.rb +159 -0
  54. data/lib/urbanopt/scenario/default_reports/end_uses.rb +140 -0
  55. data/lib/urbanopt/scenario/default_reports/feature_report.rb +213 -0
  56. data/lib/urbanopt/scenario/default_reports/generator.rb +92 -0
  57. data/lib/urbanopt/scenario/default_reports/location.rb +99 -0
  58. data/lib/urbanopt/scenario/default_reports/logger.rb +44 -0
  59. data/lib/urbanopt/scenario/default_reports/program.rb +261 -0
  60. data/lib/urbanopt/scenario/default_reports/reporting_period.rb +298 -0
  61. data/lib/urbanopt/scenario/default_reports/scenario_report.rb +300 -0
  62. data/lib/urbanopt/scenario/default_reports/schema/README.md +34 -0
  63. data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +13 -0
  64. data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +830 -0
  65. data/lib/urbanopt/scenario/default_reports/solar_pv.rb +92 -0
  66. data/lib/urbanopt/scenario/default_reports/storage.rb +105 -0
  67. data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +258 -0
  68. data/lib/urbanopt/scenario/default_reports/validator.rb +97 -0
  69. data/lib/urbanopt/scenario/default_reports/wind.rb +92 -0
  70. data/lib/urbanopt/scenario/extension.rb +63 -0
  71. data/lib/urbanopt/scenario/logger.rb +42 -0
  72. data/lib/urbanopt/scenario/scenario_base.rb +79 -0
  73. data/lib/urbanopt/scenario/scenario_csv.rb +122 -0
  74. data/lib/urbanopt/scenario/scenario_datapoint_base.rb +162 -0
  75. data/lib/urbanopt/scenario/scenario_post_processor_base.rb +69 -0
  76. data/lib/urbanopt/scenario/scenario_post_processor_default.rb +98 -0
  77. data/lib/urbanopt/scenario/scenario_runner_base.rb +63 -0
  78. data/lib/urbanopt/scenario/scenario_runner_osw.rb +158 -0
  79. data/lib/urbanopt/scenario/simulation_dir_base.rb +90 -0
  80. data/lib/urbanopt/scenario/simulation_dir_osw.rb +261 -0
  81. data/lib/urbanopt/scenario/simulation_mapper_base.rb +47 -0
  82. data/lib/urbanopt/scenario/version.rb +35 -0
  83. data/urbanopt-scenario-gem.gemspec +38 -0
  84. metadata +251 -0
@@ -0,0 +1,92 @@
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 'json-schema'
33
+
34
+ module URBANopt
35
+ module Scenario
36
+ module DefaultReports
37
+ ##
38
+ # Onsite generator system attributes
39
+ ##
40
+ class Generator
41
+ ##
42
+ # _Float_ - power capacity in kilowatts
43
+ #
44
+ attr_accessor :size_kw
45
+
46
+ ##
47
+ # Intialize Generator attributes from a hash. Generator attributes currently are limited to power capacity.
48
+ ##
49
+ # [parameters:]
50
+ #
51
+ # * +hash+ - _Hash_ - A hash containting a +:size_kw+ key/value pair which represents the nameplate capacity in kilowatts (kW)
52
+ #
53
+ def initialize(hash = {})
54
+ hash.delete_if { |k, v| v.nil? }
55
+
56
+ @size_kw = hash[:size_kw]
57
+
58
+ # initialize class variables @@validator and @@schema
59
+ @@validator ||= Validator.new
60
+ @@schema ||= @@validator.schema
61
+
62
+ # initialize @@logger
63
+ @@logger ||= URBANopt::Scenario::DefaultReports.logger
64
+ end
65
+
66
+ ##
67
+ # Convert to a Hash equivalent for JSON serialization
68
+ ##
69
+ def to_hash
70
+ result = {}
71
+
72
+ result[:size_kw] = @size_kw if @size_kw
73
+
74
+ return result
75
+ end
76
+
77
+ ##
78
+ # Merge Generator systems
79
+ ##
80
+ def self.add_generator(existing_generator, new_generator)
81
+ if existing_generator.size_kw.nil? && new_generator.size_kw.nil?
82
+ existing_generator.size_kw = nil
83
+ else
84
+ existing_generator.size_kw = (existing_generator.size_kw || 0) + (new_generator.size_kw || 0)
85
+ end
86
+
87
+ return existing_generator
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -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 'urbanopt/scenario/default_reports/validator'
32
+ require 'json-schema'
33
+ require 'json'
34
+
35
+ module URBANopt
36
+ module Scenario
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 Scenario
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,261 @@
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 'urbanopt/scenario/default_reports/validator'
32
+ require 'json-schema'
33
+ require 'json'
34
+
35
+ module URBANopt
36
+ module Scenario
37
+ module DefaultReports
38
+ ##
39
+ # Program includes all building program related information.
40
+ ##
41
+ class Program
42
+ attr_accessor :site_area, :floor_area, :conditioned_area, :unconditioned_area, :footprint_area, :maximum_roof_height,
43
+ :maximum_number_of_stories, :maximum_number_of_stories_above_ground, :parking_area, :number_of_parking_spaces,
44
+ :number_of_parking_spaces_charging, :parking_footprint_area, :maximum_parking_height, :maximum_number_of_parking_stories,
45
+ :maximum_number_of_parking_stories_above_ground, :number_of_residential_units, :building_types, :building_type, :maximum_occupancy,
46
+ :area, :window_area, :north_window_area, :south_window_area, :east_window_area, :west_window_area, :wall_area, :roof_area, :equipment_roof_area,
47
+ :photovoltaic_roof_area, :available_roof_area, :total_roof_area, :orientation, :aspect_ratio # :nodoc:
48
+ # Program class initialize building program attributes: +:site_area+ , +:floor_area+ , +:conditioned_area+ , +:unconditioned_area+ ,
49
+ # +:footprint_area+ , +:maximum_roof_height, +:maximum_number_of_stories+ , +:maximum_number_of_stories_above_ground+ , +:parking_area+ ,
50
+ # +:number_of_parking_spaces+ , +:number_of_parking_spaces_charging+ , +:parking_footprint_area+ , +:maximum_parking_height+ , +:maximum_number_of_parking_stories+ ,
51
+ # +:maximum_number_of_parking_stories_above_ground+ , +:number_of_residential_units+ , +:building_types+ , +:building_type+ , +:maximum_occupancy+ ,
52
+ # +:area+ , +:window_area+ , +:north_window_area+ , +:south_window_area+ , +:east_window_area+ , +:west_window_area+ , +:wall_area+ , +:roof_area+ ,
53
+ # +:equipment_roof_area+ , +:photovoltaic_roof_area+ , +:available_roof_area+ , +:total_roof_area+ , +:orientation+ , +:aspect_ratio+
54
+ ##
55
+ # [parameters:]
56
+ # +hash+ - _Hash_ - A hash which may contain a deserialized program.
57
+ ##
58
+ def initialize(hash = {})
59
+ hash.delete_if { |k, v| v.nil? }
60
+ hash = defaults.merge(hash)
61
+
62
+ @site_area = hash[:site_area]
63
+ @floor_area = hash[:floor_area]
64
+ @conditioned_area = hash[:conditioned_area]
65
+ @unconditioned_area = hash[:unconditioned_area]
66
+ @footprint_area = hash[:footprint_area]
67
+ @maximum_roof_height = hash[:maximum_roof_height]
68
+ @maximum_number_of_stories = hash[:maximum_number_of_stories]
69
+ @maximum_number_of_stories_above_ground = hash[:maximum_number_of_stories_above_ground]
70
+ @parking_area = hash[:parking_area]
71
+ @number_of_parking_spaces = hash[:number_of_parking_spaces]
72
+ @number_of_parking_spaces_charging = hash[:number_of_parking_spaces_charging]
73
+ @parking_footprint_area = hash[:parking_footprint_area]
74
+ @maximum_parking_height = hash[:maximum_parking_height]
75
+ @maximum_number_of_parking_stories = hash[:maximum_number_of_parking_stories]
76
+ @maximum_number_of_parking_stories_above_ground = hash[:maximum_number_of_parking_stories_above_ground]
77
+ @number_of_residential_units = hash[:number_of_residential_units]
78
+ @building_types = hash[:building_types]
79
+ @window_area = hash[:window_area]
80
+ @wall_area = hash[:wall_area]
81
+ @roof_area = hash[:roof_area]
82
+ @orientation = hash[:orientation]
83
+ @aspect_ratio = hash[:aspect_ratio]
84
+
85
+ # initialize class variables @@validator and @@schema
86
+ @@validator ||= Validator.new
87
+ @@schema ||= @@validator.schema
88
+ end
89
+
90
+ ##
91
+ # Assigns default values if values do not exist.
92
+ ##
93
+ def defaults
94
+ hash = {}
95
+ hash[:site_area] = nil
96
+ hash[:floor_area] = nil
97
+ hash[:conditioned_area] = nil
98
+ hash[:unconditioned_area] = nil
99
+ hash[:footprint_area] = nil
100
+ hash[:maximum_roof_height] = nil
101
+ hash[:maximum_number_of_stories] = nil
102
+ hash[:maximum_number_of_stories_above_ground] = nil
103
+ hash[:parking_area] = nil
104
+ hash[:number_of_parking_spaces] = nil
105
+ hash[:number_of_parking_spaces_charging] = nil
106
+ hash[:parking_footprint_area] = nil
107
+ hash[:maximum_parking_height] = nil
108
+ hash[:maximum_number_of_parking_stories] = nil
109
+ hash[:maximum_number_of_parking_stories_above_ground] = nil
110
+ hash[:number_of_residential_units] = nil
111
+ hash[:building_types] = [{ building_type: nil, maximum_occupancy: nil, floor_area: nil }]
112
+ hash[:window_area] = { north_window_area: nil, south_window_area: nil, east_window_area: nil, west_window_area: nil, total_window_area: nil }
113
+ hash[:wall_area] = { north_wall_area: nil, south_wall_area: nil, east_wall_area: nil, west_wall_area: nil, total_wall_area: nil }
114
+ hash[:roof_area] = { equipment_roof_area: nil, photovoltaic_roof_area: nil, available_roof_area: nil, total_roof_area: nil }
115
+ hash[:orientation] = nil
116
+ hash[:aspect_ratio] = nil
117
+ return hash
118
+ end
119
+
120
+ ##
121
+ # Convert to a Hash equivalent for JSON serialization.
122
+ ##
123
+ # - Exclude attributes with nil values.
124
+ # - Validate program hash properties against schema.
125
+ ##
126
+ def to_hash
127
+ result = {}
128
+ result[:site_area] = @site_area if @site_area
129
+ result[:floor_area] = @floor_area if @floor_area
130
+ result[:conditioned_area] = @conditioned_area if @conditioned_area
131
+ result[:unconditioned_area] = @unconditioned_area if @unconditioned_area
132
+ result[:footprint_area] = @footprint_area if @footprint_area
133
+ result[:maximum_roof_height] = @maximum_roof_height if @maximum_roof_height
134
+ result[:maximum_number_of_stories] = @maximum_number_of_stories if @maximum_number_of_stories
135
+ result[:maximum_number_of_stories_above_ground] = @maximum_number_of_stories_above_ground if @maximum_number_of_parking_stories_above_ground
136
+ result[:parking_area] = @parking_area if @parking_area
137
+ result[:number_of_parking_spaces] = @number_of_parking_spaces if @number_of_parking_spaces
138
+ result[:number_of_parking_spaces_charging] = @number_of_parking_spaces_charging if @number_of_parking_spaces_charging
139
+ result[:parking_footprint_area] = @parking_footprint_area if @parking_footprint_area
140
+ result[:maximum_parking_height] = @maximum_parking_height if @maximum_parking_height
141
+ result[:maximum_number_of_parking_stories] = @maximum_number_of_parking_stories if @maximum_number_of_parking_stories
142
+ result[:maximum_number_of_parking_stories_above_ground] = @maximum_number_of_parking_stories_above_ground if @maximum_number_of_parking_stories_above_ground
143
+ result[:number_of_residential_units] = @number_of_residential_units if @number_of_residential_units
144
+
145
+ if @building_types.any?
146
+ result[:building_types] = @building_types
147
+ @building_types.each do |bt|
148
+ bt.delete_if { |k, v| v.nil? } if bt
149
+ end
150
+ end
151
+
152
+ # result[:window_area] = @window_area if @window_area
153
+ window_area_hash = @window_area if @window_area
154
+ window_area_hash.delete_if { |k, v| v.nil? }
155
+ result[:window_area] = window_area_hash if @window_area
156
+
157
+ # result[:wall_area] = @wall_area if @wall_area
158
+ wall_area_hash = @wall_area if @wall_area
159
+ wall_area_hash.delete_if { |k, v| v.nil? }
160
+ result[:wall_area] = wall_area_hash if @wall_area
161
+
162
+ # result[:roof_area] = @roof_area if @roof_area
163
+ roof_area_hash = @roof_area if @roof_area
164
+ roof_area_hash.delete_if { |k, v| v.nil? }
165
+ result[:roof_area] = roof_area_hash if @roof_area
166
+
167
+ result[:orientation] = @orientation if @orientation
168
+ result[:aspect_ratio] = @aspect_ratio if @aspect_ratio
169
+
170
+ # validate program properties against schema
171
+ if @@validator.validate(@@schema[:definitions][:Program][:properties], result).any?
172
+ raise "program properties does not match schema: #{@@validator.validate(@@schema[:definitions][:Program][:properties], result)}"
173
+ end
174
+
175
+ return result
176
+ end
177
+
178
+ ##
179
+ # Return the maximum value from +existing_value+ and +new_value+.
180
+ ##
181
+ # [parameters:]
182
+ # +existing_value+ - _Float_ - A value corresponding to a Program attribute.
183
+ ##
184
+ # +new_value+ - _Float_ - A value corresponding to a Program attribute.
185
+ ##
186
+ def max_value(existing_value, new_value)
187
+ if existing_value && new_value
188
+ [existing_value, new_value].max
189
+ elsif new_value
190
+ existing_value = new_value
191
+ end
192
+ return existing_value
193
+ end
194
+
195
+ ##
196
+ # Adds up +existing_value+ and +new_values+ if not nill.
197
+ ##
198
+ # [parameters:]
199
+ # +existing_value+ - _Float_ - A value corresponding to a Program attribute.
200
+ ##
201
+ # +new_value+ - _Float_ - A value corresponding to a Program attribute.
202
+ ##
203
+ def add_values(existing_value, new_value)
204
+ if existing_value && new_value
205
+ existing_value += new_value
206
+ elsif new_value
207
+ existing_value = new_value
208
+ end
209
+ return existing_value
210
+ end
211
+
212
+ ##
213
+ # Merges program objects to each other by summing up values or taking the maximum value of the attributes.
214
+ ##
215
+ # [parameters:]
216
+ # +other+ - _Program_ - An object of Program class.
217
+ ##
218
+ # rubocop:disable Metrics/AbcSize # :nodoc:
219
+ def add_program(other)
220
+ @site_area = add_values(@site_area, other.site_area)
221
+
222
+ @floor_area = add_values(@floor_area, other.floor_area)
223
+ @conditioned_area = add_values(@conditioned_area, other.conditioned_area)
224
+ @unconditioned_area = add_values(@unconditioned_area, other.unconditioned_area)
225
+ @footprint_area = add_values(@footprint_area, other.footprint_area)
226
+ @maximum_roof_height = max_value(@maximum_roof_height, other.maximum_roof_height)
227
+ @maximum_number_of_stories = max_value(@maximum_number_of_stories, other.maximum_number_of_stories)
228
+ @maximum_number_of_stories_above_ground = max_value(@maximum_number_of_stories_above_ground, other.maximum_number_of_stories_above_ground)
229
+ @parking_area = add_values(@parking_area, other.parking_area)
230
+ @number_of_parking_spaces = add_values(@number_of_parking_spaces, other.number_of_parking_spaces)
231
+ @number_of_parking_spaces_charging = add_values(@number_of_parking_spaces_charging, other.number_of_parking_spaces_charging)
232
+ @parking_footprint_area = add_values(@parkig_footprint_area, other.parking_footprint_area)
233
+ @maximum_parking_height = max_value(@maximum_parking_height, other.maximum_parking_height)
234
+ @maximum_number_of_parking_stories = max_value(@maximum_number_of_parking_stories, other.maximum_number_of_parking_stories)
235
+ @maximum_number_of_parking_stories_above_ground = max_value(maximum_number_of_parking_stories_above_ground, other.maximum_number_of_parking_stories_above_ground)
236
+ @number_of_residential_units = add_values(@number_of_residential_units, other.number_of_residential_units)
237
+
238
+ @building_types = other.building_types
239
+
240
+ @window_area[:north_window_area] = add_values(@window_area[:north_window_area], other.window_area[:north_window_area])
241
+ @window_area[:south_window_area] = add_values(@window_area[:south_window_area], other.window_area[:south_window_area])
242
+ @window_area[:east_window_area] = add_values(@window_area[:east_window_area], other.window_area[:east_window_area])
243
+ @window_area[:west_window_area] = add_values(@window_area[:west_window_area], other.window_area[:west_window_area])
244
+ @window_area[:total_window_area] = add_values(@window_area[:total_window_area], other.window_area[:total_window_area])
245
+
246
+ @wall_area[:north_wall_area] = add_values(@wall_area[:north_wall_area], other.wall_area[:north_wall_area])
247
+ @wall_area[:south_wall_area] = add_values(@wall_area[:south_wall_area], other.wall_area[:south_wall_area])
248
+ @wall_area[:east_wall_area] = add_values(@wall_area[:east_wall_area], other.wall_area[:east_wall_area])
249
+ @wall_area[:west_wall_area] = add_values(@wall_area[:west_wall_area], other.wall_area[:west_wall_area])
250
+ @wall_area[:total_wall_area] = add_values(@wall_area[:total_wall_area], other.wall_area[:total_wall_area])
251
+
252
+ @roof_area[:equipment_roof_area] = add_values(@roof_area[:equipment_roof_area], other.roof_area[:equipment_roof_area])
253
+ @roof_area[:photovoltaic_roof_area] = add_values(@roof_area[:photovoltaic_roof_area], other.roof_area[:photovoltaic_roof_area])
254
+ @roof_area[:available_roof_area] = add_values(@roof_area[:available_roof_area], other.roof_area[:available_roof_area])
255
+ @roof_area[:total_roof_area] = add_values(@roof_area[:total_roof_area], other.roof_area[:total_roof_area])
256
+ end
257
+ # rubocop:enable Metrics/AbcSize
258
+ end
259
+ end
260
+ end
261
+ end