openstudio-calibration 0.3.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +3 -1
  4. data/CHANGELOG.md +18 -0
  5. data/Gemfile +6 -0
  6. data/Jenkinsfile +4 -3
  7. data/LICENSE.md +1 -1
  8. data/README.md +13 -2
  9. data/Rakefile +1 -1
  10. data/doc_templates/LICENSE.md +1 -1
  11. data/doc_templates/copyright_erb.txt +1 -1
  12. data/doc_templates/copyright_js.txt +1 -1
  13. data/doc_templates/copyright_ruby.txt +1 -1
  14. data/lib/measures/AddMonthlyJSONUtilityData/LICENSE.md +1 -1
  15. data/lib/measures/AddMonthlyJSONUtilityData/measure.rb +1 -1
  16. data/lib/measures/AddMonthlyJSONUtilityData/measure.xml +12 -11
  17. data/lib/measures/AddMonthlyUtilityData/LICENSE.md +1 -1
  18. data/lib/measures/AddMonthlyUtilityData/measure.rb +1 -1
  19. data/lib/measures/AddMonthlyUtilityData/measure.xml +13 -12
  20. data/lib/measures/CalibrationReports/LICENSE.md +1 -1
  21. data/lib/measures/CalibrationReports/README.md +2 -2
  22. data/lib/measures/CalibrationReports/measure.rb +1 -1
  23. data/lib/measures/CalibrationReports/measure.xml +11 -11
  24. data/lib/measures/CalibrationReportsEnhanced/LICENSE.md +1 -1
  25. data/lib/measures/CalibrationReportsEnhanced/measure.rb +2 -2
  26. data/lib/measures/CalibrationReportsEnhanced/measure.xml +18 -17
  27. data/lib/measures/CoilCoolingDXSingleSpeedMultiplier/LICENSE.md +1 -1
  28. data/lib/measures/CoilCoolingDXSingleSpeedMultiplier/measure.rb +1 -1
  29. data/lib/measures/CoilCoolingDXSingleSpeedMultiplier/measure.xml +14 -13
  30. data/lib/measures/CoilCoolingDXSingleSpeedPercentChange/LICENSE.md +1 -1
  31. data/lib/measures/CoilCoolingDXSingleSpeedPercentChange/measure.rb +1 -1
  32. data/lib/measures/CoilCoolingDXSingleSpeedPercentChange/measure.xml +14 -13
  33. data/lib/measures/CoilCoolingDXTwoSpeedMultiplier/LICENSE.md +1 -1
  34. data/lib/measures/CoilCoolingDXTwoSpeedMultiplier/measure.rb +1 -1
  35. data/lib/measures/CoilCoolingDXTwoSpeedMultiplier/measure.xml +14 -13
  36. data/lib/measures/CoilCoolingDXTwoSpeedPercentChange/LICENSE.md +1 -1
  37. data/lib/measures/CoilCoolingDXTwoSpeedPercentChange/measure.rb +1 -1
  38. data/lib/measures/CoilCoolingDXTwoSpeedPercentChange/measure.xml +14 -13
  39. data/lib/measures/CoilCoolingWaterMultiplier/LICENSE.md +1 -1
  40. data/lib/measures/CoilCoolingWaterMultiplier/measure.rb +1 -1
  41. data/lib/measures/CoilCoolingWaterMultiplier/measure.xml +14 -13
  42. data/lib/measures/CoilCoolingWaterPercentChange/LICENSE.md +1 -1
  43. data/lib/measures/CoilCoolingWaterPercentChange/measure.rb +1 -1
  44. data/lib/measures/CoilCoolingWaterPercentChange/measure.xml +14 -13
  45. data/lib/measures/CoilHeatingElectricMultiplier/LICENSE.md +1 -1
  46. data/lib/measures/CoilHeatingElectricMultiplier/measure.rb +1 -1
  47. data/lib/measures/CoilHeatingElectricMultiplier/measure.xml +14 -13
  48. data/lib/measures/CoilHeatingElectricPercentChange/LICENSE.md +1 -1
  49. data/lib/measures/CoilHeatingElectricPercentChange/measure.rb +1 -1
  50. data/lib/measures/CoilHeatingElectricPercentChange/measure.xml +14 -13
  51. data/lib/measures/CoilHeatingGasMultiplier/LICENSE.md +1 -1
  52. data/lib/measures/CoilHeatingGasMultiplier/measure.rb +1 -1
  53. data/lib/measures/CoilHeatingGasMultiplier/measure.xml +14 -13
  54. data/lib/measures/CoilHeatingGasPercentChange/LICENSE.md +1 -1
  55. data/lib/measures/CoilHeatingGasPercentChange/measure.rb +1 -1
  56. data/lib/measures/CoilHeatingGasPercentChange/measure.xml +14 -13
  57. data/lib/measures/CoilHeatingWaterMultiplier/LICENSE.md +1 -1
  58. data/lib/measures/CoilHeatingWaterMultiplier/measure.rb +1 -1
  59. data/lib/measures/CoilHeatingWaterMultiplier/measure.xml +14 -13
  60. data/lib/measures/CoilHeatingWaterPercentChange/LICENSE.md +1 -1
  61. data/lib/measures/CoilHeatingWaterPercentChange/measure.rb +1 -1
  62. data/lib/measures/CoilHeatingWaterPercentChange/measure.xml +14 -13
  63. data/lib/measures/ConstructionLayerZeroMaterialProperties/LICENSE.md +1 -1
  64. data/lib/measures/ConstructionLayerZeroMaterialProperties/measure.rb +1 -1
  65. data/lib/measures/ConstructionLayerZeroMaterialProperties/measure.xml +12 -11
  66. data/lib/measures/ElectricBaseboardEfficiencyAndCapacity/LICENSE.md +1 -1
  67. data/lib/measures/ElectricBaseboardEfficiencyAndCapacity/measure.rb +1 -1
  68. data/lib/measures/ElectricBaseboardEfficiencyAndCapacity/measure.xml +12 -11
  69. data/lib/measures/ExteriorWallThermalPropertiesMultiplier/LICENSE.md +1 -1
  70. data/lib/measures/ExteriorWallThermalPropertiesMultiplier/measure.rb +3 -3
  71. data/lib/measures/ExteriorWallThermalPropertiesMultiplier/measure.xml +25 -13
  72. data/lib/measures/ExteriorWallThermalPropertiesPercentChange/LICENSE.md +1 -1
  73. data/lib/measures/ExteriorWallThermalPropertiesPercentChange/measure.rb +3 -3
  74. data/lib/measures/ExteriorWallThermalPropertiesPercentChange/measure.xml +25 -13
  75. data/lib/measures/FansMultiplier/LICENSE.md +1 -1
  76. data/lib/measures/FansMultiplier/measure.rb +1 -1
  77. data/lib/measures/FansMultiplier/measure.xml +14 -13
  78. data/lib/measures/FansPercentChange/LICENSE.md +1 -1
  79. data/lib/measures/FansPercentChange/measure.rb +1 -1
  80. data/lib/measures/FansPercentChange/measure.xml +14 -13
  81. data/lib/measures/GeneralCalibrationMeasureMultiplier/LICENSE.md +1 -1
  82. data/lib/measures/GeneralCalibrationMeasureMultiplier/measure.rb +1 -1
  83. data/lib/measures/GeneralCalibrationMeasureMultiplier/measure.xml +16 -15
  84. data/lib/measures/GeneralCalibrationMeasurePercentChange/LICENSE.md +1 -1
  85. data/lib/measures/GeneralCalibrationMeasurePercentChange/measure.rb +1 -1
  86. data/lib/measures/GeneralCalibrationMeasurePercentChange/measure.xml +16 -15
  87. data/lib/measures/HardSizeHvac/LICENSE.md +1 -1
  88. data/lib/measures/HardSizeHvac/measure.rb +9 -7
  89. data/lib/measures/HardSizeHvac/measure.xml +26 -13
  90. data/lib/measures/RValueOfInsulationForConstructionMultiplier/LICENSE.md +1 -1
  91. data/lib/measures/RValueOfInsulationForConstructionMultiplier/measure.rb +1 -1
  92. data/lib/measures/RValueOfInsulationForConstructionMultiplier/measure.xml +12 -11
  93. data/lib/measures/RValueOfInsulationForConstructionPercentageChange/LICENSE.md +1 -1
  94. data/lib/measures/RValueOfInsulationForConstructionPercentageChange/measure.rb +1 -1
  95. data/lib/measures/RValueOfInsulationForConstructionPercentageChange/measure.xml +12 -11
  96. data/lib/measures/RoofThermalPropertiesMultiplier/LICENSE.md +1 -1
  97. data/lib/measures/RoofThermalPropertiesMultiplier/measure.rb +3 -3
  98. data/lib/measures/RoofThermalPropertiesMultiplier/measure.xml +25 -13
  99. data/lib/measures/RoofThermalPropertiesPercentChange/LICENSE.md +1 -1
  100. data/lib/measures/RoofThermalPropertiesPercentChange/measure.rb +3 -3
  101. data/lib/measures/RoofThermalPropertiesPercentChange/measure.xml +25 -13
  102. data/lib/measures/TimeseriesObjectiveFunction/LICENSE.md +1 -1
  103. data/lib/measures/TimeseriesObjectiveFunction/README.md +171 -5
  104. data/lib/measures/TimeseriesObjectiveFunction/measure.rb +2 -2
  105. data/lib/measures/TimeseriesObjectiveFunction/measure.xml +6 -81
  106. data/lib/measures/TimeseriesObjectiveFunction/resources/report.html.erb +4 -1
  107. data/lib/measures/TimeseriesPlot/LICENSE.md +1 -1
  108. data/lib/measures/TimeseriesPlot/README.md +35 -5
  109. data/lib/measures/TimeseriesPlot/measure.rb +2 -2
  110. data/lib/measures/TimeseriesPlot/measure.xml +11 -26
  111. data/lib/measures/TimeseriesPlot/resources/report.html.erb +4 -1
  112. data/lib/measures/WaterHeaterMixedMultiplier/LICENSE.md +1 -1
  113. data/lib/measures/WaterHeaterMixedMultiplier/measure.rb +1 -1
  114. data/lib/measures/WaterHeaterMixedMultiplier/measure.xml +14 -13
  115. data/lib/measures/WaterHeaterMixedPercentChange/LICENSE.md +1 -1
  116. data/lib/measures/WaterHeaterMixedPercentChange/measure.rb +1 -1
  117. data/lib/measures/WaterHeaterMixedPercentChange/measure.xml +14 -13
  118. data/lib/measures/{zone_report → inspect_and_edit_parametric_schedules}/LICENSE.md +1 -1
  119. data/lib/measures/inspect_and_edit_parametric_schedules/README.md +41 -0
  120. data/lib/measures/inspect_and_edit_parametric_schedules/README.md.erb +53 -0
  121. data/lib/measures/inspect_and_edit_parametric_schedules/docs/apply_measures_now.png +0 -0
  122. data/lib/measures/inspect_and_edit_parametric_schedules/measure.rb +224 -0
  123. data/lib/measures/inspect_and_edit_parametric_schedules/measure.xml +125 -0
  124. data/lib/measures/shift_hours_of_operation/LICENSE.md +27 -0
  125. data/lib/measures/shift_hours_of_operation/README.md +147 -0
  126. data/lib/measures/{zone_report → shift_hours_of_operation}/README.md.erb +5 -0
  127. data/lib/measures/shift_hours_of_operation/docs/.gitkeep +0 -0
  128. data/lib/measures/shift_hours_of_operation/measure.rb +525 -0
  129. data/lib/measures/shift_hours_of_operation/measure.xml +280 -0
  130. data/lib/openstudio/calibration/extension.rb +1 -1
  131. data/lib/openstudio/calibration/version.rb +2 -2
  132. data/lib/openstudio/calibration.rb +1 -1
  133. data/lib/openstudio-calibration.rb +1 -1
  134. data/openstudio-calibration.gemspec +3 -3
  135. metadata +23 -17
  136. data/lib/measures/zone_report/README.md +0 -26
  137. data/lib/measures/zone_report/measure.rb +0 -706
  138. data/lib/measures/zone_report/measure.xml +0 -69
  139. data/lib/measures/zone_report/resources/report.html.in +0 -342
@@ -0,0 +1,525 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2021, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ # insert your copyright here
37
+
38
+ # see the URL below for information on how to write OpenStudio measures
39
+ # http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
40
+
41
+ require 'openstudio-standards'
42
+
43
+ # load OpenStudio measure libraries from openstudio-extension gem
44
+ require 'openstudio-extension'
45
+ require 'openstudio/extension/core/os_lib_helper_methods'
46
+
47
+ # start the measure
48
+ class ShiftHoursOfOperation < OpenStudio::Measure::ModelMeasure
49
+ # resource file modules
50
+ include OsLib_HelperMethods
51
+
52
+ # human readable name
53
+ def name
54
+ # Measure name should be the title case of the class name.
55
+ return 'Shift Hours of Operation'
56
+ end
57
+
58
+ # human readable description
59
+ def description
60
+ return 'This measure will infer the hours of operation for the building and then will shift the start of the hours of operation and change the duration of the hours of operation. In an alternate workflow you can directly pass in target start and duration rather than a shift and delta. Inputs can vary for weekday, Saturday, and Sunday. if a day does not have any hours of operation to start with increasing hours of operation may not have any impact as the auto generated data may not know what to do during operating hours. Future version may be able to borrow a profile formula but would probably require additional user arguments.'
61
+ end
62
+
63
+ # human readable description of modeling approach
64
+ def modeler_description
65
+ return 'This will only impact schedule rulesets. It will use methods in openstudio-standards to infer hours of operation, develop a parametric formula for all of the ruleset schedules, alter the hours of operation inputs to that formula and then re-apply the schedules. Input is expose to set ramp frequency of the resulting schedules. If inputs are such that no changes are requested, bypass the measure with NA so that it will not be parameterized. An advanced option for this measure would be bool to use hours of operation from OSM schedule ruleset hours of operation instead of inferring from standards. This should allow different parts of the building to have different hours of operation in the seed model.'
66
+ end
67
+
68
+ # define the arguments that the user will input
69
+ def arguments(model)
70
+ args = OpenStudio::Measure::OSArgumentVector.new
71
+
72
+ # delta hoo_start for weekdays
73
+ hoo_start_weekday = OpenStudio::Measure::OSArgument.makeDoubleArgument('hoo_start_weekday', true)
74
+ hoo_start_weekday.setDisplayName('Shift the weekday start of hours of operation.')
75
+ hoo_start_weekday.setDescription('Use decimal hours so an 1 hour and 15 minute shift would be 1.25. Positive value moves the hour of operation later')
76
+ hoo_start_weekday.setDefaultValue(0.0)
77
+ hoo_start_weekday.setUnits('Hours')
78
+ args << hoo_start_weekday
79
+
80
+ # delta hoo_dur for weekday
81
+ hoo_dur_weekday = OpenStudio::Measure::OSArgument.makeDoubleArgument('hoo_dur_weekday', true)
82
+ hoo_dur_weekday.setDisplayName('Extend the weekday of hours of operation.')
83
+ hoo_dur_weekday.setDescription('Use decimal hours so an 1 hour and 15 minute would be 1.25. Positive value makes the hour of operation longer.')
84
+ hoo_dur_weekday .setDefaultValue(0.0)
85
+ hoo_dur_weekday.setUnits('Hours')
86
+ args << hoo_dur_weekday
87
+
88
+ # TODO: - could include every day of the week
89
+
90
+ # delta hoo_start for saturdays
91
+ hoo_start_saturday = OpenStudio::Measure::OSArgument.makeDoubleArgument('hoo_start_saturday', true)
92
+ hoo_start_saturday.setDisplayName('Shift the saturday start of hours of operation.')
93
+ hoo_start_saturday.setDescription('Use decimal hours so an 1 hour and 15 minute shift would be 1.25. Positive value moves the hour of operation later')
94
+ hoo_start_saturday.setDefaultValue(0.0)
95
+ hoo_start_saturday.setUnits('Hours')
96
+ args << hoo_start_saturday
97
+
98
+ # delta hoo_dur for saturday
99
+ hoo_dur_saturday = OpenStudio::Measure::OSArgument.makeDoubleArgument('hoo_dur_saturday', true)
100
+ hoo_dur_saturday.setDisplayName('Extend the saturday of hours of operation.')
101
+ hoo_dur_saturday.setDescription('Use decimal hours so an 1 hour and 15 minute would be 1.25. Positive value makes the hour of operation longer.')
102
+ hoo_dur_saturday.setDefaultValue(0.0)
103
+ hoo_dur_saturday.setUnits('Hours')
104
+ args << hoo_dur_saturday
105
+
106
+ # delta hoo_start for sundays
107
+ hoo_start_sunday = OpenStudio::Measure::OSArgument.makeDoubleArgument('hoo_start_sunday', true)
108
+ hoo_start_sunday.setDisplayName('Shift the sunday start of hours of operation.')
109
+ hoo_start_sunday.setDescription('Use decimal hours so an 1 hour and 15 minute shift would be 1.25. Positive value moves the hour of operation later')
110
+ hoo_start_sunday.setDefaultValue(0.0)
111
+ hoo_start_sunday.setUnits('Hours')
112
+ args << hoo_start_sunday
113
+
114
+ # delta hoo_dur for sunday
115
+ hoo_dur_sunday = OpenStudio::Measure::OSArgument.makeDoubleArgument('hoo_dur_sunday', true)
116
+ hoo_dur_sunday.setDisplayName('Extend the sunday of hours of operation.')
117
+ hoo_dur_sunday.setDescription('Use decimal hours so an 1 hour and 15 minute would be 1.25. Positive value makes the hour of operation longer.')
118
+ hoo_dur_sunday.setDefaultValue(0.0)
119
+ hoo_dur_sunday.setUnits('Hours')
120
+ args << hoo_dur_sunday
121
+
122
+ # TODO: - could include start and end days to have delta or absolute values applied to. (maybe decimal between 1.0 and 13.0 month where 3.50 would be March 15th)
123
+
124
+ # make an argument for delta_values
125
+ delta_values = OpenStudio::Measure::OSArgument.makeBoolArgument('delta_values', true)
126
+ delta_values.setDisplayName('Hours of operation values treated as deltas')
127
+ delta_values.setDescription('When this is true the hours of operation start and duration represent a delta from the original model values. When switched to false they represent absolute values.')
128
+ delta_values.setDefaultValue(true)
129
+ args << delta_values
130
+
131
+ # make an argument for infer_parametric_schedules
132
+ infer_parametric_schedules = OpenStudio::Measure::OSArgument.makeBoolArgument('infer_parametric_schedules', true)
133
+ infer_parametric_schedules.setDisplayName('Dynamically generate parametric schedules from current ruleset schedules.')
134
+ infer_parametric_schedules.setDescription('When this is true the parametric schedule formulas and hours of operation will be generated from the existing model schedules. When false it expects the model already has parametric formulas stored.')
135
+ infer_parametric_schedules.setDefaultValue(true)
136
+ args << infer_parametric_schedules
137
+
138
+ # delta hoo_start for sundays
139
+ fraction_of_daily_occ_range = OpenStudio::Measure::OSArgument.makeDoubleArgument('fraction_of_daily_occ_range', true)
140
+ fraction_of_daily_occ_range.setDisplayName('Fraction of Daily Occupancy Range.')
141
+ fraction_of_daily_occ_range.setDescription('This determine what fraction of occupancy be considered operating conditions. This fraction is normalized to expanded to range seen over the full year and does not necessary equal fraction of design occupancy. This value should be between 0 and 1.0 and is only used if dynamically generated parametric schedules are used.')
142
+ fraction_of_daily_occ_range.setDefaultValue(0.25)
143
+ fraction_of_daily_occ_range.setUnits('Hours')
144
+ args << fraction_of_daily_occ_range
145
+
146
+ # argument to choose hour of operation variable method
147
+ choices = OpenStudio::StringVector.new
148
+ choices << 'fractional'
149
+ choices << 'hours'
150
+ hoo_var_method = OpenStudio::Measure::OSArgument.makeChoiceArgument('hoo_var_method', choices, true)
151
+ hoo_var_method.setDisplayName('Hours of Operation Variable Method for Scheudle Profile Formula.')
152
+ hoo_var_method.setDescription('If dynamically generate parametric schedules from current ruleset scheudles is selected ,this argument is used to determine if the schedule profile formulas define time of points in a profile as a a specific delta from the star, middle, or end of the horus of operation, or if the delta is fractaionl percentate of the horus fo operation ro non-operation/vacant time.')
153
+ hoo_var_method.setDefaultValue('fractional')
154
+ args << hoo_var_method
155
+
156
+ # make an argument for target_hoo_from_model
157
+ # Should only be true when infer_parametric_schedules is false
158
+ target_hoo_from_model = OpenStudio::Measure::OSArgument.makeBoolArgument('target_hoo_from_model', true)
159
+ target_hoo_from_model.setDisplayName('Use model hours of operation as target')
160
+ target_hoo_from_model.setDescription('The default behavior is for this to be false. This can not be used unless Dynamically generate parametric schedules from current ruleset schedules is set to false and if the schedules in the model already have parametric profiles. When changed to true all of the hours of operation start and duration values will be ignored as the bool to treat those values as relative or absolute. Instead the hours of operation schedules for the model will be used.')
161
+ target_hoo_from_model.setDefaultValue(false)
162
+ args << target_hoo_from_model
163
+
164
+ # TODO: - add argument for step frequency, which is hours per step (should be fractional 1 or less generally).
165
+ # For now it defaults to simulation timestep
166
+
167
+ return args
168
+ end
169
+
170
+ # get model hoo info
171
+ def hoo_summary(model, runner, standard)
172
+ hoo_summary_hash = {}
173
+ hoo_summary_hash[:zero_hoo] = []
174
+ hoo_summary_hash[:final_hoo_start_range] = []
175
+ hoo_summary_hash[:final_hoo_dur_range] = []
176
+ model.getSpaces.sort.each do |space|
177
+ default_sch_type = OpenStudio::Model::DefaultScheduleType.new('HoursofOperationSchedule')
178
+ hours_of_operation = space.getDefaultSchedule(default_sch_type)
179
+ if !hours_of_operation.is_initialized
180
+ runner.registerWarning("Hours of Operation Schedule is not set for #{space.name}.")
181
+ next
182
+ end
183
+ hours_of_operation_hash = standard.space_hours_of_operation(space)
184
+ hours_of_operation_hash.each do |hoo_key, val|
185
+ if val[:hoo_hours] == 0.0
186
+ hoo_summary_hash[:zero_hoo] << val[:hoo_hours]
187
+ else
188
+ hoo_summary_hash[:final_hoo_dur_range] << val[:hoo_hours]
189
+ hoo_summary_hash[:final_hoo_start_range] << val[:hoo_start]
190
+ end
191
+ end
192
+ end
193
+
194
+ return hoo_summary_hash
195
+ end
196
+
197
+ # process hoo schedules for various days of the week
198
+ # todo - when date range arg is used and not full year will never want to change default profile
199
+ def process_hoo(used_hoo_sch_sets, model, runner, args, days_of_week, hoo_start_dows, hoo_dur_dows)
200
+ # profiles added to this will be processed
201
+ altered_schedule_days = {} # key is profile value is original index position defined in used_hoo_sch_sets
202
+
203
+ # loop through horus of operation schedules
204
+ used_hoo_sch_sets.uniq.each do |hoo_sch, hours_of_operation_hash|
205
+ if !hoo_sch.to_ScheduleRuleset.is_initialized
206
+ runner.registerWarning("#{hoo_sch.name} is not schedule schedule ruleset, will not be altered by this method.")
207
+ next
208
+ end
209
+ hoo_sch = hoo_sch.to_ScheduleRuleset.get
210
+
211
+ year_description = hoo_sch.model.yearDescription.get
212
+ year = year_description.assumedYear
213
+ year_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, year)
214
+ year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, year)
215
+
216
+ orig_profile_indexes_used = hoo_sch.getActiveRuleIndices(year_start_date, year_end_date)
217
+ orig_num_days_default_used = orig_profile_indexes_used.count(-1)
218
+ if orig_num_days_default_used > 0
219
+ default_prof = hoo_sch.defaultDaySchedule
220
+
221
+ # clone default profile as rule that sits above it so it can be evauluated and used if needed
222
+ new_prof = default_prof.clone(model).to_ScheduleDay.get
223
+ new_rule = OpenStudio::Model::ScheduleRule.new(hoo_sch, new_prof)
224
+ hoo_sch.setScheduleRuleIndex(new_rule, hoo_sch.scheduleRules.size - 1)
225
+
226
+ # set days of week for clone to match days of week passed into the method
227
+ if days_of_week.include?('mon')
228
+ new_rule.setApplyMonday(true)
229
+ end
230
+ if days_of_week.include?('tue')
231
+ new_rule.setApplyTuesday(true)
232
+ end
233
+ if days_of_week.include?('wed')
234
+ new_rule.setApplyWednesday(true)
235
+ end
236
+ if days_of_week.include?('thur')
237
+ new_rule.setApplyThursday(true)
238
+ end
239
+ if days_of_week.include?('fri')
240
+ new_rule.setApplyFriday(true)
241
+ end
242
+ if days_of_week.include?('sat')
243
+ new_rule.setApplySaturday(true)
244
+ end
245
+ if days_of_week.include?('sun')
246
+ new_rule.setApplySunday(true)
247
+ end
248
+
249
+ # check if default days are used at all
250
+ profile_indexes_used = hoo_sch.getActiveRuleIndices(year_start_date, year_end_date)
251
+ num_days_new_profile_used = profile_indexes_used.count(hoo_sch.scheduleRules.size - 1)
252
+ if !profile_indexes_used.uniq.include?(-1) && num_days_new_profile_used > 0
253
+ # don't need new profile, can use default
254
+ new_rule.remove
255
+ altered_schedule_days[hoo_sch.defaultDaySchedule] = -1
256
+ elsif num_days_new_profile_used == 0.0
257
+ # can remove cloned rule and skip the default profile (don't pass into array)
258
+ new_rule.remove
259
+ else
260
+ altered_schedule_days[new_rule.daySchedule] = -1 # use hoo that was applicable to the default before it was cloned
261
+ end
262
+ end
263
+
264
+ # use this to link to hoo from hours_of_operation_hash
265
+ counter_of_orig_index = hours_of_operation_hash.size - 2 # this is not impacted by cloning that may have happened above
266
+
267
+ hoo_sch.scheduleRules.reverse.each do |rule|
268
+ # inspect days of the week
269
+ actual_days_of_week_for_profile = []
270
+ if rule.applyMonday then actual_days_of_week_for_profile << 'mon' end
271
+ if rule.applyTuesday then actual_days_of_week_for_profile << 'tue' end
272
+ if rule.applyWednesday then actual_days_of_week_for_profile << 'wed' end
273
+ if rule.applyThursday then actual_days_of_week_for_profile << 'thur' end
274
+ if rule.applyFriday then actual_days_of_week_for_profile << 'fri' end
275
+ if rule.applySaturday then actual_days_of_week_for_profile << 'sat' end
276
+ if rule.applySunday then actual_days_of_week_for_profile << 'sun' end
277
+
278
+ # if an exact match for the rules passed in are met, this rule can be edited in place (update later for date range)
279
+ day_of_week_intersect = days_of_week & actual_days_of_week_for_profile
280
+ current_rule_index = rule.ruleIndex
281
+ if days_of_week == actual_days_of_week_for_profile
282
+ altered_schedule_days[rule.daySchedule] = counter_of_orig_index
283
+
284
+ # if this rule contains the requested days of the week and another then a clone should be made above this with only the requested days of the week that are also already on for this rule
285
+ elsif !day_of_week_intersect.empty?
286
+
287
+ # clone default profile as rule that sits above it so it can be evaluated and used if needed
288
+ new_rule = rule.clone(model).to_ScheduleRule.get
289
+ hoo_sch.setScheduleRuleIndex(new_rule, current_rule_index) # the cloned rule should be just above what was cloned
290
+
291
+ # update days of week for rule
292
+ if day_of_week_intersect.include?('mon')
293
+ new_rule.setApplyMonday(true)
294
+ else
295
+ new_rule.setApplyMonday(false)
296
+ end
297
+ if day_of_week_intersect.include?('tue')
298
+ new_rule.setApplyTuesday(true)
299
+ else
300
+ new_rule.setApplyTuesday(false)
301
+ end
302
+ if day_of_week_intersect.include?('wed')
303
+ new_rule.setApplyWednesday(true)
304
+ else
305
+ new_rule.setApplyWednesday(false)
306
+ end
307
+ if day_of_week_intersect.include?('thur')
308
+ new_rule.setApplyThursday(true)
309
+ else
310
+ new_rule.setApplyThursday(false)
311
+ end
312
+ if day_of_week_intersect.include?('fri')
313
+ new_rule.setApplyFriday(true)
314
+ else
315
+ new_rule.setApplyFriday(false)
316
+ end
317
+ if day_of_week_intersect.include?('sat')
318
+ new_rule.setApplySaturday(true)
319
+ else
320
+ new_rule.setApplySaturday(false)
321
+ end
322
+ if day_of_week_intersect.include?('sun')
323
+ new_rule.setApplySunday(true)
324
+ else
325
+ new_rule.setApplySunday(false)
326
+ end
327
+
328
+ # add to array
329
+ altered_schedule_days[new_rule.daySchedule] = counter_of_orig_index
330
+ end
331
+
332
+ # adjust the count used to find hoo from hours_of_operation_hash
333
+ counter_of_orig_index -= 1
334
+ end
335
+ runner.registerInfo("For #{hoo_sch.name} #{days_of_week.inspect} #{altered_schedule_days.size} profiles will be processed.")
336
+
337
+ # convert altered_schedule_days to hash where key is profile and value is key of index in hours_of_operation_hash
338
+
339
+ # loop through profiles to changes
340
+ altered_schedule_days.each do |new_profile, hoo_hash_index|
341
+ # gather info and edit selected profile
342
+ if args['delta_values']
343
+ orig_hoo_start = hours_of_operation_hash[hoo_hash_index][:hoo_start]
344
+ orig_hoo_dur = hours_of_operation_hash[hoo_hash_index][:hoo_hours]
345
+
346
+ # check for duration grater than 24 or lower than 0
347
+ max_dur_delta = 24 - orig_hoo_dur
348
+ min_dur_delta = orig_hoo_dur * -1.0
349
+ if hoo_dur_dows > max_dur_delta
350
+ target_dur = 24.0
351
+ runner.registerWarning("For profile in #{hoo_sch.name} duration is being capped at 24 hours.")
352
+ elsif hoo_dur_dows < min_dur_delta
353
+ target_dur = 0.0
354
+ runner.registerWarning("For profile in #{hoo_sch.name} duration is being limited to a low of 0 hours.")
355
+ else
356
+ target_dur = hoo_dur_dows + orig_hoo_dur
357
+ end
358
+
359
+ # setup new hoo values with delta
360
+ if orig_hoo_start + hoo_start_dows <= 24.0
361
+ new_hoo_start = orig_hoo_start + hoo_start_dows
362
+ else
363
+ new_hoo_start = orig_hoo_start + hoo_start_dows - 24.0
364
+ end
365
+ if new_hoo_start + hoo_dur_dows + orig_hoo_dur <= 24.0
366
+ new_hoo_end = new_hoo_start + target_dur
367
+ else
368
+ new_hoo_end = new_hoo_start + target_dur - 24.0
369
+ end
370
+ else
371
+ new_hoo_start = hoo_start_dows
372
+ target_dur = hoo_dur_dows
373
+ if new_hoo_start + target_dur < 24.0
374
+ new_hoo_end = new_hoo_start + target_dur
375
+ elsif new_hoo_start + target_dur == 24.0
376
+ new_hoo_end = 0.0
377
+ else # greater than 24
378
+ new_hoo_end = new_hoo_start + target_dur - 24.0
379
+ end
380
+ end
381
+
382
+ # setup hoo start time
383
+ target_start_hr = new_hoo_start.truncate
384
+ target_start_min = ((new_hoo_start - target_start_hr) * 60.0).truncate
385
+ target_start_time = OpenStudio::Time.new(0, target_start_hr, target_start_min, 0)
386
+
387
+ # setup hoo end time
388
+ target_end_hr = new_hoo_end.truncate
389
+ target_end_min = ((new_hoo_end - target_end_hr) * 60.0).truncate
390
+ target_end_time = OpenStudio::Time.new(0, target_end_hr, target_end_min, 0)
391
+
392
+ # adding new values
393
+ new_profile.clearValues
394
+ if target_dur < 24
395
+ new_profile.addValue(target_start_time, 0)
396
+ end
397
+ if target_dur > 0
398
+ new_profile.addValue(target_end_time, 1)
399
+ end
400
+ os_time_24 = OpenStudio::Time.new(0, 24, 0, 0)
401
+ if target_end_time > target_start_time || target_start_time == os_time_24
402
+ new_profile.addValue(os_time_24, 0)
403
+ elsif target_end_time < target_start_time
404
+ new_profile.addValue(os_time_24, 1)
405
+ else # they are equal
406
+ if target_dur == 24.0
407
+ new_profile.addValue(os_time_24, 1)
408
+ else
409
+ new_profile.addValue(os_time_24, 0)
410
+ end
411
+ end
412
+ end
413
+ end
414
+
415
+ return altered_schedule_days
416
+ end
417
+
418
+ # define what happens when the measure is run
419
+ def run(model, runner, user_arguments)
420
+ super(model, runner, user_arguments)
421
+
422
+ # assign the user inputs to variables
423
+ args = OsLib_HelperMethods.createRunVariables(runner, model, user_arguments, arguments(model))
424
+ if !args then return false end
425
+
426
+ # check expected values of double arguments
427
+ fraction = OsLib_HelperMethods.checkDoubleAndIntegerArguments(runner, user_arguments, 'min' => 0.0, 'max' => 1.0, 'min_eq_bool' => true, 'max_eq_bool' => true, 'arg_array' => ['fraction_of_daily_occ_range'])
428
+
429
+ neg_24__24 = ['hoo_start_weekday',
430
+ 'hoo_dur_weekday',
431
+ 'hoo_start_saturday',
432
+ 'hoo_dur_saturday',
433
+ 'hoo_start_sunday',
434
+ 'hoo_dur_sunday']
435
+ time_hours = OsLib_HelperMethods.checkDoubleAndIntegerArguments(runner, user_arguments, 'min' => -24.0, 'max' => 24.0, 'min_eq_bool' => true, 'max_eq_bool' => true, 'arg_array' => neg_24__24)
436
+
437
+ # setup log messages that will come from standards
438
+ OsLib_HelperMethods.setup_log_msgs(runner, true) # bool is debug
439
+
440
+ # load standards
441
+ standard = Standard.build('90.1-2004') # selected template doesn't matter
442
+
443
+ if args['infer_parametric_schedules']
444
+ # infer hours of operation for the building
445
+ # @param fraction_of_daily_occ_range [Double] fraction above/below daily min range required to start and end hours of operation
446
+ occ_fraction = args['fraction_of_daily_occ_range']
447
+ standard.model_infer_hours_of_operation_building(model, fraction_of_daily_occ_range: occ_fraction, gen_occ_profile: true)
448
+ runner.registerInfo('Inferring initial hours of operation for the building and generating parametric profile formulas.')
449
+
450
+ # report back hours of operation
451
+ initial_hoo_range = []
452
+ hours_of_operation_hash_test = standard.space_hours_of_operation(model.getSpaces.first)
453
+ hours_of_operation_hash_test.each do |hoo_key, val|
454
+ initial_hoo_range << val[:hoo_hours]
455
+ runner.registerInfo("For Profile Index #{hoo_key} hours of operation run for #{val[:hoo_hours]} hours, from #{val[:hoo_start]} to #{val[:hoo_end]} and is used for #{val[:days_used].size} days of the year.")
456
+ end
457
+
458
+ # model_setup_parametric_schedules
459
+ standard.model_setup_parametric_schedules(model, gather_data_only: false, hoo_var_method: args['hoo_var_method'])
460
+ end
461
+
462
+ # report initial condition of model
463
+ hoo_summary_hash = hoo_summary(model, runner, standard)
464
+ if !hoo_summary_hash[:zero_hoo].empty?
465
+ runner.registerInitialCondition("Across the building the non-zero hours of operation range from #{hoo_summary_hash[:final_hoo_dur_range].min} hours to #{hoo_summary_hash[:final_hoo_dur_range].max} hours. Start of hours of operation range from #{hoo_summary_hash[:final_hoo_start_range].min} to #{hoo_summary_hash[:final_hoo_start_range].max}. One or more hours of operation schedules used contain a profile with 0 hours of operation.")
466
+ else
467
+ runner.registerInitialCondition("Across the building the hours of operation range from #{hoo_summary_hash[:final_hoo_dur_range].min} hours to #{hoo_summary_hash[:final_hoo_dur_range].max} hours. Start of hours of operation range from #{hoo_summary_hash[:final_hoo_start_range].min} to #{hoo_summary_hash[:final_hoo_start_range].max}.")
468
+ end
469
+
470
+ # gather hours of operation schedules used by this model
471
+ used_hoo_sch_sets = {}
472
+ model.getSpaces.sort.each do |space|
473
+ default_sch_type = OpenStudio::Model::DefaultScheduleType.new('HoursofOperationSchedule')
474
+ hours_of_operation = space.getDefaultSchedule(default_sch_type)
475
+ if !hours_of_operation.is_initialized
476
+ runner.registerWarning("Hours of Operation Schedule is not set for #{space.name}.")
477
+ next
478
+ end
479
+ hours_of_operation_hash = standard.space_hours_of_operation(space)
480
+ used_hoo_sch_sets[hours_of_operation.get] = hours_of_operation_hash
481
+ end
482
+
483
+ # loop through and alter hours of operation schedules
484
+ runner.registerInfo("There are #{used_hoo_sch_sets.uniq.size} hours of operation schedules in the model to alter.")
485
+
486
+ # process weekday profiles
487
+ runner.registerInfo('Altering hours of operation schedules for weekday profiles')
488
+ weekday = process_hoo(used_hoo_sch_sets, model, runner, args, ['mon', 'tue', 'wed', 'thur', 'fri'], args['hoo_start_weekday'], args['hoo_dur_weekday'])
489
+
490
+ # process saturday profiles
491
+ runner.registerInfo('Altering hours of operation schedules for saturday profiles')
492
+ saturday = process_hoo(used_hoo_sch_sets, model, runner, args, ['sat'], args['hoo_start_saturday'], args['hoo_dur_saturday'])
493
+
494
+ # process sunday profiles
495
+ runner.registerInfo('Altering hours of operation schedules for sunday profiles')
496
+ sunday = process_hoo(used_hoo_sch_sets, model, runner, args, ['sun'], args['hoo_start_sunday'], args['hoo_dur_sunday'])
497
+
498
+ # TODO: - need to address this error when manipulating schedules
499
+ # [openstudio.standards.ScheduleRuleset] <1> Pre-interpolated processed hash for Large Office Bldg Equip Default Schedule has one or more out of order conflicts: [[3.5, 0.8], [4.5, 0.6], [5.0, 0.6], [7.0, 0.5], [9.0, 0.4], [6.0, 0.4], [10.0, 0.9], [16.5, 0.9], [17.5, 0.8], [18.5, 0.9], [21.5, 0.9]]. Method will stop because Error on Out of Order was set to true.
500
+ # model_build_parametric_schedules
501
+ parametric_schedules = standard.model_apply_parametric_schedules(model, ramp_frequency: nil, infer_hoo_for_non_assigned_objects: true, error_on_out_of_order: true)
502
+ runner.registerInfo("Created #{parametric_schedules.size} parametric schedules.")
503
+
504
+ # report final condition of model
505
+ hoo_summary_hash = hoo_summary(model, runner, standard)
506
+ if !hoo_summary_hash[:zero_hoo].empty?
507
+ runner.registerFinalCondition("Across the building the non-zero hours of operation range from #{hoo_summary_hash[:final_hoo_dur_range].min} hours to #{hoo_summary_hash[:final_hoo_dur_range].max} hours. Start of hours of operation range from #{hoo_summary_hash[:final_hoo_start_range].min} to #{hoo_summary_hash[:final_hoo_start_range].max}. One or more hours of operation schedules used contain a profile with 0 hours of operation.")
508
+ else
509
+ runner.registerFinalCondition("Across the building the hours of operation range from #{hoo_summary_hash[:final_hoo_dur_range].min} hours to #{hoo_summary_hash[:final_hoo_dur_range].max} hours. Start of hours of operation range from #{hoo_summary_hash[:final_hoo_start_range].min} to #{hoo_summary_hash[:final_hoo_start_range].max}.")
510
+ end
511
+
512
+ # get log messages (if debug in setup is true this will fail for error)
513
+ OsLib_HelperMethods.log_msgs
514
+
515
+ # TODO: - adding hours of operation to a schedule that doesn't have them to start with, like a sunday, can be problematic
516
+ # todo - start of day may not be reliable and there may not be formula inputs to show what occupied behavior is
517
+ # todo - in a situation like that it could be good to get formula from day that was non-zero to start with like weekday or saturday.
518
+ # todo - maybe standards can do something like this when making the formulas in the first place.
519
+
520
+ return true
521
+ end
522
+ end
523
+
524
+ # register the measure to be used by the application
525
+ ShiftHoursOfOperation.new.registerWithApplication