openstudio-standards 0.1.11 → 0.1.12

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/OpenStudio_Standards.xlsx +0 -0
  3. data/data/standards/OpenStudio_Standards_climate_zone_sets.json +96 -0
  4. data/data/standards/OpenStudio_Standards_climate_zones.json +96 -0
  5. data/data/standards/OpenStudio_Standards_construction_properties.json +11424 -0
  6. data/data/standards/OpenStudio_Standards_construction_sets.json +1746 -24
  7. data/data/standards/OpenStudio_Standards_constructions.json +409 -0
  8. data/data/standards/OpenStudio_Standards_curve_biquadratics.json +160 -0
  9. data/data/standards/OpenStudio_Standards_curve_quadratics.json +165 -0
  10. data/data/standards/OpenStudio_Standards_entryways.json +191 -0
  11. data/data/standards/OpenStudio_Standards_exterior_lighting.json +964 -0
  12. data/data/standards/OpenStudio_Standards_exterior_lighting_assumptions.json +191 -0
  13. data/data/standards/OpenStudio_Standards_ground_temperatures.json +11424 -0
  14. data/data/standards/OpenStudio_Standards_illuminated_parking_area.json +157 -0
  15. data/data/standards/OpenStudio_Standards_materials.json +2539 -745
  16. data/data/standards/OpenStudio_Standards_motors.json +420 -0
  17. data/data/standards/OpenStudio_Standards_parking.json +157 -0
  18. data/data/standards/OpenStudio_Standards_prototype_inputs.json +4410 -10
  19. data/data/standards/OpenStudio_Standards_schedules.json +13756 -8383
  20. data/data/standards/OpenStudio_Standards_space_types.json +8593 -907
  21. data/data/standards/OpenStudio_Standards_standards.json +9 -0
  22. data/data/standards/OpenStudio_Standards_templates.json +24 -0
  23. data/data/standards/OpenStudio_Standards_unitary_acs.json +924 -0
  24. data/data/standards/manage_OpenStudio_Standards.rb +1 -1
  25. data/lib/openstudio-standards/btap/btap.rb +0 -1
  26. data/lib/openstudio-standards/btap/compliance.rb +2 -5
  27. data/lib/openstudio-standards/btap/economics.rb +16 -16
  28. data/lib/openstudio-standards/btap/envelope.rb +1 -1
  29. data/lib/openstudio-standards/btap/equest.rb +7 -7
  30. data/lib/openstudio-standards/btap/fileio.rb +2 -2
  31. data/lib/openstudio-standards/btap/geometry.rb +1 -1
  32. data/lib/openstudio-standards/btap/measures.rb +16 -16
  33. data/lib/openstudio-standards/btap/mpc.rb +18 -18
  34. data/lib/openstudio-standards/btap/schedules.rb +35 -2
  35. data/lib/openstudio-standards/btap/simmanager.rb +1 -1
  36. data/lib/openstudio-standards/btap/spaceloads.rb +1 -1
  37. data/lib/openstudio-standards/btap/vintagizer.rb +1 -1
  38. data/lib/openstudio-standards/prototypes/Prototype.Model.elevators.rb +218 -0
  39. data/lib/openstudio-standards/prototypes/Prototype.Model.exterior_lights.rb +399 -0
  40. data/lib/openstudio-standards/prototypes/Prototype.Model.hvac.rb +2 -2
  41. data/lib/openstudio-standards/prototypes/Prototype.Model.rb +237 -0
  42. data/lib/openstudio-standards/prototypes/Prototype.Model.swh.rb +536 -0
  43. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +6 -0
  44. data/lib/openstudio-standards/standards/Standards.Model.rb +249 -10
  45. data/lib/openstudio-standards/standards/Standards.SpaceType.rb +5 -5
  46. data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +161 -0
  47. data/lib/openstudio-standards/utilities/simulation.rb +6 -4
  48. data/lib/openstudio-standards/version.rb +1 -1
  49. metadata +223 -204
  50. data/lib/openstudio-standards/btap/os_lib_schedules.rb +0 -677
@@ -318,13 +318,13 @@ module BTAP
318
318
  #Get the occupancy schedule.
319
319
  occ_schedule = people.numberofPeopleSchedule.get
320
320
  #Convert schedule to timeseries and sum up timeseries objects for this space / zone.
321
- occ_time_series = OsLib_Schedules.create_timeseries_from_schedule_ruleset(model,occ_schedule)
321
+ occ_time_series = create_timeseries_from_schedule_ruleset(model,occ_schedule)
322
322
  timeseries = timeseries + occ_time_series
323
323
  end
324
324
  end
325
325
  end
326
326
  #return the timeseries converted to
327
- return OsLib_Schedules.create_schedule_variable_interval_from_time_series(model,timeseries)
327
+ return create_schedule_variable_interval_from_time_series(model,timeseries)
328
328
  end
329
329
 
330
330
 
@@ -897,6 +897,39 @@ module BTAP
897
897
  return create_annual_ruleset_schedule(model, name,type, [Array.new(24){value}, Array.new(24){value},Array.new(24){value}])
898
898
  end
899
899
 
900
+ # Creates TimeSeries from ScheduleRuleset
901
+ # @author david.goldwasser@nrel.gov
902
+ # @param model [OpenStudio::Model::Model] A model object
903
+ # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] A schedule ruleset
904
+ # @return [OpenStudio::TimeSeries] A TimeSeries object
905
+ def self.create_timeseries_from_schedule_ruleset(model, schedule_ruleset)
906
+ yd = model.getYearDescription
907
+ start_date = yd.makeDate(1, 1)
908
+ end_date = yd.makeDate(12, 31)
909
+
910
+ values = OpenStudio::DoubleVector.new
911
+ day = OpenStudio::Time.new(1.0)
912
+ interval = OpenStudio::Time.new(1.0 / 48.0)
913
+ day_schedules = schedule_ruleset.to_ScheduleRuleset.get.getDaySchedules(start_date, end_date)
914
+ day_schedules.each do |day_schedule|
915
+ time = interval
916
+ while time < day
917
+ values << day_schedule.getValue(time)
918
+ time += interval
919
+ end
920
+ end
921
+ time_series = OpenStudio::TimeSeries.new(start_date, interval, OpenStudio.createVector(values), "")
922
+ end
923
+
924
+ # Creates ScheduleVariableInterval from TimeSeries
925
+ # @author david.goldwasser@nrel.gov
926
+ # @param model [OpenStudio::model::Model] A model object
927
+ # @param time_series [OpenStudio::TimeSeries] A TimeSeries object
928
+ # @return [OpenStudio::Model::ScheduleInterval] An interval schedule
929
+ def self.create_schedule_variable_interval_from_time_series(model, time_series)
930
+ result = OpenStudio::Model::ScheduleInterval.fromTimeSeries(time_series, model).get
931
+ end
932
+
900
933
  end #module Schedules
901
934
 
902
935
  end #module Resources
@@ -231,7 +231,7 @@ module BTAP
231
231
  #@return [String] self
232
232
  def addModel(model, folder = @analysisFolder )
233
233
 
234
- run_name = model.building.get.getAttribute("name").get.valueAsString
234
+ run_name = model.building.get.name.get.to_s
235
235
  working_folder = OpenStudio::Path.new(folder.to_s + "\\simulations\\" + run_name )
236
236
  osm_save_path = OpenStudio::Path.new(folder.to_s + "\\simulations\\" + run_name + "\\" + run_name + ".osm" )
237
237
  model.save(OpenStudio::Path.new(osm_save_path), true);
@@ -187,7 +187,7 @@ module BTAP
187
187
  infiltration_load.setDesignFlowRate( setDesignFlowRate ) unless setDesignFlowRate.nil?
188
188
  infiltration_load.setFlowperSpaceFloorArea(setFlowperSpaceFloorArea ) unless setFlowperSpaceFloorArea.nil?
189
189
  infiltration_load.setFlowperExteriorSurfaceArea(setFlowperExteriorSurfaceArea ) unless setFlowperExteriorSurfaceArea.nil?
190
- table << infiltration_load.getAttribute("name").get.valueAsString << ","
190
+ table << infiltration_load.name.get.to_s << ","
191
191
  table << infiltration_load.designFlowRateCalculationMethod << ","
192
192
  infiltration_load.airChangesperHour.empty? ? ach = "NA" : ach = infiltration_load.airChangesperHour.get
193
193
  infiltration_load.designFlowRate.empty? ? dfr = "NA" : dfr = infiltration_load.designFlowRate.get
@@ -96,7 +96,7 @@ class Vintagizer
96
96
  #Remove all existing constructions from model.
97
97
  BTAP::Resources::Envelope::remove_all_envelope_information( constructions_model )
98
98
  #Save to model.
99
- new_construction_set.setAttribute("name",construction_id)
99
+ new_construction_set.setName(construction_id)
100
100
  constructions_model.building.get.setDefaultConstructionSet( new_construction_set.clone( constructions_model ).to_DefaultConstructionSet.get )
101
101
 
102
102
  #Give adiabatic surfaces a construction. Does not matter what. This is a bug in Openstudio that leave these surfaces unassigned by the default construction set.
@@ -0,0 +1,218 @@
1
+ # open the class to add methods to add elevators
2
+ class OpenStudio::Model::Model
3
+
4
+ # Add elevators to the model
5
+ #
6
+ # @param template [String] Valid choices are
7
+ # @return [OpenStudio::Model::ElectricEquipment] the resulting elevator
8
+ def add_elevators(template)
9
+
10
+ # determine effective number of stories
11
+ effective_num_stories = self.effective_num_stories
12
+
13
+ # determine elevator type
14
+ # todo - add logic here or upstream to have some multi-story buildings without elevators (e.g. small multi-family and small hotels)
15
+ elevator_type = nil
16
+ if effective_num_stories[:below_grade] + effective_num_stories[:above_grade] < 2
17
+ return nil # don't add elevators
18
+ elsif effective_num_stories[:below_grade] + effective_num_stories[:above_grade] < 6
19
+ elevator_type = "Hydraulic"
20
+ else
21
+ elevator_type = "Traction"
22
+ end
23
+
24
+ # determine space to put elevator load in
25
+ # largest bottom story (including basement) space that has multiplier of 1
26
+ bottom_spaces = {}
27
+ bottom_story = effective_num_stories[:story_hash].keys.first
28
+ bottom_story.spaces.each do |space|
29
+ next if space.multiplier > 1
30
+ bottom_spaces[space] = space.floorArea
31
+ end
32
+ target_space = bottom_spaces.key(bottom_spaces.values.max)
33
+
34
+ # determine number of elevators
35
+ number_of_pass_elevators = 0.0
36
+ number_of_freight_elevators = 0.0
37
+ building_type_hash = {}
38
+
39
+ # apply building type specific log to add to number of elevators based on Beyer (2009) rules of thumb
40
+ space_type_hash = self.create_space_type_hash(template)
41
+ space_type_hash.each do |space_type,hash|
42
+
43
+ # update building_type_hash
44
+ if building_type_hash.has_key?(hash[:stds_bldg_type])
45
+ building_type_hash[hash[:stds_bldg_type]] += hash[:floor_area]
46
+ else
47
+ building_type_hash[hash[:stds_bldg_type]] = hash[:floor_area]
48
+ end
49
+
50
+ # building type specific notes; prototype uses Beyer (2009) rules fo thumb
51
+ if ["Office","SmallOffice","MediumOffice","LargeOffice"].include?(hash[:stds_bldg_type])
52
+ # The office buildings have one elevator for every 45,000 ft2 (4,181 m2),
53
+ # plus one service elevator for the large office building.
54
+ pass_elevator_per_area = OpenStudio::convert(45000.0,"ft^2","m^2").get
55
+ number_of_pass_elevators += hash[:floor_area]/pass_elevator_per_area
56
+ # add freight elevators as separate step 1 if over 5k m^2 but more than one if over 50k m^2
57
+ if self.getBuilding.floorArea > 45000.0 # m^2
58
+ number_of_freight_elevators += self.getBuilding.floorArea/500000.0 # m^2
59
+ end
60
+ elsif["SmallHotel","LargeHotel"].include?(hash[:stds_bldg_type]) && hash[:stds_space_type].include?("GuestRoom")
61
+ # The hotels have one elevator for every 75 rooms, and the large hotel includes one service elevator for every two public elevators,
62
+ # plus one additional elevator for the dining and banquet facilities on the top floor.
63
+ units_per_pass_elevator = 75.0
64
+ freight_elevators_per_unit = units_per_pass_elevator/2.0
65
+ number_of_pass_elevators += hash[:num_units]/units_per_pass_elevator
66
+ number_of_freight_elevators += hash[:num_units]/freight_elevators_per_unit
67
+ elsif["LargeHotel"].include?(hash[:stds_bldg_type]) && ["Banquet","Cafe"].include?(hash[:stds_space_type])
68
+ pass_elevator_per_area = OpenStudio::convert(10000.0,"ft^2","m^2").get
69
+ number_of_pass_elevators += hash[:floor_area]/pass_elevator_per_area
70
+ elsif["MidriseApartment","HighriseApartment"].include?(hash[:stds_bldg_type]) && hash[:stds_space_type].include?("Apartment")
71
+ # The apartment building has one elevator for every 90 units
72
+ units_per_pass_elevator = 90.0
73
+ number_of_pass_elevators += hash[:num_units]/units_per_pass_elevator
74
+ elsif["Hospital"].include?(hash[:stds_bldg_type]) && ["PatRoom","ICU_PatRm","ICU_Open"].include?(hash[:stds_space_type])
75
+ # The hospital has one public and one service elevator for every 100 beds (250 total),
76
+ # plus two elevators for the offices and cafeteria on the top floor.
77
+ beds_per_pass_elevator = 100.0
78
+ number_of_pass_elevators += hash[:num_beds]/beds_per_pass_elevator
79
+ number_of_freight_elevators += hash[:num_beds]/beds_per_pass_elevator
80
+ elsif["Hospital"].include?(hash[:stds_bldg_type]) && ["Dining","Kitchen","Office"].include?(hash[:stds_space_type])
81
+ pass_elevator_per_area = OpenStudio::convert(12500.0,"ft^2","m^2").get
82
+ number_of_pass_elevators += hash[:floor_area]/pass_elevator_per_area
83
+ elsif ["PrimarySchool","SecondarySchool"].include?(hash[:stds_bldg_type])
84
+ # 210,887 ft^2 secondary school prototype has 2 elevators
85
+ pass_elevator_per_area = OpenStudio::convert(100000.0,"ft^2","m^2").get
86
+ number_of_pass_elevators += hash[:floor_area]/(pass_elevator_per_area) # freight and passenger combined
87
+ elsif ["Outpatient"].include?(hash[:stds_bldg_type])
88
+ # 210,887 ft^2 secondary school prototype has 2 elevators
89
+ #pass_elevator_per_area = OpenStudio::convert(15000.0,"ft^2","m^2").get
90
+ #number_of_pass_elevators += hash[:floor_area]/(pass_elevator_per_area) # freight and passenger combined
91
+ elsif ["Warehouse"].include?(hash[:stds_bldg_type])
92
+ freight_elevator_per_area = OpenStudio::convert(250000.0,"ft^2","m^2").get
93
+ number_of_freight_elevators += hash[:floor_area]/freight_elevator_per_area
94
+ else
95
+ # todo - improve catchall for building types without elevator data, using same value as what Outpatient would be if not already in space type
96
+ # includes RetailStandalone, RetailStripmall, QuickServiceRestaurant, FullServiceRestaurant, SuperMarket (made unique logic above for warehouse)
97
+
98
+ pass_elevator_per_area = OpenStudio::convert(15000.0,"ft^2","m^2").get
99
+ number_of_pass_elevators += hash[:floor_area]/pass_elevator_per_area
100
+ end
101
+
102
+ end
103
+
104
+ # adjust number of elevators (can be double but if not 0 must be at least 1.0)
105
+ if number_of_pass_elevators > 0.0 and number_of_pass_elevators < 1.0
106
+ number_of_pass_elevators = 1.0
107
+ end
108
+ if number_of_freight_elevators > 0.0 and number_of_freight_elevators < 1.0
109
+ number_of_freight_elevators = 1.0
110
+ end
111
+ number_of_elevators = number_of_pass_elevators + number_of_freight_elevators
112
+
113
+
114
+ building_type = building_type_hash.key(building_type_hash.values.max)
115
+ # rename space types as needed
116
+ if building_type == "Office"
117
+ building_type = self.remap_office(building_type_hash["Office"])
118
+ end
119
+ if building_type == "SmallHotel" then building_type = "LargeHotel" end # no elevator schedules for SmallHotel
120
+ if building_type == "PrimarySchool" then building_type = "SecondarySchool" end # no elevator schedules for PrimarySchool
121
+ if building_type == "Retail" then building_type = "RetailStandalone" end # no elevator schedules for PrimarySchool
122
+ if building_type == "StripMall" then building_type = "RetailStripmall" end # no elevator schedules for PrimarySchool
123
+ if building_type == "Outpatient"
124
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.elevators', "Outpatient ElevatorPumpRoom plug loads contain the elevator loads. Not adding extra elevator loads on top of it.")
125
+ end
126
+
127
+ # Retrieve the Prototype Inputs from JSON
128
+ search_criteria = {
129
+ 'template' => template,
130
+ 'building_type' => building_type
131
+ }
132
+
133
+ prototype_input = find_object($os_standards['prototype_inputs'], search_criteria, nil)
134
+ if prototype_input.nil?
135
+ OpenStudio.logFree(OpenStudio::Error, 'Prototype.Model.elevators', "Could not find prototype inputs for #{search_criteria}.")
136
+ return false
137
+ end
138
+
139
+ # assign schedules
140
+ if ["Office","SmallOffice","MediumOffice","LargeOffice","MidriseApartment","HighriseApartment","SecondarySchool"].include?(building_type)
141
+ elevator_schedule = prototype_input['elevator_schedule']
142
+ elevator_fan_schedule = prototype_input['elevator_fan_schedule']
143
+ elevator_lights_schedule = prototype_input['elevator_fan_schedule']
144
+ elsif ["LargeHotel","Hospital"].include?(building_type)
145
+ elevator_schedule = prototype_input['exterior_fuel_equipment1_schedule']
146
+ elevator_fan_schedule = prototype_input['exterior_fuel_equipment2_schedule']
147
+ elevator_lights_schedule = prototype_input['exterior_fuel_equipment2_schedule']
148
+ else
149
+
150
+ # identify occupancy schedule from largest space type of this building type
151
+ space_type_size = {}
152
+ space_type_hash.each do |space_type,hash|
153
+ next if not building_type.include?(hash[:stds_bldg_type])
154
+ space_type_size[space_type] = hash[:floor_area]
155
+ end
156
+ occ_sch = space_type_size.key(space_type_size.values.max).defaultScheduleSet.get.numberofPeopleSchedule.get
157
+
158
+ # clone and assign to elevator
159
+ elev_sch = occ_sch.clone(self)
160
+ elevator_schedule = elev_sch.name.to_s
161
+ elevator_fan_schedule = elev_sch.name.to_s
162
+ elevator_lights_schedule = elev_sch.name.to_s
163
+
164
+ # todo - scale down peak value based on building type lookup, or make parametric schedule based on hours of operation
165
+ # includes RetailStandalone, RetailStripmall, QuickServiceRestaurant, FullServiceRestaurant, SuperMarket (made unique logic above for warehouse)
166
+
167
+ if building_type == "Warehouse"
168
+ # alter default profile, summer, winter, and rules
169
+ max_value = 0.2
170
+ elev_sch = elev_sch.to_ScheduleRuleset.get
171
+ day_schedules = []
172
+ elev_sch.scheduleRules.each do |rule|
173
+ day_schedules << rule.daySchedule
174
+ end
175
+ day_schedules << elev_sch.defaultDaySchedule
176
+ day_schedules << elev_sch.summerDesignDaySchedule
177
+ day_schedules << elev_sch.winterDesignDaySchedule
178
+
179
+ day_schedules.each do |day_schedule|
180
+ values = day_schedule.values
181
+ times = day_schedule.times
182
+ values.each_with_index do |value,i|
183
+ if value > max_value
184
+ day_schedule.addValue(times[i],max_value)
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ end
191
+
192
+ # todo - currently add elevator doesn't allow me to choose the size of the elevator?
193
+ # ref bldg pdf has formula for motor hp based on weight, speed, counterweight fraction and mech eff (in 5.1.4)
194
+
195
+ # todo - should schedules change based on traction vs. hydraulic vs. just taking what is in prototype.
196
+
197
+ # call add_elevator in Prototype.hvac_systems.rb to create elevator objects
198
+ elevator = self.add_elevator(template,
199
+ target_space,
200
+ number_of_elevators,
201
+ elevator_type,
202
+ elevator_schedule,
203
+ elevator_fan_schedule,
204
+ elevator_lights_schedule,
205
+ building_type)
206
+
207
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.elevators', "Adding #{elevator.multiplier} #{elevator_type} elevators to the model in #{target_space.name}.")
208
+
209
+ # check fraction lost on heat from elevator if traction, change to 100% lost if not setup that way.
210
+ if elevator_type == "Traction"
211
+ elevator.definition.to_ElectricEquipmentDefinition.get.setFractionLost(1.0)
212
+ end
213
+
214
+ return elevator
215
+
216
+ end
217
+
218
+ end
@@ -0,0 +1,399 @@
1
+ # open the class to add methods to add exterior lighting
2
+ class OpenStudio::Model::Model
3
+
4
+ # Add exterior lighting to the model
5
+ #
6
+ # @param template [String] Valid choices are
7
+ # @param exterior_lighting_zone_number [Integer] Valid choices are
8
+ # @return [Hash] the resulting exterior lights
9
+ # @todo - would be nice to add argument for some building types (SmallHotel, MidriseApartment, PrimarySchool, SecondarySchool, RetailStripmall) if it has interior or exterior circulation.
10
+ def add_typical_exterior_lights(template,exterior_lighting_zone_number,onsite_parking_fraction = 1.0, add_base_site_allowance = false, use_model_for_entries_and_canopies = false)
11
+
12
+ exterior_lights = {}
13
+ installed_power = 0.0
14
+
15
+ # populate search hash
16
+ search_criteria = {
17
+ 'template' => template,
18
+ 'exterior_lighting_zone_number' => exterior_lighting_zone_number,
19
+ }
20
+
21
+ # load exterior_lighting_properties
22
+ exterior_lighting_properties = self.find_object($os_standards['exterior_lighting'], search_criteria)
23
+
24
+ # get building types and ratio (needed to get correct schedules, parking area, entries, canopies, and drive throughs)
25
+ space_type_hash = self.create_space_type_hash(template)
26
+
27
+ # get model specific values to map to exterior_lighting_properties
28
+ area_length_count_hash = self.create_exterior_lighting_area_length_count_hash(template,space_type_hash,use_model_for_entries_and_canopies)
29
+
30
+ # using midnight to 6am setback or shutdown
31
+ start_setback_shutoff = {:hr => 24, :min => 0}
32
+ end_setback_shutoff = {:hr => 6, :min => 0}
33
+ shuttoff = false
34
+ setback = false
35
+ if exterior_lighting_properties["building_facade_and_landscape_automatic_shut_off"] == 1
36
+ ext_lights_sch_facade_and_landscape = OpenStudio::Model::ScheduleRuleset.new(self)
37
+ default_day = ext_lights_sch_facade_and_landscape.defaultDaySchedule
38
+ default_day.addValue(OpenStudio::Time.new(0, end_setback_shutoff[:hr], end_setback_shutoff[:min], 0), 0.0)
39
+ default_day.addValue(OpenStudio::Time.new(0, start_setback_shutoff[:hr], start_setback_shutoff[:min], 0), 1.0)
40
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Facade and Landscape exterior lights shut off from #{start_setback_shutoff} to #{end_setback_shutoff}")
41
+ else
42
+ ext_lights_sch_facade_and_landscape = self.alwaysOnDiscreteSchedule
43
+ end
44
+ if not exterior_lighting_properties["occupancy_setback_reduction"].nil? and exterior_lighting_properties["occupancy_setback_reduction"] > 0.0
45
+ ext_lights_sch_other = OpenStudio::Model::ScheduleRuleset.new(self)
46
+ setback_value = 1.0 - exterior_lighting_properties["occupancy_setback_reduction"]
47
+ default_day = ext_lights_sch_other.defaultDaySchedule
48
+ default_day.addValue(OpenStudio::Time.new(0, end_setback_shutoff[:hr], end_setback_shutoff[:min], 0), setback_value)
49
+ default_day.addValue(OpenStudio::Time.new(0, start_setback_shutoff[:hr], start_setback_shutoff[:min], 0), 1.0)
50
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Non Facade and Landscape lights reduce by #{exterior_lighting_properties["occupancy_setback_reduction"]*100} % from #{start_setback_shutoff} to #{end_setback_shutoff}")
51
+ else
52
+ ext_lights_sch_other = self.alwaysOnDiscreteSchedule
53
+ end
54
+
55
+ # add exterior lights for parking area
56
+ if area_length_count_hash[:parking_area_and_drives_area] > 0
57
+
58
+ # lighting values
59
+ multiplier = area_length_count_hash[:parking_area_and_drives_area] * onsite_parking_fraction
60
+ power = exterior_lighting_properties["parking_areas_and_drives"]
61
+ name_prefix = "Parking Areas and Drives"
62
+
63
+ # create ext light def
64
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/ft^2 of lighting for #{multiplier} ft^2 of parking area.")
65
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
66
+ ext_lights_def.setName("#{name_prefix} Def (W/ft^2)")
67
+ ext_lights_def.setDesignLevel(power)
68
+
69
+ # create ext light inst
70
+ #creating exterior lights object
71
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
72
+ ext_lights.setMultiplier(multiplier)
73
+ ext_lights.setName(name_prefix)
74
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
75
+ ext_lights.setEndUseSubcategory(name_prefix)
76
+ exterior_lights[name_prefix] = ext_lights
77
+
78
+ # update installed power
79
+ installed_power += power * multiplier
80
+ end
81
+
82
+ # add exterior lights for facades
83
+ if area_length_count_hash[:building_facades] > 0
84
+
85
+ # lighting values
86
+ multiplier = area_length_count_hash[:building_facades]
87
+ power = exterior_lighting_properties["building_facades"]
88
+ name_prefix = "Building Facades"
89
+
90
+ # create ext light def
91
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/ft^2 of lighting for #{multiplier} ft^2 of building facade area.")
92
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
93
+ ext_lights_def.setName("#{name_prefix} Def (W/ft^2)")
94
+ ext_lights_def.setDesignLevel(power)
95
+
96
+ # create ext light inst
97
+ #creating exterior lights object
98
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_facade_and_landscape)
99
+ ext_lights.setMultiplier(multiplier)
100
+ ext_lights.setName(name_prefix)
101
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
102
+ ext_lights.setEndUseSubcategory(name_prefix)
103
+ exterior_lights[name_prefix] = ext_lights
104
+
105
+ # update installed power
106
+ installed_power += power * multiplier
107
+ end
108
+
109
+ # add exterior lights for main entries
110
+ if area_length_count_hash[:main_entries] > 0
111
+
112
+ # lighting values
113
+ multiplier = area_length_count_hash[:main_entries]
114
+ power = exterior_lighting_properties["main_entries"]
115
+ name_prefix = "Main Entries"
116
+
117
+ # create ext light def
118
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/ft of lighting for #{multiplier} ft of main entry length.")
119
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
120
+ ext_lights_def.setName("#{name_prefix} Def (W/ft)")
121
+ ext_lights_def.setDesignLevel(power)
122
+
123
+ # create ext light inst
124
+ #creating exterior lights object
125
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
126
+ ext_lights.setMultiplier(multiplier)
127
+ ext_lights.setName(name_prefix)
128
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
129
+ ext_lights.setEndUseSubcategory(name_prefix)
130
+ exterior_lights[name_prefix] = ext_lights
131
+
132
+ # update installed power
133
+ installed_power += power * multiplier
134
+ end
135
+
136
+ # add exterior lights for other doors
137
+ if area_length_count_hash[:other_doors] > 0
138
+
139
+ # lighting values
140
+ multiplier = area_length_count_hash[:other_doors]
141
+ power = exterior_lighting_properties["other_doors"]
142
+ name_prefix = "Other Doors"
143
+
144
+ # create ext light def
145
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/ft of lighting for #{multiplier} ft of other doors.")
146
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
147
+ ext_lights_def.setName("#{name_prefix} Def (W/ft)")
148
+ ext_lights_def.setDesignLevel(power)
149
+
150
+ # create ext light inst
151
+ #creating exterior lights object
152
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
153
+ ext_lights.setMultiplier(multiplier)
154
+ ext_lights.setName(name_prefix)
155
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
156
+ ext_lights.setEndUseSubcategory(name_prefix)
157
+ exterior_lights[name_prefix] = ext_lights
158
+
159
+ # update installed power
160
+ installed_power += power * multiplier
161
+ end
162
+
163
+ # add exterior lights for entry canopies
164
+ if area_length_count_hash[:canopy_entry_area] > 0
165
+
166
+ # lighting values
167
+ multiplier = area_length_count_hash[:canopy_entry_area]
168
+ power = exterior_lighting_properties["entry_canopies"]
169
+ name_prefix = "Entry Canopies"
170
+
171
+ # create ext light def
172
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/ft^2 of lighting for #{multiplier} ft^2 of building entry canopies.")
173
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
174
+ ext_lights_def.setName("#{name_prefix} Def (W/ft^2)")
175
+ ext_lights_def.setDesignLevel(power)
176
+
177
+ # create ext light inst
178
+ #creating exterior lights object
179
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
180
+ ext_lights.setMultiplier(multiplier)
181
+ ext_lights.setName(name_prefix)
182
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
183
+ ext_lights.setEndUseSubcategory(name_prefix)
184
+ exterior_lights[name_prefix] = ext_lights
185
+
186
+ # update installed power
187
+ installed_power += power * multiplier
188
+ end
189
+
190
+ # add exterior lights for emergency canopies
191
+ if area_length_count_hash[:canopy_emergency_area] > 0
192
+
193
+ # lighting values
194
+ multiplier = area_length_count_hash[:canopy_emergency_area]
195
+ power = exterior_lighting_properties["loading_areas_for_emergency_vehicles"]
196
+ name_prefix = "Emergency Canopies"
197
+
198
+ # create ext light def
199
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/ft^2 of lighting for #{multiplier} ft^2 of building emergency canopies.")
200
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
201
+ ext_lights_def.setName("#{name_prefix} Def (W/ft^2)")
202
+ ext_lights_def.setDesignLevel(power)
203
+
204
+ # create ext light inst
205
+ #creating exterior lights object
206
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
207
+ ext_lights.setMultiplier(multiplier)
208
+ ext_lights.setName(name_prefix)
209
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
210
+ ext_lights.setEndUseSubcategory(name_prefix)
211
+ exterior_lights[name_prefix] = ext_lights
212
+
213
+ # update installed power
214
+ installed_power += power * multiplier
215
+ end
216
+
217
+ # add exterior lights for drive through windows
218
+ if area_length_count_hash[:drive_through_windows] > 0
219
+
220
+ # lighting values
221
+ multiplier = area_length_count_hash[:drive_through_windows]
222
+ power = exterior_lighting_properties["drive_through_windows_and_doors"]
223
+ name_prefix = "Drive Through Windows"
224
+
225
+ # create ext light def
226
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W/drive through window of lighting for #{multiplier} drie through windows.")
227
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
228
+ ext_lights_def.setName("#{name_prefix} Def (W/ft^2)")
229
+ ext_lights_def.setDesignLevel(power)
230
+
231
+ # create ext light inst
232
+ #creating exterior lights object
233
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
234
+ ext_lights.setMultiplier(multiplier)
235
+ ext_lights.setName(name_prefix)
236
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
237
+ ext_lights.setEndUseSubcategory(name_prefix)
238
+ exterior_lights[name_prefix] = ext_lights
239
+
240
+ # update installed power
241
+ installed_power += power * multiplier
242
+ end
243
+
244
+ # todo - add_base_site_lighting_allowance (non landscaping tradable lighting)
245
+ # add exterior lights for drive through windows
246
+ if add_base_site_allowance
247
+
248
+ # lighting values
249
+ if not exterior_lighting_properties["base_site_allowance_power"].nil?
250
+ power = exterior_lighting_properties["base_site_allowance_power"]
251
+ elsif not exterior_lighting_properties["base_site_allowance_fraction"].nil?
252
+ power = exterior_lighting_properties["base_site_allowance_fraction"] * installed_power # shold be of allowed vs. installed, but hard to calculate
253
+ else
254
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Cannot determine target base site allowance power, will set to 0 W.")
255
+ power = 0.0
256
+ end
257
+ name_prefix = "Base Site Allowance"
258
+
259
+ # create ext light def
260
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Added #{power} W of non landscape tradable exterior lighting. Wil follow occupancy setback reduction.")
261
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(self)
262
+ ext_lights_def.setName("#{name_prefix} Def (W)")
263
+ ext_lights_def.setDesignLevel(power)
264
+
265
+ # create ext light inst
266
+ #creating exterior lights object
267
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def,ext_lights_sch_other)
268
+ ext_lights.setName(name_prefix)
269
+ ext_lights.setControlOption(exterior_lighting_properties["control_option"])
270
+ ext_lights.setEndUseSubcategory(name_prefix)
271
+ exterior_lights[name_prefix] = ext_lights
272
+
273
+ # don't need to update installed power for this
274
+ end
275
+
276
+ return exterior_lights
277
+
278
+ end
279
+
280
+ # get exterior lighting area's, distances, and counts
281
+ #
282
+ # @return [hash] hash of exterior lighting value types and building type and model specific values
283
+ # @todo - add code in to determine number of entries and canopy area from model geoemtry
284
+ # @todo - come up with better logic for entry widths
285
+ def create_exterior_lighting_area_length_count_hash(template,space_type_hash,use_model_for_entries_and_canopies)
286
+
287
+ area_length_count_hash = {}
288
+
289
+ # populate building_type_hash, used to remap office
290
+ building_type_hash = {}
291
+ space_type_hash.each do |space_type,hash|
292
+
293
+ # update building_type_hash
294
+ if building_type_hash.has_key?(hash[:stds_bldg_type])
295
+ building_type_hash[hash[:stds_bldg_type]] += hash[:floor_area]
296
+ else
297
+ building_type_hash[hash[:stds_bldg_type]] = hash[:floor_area]
298
+ end
299
+ end
300
+ # rename Office to SmallOffice MediumOffice or LargeOffice
301
+ office_type = nil
302
+ if building_type_hash.has_key?("Office")
303
+ office_type = self.remap_office(building_type_hash["Office"])
304
+ end
305
+
306
+ # parking areas and drives area
307
+ parking_area_and_drives_area = 0.0
308
+ main_entries = 0.0
309
+ other_doors = 0.0
310
+ canopy_entry_area = 0.0
311
+ canopy_emergency_area = 0.0
312
+ drive_through_windows = 0.0
313
+ # run space_type_hash to get number of students and units and building type floor area totals
314
+ space_type_hash.each do |space_type,hash|
315
+
316
+ # rename space types as needed
317
+ if hash[:stds_bldg_type] == "Office"
318
+ building_type = office_type
319
+ else
320
+ building_type = hash[:stds_bldg_type]
321
+ end
322
+
323
+ # store floor area ip
324
+ floor_area_ip = OpenStudio::convert(hash[:floor_area],"m^2","ft^2").get
325
+ num_spots = 0.0
326
+
327
+ # load illuminated_parking_area_properties
328
+ search_criteria = {'building_type' => building_type}
329
+ illuminated_parking_area_lookup = self.find_object($os_standards['parking'], search_criteria)
330
+ if not illuminated_parking_area_lookup["building_area_per_spot"].nil?
331
+ num_spots += floor_area_ip / illuminated_parking_area_lookup["building_area_per_spot"].to_f
332
+ elsif not illuminated_parking_area_lookup["units_per_spot"].nil?
333
+ num_spots += hash[:num_units] / illuminated_parking_area_lookup["units_per_spot"].to_f
334
+ elsif not illuminated_parking_area_lookup["students_per_spot"].nil?
335
+ num_spots += hash[:num_students] / illuminated_parking_area_lookup["students_per_spot"].to_f
336
+ elsif not illuminated_parking_area_lookup["beds_per_spot"].nil?
337
+ num_spots += hash[:num_beds] / illuminated_parking_area_lookup["beds_per_spot"].to_f
338
+ else
339
+ OpenStudio.logFree(OpenStudio::Info, 'Prototype.Model.exterior_lights', "Unexpected key, can't calculate number of parking spots from #{illuminated_parking_area_lookup.keys.first}.")
340
+ end
341
+ parking_area_and_drives_area += num_spots * illuminated_parking_area_lookup["parking_area_per_spot"]
342
+
343
+ # load illuninated_parking_area_properties
344
+ search_criteria = {'building_type' => building_type}
345
+ exterior_lighting_assumptions_lookup = self.find_object($os_standards['entryways'], search_criteria)
346
+
347
+ # lookup doors
348
+ if use_model_for_entries_and_canopies
349
+ # todo - get number of entries and canopy size from model geometry
350
+ else
351
+
352
+ # no source for width of different entry types
353
+ main_entry_width_ip = 8 # ft
354
+ other_doors_width_ip = 4 # ft
355
+
356
+ # rollup not used
357
+ main_entries += (floor_area_ip/10000.0) * exterior_lighting_assumptions_lookup["entrance_doors_per_10,000"] * main_entry_width_ip
358
+ other_doors += (floor_area_ip/10000.0) * exterior_lighting_assumptions_lookup["others_doors_per_10,000"] * other_doors_width_ip
359
+ if not exterior_lighting_assumptions_lookup["floor_area_per_drive_through_window"].nil?
360
+ drive_through_windows += floor_area_ip / exterior_lighting_assumptions_lookup["floor_area_per_drive_through_window"].to_f
361
+ end
362
+
363
+
364
+ # if any space types of building type that has canopy, then use that value, don't add to count for additional space types
365
+ if not exterior_lighting_assumptions_lookup["entrance_canopies"].nil? and not exterior_lighting_assumptions_lookup["canopy_size"].nil?
366
+ canopy_entry_area = exterior_lighting_assumptions_lookup["entrance_canopies"] * exterior_lighting_assumptions_lookup["canopy_size"]
367
+ end
368
+ if not exterior_lighting_assumptions_lookup["emergency_canopies"].nil? and not exterior_lighting_assumptions_lookup["canopy_size"].nil?
369
+ canopy_emergency_area = exterior_lighting_assumptions_lookup["emergency_canopies"]* exterior_lighting_assumptions_lookup["canopy_size"]
370
+ end
371
+
372
+ end
373
+
374
+ end
375
+
376
+ # populate hash
377
+ area_length_count_hash[:parking_area_and_drives_area] = parking_area_and_drives_area
378
+ area_length_count_hash[:main_entries] = main_entries
379
+ area_length_count_hash[:other_doors] = other_doors
380
+ area_length_count_hash[:canopy_entry_area] = canopy_entry_area
381
+ area_length_count_hash[:canopy_emergency_area] = canopy_emergency_area
382
+ area_length_count_hash[:drive_through_windows] = drive_through_windows
383
+
384
+ # determine effective number of stories to find first above grade story exterior wall area
385
+ effective_num_stories = self.effective_num_stories
386
+ ground_story = effective_num_stories[:story_hash].keys[effective_num_stories[:below_grade]]
387
+ ground_story_ext_wall_area_si = effective_num_stories[:story_hash][ground_story][:ext_wall_area]
388
+ ground_story_ext_wall_area_ip = OpenStudio::convert(ground_story_ext_wall_area_si,"m^2","ft^2").get
389
+
390
+ # building_facades
391
+ # reference buildings uses first story and plenum area all around
392
+ # prototype uses Table 4.19 by building type lit facde vs. total facade.
393
+ area_length_count_hash[:building_facades] = ground_story_ext_wall_area_ip
394
+
395
+ return area_length_count_hash
396
+
397
+ end
398
+
399
+ end