openstudio-extension 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.md +1 -1
- data/README.md +2 -0
- data/lib/openstudio/extension/runner.rb +12 -8
- data/lib/openstudio/extension/runner_config.rb +33 -6
- data/lib/openstudio/extension/version.rb +1 -1
- data/openstudio-extension.gemspec +6 -6
- metadata +15 -66
- data/lib/openstudio/extension/core/CreateResults.rb +0 -1033
- data/lib/openstudio/extension/core/check_air_sys_temps.rb +0 -160
- data/lib/openstudio/extension/core/check_calibration.rb +0 -125
- data/lib/openstudio/extension/core/check_cond_zns.rb +0 -54
- data/lib/openstudio/extension/core/check_domestic_hot_water.rb +0 -304
- data/lib/openstudio/extension/core/check_envelope_conductance.rb +0 -423
- data/lib/openstudio/extension/core/check_eui_by_end_use.rb +0 -132
- data/lib/openstudio/extension/core/check_eui_reasonableness.rb +0 -105
- data/lib/openstudio/extension/core/check_fan_pwr.rb +0 -68
- data/lib/openstudio/extension/core/check_internal_loads.rb +0 -363
- data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +0 -196
- data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +0 -296
- data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +0 -434
- data/lib/openstudio/extension/core/check_mech_sys_type.rb +0 -109
- data/lib/openstudio/extension/core/check_part_loads.rb +0 -421
- data/lib/openstudio/extension/core/check_placeholder.rb +0 -45
- data/lib/openstudio/extension/core/check_plant_cap.rb +0 -93
- data/lib/openstudio/extension/core/check_plant_temps.rb +0 -129
- data/lib/openstudio/extension/core/check_plenum_loads.rb +0 -57
- data/lib/openstudio/extension/core/check_pump_pwr.rb +0 -78
- data/lib/openstudio/extension/core/check_sch_coord.rb +0 -211
- data/lib/openstudio/extension/core/check_schedules.rb +0 -281
- data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +0 -128
- data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +0 -118
- data/lib/openstudio/extension/core/check_weather_files.rb +0 -102
- data/lib/openstudio/extension/core/deer_vintages.rb +0 -281
- data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +0 -461
- data/lib/openstudio/extension/core/os_lib_constructions.rb +0 -353
- data/lib/openstudio/extension/core/os_lib_geometry.rb +0 -1169
- data/lib/openstudio/extension/core/os_lib_helper_methods.rb +0 -383
- data/lib/openstudio/extension/core/os_lib_hvac.rb +0 -2163
- data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +0 -184
- data/lib/openstudio/extension/core/os_lib_model_generation.rb +0 -3584
- data/lib/openstudio/extension/core/os_lib_model_simplification.rb +0 -1019
- data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +0 -135
- data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +0 -170
- data/lib/openstudio/extension/core/os_lib_schedules.rb +0 -933
@@ -1,129 +0,0 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
-
# See also https://openstudio.net/license
|
4
|
-
# *******************************************************************************
|
5
|
-
|
6
|
-
module OsLib_QAQC
|
7
|
-
# Check the plant loop operational vs. sizing temperatures
|
8
|
-
# and make sure everything is coordinated. This identifies problems
|
9
|
-
# caused by sizing to one set of conditions and operating at a different set.
|
10
|
-
def check_plant_temps(category, target_standard, max_sizing_temp_delta = 0.1, name_only = false)
|
11
|
-
# summary of the check
|
12
|
-
check_elems = OpenStudio::AttributeVector.new
|
13
|
-
check_elems << OpenStudio::Attribute.new('name', 'Plant Loop Temperatures')
|
14
|
-
check_elems << OpenStudio::Attribute.new('category', category)
|
15
|
-
check_elems << OpenStudio::Attribute.new('description', 'Check that plant loop sizing and operation temperatures are coordinated.')
|
16
|
-
|
17
|
-
# stop here if only name is requested this is used to populate display name for arguments
|
18
|
-
if name_only == true
|
19
|
-
results = []
|
20
|
-
check_elems.each do |elem|
|
21
|
-
results << elem.valueAsString
|
22
|
-
end
|
23
|
-
return results
|
24
|
-
end
|
25
|
-
|
26
|
-
std = Standard.build(target_standard)
|
27
|
-
|
28
|
-
begin
|
29
|
-
# Check each plant loop in the model
|
30
|
-
@model.getPlantLoops.sort.each do |plant_loop|
|
31
|
-
loop_name = plant_loop.name.to_s
|
32
|
-
|
33
|
-
# Get the central heating and cooling SAT for sizing
|
34
|
-
sizing_plant = plant_loop.sizingPlant
|
35
|
-
loop_siz_f = OpenStudio.convert(sizing_plant.designLoopExitTemperature, 'C', 'F').get
|
36
|
-
|
37
|
-
# Determine the min and max operational temperatures
|
38
|
-
loop_op_min_f = nil
|
39
|
-
loop_op_max_f = nil
|
40
|
-
plant_loop.supplyOutletNode.setpointManagers.each do |spm|
|
41
|
-
obj_type = spm.iddObjectType.valueName.to_s
|
42
|
-
case obj_type
|
43
|
-
when 'OS_SetpointManager_Scheduled'
|
44
|
-
sch = spm.to_SetpointManagerScheduled.get.schedule
|
45
|
-
if sch.to_ScheduleRuleset.is_initialized
|
46
|
-
min_c = std.schedule_ruleset_annual_min_max_value(sch.to_ScheduleRuleset.get)['min']
|
47
|
-
max_c = std.schedule_ruleset_annual_min_max_value(sch.to_ScheduleRuleset.get)['max']
|
48
|
-
elsif sch.to_ScheduleConstant.is_initialized
|
49
|
-
min_c = std.schedule_constant_annual_min_max_value(sch.to_ScheduleConstant.get)['min']
|
50
|
-
max_c = std.schedule_constant_annual_min_max_value(sch.to_ScheduleConstant.get)['max']
|
51
|
-
else
|
52
|
-
next
|
53
|
-
end
|
54
|
-
loop_op_min_f = OpenStudio.convert(min_c, 'C', 'F').get
|
55
|
-
loop_op_max_f = OpenStudio.convert(max_c, 'C', 'F').get
|
56
|
-
when 'OS_SetpointManager_Scheduled_DualSetpoint'
|
57
|
-
spm = spm.to_SetpointManagerSingleZoneReheat.get
|
58
|
-
# Lowest setpoint is minimum of low schedule
|
59
|
-
low_sch = spm.to_SetpointManagerScheduled.get.lowSetpointSchedule
|
60
|
-
next if low_sch.empty?
|
61
|
-
low_sch = low_sch.get
|
62
|
-
if low_sch.to_ScheduleRuleset.is_initialized
|
63
|
-
min_c = std.schedule_ruleset_annual_min_max_value(low_sch.to_ScheduleRuleset.get)['min']
|
64
|
-
max_c = std.schedule_ruleset_annual_min_max_value(low_sch.to_ScheduleRuleset.get)['max']
|
65
|
-
elsif low_sch.to_ScheduleConstant.is_initialized
|
66
|
-
min_c = std.schedule_constant_annual_min_max_value(low_sch.to_ScheduleConstant.get)['min']
|
67
|
-
max_c = std.schedule_constant_annual_min_max_value(low_sch.to_ScheduleConstant.get)['max']
|
68
|
-
else
|
69
|
-
next
|
70
|
-
end
|
71
|
-
loop_op_min_f = OpenStudio.convert(min_c, 'C', 'F').get
|
72
|
-
# Highest setpoint it maximum of high schedule
|
73
|
-
high_sch = spm.to_SetpointManagerScheduled.get.highSetpointSchedule
|
74
|
-
next if high_sch.empty?
|
75
|
-
high_sch = high_sch.get
|
76
|
-
if high_sch.to_ScheduleRuleset.is_initialized
|
77
|
-
min_c = std.schedule_ruleset_annual_min_max_value(high_sch.to_ScheduleRuleset.get)['min']
|
78
|
-
max_c = std.schedule_ruleset_annual_min_max_value(high_sch.to_ScheduleRuleset.get)['max']
|
79
|
-
elsif high_sch.to_ScheduleConstant.is_initialized
|
80
|
-
min_c = std.schedule_constant_annual_min_max_value(high_sch.to_ScheduleConstant.get)['min']
|
81
|
-
max_c = std.schedule_constant_annual_min_max_value(high_sch.to_ScheduleConstant.get)['max']
|
82
|
-
else
|
83
|
-
next
|
84
|
-
end
|
85
|
-
loop_op_max_f = OpenStudio.convert(max_c, 'C', 'F').get
|
86
|
-
when 'OS_SetpointManager_OutdoorAirReset'
|
87
|
-
spm = spm.to_SetpointManagerOutdoorAirReset.get
|
88
|
-
temp_1_f = OpenStudio.convert(spm.setpointatOutdoorHighTemperature, 'C', 'F').get
|
89
|
-
temp_2_f = OpenStudio.convert(spm.setpointatOutdoorLowTemperature, 'C', 'F').get
|
90
|
-
loop_op_min_f = [temp_1_f, temp_2_f].min
|
91
|
-
loop_op_max_f = [temp_1_f, temp_2_f].max
|
92
|
-
else
|
93
|
-
next # Only check the commonly used setpoint managers
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# Compare plant loop sizing temperatures to operational temperatures
|
98
|
-
case sizing_plant.loopType
|
99
|
-
when 'Heating'
|
100
|
-
if loop_op_max_f
|
101
|
-
if ((loop_op_max_f - loop_siz_f) / loop_op_max_f).abs > max_sizing_temp_delta
|
102
|
-
check_elems << OpenStudio::Attribute.new('flag', "For #{plant_loop.name}, the sizing is done with a supply water temp of #{loop_siz_f.round(2)}F, but the setpoint manager controlling the loop operates up to #{loop_op_max_f.round(2)}F. These are farther apart than the acceptable #{(max_sizing_temp_delta * 100.0).round(2)}% difference.")
|
103
|
-
end
|
104
|
-
end
|
105
|
-
when 'Cooling'
|
106
|
-
if loop_op_min_f
|
107
|
-
if ((loop_op_min_f - loop_siz_f) / loop_op_min_f).abs > max_sizing_temp_delta
|
108
|
-
check_elems << OpenStudio::Attribute.new('flag', "For #{plant_loop.name}, the sizing is done with a supply water temp of #{loop_siz_f.round(2)}F, but the setpoint manager controlling the loop operates down to #{loop_op_min_f.round(2)}F. These are farther apart than the acceptable #{(max_sizing_temp_delta * 100.0).round(2)}% difference.")
|
109
|
-
end
|
110
|
-
end
|
111
|
-
when 'Condenser'
|
112
|
-
# Not checking sizing of condenser loops
|
113
|
-
end
|
114
|
-
end
|
115
|
-
rescue StandardError => e
|
116
|
-
# brief description of ruby error
|
117
|
-
check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
|
118
|
-
|
119
|
-
# backtrace of ruby error for diagnostic use
|
120
|
-
if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
|
121
|
-
end
|
122
|
-
|
123
|
-
# add check_elms to new attribute
|
124
|
-
check_elem = OpenStudio::Attribute.new('check', check_elems)
|
125
|
-
|
126
|
-
return check_elem
|
127
|
-
# note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
|
128
|
-
end
|
129
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
-
# See also https://openstudio.net/license
|
4
|
-
# *******************************************************************************
|
5
|
-
|
6
|
-
module OsLib_QAQC
|
7
|
-
# Check that there are no people or lights in plenums.
|
8
|
-
def check_plenum_loads(category, target_standard, name_only = false)
|
9
|
-
# summary of the check
|
10
|
-
check_elems = OpenStudio::AttributeVector.new
|
11
|
-
check_elems << OpenStudio::Attribute.new('name', 'Plenum Loads')
|
12
|
-
check_elems << OpenStudio::Attribute.new('category', category)
|
13
|
-
check_elems << OpenStudio::Attribute.new('description', 'Check that the plenums do not have people or lights.')
|
14
|
-
|
15
|
-
# stop here if only name is requested this is used to populate display name for arguments
|
16
|
-
if name_only == true
|
17
|
-
results = []
|
18
|
-
check_elems.each do |elem|
|
19
|
-
results << elem.valueAsString
|
20
|
-
end
|
21
|
-
return results
|
22
|
-
end
|
23
|
-
|
24
|
-
std = Standard.build(target_standard)
|
25
|
-
|
26
|
-
begin
|
27
|
-
@model.getThermalZones.each do |zone|
|
28
|
-
# Only check plenums
|
29
|
-
next unless std.thermal_zone_plenum?(zone)
|
30
|
-
|
31
|
-
# People
|
32
|
-
num_people = zone.numberOfPeople
|
33
|
-
if num_people > 0
|
34
|
-
check_elems << OpenStudio::Attribute.new('flag', "#{zone.name} is a plenum, but has #{num_people.round(1)} people. Plenums should not contain people.")
|
35
|
-
end
|
36
|
-
|
37
|
-
# Lights
|
38
|
-
lights_w = zone.lightingPower
|
39
|
-
if lights_w > 0
|
40
|
-
check_elems << OpenStudio::Attribute.new('flag', "#{zone.name} is a plenum, but has #{lights_w.round(1)} W of lights. Plenums should not contain lights.")
|
41
|
-
end
|
42
|
-
end
|
43
|
-
rescue StandardError => e
|
44
|
-
# brief description of ruby error
|
45
|
-
check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
|
46
|
-
|
47
|
-
# backtrace of ruby error for diagnostic use
|
48
|
-
if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
|
49
|
-
end
|
50
|
-
|
51
|
-
# add check_elms to new attribute
|
52
|
-
check_elem = OpenStudio::Attribute.new('check', check_elems)
|
53
|
-
|
54
|
-
return check_elem
|
55
|
-
# note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
|
56
|
-
end
|
57
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
-
# See also https://openstudio.net/license
|
4
|
-
# *******************************************************************************
|
5
|
-
|
6
|
-
module OsLib_QAQC
|
7
|
-
# Check the pumping power (W/gpm) for each pump in the model to identify
|
8
|
-
# unrealistically sized pumps.
|
9
|
-
def check_pump_pwr(category, target_standard, max_pwr_delta = 0.1, name_only = false)
|
10
|
-
# summary of the check
|
11
|
-
check_elems = OpenStudio::AttributeVector.new
|
12
|
-
check_elems << OpenStudio::Attribute.new('name', 'Pump Power')
|
13
|
-
check_elems << OpenStudio::Attribute.new('category', category)
|
14
|
-
check_elems << OpenStudio::Attribute.new('description', 'Check that pump power vs flow makes sense.')
|
15
|
-
|
16
|
-
# stop here if only name is requested this is used to populate display name for arguments
|
17
|
-
if name_only == true
|
18
|
-
results = []
|
19
|
-
check_elems.each do |elem|
|
20
|
-
results << elem.valueAsString
|
21
|
-
end
|
22
|
-
return results
|
23
|
-
end
|
24
|
-
|
25
|
-
std = Standard.build(target_standard)
|
26
|
-
|
27
|
-
begin
|
28
|
-
# Check each plant loop
|
29
|
-
@model.getPlantLoops.each do |plant_loop|
|
30
|
-
# Set the expected/typical W/gpm
|
31
|
-
loop_type = plant_loop.sizingPlant.loopType
|
32
|
-
case loop_type
|
33
|
-
when 'Heating'
|
34
|
-
expected_w_per_gpm = 19.0
|
35
|
-
when 'Cooling'
|
36
|
-
expected_w_per_gpm = 22.0
|
37
|
-
when 'Condenser'
|
38
|
-
expected_w_per_gpm = 19.0
|
39
|
-
end
|
40
|
-
|
41
|
-
# Check the W/gpm for each pump on each plant loop
|
42
|
-
plant_loop.supplyComponents.each do |sc|
|
43
|
-
# Get the W/gpm for the pump
|
44
|
-
obj_type = sc.iddObjectType.valueName.to_s
|
45
|
-
case obj_type
|
46
|
-
when 'OS_Pump_ConstantSpeed'
|
47
|
-
actual_w_per_gpm = std.pump_rated_w_per_gpm(sc.to_PumpConstantSpeed.get)
|
48
|
-
when 'OS_Pump_VariableSpeed'
|
49
|
-
actual_w_per_gpm = std.pump_rated_w_per_gpm(sc.to_PumpVariableSpeed.get)
|
50
|
-
when 'OS_HeaderedPumps_ConstantSpeed'
|
51
|
-
actual_w_per_gpm = std.pump_rated_w_per_gpm(sc.to_HeaderedPumpsConstantSpeed.get)
|
52
|
-
when 'OS_HeaderedPumps_VariableSpeed'
|
53
|
-
actual_w_per_gpm = std.pump_rated_w_per_gpm(sc.to_HeaderedPumpsVariableSpeed.get)
|
54
|
-
else
|
55
|
-
next # Skip non-pump objects
|
56
|
-
end
|
57
|
-
|
58
|
-
# Compare W/gpm to expected/typical values
|
59
|
-
if ((expected_w_per_gpm - actual_w_per_gpm) / actual_w_per_gpm).abs > max_pwr_delta
|
60
|
-
check_elems << OpenStudio::Attribute.new('flag', "For #{sc.name} on #{plant_loop.name}, the actual pumping power of #{actual_w_per_gpm.round(1)} W/gpm is more than #{(max_pwr_delta * 100.0).round(2)}% different from the expected #{expected_w_per_gpm} W/gpm for a #{loop_type} plant loop.")
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
rescue StandardError => e
|
65
|
-
# brief description of ruby error
|
66
|
-
check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
|
67
|
-
|
68
|
-
# backtrace of ruby error for diagnostic use
|
69
|
-
if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
|
70
|
-
end
|
71
|
-
|
72
|
-
# add check_elms to new attribute
|
73
|
-
check_elem = OpenStudio::Attribute.new('check', check_elems)
|
74
|
-
|
75
|
-
return check_elem
|
76
|
-
# note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
|
77
|
-
end
|
78
|
-
end
|
@@ -1,211 +0,0 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
-
# See also https://openstudio.net/license
|
4
|
-
# *******************************************************************************
|
5
|
-
|
6
|
-
module OsLib_QAQC
|
7
|
-
# Determine the hour when the schedule first exceeds the starting value and when
|
8
|
-
# it goes back down to the ending value at the end of the day.
|
9
|
-
# This method only works for ScheduleRuleset schedules.
|
10
|
-
def get_start_and_end_times(schedule_ruleset)
|
11
|
-
# Ensure that this is a ScheduleRuleset
|
12
|
-
schedule_ruleset = schedule_ruleset.to_ScheduleRuleset
|
13
|
-
return [nil, nil] if schedule_ruleset.empty?
|
14
|
-
schedule_ruleset = schedule_ruleset.get
|
15
|
-
|
16
|
-
# Define the start and end date
|
17
|
-
year_start_date = nil
|
18
|
-
year_end_date = nil
|
19
|
-
if schedule_ruleset.model.yearDescription.is_initialized
|
20
|
-
year_description = schedule_ruleset.model.yearDescription.get
|
21
|
-
year = year_description.assumedYear
|
22
|
-
year_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, year)
|
23
|
-
year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, year)
|
24
|
-
else
|
25
|
-
year_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, 2009)
|
26
|
-
year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, 2009)
|
27
|
-
end
|
28
|
-
|
29
|
-
# Get the ordered list of all the day schedules that are used by this schedule ruleset
|
30
|
-
day_schs = schedule_ruleset.getDaySchedules(year_start_date, year_end_date)
|
31
|
-
|
32
|
-
# Get a 365-value array of which schedule is used on each day of the year,
|
33
|
-
day_schs_used_each_day = schedule_ruleset.getActiveRuleIndices(year_start_date, year_end_date)
|
34
|
-
|
35
|
-
# Create a map that shows how many days each schedule is used
|
36
|
-
day_sch_freq = day_schs_used_each_day.group_by { |n| n }
|
37
|
-
day_sch_freq = day_sch_freq.sort_by { |freq| freq[1].size }
|
38
|
-
common_day_freq = day_sch_freq.last
|
39
|
-
|
40
|
-
# Build a hash that maps schedule day index to schedule day
|
41
|
-
schedule_index_to_day = {}
|
42
|
-
day_schs.each_with_index do |day_sch, i|
|
43
|
-
schedule_index_to_day[day_schs_used_each_day[i]] = day_sch
|
44
|
-
end
|
45
|
-
|
46
|
-
# Get the most common day schedule
|
47
|
-
sch_index = common_day_freq[0]
|
48
|
-
number_of_days_sch_used = common_day_freq[1].size
|
49
|
-
|
50
|
-
# Get the day schedule at this index
|
51
|
-
day_sch = if sch_index == -1 # If index = -1, this day uses the default day schedule (not a rule)
|
52
|
-
schedule_ruleset.defaultDaySchedule
|
53
|
-
else
|
54
|
-
schedule_index_to_day[sch_index]
|
55
|
-
end
|
56
|
-
|
57
|
-
# Determine the full load hours for just one day
|
58
|
-
values = []
|
59
|
-
times = []
|
60
|
-
day_sch.times.each_with_index do |time, i|
|
61
|
-
times << day_sch.times[i]
|
62
|
-
values << day_sch.values[i]
|
63
|
-
end
|
64
|
-
|
65
|
-
# Get the minimum value
|
66
|
-
start_val = values.first
|
67
|
-
end_val = values.last
|
68
|
-
|
69
|
-
# Get the start time (first time value goes above minimum)
|
70
|
-
start_time = nil
|
71
|
-
values.each_with_index do |val, i|
|
72
|
-
break if i == values.size - 1 # Stop if we reach end of array
|
73
|
-
if val == start_val && values[i + 1] > start_val
|
74
|
-
start_time = times[i + 1]
|
75
|
-
break
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Get the end time (first time value goes back down to minimum)
|
80
|
-
end_time = nil
|
81
|
-
values.each_with_index do |val, i|
|
82
|
-
if i < values.size - 1
|
83
|
-
if val > end_val && values[i + 1] == end_val
|
84
|
-
end_time = times[i]
|
85
|
-
break
|
86
|
-
end
|
87
|
-
else
|
88
|
-
if val > end_val && values[0] == start_val # Check first hour of day for schedules that end at midnight
|
89
|
-
end_time = OpenStudio::Time.new(0, 24, 0, 0)
|
90
|
-
break
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
return [start_time, end_time]
|
96
|
-
end
|
97
|
-
|
98
|
-
# Check that the lighting, equipment, and HVAC setpoint schedules
|
99
|
-
# coordinate with the occupancy schedules. This is defined as having start and end
|
100
|
-
# times within the specified number of hours away from the occupancy schedule.
|
101
|
-
def check_sch_coord(category, target_standard, max_hrs, name_only = false)
|
102
|
-
# summary of the check
|
103
|
-
check_elems = OpenStudio::AttributeVector.new
|
104
|
-
check_elems << OpenStudio::Attribute.new('name', 'Conditioned Zones')
|
105
|
-
check_elems << OpenStudio::Attribute.new('category', category)
|
106
|
-
check_elems << OpenStudio::Attribute.new('description', 'Check that lighting, equipment, and HVAC schedules coordinate with occupancy.')
|
107
|
-
|
108
|
-
# stop here if only name is requested this is used to populate display name for arguments
|
109
|
-
if name_only == true
|
110
|
-
results = []
|
111
|
-
check_elems.each do |elem|
|
112
|
-
results << elem.valueAsString
|
113
|
-
end
|
114
|
-
return results
|
115
|
-
end
|
116
|
-
|
117
|
-
std = Standard.build(target_standard)
|
118
|
-
|
119
|
-
begin
|
120
|
-
# Convert max hr limit to OpenStudio Time
|
121
|
-
max_hrs = OpenStudio::Time.new(0, max_hrs, 0, 0)
|
122
|
-
|
123
|
-
# Check schedules in each space
|
124
|
-
@model.getSpaces.each do |space|
|
125
|
-
# Occupancy, Lighting, and Equipment Schedules
|
126
|
-
coord_schs = []
|
127
|
-
occ_schs = []
|
128
|
-
# Get the space type (optional)
|
129
|
-
space_type = space.spaceType
|
130
|
-
|
131
|
-
# Occupancy
|
132
|
-
occs = []
|
133
|
-
occs += space.people # From space directly
|
134
|
-
occs += space_type.get.people if space_type.is_initialized # Inherited from space type
|
135
|
-
occs.each do |occ|
|
136
|
-
occ_schs << occ.numberofPeopleSchedule.get if occ.numberofPeopleSchedule.is_initialized
|
137
|
-
end
|
138
|
-
|
139
|
-
# Lights
|
140
|
-
lts = []
|
141
|
-
lts += space.lights # From space directly
|
142
|
-
lts += space_type.get.lights if space_type.is_initialized # Inherited from space type
|
143
|
-
lts.each do |lt|
|
144
|
-
coord_schs << lt.schedule.get if lt.schedule.is_initialized
|
145
|
-
end
|
146
|
-
|
147
|
-
# Equip
|
148
|
-
plugs = []
|
149
|
-
plugs += space.electricEquipment # From space directly
|
150
|
-
plugs += space_type.get.electricEquipment if space_type.is_initialized # Inherited from space type
|
151
|
-
plugs.each do |plug|
|
152
|
-
coord_schs << plug.schedule.get if plug.schedule.is_initialized
|
153
|
-
end
|
154
|
-
|
155
|
-
# HVAC Schedule (airloop-served zones only)
|
156
|
-
if space.thermalZone.is_initialized
|
157
|
-
zone = space.thermalZone.get
|
158
|
-
if zone.airLoopHVAC.is_initialized
|
159
|
-
coord_schs << zone.airLoopHVAC.get.availabilitySchedule
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Cannot check spaces with no occupancy schedule to compare against
|
164
|
-
next if occ_schs.empty?
|
165
|
-
|
166
|
-
# Get start and end occupancy times from the first occupancy schedule
|
167
|
-
occ_start_time, occ_end_time = get_start_and_end_times(occ_schs[0])
|
168
|
-
|
169
|
-
# Cannot check a space where the occupancy start time or end time cannot be determined
|
170
|
-
next if occ_start_time.nil? || occ_end_time.nil?
|
171
|
-
|
172
|
-
# Check all schedules against occupancy
|
173
|
-
|
174
|
-
# Lights should have a start and end within X hrs of the occupancy start and end
|
175
|
-
coord_schs.each do |coord_sch|
|
176
|
-
# Get start and end time of load/HVAC schedule
|
177
|
-
start_time, end_time = get_start_and_end_times(coord_sch)
|
178
|
-
if start_time.nil?
|
179
|
-
check_elems << OpenStudio::Attribute.new('flag', "Could not determine start time of a schedule called #{coord_sch.name}, cannot determine if schedule coordinates with occupancy schedule.")
|
180
|
-
next
|
181
|
-
elsif end_time.nil?
|
182
|
-
check_elems << OpenStudio::Attribute.new('flag', "Could not determine end time of a schedule called #{coord_sch.name}, cannot determine if schedule coordinates with occupancy schedule.")
|
183
|
-
next
|
184
|
-
end
|
185
|
-
|
186
|
-
# Check start time
|
187
|
-
if (occ_start_time - start_time) > max_hrs || (start_time - occ_start_time) > max_hrs
|
188
|
-
check_elems << OpenStudio::Attribute.new('flag', "The start time of #{coord_sch.name} is #{start_time}, which is more than #{max_hrs} away from the occupancy schedule start time of #{occ_start_time} for #{occ_schs[0].name} in #{space.name}. Schedules do not coordinate.")
|
189
|
-
end
|
190
|
-
|
191
|
-
# Check end time
|
192
|
-
if (occ_end_time - end_time) > max_hrs || (end_time - occ_end_time) > max_hrs
|
193
|
-
check_elems << OpenStudio::Attribute.new('flag', "The end time of #{coord_sch.name} is #{end_time}, which is more than #{max_hrs} away from the occupancy schedule end time of #{occ_end_time} for #{occ_schs[0].name} in #{space.name}. Schedules do not coordinate.")
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
rescue StandardError => e
|
198
|
-
# brief description of ruby error
|
199
|
-
check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
|
200
|
-
|
201
|
-
# backtrace of ruby error for diagnostic use
|
202
|
-
if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
|
203
|
-
end
|
204
|
-
|
205
|
-
# add check_elms to new attribute
|
206
|
-
check_elem = OpenStudio::Attribute.new('check', check_elems)
|
207
|
-
|
208
|
-
return check_elem
|
209
|
-
# note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
|
210
|
-
end
|
211
|
-
end
|