Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for infiltration function. #1578

Merged
merged 3 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/openstudio-standards/standards/Standards.Space.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,7 @@ def space_apply_infiltration_rate(space)
#
# @param space [OpenStudio::Model::Space] space object
# @return [Double] the baseline infiltration rate, in cfm/ft^2 exterior above grade wall area at 75 Pa
def space_infiltration_rate_75_pa(space)
def space_infiltration_rate_75_pa(space = nil)
basic_infil_rate_cfm_per_ft2 = 1.8
return basic_infil_rate_cfm_per_ft2
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,13 @@ def model_baseline_apply_infiltration_standard(model, climate_zone)
# Excerpt from the EnergyPlus Input/Output reference manual:
# "This model is based on work by Sherman and Grimsrud (1980)
# and is appropriate for smaller, residential-type buildings."
# Return an error if the model does use this object
# Raise exception if the model does use this object
ela = 0
model.getSpaceInfiltrationEffectiveLeakageAreas.sort.each do |eff_la|
model.getSpaceInfiltrationEffectiveLeakageAreas.each do |eff_la|
ela += 1
end
if ela > 0
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Model', 'The current model cannot include SpaceInfiltrationEffectiveLeakageArea. These objects cannot be used to model infiltration according to the 90.1-PRM rules.')
OpenStudio.logFree(OpenStudio::Warn, 'prm.log', 'The current model cannot include SpaceInfiltrationEffectiveLeakageArea. These objects will be skipped in modeling infiltration according to the 90.1-PRM rules.')
end

# Get the space building envelope area
Expand All @@ -145,21 +145,19 @@ def model_baseline_apply_infiltration_standard(model, climate_zone)
# transferred to or from the exterior, to or from unconditioned spaces or to or
# from conditioned spaces."
building_envelope_area_m2 = 0
model.getSpaces.sort.each do |space|
model.getSpaces.each do |space|
building_envelope_area_m2 += space_envelope_area(space, climate_zone)
end
if building_envelope_area_m2 == 0.0
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', 'Calculated building envelope area is 0 m2, no infiltration will be added.')
return 0.0
end
prm_raise(building_envelope_area_m2 > 0.0, @sizing_run_dir, 'Calculated building envelope area is 0 m2, Please check model inputs.')


# Calculate current model air leakage rate @ 75 Pa and report it
curr_tot_infil_m3_per_s_per_envelope_area = model_current_building_envelope_infiltration_at_75pa(model, building_envelope_area_m2)
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The proposed model I_75Pa is estimated to be #{curr_tot_infil_m3_per_s_per_envelope_area} m3/s per m2 of total building envelope.")
OpenStudio.logFree(OpenStudio::Info, 'prm.log', "The proposed model I_75Pa is estimated to be #{curr_tot_infil_m3_per_s_per_envelope_area} m3/s per m2 of total building envelope.")

# Calculate building adjusted building envelope
# air infiltration following the 90.1 PRM rules
tot_infil_m3_per_s = model_adjusted_building_envelope_infiltration(model, building_envelope_area_m2)
tot_infil_m3_per_s = model_adjusted_building_envelope_infiltration(building_envelope_area_m2)

# Find infiltration method used in the model, if any.
#
Expand All @@ -171,26 +169,26 @@ def model_baseline_apply_infiltration_standard(model, climate_zone)
infil_coefficients = model_get_infiltration_coefficients(model)

# Set the infiltration rate at each space
model.getSpaces.sort.each do |space|
model.getSpaces.each do |space|
space_apply_infiltration_rate(space, tot_infil_m3_per_s, infil_method, infil_coefficients)
end

# Remove infiltration rates set at the space type
model.getSpaceTypes.sort.each do |space_type|
model.getSpaceTypes.each do |space_type|
space_type.spaceInfiltrationDesignFlowRates.each(&:remove)
end

return true
end

# This method retrieves the type of infiltration input
# used in the model. If input is inconsitent, returns
# used in the model. If input is inconsistent, returns
# Flow/Area
#
# @return [String] infiltration input type
def model_get_infiltration_method(model)
infil_method = nil
model.getSpaces.sort.each do |space|
model.getSpaces.each do |space|
# Infiltration at the space level
unless space.spaceInfiltrationDesignFlowRates.empty?
old_infil = space.spaceInfiltrationDesignFlowRates[0]
Expand Down Expand Up @@ -229,7 +227,7 @@ def model_get_infiltration_coefficients(model)
vel = nil
vel_2 = nil
infil_coeffs = [cst, temp, vel, vel_2]
model.getSpaces.sort.each do |space|
model.getSpaces.each do |space|
# Infiltration at the space level
unless space.spaceInfiltrationDesignFlowRates.empty?
old_infil = space.spaceInfiltrationDesignFlowRates[0]
Expand Down Expand Up @@ -280,7 +278,7 @@ def model_get_infiltration_coefficients(model)
# @return [Float] building model air leakage rate
def model_current_building_envelope_infiltration_at_75pa(model, building_envelope_area_m2)
bldg_air_leakage_rate = 0
model.getSpaces.sort.each do |space|
model.getSpaces.each do |space|
# Infiltration at the space level
unless space.spaceInfiltrationDesignFlowRates.empty?
infil_obj = space.spaceInfiltrationDesignFlowRates[0]
Expand All @@ -301,6 +299,9 @@ def model_current_building_envelope_infiltration_at_75pa(model, building_envelop
if space.spaceType.is_initialized
space_type = space.spaceType.get
unless space_type.spaceInfiltrationDesignFlowRates.empty?
if bldg_air_leakage_rate > 0
OpenStudio.logFree(OpenStudio::Warn, 'prm.log', "A duplicated infiltration definition is found in spaceType. Verify your model inputs.")
end
infil_obj = space_type.spaceInfiltrationDesignFlowRates[0]
unless infil_obj.designFlowRate.is_initialized
if infil_obj.flowperSpaceFloorArea.is_initialized
Expand All @@ -324,15 +325,12 @@ def model_current_building_envelope_infiltration_at_75pa(model, building_envelop

# This method calculates the building envelope infiltration,
# this approach uses the 90.1 PRM rules
#
# @param building_envelope_area_m2 [Double] building envelope area
# @return [Float] building envelope infiltration
def model_adjusted_building_envelope_infiltration(model, building_envelope_area_m2)
def model_adjusted_building_envelope_infiltration(building_envelope_area_m2)
# Determine the total building baseline infiltration rate in cfm per ft2 of the building envelope at 75 Pa
basic_infil_rate_cfm_per_ft2 = space_infiltration_rate_75_pa

# Do nothing if no infiltration
return 0.0 if basic_infil_rate_cfm_per_ft2.zero?

# Conversion factor
conv_fact = OpenStudio.convert(1, 'm^3/s', 'ft^3/min').to_f / OpenStudio.convert(1, 'm^2', 'ft^2').to_f

Expand Down Expand Up @@ -717,7 +715,7 @@ def model_apply_baseline_exterior_lighting(model)
ext_lights_obj.setMultiplier(1)
ext_lights_def = ext_lights_obj.exteriorLightsDefinition
ext_ltg_pwr = get_additional_property_as_double(ext_lights_obj, 'design_level', 0.0)
if ext_ltg_pwr >= 0.0
if ext_ltg_pwr > 0.0
ext_lights_def.setDesignLevel(ext_ltg_pwr)
end
end
Expand Down Expand Up @@ -2396,10 +2394,7 @@ def get_baseline_system_groups_for_one_building_type(model, hvac_building_type,
zones = model_zones_with_occ_and_fuel_type(model, 'custom')

# Ensure that there is at least one conditioned zone
if zones.size.zero?
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Model', 'The building does not appear to have any conditioned zones. Make sure zones have thermostat with appropriate heating and cooling setpoint schedules.')
return []
end
prm_raise(zones.size > 0, @sizing_run_dir, 'The building does not appear to have any conditioned zones. Make sure zones have thermostat with appropriate heating and cooling setpoint schedules.')

# Consider special rules for computer rooms
# need load of all
Expand Down Expand Up @@ -2431,7 +2426,7 @@ def get_baseline_system_groups_for_one_building_type(model, hvac_building_type,
# Make list of zone objects that contain laboratory spaces
lab_zones = []
has_lab_spaces = {}
model.getThermalZones.sort.each do |zone|
model.getThermalZones.each do |zone|
# Check if this zone includes laboratory space
zone.spaces.each do |space|
space_type = prm_get_optional_handler(space, @sizing_run_dir, 'spaceType', 'standardsSpaceType')
Expand Down Expand Up @@ -2467,17 +2462,16 @@ def get_baseline_system_groups_for_one_building_type(model, hvac_building_type,
# Take from hourly reports created during sizing run
node_list.each do |node_name, zone_name|
sql = model.sqlFile
if sql.is_initialized
sql = sql.get
query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
val = sql.execAndReturnFirstDouble(query)
query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{val.get}'"
val = sql.execAndReturnFirstDouble(query)
if val.is_initialized
result = OpenStudio::OptionalDouble.new(val.get)
end
zone_return_flow_si[zone_name] += result.to_f
end
prm_raise(sql.is_initialized, @sizing_run_dir, 'Model is missing SQL file. It is likely caused by: 1. unsuccessful simulation, 2. SQL is not set as one of the output file.')
sql = sql.get
query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
val = sql.execAndReturnFirstDouble(query)
prm_raise(val.is_initialized, @sizing_run_dir, "No hourly return air flow data reported for node #{node_name}")
report_data_dict_index = val.get
query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{report_data_dict_index}'"
val = sql.execAndReturnFirstDouble(query)
prm_raise(val.is_initialized, @sizing_run_dir, "No hourly return air flow data reported at report index #{report_data_dict_index}")
zone_return_flow_si[zone_name] += OpenStudio::OptionalDouble.new(val.get).to_f
end

# Calc ratio of Air Loop relief to sum of zone return for each air loop
Expand All @@ -2486,24 +2480,23 @@ def get_baseline_system_groups_for_one_building_type(model, hvac_building_type,
# For each air loop, get relief air flow and calculate lab exhaust from the central air handler
# Take from hourly reports created during sizing run
zone_relief_flow_si = {}
model.getAirLoopHVACs.sort.each do |air_loop_hvac|
model.getAirLoopHVACs.each do |air_loop_hvac|
# First get relief air flow from sizing run sql file
relief_node = air_loop_hvac.reliefAirNode.get
relief_node = prm_get_optional_handler(air_loop_hvac, @sizing_run_dir, 'reliefAirNode')
node_name = relief_node.nameString
relief_flow_si = 0
relief_fraction = 0
sql = model.sqlFile
if sql.is_initialized
sql = sql.get
query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
val = sql.execAndReturnFirstDouble(query)
query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{val.get}'"
val = sql.execAndReturnFirstDouble(query)
if val.is_initialized
result = OpenStudio::OptionalDouble.new(val.get)
end
relief_flow_si = result.to_f
end
prm_raise(sql.is_initialized, @sizing_run_dir, 'Model is missing SQL file. It is likely caused by: 1. unsuccessful simulation, 2. SQL is not set as one of the output file.')
sql = sql.get
query = "SELECT ReportDataDictionaryIndex FROM ReportDataDictionary WHERE KeyValue = '#{node_name}' COLLATE NOCASE"
val = sql.execAndReturnFirstDouble(query)
query = "SELECT MAX(Value) FROM ReportData WHERE ReportDataDictionaryIndex = '#{val.get}'"
val = sql.execAndReturnFirstDouble(query)
if val.is_initialized
result = OpenStudio::OptionalDouble.new(val.get)
end
relief_flow_si = result.to_f

# Get total flow of zones on this air loop
total_zone_return_si = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,30 @@ class ASHRAE901PRM < Standard
#
# @return [Double] true if successful, false if not
def space_apply_infiltration_rate(space, tot_infil_m3_per_s, infil_method, infil_coefficients)
# get the climate zone
climate_zone = model_standards_climate_zone(space.model)

# Calculate infiltration rate
case infil_method.to_s
when 'Flow/ExteriorWallArea'
# Spread the total infiltration rate
total_exterior_wall_area = 0
space.model.getSpaces.sort.each do |spc|
space.model.getSpaces.each do |spc|
# Get the space conditioning type
space_cond_type = space_conditioning_category(spc)
total_exterior_wall_area += spc.exteriorWallArea unless space_cond_type == 'Unconditioned'
end
prm_raise(total_exterior_wall_area > 0, @sizing_run_dir, 'Total exterior wall area in the model is 0. Check your model inputs')
adj_infil_flow_ext_wall_area = tot_infil_m3_per_s / total_exterior_wall_area
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Space', "For #{space.name}, adj infil = #{adj_infil_flow_ext_wall_area.round(8)} m^3/s*m^2 of above grade wall area.")
OpenStudio.logFree(OpenStudio::Debug, 'prm.log', "For #{space.name}, adj infil = #{adj_infil_flow_ext_wall_area.round(8)} m^3/s*m^2 of above grade wall area.")
when 'Flow/Area'
# Spread the total infiltration rate
total_floor_area = 0
space.model.getSpaces.sort.each do |spc|
space.model.getSpaces.each do |spc|
# Get the space conditioning type
space_cond_type = space_conditioning_category(spc)
total_floor_area += spc.floorArea unless space_cond_type == 'Unconditioned' || space.exteriorArea == 0
end
prm_raise(total_floor_area > 0, @sizing_run_dir, 'Sum of the floor area in exterior spaces in the model is 0. Check your model inputs')
adj_infil_flow_area = tot_infil_m3_per_s / total_floor_area
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Space', "For #{space.name}, adj infil = #{adj_infil_flow_area.round(8)} m^3/s*m^2 of space floor area.")
OpenStudio.logFree(OpenStudio::Debug, 'prm.log', "For #{space.name}, adj infil = #{adj_infil_flow_area.round(8)} m^3/s*m^2 of space floor area.")
end

# Get any infiltration schedule already assigned to this space or its space type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ def load_standards_database(data_directories = [])

# Determine the base infiltration rate at 75 PA.
#
# @param space [OpenStudio::Model::Space] space object
# @return [Double] the baseline infiltration rate, in cfm/ft^2
# defaults to no infiltration.
def space_infiltration_rate_75_pa
def space_infiltration_rate_75_pa(space=nil)
basic_infil_rate_cfm_per_ft2 = 1.0
return basic_infil_rate_cfm_per_ft2
end
Expand Down
Loading