urbanopt-geojson 0.3.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +2 -2
  3. data/CHANGELOG.md +54 -4
  4. data/CONTRIBUTING.md +1 -1
  5. data/LICENSE.md +1 -1
  6. data/RDOC_MAIN.md +26 -22
  7. data/README.md +2 -2
  8. data/Rakefile +1 -1
  9. data/doc_templates/LICENSE.md +1 -1
  10. data/doc_templates/copyright_erb.txt +1 -1
  11. data/doc_templates/copyright_js.txt +1 -1
  12. data/doc_templates/copyright_ruby.txt +1 -1
  13. data/docs/.vuepress/components/InnerJsonSchema.vue +7 -11
  14. data/docs/.vuepress/config.js +11 -1
  15. data/docs/.vuepress/highlight.js +1 -1
  16. data/docs/.vuepress/json-schema-deref-loader.js +22 -0
  17. data/docs/README.md +3 -4
  18. data/docs/package-lock.json +5205 -6885
  19. data/docs/package.json +13 -9
  20. data/lib/measures/.rubocop.yml +1 -1
  21. data/lib/measures/urban_geometry_creation/LICENSE.md +1 -1
  22. data/lib/measures/urban_geometry_creation/README.md +8 -0
  23. data/lib/measures/urban_geometry_creation/measure.rb +21 -4
  24. data/lib/measures/urban_geometry_creation/measure.xml +34 -20
  25. data/lib/measures/urban_geometry_creation_zoning/LICENSE.md +1 -1
  26. data/lib/measures/urban_geometry_creation_zoning/measure.rb +3 -3
  27. data/lib/measures/urban_geometry_creation_zoning/measure.xml +8 -13
  28. data/lib/urbanopt-geojson.rb +1 -1
  29. data/lib/urbanopt/geojson.rb +2 -1
  30. data/lib/urbanopt/geojson/building.rb +61 -10
  31. data/lib/urbanopt/geojson/derived_extension.rb +1 -1
  32. data/lib/urbanopt/geojson/district_system.rb +1 -1
  33. data/lib/urbanopt/geojson/feature.rb +117 -3
  34. data/lib/urbanopt/geojson/geo_file.rb +3 -4
  35. data/lib/urbanopt/geojson/helper.rb +46 -3
  36. data/lib/urbanopt/geojson/logging.rb +1 -1
  37. data/lib/urbanopt/geojson/mapper_classes.rb +1 -1
  38. data/lib/urbanopt/geojson/model.rb +1 -1
  39. data/lib/urbanopt/geojson/region.rb +1 -1
  40. data/lib/urbanopt/geojson/scale_area.rb +95 -0
  41. data/lib/urbanopt/geojson/schema/building_properties.json +57 -7
  42. data/lib/urbanopt/geojson/schema/electrical_connector_properties.json +9 -9
  43. data/lib/urbanopt/geojson/schema/electrical_junction_properties.json +4 -5
  44. data/lib/urbanopt/geojson/schema/thermal_connector_properties.json +1 -1
  45. data/lib/urbanopt/geojson/schema/thermal_junction_properties.json +1 -1
  46. data/lib/urbanopt/geojson/update_areas.rb +1 -1
  47. data/lib/urbanopt/geojson/validate_geojson.rb +1 -1
  48. data/lib/urbanopt/geojson/version.rb +2 -2
  49. data/lib/urbanopt/geojson/zoning.rb +21 -16
  50. data/urbanopt-geojson-gem.gemspec +4 -8
  51. metadata +12 -23
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -47,9 +47,8 @@ module URBANopt
47
47
  # rubocop:disable Style/MethodMissing
48
48
  def method_missing(name, *args, &blk)
49
49
  # rubocop:enable Style/MethodMissing
50
- # rubocop:disable Style/GuardClause
51
50
  if @feature_json[:properties].keys.map(&:to_sym).include? name.to_sym
52
- # rubocop:enable Style/GuardClause
51
+
53
52
  return @feature_json[:properties][name.to_sym]
54
53
  else
55
54
  super
@@ -99,6 +98,89 @@ module URBANopt
99
98
  return @@feature_schema[feature_type]
100
99
  end
101
100
 
101
+ ##
102
+ # Used to calculate the aspect ratio for a given floor polygon.
103
+ #
104
+ def calculate_aspect_ratio
105
+ multi_polygons = get_multi_polygons(@feature_json)
106
+ rad_per_deg = 0.017453293
107
+
108
+ multi_polygons.each do |multi_polygon|
109
+ if multi_polygon.size > 1
110
+ runner.registerWarning('Ignoring holes in polygon')
111
+ end
112
+ multi_polygon.each do |polygon|
113
+ n = polygon.size
114
+ length = 0
115
+ north = 0
116
+ east = 0
117
+ south = 0
118
+ west = 0
119
+ aspect_ratio = 0
120
+
121
+ for i in (0..n - 2) do i
122
+ vertex_1 = nil
123
+ vertex_2 = nil
124
+ if i == n - 2
125
+ vertex_1 = polygon[n - 2]
126
+ vertex_2 = polygon[0]
127
+ else
128
+ vertex_1 = polygon[i]
129
+ vertex_2 = polygon[i + 1]
130
+ end
131
+ x_1 = vertex_1[0]
132
+ y_1 = vertex_1[1]
133
+ x_2 = vertex_2[0]
134
+ y_2 = vertex_2[1]
135
+
136
+ dist = (x_2 - x_1)**2 + (y_2 - y_1)**2
137
+
138
+ length = Math.sqrt(dist)
139
+
140
+ # delta latitude
141
+ dlat = x_2 - x_1
142
+ # delta longitude
143
+ dlon = y_2 - y_1
144
+
145
+ # convert radian to degree
146
+ sin_angle = Math.asin(dlon / length) * (1 / rad_per_deg)
147
+ sin_angle = sin_angle.round(4)
148
+
149
+ cos_angle = Math.acos(dlat / length) * (1 / rad_per_deg)
150
+ cos_angle = cos_angle.round(4)
151
+
152
+ if cos_angle >= 45 && cos_angle <= 135 && sin_angle >= 45 && sin_angle <= 90
153
+ north += length
154
+ elsif cos_angle >= 0 && cos_angle < 45 && sin_angle > -45 && sin_angle < 45
155
+ east += length
156
+ elsif cos_angle >= 45 && cos_angle <= 135 && sin_angle >= -90 && sin_angle <= -45
157
+ south += length
158
+ elsif cos_angle > 135 && cos_angle <= 180 && sin_angle > -45 && sin_angle < 45
159
+ west += length
160
+ end
161
+
162
+ if east + west != 0
163
+ aspect_ratio = (north + south) / (east + west)
164
+ else
165
+ aspect_ratio = 1
166
+ end
167
+
168
+ end
169
+
170
+ aspect_ratio = aspect_ratio.round(4)
171
+ return aspect_ratio
172
+ end
173
+ end
174
+ end
175
+
176
+ ##
177
+ # Used to calculate the perimeter multiplier given the aspect ratio, original perimeter and area.
178
+ def get_perimeter_multiplier(area, aspect_ratio, perimeter_original)
179
+ perimeter_new = 2 * (Math.sqrt(area * aspect_ratio) + Math.sqrt(area / aspect_ratio))
180
+ perimeter_ratio = perimeter_original / perimeter_new
181
+ return perimeter_ratio
182
+ end
183
+
102
184
  ##
103
185
  # Returns coordinate with the minimum longitute and latitude within a given +building_json+ .
104
186
  def get_min_lon_lat
@@ -168,6 +250,38 @@ module URBANopt
168
250
  return OpenStudio::PointLatLon.new(min_lat, min_lon, 0)
169
251
  end
170
252
 
253
+ ##
254
+ # Used to determine the centroid for the feature's coordinates.
255
+ #
256
+ # [Parameters]
257
+ # * +vertices+ - The first set polygon vertices in the array of feature coordinates.
258
+ def find_feature_center(vertices)
259
+ number_of_locations = vertices.length
260
+
261
+ return vertices.first if number_of_locations == 1
262
+
263
+ x = y = z = 0.0
264
+ vertices.each do |station|
265
+ latitude = station[0] * Math::PI / 180
266
+ longitude = station[1] * Math::PI / 180
267
+
268
+ x += Math.cos(latitude) * Math.cos(longitude)
269
+ y += Math.cos(latitude) * Math.sin(longitude)
270
+ z += Math.sin(latitude)
271
+ end
272
+
273
+ x /= number_of_locations
274
+ y /= number_of_locations
275
+ z /= number_of_locations
276
+
277
+ central_longitude = Math.atan2(y, x)
278
+ central_square_root = Math.sqrt(x * x + y * y)
279
+ central_latitude = Math.atan2(z, central_square_root)
280
+
281
+ [central_latitude * 180 / Math::PI,
282
+ central_longitude * 180 / Math::PI]
283
+ end
284
+
171
285
  private
172
286
 
173
287
  ##
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -161,9 +161,8 @@ module URBANopt
161
161
  if f[:properties] && f[:properties][:id] == feature_id
162
162
  # merge site origin properties
163
163
  f = merge_site_properties(f)
164
- # rubocop:disable Style/GuardClause
165
164
  if f[:properties][:type] == 'Building'
166
- # rubocop:enable Style/GuardClause
165
+
167
166
  return URBANopt::GeoJSON::Building.new(f)
168
167
  elsif f[:properties] && f[:properties][:type] == 'District System'
169
168
  return URBANopt::GeoJSON::DistrictSystem.new(f)
@@ -200,7 +199,7 @@ module URBANopt
200
199
  add_props.each do |prop|
201
200
  if project.key?(prop[:site]) && project[prop[:site]]
202
201
  # property exists in site
203
- if !feature[:properties].key?(prop[:feature]) || feature[:properties][prop[:feature]].nil? || feature[:properties][prop[:feature]].empty?
202
+ if !feature[:properties].key?(prop[:feature]) || feature[:properties][prop[:feature]].nil? || feature[:properties][prop[:feature]].to_s.empty?
204
203
  # property does not exist in feature or is nil: add site property (don't overwrite)
205
204
  feature[:properties][prop[:feature]] = project[prop[:site]]
206
205
  end
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -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)
@@ -183,7 +183,9 @@ module URBANopt
183
183
  # * +origin_lat_lon+ - _Type:Float_ - An instance of +OpenStudio::PointLatLon+ indicating the origin's latitude and longitude.
184
184
  # * +runner+ - _Type:String_ - The measure run's instance of +OpenStudio::Measure::OSRunner+ .
185
185
  # * +zoning+ - _Type:Boolean_ - Value is +True+ if utilizing detailed zoning, else +False+. Zoning is set to False by default.
186
- def self.floor_print_from_polygon(polygon, elevation, origin_lat_lon, runner, zoning = false)
186
+ # * +scaled_footprint_area+ - Used to scale the footprint area using the floor area. 0 by
187
+ # default (no scaling).
188
+ def self.floor_print_from_polygon(polygon, elevation, origin_lat_lon, runner, zoning = false, scaled_footprint_area = 0)
187
189
  floor_print = OpenStudio::Point3dVector.new
188
190
  all_points = OpenStudio::Point3dVector.new
189
191
  polygon.each do |p|
@@ -207,9 +209,42 @@ module URBANopt
207
209
  floor_print = OpenStudio.reverse(floor_print)
208
210
  runner.registerWarning('Reversing floor print')
209
211
  end
212
+
213
+ # check for scaling
214
+ if scaled_footprint_area > 0
215
+
216
+ # check that the scaled_footprint_area desired is no less than X % of the original
217
+ original_floor_print_area = OpenStudio.getArea(floor_print).get
218
+ if scaled_footprint_area / original_floor_print_area <= 0.5 || scaled_footprint_area / original_floor_print_area >= 2
219
+ # TOO MUCH SCALING...using original footprint when scaled is 2x bigger or smaller than the original
220
+ runner.registerWarning('Desired scaled_footprint_area is a factor of 2 of the original footprint...keeping original footprint (no scaling!)')
221
+ else
222
+ new_floor_print = adjust_vertices_to_area(floor_print, scaled_footprint_area, runner)
223
+ new_footprint_area = OpenStudio.getArea(new_floor_print).get
224
+ runner.registerInfo("New floor area: #{new_footprint_area}, compared to scaled area desired: #{scaled_footprint_area}")
225
+ floor_print = new_floor_print
226
+ end
227
+ end
228
+
210
229
  return floor_print
211
230
  end
212
231
 
232
+ ##
233
+ # Used to scale footprint to desired area while keeping the original shape.
234
+ #
235
+ # [Parameters]
236
+ # * +vertices+ - _Type:Array_ - An array of vertices for the original floorprint
237
+ # * +desired_area+ - _Type:String_ - Area to which you want to scale the vertices to
238
+ # * +runner+ - _Type:String_ - An instance of +Openstudio::Measure::OSRunner+ for the measure run.
239
+ #
240
+ def self.adjust_vertices_to_area(vertices, desired_area, runner, eps = 0.1)
241
+ ar = ScaleArea.new(vertices, desired_area, runner, eps)
242
+
243
+ n = Newton.nlsolve(ar, [0])
244
+
245
+ return ar.new_vertices
246
+ end
247
+
213
248
  ##
214
249
  # Calculate which other buildings are shading the current feature and return as an array of
215
250
  # +OpenStudio::Model::Space+.
@@ -243,6 +278,7 @@ module URBANopt
243
278
  if number_of_stories_above_ground.nil?
244
279
  number_of_stories_above_ground = number_of_stories
245
280
  number_of_stories_below_ground = 0
281
+
246
282
  else
247
283
  number_of_stories_below_ground = number_of_stories - number_of_stories_above_ground
248
284
  end
@@ -251,6 +287,13 @@ module URBANopt
251
287
  if number_of_stories_above_ground && number_of_stories_above_ground > 0 && maximum_roof_height
252
288
  floor_to_floor_height = maximum_roof_height / number_of_stories_above_ground
253
289
  end
290
+
291
+ # check that feature has a # stories
292
+ if number_of_stories_above_ground.nil?
293
+ runner.registerWarning("[geojson process_other_buildings] Unable to include feature #{other_building[:properties][:id]} in shading calculations: no 'number of stories' data")
294
+ end
295
+ next if number_of_stories_above_ground.nil?
296
+
254
297
  other_height = number_of_stories_above_ground * floor_to_floor_height
255
298
  # find the polygon of the other_building by passing it to the get_multi_polygons method
256
299
  other_building_points = building.other_points(other_building, other_height, origin_lat_lon, runner, zoning)
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -0,0 +1,95 @@
1
+ # *********************************************************************************
2
+ # URBANopt (tm), 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 'net/http'
33
+ require 'uri'
34
+ require 'openssl'
35
+ require 'bigdecimal/newton'
36
+
37
+ module Newton
38
+ def self.jacobian(f, fx, x)
39
+ Jacobian.jacobian(f, fx, x)
40
+ end
41
+
42
+ def self.ludecomp(a, n, zero = 0, one = 1)
43
+ LUSolve.ludecomp(a, n, zero, one)
44
+ end
45
+
46
+ def self.lusolve(a, b, ps, zero = 0.0)
47
+ LUSolve.lusolve(a, b, ps, zero)
48
+ end
49
+ end
50
+
51
+ module URBANopt
52
+ module GeoJSON
53
+ class ScaleArea
54
+ def initialize(vertices, desired_area, runner, eps)
55
+ @vertices = vertices
56
+ @centroid = OpenStudio.getCentroid(vertices)
57
+ raise "Cannot compute centroid for '#{vertices}'" if @centroid.empty?
58
+ @centroid = @centroid.get
59
+ @desired_area = desired_area
60
+ @new_vertices = vertices
61
+ @runner = runner
62
+ @zero = BigDecimal('0.0')
63
+ @one = BigDecimal('1.0')
64
+ @two = BigDecimal('2.0')
65
+ @ten = BigDecimal('10.0')
66
+ @eps = eps
67
+ end
68
+
69
+ attr_reader :zero
70
+
71
+ attr_reader :one
72
+
73
+ attr_reader :two
74
+
75
+ attr_reader :ten
76
+
77
+ attr_reader :eps
78
+
79
+ ##
80
+ # Used to determine new scaled vertices, by iteratively passing in the perimeter distance to
81
+ # minimise the difference of the new and scaled area. Returns the difference of the new area and desired area.
82
+ #
83
+ def values(x)
84
+ @new_vertices = URBANopt::GeoJSON::Zoning.divide_floor_print(@vertices, x[0].to_f, @runner, scale = true)
85
+ new_area = OpenStudio.getArea(@new_vertices)
86
+ raise "Cannot compute area for '#{@new_vertices}'" if new_area.empty?
87
+ new_area = new_area.get
88
+
89
+ return [new_area - @desired_area]
90
+ end
91
+
92
+ attr_reader :new_vertices
93
+ end # ScaleArea
94
+ end # GeoJSON
95
+ end # URBANopt
@@ -87,7 +87,7 @@
87
87
  "type": "string"
88
88
  },
89
89
  "floor_area": {
90
- "description": "Usable floor area (ft^2).",
90
+ "description": "Usable floor area (ft^2). Required for residential buildings, this represents conditioned floor area.",
91
91
  "type": "number"
92
92
  },
93
93
  "number_of_stories": {
@@ -95,7 +95,7 @@
95
95
  "type": "integer"
96
96
  },
97
97
  "number_of_stories_above_ground": {
98
- "description": "The number of building stories above ground. Defaults to number_of_stories.",
98
+ "description": "The number of building stories above ground. Defaults to number_of_stories. Required for residential buildings.",
99
99
  "type": "integer"
100
100
  },
101
101
  "maximum_roof_height": {
@@ -194,7 +194,7 @@
194
194
  "maximum": 100
195
195
  },
196
196
  "number_of_residential_units": {
197
- "description": "Total number of residential units in the building. Required for single-family attached or multifamily residential buildings.",
197
+ "description": "Total number of residential units in the building. Required for single-family attached and multifamily residential buildings.",
198
198
  "type": "integer"
199
199
  },
200
200
  "number_of_bedrooms": {
@@ -225,6 +225,45 @@
225
225
  },
226
226
  "user_data": {
227
227
  "description": "Arbitrary user data"
228
+ },
229
+ "ev_charging": {
230
+ "description": "Should be set to true if EV charging is associated with the building.",
231
+ "type": "boolean"
232
+ },
233
+ "ev_charging_station_type": {
234
+ "description": "Indicates whether EV charging associated with the building is for Typical Home, Public or Work charging station",
235
+ "type": "string",
236
+ "enum": [
237
+ "Typical Home",
238
+ "Typical Public",
239
+ "Typical Work"
240
+ ]
241
+ },
242
+ "delay_type": {
243
+ "description": "Adds workplace charging flexibility for different scenarios. Min delay scenario indicates EV charging immediately upon arriving to work, Max delay indicates not charging until necessary thereby shifting EV charging load to later in the day and Min power indicates charging EVs at a minimum rate over the parking event.",
244
+ "type": "string",
245
+ "enum": [
246
+ "Min Delay",
247
+ "Max Delay",
248
+ "Min Power"
249
+ ]
250
+ },
251
+ "ev_charging_behavior": {
252
+ "description": "Describes scenarios for EV charging behavior, Business as Usual implies home dominant charging behavior, Free Workplace Charging at Project Site implies peak power draw from EV charging during morning hours due to EV charging at workplaces and Free Workplace Charging Across Metro Area scenario reduces Home EV charging relative to Free Workplace Charging at Project Site for residents who work elsewhere and can charge their vehicles for free at those workplaces.",
253
+ "type": "string",
254
+ "enum": [
255
+ "Business as Usual",
256
+ "Free Workplace Charging at Project Site",
257
+ "Free Workplace Charging Across Metro Area"
258
+ ]
259
+ },
260
+ "ev_percent": {
261
+ "description": "Denotes the % of vehicles parked at the building that are EVs.",
262
+ "type": "number"
263
+ },
264
+ "ev_curtailment_frac": {
265
+ "description": "Fraction between 0 and 1 that denotes curtailment of EV charging load to better align EV charging with expected energy production from a solar PV system",
266
+ "type": "number"
228
267
  }
229
268
  },
230
269
  "oneOf": [
@@ -241,8 +280,8 @@
241
280
  "type",
242
281
  "name",
243
282
  "floor_area",
283
+ "number_of_stories_above_ground",
244
284
  "number_of_stories",
245
- "footprint_area",
246
285
  "building_type",
247
286
  "number_of_bedrooms",
248
287
  "foundation_type",
@@ -262,8 +301,8 @@
262
301
  "type",
263
302
  "name",
264
303
  "floor_area",
304
+ "number_of_stories_above_ground",
265
305
  "number_of_stories",
266
- "footprint_area",
267
306
  "building_type",
268
307
  "number_of_residential_units",
269
308
  "number_of_bedrooms",
@@ -284,8 +323,8 @@
284
323
  "type",
285
324
  "name",
286
325
  "floor_area",
326
+ "number_of_stories_above_ground",
287
327
  "number_of_stories",
288
- "footprint_area",
289
328
  "building_type",
290
329
  "number_of_residential_units",
291
330
  "number_of_bedrooms",
@@ -296,6 +335,9 @@
296
335
  "properties": {
297
336
  "building_type": {
298
337
  "enum": [
338
+ "Single-Family",
339
+ "Multifamily (2 to 4 units)",
340
+ "Multifamily (5 or more units)",
299
341
  "Vacant",
300
342
  "Office",
301
343
  "Laboratory",
@@ -341,6 +383,9 @@
341
383
  "Single-Family Detached",
342
384
  "Single-Family Attached",
343
385
  "Multifamily",
386
+ "Single-Family",
387
+ "Multifamily (2 to 4 units)",
388
+ "Multifamily (5 or more units)",
344
389
  "Vacant",
345
390
  "Office",
346
391
  "Laboratory",
@@ -513,7 +558,12 @@
513
558
  "DEER 2060",
514
559
  "DEER 2065",
515
560
  "DEER 2070",
516
- "DEER 2075"
561
+ "DEER 2075",
562
+ "Residential IECC 2006 - Customizable Template Sep 2020",
563
+ "Residential IECC 2009 - Customizable Template Sep 2020",
564
+ "Residential IECC 2012 - Customizable Template Sep 2020",
565
+ "Residential IECC 2015 - Customizable Template Sep 2020",
566
+ "Residential IECC 2018 - Customizable Template Sep 2020"
517
567
  ]
518
568
  },
519
569
  "foundationType": {