openstudio-geb 0.2.1 → 0.3.1r

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/CHANGELOG.md +3 -0
  4. data/Gemfile +6 -1
  5. data/doc_templates/LICENSE.md +7 -21
  6. data/doc_templates/copyright_erb.txt +2 -32
  7. data/doc_templates/copyright_js.txt +2 -2
  8. data/doc_templates/copyright_ruby.txt +3 -33
  9. data/lib/measures/Add Output Variable/LICENSE.md +7 -21
  10. data/lib/measures/Add Output Variable/measure.rb +3 -33
  11. data/lib/measures/Add Output Variable/measure.xml +128 -125
  12. data/lib/measures/Add Output Variable/tests/AddOutputVariable_Test.rb +2 -32
  13. data/lib/measures/AddElectricVehicleChargingLoad/LICENSE.md +13 -1
  14. data/lib/measures/AddElectricVehicleChargingLoad/measure.rb +16 -52
  15. data/lib/measures/AddElectricVehicleChargingLoad/measure.xml +24 -23
  16. data/lib/measures/AddElectricVehicleChargingLoad/tests/add_electric_vehicle_charging_load_test.rb +2 -32
  17. data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/LICENSE.md +7 -21
  18. data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/README.md +22 -16
  19. data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/measure.rb +14 -35
  20. data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/measure.xml +41 -30
  21. data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/tests/AdjustThermostatSetpointsByDegreesForPeakHours_Test.rb +2 -32
  22. data/lib/measures/Enable Demand Controlled Ventilation/LICENSE.md +7 -21
  23. data/lib/measures/Enable Demand Controlled Ventilation/measure.rb +2 -32
  24. data/lib/measures/Enable Demand Controlled Ventilation/measure.xml +14 -13
  25. data/lib/measures/Enable Demand Controlled Ventilation/tests/EnableDemandControlledVentilation_Test.rb +2 -32
  26. data/lib/measures/GEB Metrics Report/LICENSE.md +13 -1
  27. data/lib/measures/GEB Metrics Report/measure.rb +2 -37
  28. data/lib/measures/GEB Metrics Report/measure.xml +238 -23
  29. data/lib/measures/GEB Metrics Report/resources/os_lib_helper_methods.rb +2 -32
  30. data/lib/measures/GEB Metrics Report/resources/os_lib_reporting.rb +2 -32
  31. data/lib/measures/GEB Metrics Report/tests/geb_metrics_report_test.rb +2 -32
  32. data/lib/measures/add_ceiling_fan/LICENSE.md +13 -0
  33. data/lib/measures/add_ceiling_fan/README.md +119 -0
  34. data/lib/measures/add_ceiling_fan/README.md.erb +45 -0
  35. data/lib/measures/add_ceiling_fan/docs/.gitkeep +0 -0
  36. data/lib/measures/add_ceiling_fan/measure.rb +565 -0
  37. data/lib/measures/add_ceiling_fan/measure.xml +226 -0
  38. data/lib/measures/add_ceiling_fan/tests/CZ06RV2.epw +8768 -0
  39. data/lib/measures/add_ceiling_fan/tests/MediumOffice-90.1-2010-ASHRAE 169-2013-5A.osm +13669 -0
  40. data/lib/measures/add_ceiling_fan/tests/SFD_1story_UB_UA_ASHP2_HPWH.osm +13110 -0
  41. data/lib/measures/add_ceiling_fan/tests/add_ceiling_fan_test.rb +82 -0
  42. data/lib/measures/add_chilled_water_storage_tank/LICENSE.md +13 -1
  43. data/lib/measures/add_chilled_water_storage_tank/measure.rb +56 -56
  44. data/lib/measures/add_chilled_water_storage_tank/measure.xml +201 -28
  45. data/lib/measures/add_chilled_water_storage_tank/tests/add_chilled_water_storage_tank_test.rb +4 -34
  46. data/lib/measures/add_chilled_water_storage_tank/tests/example_model.osm +8077 -21624
  47. data/lib/measures/add_electrochromic_window/LICENSE.md +13 -1
  48. data/lib/measures/add_electrochromic_window/measure.rb +2 -32
  49. data/lib/measures/add_electrochromic_window/measure.xml +28 -27
  50. data/lib/measures/add_electrochromic_window/tests/add_electrochromic_window_test.rb +2 -32
  51. data/lib/measures/add_exterior_blinds_and_control/LICENSE.md +13 -1
  52. data/lib/measures/add_exterior_blinds_and_control/measure.rb +2 -34
  53. data/lib/measures/add_exterior_blinds_and_control/measure.xml +15 -26
  54. data/lib/measures/add_exterior_blinds_and_control/tests/add_exterior_blinds_and_control_test.rb +2 -34
  55. data/lib/measures/add_heat_pump_water_heater/LICENSE.md +7 -21
  56. data/lib/measures/add_heat_pump_water_heater/measure.rb +2 -32
  57. data/lib/measures/add_heat_pump_water_heater/measure.xml +89 -17
  58. data/lib/measures/add_heat_pump_water_heater/tests/add_hpwh_test.rb +2 -32
  59. data/lib/measures/add_interior_blinds_and_control/LICENSE.md +13 -1
  60. data/lib/measures/add_interior_blinds_and_control/measure.rb +2 -32
  61. data/lib/measures/add_interior_blinds_and_control/measure.xml +21 -33
  62. data/lib/measures/add_interior_blinds_and_control/tests/add_exterior_blinds_and_control_test.rb +2 -32
  63. data/lib/measures/add_natural_ventilation_with_hybrid_control/LICENSE.md +13 -0
  64. data/lib/measures/add_natural_ventilation_with_hybrid_control/README.md +133 -0
  65. data/lib/measures/add_natural_ventilation_with_hybrid_control/README.md.erb +45 -0
  66. data/lib/measures/add_natural_ventilation_with_hybrid_control/docs/.gitkeep +0 -0
  67. data/lib/measures/add_natural_ventilation_with_hybrid_control/measure.rb +453 -0
  68. data/lib/measures/add_natural_ventilation_with_hybrid_control/measure.xml +247 -0
  69. data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/CZ06RV2.epw +8768 -0
  70. data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/MediumOffice-90.1-2010-ASHRAE 169-2013-5A.osm +13669 -0
  71. data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/SmallHotel-2A.osm +42899 -0
  72. data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/USA_NY_Buffalo.Niagara.Intl.AP.725280_TMY3.epw +8768 -0
  73. data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/USA_TX_Houston-Bush.Intercontinental.AP.722430_TMY3.epw +8768 -0
  74. data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/add_natural_ventilation_with_hybrid_control_test.rb +95 -0
  75. data/lib/measures/add_rooftop_pv_simple/LICENSE.md +7 -21
  76. data/lib/measures/add_rooftop_pv_simple/measure.rb +2 -32
  77. data/lib/measures/add_rooftop_pv_simple/measure.xml +11 -5
  78. data/lib/measures/add_rooftop_pv_simple/tests/add_rooftop_pv_test.rb +2 -32
  79. data/lib/measures/adjust_dhw_setpoint/LICENSE.md +13 -1
  80. data/lib/measures/adjust_dhw_setpoint/measure.rb +2 -32
  81. data/lib/measures/adjust_dhw_setpoint/measure.xml +34 -21
  82. data/lib/measures/adjust_dhw_setpoint/tests/adjust_dhw_setpoint_test.rb +2 -32
  83. data/lib/measures/average_ventilation_for_peak_hours/LICENSE.md +13 -1
  84. data/lib/measures/average_ventilation_for_peak_hours/README.md +5 -45
  85. data/lib/measures/average_ventilation_for_peak_hours/measure.rb +2 -32
  86. data/lib/measures/average_ventilation_for_peak_hours/measure.xml +24 -79
  87. data/lib/measures/average_ventilation_for_peak_hours/tests/average_ventilation_for_peak_hours_test.rb +2 -32
  88. data/lib/measures/enable_occupancy_driven_lighting/LICENSE.md +13 -1
  89. data/lib/measures/enable_occupancy_driven_lighting/measure.rb +2 -32
  90. data/lib/measures/enable_occupancy_driven_lighting/measure.xml +28 -27
  91. data/lib/measures/enable_occupancy_driven_lighting/tests/enable_occupancy_driven_lighting_test.rb +2 -32
  92. data/lib/measures/precooling/LICENSE.md +7 -21
  93. data/lib/measures/precooling/measure.rb +2 -33
  94. data/lib/measures/precooling/measure.xml +12 -11
  95. data/lib/measures/preheating/LICENSE.md +7 -21
  96. data/lib/measures/preheating/measure.rb +2 -33
  97. data/lib/measures/preheating/measure.xml +18 -17
  98. data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/LICENSE.md +13 -1
  99. data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/measure.rb +2 -32
  100. data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/measure.xml +34 -27
  101. data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/tests/reduce_domestic_hot_water_use_for_peak_hours_test.rb +2 -32
  102. data/lib/measures/reduce_epd_by_percentage_for_peak_hours/LICENSE.md +13 -1
  103. data/lib/measures/reduce_epd_by_percentage_for_peak_hours/README.md +3 -3
  104. data/lib/measures/reduce_epd_by_percentage_for_peak_hours/measure.rb +2 -32
  105. data/lib/measures/reduce_epd_by_percentage_for_peak_hours/measure.xml +25 -18
  106. data/lib/measures/reduce_epd_by_percentage_for_peak_hours/tests/reduce_epd_by_percentage_for_peak_hours_copy_test.rb +2 -32
  107. data/lib/measures/reduce_exterior_lighting_loads/LICENSE.md +13 -0
  108. data/lib/measures/reduce_exterior_lighting_loads/README.md +79 -0
  109. data/lib/measures/reduce_exterior_lighting_loads/README.md.erb +45 -0
  110. data/lib/measures/reduce_exterior_lighting_loads/docs/.gitkeep +0 -0
  111. data/lib/measures/reduce_exterior_lighting_loads/measure.rb +385 -0
  112. data/lib/measures/reduce_exterior_lighting_loads/measure.xml +195 -0
  113. data/lib/measures/reduce_exterior_lighting_loads/tests/CZ06RV2.epw +8768 -0
  114. data/lib/measures/reduce_exterior_lighting_loads/tests/SFD_1story_UB_UA_ASHP2_HPWH.osm +13110 -0
  115. data/lib/measures/reduce_exterior_lighting_loads/tests/example_model.osm +8077 -0
  116. data/lib/measures/reduce_exterior_lighting_loads/tests/reduce_exterior_lighting_loads_test.rb +81 -0
  117. data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/LICENSE.md +13 -1
  118. data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/measure.rb +2 -32
  119. data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/measure.xml +30 -23
  120. data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/tests/reduce_lpd_by_percentage_for_peak_hours_test.rb +2 -32
  121. data/lib/openstudio/geb/extension.rb +2 -32
  122. data/lib/openstudio/geb/logging.rb +2 -32
  123. data/lib/openstudio/geb/run.rb +2 -32
  124. data/lib/openstudio/geb/utilities.rb +2 -32
  125. data/lib/openstudio/geb/version.rb +3 -33
  126. data/lib/openstudio/geb.rb +2 -32
  127. data/lib/openstudio-geb.rb +5 -0
  128. data/openstudio-geb.gemspec +2 -3
  129. metadata +41 -8
@@ -0,0 +1,133 @@
1
+
2
+
3
+ ###### (Automatically generated documentation)
4
+
5
+ # Add natural ventilation with hybrid control
6
+
7
+ ## Description
8
+ This measure adds natural ventilation to all the zones with operable windows, and controls natural ventilation together with HVAC in a hybrid manner. More specifically, HVAC will be disabled when windows are open, and HVAC will be available when windows are closed.
9
+
10
+ ## Modeler Description
11
+ This measures adds ZoneVentilation:WindandStackOpenArea objects to zones with operable windwos to model natural ventilation, then adds AvailabilityManager:HybridVentilation to each zone with natural ventilation and control HVAC and natural ventilation in a hybrid manner. When windows are open, HVAC will be disabled; when windows are closed, HVAC will be available. HVAC can be an airloop system or a zonal system.
12
+
13
+ ## Measure Type
14
+ ModelMeasure
15
+
16
+ ## Taxonomy
17
+
18
+
19
+ ## Arguments
20
+
21
+
22
+ ### Window Open Area Fraction (0-1)
23
+ A typical operable window does not open fully. The actual opening area in a zone is the product of the area of operable windows and the open area fraction schedule. Default 50% open.
24
+ **Name:** open_area_fraction,
25
+ **Type:** Double,
26
+ **Units:** ,
27
+ **Required:** true,
28
+ **Model Dependent:** false
29
+
30
+
31
+ ### Minimum Indoor Temperature (degC)
32
+ The indoor temperature below which ventilation is shutoff.
33
+ **Name:** min_indoor_temp,
34
+ **Type:** Double,
35
+ **Units:** ,
36
+ **Required:** false,
37
+ **Model Dependent:** false
38
+
39
+
40
+ ### Maximum Indoor Temperature (degC)
41
+ The indoor temperature above which ventilation is shutoff.
42
+ **Name:** max_indoor_temp,
43
+ **Type:** Double,
44
+ **Units:** ,
45
+ **Required:** false,
46
+ **Model Dependent:** false
47
+
48
+
49
+ ### Minimum Indoor-Outdoor Temperature Difference (degC)
50
+ This is the temperature difference between the indoor and outdoor air dry-bulb temperatures below which ventilation is shutoff. For example, a delta temperature of 2 degC means ventilation is available if the outside air temperature is at least 2 degC cooler than the zone air temperature. Values can be negative.
51
+ **Name:** delta_temp,
52
+ **Type:** Double,
53
+ **Units:** ,
54
+ **Required:** false,
55
+ **Model Dependent:** false
56
+
57
+
58
+ ### Minimum Outdoor Temperature (degC)
59
+ The outdoor temperature below which ventilation is shut off.
60
+ **Name:** min_outdoor_temp,
61
+ **Type:** Double,
62
+ **Units:** ,
63
+ **Required:** true,
64
+ **Model Dependent:** false
65
+
66
+
67
+ ### Maximum Outdoor Temperature (degC)
68
+ The outdoor temperature above which ventilation is shut off.
69
+ **Name:** max_outdoor_temp,
70
+ **Type:** Double,
71
+ **Units:** ,
72
+ **Required:** true,
73
+ **Model Dependent:** false
74
+
75
+
76
+ ### Maximum Wind Speed (m/s)
77
+ This is the wind speed above which ventilation is shut off. The default values assume windows are closed when wind is above a gentle breeze to avoid blowing around papers in the space.
78
+ **Name:** max_wind_speed,
79
+ **Type:** Double,
80
+ **Units:** ,
81
+ **Required:** false,
82
+ **Model Dependent:** false
83
+
84
+
85
+ ### Daily Start Time for natural ventilation
86
+ Use 24 hour format (HR:MM)
87
+ **Name:** nv_starttime,
88
+ **Type:** String,
89
+ **Units:** ,
90
+ **Required:** false,
91
+ **Model Dependent:** false
92
+
93
+
94
+ ### Daily End Time for natural ventilation
95
+ Use 24 hour format (HR:MM)
96
+ **Name:** nv_endtime,
97
+ **Type:** String,
98
+ **Units:** ,
99
+ **Required:** false,
100
+ **Model Dependent:** false
101
+
102
+
103
+ ### Start Date for natural ventilation
104
+ In MM-DD format
105
+ **Name:** nv_startdate,
106
+ **Type:** String,
107
+ **Units:** ,
108
+ **Required:** false,
109
+ **Model Dependent:** false
110
+
111
+
112
+ ### End Date for natural ventilation
113
+ In MM-DD format
114
+ **Name:** nv_enddate,
115
+ **Type:** String,
116
+ **Units:** ,
117
+ **Required:** false,
118
+ **Model Dependent:** false
119
+
120
+
121
+ ### Allow Natural Ventilation on Weekends
122
+
123
+ **Name:** wknds,
124
+ **Type:** Boolean,
125
+ **Units:** ,
126
+ **Required:** false,
127
+ **Model Dependent:** false
128
+
129
+
130
+
131
+
132
+
133
+
@@ -0,0 +1,45 @@
1
+ <%#= README.md.erb is used to auto-generate README.md. %>
2
+ <%#= To manually maintain README.md throw away README.md.erb and manually edit README.md %>
3
+ ###### (Automatically generated documentation)
4
+
5
+ # <%= name %>
6
+
7
+ ## Description
8
+ <%= description %>
9
+
10
+ ## Modeler Description
11
+ <%= modelerDescription %>
12
+
13
+ ## Measure Type
14
+ <%= measureType %>
15
+
16
+ ## Taxonomy
17
+ <%= taxonomy %>
18
+
19
+ ## Arguments
20
+
21
+ <% arguments.each do |argument| %>
22
+ ### <%= argument[:display_name] %>
23
+ <%= argument[:description] %>
24
+ **Name:** <%= argument[:name] %>,
25
+ **Type:** <%= argument[:type] %>,
26
+ **Units:** <%= argument[:units] %>,
27
+ **Required:** <%= argument[:required] %>,
28
+ **Model Dependent:** <%= argument[:model_dependent] %>
29
+ <% if argument[:type] == "Choice" && !argument[:model_dependent]%>
30
+ **Choice Display Names** <%= argument[:choice_display_names] %>
31
+ <% end %>
32
+ <% end %>
33
+
34
+ <% if arguments.size == 0 %>
35
+ <%= "This measure does not have any user arguments" %>
36
+ <% end %>
37
+
38
+ <% if outputs.size > 0 %>
39
+ ## Outputs
40
+ <% output_names = [] %>
41
+ <% outputs.each do |output| %>
42
+ <% output_names << output[:display_name] %>
43
+ <% end %>
44
+ <%= output_names.join(", ") %>
45
+ <% end %>
@@ -0,0 +1,453 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
3
+ # See also https://openstudio.net/license
4
+ # *******************************************************************************
5
+
6
+ # see the URL below for information on how to write OpenStudio measures
7
+ # http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
8
+
9
+ # start the measure
10
+ class AddNaturalVentilationWithHybridControl < OpenStudio::Measure::ModelMeasure
11
+ require 'time'
12
+ require 'json'
13
+
14
+ # human readable name
15
+ def name
16
+ # Measure name should be the title case of the class name.
17
+ return 'Add natural ventilation with hybrid control'
18
+ end
19
+
20
+ # human readable description
21
+ def description
22
+ return 'This measure adds natural ventilation to all the zones with operable windows, and controls natural ' \
23
+ 'ventilation together with HVAC in a hybrid manner. More specifically, HVAC will be disabled ' \
24
+ 'when windows are open, and HVAC will be available when windows are closed.'
25
+ end
26
+
27
+ # human readable description of modeling approach
28
+ def modeler_description
29
+ return 'This measures adds ZoneVentilation:WindandStackOpenArea objects to zones with operable windwos to model ' \
30
+ 'natural ventilation, then adds AvailabilityManager:HybridVentilation to each zone with natural ventilation and ' \
31
+ 'control HVAC and natural ventilation in a hybrid manner. When windows are open, HVAC will be disabled; ' \
32
+ 'when windows are closed, HVAC will be available. HVAC can be an airloop system or a zonal system. '
33
+ end
34
+
35
+ # define the arguments that the user will input
36
+ def arguments(model)
37
+ args = OpenStudio::Measure::OSArgumentVector.new
38
+
39
+ # make an argument for window's open area fraction
40
+ open_area_fraction = OpenStudio::Measure::OSArgument::makeDoubleArgument('open_area_fraction', true)
41
+ open_area_fraction.setDisplayName('Window Open Area Fraction (0-1)')
42
+ open_area_fraction.setDescription('A typical operable window does not open fully. The actual opening area in a zone is the product of the area of operable windows and the open area fraction schedule. Default 50% open.')
43
+ open_area_fraction.setDefaultValue(0.5)
44
+ args << open_area_fraction
45
+
46
+ # make an argument for min indoor temp
47
+ min_indoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('min_indoor_temp', false)
48
+ min_indoor_temp.setDisplayName('Minimum Indoor Temperature (degC)')
49
+ min_indoor_temp.setDescription('The indoor temperature below which ventilation is shutoff.')
50
+ min_indoor_temp.setDefaultValue(20)
51
+ args << min_indoor_temp
52
+
53
+ # make an argument for maximum indoor temperature
54
+ max_indoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('max_indoor_temp', false)
55
+ max_indoor_temp.setDisplayName('Maximum Indoor Temperature (degC)')
56
+ max_indoor_temp.setDescription('The indoor temperature above which ventilation is shutoff.')
57
+ max_indoor_temp.setDefaultValue(24)
58
+ args << max_indoor_temp
59
+
60
+ # make an argument for delta temperature
61
+ delta_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('delta_temp', false)
62
+ delta_temp.setDisplayName('Minimum Indoor-Outdoor Temperature Difference (degC)')
63
+ delta_temp.setDescription('This is the temperature difference between the indoor and outdoor air dry-bulb '\
64
+ 'temperatures below which ventilation is shutoff. For example, a delta temperature '\
65
+ 'of 2 degC means ventilation is available if the outside air temperature is at least '\
66
+ '2 degC cooler than the zone air temperature. Values can be negative.')
67
+ delta_temp.setDefaultValue(2.0)
68
+ args << delta_temp
69
+
70
+ # make an argument for minimum outdoor temperature
71
+ min_outdoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('min_outdoor_temp', true)
72
+ min_outdoor_temp.setDisplayName('Minimum Outdoor Temperature (degC)')
73
+ min_outdoor_temp.setDescription('The outdoor temperature below which ventilation is shut off.')
74
+ min_outdoor_temp.setDefaultValue(20)
75
+ args << min_outdoor_temp
76
+
77
+ # make an argument for maximum outdoor temperature
78
+ max_outdoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('max_outdoor_temp', true)
79
+ max_outdoor_temp.setDisplayName('Maximum Outdoor Temperature (degC)')
80
+ max_outdoor_temp.setDescription('The outdoor temperature above which ventilation is shut off.')
81
+ max_outdoor_temp.setDefaultValue(24)
82
+ args << max_outdoor_temp
83
+
84
+ # make an argument for maximum wind speed
85
+ max_wind_speed = OpenStudio::Measure::OSArgument::makeDoubleArgument('max_wind_speed', false)
86
+ max_wind_speed.setDisplayName('Maximum Wind Speed (m/s)')
87
+ max_wind_speed.setDescription('This is the wind speed above which ventilation is shut off. The default values assume windows are closed when wind is above a gentle breeze to avoid blowing around papers in the space.')
88
+ max_wind_speed.setDefaultValue(40)
89
+ args << max_wind_speed
90
+
91
+ # make an argument for the start time of natural ventilation
92
+ nv_starttime = OpenStudio::Measure::OSArgument.makeStringArgument('nv_starttime', false)
93
+ nv_starttime.setDisplayName('Daily Start Time for natural ventilation')
94
+ nv_starttime.setDescription('Use 24 hour format (HR:MM)')
95
+ nv_starttime.setDefaultValue('07:00')
96
+ args << nv_starttime
97
+
98
+ # make an argument for the end time of natural ventilation
99
+ nv_endtime = OpenStudio::Measure::OSArgument.makeStringArgument('nv_endtime', false)
100
+ nv_endtime.setDisplayName('Daily End Time for natural ventilation')
101
+ nv_endtime.setDescription('Use 24 hour format (HR:MM)')
102
+ nv_endtime.setDefaultValue('21:00')
103
+ args << nv_endtime
104
+
105
+ # make an argument for the start date of natural ventilation
106
+ nv_startdate = OpenStudio::Measure::OSArgument.makeStringArgument('nv_startdate', false)
107
+ nv_startdate.setDisplayName('Start Date for natural ventilation')
108
+ nv_startdate.setDescription('In MM-DD format')
109
+ nv_startdate.setDefaultValue('03-01')
110
+ args << nv_startdate
111
+
112
+ # make an argument for the end date of natural ventilation
113
+ nv_enddate = OpenStudio::Measure::OSArgument.makeStringArgument('nv_enddate', false)
114
+ nv_enddate.setDisplayName('End Date for natural ventilation')
115
+ nv_enddate.setDescription('In MM-DD format')
116
+ nv_enddate.setDefaultValue('10-31')
117
+ args << nv_enddate
118
+
119
+ # Make boolean arguments for natural ventilation schedule on weekends
120
+ wknds = OpenStudio::Measure::OSArgument.makeBoolArgument('wknds', false)
121
+ wknds.setDisplayName('Allow Natural Ventilation on Weekends')
122
+ wknds.setDefaultValue(true)
123
+ args << wknds
124
+
125
+ return args
126
+ end
127
+
128
+ # define what happens when the measure is run
129
+ def run(model, runner, user_arguments)
130
+ super(model, runner, user_arguments)
131
+
132
+ # use the built-in error checking
133
+ if !runner.validateUserArguments(arguments(model), user_arguments)
134
+ return false
135
+ end
136
+
137
+ # assign the user inputs to variables
138
+ open_area_fraction = runner.getDoubleArgumentValue('open_area_fraction', user_arguments)
139
+ min_indoor_temp = runner.getDoubleArgumentValue('min_indoor_temp', user_arguments)
140
+ max_indoor_temp = runner.getDoubleArgumentValue('max_indoor_temp', user_arguments)
141
+ delta_temp = runner.getDoubleArgumentValue('delta_temp', user_arguments)
142
+ min_outdoor_temp = runner.getDoubleArgumentValue('min_outdoor_temp', user_arguments)
143
+ max_outdoor_temp = runner.getDoubleArgumentValue('max_outdoor_temp', user_arguments)
144
+ max_wind_speed = runner.getDoubleArgumentValue('max_wind_speed', user_arguments)
145
+ nv_starttime = runner.getStringArgumentValue('nv_starttime', user_arguments)
146
+ nv_endtime = runner.getStringArgumentValue('nv_endtime', user_arguments)
147
+ nv_startdate = runner.getStringArgumentValue('nv_startdate', user_arguments)
148
+ nv_enddate = runner.getStringArgumentValue('nv_enddate', user_arguments)
149
+ wknds = runner.getBoolArgumentValue('wknds', user_arguments)
150
+
151
+ # check open_area_fraction input
152
+ if open_area_fraction < 0
153
+ runner.registerError('Window open area fraction should be between 0 and 1. Please double check your input.')
154
+ return false
155
+ elsif open_area_fraction > 1
156
+ runner.registerError("open_area_fraction #{open_area_fraction} is larger than 1. Window open area fraction should be between 0 and 1. Please double check your input.")
157
+ return false
158
+ elsif open_area_fraction == 0
159
+ runner.registerWarning("open_area_fraction #{open_area_fraction} is set as 0, meaning the windows will not be opened at all. Please double check your input.")
160
+ end
161
+
162
+ # check temp limit inputs
163
+ if min_indoor_temp >= max_indoor_temp
164
+ runner.registerError('Minimum indoor temperature should be lower than maximum outdoor temperature. Please double check your input.')
165
+ return false
166
+ end
167
+
168
+ if min_outdoor_temp >= max_outdoor_temp
169
+ runner.registerError('Minimum outdoor temperature should be lower than maximum outdoor temperature. Please double check your input.')
170
+ return false
171
+ end
172
+
173
+ # check max wind speed input
174
+ if max_wind_speed <= 0
175
+ runner.registerError('Maximum wind speed should be positive. Please double check your input.')
176
+ return false
177
+ end
178
+
179
+ # check delta temperature input
180
+ if delta_temp < 0
181
+ runner.registerWarning("delta_temp #{delta_temp} is negative. Normally delta temperature should be positive "\
182
+ "or at least 0 to enable free cooling and avoid introducing extra cooling load. "\
183
+ "Please double check your input.")
184
+ end
185
+
186
+ # check time format
187
+ begin
188
+ nv_starttime = Time.strptime(nv_starttime, '%H:%M')
189
+ nv_endtime = Time.strptime(nv_endtime, '%H:%M')
190
+ rescue ArgumentError
191
+ runner.registerError('Natural ventilation start and end time are required, and should be in format of %H:%M, e.g., 16:00.')
192
+ return false
193
+ end
194
+ if nv_starttime > nv_endtime
195
+ runner.registerInfo('Natural ventilation start time is later than end time, referring to overnight ' \
196
+ 'natural ventilation. Make sure this is expected.')
197
+ end
198
+
199
+ # check date format
200
+ md = /(\d\d)-(\d\d)/.match(nv_startdate)
201
+ if md
202
+ nv_start_month = md[1].to_i
203
+ nv_start_day = md[2].to_i
204
+ else
205
+ runner.registerError('Start date must be in MM-DD format.')
206
+ return false
207
+ end
208
+
209
+ md = /(\d\d)-(\d\d)/.match(nv_enddate)
210
+ if md
211
+ nv_end_month = md[1].to_i
212
+ nv_end_day = md[2].to_i
213
+ else
214
+ runner.registerError('End date must be in MM-DD format.')
215
+ return false
216
+ end
217
+
218
+ nv_startdate_os = model.getYearDescription.makeDate(nv_start_month, nv_start_day)
219
+ nv_enddate_os = model.getYearDescription.makeDate(nv_end_month, nv_end_day)
220
+
221
+ # if_overnight: 1 or 0; wknds (if applicable to weekends): 1 or 0
222
+ # by default schedule on value is 1, sometimes need specify, e.g., natural ventilation window open area fraction
223
+ def create_sch(model, sch_name, start_time, end_time, start_date, end_date, wknds, sch_on_value=1, sch_off_value=0)
224
+ day_start_time = Time.strptime("00:00", '%H:%M')
225
+ # create discharging schedule
226
+ new_sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
227
+ new_sch_ruleset.setName(sch_name)
228
+ new_sch_ruleset.defaultDaySchedule.setName(sch_name + ' default')
229
+ if start_time > end_time
230
+ if_overnight = sch_on_value # assigned as schedule on value
231
+ else
232
+ if_overnight = sch_off_value # assigned as schedule off value
233
+ end
234
+
235
+ for min in 1..24*60
236
+ if ((end_time - day_start_time)/60).to_i == min
237
+ time = OpenStudio::Time.new(0, 0, min)
238
+ new_sch_ruleset.defaultDaySchedule.addValue(time, sch_on_value)
239
+ elsif ((start_time - day_start_time)/60).to_i == min
240
+ time = OpenStudio::Time.new(0, 0, min)
241
+ new_sch_ruleset.defaultDaySchedule.addValue(time, sch_off_value)
242
+ elsif min == 24*60
243
+ time = OpenStudio::Time.new(0, 0, min)
244
+ new_sch_ruleset.defaultDaySchedule.addValue(time, if_overnight)
245
+ end
246
+ end
247
+
248
+ start_month = start_date.monthOfYear.value
249
+ start_day = start_date.dayOfMonth
250
+ end_month = end_date.monthOfYear.value
251
+ end_day = end_date.dayOfMonth
252
+ ts_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_sch_ruleset.defaultDaySchedule)
253
+ ts_rule.setName("#{new_sch_ruleset.name} #{start_month}/#{start_day}-#{end_month}/#{end_day} Rule")
254
+ ts_rule.setStartDate(start_date)
255
+ ts_rule.setEndDate(end_date)
256
+ ts_rule.setApplyWeekdays(true)
257
+ if wknds
258
+ ts_rule.setApplyWeekends(true)
259
+ else
260
+ ts_rule.setApplyWeekends(false)
261
+ end
262
+
263
+ unless start_month == 1 && start_day == 1
264
+ new_rule_day = OpenStudio::Model::ScheduleDay.new(model)
265
+ new_rule_day.addValue(OpenStudio::Time.new(0,24), 0)
266
+ new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day)
267
+ new_rule.setName("#{new_sch_ruleset.name} 01/01-#{start_month}/#{start_day} Rule")
268
+ new_rule.setStartDate(model.getYearDescription.makeDate(1, 1))
269
+ new_rule.setEndDate(model.getYearDescription.makeDate(start_month, start_day))
270
+ new_rule.setApplyAllDays(true)
271
+ end
272
+
273
+ unless end_month == 12 && end_day == 31
274
+ new_rule_day = OpenStudio::Model::ScheduleDay.new(model)
275
+ new_rule_day.addValue(OpenStudio::Time.new(0,24), 0)
276
+ new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day)
277
+ new_rule.setName("#{new_sch_ruleset.name} #{end_month}/#{end_day}-12/31 Rule")
278
+ new_rule.setStartDate(model.getYearDescription.makeDate(end_month, end_day))
279
+ new_rule.setEndDate(model.getYearDescription.makeDate(12, 31))
280
+ new_rule.setApplyAllDays(true)
281
+ end
282
+
283
+ return new_sch_ruleset
284
+ end
285
+
286
+
287
+ # report initial condition of model
288
+ runner.registerInitialCondition("The building started with #{model.getZoneVentilationWindandStackOpenAreas.size} "\
289
+ "ZoneVentilation:WindandStackOpenArea objects and #{model.getZoneVentilationDesignFlowRates.size}.")
290
+
291
+
292
+
293
+ #################### STEP 1: add zone ventilation objects ################
294
+ # TODO: update
295
+ # for spaces with existing NV object (1. ZoneVentilation:DesignFlowRate with 'Natural' as the ventilation type; or 2. ZoneVentilation:WindandStackOpenArea), skip
296
+
297
+ nv_zone_list = {} # if NV object is added, add the key-value (zone name-NV object) to this hash
298
+ window_open_area_sch = create_sch(model, "window open area fraction sch", nv_starttime, nv_endtime, nv_startdate_os, nv_enddate_os, wknds, open_area_fraction, 0)
299
+ model.getSpaces.each do |space|
300
+ next if space.thermalZone.empty?
301
+ thermal_zone = space.thermalZone.get
302
+
303
+ # check if this space has existing NV object, if yes then skip
304
+ has_nv = false
305
+ nv_objs = []
306
+ thermal_zone.equipment.each do |zone_equipment|
307
+ if zone_equipment.to_ZoneVentilationDesignFlowRate.is_initialized
308
+ if zone_equipment.to_ZoneVentilationDesignFlowRate.get.ventilationType.lowercase == "natural"
309
+ has_nv = true
310
+ nv_objs << zone_equipment.to_ZoneVentilationDesignFlowRate.get
311
+ end
312
+ elsif zone_equipment.to_ZoneVentilationWindandStackOpenArea.is_initialized
313
+ has_nv = true
314
+ nv_objs << zone_equipment.to_ZoneVentilationWindandStackOpenArea.get
315
+ end
316
+ end
317
+ if has_nv
318
+ nv_zone_list[thermal_zone.name.to_s] = nv_objs
319
+ next
320
+ end
321
+
322
+ # add NV objects
323
+ space.surfaces.sort.each do |surface|
324
+ surface.subSurfaces.sort.each do |subsurface|
325
+ if (subsurface.subSurfaceType == 'OperableWindow' || subsurface.subSurfaceType == 'FixedWindow') && subsurface.outsideBoundaryCondition == 'Outdoors'
326
+ window_azimuth_deg = OpenStudio.convert(subsurface.azimuth, 'rad', 'deg').get
327
+ window_area = subsurface.netArea
328
+
329
+ ##### Add the "ZoneVentilation:WindandStackOpenArea" for NV.
330
+ zn_vent_wind_and_stack = OpenStudio::Model::ZoneVentilationWindandStackOpenArea.new(model)
331
+ zn_vent_wind_and_stack.setName(subsurface.name.to_s + " NV object")
332
+ zn_vent_wind_and_stack.setOpeningArea(window_area)
333
+ zn_vent_wind_and_stack.setOpeningAreaFractionSchedule(window_open_area_sch)
334
+ zn_vent_wind_and_stack.setEffectiveAngle(window_azimuth_deg)
335
+ zn_vent_wind_and_stack.setMinimumIndoorTemperature(min_indoor_temp)
336
+ zn_vent_wind_and_stack.setMaximumIndoorTemperature(max_indoor_temp)
337
+ zn_vent_wind_and_stack.setMinimumOutdoorTemperature(min_outdoor_temp)
338
+ zn_vent_wind_and_stack.setMaximumOutdoorTemperature(max_outdoor_temp)
339
+ zn_vent_wind_and_stack.setDeltaTemperature(delta_temp)
340
+ zn_vent_wind_and_stack.setMaximumWindSpeed(max_wind_speed)
341
+ zn_vent_wind_and_stack.addToThermalZone(thermal_zone)
342
+ nv_zone_list[thermal_zone.name.to_s] = [] unless nv_zone_list.key?(thermal_zone.name.to_s)
343
+ nv_zone_list[thermal_zone.name.to_s] << zn_vent_wind_and_stack
344
+ end
345
+ end
346
+ end
347
+ end
348
+
349
+ #################### STEP 2: add hybrid ventilation control objects ################
350
+
351
+ # TODO: Simple Airflow Control Type Schedule Name set as 1 denote group control
352
+ # if a zone has more than one windows, group control allows them to be operated simultaneously
353
+ # if an airloopHVAC controls more than one zone, only one AvailabilityManagerHybridVentilation is allowed for an airloop, group control will
354
+ # decides the zones controlled by this airloop based on the selected ZoneVentilation object input
355
+
356
+ vent_control_sch = create_sch(model, "ventilation control sch", nv_starttime, nv_endtime, nv_startdate_os, nv_enddate_os, wknds, 1, 0)
357
+ simple_airflow_control_type_sch = OpenStudio::Model::ScheduleConstant.new(model)
358
+ simple_airflow_control_type_sch.setName("simple airflow control type sch - group control")
359
+ simple_airflow_control_type_sch.setValue(1)
360
+
361
+ # part1: loop through all the airloopHVAC and add hybrid ventilation control
362
+ model.getAirLoopHVACs.sort.each do |air_loop|
363
+ max_zone_area = 0
364
+ nv_zone_with_max_area = nil
365
+ air_loop.thermalZones.each do |thermal_zone|
366
+ if max_zone_area < thermal_zone.floorArea && nv_zone_list.key?(thermal_zone.name.to_s)
367
+ max_zone_area = thermal_zone.floorArea
368
+ nv_zone_with_max_area = thermal_zone.name.to_s
369
+ end
370
+ end
371
+ next if nv_zone_with_max_area.nil? # if there is no NV zone in this airloop, skip
372
+ has_hybrid_avail_manager = false
373
+ air_loop.availabilityManagers.sort.each do |avail_mgr|
374
+ next if avail_mgr.to_AvailabilityManagerHybridVentilation.empty?
375
+ if avail_mgr.to_AvailabilityManagerHybridVentilation.is_initialized
376
+ has_hybrid_avail_manager = true
377
+ avail_mgr_hybr_vent = avail_mgr.to_AvailabilityManagerHybridVentilation.get
378
+ avail_mgr_hybr_vent.setVentilationControlModeSchedule(vent_control_sch)
379
+ avail_mgr_hybr_vent.setControlledZone(model.getThermalZoneByName(nv_zone_with_max_area).get)
380
+ avail_mgr_hybr_vent.setMinimumOutdoorTemperature(min_outdoor_temp)
381
+ avail_mgr_hybr_vent.setMaximumOutdoorTemperature(max_outdoor_temp)
382
+ avail_mgr_hybr_vent.setMaximumWindSpeed(max_wind_speed)
383
+ avail_mgr_hybr_vent.setSimpleAirflowControlTypeSchedule(simple_airflow_control_type_sch)
384
+
385
+ # if zone has more than one window, set control based on the biggest window
386
+ nv_objs = nv_zone_list[nv_zone_with_max_area]
387
+ max_open_area = 0
388
+ nv_objs.each do |nv_obj|
389
+ max_open_area = nv_obj.openingArea if max_open_area < nv_obj.openingArea
390
+ avail_mgr_hybr_vent.setZoneVentilationObject(nv_obj)
391
+ end
392
+ end
393
+ end
394
+
395
+ unless has_hybrid_avail_manager
396
+ avail_mgr_hybr_vent = OpenStudio::Model::AvailabilityManagerHybridVentilation.new(model)
397
+ avail_mgr_hybr_vent.setName(air_loop.name.to_s + " HybridVentilation AvailabilityManager")
398
+ avail_mgr_hybr_vent.setVentilationControlModeSchedule(vent_control_sch)
399
+ avail_mgr_hybr_vent.setControlledZone(model.getThermalZoneByName(nv_zone_with_max_area).get)
400
+ avail_mgr_hybr_vent.setMinimumOutdoorTemperature(min_outdoor_temp)
401
+ avail_mgr_hybr_vent.setMaximumOutdoorTemperature(max_outdoor_temp)
402
+ avail_mgr_hybr_vent.setMaximumWindSpeed(max_wind_speed)
403
+ avail_mgr_hybr_vent.setSimpleAirflowControlTypeSchedule(simple_airflow_control_type_sch)
404
+
405
+ # if controlled zone has more than one window, set control based on the biggest window
406
+ nv_objs = nv_zone_list[nv_zone_with_max_area]
407
+ max_open_area = 0
408
+ nv_objs.each do |nv_obj|
409
+ max_open_area = nv_obj.openingArea if max_open_area < nv_obj.openingArea
410
+ avail_mgr_hybr_vent.setZoneVentilationObject(nv_obj)
411
+ end
412
+ air_loop.addAvailabilityManager(avail_mgr_hybr_vent)
413
+ end
414
+
415
+ # remove thermal zones in this airloop from the nv_zone_list hash
416
+ air_loop.thermalZones.each do |thermal_zone|
417
+ if nv_zone_list.key?(thermal_zone.name.to_s)
418
+ nv_zone_list.delete(thermal_zone.name.to_s)
419
+ end
420
+ end
421
+ end
422
+
423
+ # part2: loop through all spaces, add hybrid vent control to zones that are not connected to airloopHVAC but uses zonal equipment
424
+ nv_zone_list.each do |zone_name, nv_objs|
425
+ avail_mgr_hybr_vent = OpenStudio::Model::AvailabilityManagerHybridVentilation.new(model)
426
+ avail_mgr_hybr_vent.setName(zone_name + " HybridVentilation AvailabilityManager")
427
+ avail_mgr_hybr_vent.setVentilationControlModeSchedule(vent_control_sch)
428
+ avail_mgr_hybr_vent.setControlledZone(model.getThermalZoneByName(zone_name).get)
429
+ avail_mgr_hybr_vent.setMinimumOutdoorTemperature(min_outdoor_temp)
430
+ avail_mgr_hybr_vent.setMaximumOutdoorTemperature(max_outdoor_temp)
431
+ avail_mgr_hybr_vent.setMaximumWindSpeed(max_wind_speed)
432
+ avail_mgr_hybr_vent.setSimpleAirflowControlTypeSchedule(simple_airflow_control_type_sch)
433
+
434
+ # if controlled zone has more than one window, set control based on the biggest window
435
+ max_open_area = 0
436
+ nv_objs.each do |nv_obj|
437
+ max_open_area = nv_obj.openingArea if max_open_area < nv_obj.openingArea
438
+ avail_mgr_hybr_vent.setZoneVentilationObject(nv_obj)
439
+ end
440
+ end
441
+
442
+ # echo added AvailabilityManagerHybridVentilation object number to the user
443
+ runner.registerInfo("A total of #{model.getAvailabilityManagerHybridVentilations.size} AvailabilityManagerHybridVentilations were added.")
444
+
445
+ # report final condition of model
446
+ runner.registerFinalCondition("The building finished with #{model.getAvailabilityManagerHybridVentilations.size} AvailabilityManagerHybridVentilations.")
447
+
448
+ return true
449
+ end
450
+ end
451
+
452
+ # register the measure to be used by the application
453
+ AddNaturalVentilationWithHybridControl.new.registerWithApplication