urbanopt-geojson 0.2.0.pre2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +61 -0
  4. data/Gemfile +0 -3
  5. data/Rakefile +1 -1
  6. data/docs/package-lock.json +15 -15
  7. data/docs/package.json +3 -1
  8. data/lib/measures/.rubocop.yml +1 -1
  9. data/lib/measures/urban_geometry_creation/README.md +1 -1
  10. data/lib/measures/urban_geometry_creation/measure.rb +4 -0
  11. data/lib/measures/urban_geometry_creation/measure.xml +17 -20
  12. data/lib/measures/urban_geometry_creation_zoning/README.md +1 -1
  13. data/lib/measures/urban_geometry_creation_zoning/measure.rb +6 -3
  14. data/lib/measures/urban_geometry_creation_zoning/measure.xml +15 -18
  15. data/lib/urbanopt/geojson/building.rb +22 -19
  16. data/lib/urbanopt/geojson/feature.rb +37 -1
  17. data/lib/urbanopt/geojson/geo_file.rb +39 -39
  18. data/lib/urbanopt/geojson/helper.rb +12 -5
  19. data/lib/urbanopt/geojson/mapper_classes.rb +46 -46
  20. data/lib/urbanopt/geojson/model.rb +2 -3
  21. data/lib/urbanopt/geojson/schema/building_properties.json +244 -77
  22. data/lib/urbanopt/geojson/schema/electrical_connector_properties.json +8 -8
  23. data/lib/urbanopt/geojson/schema/electrical_junction_properties.json +4 -5
  24. data/lib/urbanopt/geojson/schema/thermal_connector_properties.json +1 -1
  25. data/lib/urbanopt/geojson/schema/thermal_junction_properties.json +1 -1
  26. data/lib/urbanopt/geojson/update_areas.rb +1 -1
  27. data/lib/urbanopt/geojson/validate_geojson.rb +2 -0
  28. data/lib/urbanopt/geojson/version.rb +1 -1
  29. data/lib/urbanopt/geojson/workflows/building.osw.out +4 -4
  30. data/urbanopt-geojson-gem.gemspec +9 -13
  31. metadata +21 -41
  32. data/lib/change_log.rb +0 -147
  33. data/lib/measures/urban_geometry_creation/tests/nrel_stm_footprints.geojson +0 -3238
  34. data/lib/measures/urban_geometry_creation/tests/shadowed_tests.rb +0 -80
  35. data/lib/measures/urban_geometry_creation/tests/urban_geometry_creation_test.rb +0 -139
  36. data/lib/measures/urban_geometry_creation_zoning/tests/nrel_stm_footprints.geojson +0 -3238
  37. data/lib/measures/urban_geometry_creation_zoning/tests/urban_geometry_creation_zoning_test.rb +0 -139
@@ -44,8 +44,11 @@ module URBANopt
44
44
  @feature_json = validate_feat(feature)
45
45
  end
46
46
 
47
+ # rubocop:disable Style/MethodMissing
47
48
  def method_missing(name, *args, &blk)
49
+ # rubocop:enable Style/MethodMissing
48
50
  if @feature_json[:properties].keys.map(&:to_sym).include? name.to_sym
51
+ # rubocop:enable Style/GuardClause
49
52
  return @feature_json[:properties][name.to_sym]
50
53
  else
51
54
  super
@@ -164,13 +167,46 @@ module URBANopt
164
167
  return OpenStudio::PointLatLon.new(min_lat, min_lon, 0)
165
168
  end
166
169
 
170
+ ##
171
+ # Used to determine the centroid for the feature's coordinates.
172
+ #
173
+ # [Parameters]
174
+ # * +vertices+ - The first set polygon vertices in the array of feature coordinates.
175
+ def find_feature_center(vertices)
176
+ number_of_locations = vertices.length
177
+
178
+ return vertices.first if number_of_locations == 1
179
+
180
+ x = y = z = 0.0
181
+ vertices.each do |station|
182
+ latitude = station[0] * Math::PI / 180
183
+ longitude = station[1] * Math::PI / 180
184
+
185
+ x += Math.cos(latitude) * Math.cos(longitude)
186
+ y += Math.cos(latitude) * Math.sin(longitude)
187
+ z += Math.sin(latitude)
188
+ end
189
+
190
+ x /= number_of_locations
191
+ y /= number_of_locations
192
+ z /= number_of_locations
193
+
194
+ central_longitude = Math.atan2(y, x)
195
+ central_square_root = Math.sqrt(x * x + y * y)
196
+ central_latitude = Math.atan2(z, central_square_root)
197
+
198
+ [central_latitude * 180 / Math::PI,
199
+ central_longitude * 180 / Math::PI]
200
+ end
201
+
167
202
  private
168
203
 
169
204
  ##
170
205
  # Used to validate the feature by checking +feature_id+ , +geometry+, +properties+
171
206
  # and +geometry_type+ .
172
-
207
+ # rubocop:disable Style/CommentedKeyword
173
208
  def validate_feat(feature) #:doc:
209
+ # rubocop:enable Style/CommentedKeyword
174
210
  if feature.nil? || feature.empty?
175
211
  raise("Feature '#{feature_id}' could not be found")
176
212
  return false
@@ -80,7 +80,7 @@ module URBANopt
80
80
 
81
81
  # initialize @@logger
82
82
  @@logger ||= URBANopt::GeoJSON.logger
83
-
83
+
84
84
  # validate each feature against schema
85
85
  geojson_file[:features].each do |feature|
86
86
  properties = feature[:properties]
@@ -93,16 +93,16 @@ module URBANopt
93
93
  # Incase detailed_model_filename present check for fewer properties
94
94
  if feature[:properties][:detailed_model_filename]
95
95
  if feature[:properties][:id].nil?
96
- raise("No id found for Building Feature")
96
+ raise('No id found for Building Feature')
97
97
  end
98
98
  if feature[:properties][:name].nil?
99
- raise("No name found for Building Feature")
99
+ raise('No name found for Building Feature')
100
100
  end
101
101
  if feature[:properties][:number_of_stories].nil?
102
102
  @@logger.warn("Number of stories is required to calculate shading using the UrbanGeometryCreation measure...ignoring #{feature[:properties][:id]} in shading calculations")
103
103
  end
104
104
  feature[:additionalProperties] = true
105
- # Else validate for all required properties in the schema
105
+ # Else validate for all required properties in the schema
106
106
  else
107
107
  errors = validate(@@building_schema, properties)
108
108
  end
@@ -114,16 +114,15 @@ module URBANopt
114
114
  errors = validate(@@electrical_junction_schema, properties)
115
115
  when 'ElectricalConnector'
116
116
  errors = validate(@@electrical_connector_schema, properties)
117
- when 'ElectricalJunction'
117
+ when 'ThermalJunction'
118
118
  errors = validate(@@thermal_junction_schema, properties)
119
119
  when 'ThermalConnector'
120
120
  errors = validate(@@thermal_connector_schema, properties)
121
121
  end
122
-
122
+
123
123
  unless errors.empty?
124
- raise ("#{type} does not adhere to schema: \n #{errors.join('\n ')}")
124
+ raise "#{type} does not adhere to schema: \n #{errors.join('\n ')}"
125
125
  end
126
-
127
126
  end
128
127
  return new(geojson_file, path)
129
128
  end
@@ -163,6 +162,7 @@ module URBANopt
163
162
  # merge site origin properties
164
163
  f = merge_site_properties(f)
165
164
  if f[:properties][:type] == 'Building'
165
+ # rubocop:enable Style/GuardClause
166
166
  return URBANopt::GeoJSON::Building.new(f)
167
167
  elsif f[:properties] && f[:properties][:type] == 'District System'
168
168
  return URBANopt::GeoJSON::DistrictSystem.new(f)
@@ -172,47 +172,48 @@ module URBANopt
172
172
  return nil
173
173
  end
174
174
 
175
- ##
176
- # Merge Site Properties in Feature. Returns feature with site properties added to its properties section. Does not overwrite existing properties.
177
- #
175
+ ##
176
+ # Merge Site Properties in Feature. Returns feature with site properties added to its properties section. Does not overwrite existing properties.
177
+ #
178
178
  # [Parameters]
179
179
  # +feature+ - _Type:Hash_ - feature object.
180
180
  def merge_site_properties(feature)
181
- site_origins = @geojson_file[:features].select {|f| f[:properties][:type] == 'Site Origin'}
182
- if site_origins.size > 0
183
- site_origin = site_origins[0]
184
- # site origin found, do some merging
185
- # this maps site properties to building/district system properties.
186
- add_props = [
187
- {site: :surface_elevation, feature: :surface_elevation},
188
- {site: :timesteps_per_hour, feature: :timesteps_per_hour},
189
- {site: :begin_date, feature: :begin_date},
190
- {site: :end_date, feature: :end_date},
191
- {site: :cec_climate_zone, feature: :cec_climate_zone},
192
- {site: :climate_zone, feature: :climate_zone},
193
- {site: :default_template, feature: :template},
194
- {site: :weather_filename, feature: :weather_filename},
195
- {site: :tariff_filename, feature: :tariff_filename}
196
- ]
181
+ project = {}
182
+ if @geojson_file.key?(:project)
183
+ project = @geojson_file[:project]
184
+ end
197
185
 
198
- add_props.each do |prop|
199
- if site_origin[:properties].key?(prop[:site]) and site_origin[:properties][prop[:site]]
200
- # property exists in site
201
- if !feature[:properties].key?(prop[:feature]) or feature[:properties][prop[:feature]].nil? or feature[:properties][prop[:feature]].empty?
202
- # property does not exist in feature or is nil: add site property (don't overwrite)
203
- feature[:properties][prop[:feature]] = site_origin[:properties][prop[:site]]
204
- end
186
+ # this maps site properties to building/district system properties.
187
+ add_props = [
188
+ { site: :surface_elevation, feature: :surface_elevation },
189
+ { site: :timesteps_per_hour, feature: :timesteps_per_hour },
190
+ { site: :begin_date, feature: :begin_date },
191
+ { site: :end_date, feature: :end_date },
192
+ { site: :cec_climate_zone, feature: :cec_climate_zone },
193
+ { site: :climate_zone, feature: :climate_zone },
194
+ { site: :default_template, feature: :template },
195
+ { site: :weather_filename, feature: :weather_filename },
196
+ { site: :tariff_filename, feature: :tariff_filename }
197
+ ]
198
+
199
+ add_props.each do |prop|
200
+ if project.key?(prop[:site]) && project[prop[:site]]
201
+ # property exists in site
202
+ if !feature[:properties].key?(prop[:feature]) || feature[:properties][prop[:feature]].nil? || feature[:properties][prop[:feature]].empty?
203
+ # property does not exist in feature or is nil: add site property (don't overwrite)
204
+ feature[:properties][prop[:feature]] = project[prop[:site]]
205
205
  end
206
206
  end
207
207
  end
208
+
208
209
  return feature
209
210
  end
210
211
 
211
212
  ##
212
- # Validate GeoJSON against schema
213
+ # Validate GeoJSON against schema.
213
214
  #
214
215
  # [Parameters]
215
- # * +data+ - + - _Type:Hash_ - Input GeoJSON file
216
+ # * +data+ - + - _Type:Hash_ - Input GeoJSON file
216
217
  def self.validate(schema_json, data)
217
218
  errors = JSON::Validator.fully_validate(schema_json, data, errors_as_objects: true)
218
219
  return errors
@@ -242,7 +243,7 @@ module URBANopt
242
243
  end
243
244
  return result
244
245
  end
245
-
246
+
246
247
  def self.get_district_system_schema(strict)
247
248
  result = nil
248
249
  File.open(File.dirname(__FILE__) + '/schema/district_system_properties.json') do |f|
@@ -255,7 +256,7 @@ module URBANopt
255
256
  end
256
257
  return result
257
258
  end
258
-
259
+
259
260
  def self.get_region_schema(strict)
260
261
  result = nil
261
262
  File.open(File.dirname(__FILE__) + '/schema/region_properties.json') do |f|
@@ -330,7 +331,6 @@ module URBANopt
330
331
  @@electrical_junction_schema = get_electrical_junction_schema(strict)
331
332
  @@thermal_connector_schema = get_thermal_connector_schema(strict)
332
333
  @@thermal_junction_schema = get_thermal_junction_schema(strict)
333
-
334
334
  end
335
335
  end
336
336
  end
@@ -154,7 +154,7 @@ module URBANopt
154
154
  stories.each_index do |i|
155
155
  space_type = nil
156
156
  space = stories[i].spaces.first
157
- if space && space.spaceType.is_initialized
157
+ if space&.spaceType&.is_initialized
158
158
  space_type = space.spaceType.get
159
159
  else
160
160
  space_type = OpenStudio::Model::SpaceType.new(model)
@@ -216,13 +216,13 @@ module URBANopt
216
216
  #
217
217
  # [Parameters]
218
218
  # * +building+ - _Type:URBANopt::GeoJSON::Building_ - The core building that other buildings will be referenced.
219
- # * +other_building_type+ - _Type:String_ - Describes the surrounding buildings.
219
+ # * +other_building_type+ - _Type:String_ - Describes the surrounding buildings.
220
220
  # * +other_buildings+ - _Type:URBANopt::GeoJSON::FeatureCollection_ - List of surrounding buildings to include (self will be ignored if present in list).
221
221
  # * +model+ - _Type:OpenStudio::Model::Model_ - An instance of an OpenStudio Model.
222
222
  # * +origin_lat_lon+ - _Type:Float_ - An instance of +OpenStudio::PointLatLon+ indicating the latitude and longitude of the origin.
223
223
  # * +runner+ - _Type:String_ - An instance of +Openstudio::Measure::OSRunner+ for the measure run.
224
224
  # * +zoning+ - _Type:Boolean_ - Value is +true+ if utilizing detailed zoning, else
225
- # +false+. Zoning is set to false by default.
225
+ # +false+. Zoning is set to false by default.
226
226
  def self.process_other_buildings(building, other_building_type, other_buildings, model, origin_lat_lon, runner, zoning = false)
227
227
  # Empty array to store the new OpenStudio model spaces that need to be converted to shading objects
228
228
  feature_points = building.feature_points(origin_lat_lon, runner, zoning)
@@ -243,6 +243,7 @@ module URBANopt
243
243
  if number_of_stories_above_ground.nil?
244
244
  number_of_stories_above_ground = number_of_stories
245
245
  number_of_stories_below_ground = 0
246
+
246
247
  else
247
248
  number_of_stories_below_ground = number_of_stories - number_of_stories_above_ground
248
249
  end
@@ -251,11 +252,18 @@ module URBANopt
251
252
  if number_of_stories_above_ground && number_of_stories_above_ground > 0 && maximum_roof_height
252
253
  floor_to_floor_height = maximum_roof_height / number_of_stories_above_ground
253
254
  end
255
+
256
+ # check that feature has a # stories
257
+ if number_of_stories_above_ground.nil?
258
+ runner.registerWarning("[geojson process_other_buildings] Unable to include feature #{other_building[:properties][:id]} in shading calculations: no 'number of stories' data")
259
+ end
260
+ next if number_of_stories_above_ground.nil?
261
+
254
262
  other_height = number_of_stories_above_ground * floor_to_floor_height
255
263
  # find the polygon of the other_building by passing it to the get_multi_polygons method
256
264
  other_building_points = building.other_points(other_building, other_height, origin_lat_lon, runner, zoning)
257
265
  shadowed = URBANopt::GeoJSON::Helper.is_shadowed(feature_points, other_building_points, origin_lat_lon)
258
- next unless shadowed
266
+ next unless shadowed
259
267
  new_building = building.create_other_building(:space_per_building, model, origin_lat_lon, runner, zoning, other_building)
260
268
  if new_building.nil? || new_building.empty?
261
269
  runner.registerWarning("Failed to create spaces for other building '#{name}'")
@@ -264,7 +272,6 @@ module URBANopt
264
272
 
265
273
  elsif other_building_type == 'None'
266
274
  end
267
-
268
275
  end
269
276
  return other_spaces
270
277
  end
@@ -32,54 +32,54 @@ require 'urbanopt/scenario'
32
32
  require 'json'
33
33
 
34
34
  module URBANopt
35
- module GeoJSON
36
- class Mapper < MapperBase
37
- @@instance_lock = Mutex.new
38
- @@osw = nil
35
+ module GeoJSON
36
+ class Mapper < MapperBase
37
+ @@instance_lock = Mutex.new
38
+ @@osw = nil
39
39
 
40
- ##
41
- # This class inherits from the +MapperBase+ .
42
- # Used to perform initializing functions, used to define the osw_path for
43
- # baseline.osw for the URBANopt GeoJSON example project and the weather file.
40
+ ##
41
+ # This class inherits from the +MapperBase+ .
42
+ # Used to perform initializing functions, used to define the osw_path for
43
+ # baseline.osw for the URBANopt GeoJSON example project and the weather file.
44
44
 
45
- def initialize()
46
- @@instance_lock.synchronize do
47
- if @@osw.nil?
48
- osw_path = File.join(File.dirname(__FILE__), 'baseline.osw')
49
- File.open(osw_path, 'r') do |file|
50
- @@osw = JSON.parse(file.read, symbolize_names: true)
51
- end
52
- @@osw[:file_paths] << File.join(File.dirname(__FILE__), '../weather/')
53
- @@osw = OpenStudio::Extension.configure_osw(@@osw)
54
- end
55
- end
56
-
57
- ##
58
- # Creates an OpenStudio Workflow file for a given ScenarioBase object,
59
- # feature id and feature name.
60
- #
61
- # [Parameters]
62
- # * +scenario+ - _Type:String_ - Used to define the Scenario for the osw.
63
-
64
- # * +feature_id+ - _Type:String/Number_ - Used to define the feature_id for
65
- # which the osw is implemented.
66
- #
67
- # * +feature_name+ - _Type:String_ - The name of the feature.
68
- def create_osw(scenario, feature_id, feature_name)
69
- # get the feature from the scenario's feature_file #:nodoc:
70
- feature_file = scenario.feature_file
71
- feature = feature_file.get_feature_by_id(feature_id)
72
-
73
- raise "Cannot find feature '#{feature_id}' in '#{scenario.geometry_file}'" if feature.nil?
74
-
75
- # deep clone of @@osw before we configure it #:nodoc:
76
- osw = Marshal.load(Marshal.dump(@@osw))
77
-
78
- osw[:name] = feature_name
79
- osw[:description] = feature_name
80
-
81
- return osw
45
+ def initialize
46
+ @@instance_lock.synchronize do
47
+ if @@osw.nil?
48
+ osw_path = File.join(File.dirname(__FILE__), 'baseline.osw')
49
+ File.open(osw_path, 'r') do |file|
50
+ @@osw = JSON.parse(file.read, symbolize_names: true)
82
51
  end
52
+ @@osw[:file_paths] << File.join(File.dirname(__FILE__), '../weather/')
53
+ @@osw = OpenStudio::Extension.configure_osw(@@osw)
54
+ end
83
55
  end
56
+
57
+ ##
58
+ # Creates an OpenStudio Workflow file for a given ScenarioBase object,
59
+ # feature id and feature name.
60
+ #
61
+ # [Parameters]
62
+ # * +scenario+ - _Type:String_ - Used to define the Scenario for the osw.
63
+ # * +feature_id+ - _Type:String/Number_ - Used to define the feature_id for
64
+ # which the osw is implemented.
65
+ #
66
+ # * +feature_name+ - _Type:String_ - The name of the feature.
67
+ # rubocop:disable Lint/NestedMethodDefinition
68
+ def create_osw(scenario, feature_id, feature_name)
69
+ # rubocop:enable Lint/NestedMethodDefinition
70
+ # get the feature from the scenario's feature_file #:nodoc:
71
+ feature_file = scenario.feature_file
72
+ feature = feature_file.get_feature_by_id(feature_id)
73
+ raise "Cannot find feature '#{feature_id}' in '#{scenario.geometry_file}'" if feature.nil?
74
+ # deep clone of @@osw before we configure it #:nodoc:
75
+ osw = Marshal.load(Marshal.dump(@@osw))
76
+ osw[:name] = feature_name
77
+ osw[:description] = feature_name
78
+ end
79
+ # rubocop:disable Lint/ReturnInVoidContext
80
+ return osw
81
+ # rubocop:enable Lint/ReturnInVoidContext
82
+ end
84
83
  end
85
- end
84
+ end
85
+ end
@@ -65,17 +65,16 @@ module URBANopt
65
65
  # * +runner+ - _Type:String_ - Measure run's instance of +OpenStudio::Measure::OSRunner+ .
66
66
  def self.change_adjacent_surfaces_to_adiabatic(model, runner)
67
67
  runner.registerInfo('Changing adjacent surfaces to adiabatic')
68
- model.getSurfaces.each do |surface|
68
+ model.getSurfaces.sort.each do |surface|
69
69
  adjacent_surface = surface.adjacentSurface
70
70
  if !adjacent_surface.empty?
71
71
  surface_construction = surface.construction
72
72
  if !surface_construction.empty?
73
73
  surface.setConstruction(surface_construction.get)
74
74
  end
75
- surface.setOutsideBoundaryCondition('Adiabatic')
76
-
77
75
  adjacent_surface_construction = adjacent_surface.get.construction
78
76
  if !adjacent_surface_construction.empty?
77
+ surface.setOutsideBoundaryCondition('Adiabatic')
79
78
  adjacent_surface.get.setConstruction(adjacent_surface_construction.get)
80
79
  end
81
80
  adjacent_surface.get.setOutsideBoundaryCondition('Adiabatic')
@@ -111,6 +111,12 @@
111
111
  "Hip"
112
112
  ]
113
113
  },
114
+ "foundation_type": {
115
+ "$ref": "#/definitions/foundationType"
116
+ },
117
+ "attic_type": {
118
+ "$ref": "#/definitions/atticType"
119
+ },
114
120
  "footprint_area": {
115
121
  "description": "Area of the footprint (ft^2). Calculated on export.",
116
122
  "type": "number"
@@ -132,6 +138,9 @@
132
138
  "system_type": {
133
139
  "$ref": "#/definitions/systemType"
134
140
  },
141
+ "heating_system_fuel_type": {
142
+ "$ref": "#/definitions/heatingSystemFuelType"
143
+ },
135
144
  "weekday_start_time": {
136
145
  "description": "Weekday operating hours start time in 08:30 format, using 24-hr clock. Leave blank to use default. Should this be part of a mixed type struct?",
137
146
  "type": "string"
@@ -185,7 +194,11 @@
185
194
  "maximum": 100
186
195
  },
187
196
  "number_of_residential_units": {
188
- "description": "Total number of residential units in the building. Required for residential buildings or mixed-use buildings with residential use types.",
197
+ "description": "Total number of residential units in the building. Required for single-family attached or multifamily residential buildings.",
198
+ "type": "integer"
199
+ },
200
+ "number_of_bedrooms": {
201
+ "description": "Total number of bedrooms in the building. Required for residential buildings. Must be divisible by the number of residential units.",
189
202
  "type": "integer"
190
203
  },
191
204
  "exterior_lighting_zone": {
@@ -214,14 +227,113 @@
214
227
  "description": "Arbitrary user data"
215
228
  }
216
229
  },
217
- "required": [
218
- "id",
219
- "type",
220
- "name",
221
- "floor_area",
222
- "number_of_stories",
223
- "footprint_area",
224
- "building_type"
230
+ "oneOf": [
231
+ {
232
+ "properties": {
233
+ "building_type": {
234
+ "enum": [
235
+ "Single-Family Detached"
236
+ ]
237
+ }
238
+ },
239
+ "required": [
240
+ "id",
241
+ "type",
242
+ "name",
243
+ "floor_area",
244
+ "number_of_stories",
245
+ "footprint_area",
246
+ "building_type",
247
+ "number_of_bedrooms",
248
+ "foundation_type",
249
+ "attic_type"
250
+ ]
251
+ },
252
+ {
253
+ "properties": {
254
+ "building_type": {
255
+ "enum": [
256
+ "Single-Family Attached"
257
+ ]
258
+ }
259
+ },
260
+ "required": [
261
+ "id",
262
+ "type",
263
+ "name",
264
+ "floor_area",
265
+ "number_of_stories",
266
+ "footprint_area",
267
+ "building_type",
268
+ "number_of_residential_units",
269
+ "number_of_bedrooms",
270
+ "foundation_type",
271
+ "attic_type"
272
+ ]
273
+ },
274
+ {
275
+ "properties": {
276
+ "building_type": {
277
+ "enum": [
278
+ "Multifamily"
279
+ ]
280
+ }
281
+ },
282
+ "required": [
283
+ "id",
284
+ "type",
285
+ "name",
286
+ "floor_area",
287
+ "number_of_stories",
288
+ "footprint_area",
289
+ "building_type",
290
+ "number_of_residential_units",
291
+ "number_of_bedrooms",
292
+ "foundation_type"
293
+ ]
294
+ },
295
+ {
296
+ "properties": {
297
+ "building_type": {
298
+ "enum": [
299
+ "Single-Family",
300
+ "Multifamily (2 to 4 units)",
301
+ "Multifamily (5 or more units)",
302
+ "Vacant",
303
+ "Office",
304
+ "Laboratory",
305
+ "Nonrefrigerated warehouse",
306
+ "Food sales",
307
+ "Public order and safety",
308
+ "Outpatient health care",
309
+ "Refrigerated warehouse",
310
+ "Religious worship",
311
+ "Public assembly",
312
+ "Education",
313
+ "Food service",
314
+ "Inpatient health care",
315
+ "Nursing",
316
+ "Lodging",
317
+ "Strip shopping mall",
318
+ "Enclosed mall",
319
+ "Retail other than mall",
320
+ "Service",
321
+ "Mixed use",
322
+ "Uncovered Parking",
323
+ "Covered Parking"
324
+ ]
325
+ }
326
+ },
327
+ "required": [
328
+ "id",
329
+ "type",
330
+ "name",
331
+ "floor_area",
332
+ "number_of_stories",
333
+ "footprint_area",
334
+ "building_type"
335
+ ]
336
+ }
225
337
  ],
226
338
  "additionalProperties": true,
227
339
  "definitions": {
@@ -229,10 +341,12 @@
229
341
  "description": "Primary building space type.",
230
342
  "type": "string",
231
343
  "enum": [
344
+ "Single-Family Detached",
345
+ "Single-Family Attached",
346
+ "Multifamily",
232
347
  "Single-Family",
233
348
  "Multifamily (2 to 4 units)",
234
349
  "Multifamily (5 or more units)",
235
- "Mobile Home",
236
350
  "Vacant",
237
351
  "Office",
238
352
  "Laboratory",
@@ -262,85 +376,116 @@
262
376
  "description": "Building HVAC system type. Should this be part of a mixed type struct?",
263
377
  "type": "string",
264
378
  "enum": [
265
- "Ideal Air Loads",
266
- "PTAC with hot water heat",
267
- "PTAC with hot water heat with central air source heat pump",
268
- "PTAC with gas coil heat",
269
- "PTAC with electric baseboard heat",
379
+ "PTAC with baseboard electric",
380
+ "PTAC with baseboard gas boiler",
381
+ "PTAC with baseboard district hot water",
382
+ "PTAC with gas unit heaters",
383
+ "PTAC with electric coil",
384
+ "PTAC with gas coil",
385
+ "PTAC with gas boiler",
270
386
  "PTAC with no heat",
271
- "PTAC with district hot water heat",
272
- "PTAC with central air source heat pump heat",
387
+ "PTAC with district hot water",
388
+ "PTAC with central air source heat pump",
273
389
  "PTHP",
274
- "PSZ-AC with gas coil heat",
275
- "PSZ-AC with electric baseboard heat",
390
+ "PSZ-AC with gas coil",
391
+ "PSZ-AC with baseboard electric",
276
392
  "PSZ-AC with no heat",
277
- "PSZ-AC with district hot water heat",
278
- "PSZ-AC with central air source heat pump heat",
393
+ "PSZ-AC with district hot water",
394
+ "PSZ-AC with central air source heat pump",
279
395
  "PSZ-HP",
280
396
  "Fan coil district chilled water with no heat",
281
- "Fan coil district chilled water and boiler",
282
- "Fan coil district chilled water and central air source heat pump",
283
- "Fan coil district chilled water unit heaters",
284
- "Fan coil district chilled water electric baseboard heat",
285
- "Fan coil district hot and chilled water",
286
- "Fan coil district hot water and chiller",
287
- "Fan coil district hot water and air-cooled chiller",
288
- "Fan coil chiller and boiler",
289
- "Fan coil air-cooled chiller and boiler",
290
- "Fan coil chiller and central air source heat pump",
291
- "Fan coil air-cooled chiller and central air source heat pump",
397
+ "Fan coil district chilled water with boiler",
398
+ "Fan coil district chilled water with central air source heat pump",
399
+ "Fan coil district chilled water with gas unit heaters",
400
+ "Fan coil district chilled water with baseboard electric",
401
+ "Fan coil district chilled water with district hot water",
402
+ "Fan coil chiller with district hot water",
403
+ "Fan coil air-cooled chiller with district hot water",
404
+ "Fan coil chiller with boiler",
405
+ "Fan coil air-cooled chiller with boiler",
406
+ "Fan coil chiller with central air source heat pump",
407
+ "Fan coil air-cooled chiller with central air source heat pump",
292
408
  "Fan coil chiller with no heat",
293
409
  "DOAS with fan coil district chilled water with no heat",
294
410
  "DOAS with fan coil district chilled water and boiler",
295
- "DOAS with fan coil district chilled water and central air source heat pump",
296
- "DOAS with fan coil district chilled water unit heaters",
297
- "DOAS with fan coil district chilled water electric baseboard heat",
298
- "DOAS with fan coil district hot and chilled water",
299
- "DOAS with fan coil district hot water and chiller",
300
- "DOAS with fan coil district hot water and air-cooled chiller",
301
- "DOAS with fan coil chiller and boiler",
302
- "DOAS with fan coil air-cooled chiller and boiler",
303
- "DOAS with fan coil chiller and central air source heat pump",
304
- "DOAS with fan coil air-cooled chiller and central air source heat pump",
411
+ "DOAS with fan coil district chilled water with central air source heat pump",
412
+ "DOAS with fan coil district chilled water with gas unit heaters",
413
+ "DOAS with fan coil district chilled water with baseboard electric",
414
+ "DOAS with fan coil district chilled water with district hot water",
415
+ "DOAS with fan coil chiller with district hot water",
416
+ "DOAS with fan coil air-cooled chiller with district hot water",
417
+ "DOAS with fan coil air-cooled chiller with boiler",
418
+ "DOAS with fan coil chiller with central air source heat pump",
419
+ "DOAS with fan coil air-cooled chiller with central air source heat pump",
305
420
  "DOAS with fan coil chiller with no heat",
306
- "VRF with DOAS",
307
- "Ground Source Heat Pumps with DOAS",
308
- "Baseboard district hot water heat",
309
- "Baseboard district hot water heat with direct evap coolers",
310
- "Baseboard electric heat",
311
- "Baseboard electric heat with direct evap coolers",
312
- "Baseboard hot water heat",
313
- "Baseboard hot water heat with direct evap coolers",
421
+ "DOAS with VRF",
422
+ "VRF",
423
+ "DOAS with water source heat pumps with ground source heat pump",
424
+ "Forced air furnace",
425
+ "Baseboard district hot water",
426
+ "Baseboard electric",
427
+ "Baseboard gas boiler",
428
+ "Baseboard central air source heat pump",
314
429
  "Window AC with no heat",
315
430
  "Window AC with forced air furnace",
316
- "Window AC with district hot water baseboard heat",
317
- "Window AC with hot water baseboard heat",
318
- "Window AC with electric baseboard heat",
431
+ "Window AC with baseboard district hot water",
432
+ "Window AC with baseboard electric",
319
433
  "Window AC with unit heaters",
320
- "Direct evap coolers",
321
- "Direct evap coolers with unit heaters",
322
- "Unit heaters",
323
- "Heat pump heat with no cooling",
324
- "Heat pump heat with direct evap cooler",
325
- "VAV with reheat",
326
- "VAV with reheat central air source heat pump",
327
- "VAV with PFP boxes",
328
- "VAV with gas reheat",
329
- "VAV with zone unit heaters",
330
- "VAV with electric baseboard heat",
331
- "VAV cool with zone heat pump heat",
332
- "PVAV with reheat",
333
- "PVAV with reheat with central air source heat pump",
434
+ "Window AC with baseboard gas boiler",
435
+ "Window AC with baseboard central air source heat pump",
436
+ "Direct evap coolers with baseboard district hot water",
437
+ "Direct evap coolers with baseboard electric",
438
+ "Direct evap coolers with baseboard gas boiler",
439
+ "Direct evap coolers with baseboard central air source heat pump",
440
+ "Direct evap coolers with no heat",
441
+ "Direct evap coolers with gas unit heaters",
442
+ "Direct evap coolers with forced air furnace",
443
+ "Gas unit heaters",
444
+ "VAV chiller with gas boiler reheat",
445
+ "VAV chiller with gas coil reheat",
446
+ "VAV chiller with central air source heat pump reheat",
447
+ "VAV chiller with PFP boxes",
448
+ "VAV air-cooled chiller with gas boiler reheat",
449
+ "VAV air-cooled chiller with central air source heat pump reheat",
450
+ "VAV air-cooled chiller with district hot water reheat",
451
+ "VAV air-cooled chiller with gas coil reheat",
452
+ "VAV air-cooled chiller with no reheat with gas unit heaters",
453
+ "VAV district chilled water with gas boiler reheat",
454
+ "VAV district chilled water with central air source heat pump reheat",
455
+ "VAV district chilled water with no reheat with zone heat pump",
456
+ "VAV chiller with no reheat with baseboard electric",
457
+ "VAV air-cooled chiller with no reheat with zone heat pump",
458
+ "VAV district chilled water with district hot water reheat",
459
+ "VAV district chilled water with gas coil reheat",
460
+ "PVAV with gas heat with electric reheat",
461
+ "PVAV with central air source heat pump reheat",
334
462
  "PVAV with PFP boxes",
335
- "Residential forced air",
336
- "Residential forced air cooling hot water baseboard heat",
337
- "Residential forced air with district hot water",
338
- "Residential heat pump",
339
- "Forced air furnace",
340
- "Forced air furnace district chilled water fan coil",
341
- "Forced air furnace direct evap cooler",
342
- "Residential AC with no heat",
343
- "Residential AC with electric baseboard heat"
463
+ "Residential - electric resistance and no cooling",
464
+ "Residential - electric resistance and central air conditioner",
465
+ "Residential - electric resistance and room air conditioner",
466
+ "Residential - electric resistance and evaporative cooler",
467
+ "Residential - furnace and no cooling",
468
+ "Residential - furnace and central air conditioner",
469
+ "Residential - furnace and room air conditioner",
470
+ "Residential - furnace and evaporative cooler",
471
+ "Residential - boiler and no cooling",
472
+ "Residential - boiler and central air conditioner",
473
+ "Residential - boiler and room air conditioner",
474
+ "Residential - boiler and evaporative cooler",
475
+ "Residential - air-to-air heat pump",
476
+ "Residential - mini-split heat pump",
477
+ "Residential - ground-to-air heat pump"
478
+ ]
479
+ },
480
+ "heatingSystemFuelType": {
481
+ "description": "The fuel type of the heating system. This does not apply for certain system types (e.g., electric resistance or heat pumps).",
482
+ "type": "string",
483
+ "enum": [
484
+ "electricity",
485
+ "natural gas",
486
+ "fuel oil",
487
+ "propane",
488
+ "wood"
344
489
  ]
345
490
  },
346
491
  "templateType": {
@@ -376,6 +521,28 @@
376
521
  "DEER 2070",
377
522
  "DEER 2075"
378
523
  ]
524
+ },
525
+ "foundationType": {
526
+ "description": "The foundation type of the building. Required for residential buildings.",
527
+ "type": "string",
528
+ "enum": [
529
+ "slab",
530
+ "crawlspace - vented",
531
+ "crawlspace - unvented",
532
+ "basement - unconditioned",
533
+ "basement - conditioned",
534
+ "ambient"
535
+ ]
536
+ },
537
+ "atticType": {
538
+ "description": "The attic type of the building. Required for single-family residential buildings.",
539
+ "type": "string",
540
+ "enum": [
541
+ "attic - vented",
542
+ "attic - unvented",
543
+ "attic - conditioned",
544
+ "flat roof"
545
+ ]
379
546
  }
380
547
  }
381
- }
548
+ }