module BTAP
  def self.perform_qaqc(model)
    surfaces = {}
    outdoor_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(model.getSurfaces(), "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(model.getSurfaces(), "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" ])
    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'")

    # Create hash to store all the collected data. 
    qaqc = {}
    error_warning=[]
    qaqc[:os_standards_revision] = OpenstudioStandards::git_revision
    qaqc[:os_standards_version] = OpenstudioStandards::VERSION
    # 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
    # Store Geography Data
    qaqc[:geography] ={}
    qaqc[:geography][:hdd] = BTAP::Environment::WeatherFile.new( model.getWeatherFile.path.get.to_s ).hdd18
    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] = {}
    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] = {}
	
    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
    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
	

    # 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
        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.get
          #                             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_person]
          #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]=[]

      #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
      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
       
    
    # Perform qaqc
    necb_2011_qaqc(qaqc, model) if qaqc[:building][:name].include?("NECB2011") #had to nodify this because this is specifically for "NECB-2011" standard
    sanity_check(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
end

def validate_optional (var, model, return_value = "N/A")
  if var.empty?
    return return_value
  else
    return var.get
  end
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


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 get_total_nominal_capacity (model)
  total_nominal_capacity = 0
  model.getSpaces.sort.each do |space|
    zone_name = space.thermalZone.get.name.get.upcase
    area = model.sqlFile().get().execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='InputVerificationandResultsSummary' AND ReportForString='Entire Facility' AND TableName='Zone Summary' AND ColumnName='Area' AND RowName='#{zone_name}'")
    area = validate_optional(area, model, -1)
    multiplier = model.sqlFile().get().execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='InputVerificationandResultsSummary' AND ReportForString='Entire Facility' AND TableName='Zone Summary' AND ColumnName='Multipliers' AND RowName='#{zone_name}'")
    multiplier = validate_optional(multiplier, model, -1)
    area_per_person = model.sqlFile().get().execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='InputVerificationandResultsSummary' AND ReportForString='Entire Facility' AND TableName='Zone Summary' AND ColumnName='People' AND RowName='#{zone_name}'")
    area_per_person = validate_optional(area_per_person, model, -1)
    next if area_per_person == 0
    puts "area: #{area}  multiplier: #{multiplier}   area_per_person: #{area_per_person}"
    total_nominal_capacity += area*multiplier/area_per_person
  end
  return total_nominal_capacity
end

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
  
  
def necb_2011_qaqc(qaqc, model)
  #Now perform basic QA/QC on items for NECB2011 
  qaqc[:information] = []
  qaqc[:warnings] =[]
  qaqc[:errors] = []
  qaqc[:unique_errors]=[]
    

  #    #Padmassun's Code Start
  csv_file_name ="#{File.dirname(__FILE__)}/necb_2011_spacetype_info.csv"
  puts csv_file_name
  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
    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
    

  # 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
    )
  }

  #Infiltration
  necb_section_name = "NECB2011-Section 8.4.3.6"
  qaqc[:spaces].each do |spaceinfo|
    data = {}
    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,
        value[0],
        '==',
        value[1],
        necb_section_name,
        "[SPACE][#{spaceinfo[:name]}]-#{key}",
        value[2]
      )
    end
  end
  #Exterior Opaque
  necb_section_name = "NECB2011-Section 3.2.2.2"
  climate_index = BTAP::Compliance::NECB2011::get_climate_zone_index(qaqc[:geography][:hdd])
  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
    )
  }
  #Exterior Fenestration
  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    
  #Exterior Ground surfaces
  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
    )
  }
  #Zone Sizing and design supply temp tests
  necb_section_name = "NECB2011-?"
  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	
  #Air flow sizing check
  #determine correct economizer usage according to section 5.2.2.7 of NECB2011
  necb_section_name = "NECB2011-5.2.2.7"
  qaqc[:air_loops].each do |air_loop_info|
    #    air_loop_info[:name] 
    #    air_loop_info[:thermal_zones] 
    #    air_loop_info[:total_floor_area_served]
    #    air_loop_info[:cooling_coils][:dx_single_speed]
    #    air_loop_info[:cooling_coils][:dx_two_speed]
    #    air_loop_info[:supply_fan][:max_air_flow_rate]
    #    
    #    air_loop_info[:heating_coils][:coil_heating_gas][:nominal_capacity]
    #    air_loop_info[:heating_coils][:coil_heating_electric][:nominal_capacity]
    #    air_loop_info[:heating_coils][:coil_heating_water][:nominal_capacity]
    #    
    #    air_loop_info[:economizer][:control_type]
    
    capacity = -1.0
    
    if !air_loop_info[:cooling_coils][:dx_single_speed][0].nil?
      puts "air_loop_info[:heating_coils][:coil_heating_gas][0][:nominal_capacity]"
      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[:heating_coils][:coil_heating_electric]"
      capacity = air_loop_info[:cooling_coils][:dx_two_speed][0][:cop_high]
    end
    puts capacity
    if capacity == -1.0
      #This should not happen
      puts "air_loop_info[:heating_coils] does not have a capacity or the type is not gas/electric/water for #{air_loop_info[:name]}"
    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
        if capacity > 20000 or air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s]*1000 >1500
          #diff enth
          #puts "diff"
          necb_section_test( 
            qaqc,
            "DifferentialEnthalpy",
            '==',
            air_loop_info[:economizer][:control_type],
            necb_section_name,
            "[AIR LOOP][#{air_loop_info[:name]}][:economizer][:control_type]"
          )
        else
          #no economizer
          #puts "no econ"
          necb_section_test( 
            qaqc,
            'NoEconomizer',
            '==',
            air_loop_info[:economizer][:control_type],
            necb_section_name,
            "[AIR LOOP][#{air_loop_info[:name]}][:economizer][:control_type]"
          )
        end
      end
    end
  end
  
  #*TODO*
  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?"
      )
      
    end
  end
  
  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[:ruby_warnings] << "air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] is nil"
      next
    end
    necb_supply_fan_w = (air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s]*1000*1.6).round(2)

    if air_loop_info[:name].include? "PSZ"
      necb_supply_fan_w = (air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s]*1000*1.6).round(2)
    elsif air_loop_info[:name].include? "VAV"
      necb_supply_fan_w = (air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s]*1000*2.65).round(2)
    end

    if air_loop_info[:supply_fan][:rated_electric_power_w].nil?
      qaqc[:ruby_warnings] << "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
  
  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
    
    necb_section_test( 
      qaqc,
      20, #diff of 20%
      '>=',
      percent_diff,
      necb_section_name,
      "[PLANT LOOP][#{plant_loop_info[:name]}][:pumps][0][:electric_power_hp] [#{pump_power_hp}] Percent Diff from NECB value [#{hp_check}]"
    )
    
  end
end