class NECB2011 attr_accessor :qaqc_data def get_sql_table_to_json(model, report_name, report_for_string, table_name) table = [] query_row_names = " SELECT DISTINCT RowName FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}'" row_names = model.sqlFile.get.execAndReturnVectorOfString(query_row_names).get # get Columns query_col_names = " SELECT DISTINCT ColumnName FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}'" col_names = model.sqlFile.get.execAndReturnVectorOfString(query_col_names).get # get units query_unit_names = " SELECT DISTINCT Units FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}'" unit_names = model.sqlFile.get.execAndReturnVectorOfString(query_unit_names).get row_names.each do |row| next if row.nil? || row == '' row_hash = {} row_hash[:name] = row col_names.each do |col| unit_names.each do |unit| query = " SELECT Value FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}' AND RowName='#{row}' AND ColumnName='#{col}' AND Units='#{unit}' " column_name = col.to_s.gsub(/\s+/, '_').downcase # If the column name is "additional_fuel" and the file contains a boiler with a FuelOilNo2 fuel type assume # the column name should be "fueloilno2". if column_name.include? 'additional_fuel' model.getPlantLoops.sort.each do |iplantloop| boilers = iplantloop.components.select { |icomponent| icomponent.to_BoilerHotWater.is_initialized } column_name = 'fueloilno2' unless boilers.select { |boiler| boiler.to_BoilerHotWater.get.fuelType.to_s == 'FuelOilNo2' }.empty? end end column_name += "_#{unit}" if unit != '' value = model.sqlFile.get.execAndReturnFirstString(query) next if value.empty? || value.get.nil? value = value.get.strip # check is value is a number if (begin Float(value) rescue StandardError false end) && value.to_f != 0 row_hash[column_name] = value.to_f # Check if value is a date elsif unit == '' && value =~ /\d\d-\D\D\D-\d\d:\d\d/ row_hash[column_name] = DateTime.parse(value) # skip if value in an empty string or a zero value elsif value != '' && value != '0.00' row_hash[column_name] = value end end end if row_hash.size > 1 table << row_hash end end result = { report_name: report_name, report_for_string: report_for_string, table_name: table_name, table: table } return result end def merge_recursively(a, b) a.merge(b) { |key, a_item, b_item| merge_recursively(a_item, b_item) } end def get_sql_tables_to_json(model) sql_data = [] sql_data << get_sql_table_to_json(model, 'AnnualBuildingUtilityPerformanceSummary', 'Entire Facility', 'End Uses') sql_data << get_sql_table_to_json(model, 'AnnualBuildingUtilityPerformanceSummary', 'Entire Facility', 'Site and Source Energy') # sql_data << get_sql_table_to_json(model, "AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "On-Site Thermal Sources") # sql_data << get_sql_table_to_json(model, "AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "Comfort and Setpoint Not Met Summary") # sql_data << get_sql_table_to_json(model, "InputVerificationandResultsSummary", "Entire Facility", "Window-Wall Ratio") # sql_data << get_sql_table_to_json(model, "InputVerificationandResultsSummary", "Entire Facility", "Conditioned Window-Wall Ratio") # sql_data << get_sql_table_to_json(model, "InputVerificationandResultsSummary", "Entire Facility", "Skylight-Roof Ratio") # sql_data << get_sql_table_to_json(model, "DemandEndUseComponentsSummary", "Entire Facility", "End Uses") # sql_data << get_sql_table_to_json(model, "ComponentSizingSummary", "Entire Facility", "AirLoopHVAC") return sql_data end def load_qaqc_database_new # Combine the data from the JSON files into a single hash files = Dir.glob("#{File.dirname(__FILE__)}/qaqc_data/*.json").select { |e| File.file? e } @qaqc_data = {} @qaqc_data['tables'] = {} files.each do |file| # puts "loading qaqc data from #{file}" data = JSON.parse(File.read(file)) if !data['tables'].nil? @qaqc_data['tables'] = [*@qaqc_data['tables'], *data['tables']].to_h else @qaqc_data[data.keys.first] = data[data.keys.first] end end # Write test report file. test_result_file = File.join(File.dirname(__FILE__), '..', 'NECB2011_QAQC.json') File.open(test_result_file, 'w') { |f| f.write(JSON.pretty_generate(@qaqc_data)) } return @qaqc_data end def get_qaqc_table(table_name:, search_criteria: nil) return_objects = nil table = @qaqc_data['tables'][table_name] raise("could not find #{table_name} in qaqc table database. ") if table.nil? return table if search_criteria.nil? # removed table beause need to use the object['refs'] rows = table['table'] search_criteria.each do |key, value| rows = rows.select { |row| row[key] == value } end return rows end # generates full qaqc.json def init_qaqc(model) # load the qaqc.json files # This is currently disabled as most tests are now done using regression and unit tests.. but we may bring this back. # @qaqc_data = self.load_qaqc_database_new() # generate base qaqc hash qaqc = create_base_data(model) # performs the qaqc on the given base qaqc hash # necb_qaqc(qaqc, model) return qaqc end # generates only qaqc component def qaqc_only(model) # load the qaqc.json files @qaqc_data = load_qaqc_database_new # generate base qaqc hash qaqc = create_base_data(model) # performs the qaqc on the given base qaqc hash. # using `qaqc.clone` as an argument to pass in a shallow copy, so that the argument passed can stay unmodified. necb_qaqc_with_base = necb_qaqc(qaqc.clone, model) # subract base data from qaqc return (necb_qaqc_with_base.to_a - qaqc.to_a).to_h end # Generates the base data hash mainly used to perform qaqc. def create_base_data(model) # construct command with local libs os_version = OpenStudio.openStudioLongVersion eplus_version = OpenStudio.energyPlusVersion puts "\n\n\nOS_version is [#{os_version.strip}]" puts "\n\n\nEP_version is [#{eplus_version.strip}]" # Ensure all surfaces are unique. surfaces = model.getSurfaces.sort # Sort surfaces by type interior_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Surface', 'Adiabatic']) interior_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(interior_surfaces, 'Floor') outdoor_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, 'Outdoors') outdoor_walls = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'Wall') outdoor_roofs = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'RoofCeiling') outdoor_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'Floor') outdoor_subsurfaces = BTAP::Geometry::Surfaces.get_subsurfaces_from_surfaces(outdoor_surfaces) ground_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Ground', 'Foundation']) ground_walls = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'Wall') ground_roofs = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'RoofCeiling') ground_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'Floor') windows = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['FixedWindow', 'OperableWindow']) skylights = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['Skylight', 'TubularDaylightDiffuser', 'TubularDaylightDome']) doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['Door', 'GlassDoor']) overhead_doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['OverheadDoor']) # Peaks electric_peak = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters'" \ " AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Electricity' AND RowName='Electricity:Facility'" \ " AND ColumnName='Electricity Maximum Value' AND Units='W'") natural_gas_peak = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters'" \ " AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Natural Gas' AND RowName='NaturalGas:Facility'" \ " AND ColumnName='Natural Gas Maximum Value' AND Units='W'") get_sql_tables_to_json(model) # Create hash to store all the collected data. qaqc = {} qaqc[:sql_data] = get_sql_tables_to_json(model) error_warning = [] qaqc[:os_standards_revision] = OpenstudioStandards.git_revision qaqc[:os_standards_version] = OpenstudioStandards::VERSION qaqc[:openstudio_version] = os_version.strip qaqc[:energyplus_version] = eplus_version.strip # Store Building data. qaqc[:building] = {} qaqc[:building][:name] = model.building.get.name.get qaqc[:building][:conditioned_floor_area_m2] = nil if model.building.get.conditionedFloorArea.empty? error_warning << "model.building.get.conditionedFloorArea() is empty for #{model.building.get.name.get}" else qaqc[:building][:conditioned_floor_area_m2] = model.building.get.conditionedFloorArea.get end qaqc[:building][:exterior_area_m2] = model.building.get.exteriorSurfaceArea # m2 qaqc[:building][:volume] = model.building.get.airVolume # m3 qaqc[:building][:number_of_stories] = model.getBuildingStorys.size qaqc[:building][:standards_number_of_stories] = nil qaqc[:building][:standards_number_of_stories] = model.building.get.standardsNumberOfStories.get unless model.building.get.standardsNumberOfStories.empty? qaqc[:building][:standards_number_of_above_ground_stories] = nil qaqc[:building][:standards_number_of_above_ground_stories] = model.building.get.standardsNumberOfAboveGroundStories.get unless model.building.get.standardsNumberOfAboveGroundStories.empty? qaqc[:building][:standards_number_of_living_units] = nil qaqc[:building][:standards_number_of_living_units] = model.building.get.standardsNumberOfLivingUnits.get unless model.building.get.standardsNumberOfLivingUnits.empty? qaqc[:building][:nominal_floor_to_ceiling_height] = nil qaqc[:building][:nominal_floor_to_ceiling_height] = model.building.get.nominalFloortoCeilingHeight.get unless model.building.get.nominalFloortoCeilingHeight.empty? qaqc[:building][:nominal_floor_to_floor_height] = nil qaqc[:building][:nominal_floor_to_floor_height] = model.building.get.nominalFloortoFloorHeight.get unless model.building.get.nominalFloortoFloorHeight.empty? # Store Geography Data qaqc[:geography] = {} qaqc[:geography][:hdd] = get_necb_hdd18(model) qaqc[:geography][:cdd] = BTAP::Environment::WeatherFile.new(model.getWeatherFile.path.get.to_s).cdd18 qaqc[:geography][:climate_zone] = NECB2011.new.get_climate_zone_name(qaqc[:geography][:hdd]) qaqc[:geography][:city] = model.getWeatherFile.city qaqc[:geography][:state_province_region] = model.getWeatherFile.stateProvinceRegion qaqc[:geography][:country] = model.getWeatherFile.country qaqc[:geography][:latitude] = model.getWeatherFile.latitude qaqc[:geography][:longitude] = model.getWeatherFile.longitude # Spacetype Breakdown qaqc[:spacetype_area_breakdown] = {} model.getSpaceTypes.sort.each do |spaceType| next if spaceType.floorArea == 0 # data for space type breakdown display = spaceType.name.get floor_area_si = 0 # loop through spaces so I can skip if not included in floor area spaceType.spaces.sort.each do |space| next if !space.partofTotalFloorArea floor_area_si += space.floorArea * space.multiplier end qaqc[:spacetype_area_breakdown][spaceType.name.get.gsub(/\s+/, '_').downcase.to_sym] = floor_area_si end # Economics Section qaqc[:economics] = {} provinces_names_map = { 'QC' => 'Quebec', 'NL' => 'Newfoundland and Labrador', 'NS' => 'Nova Scotia', 'PE' => 'Prince Edward Island', 'ON' => 'Ontario', 'MB' => 'Manitoba', 'SK' => 'Saskatchewan', 'AB' => 'Alberta', 'BC' => 'British Columbia', 'YT' => 'Yukon', 'NT' => 'Northwest Territories', 'NB' => 'New Brunswick', 'NU' => 'Nunavut' } neb_prices_csv_file_name = "#{File.dirname(__FILE__)}/qaqc_resources/neb_end_use_prices.csv" puts neb_prices_csv_file_name building_type = 'Commercial' province = provinces_names_map[qaqc[:geography][:state_province_region]] neb_fuel_list = ['Electricity', 'Natural Gas', 'Oil'] neb_eplus_fuel_map = { 'Electricity' => 'Electricity', 'Natural Gas' => 'Gas', 'Oil' => 'FuelOilNo2' } qaqc[:economics][:total_neb_cost] = 0.0 qaqc[:economics][:total_neb_cost_per_m2] = 0.0 neb_eplus_fuel_map.each do |neb_fuel, ep_fuel| search_info = { 0 => building_type, 1 => province, 2 => neb_fuel } row = look_up_csv_data(neb_prices_csv_file_name, search_info) neb_fuel_cost = row['2020'] fuel_consumption_gj = 0.0 if neb_fuel == 'Electricity' || neb_fuel == 'Natural Gas' if model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - #{ep_fuel}' AND RowName='#{ep_fuel}:Facility' AND ColumnName='#{ep_fuel} Annual Value' AND Units='GJ'").is_initialized fuel_consumption_gj = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - #{ep_fuel}' AND RowName='#{ep_fuel}:Facility' AND ColumnName='#{ep_fuel} Annual Value' AND Units='GJ'").get end else if model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Other' AND RowName='#{ep_fuel}:Facility' AND ColumnName='Annual Value' AND Units='GJ'").is_initialized fuel_consumption_gj = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Other' AND RowName='#{ep_fuel}:Facility' AND ColumnName='Annual Value' AND Units='GJ'").get end end qaqc[:economics][:"#{neb_fuel}_neb_cost"] = fuel_consumption_gj * neb_fuel_cost.to_f qaqc[:economics][:"#{neb_fuel}_neb_cost_per_m2"] = qaqc[:economics][:"#{neb_fuel}_neb_cost"] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? qaqc[:economics][:total_neb_cost] += qaqc[:economics][:"#{neb_fuel}_neb_cost"] qaqc[:economics][:total_neb_cost_per_m2] += qaqc[:economics][:"#{neb_fuel}_neb_cost_per_m2"] || 0.0 end # Fuel cost based local utility rates costing_rownames = model.sqlFile.get.execAndReturnVectorOfString("SELECT RowName FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost'") #==> ["Electricity", "Natural Gas", "Additional", "Total"] costing_rownames = validate_optional(costing_rownames, model, 'N/A') if costing_rownames != 'N/A' costing_rownames.each do |rowname| case rowname when 'Electricity' qaqc[:economics][:electricity_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:electricity_cost_per_m2] = qaqc[:economics][:electricity_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? when 'Natural Gas' qaqc[:economics][:natural_gas_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:natural_gas_cost_per_m2] = qaqc[:economics][:natural_gas_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? when 'Additional' qaqc[:economics][:additional_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:additional_cost_per_m2] = qaqc[:economics][:additional_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? when 'Total' qaqc[:economics][:total_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:total_cost_per_m2] = qaqc[:economics][:total_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? end end else error_warning << "costing is unavailable because the sql statement is nil RowName FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost'" end # Store end_use data end_uses = [ 'Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps', 'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators', 'Total End Uses' ] fuels = [ ['Electricity', 'GJ'], ['Natural Gas', 'GJ'], ['Additional Fuel', 'GJ'], ['District Cooling', 'GJ'], ['District Heating', 'GJ'] ] qaqc[:end_uses] = {} qaqc[:end_uses_eui] = {} end_uses.each do |use_type| qaqc[:end_uses]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj"] = 0 qaqc[:end_uses_eui]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj_per_m2"] = 0 fuels.each do |fuel_type| value = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND RowName='#{use_type}' AND ColumnName='#{fuel_type[0]}' AND Units='#{fuel_type[1]}'") if value.empty? || (value.get == 0) else qaqc[:end_uses]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj"] += value.get unless qaqc[:building][:conditioned_floor_area_m2].nil? qaqc[:end_uses_eui]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj_per_m2"] += value.get / qaqc[:building][:conditioned_floor_area_m2] end end end value = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND RowName='#{use_type}' AND ColumnName='Water' AND Units='m3'") if value.empty? || (value.get == 0) else qaqc[:end_uses]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_water_m3"] = value.get unless qaqc[:building][:conditioned_floor_area_m2].nil? qaqc[:end_uses_eui]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_water_m3_per_m2"] = value.get / qaqc[:building][:conditioned_floor_area_m2] end end end # Store Peak Data qaqc[:meter_peaks] = {} qaqc[:meter_peaks][:electric_w] = electric_peak.empty? ? 'NA' : electric_peak.get qaqc[:meter_peaks][:natural_gas_w] = natural_gas_peak.empty? ? 'NA' : natural_gas_peak.get # Store unmet hour data qaqc[:unmet_hours] = {} qaqc[:unmet_hours][:cooling] = model.getFacility.hoursCoolingSetpointNotMet.get unless model.getFacility.hoursCoolingSetpointNotMet.empty? qaqc[:unmet_hours][:heating] = model.getFacility.hoursHeatingSetpointNotMet.get unless model.getFacility.hoursHeatingSetpointNotMet.empty? # puts "\n\n\n#{costing_rownames}\n\n\n" # Padmassun's Code -- Tarrif end # Padmassun's Code -- Service Hotwater Heating *start* qaqc[:service_water_heating] = {} qaqc[:service_water_heating][:total_nominal_occupancy] = -1 # qaqc[:service_water_heating][:total_nominal_occupancy]=model.sqlFile().get().execAndReturnVectorOfDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='OutdoorAirSummary' AND ReportForString='Entire Facility' AND TableName='Average Outdoor Air During Occupied Hours' AND ColumnName='Nominal Number of Occupants'").get.inject(0, :+) qaqc[:service_water_heating][:total_nominal_occupancy] = get_total_nominal_capacity(model) qaqc[:service_water_heating][:electricity_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Electricity' AND RowName='Water Systems'") qaqc[:service_water_heating][:electricity_per_year] = validate_optional(qaqc[:service_water_heating][:electricity_per_year], model, -1) qaqc[:service_water_heating][:electricity_per_day] = qaqc[:service_water_heating][:electricity_per_year] / 365.5 qaqc[:service_water_heating][:electricity_per_day_per_occupant] = qaqc[:service_water_heating][:electricity_per_day] / qaqc[:service_water_heating][:total_nominal_occupancy] qaqc[:service_water_heating][:natural_gas_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Natural Gas' AND RowName='Water Systems'") qaqc[:service_water_heating][:natural_gas_per_year] = validate_optional(qaqc[:service_water_heating][:natural_gas_per_year], model, -1) qaqc[:service_water_heating][:additional_fuel_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Additional Fuel' AND RowName='Water Systems'") qaqc[:service_water_heating][:additional_fuel_per_year] = validate_optional(qaqc[:service_water_heating][:additional_fuel_per_year], model, -1) qaqc[:service_water_heating][:water_m3_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Water' AND RowName='Water Systems'") qaqc[:service_water_heating][:water_m3_per_year] = validate_optional(qaqc[:service_water_heating][:water_m3_per_year], model, -1) qaqc[:service_water_heating][:water_m3_per_day] = qaqc[:service_water_heating][:water_m3_per_year] / 365.5 qaqc[:service_water_heating][:water_m3_per_day_per_occupant] = qaqc[:service_water_heating][:water_m3_per_day] / qaqc[:service_water_heating][:total_nominal_occupancy] # puts qaqc[:service_water_heating][:total_nominal_occupancy] # Padmassun's Code -- Service Hotwater Heating *end* # Store Envelope data. qaqc[:envelope] = {} # Get Areas qaqc[:envelope][:outdoor_walls_area_m2] = outdoor_walls.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:outdoor_roofs_area_m2] = outdoor_roofs.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:outdoor_floors_area_m2] = outdoor_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:ground_walls_area_m2] = ground_walls.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:ground_roofs_area_m2] = ground_roofs.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:ground_floors_area_m2] = ground_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:interior_floors_area_m2] = interior_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } # Subsurface areas qaqc[:envelope][:windows_area_m2] = windows.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } qaqc[:envelope][:skylights_area_m2] = skylights.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } qaqc[:envelope][:doors_area_m2] = doors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } qaqc[:envelope][:overhead_doors_area_m2] = overhead_doors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } # Total Building Surface Area. qaqc[:envelope][:total_exterior_area_m2] = qaqc[:envelope][:outdoor_walls_area_m2] + qaqc[:envelope][:outdoor_roofs_area_m2] + qaqc[:envelope][:outdoor_floors_area_m2] + qaqc[:envelope][:ground_walls_area_m2] + qaqc[:envelope][:ground_roofs_area_m2] + qaqc[:envelope][:ground_floors_area_m2] + qaqc[:envelope][:windows_area_m2] + qaqc[:envelope][:skylights_area_m2] + qaqc[:envelope][:doors_area_m2] + qaqc[:envelope][:overhead_doors_area_m2] # Total Building Ground Surface Area. qaqc[:envelope][:total_ground_area_m2] = qaqc[:envelope][:ground_walls_area_m2] + qaqc[:envelope][:ground_roofs_area_m2] + qaqc[:envelope][:ground_floors_area_m2] # Total Building Outdoor Surface Area. qaqc[:envelope][:total_outdoor_area_m2] = qaqc[:envelope][:outdoor_walls_area_m2] + qaqc[:envelope][:outdoor_roofs_area_m2] + qaqc[:envelope][:outdoor_floors_area_m2] + qaqc[:envelope][:windows_area_m2] + qaqc[:envelope][:skylights_area_m2] + qaqc[:envelope][:doors_area_m2] + qaqc[:envelope][:overhead_doors_area_m2] # Average Conductances by surface Type qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(outdoor_walls).round(4) if !outdoor_walls.empty? qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(outdoor_roofs).round(4) if !outdoor_roofs.empty? qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(outdoor_floors).round(4) if !outdoor_floors.empty? qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(ground_walls).round(4) if !ground_walls.empty? qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(ground_roofs).round(4) if !ground_roofs.empty? qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(ground_floors).round(4) if !ground_floors.empty? qaqc[:envelope][:windows_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(windows).round(4) if !windows.empty? qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(skylights).round(4) if !skylights.empty? qaqc[:envelope][:doors_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(doors).round(4) if !doors.empty? qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces.get_weighted_average_surface_conductance(overhead_doors).round(4) if !overhead_doors.empty? # #Average Conductances for building whole weight factors !outdoor_walls.empty? ? o_wall_cond_weight = qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k] * qaqc[:envelope][:outdoor_walls_area_m2] : o_wall_cond_weight = 0 !outdoor_roofs.empty? ? o_roof_cond_weight = qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k] * qaqc[:envelope][:outdoor_roofs_area_m2] : o_roof_cond_weight = 0 !outdoor_floors.empty? ? o_floor_cond_weight = qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k] * qaqc[:envelope][:outdoor_floors_area_m2] : o_floor_cond_weight = 0 !ground_walls.empty? ? g_wall_cond_weight = qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] * qaqc[:envelope][:ground_walls_area_m2] : g_wall_cond_weight = 0 !ground_roofs.empty? ? g_roof_cond_weight = qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] * qaqc[:envelope][:ground_roofs_area_m2] : g_roof_cond_weight = 0 !ground_floors.empty? ? g_floor_cond_weight = qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] * qaqc[:envelope][:ground_floors_area_m2] : g_floor_cond_weight = 0 !windows.empty? ? win_cond_weight = qaqc[:envelope][:windows_average_conductance_w_per_m2_k] * qaqc[:envelope][:windows_area_m2] : win_cond_weight = 0 # doors.size > 0 ? sky_cond_weight = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] * qaqc[:envelope][:skylights_area_m2] : sky_cond_weight = 0 if !doors.empty? && !qaqc[:envelope][:skylights_average_conductance_w_per_m2_k].nil? && !qaqc[:envelope][:skylights_area_m2].nil? sky_cond_weight = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] * qaqc[:envelope][:skylights_area_m2] else sky_cond_weight = 0 end !overhead_doors.empty? ? door_cond_weight = qaqc[:envelope][:doors_average_conductance_w_per_m2_k] * qaqc[:envelope][:doors_area_m2] : door_cond_weight = 0 !overhead_doors.empty? ? overhead_door_cond_weight = qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k] * qaqc[:envelope][:overhead_doors_area_m2] : overhead_door_cond_weight = 0 # Building Average Conductance qaqc[:envelope][:building_outdoor_average_conductance_w_per_m2_k] = ( o_floor_cond_weight + o_roof_cond_weight + o_wall_cond_weight + win_cond_weight + sky_cond_weight + door_cond_weight + overhead_door_cond_weight) / qaqc[:envelope][:total_outdoor_area_m2] # Building Average Ground Conductance qaqc[:envelope][:building_ground_average_conductance_w_per_m2_k] = ( g_floor_cond_weight + g_roof_cond_weight + g_wall_cond_weight) / qaqc[:envelope][:total_ground_area_m2] # Building Average Conductance qaqc[:envelope][:building_average_conductance_w_per_m2_k] = ( (qaqc[:envelope][:building_ground_average_conductance_w_per_m2_k] * qaqc[:envelope][:total_ground_area_m2]) + (qaqc[:envelope][:building_outdoor_average_conductance_w_per_m2_k] * qaqc[:envelope][:total_outdoor_area_m2]) ) / (qaqc[:envelope][:total_ground_area_m2] + qaqc[:envelope][:total_outdoor_area_m2]) qaqc[:envelope][:fdwr] = (BTAP::Geometry.get_fwdr(model) * 100.0).round(1) qaqc[:envelope][:srr] = (BTAP::Geometry.get_srr(model) * 100.0).round(1) qaqc[:envelope][:constructions] = {} qaqc[:envelope][:constructions][:exterior_fenestration] = [] constructions = [] outdoor_subsurfaces.each { |surface| constructions << surface.construction.get } ext_const_base = Hash.new(0) constructions.each { |name| ext_const_base[name] += 1 } # iterate thought each construction and get store data ext_const_base.sort.each do |construction, count| construction_info = {} qaqc[:envelope][:constructions][:exterior_fenestration] << construction_info construction_info[:name] = construction.name.get construction_info[:net_area_m2] = construction.getNetArea.round(2) construction_info[:thermal_conductance_m2_w_per_k] = BTAP::Resources::Envelope::Constructions.get_conductance(construction).round(3) construction_info[:solar_transmittance] = BTAP::Resources::Envelope::Constructions.get_shgc(model, construction).round(3) construction_info[:visible_tranmittance] = BTAP::Resources::Envelope::Constructions.get_tvis(model, construction).round(3) end # Exterior qaqc[:envelope][:constructions][:exterior_opaque] = [] constructions = [] outdoor_surfaces.each { |surface| constructions << surface.construction.get } ext_const_base = Hash.new(0) constructions.each { |name| ext_const_base[name] += 1 } # iterate thought each construction and get store data ext_const_base.sort.each do |construction, count| construction_info = {} qaqc[:envelope][:constructions][:exterior_opaque] << construction_info construction_info[:name] = construction.name.get construction_info[:net_area_m2] = construction.getNetArea.round(2) construction_info[:thermal_conductance_m2_w_per_k] = construction.thermalConductance.get.round(3) if construction.thermalConductance.is_initialized construction_info[:net_area_m2] = construction.to_Construction.get.getNetArea.round(2) construction_info[:solar_absorptance] = construction.to_Construction.get.layers[0].exteriorVisibleAbsorptance.get end # Ground qaqc[:envelope][:constructions][:ground] = [] constructions = [] ground_surfaces.each { |surface| constructions << surface.construction.get } ext_const_base = Hash.new(0) constructions.each { |name| ext_const_base[name] += 1 } # iterate thought each construction and get store data ext_const_base.sort.each do |construction, count| construction_info = {} qaqc[:envelope][:constructions][:ground] << construction_info construction_info[:name] = construction.name.get construction_info[:net_area_m2] = construction.getNetArea.round(2) construction_info[:thermal_conductance_m2_w_per_k] = construction.thermalConductance.get.round(3) if construction.thermalConductance.is_initialized construction_info[:net_area_m2] = construction.to_Construction.get.getNetArea.round(2) construction_info[:solar_absorptance] = construction.to_Construction.get.layers[0].exteriorVisibleAbsorptance.get end qaqc[:envelope][:average_thermal_conductance_m2_w_per_k] = # Store Space data. qaqc[:spaces] = [] model.getSpaces.sort.each do |space| spaceinfo = {} qaqc[:spaces] << spaceinfo spaceinfo[:name] = space.name.get # name should be defined test spaceinfo[:multiplier] = space.multiplier spaceinfo[:volume] = space.volume # should be greater than zero spaceinfo[:exterior_wall_area] = space.exteriorWallArea # just for information. spaceinfo[:space_type_name] = space.spaceType.get.name.get unless space.spaceType.empty? # should have a space types name defined. spaceinfo[:thermal_zone] = space.thermalZone.get.name.get unless space.thermalZone.empty? # should be assigned a thermalzone name. # puts space.name.get # puts space.thermalZone.empty? spaceinfo[:breathing_zone_outdoor_airflow_vbz] = -1 breathing_zone_outdoor_airflow_vbz = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='Standard62.1Summary' AND ReportForString='Entire Facility' AND TableName='Zone Ventilation Parameters' AND ColumnName='Breathing Zone Outdoor Airflow - Vbz' AND Units='m3/s' AND RowName='#{spaceinfo[:thermal_zone].to_s.upcase}' ") spaceinfo[:breathing_zone_outdoor_airflow_vbz] = breathing_zone_outdoor_airflow_vbz.get unless breathing_zone_outdoor_airflow_vbz.empty? spaceinfo[:infiltration_method] = 'N/A' spaceinfo[:infiltration_flow_per_m2] = -1.0 if space.spaceInfiltrationDesignFlowRates[0].nil? error_warning << "space.spaceInfiltrationDesignFlowRates[0] is empty for #{spaceinfo[:name]}" error_warning << "space.spaceInfiltrationDesignFlowRates[0].designFlowRateCalculationMethod is empty for #{spaceinfo[:name]}" error_warning << "space.spaceInfiltrationDesignFlowRates[0].flowperExteriorSurfaceArea is empty for #{spaceinfo[:name]}" else spaceinfo[:infiltration_method] = space.spaceInfiltrationDesignFlowRates[0].designFlowRateCalculationMethod spaceinfo[:infiltration_flow_per_m2] = 'N/A' spaceinfo[:infiltration_flow_per_m2] = space.spaceInfiltrationDesignFlowRates[0].flowperExteriorSurfaceArea.get.round(5) unless space.spaceInfiltrationDesignFlowRates[0].flowperExteriorSurfaceArea.empty? end # the following should have values unless the spacetype is "undefined" other they should be set to the correct NECB values. if space.spaceType.empty? error_warning << "space.spaceType is empty for #{space.name.get}" else spaceinfo[:occupancy_schedule] = nil if space.spaceType.get.defaultScheduleSet.empty? error_warning << "space.spaceType.get.defaultScheduleSet is empty for #{space.name.get}" else if space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule.empty? error_warning << "space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule is empty for #{space.name.get}" else spaceinfo[:occupancy_schedule] = space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule.get.name.get # should not empty. end end spaceinfo[:occ_per_m2] = space.spaceType.get.people[0].peopleDefinition.peopleperSpaceFloorArea.get.round(3) unless space.spaceType.get.people[0].nil? || space.spaceType.get.people[0].peopleDefinition.peopleperSpaceFloorArea.empty? if space.spaceType.get.lights[0].nil? error_warning << "space.spaceType.get.lights[0] is nil for Space:[#{space.name.get}] Space Type:[#{spaceinfo[:space_type_name]}]" else spaceinfo[:lighting_w_per_m2] = space.spaceType.get.lights[0].lightsDefinition.wattsperSpaceFloorArea # .get.round(3) unless space.spaceType.get.lights[0].nil? spaceinfo[:lighting_w_per_m2] = validate_optional(spaceinfo[:lighting_w_per_m2], model, -1.0) unless spaceinfo[:lighting_w_per_m2].nil? spaceinfo[:lighting_w_per_m2] = spaceinfo[:lighting_w_per_m2].round(3) end end # spaceinfo[:electric_w_per_m2] = space.spaceType.get.electricEquipment[0].electricEquipmentDefinition.wattsperSpaceFloorArea.get.round(3) unless space.spaceType.get.electricEquipment[0].nil? unless space.spaceType.get.electricEquipment[0].nil? unless space.spaceType.get.electricEquipment[0].electricEquipmentDefinition.wattsperSpaceFloorArea.empty? spaceinfo[:electric_w_per_m2] = space.spaceType.get.electricEquipment[0].electricEquipmentDefinition.wattsperSpaceFloorArea.get.round(3) end end spaceinfo[:shw_m3_per_s] = space.waterUseEquipment[0].waterUseEquipmentDefinition.peakFlowRate.round(3) unless space.waterUseEquipment[0].nil? spaceinfo[:waterUseEquipment] = [] if !space.waterUseEquipment.empty? waterUseEquipment_info = {} spaceinfo[:waterUseEquipment] << waterUseEquipment_info waterUseEquipment_info[:peak_flow_rate] = space.waterUseEquipment[0].waterUseEquipmentDefinition.peakFlowRate waterUseEquipment_info[:peak_flow_rate_per_area] = waterUseEquipment_info[:peak_flow_rate] / space.floorArea area_per_occ = space.spaceType.get.people[0].nil? ? 0.0 : validate_optional(space.spaceType.get.people[0].spaceFloorAreaPerPerson, model, -1.0) # Watt per person = m3/s/m3 * 1000W/kW * (specific heat * dT) * m2/person waterUseEquipment_info[:shw_watts_per_person] = waterUseEquipment_info[:peak_flow_rate_per_area] * 1000 * (4.19 * 44.4) * 1000 * area_per_occ # puts waterUseEquipment_info[:shw_watts_per_ponce the erson] # puts "\n\n\n" end end end # Store Thermal zone data qaqc[:thermal_zones] = [] model.getThermalZones.sort.each do |zone| zoneinfo = {} qaqc[:thermal_zones] << zoneinfo zoneinfo[:name] = zone.name.get zoneinfo[:floor_area] = zone.floorArea zoneinfo[:multiplier] = zone.multiplier zoneinfo[:is_conditioned] = 'N/A' if zone.isConditioned.empty? error_warning << "zone.isConditioned is empty for #{zone.name.get}" else zoneinfo[:is_conditioned] = zone.isConditioned.get end zoneinfo[:is_ideal_air_loads] = zone.useIdealAirLoads zoneinfo[:heating_sizing_factor] = -1.0 if zone.sizingZone.zoneHeatingSizingFactor.empty? error_warning << "zone.sizingZone.zoneHeatingSizingFactor is empty for #{zone.name.get}" else zoneinfo[:heating_sizing_factor] = zone.sizingZone.zoneHeatingSizingFactor.get end zoneinfo[:cooling_sizing_factor] = -1.0 # zone.sizingZone.zoneCoolingSizingFactor.get if zone.sizingZone.zoneCoolingSizingFactor.empty? error_warning << "zone.sizingZone.zoneCoolingSizingFactor is empty for #{zone.name.get}" else zoneinfo[:cooling_sizing_factor] = zone.sizingZone.zoneCoolingSizingFactor.get end zoneinfo[:zone_heating_design_supply_air_temperature] = zone.sizingZone.zoneHeatingDesignSupplyAirTemperature zoneinfo[:zone_cooling_design_supply_air_temperature] = zone.sizingZone.zoneCoolingDesignSupplyAirTemperature zoneinfo[:spaces] = [] zone.spaces.sort.each do |space| spaceinfo = {} zoneinfo[:spaces] << spaceinfo spaceinfo[:name] = space.name.get spaceinfo[:type] = space.spaceType.get.name.get unless space.spaceType.empty? end zoneinfo[:equipment] = [] zone.equipmentInHeatingOrder.each do |equipment| item = {} zoneinfo[:equipment] << item item[:name] = equipment.name.get if equipment.to_ZoneHVACComponent.is_initialized item[:type] = 'ZoneHVACComponent' elsif equipment.to_StraightComponent.is_initialized item[:type] = 'StraightComponent' end end # zone end # Store Air Loop Information qaqc[:air_loops] = [] model.getAirLoopHVACs.sort.each do |air_loop| air_loop_info = {} air_loop_info[:name] = air_loop.name.get air_loop_info[:thermal_zones] = [] air_loop_info[:total_floor_area_served] = 0.0 air_loop_info[:total_breathing_zone_outdoor_airflow_vbz] = 0.0 air_loop.thermalZones.sort.each do |zone| air_loop_info[:thermal_zones] << zone.name.get vbz = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='Standard62.1Summary' AND ReportForString='Entire Facility' AND TableName='Zone Ventilation Parameters' AND ColumnName='Breathing Zone Outdoor Airflow - Vbz' AND Units='m3/s' AND RowName='#{zone.name.get.to_s.upcase}' ") vbz = validate_optional(vbz, model, 0) air_loop_info[:total_breathing_zone_outdoor_airflow_vbz] += vbz air_loop_info[:total_floor_area_served] += zone.floorArea * zone.multiplier.to_f end air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='Standard62.1Summary' AND ReportForString='Entire Facility' AND TableName='System Ventilation Parameters' AND ColumnName='Area Outdoor Air Rate - Ra' AND Units='m3/s-m2' AND RowName='#{air_loop_info[:name].to_s.upcase}' ") air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] = validate_optional(air_loop_info[:area_outdoor_air_rate_m3_per_s_m2], model, -1.0) air_loop_info[:outdoor_air_L_per_s] = -1.0 unless air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] == -1.0 air_loop_info[:outdoor_air_L_per_s] = air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] * air_loop_info[:total_floor_area_served] * 1000 end # Fan unless air_loop.supplyFan.empty? air_loop_info[:supply_fan] = {} if air_loop.supplyFan.get.to_FanConstantVolume.is_initialized air_loop_info[:supply_fan][:type] = 'CV' fan = air_loop.supplyFan.get.to_FanConstantVolume.get elsif air_loop.supplyFan.get.to_FanVariableVolume.is_initialized air_loop_info[:supply_fan][:type] = 'VV' fan = air_loop.supplyFan.get.to_FanVariableVolume.get end air_loop_info[:supply_fan][:name] = fan.name.get # puts "\n\n\n\n#{fan.name.get}\n\n\n\n" air_loop_info[:supply_fan][:fan_efficiency] = fan.fanEfficiency air_loop_info[:supply_fan][:motor_efficiency] = fan.motorEfficiency air_loop_info[:supply_fan][:pressure_rise] = fan.pressureRise air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] = -1.0 max_air_flow_info = model.sqlFile.get.execAndReturnVectorOfString("SELECT RowName FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' ") max_air_flow_info = validate_optional(max_air_flow_info, model, 'N/A') if max_air_flow_info == 'N/A' error_warning << "max_air_flow_info is nil because the following sql statement returned nil: RowName FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' " else if max_air_flow_info.include? air_loop_info[:supply_fan][:name].to_s.upcase.to_s air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' AND RowName='#{air_loop_info[:supply_fan][:name].upcase}' ").get air_loop_info[:supply_fan][:rated_electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Rated Electric Power' AND Units='W' AND RowName='#{air_loop_info[:supply_fan][:name].upcase}' ") # Version 3.2.0 has renamed rated electric_power to rated electricity rate if air_loop_info[:supply_fan][:rated_electric_power_w].empty? air_loop_info[:supply_fan][:rated_electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Rated Electricity Rate' AND Units='W' AND RowName='#{air_loop_info[:supply_fan][:name].upcase}' ") end air_loop_info[:supply_fan][:rated_electric_power_w] = air_loop_info[:supply_fan][:rated_electric_power_w].get else error_warning << "#{air_loop_info[:supply_fan][:name]} does not exist in sql file WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s'" end end end # economizer air_loop_info[:economizer] = {} air_loop_info[:economizer][:name] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.name.get unless air_loop.airLoopHVACOutdoorAirSystem.empty? or air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.name.empty? air_loop_info[:economizer][:control_type] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.getEconomizerControlType unless air_loop.airLoopHVACOutdoorAirSystem.empty? # DX cooling coils air_loop_info[:cooling_coils] = {} air_loop_info[:cooling_coils][:dx_single_speed] = [] air_loop_info[:cooling_coils][:dx_two_speed] = [] air_loop_info[:cooling_coils][:coil_cooling_water] = [] # Heating Coil air_loop_info[:heating_coils] = {} air_loop_info[:heating_coils][:coil_heating_gas] = [] air_loop_info[:heating_coils][:coil_heating_electric] = [] air_loop_info[:heating_coils][:coil_heating_water] = [] # Heat Excahnger air_loop_info[:heat_exchanger] = {} air_loop.supplyComponents.each do |supply_comp| if supply_comp.to_CoilHeatingGas.is_initialized coil = {} air_loop_info[:heating_coils][:coil_heating_gas] << coil gas = supply_comp.to_CoilHeatingGas.get coil[:name] = gas.name.get coil[:type] = 'Gas' coil[:efficency] = gas.gasBurnerEfficiency # coil[:nominal_capacity]= gas.nominalCapacity() coil[:nominal_capacity] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Heating Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].to_s.upcase}'") coil[:nominal_capacity] = validate_optional(coil[:nominal_capacity], model, -1.0) end if supply_comp.to_CoilHeatingElectric.is_initialized coil = {} air_loop_info[:heating_coils][:coil_heating_electric] << coil electric = supply_comp.to_CoilHeatingElectric.get coil[:name] = electric.name.get coil[:type] = 'Electric' coil[:nominal_capacity] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Heating Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].to_s.upcase}'") coil[:nominal_capacity] = validate_optional(coil[:nominal_capacity], model, -1.0) end if supply_comp.to_CoilHeatingWater.is_initialized coil = {} air_loop_info[:heating_coils][:coil_heating_water] << coil water = supply_comp.to_CoilHeatingWater.get coil[:name] = water.name.get coil[:type] = 'Water' coil[:nominal_capacity] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Heating Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].to_s.upcase}'") coil[:nominal_capacity] = validate_optional(coil[:nominal_capacity], model, -1.0) end if supply_comp.to_HeatExchangerAirToAirSensibleAndLatent.is_initialized heatExchanger = supply_comp.to_HeatExchangerAirToAirSensibleAndLatent.get air_loop_info[:heat_exchanger][:name] = heatExchanger.name.get end end # I dont think i need to get the type of heating coil from the sql file, because the coils are differentiated by class, and I have hard coded the information # model.sqlFile().get().execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName= 'Heating Coils' AND ColumnName='Type' ").get #padmussen to complete #AND RowName='#{air_loop_info[:heating_coils][:name].upcase}' # Collect all the fans into the the array. air_loop.supplyComponents.each do |supply_comp| if supply_comp.to_CoilCoolingDXSingleSpeed.is_initialized coil = {} air_loop_info[:cooling_coils][:dx_single_speed] << coil single_speed = supply_comp.to_CoilCoolingDXSingleSpeed.get coil[:name] = single_speed.name.get coil[:cop] = single_speed.ratedCOP.to_f coil[:nominal_total_capacity_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_total_capacity_w] = validate_optional(coil[:nominal_total_capacity_w], model, -1.0) end if supply_comp.to_CoilCoolingDXTwoSpeed.is_initialized coil = {} air_loop_info[:cooling_coils][:dx_two_speed] << coil two_speed = supply_comp.to_CoilCoolingDXTwoSpeed.get coil[:name] = two_speed.name.get coil[:cop_low] = two_speed.ratedLowSpeedCOP.to_f coil[:cop_high] = two_speed.ratedHighSpeedCOP.to_f coil[:nominal_total_capacity_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_total_capacity_w] = validate_optional(coil[:nominal_total_capacity_w], model, -1.0) end if supply_comp.to_CoilCoolingWater.is_initialized coil = {} air_loop_info[:cooling_coils][:coil_cooling_water] << coil coil_cooling_water = supply_comp.to_CoilCoolingWater.get coil[:name] = coil_cooling_water.name.get coil[:nominal_total_capacity_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_total_capacity_w] = validate_optional(coil[:nominal_total_capacity_w], model, -1.0) coil[:nominal_sensible_heat_ratio] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Sensible Heat Ratio' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_sensible_heat_ratio] = validate_optional(coil[:nominal_sensible_heat_ratio], model, -1.0) end end qaqc[:air_loops] << air_loop_info end qaqc[:plant_loops] = [] model.getPlantLoops.sort.each do |plant_loop| plant_loop_info = {} qaqc[:plant_loops] << plant_loop_info plant_loop_info[:name] = plant_loop.name.get sizing = plant_loop.sizingPlant plant_loop_info[:design_loop_exit_temperature] = sizing.designLoopExitTemperature plant_loop_info[:loop_design_temperature_difference] = sizing.loopDesignTemperatureDifference # Create Container for plant equipment arrays. plant_loop_info[:pumps] = [] plant_loop_info[:boilers] = [] plant_loop_info[:chiller_electric_eir] = [] plant_loop_info[:cooling_tower_single_speed] = [] plant_loop_info[:water_heater_mixed] = [] plant_loop.supplyComponents.each do |supply_comp| # Collect Constant Speed if supply_comp.to_PumpConstantSpeed.is_initialized pump = supply_comp.to_PumpConstantSpeed.get pump_info = {} plant_loop_info[:pumps] << pump_info pump_info[:name] = pump.name.get pump_info[:type] = 'Pump:ConstantSpeed' pump_info[:head_pa] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Head' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:head_pa] = validate_optional(pump_info[:head_pa], model, -1.0) pump_info[:water_flow_m3_per_s] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Water Flow' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:water_flow_m3_per_s] = validate_optional(pump_info[:water_flow_m3_per_s], model, -1.0) pump_info[:electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Electric Power' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:electric_power_w] = validate_optional(pump_info[:electric_power_w], model, -1.0) pump_info[:motor_efficency] = pump.motorEfficiency end # Collect Variable Speed if supply_comp.to_PumpVariableSpeed.is_initialized pump = supply_comp.to_PumpVariableSpeed.get pump_info = {} plant_loop_info[:pumps] << pump_info pump_info[:name] = pump.name.get pump_info[:type] = 'Pump:VariableSpeed' pump_info[:head_pa] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Head' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:head_pa] = validate_optional(pump_info[:head_pa], model, -1.0) pump_info[:water_flow_m3_per_s] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Water Flow' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:water_flow_m3_per_s] = validate_optional(pump_info[:water_flow_m3_per_s], model, -1.0) pump_info[:electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Electric Power' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:electric_power_w] = validate_optional(pump_info[:electric_power_w], model, -1.0) pump_info[:motor_efficency] = pump.motorEfficiency end # Collect HotWaterBoilers if supply_comp.to_BoilerHotWater.is_initialized boiler = supply_comp.to_BoilerHotWater.get boiler_info = {} plant_loop_info[:boilers] << boiler_info boiler_info[:name] = boiler.name.get boiler_info[:type] = 'Boiler:HotWater' boiler_info[:fueltype] = boiler.fuelType boiler_info[:nominal_capacity] = boiler.nominalCapacity boiler_info[:nominal_capacity] = validate_optional(boiler_info[:nominal_capacity], model, -1.0) end # Collect ChillerElectricEIR if supply_comp.to_ChillerElectricEIR.is_initialized chiller = supply_comp.to_ChillerElectricEIR.get chiller_info = {} plant_loop_info[:chiller_electric_eir] << chiller_info chiller_info[:name] = chiller.name.get chiller_info[:type] = 'Chiller:Electric:EIR' chiller_info[:reference_capacity] = validate_optional(chiller.referenceCapacity, model, -1.0) chiller_info[:reference_leaving_chilled_water_temperature] = chiller.referenceLeavingChilledWaterTemperature end # Collect CoolingTowerSingleSpeed if supply_comp.to_CoolingTowerSingleSpeed.is_initialized coolingTower = supply_comp.to_CoolingTowerSingleSpeed.get coolingTower_info = {} plant_loop_info[:cooling_tower_single_speed] << coolingTower_info coolingTower_info[:name] = coolingTower.name.get coolingTower_info[:type] = 'CoolingTower:SingleSpeed' coolingTower_info[:fan_power_at_design_air_flow_rate] = validate_optional(coolingTower.fanPoweratDesignAirFlowRate, model, -1.0) end # Collect WaterHeaterMixed if supply_comp.to_WaterHeaterMixed.is_initialized waterHeaterMixed = supply_comp.to_WaterHeaterMixed.get waterHeaterMixed_info = {} plant_loop_info[:water_heater_mixed] << waterHeaterMixed_info waterHeaterMixed_info[:name] = waterHeaterMixed.name.get waterHeaterMixed_info[:type] = 'WaterHeater:Mixed' waterHeaterMixed_info[:heater_thermal_efficiency] = waterHeaterMixed.heaterThermalEfficiency.get unless waterHeaterMixed.heaterThermalEfficiency.empty? waterHeaterMixed_info[:heater_fuel_type] = waterHeaterMixed.heaterFuelType end end qaqc[:eplusout_err] = {} warnings = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='0' ") warnings = validate_optional(warnings, model, 'N/A') unless warnings == 'N/A' qaqc[:eplusout_err][:warnings] = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='0' ").get qaqc[:eplusout_err][:fatal] = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='2' ").get qaqc[:eplusout_err][:severe] = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='1' ").get end qaqc[:ruby_warnings] = error_warning end qaqc[:code_metrics] = {} qaqc[:code_metrics]['heating_gj'] = qaqc[:end_uses]['heating_gj'] qaqc[:code_metrics]['cooling_gj'] = qaqc[:end_uses]['cooling_gj'] qaqc[:code_metrics][:ep_conditioned_floor_area_m2] = qaqc[:building][:conditioned_floor_area_m2] qaqc[:code_metrics][:os_conditioned_floor_area_m2] = qaqc[:envelope][:interior_floors_area_m2] + qaqc[:envelope][:outdoor_floors_area_m2] + qaqc[:envelope][:ground_floors_area_m2] # TEDI unless qaqc[:building][:conditioned_floor_area_m2].nil? qaqc[:code_metrics][:building_tedi_gj_per_m2] = (qaqc[:end_uses]['heating_gj'] + qaqc[:end_uses]['cooling_gj'] ) / qaqc[:building][:conditioned_floor_area_m2] # Mech TEDI? qaqc[:code_metrics][:building_medi_gj_per_m2] = (qaqc[:end_uses]['fans_gj'] + qaqc[:end_uses]['pumps_gj'] + qaqc[:end_uses]['heat_rejection_gj'] + qaqc[:end_uses]['humidification_gj'] + qaqc[:end_uses]['heat_recovery_gj'] ) / qaqc[:building][:conditioned_floor_area_m2] end return qaqc end # Checks if a space with a proper schedule is conditioned or not def sanity_check(qaqc) qaqc[:sanity_check] = {} qaqc[:sanity_check][:fail] = [] qaqc[:sanity_check][:pass] = [] # Padmassun's code for isConditioned start qaqc[:thermal_zones].each do |zoneinfo| zoneinfo[:spaces].each do |space| # skip plenums and undefined spaces/zones if zoneinfo[:name].to_s.include? '- undefined -' next end if zoneinfo[:space_type_name].to_s.include? 'Space Function - undefined -' if zoneinfo[:is_conditioned].to_s == 'No' qaqc[:sanity_check][:pass] << "[TEST-PASS][SANITY_CHECK-PASS] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'No' "] and found as #{zoneinfo[:is_conditioned]}" else qaqc[:sanity_check][:fail] << "[ERROR][SANITY_CHECK-FAIL] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'No' "] but found as #{zoneinfo[:is_conditioned]}" end else if zoneinfo[:is_conditioned].to_s == 'Yes' qaqc[:sanity_check][:pass] << "[TEST-PASS][SANITY_CHECK-PASS] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'Yes' "] and found as #{zoneinfo[:is_conditioned]}" elsif zoneinfo[:name] qaqc[:sanity_check][:fail] << "[ERROR][SANITY_CHECK-FAIL] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'Yes' "] but found as #{zoneinfo[:is_conditioned]}" end end end end qaqc[:sanity_check][:fail] = qaqc[:sanity_check][:fail].sort qaqc[:sanity_check][:pass] = qaqc[:sanity_check][:pass].sort # Padmassun's code for isConditioned end end # checks the pump power using pressure, and flowrate def necb_plantloop_sanity(qaqc) necb_section_name = 'SANITY-??' qaqc[:plant_loops].each do |plant_loop_info| pump_head = plant_loop_info[:pumps][0][:head_pa] flow_rate = plant_loop_info[:pumps][0][:water_flow_m3_per_s] * 1000 hp_check = ((flow_rate * 60 * 60) / 1000 * 1000 * 9.81 * pump_head * 0.000101997) / 3600000 puts "\npump_head #{pump_head}" puts "name: #{qaqc[:building][:name]}" puts "name: #{plant_loop_info[:name]}" puts "flow_rate #{flow_rate}" puts "hp_check #{hp_check}\n" pump_power_hp = plant_loop_info[:pumps][0][:electric_power_w] / 1000 * 0.746 percent_diff = (hp_check - pump_power_hp).to_f.abs / hp_check * 100 if percent_diff.nan? qaqc[:ruby_warnings] << "(hp_check - pump_power_hp).to_f.abs/hp_check * 100 for #{plant_loop_info[:name]} is NaN" next end if pump_power_hp < 1.0 qaqc[:warnings] << "necb_plantloop_sanity [SKIP] [PLANT LOOP][#{plant_loop_info[:name]}][:pumps][0][:electric_power_hp] because pump_power_hp: [#{pump_power_hp}] < 1 hp" next end necb_section_test( qaqc, percent_diff, '<=', 20, # diff of 20% necb_section_name, "[PLANT LOOP][#{plant_loop_info[:name]}][:pumps][0][:electric_power_hp] [#{pump_power_hp}]; NECB value [#{hp_check}]; Percent Diff" ) end end # checks space compliance # Re: lighting_per_area, occupancy_per_area, occupancy_schedule, electric_equipment_per_area def necb_space_compliance(qaqc) # #Padmassun's Code Start # csv_file_name ="#{File.dirname(__FILE__)}/necb_2011_spacetype_info.csv" qaqc[:spaces].each do |space| building_type = '' space_type = '' if space[:space_type_name].include? 'Space Function ' space_type = space[:space_type_name].to_s.rpartition('Space Function ')[2].strip building_type = 'Space Function' elsif space[:space_type_name].include? ' WholeBuilding' space_type = space[:space_type_name].to_s.rpartition(' WholeBuilding')[0].strip building_type = 'WholeBuilding' end ['lighting_per_area_w_per_m2', 'occupancy_per_area_people_per_m2', 'occupancy_schedule', 'electric_equipment_per_area_w_per_m2'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'space_compliance', search_criteria: { 'building_type' => building_type, 'space_type' => space_type }).first puts "\n#{qaqc_table}\n" necb_section_name = get_qaqc_table(table_name: 'space_compliance')['refs'][compliance_var] tolerance = get_qaqc_table(table_name: 'space_compliance')['tolerance'][compliance_var] # puts "\ncompliance_var:#{compliance_var}\n\tnecb_section_name:#{necb_section_name}\n\texp Value:#{qaqc_table[compliance_var]}\n" if compliance_var == 'lighting_per_area_w_per_m2' if space[:lighting_w_per_m2].nil? result_value = 0 else result_value = space[:lighting_w_per_m2] * qaqc_table['lpd_ratio'] end elsif compliance_var == 'occupancy_per_area_people_per_m2' result_value = space[:occ_per_m2] elsif compliance_var == 'occupancy_schedule' result_value = space[:occupancy_schedule] elsif compliance_var == 'electric_equipment_per_area_w_per_m2' result_value = space[:electric_w_per_m2] end test_text = "[SPACE][#{space[:name]}]-[TYPE:][#{space_type}]-#{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, '==', qaqc_table[compliance_var], necb_section_name, test_text, tolerance ) end # row = look_up_csv_data(csv_file_name,{2 => space_type, 1 => building_type}) # if row.nil? # #raise ("space type of [#{space_type}] and/or building type of [#{building_type}] was not found in the excel sheet for space: [#{space[:name]}]") # qaqc[:ruby_warnings] << "space type of [#{space_type}] and/or building type of [#{building_type}] was not found in the excel sheet for space: [#{space[:name]}]" # puts "space type of [#{space_type}] and/or building type of [#{building_type}] was not found in the excel sheet for space: [#{space[:name]}]" # else # #correct the data from the csv file to include a multiplier of 0.9 for specific space types. # reduceLPDSpaces = ["Classroom/lecture/training", "Conf./meet./multi-purpose", "Lounge/recreation", # "Washroom-sch-A", "Washroom-sch-B", "Washroom-sch-C", "Washroom-sch-D", "Washroom-sch-E", # "Washroom-sch-F", "Washroom-sch-G", "Washroom-sch-H", "Washroom-sch-I", "Dress./fitt. - performance arts", # "Locker room", "Retail - dressing/fitting","Locker room-sch-A","Locker room-sch-B","Locker room-sch-C", # "Locker room-sch-D","Locker room-sch-E","Locker room-sch-F","Locker room-sch-G","Locker room-sch-H", # "Locker room-sch-I", "Office - open plan - occsens", "Office - enclosed - occsens", "Storage area - occsens", # "Hospital - medical supply - occsens", "Storage area - refrigerated - occsens"] # if reduceLPDSpaces.include?(space_type) # row[3] = row[3]*0.9 # puts "\n============================\nspace_type: #{space_type}\n============================\n" # end # # Start of Space Compliance # necb_section_name = "NECB2011-Section 8.4.3.6" # data = {} # data[:lighting_per_area] = [ row[3],'==',space[:lighting_w_per_m2] , "Table 4.2.1.6" ,1 ] unless space[:lighting_w_per_m2].nil? # data[:occupancy_per_area] = [ row[4],'==',space[:occ_per_m2] , "Table A-8.4.3.3.1" ,3 ] unless space[:occ_per_m2].nil? # data[:occupancy_schedule] = [ row[5],'==',space[:occupancy_schedule], "Table A-8.4.3.3.1" ,nil ] unless space[:occupancy_schedule].nil? # data[:electric_equipment_per_area] = [ row[6],'==',space[:electric_w_per_m2] , "Table A-8.4.3.3.1" ,1 ] unless space[:electric_w_per_m2].nil? # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[0], # value[1], # value[2], # value[3], # "[SPACE][#{space[:name]}]-[TYPE:][#{space_type}]#{key}", # value[4] # ) # end # end#space Compliance end # Padmassun's Code End end # checks envelope compliance # fenestration_to_door_and_window_percentage, skylight_to_roof_percentage def necb_envelope_compliance(qaqc) # Envelope necb_section_name = 'NECB2011-Section 3.2.1.4' # store hdd in short form hdd = qaqc[:geography][:hdd] # calculate fdwr based on hdd. fdwr = 0 if hdd < 4000 fdwr = 0.40 elsif (hdd >= 4000) && (hdd <= 7000) fdwr = (2000 - 0.2 * hdd) / 3000 elsif hdd > 7000 fdwr = 0.20 end # hardset srr to 0.05 srr = 0.05 # create table of expected values and results. data = {} data[:fenestration_to_door_and_window_percentage] = [fdwr * 100, qaqc[:envelope][:fdwr].round(3)] data[:skylight_to_roof_percentage] = [srr * 100, qaqc[:envelope][:srr].round(3)] # perform test. result must be less than or equal to. data.each do |key, value| necb_section_test( qaqc, value[0], '>=', value[1], necb_section_name, "[ENVELOPE]#{key}", 1 # padmassun added tollerance ) end end def necb_infiltration_compliance(qaqc, model) # Infiltration # puts "\n" # puts get_qaqc_table("infiltration_compliance") # puts "\n" # puts "\n" # puts get_qaqc_table("infiltration_compliance", {"var" => ":infiltration_method"} ) # puts "\n" # puts "\n" infiltration_compliance = get_qaqc_table(table_name: 'infiltration_compliance')['table'] necb_section_name = get_qaqc_table(table_name: 'infiltration_compliance')['refs'].join(',') qaqc[:spaces].each do |spaceinfo| model.getSpaces.sort.each do |space| next unless space.name.get == spaceinfo[:name] found = false space.surfaces.each do |surface| next unless surface.outsideBoundaryCondition == 'Outdoors' found = true # peform this infiltration qaqc if and only if the space's surface is in contact with outdoors infiltration_compliance.each do |compliance| # puts "\nspaceinfo[#{compliance['var']}]" eval_string = "spaceinfo[:#{compliance['var']}]" result_value = eval(eval_string) # puts "#{compliance['test_text']}" test_text = "[SPACE][#{spaceinfo[:name]}]-#{compliance['var']}" # puts "result_value: #{result_value}" # puts "test_text: #{test_text}\n" # data[:infiltration_method] = [ "Flow/ExteriorArea", spaceinfo[:infiltration_method] , nil ] # data[:infiltration_flow_per_m2] = [ 0.00025, spaceinfo[:infiltration_flow_per_m2], 5 ] # data.each do |key,value| # puts key necb_section_test( qaqc, result_value, compliance['bool_operator'], compliance['expected_value'], necb_section_name, test_text, compliance['tolerance'] ) end # peform qaqc only once per space break end if !found qaqc[:warnings] << "necb_infiltration_compliance for SPACE:[#{spaceinfo[:name]}] was skipped because it does not contain surfaces with 'Outside' boundary condition." end end end end def necb_exterior_opaque_compliance(qaqc) # puts JSON.pretty_generate @qaqc_data # Exterior Opaque necb_section_name = get_qaqc_table(table_name: 'exterior_opaque_compliance')['refs'].join(',') climate_index = NECB2011.new.get_climate_zone_index(qaqc[:geography][:hdd]) puts "HDD #{qaqc[:geography][:hdd]}" tolerance = 3 # puts "\n\n" # puts "climate_index: #{climate_index}" # puts get_qaqc_table("exterior_opaque_compliance", {"var" => "ext_wall_conductances", "climate_index" => 2}) ['ext_wall_conductances', 'ext_roof_conductances', 'ext_floor_conductances'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'exterior_opaque_compliance', search_criteria: { 'var' => compliance_var, 'climate_index' => climate_index }).first # puts "\n#{qaqc_table}\n" if compliance_var == 'ext_wall_conductances' result_value = qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_roof_conductances' result_value = qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_floor_conductances' result_value = qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k] end test_text = "[ENVELOPE] #{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, qaqc_table['bool_operator'], qaqc_table['expected_value'], necb_section_name, test_text, tolerance ) end # result_value_index = 6 # round_precision = 3 # data = {} # data[:ext_wall_conductances] = [0.315,0.278,0.247,0.210,0.210,0.183,qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k].nil? # data[:ext_roof_conductances] = [0.227,0.183,0.183,0.162,0.162,0.142,qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k].nil? # data[:ext_floor_conductances] = [0.227,0.183,0.183,0.162,0.162,0.142,qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k].nil? # data.each {|key,value| necb_section_test( # qaqc, # value[result_value_index], # '==', # value[climate_index], # necb_section_name, # "[ENVELOPE]#{key}", # round_precision # ) # } end def necb_exterior_fenestration_compliance(qaqc) # Exterior Fenestration necb_section_name = get_qaqc_table(table_name: 'exterior_fenestration_compliance')['refs'].join(',') climate_index = NECB2011.new.get_climate_zone_index(qaqc[:geography][:hdd]) tolerance = 3 # puts "\n\n" # puts "climate_index: #{climate_index}" # puts get_qaqc_table("exterior_fenestration_compliance", {"var" => "ext_window_conductances", "climate_index" => 2}) ['ext_window_conductances', 'ext_door_conductances', 'ext_overhead_door_conductances', 'ext_skylight_conductances'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'exterior_fenestration_compliance', search_criteria: { 'var' => compliance_var, 'climate_index' => climate_index }).first # puts "\n#{qaqc_table}\n" if compliance_var == 'ext_window_conductances' result_value = qaqc[:envelope][:windows_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_door_conductances' result_value = qaqc[:envelope][:doors_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_overhead_door_conductances' result_value = qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_skylight_conductances' result_value = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] end test_text = "[ENVELOPE] #{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, qaqc_table['bool_operator'], qaqc_table['expected_value'], necb_section_name, test_text, tolerance ) end # necb_section_name = "NECB2011-Section 3.2.2.3" # climate_index = BTAP::Compliance::NECB2011::get_climate_zone_index(qaqc[:geography][:hdd]) # result_value_index = 6 # round_precision = 3 # data = {} # data[:ext_window_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:windows_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:windows_average_conductance_w_per_m2_k].nil? # data[:ext_door_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:doors_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:doors_average_conductance_w_per_m2_k].nil? # data[:ext_overhead_door_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k].nil? # data[:ext_skylight_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:skylights_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:skylights_average_conductance_w_per_m2_k].nil? # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[result_value_index].round(round_precision), # '==', # value[climate_index].round(round_precision), # necb_section_name, # "[ENVELOPE]#{key}", # round_precision # ) # end end def necb_exterior_ground_surfaces_compliance(qaqc) # Exterior Ground surfaces necb_section_name = get_qaqc_table(table_name: 'exterior_ground_surfaces_compliance')['refs'].join(',') climate_index = NECB2011.new.get_climate_zone_index(qaqc[:geography][:hdd]) tolerance = 3 # puts "\n\n" # puts "climate_index: #{climate_index}" # puts get_qaqc_table("exterior_ground_surfaces_compliance", {"var" => "ground_wall_conductances", "climate_index" => 2}) ['ground_wall_conductances', 'ground_roof_conductances', 'ground_floor_conductances'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'exterior_ground_surfaces_compliance', search_criteria: { 'var' => compliance_var, 'climate_index' => climate_index }).first # puts "\n#{qaqc_table}\n" if compliance_var == 'ground_wall_conductances' result_value = qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] elsif compliance_var == 'ground_roof_conductances' result_value = qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] elsif compliance_var == 'ground_floor_conductances' result_value = qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] end test_text = "[ENVELOPE] #{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, qaqc_table['bool_operator'], qaqc_table['expected_value'], necb_section_name, test_text, tolerance ) end # necb_section_name = "NECB2011-Section 3.2.3.1" # climate_index = BTAP::Compliance::NECB2011::get_climate_zone_index(qaqc[:geography][:hdd]) # result_value_index = 6 # round_precision = 3 # data = {} # data[:ground_wall_conductances] = [ 0.568,0.379,0.284,0.284,0.284,0.210, qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] ] unless qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k].nil? # data[:ground_roof_conductances] = [ 0.568,0.379,0.284,0.284,0.284,0.210, qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] ] unless qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k].nil? # data[:ground_floor_conductances] = [ 0.757,0.757,0.757,0.757,0.757,0.379, qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] ] unless qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k].nil? # data.each {|key,value| necb_section_test( # qaqc, # value[result_value_index], # '==', # value[climate_index], # necb_section_name, # "[ENVELOPE]#{key}", # round_precision # ) # } end def necb_zone_sizing_compliance(qaqc) # Zone Sizing test necb_section_name = get_qaqc_table(table_name: 'zone_sizing_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'zone_sizing_compliance') tolerance = 3 # necb_section_name = "NECB2011-?" # round_precision = 3 qaqc[:thermal_zones].each do |zoneinfo| # skipping undefined schedules if (qaqc_table['exclude']['exclude_string'].any? { |ex_string| zoneinfo[:name].to_s.include? ex_string }) && !qaqc_table['exclude']['exclude_string'].empty? # if zoneinfo[:name].to_s.include?"- undefined -" puts "#{zoneinfo[:name]} was skipped in necb_zone_sizing_compliance because it contains #{qaqc_table['exclude']['exclude_string'].join(',')}" next end zone_sizing_compliance = qaqc_table['table'] zone_sizing_compliance.each do |compliance| eval_string = "zoneinfo[:#{compliance['var']}]" result_value = eval(eval_string) next if result_value.nil? test_text = "[ZONE][#{zoneinfo[:name]}] #{compliance['var']}" # puts key necb_section_test( qaqc, result_value, compliance['bool_operator'], compliance['expected_value'], necb_section_name, test_text, tolerance ) end # data = {} # data[:heating_sizing_factor] = [1.3 , zoneinfo[:heating_sizing_factor]] # data[:cooling_sizing_factor] = [1.1 ,zoneinfo[:cooling_sizing_factor]] # #data[:heating_design_supply_air_temp] = [43.0, zoneinfo[:zone_heating_design_supply_air_temperature] ] #unless zoneinfo[:zone_heating_design_supply_air_temperature].nil? # #data[:cooling_design_supply_temp] = [13.0, zoneinfo[:zone_cooling_design_supply_air_temperature] ] # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[0], # '==', # value[1], # necb_section_name, # "[ZONE][#{zoneinfo[:name]}] #{key}", # round_precision # ) # end end end def necb_design_supply_temp_compliance(qaqc) necb_section_name = get_qaqc_table(table_name: 'design_supply_temp_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'design_supply_temp_compliance') tolerance = 3 qaqc[:thermal_zones].each do |zoneinfo| # skipping undefined schedules if (qaqc_table['exclude']['exclude_string'].any? { |ex_string| zoneinfo[:name].to_s.include? ex_string }) && !qaqc_table['exclude']['exclude_string'].empty? puts "#{zoneinfo[:name]} was skipped in necb_zone_sizing_compliance because it contains #{qaqc_table['exclude']['exclude_string'].join(',')}" next end design_supply_temp_compliance = qaqc_table['table'] design_supply_temp_compliance.each do |compliance| if compliance['var'] == 'heating_design_supply_air_temp' result_value = zoneinfo[:zone_heating_design_supply_air_temperature] elsif compliance['var'] == 'cooling_design_supply_temp' result_value = zoneinfo[:zone_cooling_design_supply_air_temperature] end next if result_value.nil? test_text = "[ZONE][#{zoneinfo[:name]}] #{compliance['var']}" # puts key necb_section_test( qaqc, result_value, compliance['bool_operator'], compliance['expected_value'], necb_section_name, test_text, tolerance ) end end # Design supply temp test # necb_section_name = "NECB2011-?" # round_precision = 3 # qaqc[:thermal_zones].each do |zoneinfo| # # skipping undefined schedules # if zoneinfo[:name].to_s.include?"- undefined -" # next # end # data = {} # #data[:heating_sizing_factor] = [1.3 , zoneinfo[:heating_sizing_factor]] # #data[:cooling_sizing_factor] = [1.1 ,zoneinfo[:cooling_sizing_factor]] # data[:heating_design_supply_air_temp] = [43.0, zoneinfo[:zone_heating_design_supply_air_temperature] ] #unless zoneinfo[:zone_heating_design_supply_air_temperature].nil? # data[:cooling_design_supply_temp] = [13.0, zoneinfo[:zone_cooling_design_supply_air_temperature] ] # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[0], # '==', # value[1], # necb_section_name, # "[ZONE][#{zoneinfo[:name]}] #{key}", # round_precision # ) # end # end end def necb_economizer_compliance(qaqc) # determine correct economizer usage according to section 5.2.2.7 of NECB2011 necb_section_name = get_qaqc_table(table_name: 'economizer_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'economizer_compliance') # stores the full hash of qaqc for economizer_compliance # necb_section_name = "NECB2011-5.2.2.7" qaqc[:air_loops].each do |air_loop_info| capacity = -1.0 if !air_loop_info[:cooling_coils][:dx_single_speed][0].nil? puts 'capacity = air_loop_info[:cooling_coils][:dx_single_speed][0][:nominal_total_capacity_w]' capacity = air_loop_info[:cooling_coils][:dx_single_speed][0][:nominal_total_capacity_w] elsif !air_loop_info[:cooling_coils][:dx_two_speed][0].nil? puts 'capacity = air_loop_info[:cooling_coils][:dx_two_speed][0][:cop_high]' capacity = air_loop_info[:cooling_coils][:dx_two_speed][0][:cop_high] elsif !air_loop_info[:cooling_coils][:coil_cooling_water][0].nil? puts 'capacity = air_loop_info[:cooling_coils][:coil_cooling_water][0][:nominal_total_capacity_w]' capacity = air_loop_info[:cooling_coils][:coil_cooling_water][0][:nominal_total_capacity_w] end puts capacity if capacity == -1.0 # This should not happen qaqc[:errors] << "[necb_economizer_compliance] air_loop_info[:cooling_coils] for #{air_loop_info[:name]} does not have a capacity " else # check for correct economizer usage # puts "air_loop_info[:supply_fan][:max_air_flow_rate]: #{air_loop_info[:supply_fan][:max_air_flow_rate]}" unless air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 # capacity should be in kW max_air_flow_rate_m3_per_s = air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] necb_section_test( qaqc, eval(qaqc_table['table'][0]['expected_value']), '==', air_loop_info[:economizer][:control_type], necb_section_name, "[AIR LOOP][#{air_loop_info[:name]}][:economizer][:control_type]" ) end end end end def necb_hrv_compliance(qaqc, model) # HRV check hrv_compliance = get_qaqc_table(table_name: 'hrv_compliance')['table'] necb_section_name = get_qaqc_table(table_name: 'hrv_compliance')['refs'].join(',') qaqc[:air_loops].each do |air_loop_info| hrv_compliance.each do |compliance| data = {} # puts "\nspaceinfo[#{compliance['var']}]" result_value = !air_loop_info[:heat_exchanger].empty? # puts "#{compliance['test_text']}" test_text = "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # puts "result_value: #{result_value}" # puts "test_text: #{test_text}\n" # data[:infiltration_method] = [ "Flow/ExteriorArea", spaceinfo[:infiltration_method] , nil ] # data[:infiltration_flow_per_m2] = [ 0.00025, spaceinfo[:infiltration_flow_per_m2], 5 ] # data.each do |key,value| # puts key outdoor_air_L_per_s = air_loop_info[:outdoor_air_L_per_s] db990 = BTAP::Environment::WeatherFile.new(model.getWeatherFile.path.get.to_s).db990 necb_section_test( qaqc, result_value, '==', eval(compliance['expected_value']), necb_section_name, test_text, compliance['tolerance'] ) end end # necb_section_name = "NECB2011-5.2.10.1" # qaqc[:air_loops].each do |air_loop_info| # unless air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 # hrv_calc = 0.00123*air_loop_info[:outdoor_air_L_per_s]*(21-BTAP::Environment::WeatherFile.new( model.getWeatherFile.path.get.to_s ).db990) #=AP46*(21-O$1) # hrv_reqd = hrv_calc > 150 ? true : false # #qaqc[:information] << "[Info][TEST-PASS][#{necb_section_name}]:#{test_text} result value:#{result_value} #{bool_operator} expected value:#{expected_value}" # hrv_present = false # unless air_loop_info[:heat_exchanger].empty? # hrv_present = true # end # necb_section_test( # qaqc, # hrv_reqd, # '==', # hrv_present, # necb_section_name, # "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # ) # else # qaqc['warnings'] << "[hrv_compliance] air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 for [#{air_loop_info[:name]}]" # end # end end # This methos is not used as part of the QAQC process, # because support for MURBS has not been implemented for HRV # in NECB 2011 and 2015 def necb_hrv_compliance_inc_murb(qaqc, model) murb_hrv_compliance = get_qaqc_table('murb_hrv_compliance') hrv_spacetpye_ignore_regex = murb_hrv_compliance['ignored_spacetypes_regex'] hrv_dwelling_unit_spacetpye_regex = murb_hrv_compliance['dwelling_unit_spacetype_regex'] necb_section_name = murb_hrv_compliance['refs'].join(',') model.getAirLoopHVACs.sort.each do |air_loop| air_loop_info = {} qaqc[:air_loops].each do |air_loop_i| next unless air_loop_i[:name] == air_loop.name.get air_loop_info = air_loop_i end zones = air_loop.thermalZones if zones.length == 1 # here the Airloop is serving only one zone # So, next we need to determine if the zone has only # one dwelling unit and no other space types other than stairs, corridor, and lobby zone = zones.first # get the spaces and keep track of the number of spaces and dewlling units num_of_served_spaces = zone.spaces.length contains_dwelling_unit = false if num_of_served_spaces == 0 qaqc[:warnings] << "[necb_murb_hrv_compliance] Thermal Zone [#{zone.name}] does not serve any Spaces" else spaces = zone.spaces() spaces.each do |z_space| spacetype = z_space.spaceType spacetype = validate_optional(spacetype, model, nil) if spacetype.nil? qaqc[:warnings] << "[necb_murb_hrv_compliance] Space [#{z_space.name}] does not have a SpaceType" else # reduce the number of spaces if the space served by the thermal zone is a # stairwell/staircase/lobby/corridor spacetype_name = spacetype.name.to_s ignored_spacetypes_regex = Regexp.new(hrv_spacetpye_ignore_regex, Regexp::IGNORECASE) if ignored_spacetypes_regex =~ spacetype_name num_of_served_spaces -= 1 end # detect is the thermal zone serves a Dwelling Unit dwelling_unit_regex = Regexp.new(hrv_dwelling_unit_spacetpye_regex, Regexp::IGNORECASE) if dwelling_unit_regex =~ spacetype_name contains_dwelling_unit = true end end end if (num_of_served_spaces == 1) && contains_dwelling_unit # here the Thermal zone serves one space that is a dwelling unit # and other space types such as lobby, stairs, or corridors are ignored # So in this case, an HRV is required test_text = "[AIR LOOP][:heat_exchanger] (murb) for [#{air_loop_info[:name]}] is present?" result_value = murb_hrv_compliance['table']['expected_value'] necb_section_test( qaqc, result_value, '==', true, necb_section_name, test_text, nil ) else # Here either the number of served spaces exceed 1 or # does not contain a dwelling unit, So a regular HRV check has to be done for this air loop qaqc[:warnings] << "[necb_murb_hrv_compliance] Regular HRV compliance check for airloop: [#{air_loop.name}], because (it does not serve a single dwelling unit) OR (serves multiple spacetypes)" necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) end end else # here the Airloop does not serve any zones, or it serves more than one zone # So, a regular hrv compliance must be done for this air loop qaqc[:warnings] << "[necb_murb_hrv_compliance] Regular HRV compliance check for airloop: [#{air_loop.name}], because it serves multiple Thermal zones" necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) end end end # This methos is not used as part of the QAQC process, # because support for MURBS has not been implemented for HRV # in NECB 2011 and 2015 # # This method will run the HRV compliance for a single air loop # # @param qaqc [:hash] Hash that contains the base data with qaqc keys # @param model [:OS:Model] OpenStudio Model # @param air_loop_info [:hash] single air_loop object from the qaqc hash def necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) # HRV check hrv_compliance = get_qaqc_table('hrv_compliance')['table'] necb_section_name = get_qaqc_table('hrv_compliance')['refs'].join(',') hrv_compliance.each do |compliance| data = {} # puts "\nspaceinfo[#{compliance['var']}]" result_value = !air_loop_info[:heat_exchanger].empty? # puts "#{compliance['test_text']}" test_text = "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # puts "result_value: #{result_value}" # puts "test_text: #{test_text}\n" # data[:infiltration_method] = [ "Flow/ExteriorArea", spaceinfo[:infiltration_method] , nil ] # data[:infiltration_flow_per_m2] = [ 0.00025, spaceinfo[:infiltration_flow_per_m2], 5 ] # data.each do |key,value| # puts key outdoor_air_L_per_s = air_loop_info[:outdoor_air_L_per_s] db990 = BTAP::Environment::WeatherFile.new(model.getWeatherFile.path.get.to_s).db990 necb_section_test( qaqc, result_value, '==', eval(compliance['expected_value']), necb_section_name, test_text, compliance['tolerance'] ) end # necb_section_name = "NECB2011-5.2.10.1" # qaqc[:air_loops].each do |air_loop_info| # unless air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 # hrv_calc = 0.00123*air_loop_info[:outdoor_air_L_per_s]*(21-BTAP::Environment::WeatherFile.new( model.getWeatherFile.path.get.to_s ).db990) #=AP46*(21-O$1) # hrv_reqd = hrv_calc > 150 ? true : false # #qaqc[:information] << "[Info][TEST-PASS][#{necb_section_name}]:#{test_text} result value:#{result_value} #{bool_operator} expected value:#{expected_value}" # hrv_present = false # unless air_loop_info[:heat_exchanger].empty? # hrv_present = true # end # necb_section_test( # qaqc, # hrv_reqd, # '==', # hrv_present, # necb_section_name, # "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # ) # else # qaqc['warnings'] << "[hrv_compliance] air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 for [#{air_loop_info[:name]}]" # end # end end def necb_vav_fan_power_compliance(qaqc) necb_section_name = get_qaqc_table(table_name: 'vav_fan_power_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'vav_fan_power_compliance') # necb_section_name = "NECB2011-5.2.3.3" qaqc[:air_loops].each do |air_loop_info| # necb_clg_cop = air_loop_info[:cooling_coils][:dx_single_speed][:cop] #*assuming that the cop is defined correctly* if air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s].nil? qaqc[:warnings] << '[vav_fan_power_compliance] air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] is nil' next end max_air_flow_rate_m3_per_s = air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] necb_supply_fan_w = -1 if air_loop_info[:name].include? 'PSZ' necb_supply_fan_w = eval(qaqc_table['formulas']['NECB PSZ fan power (W)']).round(2) elsif air_loop_info[:name].include? 'VAV' necb_supply_fan_w = eval(qaqc_table['formulas']['NECB VAV fan power (W)']).round(2) end if air_loop_info[:supply_fan][:rated_electric_power_w].nil? qaqc[:warnings] << '[vav_fan_power_compliance] air_loop_info[:supply_fan][:rated_electric_power_w] is nil' next end supply_fan_w = (air_loop_info[:supply_fan][:rated_electric_power_w]).round(3) absolute_diff = (necb_supply_fan_w - supply_fan_w).to_f.abs if absolute_diff < 10 # This case should ALWAYS PASS necb_section_test( qaqc, 10, '>=', absolute_diff, necb_section_name, "[AIR LOOP][#{air_loop_info[:name]}][:supply_fan][:rated_electric_power_w] [#{supply_fan_w}] Absolute Difference from NECB value [#{necb_supply_fan_w}]" ) next else # The test should pass if and only if the percent difference is less than 10% percent_diff = ((necb_supply_fan_w - supply_fan_w).to_f.abs / necb_supply_fan_w * 100).round(3) necb_section_test( qaqc, 10, '>=', percent_diff, necb_section_name, "[AIR LOOP][#{air_loop_info[:name]}][:supply_fan][:rated_electric_power_w] [#{supply_fan_w}] Percent Diff from NECB value [#{necb_supply_fan_w}]" ) end end end def necb_qaqc(qaqc, model) puts "\n\nin necb_qaqc 2011 now\n\n" # Now perform basic QA/QC on items for NECB2011 qaqc[:information] = [] qaqc[:warnings] = [] qaqc[:errors] = [] qaqc[:unique_errors] = [] necb_space_compliance(qaqc) necb_envelope_compliance(qaqc) necb_infiltration_compliance(qaqc, model) necb_exterior_opaque_compliance(qaqc) necb_exterior_fenestration_compliance(qaqc) necb_exterior_ground_surfaces_compliance(qaqc) necb_zone_sizing_compliance(qaqc) necb_design_supply_temp_compliance(qaqc) necb_economizer_compliance(qaqc) necb_hrv_compliance(qaqc, model) necb_vav_fan_power_compliance(qaqc) sanity_check(qaqc) necb_plantloop_sanity(qaqc) qaqc[:information] = qaqc[:information].sort qaqc[:warnings] = qaqc[:warnings].sort qaqc[:errors] = qaqc[:errors].sort qaqc[:unique_errors] = qaqc[:unique_errors].sort return qaqc end def necb_section_test(qaqc, result_value, bool_operator, expected_value, necb_section_name, test_text, tolerance = nil) test = 'eval_failed' command = '' if tolerance.is_a?(Integer) command = "#{result_value}.round(#{tolerance}) #{bool_operator} #{expected_value}.round(#{tolerance})" elsif expected_value.is_a?(String) && result_value.is_a?(String) command = "'#{result_value}' #{bool_operator} '#{expected_value}'" else command = "#{result_value} #{bool_operator} #{expected_value}" end test = eval(command) test_res = nil test_res = true if test.to_s.downcase == 'true' test_res = false if test.to_s.downcase == 'false' raise "Eval command failed #{test}" if test_res.nil? if test_res qaqc[:information] << "[Info][TEST-PASS][#{necb_section_name}]:#{test_text} result value:#{result_value} #{bool_operator} expected value:#{expected_value}" else qaqc[:errors] << "[ERROR][TEST-FAIL][#{necb_section_name}]:#{test_text} expected value:#{expected_value} #{bool_operator} result value:#{result_value}" unless (expected_value == -1.0) || (expected_value == 'N/A') qaqc[:unique_errors] << "[ERROR][TEST-FAIL][#{necb_section_name}]:#{test_text} expected value:#{expected_value} #{bool_operator} result value:#{result_value}" end end end def check_boolean_value(value, varname) return true if value =~ /^(true|t|yes|y|1)$/i return false if value.empty? || value =~ /^(false|f|no|n|0)$/i raise ArgumentError, "invalid value for #{varname}: #{value}" end def look_up_csv_data(csv_fname, search_criteria) options = { headers: :first_row, converters: [:numeric] } unless File.exist?(csv_fname) raise "File: [#{csv_fname}] Does not exist" end # we'll save the matches here matches = nil # save a copy of the headers headers = nil CSV.open(csv_fname, 'r', options) do |csv| # Since CSV includes Enumerable we can use 'find_all' # which will return all the elements of the Enumerble for # which the block returns true matches = csv.find_all do |row| match = true search_criteria.keys.each do |key| match &&= (row[key].strip == search_criteria[key].strip) end match end headers = csv.headers end # puts matches raise('More than one match') if matches.size > 1 puts "Zero matches found for [#{search_criteria}]" if matches.empty? # return matches[0] return matches[0] end end