openstudio-standards 0.8.5 → 0.8.7

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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/test_performance_expected_dd_results.csv +90 -90
  3. data/lib/openstudio-standards/btap/activity.rb +858 -61
  4. data/lib/openstudio-standards/btap/attributes.rb +297 -103
  5. data/lib/openstudio-standards/btap/bridging.rb +1335 -593
  6. data/lib/openstudio-standards/btap/btap.rb +60 -181
  7. data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
  8. data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
  9. data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
  10. data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
  11. data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
  12. data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
  13. data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
  14. data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
  15. data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
  16. data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
  17. data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
  18. data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
  19. data/lib/openstudio-standards/btap/costing/database.rb +104 -0
  20. data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
  21. data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
  22. data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
  23. data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
  24. data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
  25. data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
  26. data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
  27. data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
  28. data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
  29. data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
  30. data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
  31. data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
  32. data/lib/openstudio-standards/btap/envelope.rb +313 -572
  33. data/lib/openstudio-standards/btap/fileio.rb +152 -1070
  34. data/lib/openstudio-standards/btap/geometry.rb +486 -766
  35. data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
  36. data/lib/openstudio-standards/btap/paths.rb +46 -0
  37. data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
  38. data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
  39. data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
  40. data/lib/openstudio-standards/btap/schedules.rb +11 -611
  41. data/lib/openstudio-standards/btap/structure.rb +574 -189
  42. data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
  43. data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
  44. data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
  45. data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
  46. data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
  47. data/lib/openstudio-standards/geometry/create.rb +7 -3
  48. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
  49. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
  50. data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
  51. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
  52. data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
  53. data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
  54. data/lib/openstudio-standards/schedules/parametric.rb +6 -0
  55. data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
  56. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
  57. data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
  58. data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
  59. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
  60. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
  61. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
  62. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
  63. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
  64. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
  65. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
  66. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
  67. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
  68. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
  69. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
  70. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
  71. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
  72. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
  73. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
  74. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
  75. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
  76. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
  77. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
  78. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
  79. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
  80. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6900 -1150
  81. data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
  82. data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
  83. data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
  84. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
  85. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
  86. data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
  87. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
  88. data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
  89. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
  90. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
  91. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
  92. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
  93. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
  94. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
  95. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
  96. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
  97. data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
  98. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
  99. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
  100. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
  101. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
  102. data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
  103. data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
  104. data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
  105. data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
  106. data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
  107. data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
  108. data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
  109. data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
  110. data/lib/openstudio-standards/standards/standard.rb +1 -1
  111. data/lib/openstudio-standards/version.rb +1 -1
  112. data/lib/openstudio-standards.rb +9 -4
  113. metadata +59 -66
  114. data/data/inventory/thermal_bridging.csv +0 -90
  115. data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
  116. data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
  117. data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
  118. data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
  119. data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
  120. data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
  121. data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
  122. data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
  123. data/lib/openstudio-standards/btap/common_paths.rb +0 -81
  124. data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
  125. data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
  126. data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
  127. data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
  128. data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
  129. data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
  130. data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
  131. data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
  132. data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
  133. data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
  134. data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
  135. data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
  136. data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
  137. data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
  138. data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
  139. data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
  140. data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
  141. data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
  142. data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
  143. data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
  144. data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
  145. data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
  146. data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
  147. data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
  148. data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
  149. data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
  150. data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
  151. data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
  152. data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
  153. data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
  154. data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
  155. data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
  156. data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
  157. data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
  158. data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
  159. data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
  160. data/lib/openstudio-standards/btap/visualization.rb +0 -72
  161. data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
  162. data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
  163. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
  164. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
  165. data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
  166. data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
  167. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
  168. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
  169. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
  170. /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
  171. /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
  172. /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
  173. /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
  174. /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
@@ -0,0 +1,1033 @@
1
+ # BTAP Deprecated
2
+ #
3
+ # 2026-07-09
4
+ # This file will house methods that aren't used inside OpenStudio Standards.
5
+ # NRCan task 486 involves cleaning up unused code, however since OpenStudio
6
+ # Standards is a library, other people could be using these methods despite them
7
+ # not being used inside this repository. For now, they are marked as deprecated
8
+ # unless someone expresses otherwise. If there are no concerns these methods
9
+ # will soon be removed for the purpose of making developer operations simpler.
10
+
11
+ module BTAP
12
+ module Deprecated
13
+ def self.msg(klass, method_name)
14
+ warn("[BTAP::Deprecated] #{klass}##{method_name} is considered for " \
15
+ "deletion for the future as of July 2026. If you are still using" \
16
+ "this method, please contact" \
17
+ "nicholas.pneumaticos@nrcan-rncan.gc.ca.")
18
+ end
19
+ end # module Deprecated
20
+
21
+ module Geometry
22
+ def self.enumerate_spaces_model(model, prepend_name = false)
23
+ #enumerate stories.
24
+ BTAP::Geometry::BuildingStoreys::auto_assign_spaces_to_stories(model)
25
+ #Enumerate spaces
26
+ model.getBuildingStorys.sort.each do |story|
27
+ spaces = Array.new
28
+ spaces.concat(story.spaces)
29
+ spaces.sort! do |a, b|
30
+ (a.xOrigin <=> b.xOrigin).nonzero? ||
31
+ (a.yOrigin <=> b.yOrigin)
32
+ end
33
+ counter = 1
34
+ spaces.sort.each do |space|
35
+ #puts "old space name : #{space.name}"
36
+ if prepend_name == true
37
+ space.setName("#{story.name}-#{counter.to_s}:#{space.name}")
38
+ else
39
+ space.setName("#{story.name}-#{counter.to_s}")
40
+ end
41
+ counter = counter + 1
42
+ p #uts "new space name : #{space.name}"
43
+ end
44
+ end
45
+ end
46
+
47
+ # this was a copy of the sketchup plugin method.
48
+ def self.rename_zones_based_on_spaces(model)
49
+
50
+ # loop through thermal zones
51
+ model.getThermalZones.sort.each do |thermal_zone| # this is going through all, not just selection
52
+ #puts "old zone name : #{thermal_zone.name}"
53
+ # reset the array of spaces to be empty
54
+ spaces_in_thermal_zone = []
55
+ # reset length of array of spaces
56
+ number_of_spaces = 0
57
+
58
+ # get list of spaces in thermal zone
59
+ spaces = thermal_zone.spaces
60
+ spaces.sort.each do |space|
61
+
62
+ # make an array instead of the puts statement
63
+ spaces_in_thermal_zone.push space.name.to_s
64
+
65
+ end
66
+
67
+ # store length of array
68
+ number_of_spaces = spaces_in_thermal_zone.size
69
+
70
+ # sort the array
71
+ spaces_in_thermal_zone = spaces_in_thermal_zone.sort
72
+
73
+ # setup a suffix if the thermal zone contains more than one space
74
+ if number_of_spaces > 1
75
+ multi = " - Plus"
76
+ else
77
+ multi = ""
78
+ end
79
+
80
+ # rename thermal zone based on first space with prefix added e.g. ThermalZone 203
81
+ if number_of_spaces > 0
82
+ new_name = "ZN:" + spaces_in_thermal_zone[0] + multi
83
+ thermal_zone.setName(new_name)
84
+ else
85
+ puts "#{thermal_zone.name.to_s} did not have any spaces, and will not be renamed."
86
+ end
87
+ #puts "new zone name : #{thermal_zone.name}"
88
+ end
89
+ end
90
+
91
+ #This method will rename the zone equipment to have the zone name as a prefix for a model.
92
+ #It will also rename the hot water coils for:
93
+ # AirTerminalSingleDuctVAVReheat
94
+ # ZoneHVACBaseboardConvectiveWater
95
+ # ZoneHVACUnitHeater
96
+
97
+ def self.prefix_equipment_with_zone_name(model)
98
+ #puts "Renaming zone equipment."
99
+ # get all thermal zones
100
+ thermal_zones = model.getThermalZones
101
+
102
+ # loop through thermal zones
103
+ thermal_zones.each do |thermal_zone| # this is going through all, not just selection
104
+
105
+ thermal_zone.equipment.each do |equip|
106
+
107
+ #For the hydronic conditions below only, it will rename the zonal coils as well.
108
+ if not equip.to_AirTerminalSingleDuctVAVReheat.empty?
109
+
110
+ equip.setName("#{thermal_zone.name}:AirTerminalSingleDuctVAVReheat")
111
+ reheat_coil = equip.to_AirTerminalSingleDuctVAVReheat.get.reheatCoil
112
+ reheat_coil.setName("#{thermal_zone.name}:ReheatCoil")
113
+ #puts reheat_coil.name
114
+ elsif not equip.to_ZoneHVACBaseboardConvectiveWater.empty?
115
+ equip.setName("#{thermal_zone.name}:ZoneHVACBaseboardConvectiveWater")
116
+ heatingCoil = equip.to_ZoneHVACBaseboardConvectiveWater.get.heatingCoil
117
+ heatingCoil.setName("#{thermal_zone.name}:Baseboard HW Htg Coil")
118
+ #puts heatingCoil.name
119
+ elsif not equip.to_ZoneHVACUnitHeater.empty?
120
+ equip.setName("#{thermal_zone.name}:ZoneHVACUnitHeater")
121
+ heatingCoil = equip.to_ZoneHVACUnitHeater.get.heatingCoil
122
+ heatingCoil.setName("#{thermal_zone.name}:Unit Heater Htg Coil")
123
+ #puts heatingCoil.name
124
+ #Add more cases if you wish!!!!!
125
+ else #if the equipment does not follow the above cases, rename
126
+ # it generically and not touch the underlying coils, etc.
127
+ equip.setName("#{thermal_zone.name}:#{equip.name}")
128
+ end
129
+
130
+ end
131
+ end
132
+ end
133
+
134
+ module BuildingStoreys
135
+ #This method will delete any exisiting stories and then try to assign stories based on
136
+ # the z-axis origin of the space.
137
+ def self.auto_assign_spaces_to_stories(model)
138
+ #delete existing stories.
139
+ model.getBuildingStorys.sort.each {|buildingstory| buildingstory.remove}
140
+ #create hash of building storeys, index is the Z-axis origin of the space.
141
+ building_story_hash = Hash.new()
142
+ model.getSpaces.sort.each do |space|
143
+ if building_story_hash[space.zOrigin].nil?
144
+ building_story_hash[space.zOrigin] = OpenStudio::Model::BuildingStory.new(model)
145
+ building_story_hash[space.zOrigin].setName(building_story_hash.length.to_s)
146
+ end
147
+
148
+
149
+ space.setBuildingStory(building_story_hash[space.zOrigin])
150
+ end
151
+ end
152
+
153
+ # override run to implement the functionality of your script
154
+ # model is an OpenStudio::Model::Model, runner is a OpenStudio::Ruleset::UserScriptRunner
155
+ def self.auto_assign_stories(model)
156
+
157
+ # get all spaces
158
+ spaces = model.getSpaces
159
+
160
+ #puts("Assigning Stories to Spaces")
161
+
162
+ # make has of spaces and minz values
163
+ sorted_spaces = Hash.new
164
+ spaces.sort.each do |space|
165
+ # loop through space surfaces to find min z value
166
+ z_points = []
167
+ space.surfaces.each do |surface|
168
+ surface.vertices.each do |vertex|
169
+ z_points << vertex.z
170
+ end
171
+ end
172
+ minz = z_points.min + space.zOrigin
173
+ sorted_spaces[space] = minz
174
+ end
175
+
176
+ # pre-sort spaces
177
+ sorted_spaces = sorted_spaces.sort {|a, b| a[1] <=> b[1]}
178
+
179
+
180
+ # this should take the sorted list and make and assign stories
181
+ sorted_spaces.sort.each do |space|
182
+ space_obj = space[0]
183
+ space_minz = space[1]
184
+ if space_obj.buildingStory.empty?
185
+ story = OpenstudioStandards::Geometry.model_get_building_story_for_nominal_height(model, space_minz)
186
+ if story.nil?
187
+ story = OpenStudio::Model::BuildingStory.new(model)
188
+ story.setNominalZCoordinate(space_minz)
189
+ story.setName("Building Story #{space_minz.round(1)}m")
190
+ end
191
+ space_obj.setBuildingStory(story)
192
+ end
193
+ end
194
+ end
195
+ end # module BuildingStoreys
196
+
197
+ #This module contains helper functions that deal with Space objects.
198
+ module Spaces
199
+
200
+ # This method will return the horizontal placement type. (N,S,W,E,C) In the
201
+ # case of a corner, it will take whatever surface area it faces is the
202
+ # largest. It will also return the top, bottom or middle conditions.
203
+ def self.get_space_placement(space)
204
+ horizontal_placement = nil
205
+ vertical_placement = nil
206
+ json_data = nil
207
+
208
+ #get all exterior surfaces.
209
+ surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces,
210
+ ["Outdoors",
211
+ "Ground",
212
+ "GroundFCfactorMethod",
213
+ "GroundSlabPreprocessorAverage",
214
+ "GroundSlabPreprocessorCore",
215
+ "GroundSlabPreprocessorPerimeter",
216
+ "GroundBasementPreprocessorAverageWall",
217
+ "GroundBasementPreprocessorAverageFloor",
218
+ "GroundBasementPreprocessorUpperWall",
219
+ "GroundBasementPreprocessorLowerWall"])
220
+
221
+ #exterior Surfaces
222
+ ext_wall_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["Wall"])
223
+ ext_bottom_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["Floor"])
224
+ ext_top_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, ["RoofCeiling"])
225
+
226
+ #Interior Surfaces..if needed....
227
+ internal_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, ["Surface"])
228
+ int_wall_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["Wall"])
229
+ int_bottom_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["Floor"])
230
+ int_top_surface = BTAP::Geometry::Surfaces::filter_by_surface_types(internal_surfaces, ["RoofCeiling"])
231
+
232
+
233
+ vertical_placement = "NA"
234
+ #determine if space is a top or bottom, both or middle space.
235
+ if ext_bottom_surface.size > 0 and ext_top_surface.size > 0 and int_bottom_surface.size == 0 and int_top_surface.size == 0
236
+ vertical_placement = "single_story_space"
237
+ elsif int_bottom_surface.size > 0 and ext_top_surface.size > 0 and int_bottom_surface.size > 0
238
+ vertical_placement = "top"
239
+ elsif ext_bottom_surface.size > 0 and ext_top_surface.size == 0
240
+ vertical_placement = "bottom"
241
+ elsif ext_bottom_surface.size == 0 and ext_top_surface.size == 0
242
+ vertical_placement = "middle"
243
+ end
244
+
245
+
246
+ #determine if what cardinal direction has the majority of external
247
+ #surface area of the space.
248
+ #set this to 'core' by default and change it if it is found to be a space exposed to a cardinal direction.
249
+ horizontal_placement = nil
250
+ #set up summing hashes for each direction.
251
+ json_data = Hash.new
252
+ walls_area_array = Hash.new
253
+ subsurface_area_array = Hash.new
254
+ boundary_conditions = {}
255
+ boundary_conditions[:outdoors] = ["Outdoors"]
256
+ boundary_conditions[:ground] = [
257
+ "Ground",
258
+ "GroundFCfactorMethod",
259
+ "GroundSlabPreprocessorAverage",
260
+ "GroundSlabPreprocessorCore",
261
+ "GroundSlabPreprocessorPerimeter",
262
+ "GroundBasementPreprocessorAverageWall",
263
+ "GroundBasementPreprocessorAverageFloor",
264
+ "GroundBasementPreprocessorUpperWall",
265
+ "GroundBasementPreprocessorLowerWall"]
266
+ #go through all directions.. need to do north twice since that goes around zero degree mark.
267
+ orientations = [
268
+ {:surface_type => 'Wall', :direction => 'north', :azimuth_from => 0.00, :azimuth_to => 45.0, :tilt_from => 0.0, :tilt_to => 180.0},
269
+ {:surface_type => 'Wall', :direction => 'north', :azimuth_from => 315.001, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0},
270
+ {:surface_type => 'Wall', :direction => 'east', :azimuth_from => 45.001, :azimuth_to => 135.0, :tilt_from => 0.0, :tilt_to => 180.0},
271
+ {:surface_type => 'Wall', :direction => 'south', :azimuth_from => 135.001, :azimuth_to => 225.0, :tilt_from => 0.0, :tilt_to => 180.0},
272
+ {:surface_type => 'Wall', :direction => 'west', :azimuth_from => 225.001, :azimuth_to => 315.0, :tilt_from => 0.0, :tilt_to => 180.0},
273
+ {:surface_type => 'RoofCeiling', :direction => 'top', :azimuth_from => 0.0, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0},
274
+ {:surface_type => 'Floor', :direction => 'bottom', :azimuth_from => 0.0, :azimuth_to => 360.0, :tilt_from => 0.0, :tilt_to => 180.0}
275
+ ]
276
+ [:outdoors, :ground].each do |bc|
277
+ orientations.each do |orientation|
278
+ walls_area_array[orientation[:direction]] = 0.0
279
+ subsurface_area_array[orientation[:direction]] = 0.0
280
+ json_data[orientation[:direction]] = {} if json_data[orientation[:direction]].nil?
281
+ json_data[orientation[:direction]][bc] = {:surface_area => 0.0,
282
+ :glazed_subsurface_area => 0.0,
283
+ :opaque_subsurface_area => 0.0}
284
+
285
+ end
286
+ end
287
+
288
+ [:outdoors, :ground].each do |bc|
289
+ orientations.each do |orientation|
290
+ # puts "bc= #{bc}"
291
+ # puts boundary_conditions[bc.to_sym]
292
+ # puts boundary_conditions
293
+ surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, boundary_conditions[bc])
294
+ selected_surfaces = BTAP::Geometry::Surfaces::filter_by_surface_types(surfaces, [orientation[:surface_type]])
295
+ BTAP::Geometry::Surfaces::filter_by_azimuth_and_tilt(selected_surfaces, orientation[:azimuth_from], orientation[:azimuth_to], orientation[:tilt_from], orientation[:tilt_to]).each do |surface|
296
+ #sum wall area and subsurface area by direction. This is the old way so excluding top and bottom surfaces.
297
+ walls_area_array[orientation[:direction]] += surface.grossArea unless ['RoofCeiling', 'Floor'].include?(orientation[:surface_type])
298
+ subsurface_area_array[orientation[:direction]] += surface.subSurfaces.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
299
+ json_data[orientation[:direction]][bc][:surface_area] += surface.grossArea
300
+ glazings = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(surface.subSurfaces, ["FixedWindow", "OperableWindow", "GlassDoor", "Skylight", "TubularDaylightDiffuser", "TubularDaylightDome"])
301
+ doors = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(surface.subSurfaces, ["Door", "OverheadDoor"])
302
+ json_data[orientation[:direction]][bc][:glazed_subsurface_area] += glazings.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
303
+ json_data[orientation[:direction]][bc][:opaque_subsurface_area] += doors.map {|subsurface| subsurface.grossArea}.inject(0) {|sum, x| sum + x}
304
+ end
305
+ end
306
+ end
307
+ puts JSON.pretty_generate(json_data)
308
+
309
+ puts walls_area_array
310
+ #find if no direction
311
+ sum= 0.0
312
+ ['north','east','south','west'].each do |direction|
313
+ [:outdoors,:ground].each do |bc|
314
+ sum += json_data[direction][bc][:surface_area]
315
+ end
316
+ end
317
+ if sum == 0.0
318
+ horizontal_placement = "core"
319
+ else
320
+ #find our which cardinal direction has the most exterior surface and declare it that orientation.
321
+ horizontal_placement = walls_area_array.max_by {|k, v| v}[0] #include ext and ground.
322
+ end
323
+
324
+ #save JSON data
325
+ json_data = ({:horizontal_placement => horizontal_placement,
326
+ :vertical_placement => vertical_placement,
327
+ }).merge(json_data)
328
+ puts JSON.pretty_generate(json_data)
329
+
330
+ return json_data
331
+ end
332
+
333
+ def self.is_perimeter_space?(model, space)
334
+ exterior_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(
335
+ space.surfaces,
336
+ ["Outdoors",
337
+ "Ground",
338
+ "GroundFCfactorMethod",
339
+ "GroundSlabPreprocessorAverage",
340
+ "GroundSlabPreprocessorCore",
341
+ "GroundSlabPreprocessorPerimeter",
342
+ "GroundBasementPreprocessorAverageWall",
343
+ "GroundBasementPreprocessorAverageFloor",
344
+ "GroundBasementPreprocessorUpperWall",
345
+ "GroundBasementPreprocessorLowerWall"])
346
+
347
+ return BTAP::Geometry::Surfaces::filter_by_surface_types(exterior_surfaces, ["Wall"]).size > 0
348
+ end
349
+
350
+ def self.show(model, space)
351
+ if drawing_interface = BTAP::Common::validate_array(model, space, "Space").first.drawing_interface
352
+ if entity = drawing_interface.entity
353
+ entity.visible = true
354
+ end
355
+ end
356
+ end
357
+
358
+ def self.hide(model, space)
359
+ if drawing_interface = BTAP::Common::validate_array(model, space, "Space").first.drawing_interface
360
+ if entity = drawing_interface.entity
361
+ entity.visible = false
362
+ end
363
+ end
364
+ end
365
+
366
+ # This method will filter an array of spaces that have an external wall
367
+ # passed floors. Note: if you wish to avoid to create an array of spaces,
368
+ # simply put the space variable in [] brackets
369
+ # Ex: get_all_surfaces_from_spaces( [space1,space2] )
370
+ # @param spaces_array an array of type [OpenStudio::Model::Space]
371
+ # @return an array of spaces.
372
+ def self.filter_perimeter_spaces(model, spaces_array)
373
+ spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
374
+ array = Array.new()
375
+ spaces_array.each do |space|
376
+ if space.is_a_perimeter_space?()
377
+ array.push(space)
378
+ end
379
+ end
380
+ return array
381
+ end
382
+
383
+ # This method will filter an array of spaces that have no external wall
384
+ # passed floors. Note: if you wish to avoid to create an array of spaces,
385
+ # simply put the space variable in [] brackets
386
+ # Ex: get_all_surfaces_from_spaces( [space1,space2] )
387
+ # @param spaces_array an array of type [OpenStudio::Model::Space]
388
+ # @return an array of spaces.
389
+ def self.filter_core_spaces(model, spaces_array)
390
+ spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
391
+ array = Array.new()
392
+ spaces_array.each do |space|
393
+ unless space.is_a_perimeter_space?()
394
+ array.push(space)
395
+ end
396
+ end
397
+ return array
398
+ end
399
+
400
+ def self.filter_spaces_by_space_types(model, spaces_array, spacetype_array)
401
+ spaces_array = BTAP::Common::validate_array(model, spaces_array, "Space")
402
+ spacetype_array = BTAP::Common::validate_array(model, spacetype_array, "SpaceType")
403
+ #validate space array
404
+ returnarray = Array.new()
405
+ spaces_array.each do |space|
406
+ returnarray << spacetype_array.include?(space.spaceType())
407
+ end
408
+ return returnarray
409
+ end
410
+ end # module Spaces
411
+
412
+ module Zones
413
+
414
+ # This method will filter an array of zones that have an external wall
415
+ # passed floors. Note: if you wish to avoid to create an array of spaces,
416
+ # simply put the space variable in [] brackets
417
+ # Ex: get_all_surfaces_from_spaces( [space1,space2] )
418
+ # @param thermal_zones [Array<OpenStudio::Model::ThermalZone>] an array of zones
419
+ # @return [Array<OpenStudio::Model::ThermalZone] an array of thermal zones.
420
+ def self.filter_perimeter_zones(thermal_zones)
421
+ array = Array.new()
422
+ thermal_zones.each do |zone|
423
+ zone.space.each do |space|
424
+ if space.is_a_perimeter_space?()
425
+ array.push(zone)
426
+ next
427
+ end
428
+ end
429
+ end
430
+ return array
431
+ end
432
+
433
+ # This method will filter an array of zones that have no external wall
434
+ # passed floors. Note: if you wish to avoid to create an array of spaces,
435
+ # simply put the space variable in [] brackets
436
+ # Ex: ( [space1,space2] )
437
+ # @param thermal_zones [Array<OpenStudio::Model::ThermalZone] an array of zones
438
+ # @return [Array<OpenStudio::Model::ThermalZone] an array of zones
439
+ def self.filter_core_zones(thermal_zones)
440
+ array = Array.new()
441
+ thermal_zones.getThermalZones.sort.each do |zone|
442
+ zone.space.each do |space|
443
+ if not space.is_a_perimeter_space?()
444
+ array.push(zone)
445
+ next
446
+ end
447
+ end
448
+ end
449
+ return array
450
+ end
451
+ end # module Zones
452
+
453
+ module Surfaces
454
+ def self.create_surface(model, name, os_point3d_array, boundary_condition = "", construction = "")
455
+ os_surface = OpenStudio::Model::Surface.new(os_point3d_array, model)
456
+ os_surface.setName(name)
457
+ if OpenStudio::Model::Surface::validOutsideBoundaryConditionValues.include?(boundary_condition)
458
+ self.set_surfaces_boundary_condition([os_surface], boundary_condition)
459
+ else
460
+ puts "boundary condition not set for #{name}"
461
+ end
462
+ os_surface.setConstruction(construction)
463
+ return os_surface
464
+ end
465
+
466
+ # This method will rotate a surface
467
+ # @param planar_surfaces [Array<OpenStudio::Model::Surface>] an array of surfaces
468
+ # @param azimuth_degrees [Float] rotation value
469
+ # @param tilt_degrees [Float] rotation value
470
+ # @param translation_vector [OpenStudio::Vector3d] a vector along which to move all surfaces
471
+ # @return [OpenStudio::Model::Model] the model object.
472
+ def self.rotate_tilt_translate_surfaces(planar_surfaces, azimuth_degrees, tilt_degrees = 0.0, translation_vector = OpenStudio::Vector3d.new(0.0, 0.0, 0.0))
473
+ # Identity matrix for setting space origins
474
+ azimuth_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), azimuth_degrees * Math::PI / 180)
475
+ tilt_matrix = OpenStudio::Transformation::rotation(OpenStudio::Vector3d.new(0, 0, 1), tilt_degrees * Math::PI / 180)
476
+ translation_matrix = OpenStudio::createTranslation(translation_vector)
477
+ planar_surfaces.each do |surface|
478
+ surface.changeTransformation(azimuth_matrix)
479
+ surface.changeTransformation(tilt_matrix)
480
+ surface.changeTransformation(translation_matrix)
481
+ end
482
+ return planar_surfaces
483
+ end
484
+
485
+ def self.set_fenestration_to_wall_ratio(surfaces, ratio, offset = 0, height_offset_from_floor = true, floor = "all")
486
+ surfaces.each do |surface|
487
+ result = surface.setWindowToWallRatio(ratio, offset, height_offset_from_floor)
488
+ raise("Unable to set FWR for surface " +
489
+ surface.name.get.to_s +
490
+ " . Possible reasons are if the surface is not a wall, if the surface
491
+ is not rectangular in face coordinates, if requested ratio is too large
492
+ (window area ~= surface area) or too small (min dimension of window < 1 foot),
493
+ or if the window clips any remaining sub surfaces. Otherwise, removes all
494
+ existing windows and adds new window to meet requested ratio.") unless result
495
+ end
496
+ return surfaces
497
+ end
498
+
499
+ def self.filter_by_non_defaulted_surfaces(surfaces)
500
+ non_defaulted_surfaces = Array.new()
501
+ surfaces.each {|surface| non_defaulted_surfaces << surface unless surface.isConstructionDefaulted}
502
+ return non_defaulted_surfaces
503
+ end
504
+
505
+ def self.filter_by_interzonal_surface(surfaces)
506
+ return_array = Array.new()
507
+ surfaces.each do |surface|
508
+ unless surface.adjacentSurface().empty?
509
+ return_array.push(surface)
510
+ end
511
+ return return_array
512
+ end
513
+ end
514
+
515
+ # Azimuth start from Y axis, Tilts starts from Z-axis
516
+ def self.filter_by_azimuth_and_tilt(surfaces, azimuth_from, azimuth_to, tilt_from, tilt_to, tolerance = 1.0)
517
+ return_surfaces = []
518
+ surfaces.each do |surface|
519
+ unless OpenStudio::Model::PlanarSurface::findPlanarSurfaces([surface], OpenStudio::OptionalDouble.new(azimuth_from), OpenStudio::OptionalDouble.new(azimuth_to), OpenStudio::OptionalDouble.new(tilt_from), OpenStudio::OptionalDouble.new(tilt_to), tolerance).empty?
520
+ return_surfaces << surface
521
+ end
522
+ end
523
+ return return_surfaces
524
+ end
525
+
526
+ def self.show(surfaces)
527
+ surfaces.each do |surface|
528
+ if drawing_interface = surface.drawing_interface
529
+ if entity = drawing_interface.entity
530
+ entity.visible = false
531
+ end
532
+ end
533
+ end
534
+ end
535
+
536
+ def self.hide(surfaces)
537
+ surfaces.each do |surface|
538
+ if drawing_interface = surface.drawing_interface
539
+ if entity = drawing_interface.entity
540
+ entity.visible = false
541
+ end
542
+ end
543
+ end
544
+ end
545
+ end # module Surfaces
546
+ end # module Geometry
547
+
548
+ module Schedules
549
+ module Fraction
550
+ def self.always_off(model)
551
+ fraction_always_off_name = "FRACTION_ALWAYS_OFF"
552
+ schedule = model.getScheduleRulesetByName(fraction_always_off_name)
553
+ if schedule.empty?
554
+ #create Schedule
555
+ return BTAP::Resources::Schedules::create_annual_constant_ruleset_schedule(
556
+ fraction_always_off_name,
557
+ "FRACTION",
558
+ 0.0)
559
+ else
560
+ return schedule.get
561
+ end
562
+ end
563
+
564
+ def self.always_on(model)
565
+ fraction_always_on_name = "FRACTION_ALWAYS_ON"
566
+ schedule = model.getScheduleRulesetByName(fraction_always_on_name)
567
+ if schedule.empty?
568
+ #create Schedule
569
+ return BTAP::Resources::Schedules::create_annual_constant_ruleset_schedule(
570
+ model,
571
+ fraction_always_on_name,
572
+ "FRACTION",
573
+ 1.0)
574
+ else
575
+ return schedule.get
576
+ end
577
+ end
578
+ end # module Fraction
579
+
580
+ module ON_OFF
581
+ def self.always_on(model)
582
+ on_off_always_on = "ON_OFF_ALWAYS_ON"
583
+ schedule = model.getScheduleRulesetByName(on_off_always_on)
584
+ if schedule.empty?
585
+ #create Schedule
586
+ return BTAP::Resources::Schedules::create_annual_constant_ruleset_schedule(
587
+ model,
588
+ on_off_always_on,
589
+ "ON_OFF",
590
+ 1)
591
+ else
592
+ return schedule.get
593
+ end
594
+ end
595
+ end # module ON_OFF
596
+
597
+ module Temperature
598
+ def self.no_heating(model)
599
+ no_heating = "NO_HEATING_SETPOINT"
600
+ schedule = model.getScheduleRulesetByName(no_heating)
601
+ if schedule.empty?
602
+ #create Schedule
603
+ return BTAP::Resources::Schedules::create_annual_constant_ruleset_schedule(
604
+ model,
605
+ no_heating,
606
+ "TEMPERATURE",
607
+ -200.0)
608
+ else
609
+ return schedule.get
610
+ end
611
+ end
612
+
613
+ def self.no_cooling(model)
614
+ no_cooling = "NO_COOLING_SETPOINT"
615
+ schedule = model.getScheduleRulesetByName(no_cooling)
616
+ if schedule.empty?
617
+ #create Schedule
618
+ return BTAP::Resources::Schedules::create_annual_constant_ruleset_schedule(
619
+ model,
620
+ no_cooling,
621
+ "TEMPERATURE",
622
+ 200.0)
623
+ else
624
+ return schedule.get
625
+ end
626
+ end
627
+
628
+ def self.no_heating_cooling_dual_setpoint_schedule(model)
629
+ dual_setpoint_name = "FREE_FLOATING_DUAL_SETPOINT_THERMOSTAT"
630
+ schedule = model.getScheduleRulesetByName(dual_setpoint_name)
631
+ if schedule.empty?
632
+ #create Schedule
633
+ return BTAP::Resources::Schedules::create_annual_thermostat_setpoint_dual_setpoint( model, dual_setpoint_name, self.heating_setpoint_off, self.cooling_setpoint_off)
634
+ else
635
+ return schedule.getThermostatSetpointDualSetpointByName()
636
+ end
637
+ end
638
+ end # module Temperature
639
+
640
+ def self.remove_all_schedules(model)
641
+ model.getScheduleBases.sort.each { |item| item.remove }
642
+ end
643
+
644
+ def self.create_zonal_occupancy_schedule_on_off(model, thermal_zone)
645
+ model.getFanZoneExhausts.sort.each {|zfe| puts "Fan Ex:#{zfe}"}
646
+
647
+ # Create new timeseries object to keep track of on/off states. Default to 30min intervals.
648
+ timeseries = OpenStudio::TimeSeries.new
649
+ thermal_zone.spaces.sort.each do |space|
650
+ #Iterate through the people object in the space.
651
+ space.spaceType.get.people.each do |people|
652
+ if people.numberofPeopleSchedule.is_initialized() and people.numberofPeopleSchedule.get.to_ScheduleRuleset.is_initialized
653
+ #Get the occupancy schedule.
654
+ occ_schedule = people.numberofPeopleSchedule.get
655
+ #Convert schedule to timeseries and sum up timeseries objects for this space / zone.
656
+ occ_time_series = create_timeseries_from_schedule_ruleset(model,occ_schedule)
657
+ timeseries = timeseries + occ_time_series
658
+ end
659
+ end
660
+ end
661
+ # Return the timeseries converted to
662
+ return create_schedule_variable_interval_from_time_series(model,timeseries)
663
+ end
664
+
665
+ # This method will only work with a single occupancy definition.
666
+ def self.set_exhaust_fans_availability_to_building_default_occ_schedule(model)
667
+ # get occupancy schedule if possible.
668
+ if model.building.get.defaultScheduleSet.is_initialized and
669
+ model.building.get.defaultScheduleSet.get.numberofPeopleSchedule.is_initialized and
670
+ model.building.get.defaultScheduleSet.get.numberofPeopleSchedule.get.to_ScheduleRuleset.is_initialized
671
+ occ_schedule = model.building.get.defaultScheduleSet.get.numberofPeopleSchedule.get
672
+ #get building default occupancy schedule.
673
+ model.getFanZoneExhausts.sort.each do |zfe|
674
+ zfe.setAvailabilitySchedule(occ_schedule)
675
+ zfe.setBalancedExhaustFractionSchedule(occ_schedule)
676
+ end
677
+ else
678
+ raise ("Default occupancy schedule has not been set in model! Unsure what to set exhaust fans to. Exiting.")
679
+ end
680
+ return model.getFanZoneExhausts
681
+ end
682
+
683
+ def self.modify_schedule(model, schedule_ruleset, a_coef = 0.0 ,b_coef = 0.0 ,c_coef= 0.0 ,time_shift = nil,time_sign = nil)
684
+ new_schedule = schedule_ruleset.clone( model ).to_ScheduleRuleset.get
685
+ self.modify_schedule!(model, new_schedule, a_coef,b_coef,c_coef ,time_shift,time_sign)
686
+ end
687
+
688
+ def self.modify_schedule!(model, schedule_ruleset, a_coef = 0.0 ,b_coef = 0.0 ,c_coef= 0.0 ,time_shift = nil,time_sign = nil)
689
+ schedule_ruleset.scheduleRules.each do |week_rule|
690
+ day_rule = week_rule.daySchedule()
691
+ times = day_rule.times()
692
+ times.each do |time|
693
+ old_value = day_rule.getValue(time)
694
+ day_rule.removeValue(time)
695
+ new_value = "error"
696
+ new_time = "error"
697
+ #set the new value according to Ax2+Bx+C.
698
+ new_value = a_coef * old_value ** 2.0 + b_coef * old_value + c_coef
699
+ unless time_shift.nil? or time_sign.nil?
700
+ command = "new_time = time #{time_sign} #{BTAP::Common::get_time_from_string(time_shift)}"
701
+ eval(command)
702
+ #make sure time is not past 24 hours.
703
+ new_time = new_time - BTAP::Common::get_time_from_string("24:00") if new_time > BTAP::Common::get_time_from_string("24:00")
704
+ end
705
+ day_rule.addValue(time, new_value)
706
+ end
707
+ end
708
+ end
709
+
710
+ def self.create_availability_schedule_based_on_another_schedule(model, occupancy_schedule, threshold = 0.05)
711
+ #create new On-Off schedule ruleset.
712
+ availability_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
713
+ availability_ruleset.setName("availabilty_based_on_occupancy")
714
+ #iterate though all rules.
715
+ occupancy_schedule.scheduleRules.each do |occ_rule|
716
+ #Create new hourly rule to populate availability schedule hourly data.
717
+ hourly_data = OpenStudio::Model::ScheduleDay.new(model)
718
+ #set schedule type to availabilty.
719
+ hourly_data.setScheduleTypeLimits(BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_on_off(model))
720
+ #iterate though hour / value pairs for 24 hour period.
721
+ occ_rule.daySchedule().times().each do |time|
722
+ #check if time value is greater or equal to the threshold. If true set hour value to 1, else 0.
723
+ occ_rule.daySchedule().getValue(time) >= threshold ? hourly_data.addValue(time, 1.0) : hourly_data.addValue(time, 0.0)
724
+ end
725
+ #create new rule with hourly data and add to availablity ruleset.
726
+ avail_rule = OpenStudio::Model::ScheduleRule.new(availability_ruleset,hourly_data)
727
+ #Set same start and end date.
728
+ avail_rule.setStartDate(occ_rule.getStartDate)
729
+ avail_rule.setEndDate(occ_rule.getEndDate)
730
+ end #loop occ_rule
731
+ #Make sure to set the default schedule to be the same as well.
732
+ avail_default_day = availability_ruleset.defaultDaySchedule()
733
+ avail_default_day.clearValues()
734
+ #iterate though hour / value pairs for 24 hour period.
735
+ occupancy_schedule.defaultDaySchedule().times().each do |time|
736
+ #check if time value is greater or equal to the threshold. If true set hour value to 1, else 0.
737
+ occupancy_schedule.defaultDaySchedule().getValue(time) >= threshold ? avail_default_day.addValue(time, 1.0) : avail_default_day.addValue(time, 0.0)
738
+ end
739
+ return availability_ruleset
740
+ end
741
+
742
+
743
+ def self.create_setback_schedule_based_on_another_schedule(
744
+ model,
745
+ occupancy_schedule,
746
+ threshold = 0.05,
747
+ heat_setpoint = 22.0,
748
+ heat_setback = 17.0,
749
+ cool_setpoint = 24.0,
750
+ cool_setback =99.0)
751
+ #create new On-Off schedule ruleset.
752
+ heating_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
753
+ heating_ruleset.setName("heat_thermostat_based_on_occupancy")
754
+ cooling_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
755
+ cooling_ruleset.setName("cool_thermostat_based_on_occupancy")
756
+ #iterate though all rules.
757
+ occupancy_schedule.to_ScheduleRuleset.get.scheduleRules.each do |occ_rule|
758
+ #Create new hourly rule to populate heat/cold schedule hourly data.
759
+ heating_hourly_data = OpenStudio::Model::ScheduleDay.new(model)
760
+ cooling_hourly_data = OpenStudio::Model::ScheduleDay.new(model)
761
+ #set schedule type to availabilty.
762
+ heating_hourly_data.setScheduleTypeLimits(BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_temperature(model))
763
+ cooling_hourly_data.setScheduleTypeLimits(BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_temperature(model))
764
+ #iterate though hour / value pairs for 24 hour period.
765
+ occ_rule.daySchedule().times().each do |time|
766
+ #check if time value is greater or equal to the threshold. If true set hour value to 1, else 0.
767
+ occ_rule.daySchedule().getValue(time) >= threshold ? heating_hourly_data.addValue(time, heat_setpoint) : heating_hourly_data.addValue(time,heat_setback)
768
+ occ_rule.daySchedule().getValue(time) >= threshold ? cooling_hourly_data.addValue(time, cool_setpoint) : cooling_hourly_data.addValue(time,cool_setback)
769
+ end
770
+ #create new rule with hourly data and add to availablity ruleset.
771
+ heating_avail_rule = OpenStudio::Model::ScheduleRule.new(heating_ruleset,heating_hourly_data)
772
+ cooling_avail_rule = OpenStudio::Model::ScheduleRule.new(cooling_ruleset,cooling_hourly_data)
773
+ #Set same start and end date.
774
+ heating_avail_rule.setStartDate(occ_rule.startDate.get)
775
+ heating_avail_rule.setEndDate(occ_rule.endDate.get)
776
+ cooling_avail_rule.setStartDate(occ_rule.startDate.get)
777
+ cooling_avail_rule.setEndDate(occ_rule.endDate.get)
778
+ #set days enforced.
779
+ heating_avail_rule.setApplySunday(occ_rule.applySunday)
780
+ heating_avail_rule.setApplyMonday(occ_rule.applyMonday)
781
+ heating_avail_rule.setApplyTuesday(occ_rule.applyTuesday)
782
+ heating_avail_rule.setApplyWednesday(occ_rule.applyWednesday)
783
+ heating_avail_rule.setApplyThursday(occ_rule.applyThursday)
784
+ heating_avail_rule.setApplyFriday(occ_rule.applyFriday)
785
+ heating_avail_rule.setApplySaturday(occ_rule.applySaturday)
786
+
787
+ cooling_avail_rule.setApplySunday(occ_rule.applySunday)
788
+ cooling_avail_rule.setApplyMonday(occ_rule.applyMonday)
789
+ cooling_avail_rule.setApplyTuesday(occ_rule.applyTuesday)
790
+ cooling_avail_rule.setApplyWednesday(occ_rule.applyWednesday)
791
+ cooling_avail_rule.setApplyThursday(occ_rule.applyThursday)
792
+ cooling_avail_rule.setApplyFriday(occ_rule.applyFriday)
793
+ cooling_avail_rule.setApplySaturday(occ_rule.applySaturday)
794
+
795
+
796
+ end #loop occ_rule
797
+ #Make sure to set the default schedule to be the same as well.
798
+ heating_default_day = heating_ruleset.defaultDaySchedule()
799
+ heating_default_day.clearValues()
800
+ cooling_default_day = cooling_ruleset.defaultDaySchedule()
801
+ cooling_default_day.clearValues()
802
+
803
+ #iterate though hour / value pairs for 24 hour period.
804
+ occupancy_schedule.to_ScheduleRuleset.get.defaultDaySchedule().times().each do |time|
805
+ #check if time value is greater or equal to the threshold. If true set hour value to 1, else 0.
806
+ occupancy_schedule.to_ScheduleRuleset.get.defaultDaySchedule().getValue(time) >= threshold ? heating_default_day.addValue(time, heat_setpoint) : heating_default_day.addValue(time, heat_setback)
807
+ occupancy_schedule.to_ScheduleRuleset.get.defaultDaySchedule().getValue(time) >= threshold ? cooling_default_day.addValue(time, cool_setpoint) : cooling_default_day.addValue(time, cool_setback)
808
+ end
809
+ return heating_ruleset,cooling_ruleset
810
+ end
811
+
812
+ #Sets all values in a schedule less than min_value to min_value.
813
+ def self.apply_schedule_minimum(min_value,schedule)
814
+ schedule_ruleset = schedule.to_ScheduleRuleset.get unless schedule.to_ScheduleRuleset.empty?
815
+ schedule_ruleset.scheduleRules.each do |week_rule|
816
+ day_rule = week_rule.daySchedule()
817
+ times = day_rule.times()
818
+ times.each do |time|
819
+ old_value = day_rule.getValue(time).to_f
820
+ day_rule.removeValue(time)
821
+ new_value = old_value
822
+ new_value = min_value if old_value < min_value
823
+ day_rule.addValue(time, new_value)
824
+ end
825
+ end
826
+ end
827
+
828
+ #Sets all values in a schedule greater than max_value to max_value.
829
+ def self.apply_schedule_maximum(max_value,schedule)
830
+ schedule_ruleset = schedule.to_ScheduleRuleset.get unless schedule.to_ScheduleRuleset.empty?
831
+ schedule_ruleset.scheduleRules.each do |week_rule|
832
+ day_rule = week_rule.daySchedule()
833
+ times = day_rule.times()
834
+ times.each do |time|
835
+ old_value = day_rule.getValue(time).to_f
836
+ day_rule.removeValue(time)
837
+ new_value = old_value
838
+ new_value = max_value if old_value > max_value
839
+ day_rule.addValue(time, new_value)
840
+ end
841
+ end
842
+ end
843
+
844
+ def self.create_annual_ruleset_schedule_detailed(model,name,type,schedule_struct )
845
+ #create new ruleset
846
+ ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
847
+ ruleset.setName(name)
848
+ default_day = ruleset.defaultDaySchedule
849
+
850
+ #set types limits
851
+ scheduletype = ""
852
+ case type.downcase
853
+ when "FRACTION".downcase
854
+ scheduletype = BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_fraction(model)
855
+ when "ON_OFF".downcase
856
+ scheduletype = BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_on_off(model)
857
+ when "TEMPERATURE".downcase
858
+ scheduletype = BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_temperature(model)
859
+ # this will set the default day for temperatures to 23.5C
860
+ default_day.clearValues()
861
+ raise "unable to set ScheduleDay type limits" unless default_day.setScheduleTypeLimits(scheduletype)
862
+ default_day.addValue(BTAP::Common::get_time_from_string( "24:00"), 23.5 )
863
+ when "ACTIVITY".downcase
864
+ scheduletype = BTAP::Resources::Schedules::StandardScheduleTypeLimits::get_activity(model)
865
+ # this will set the default day for temperatures to 23.5C
866
+ default_day.clearValues()
867
+ raise "unable to set ScheduleDay type limits" unless default_day.setScheduleTypeLimits(scheduletype)
868
+ default_day.addValue(BTAP::Common::get_time_from_string( "24:00"), 120.0 )
869
+ else
870
+ #if schedule type could not be found raise an exception.
871
+ raise "could not find schedule limits type :" + type
872
+ end
873
+
874
+ #loop through each schedule ruleset.
875
+ schedule_struct.each do |run_period_profile|
876
+ start_end_dates = run_period_profile[0]
877
+ days_of_the_week = run_period_profile[1]
878
+ hourly_schedule = run_period_profile[2]
879
+ day_rule = OpenStudio::Model::ScheduleDay.new(model)
880
+ day_rule.setName( name )
881
+ if not day_rule.setScheduleTypeLimits(scheduletype)
882
+ raise "unable to set ScheduleDay type limits"
883
+ end
884
+
885
+ hourly_schedule.each do |hour|
886
+ day_rule.addValue(BTAP::Common::get_time_from_string( hour[0]), hour[1] )
887
+ end
888
+
889
+ #create weekday rule
890
+ week_rule = OpenStudio::Model::ScheduleRule.new(ruleset,day_rule)
891
+ #Set Default to false
892
+ week_rule.setApplySunday(false)
893
+ week_rule.setApplyMonday(false)
894
+ week_rule.setApplyTuesday(false)
895
+ week_rule.setApplyWednesday(false)
896
+ week_rule.setApplyThursday(false)
897
+ week_rule.setApplyFriday(false)
898
+ week_rule.setApplySaturday(false)
899
+ # Now set actual days it is applied.
900
+ week_rule.setApplySunday(true) if days_of_the_week.include?("Su") or days_of_the_week.include?("Wke") or days_of_the_week.include?("All")
901
+ week_rule.setApplyMonday(true) if days_of_the_week.include?("M") or days_of_the_week.include?("Wkd") or days_of_the_week.include?("All")
902
+ week_rule.setApplyTuesday(true) if days_of_the_week.include?("T") or days_of_the_week.include?("Wkd") or days_of_the_week.include?("All")
903
+ week_rule.setApplyWednesday(true) if days_of_the_week.include?("W") or days_of_the_week.include?("Wkd") or days_of_the_week.include?("All")
904
+ week_rule.setApplyThursday(true) if days_of_the_week.include?("Th") or days_of_the_week.include?("Wkd") or days_of_the_week.include?("All")
905
+ week_rule.setApplyFriday(true) if days_of_the_week.include?("F") or days_of_the_week.include?("Wkd") or days_of_the_week.include?("All")
906
+ week_rule.setApplySaturday(true) if days_of_the_week.include?("S") or days_of_the_week.include?("Wke") or days_of_the_week.include?("All")
907
+
908
+ #Set Period Rule
909
+ week_rule.setStartDate( BTAP::Common::get_date_from_string(start_end_dates[0] ) )
910
+ week_rule.setEndDate( BTAP::Common::get_date_from_string(start_end_dates[1] ) )
911
+ end
912
+ return ruleset
913
+ end
914
+
915
+ def self.create_annual_fraction_ruleset_schedule(model,name,hourArrayValues)
916
+ self.create_annual_ruleset_schedule(model,name,"FRACTION",hourArrayValues)
917
+ end
918
+
919
+ def self.create_annual_on_off_ruleset_schedule(model,name,hourArrayValues)
920
+ self.create_annual_ruleset_schedule(model,name,"ON_OFF",hourArrayValues)
921
+ end
922
+
923
+ def self.create_annual_temperature_ruleset_schedule(model,name,hourArrayValues)
924
+ self.create_annual_ruleset_schedule(model,name,"TEMPERATURE",hourArrayValues)
925
+ end
926
+ end # module Schedules
927
+
928
+ module Resources
929
+ module Envelope
930
+ module Constructions
931
+
932
+ # This checks if the construction layer can be modified to set thermal resistance of the whole construction to
933
+ # be less than the required resistance
934
+ # @author Chris Kirney <chris.kirney@canada.ca>
935
+ # @param mat_resistance <Fixnum>
936
+ # @param total_conductance <Fixnum>
937
+ # @param req_conductance <Fixnum>
938
+ # @return [<Fixnum>] layer resistance needed to meet construction material resistance, -999 if this is not enough
939
+ def self.should_modify_layer(mat_resistance:, total_conductance:, req_conductance:)
940
+ # Determine if the amount of resistance you can modify in this layer is greater than the amount of resistance
941
+ # you have to change.
942
+ if mat_resistance > ((1.0 / total_conductance) - (1.0 / req_conductance))
943
+ # If yes, determine what the resistance for this layer should be to meet the required resistance of the
944
+ # entire assembly. Then return the new resistance value.
945
+ target_res = mat_resistance - ((1.0 / total_conductance) - (1.0 / req_conductance))
946
+ return target_res
947
+ else
948
+ # If no, then return an unambiguous no.
949
+ return -999
950
+ end
951
+ end
952
+ end # module Constructions
953
+
954
+ module ConstructionSets
955
+
956
+ #This method set the default construction set from an OSM library file and the construction set name.
957
+ #params construction_library_file [String] Path to osm file that contains the contruction set to be used.
958
+ #params construction_set_name [String] Name of the construction set to be used.
959
+ def self.set_construction_set_by_file(model, construction_library_file, construction_set_name, runner = nil)
960
+ BTAP::runner_register("Info", "set_construction_set_by_file(#{construction_library_file}, #{construction_set_name})")
961
+ #check if file exists
962
+ unless File.exist?(construction_library_file) == true
963
+ BTAP::runner_register("Error", "Could not find #{construction_library_file}", runner)
964
+ return false
965
+ end
966
+ construction_set = BTAP::Resources::Envelope::ConstructionSets::get_construction_set_from_library(construction_library_file, construction_set_name)
967
+ #check if construction set name exists and can apply to the model.
968
+ unless model.building.get.setDefaultConstructionSet(construction_set.clone(model).to_DefaultConstructionSet.get)
969
+ BTAP::runner_register("Error", "Could not use default construction set #{construction_set_name} from #{construction_library_file} ", runner)
970
+ return false
971
+ end
972
+ #sets all surfaces to use default constructions except adiabatic, where it does a hard assignment of the interior wall construction type.
973
+ model.getPlanarSurfaces.sort.each { |item| item.resetConstruction }
974
+ #if the default construction set is defined..try to assign the interior wall to the adiabatic surfaces
975
+ BTAP::Resources::Envelope::assign_interior_surface_construction_to_adiabatic_surfaces(model, runner)
976
+ BTAP::runner_register("Info", "set_construction_set_by_file(#{construction_library_file}, #{construction_set_name}) Completed Sucessfully.")
977
+ return true
978
+ end
979
+
980
+ #This method gets construction set object from external library
981
+ #@author phylroy.lopez@nrcan.gc.ca
982
+ #@param construction_library_file [String]
983
+ #@param construction_set_name [String]
984
+ #@return [Boolean] optional_construction_set
985
+ def self.get_construction_set_from_library(construction_library_file, construction_set_name)
986
+ #Load Contruction osm library.
987
+ if File.exist?(construction_library_file)
988
+ construction_lib = BTAP::FileIO::load_osm(construction_library_file)
989
+ #Get construction set..
990
+ optional_construction_set = construction_lib.getDefaultConstructionSetByName(construction_set_name)
991
+ if optional_construction_set.empty?
992
+ raise("#{construction_set_name} does not exist in #{construction_library_file} library ")
993
+ else
994
+ return optional_construction_set.get
995
+ end
996
+ else
997
+ raise("Error : Construction Lib #{construction_library_file} does not exist!")
998
+ end
999
+ return false
1000
+ end
1001
+ end # module ConstructionSets
1002
+ end # module Envelope
1003
+ end # module Resources
1004
+
1005
+ # Deprecate all the methods in the BTAP module according to the proc below.
1006
+ # Note for this to work properly this file should be the first BTAP-related
1007
+ # file required by `btap.rb` so that only the methods in this file are
1008
+ # deprecated.
1009
+ @deprecate_methods = proc do |target_klass, display_klass, methods_list|
1010
+ methods_list.each do |method_name|
1011
+ old_method = target_klass.instance_method(method_name)
1012
+ target_klass.class_eval do
1013
+ define_method(method_name) do |*args, &block|
1014
+ Deprecated.msg(display_klass, method_name)
1015
+ old_method.bind_call(self, *args, &block)
1016
+ end
1017
+ end
1018
+ end
1019
+ end
1020
+
1021
+ def self.deprecate_all_nested(namespace)
1022
+ namespace.constants(false).select { |c| c != :Deprecated }.each do |name|
1023
+ next unless namespace.const_defined?(name, false)
1024
+ const = namespace.const_get(name)
1025
+ next unless const.is_a?(Module)
1026
+ @deprecate_methods.call(const, const, const.instance_methods(false))
1027
+ @deprecate_methods.call(const.singleton_class, const, const.methods(false))
1028
+ deprecate_all_nested(const)
1029
+ end
1030
+ end
1031
+
1032
+ deprecate_all_nested(self)
1033
+ end