// Some plant components air in a containingHVACComponent() and it is that
// container which needs to go on the plant operation scheme. Here is a filter to 
// figure that out. 
HVACComponent operationSchemeComponent(const HVACComponent & component) {
    boost::optional<HVACComponent> result;

    switch(component.iddObject().type().value())
    {
      case openstudio::IddObjectType::OS_WaterHeater_Mixed :
      {
        auto waterHeater = component.cast<WaterHeaterMixed>();
        if( auto hpwh = waterHeater.containingZoneHVACComponent() ) {
          result = hpwh;
        }
        break;
      }
      case openstudio::IddObjectType::OS_WaterHeater_Stratified :
      {
        auto waterHeater = component.cast<WaterHeaterStratified>();
        if( auto hpwh = waterHeater.containingZoneHVACComponent() ) {
          result = hpwh;
        }
        break;
      }
      default:
      {
        break;
      }
    }

  if( result ) {
    return result.get();
  }

  return component;
}
ComponentType componentType(const HVACComponent & component)
{
  switch(component.iddObject().type().value())
  {
    case openstudio::IddObjectType::OS_Boiler_HotWater :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_WaterHeater_Mixed :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_WaterHeater_Stratified :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_DistrictHeating :
    {
      return ComponentType::HEATING;
    }      
    case openstudio::IddObjectType::OS_Chiller_Electric_EIR :
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_Chiller_Absorption_Indirect :
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_Chiller_Absorption :
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_ThermalStorage_Ice_Detailed :
    {
      return ComponentType::BOTH;
    }
    case openstudio::IddObjectType::OS_DistrictCooling :
    {
      return ComponentType::COOLING;
    }      
    case openstudio::IddObjectType::OS_CoolingTower_SingleSpeed :
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_CoolingTower_VariableSpeed :
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_CoolingTower_TwoSpeed:
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_EvaporativeFluidCooler_SingleSpeed:
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_FluidCooler_SingleSpeed:
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_FluidCooler_TwoSpeed:
    {
      return ComponentType::COOLING;
    }
    case openstudio::IddObjectType::OS_GroundHeatExchanger_Vertical :
    {
      return ComponentType::BOTH;
    }
    case openstudio::IddObjectType::OS_GroundHeatExchanger_HorizontalTrench :
    {
      return ComponentType::BOTH;
    }
    case openstudio::IddObjectType::OS_HeatExchanger_FluidToFluid :
    {
      return ComponentType::BOTH;
    }
    case openstudio::IddObjectType::OS_SolarCollector_FlatPlate_PhotovoltaicThermal :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_SolarCollector_FlatPlate_Water :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_SolarCollector_IntegralCollectorStorage :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_PlantComponent_TemperatureSource :
    {
      return ComponentType::BOTH;
    }
    case openstudio::IddObjectType::OS_HeatPump_WaterToWater_EquationFit_Heating :
    {
      return ComponentType::HEATING;
    }
    case openstudio::IddObjectType::OS_HeatPump_WaterToWater_EquationFit_Cooling :
    {
      return ComponentType::COOLING;
    }
    default:
    {
      return ComponentType::NONE;
    }
  }
}
boost::optional<double> flowrate(const HVACComponent & component)
{
  boost::optional<double> result;
  switch(component.iddObject().type().value()) {
    case openstudio::IddObjectType::OS_Boiler_HotWater :
    {
      auto boiler = component.cast<BoilerHotWater>();
      result = boiler.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_WaterHeater_Mixed :
    {
      auto waterHeater = component.cast<WaterHeaterMixed>();
      result = waterHeater.useSideDesignFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_WaterHeater_Stratified :
    {
      auto waterHeater = component.cast<WaterHeaterStratified>();
      result = waterHeater.useSideDesignFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_DistrictHeating :
    {
      break;
    }      
    case openstudio::IddObjectType::OS_Chiller_Electric_EIR :
    {
      auto chiller = component.cast<ChillerElectricEIR>();
      result = chiller.referenceChilledWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_Chiller_Absorption_Indirect :
    {
      auto chiller = component.cast<ChillerAbsorptionIndirect>();
      result = chiller.designChilledWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_Chiller_Absorption :
    {
      auto chiller = component.cast<ChillerAbsorption>();
      result = chiller.designChilledWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_ThermalStorage_Ice_Detailed :
    {
      break;
    }
    case openstudio::IddObjectType::OS_DistrictCooling :
    {
      break;
    }      
    case openstudio::IddObjectType::OS_CoolingTower_SingleSpeed :
    {
      auto tower = component.cast<CoolingTowerSingleSpeed>();
      result = tower.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_CoolingTower_VariableSpeed :
    {
      auto tower = component.cast<CoolingTowerVariableSpeed>();
      result = tower.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_CoolingTower_TwoSpeed:
    {
      auto tower = component.cast<CoolingTowerTwoSpeed>();
      result = tower.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_EvaporativeFluidCooler_SingleSpeed:
    {
      auto mo = component.cast<EvaporativeFluidCoolerSingleSpeed>();
      result = mo.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_FluidCooler_SingleSpeed:
    {
      auto mo = component.cast<FluidCoolerSingleSpeed>();
      result = mo.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_FluidCooler_TwoSpeed:
    {
      auto mo = component.cast<FluidCoolerTwoSpeed>();
      result = mo.designWaterFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_GroundHeatExchanger_Vertical :
    {
      auto hx = component.cast<GroundHeatExchangerVertical>();
      result = hx.maximumFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_GroundHeatExchanger_HorizontalTrench :
    {
      auto hx = component.cast<GroundHeatExchangerHorizontalTrench>();
      result = hx.designFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_HeatExchanger_FluidToFluid :
    {
      auto hx = component.cast<HeatExchangerFluidToFluid>();
      result = hx.loopSupplySideDesignFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_SolarCollector_FlatPlate_PhotovoltaicThermal :
    {
      auto mo = component.cast<SolarCollectorFlatPlatePhotovoltaicThermal>();
      result = mo.designFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_SolarCollector_FlatPlate_Water :
    {
      auto mo = component.cast<SolarCollectorFlatPlateWater>();
      result = mo.maximumFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_SolarCollector_IntegralCollectorStorage :
    {
      auto mo = component.cast<SolarCollectorIntegralCollectorStorage>();
      result = mo.maximumFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_PlantComponent_TemperatureSource :
    {
      auto mo = component.cast<PlantComponentTemperatureSource>();
      result = mo.designVolumeFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_HeatPump_WaterToWater_EquationFit_Cooling :
    {
      auto mo = component.cast<HeatPumpWaterToWaterEquationFitCooling>();
      result = mo.ratedLoadSideFlowRate();
      break;
    }
    case openstudio::IddObjectType::OS_HeatPump_WaterToWater_EquationFit_Heating :
    {
      auto mo = component.cast<HeatPumpWaterToWaterEquationFitHeating>();
      result = mo.ratedLoadSideFlowRate();
      break;
    }
    default:
    {
      break;
    }
  }

  return result;
}