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}".gsub(/\s+/, "_").downcase column_name = 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 (!!Float(value) rescue false) && 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 not 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? if search_criteria.nil? #return object['table'] return table # removed table beause need to use the object['refs'] else rows = table['table'] search_criteria.each do |key, value| rows = rows.select{ |row| row[key] == value} end return rows end 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 = self.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") 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 - Gas' AND RowName='Gas:Facility'" + " AND ColumnName='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 qaqc[:date] = Time.now # Store Building data. qaqc[:building] = {} qaqc[:building][:name] = model.building.get.name.get qaqc[:building][:conditioned_floor_area_m2]=nil unless model.building.get.conditionedFloorArea().empty? qaqc[:building][:conditioned_floor_area_m2] = model.building.get.conditionedFloorArea().get else error_warning << "model.building.get.conditionedFloorArea() is empty for #{model.building.get.name.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] = BTAP::Compliance::NECB2011::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 not 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' => "FuelOil#2"} 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| row = look_up_csv_data(neb_prices_csv_file_name, {0 => building_type, 1 => province, 2 => neb_fuel}) neb_fuel_cost = row['2018'] 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"] 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") unless 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? or 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? or 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.size > 0 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.size > 0 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.size > 0 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.size > 0 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.size > 0 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.size > 0 qaqc[:envelope][:windows_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces::get_weighted_average_surface_conductance(windows).round(4) if windows.size > 0 qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces::get_weighted_average_surface_conductance(skylights).round(4) if skylights.size > 0 qaqc[:envelope][:doors_average_conductance_w_per_m2_k] = BTAP::Geometry::Surfaces::get_weighted_average_surface_conductance(doors).round(4) if doors.size > 0 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.size > 0 # #Average Conductances for building whole weight factors outdoor_walls.size > 0 ? 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.size > 0 ? 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.size > 0 ? 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.size > 0 ? 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.size > 0 ? 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.size > 0 ? 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.size > 0 ? 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.size > 0 && !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.size > 0 ? door_cond_weight = qaqc[:envelope][:doors_average_conductance_w_per_m2_k] * qaqc[:envelope][:doors_area_m2] : door_cond_weight = 0 overhead_doors.size > 0 ?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 unless space.spaceInfiltrationDesignFlowRates[0].nil? 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? else 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]}" end #the following should have values unless the spacetype is "undefined" other they should be set to the correct NECB values. unless space.spaceType.empty? spaceinfo[:occupancy_schedule] = nil unless (space.spaceType.get.defaultScheduleSet.empty?) unless space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule.empty? spaceinfo[:occupancy_schedule] = space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule.get.name.get #should not empty. else error_warning << "space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule is empty for #{space.name.get }" end else error_warning << "space.spaceType.get.defaultScheduleSet is empty for #{space.name.get }" end spaceinfo[:occ_per_m2] = space.spaceType.get.people[0].peopleDefinition.peopleperSpaceFloorArea.get.round(3) unless space.spaceType.get.people[0].nil? unless space.spaceType.get.lights[0].nil? 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 else error_warning << "space.spaceType.get.lights[0] is nil for Space:[#{space.name.get}] Space Type:[#{spaceinfo[:space_type_name]}]" 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].spaceFloorAreaPerPerson area_per_occ = validate_optional(area_per_occ, 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 else error_warning << "space.spaceType is empty for #{space.name.get }" 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" unless zone.isConditioned.empty? zoneinfo[:is_conditioned] = zone.isConditioned.get else error_warning << "zone.isConditioned is empty for #{zone.name.get}" end zoneinfo[:is_ideal_air_loads] = zone.useIdealAirLoads zoneinfo[:heating_sizing_factor] = -1.0 unless zone.sizingZone.zoneHeatingSizingFactor.empty? zoneinfo[:heating_sizing_factor] = zone.sizingZone.zoneHeatingSizingFactor.get else error_warning << "zone.sizingZone.zoneHeatingSizingFactor is empty for #{zone.name.get}" end zoneinfo[:cooling_sizing_factor] = -1.0 #zone.sizingZone.zoneCoolingSizingFactor.get unless zone.sizingZone.zoneCoolingSizingFactor.empty? zoneinfo[:cooling_sizing_factor] = zone.sizingZone.zoneCoolingSizingFactor.get else error_warning << "zone.sizingZone.zoneCoolingSizingFactor is empty for #{zone.name.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 end #zone # 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 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") unless max_air_flow_info == "N/A" if max_air_flow_info.include? "#{air_loop_info[:supply_fan][:name].to_s.upcase}" 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}' ").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 else 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' " end end #economizer air_loop_info[:economizer] = {} air_loop_info[:economizer][:name] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.name.get air_loop_info[:economizer][:control_type] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.getEconomizerControlType #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.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) 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.get coil[:cop_high] = two_speed.ratedHighSpeedCOP.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) 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 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] 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 {|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" unless space[:lighting_w_per_m2].nil? result_value = space[:lighting_w_per_m2] * qaqc_table['lpd_ratio'] else result_value = 0 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 ) } # 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 and 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 {|key, value| necb_section_test( qaqc, value[0], '>=', value[1], necb_section_name, "[ENVELOPE]#{key}", 1 #padmassun added tollerance ) } 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 {|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 {|compliance| # puts "\nspaceinfo[#{compliance['var']}]" result_value = eval("spaceinfo[:#{compliance['var']}]") # 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"] ) } # peform qaqc only once per space break } 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 = BTAP::Compliance::NECB2011::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 {|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 ) } # 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 = BTAP::Compliance::NECB2011::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 {|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 ) } # 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 = BTAP::Compliance::NECB2011::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 {|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 ) } # 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 {|compliance| result_value = eval("zoneinfo[:#{compliance['var']}]") 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 ) } # 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 {|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 # 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 {|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 # 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.to_s}] does not serve any Spaces" else spaces = zone.spaces() spaces.each {|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.to_s}] 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 } if num_of_served_spaces == 1 and 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.to_s}], 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.to_s}], 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 {|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"] ) } # 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) and 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 == 'true' ? true : false raise ("Eval command failed #{test}") if !!test != test if test 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 or 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.new "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 = 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.size == 0 #return matches[0] return matches[0] end end