boost::optional<IdfObject> ForwardTranslator::translateGeneratorPVWatts(model::GeneratorPVWatts & modelObject)
{
  IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::Generator_PVWatts, modelObject);

  idfObject.setString(Generator_PVWattsFields::PVWattsVersion, modelObject.pvWattsVersion());

  idfObject.setDouble(Generator_PVWattsFields::DCSystemCapacity, modelObject.dcSystemCapacity());

  idfObject.setString(Generator_PVWattsFields::ModuleType, modelObject.moduleType());

  idfObject.setString(Generator_PVWattsFields::ArrayType, modelObject.arrayType());

  idfObject.setDouble(Generator_PVWattsFields::SystemLosses, modelObject.systemLosses());

  boost::optional<PlanarSurface> surface = modelObject.surface();
  if (surface){
    boost::optional<IdfObject> surfaceIdf = translateAndMapModelObject(*surface);
    if (surfaceIdf){
      idfObject.setString(Generator_PVWattsFields::ArrayGeometryType, "Surface");
      idfObject.setString(Generator_PVWattsFields::SurfaceName, surfaceIdf->name().get());
    }
  } else {
    idfObject.setString(Generator_PVWattsFields::ArrayGeometryType, "TiltAzimuth");
    idfObject.setDouble(Generator_PVWattsFields::TiltAngle, modelObject.tiltAngle());
    idfObject.setDouble(Generator_PVWattsFields::AzimuthAngle, modelObject.azimuthAngle());
  }

  idfObject.setDouble(Generator_PVWattsFields::GroundCoverageRatio, modelObject.groundCoverageRatio());

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateFFactorGroundFloorConstruction( FFactorGroundFloorConstruction & modelObject )
{
  IdfObject idfObject( openstudio::IddObjectType::Construction_FfactorGroundFloor);
  m_idfObjects.push_back(idfObject);

  for (LifeCycleCost lifeCycleCost : modelObject.lifeCycleCosts()){
    translateAndMapModelObject(lifeCycleCost);
  }

  idfObject.setString(Construction_FfactorGroundFloorFields::Name, modelObject.name().get());

  OptionalDouble d = modelObject.getDouble(OS_Construction_FfactorGroundFloorFields::FFactor, false);
  if (d){
    idfObject.setDouble(Construction_FfactorGroundFloorFields::FFactor, *d);
  }else{
    LOG(Error, "Missing required input 'F-Factor' for Construction:FfactorGroundFloor named '" << modelObject.name().get() << "'");
  }

  d = modelObject.getDouble(OS_Construction_FfactorGroundFloorFields::Area, false);
  if (d){
    idfObject.setDouble(Construction_FfactorGroundFloorFields::Area, *d);
  }else{
    LOG(Error, "Missing required input 'Area' for Construction:FfactorGroundFloor named '" << modelObject.name().get() << "'");
  }

  d = modelObject.getDouble(OS_Construction_FfactorGroundFloorFields::PerimeterExposed, false);
  if (d){
    idfObject.setDouble(Construction_FfactorGroundFloorFields::PerimeterExposed, *d);
  }else{
    LOG(Error, "Missing required input 'PerimeterExposed' for Construction:FfactorGroundFloor named '" << modelObject.name().get() << "'");
  }

  return boost::optional<IdfObject>(idfObject);
}
boost::optional<IdfObject> ForwardTranslator::translateExteriorLights(
    model::ExteriorLights& modelObject)
{
  IdfObject idfObject(IddObjectType::Exterior_Lights);

  m_idfObjects.push_back(idfObject);

  idfObject.setString(Exterior_LightsFields::Name, modelObject.name().get());

  OptionalIdfObject relatedIdfObject;

  model::Schedule schedule = modelObject.schedule();
  relatedIdfObject = translateAndMapModelObject(schedule);
  BOOST_ASSERT(relatedIdfObject);
  idfObject.setString(Exterior_LightsFields::ScheduleName,relatedIdfObject->name().get());

  model::ExteriorLightsDefinition definition = modelObject.exteriorLightsDefinition();

  double designLevel = definition.designLevel()*modelObject.multiplier();
  idfObject.setDouble(Exterior_LightsFields::DesignLevel,designLevel);

  if (!modelObject.isControlOptionDefaulted()) {
    idfObject.setString(Exterior_LightsFields::ControlOption,modelObject.controlOption());
  }

  if (!modelObject.isEndUseSubcategoryDefaulted()) {
    idfObject.setString(Exterior_LightsFields::EndUseSubcategory,modelObject.endUseSubcategory());
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::createAirLoopHVACSupplyPath( AirLoopHVAC & airLoopHVAC )
{
  std::string s;

  IdfObject supplyPathIdf(openstudio::IddObjectType::AirLoopHVAC_SupplyPath);

  m_idfObjects.push_back(supplyPathIdf);

  supplyPathIdf.createName();

  s = airLoopHVAC.demandInletNodes().front().name().get();
  supplyPathIdf.setString(openstudio::AirLoopHVAC_SupplyPathFields::SupplyAirPathInletNodeName,s);

  ModelObjectVector modelObjects;
  modelObjects = airLoopHVAC.demandComponents( airLoopHVAC.demandInletNodes().front(),
                                               airLoopHVAC.demandOutletNode(),
                                               openstudio::IddObjectType::OS_AirLoopHVAC_ZoneSplitter );
  if( modelObjects.size() == 1 )
  {
    ModelObject modelObject = modelObjects.front();
    OptionalAirLoopHVACZoneSplitter zoneSplitter = modelObject.optionalCast<AirLoopHVACZoneSplitter>();
    translateAndMapModelObject(*zoneSplitter);

    s = stripOS2(zoneSplitter->iddObject().name());
    supplyPathIdf.setString(2,s);

    s = zoneSplitter->name().get();
    supplyPathIdf.setString(3,s);
  }

  return boost::optional<IdfObject>(supplyPathIdf);
}
boost::optional<IdfObject> ForwardTranslator::translateScheduleYear( ScheduleYear & modelObject )
{
  IdfObject scheduleYear = createRegisterAndNameIdfObject(openstudio::IddObjectType::Schedule_Year,
                                                          modelObject);

  std::vector<ScheduleWeek> scheduleWeeks = modelObject.scheduleWeeks();
  std::vector<openstudio::Date> dates = modelObject.dates();
  
  unsigned N = scheduleWeeks.size();

  if( N != dates.size() )
  {
    LOG(Error,"Could not translate " << modelObject.briefDescription() << ", because the number of week schedules does not match the number of dates.");

    return boost::none;
  }

  boost::optional<ScheduleTypeLimits> scheduleTypeLimits = modelObject.scheduleTypeLimits();
  if (scheduleTypeLimits){
    boost::optional<IdfObject> idfScheduleTypeLimits = translateAndMapModelObject(*scheduleTypeLimits);
    if (idfScheduleTypeLimits){
      scheduleYear.setString(Schedule_YearFields::ScheduleTypeLimitsName, idfScheduleTypeLimits->name().get());
    }
  }

  openstudio::Date startDate(MonthOfYear::Jan, 1);

  scheduleYear.clearExtensibleGroups();

  for (unsigned i = 0; i < N; ++i){
    IdfExtensibleGroup group = scheduleYear.pushExtensibleGroup();

    boost::optional<IdfObject> idfScheduleWeek = translateAndMapModelObject(scheduleWeeks[i]);
    if (idfScheduleWeek){
      group.setString(Schedule_YearExtensibleFields::Schedule_WeekName, idfScheduleWeek->name().get());
    }

    group.setInt(Schedule_YearExtensibleFields::StartMonth, startDate.monthOfYear().value());
    group.setUnsigned(Schedule_YearExtensibleFields::StartDay, startDate.dayOfMonth());
    group.setInt(Schedule_YearExtensibleFields::EndMonth, dates[i].monthOfYear().value());
    group.setUnsigned(Schedule_YearExtensibleFields::EndDay, dates[i].dayOfMonth());

    startDate = dates[i] + openstudio::Time(1,0,0);
  }

  return scheduleYear;
}
boost::optional<IdfObject> ForwardTranslator::translateNode( Node & modelObject )
{
  std::vector<SetpointManager> _setpointManagers = modelObject.setpointManagers();
  for(auto _setpointManager : _setpointManagers )
  {
    translateAndMapModelObject( _setpointManager );
  }

  return boost::optional<IdfObject>();
}
boost::optional<IdfObject> ForwardTranslator::translateInteriorPartitionSurfaceGroup( model::InteriorPartitionSurfaceGroup & modelObject )
{
  InteriorPartitionSurfaceVector interiorPartitionSurfaces = modelObject.interiorPartitionSurfaces();
  std::sort(interiorPartitionSurfaces.begin(), interiorPartitionSurfaces.end(), WorkspaceObjectNameLess());
  for (InteriorPartitionSurface& interiorPartitionSurface : interiorPartitionSurfaces){
    translateAndMapModelObject(interiorPartitionSurface);
  }

  return boost::none;
}
boost::optional<IdfObject> ForwardTranslator::translateShadingSurfaceGroup( model::ShadingSurfaceGroup & modelObject )
{
  
  ShadingSurfaceVector shadingSurfaces = modelObject.shadingSurfaces();
  std::sort(shadingSurfaces.begin(), shadingSurfaces.end(), WorkspaceObjectNameLess());
  for (ShadingSurface& shadingSurface : shadingSurfaces){
    translateAndMapModelObject(shadingSurface);
  }

  return boost::none;
}
boost::optional<IdfObject> ForwardTranslator::translateSetpointManagerScheduledDualSetpoint( SetpointManagerScheduledDualSetpoint & modelObject )
{
  // Name
  IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::SetpointManager_Scheduled_DualSetpoint, modelObject);

  // ControlVariable
  std::string s = modelObject.controlVariable();
  idfObject.setString(SetpointManager_Scheduled_DualSetpointFields::ControlVariable,s);

  // High Setpoint Schedule Name
  if( boost::optional<Schedule> highSetpointSchedule = modelObject.highSetpointSchedule() )
  {
    boost::optional<IdfObject> _highSetpointSchedule = translateAndMapModelObject(highSetpointSchedule.get());
    if( _highSetpointSchedule && _highSetpointSchedule->name() )
    {
      idfObject.setString(SetpointManager_Scheduled_DualSetpointFields::HighSetpointScheduleName,_highSetpointSchedule->name().get());
    }
  } else {
    LOG(Error, "SetpointManager:Scheduled:DualSetpoint '" << modelObject.name().get() << "' is missing required high setpoint schedule");
  }

  // Low Setpoint Schedule Name
  if( boost::optional<Schedule> lowSetpointSchedule = modelObject.lowSetpointSchedule() )
  {
    boost::optional<IdfObject> _lowSetpointSchedule = translateAndMapModelObject(lowSetpointSchedule.get());
    if( _lowSetpointSchedule && _lowSetpointSchedule->name() )
    {
      idfObject.setString(SetpointManager_Scheduled_DualSetpointFields::LowSetpointScheduleName,_lowSetpointSchedule->name().get());
    }
  } else {
    LOG(Error, "SetpointManager:Scheduled:DualSetpoint '" << modelObject.name().get() << "' is missing required low setpoint schedule");
  }

  // SetpointNodeorNodeListName
  if( boost::optional<Node> node = modelObject.setpointNode() )
  {
    idfObject.setString(SetpointManager_Scheduled_DualSetpointFields::SetpointNodeorNodeListName,node->name().get());
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateScheduleDay( ScheduleDay & modelObject )
{
  IdfObject scheduleDay = createRegisterAndNameIdfObject(openstudio::IddObjectType::Schedule_Day_Interval,
                                                         modelObject);

  boost::optional<ScheduleTypeLimits> scheduleTypeLimits = modelObject.scheduleTypeLimits();
  if (scheduleTypeLimits){
    boost::optional<IdfObject> idfScheduleTypeLimits = translateAndMapModelObject(*scheduleTypeLimits);
    if (idfScheduleTypeLimits){
      scheduleDay.setString(Schedule_Day_IntervalFields::ScheduleTypeLimitsName, idfScheduleTypeLimits->name().get());
    }
  }

  if (modelObject.interpolatetoTimestep()){
    scheduleDay.setString(Schedule_Day_IntervalFields::InterpolatetoTimestep, "Yes");
  }else{
    scheduleDay.setString(Schedule_Day_IntervalFields::InterpolatetoTimestep, "No");
  }

  std::vector<double> values = modelObject.values();
  std::vector<openstudio::Time> times = modelObject.times();
  
  unsigned N = values.size();
  OS_ASSERT(N == times.size());

  scheduleDay.clearExtensibleGroups();

  for (unsigned i = 0; i < N; ++i){
    IdfExtensibleGroup group = scheduleDay.pushExtensibleGroup();

    std::string hourPrefix;
    std::string minutePrefix;

    int hours = times[i].hours() + 24*times[i].days();
    if (hours < 10){
      hourPrefix = "0";
    }

    int minutes = times[i].minutes() + floor((times[i].seconds()/60.0) + 0.5);
    if (minutes < 10){
      minutePrefix = "0";
    }

    std::stringstream ss;
    ss << hourPrefix << hours << ":" << minutePrefix << minutes;

    group.setString(Schedule_Day_IntervalExtensibleFields::Time, ss.str());
    group.setDouble(Schedule_Day_IntervalExtensibleFields::ValueUntilTime, values[i]);
  }

  return scheduleDay;
}
boost::optional<IdfObject> ForwardTranslator::translateConstruction( Construction & modelObject )
{
  IdfObject construction( openstudio::IddObjectType::Construction );
  m_idfObjects.push_back(construction);

  for (LifeCycleCost lifeCycleCost : modelObject.lifeCycleCosts()){
    translateAndMapModelObject(lifeCycleCost);
  }

  construction.setName(modelObject.name().get());

  MaterialVector layers = modelObject.layers();

  unsigned fieldIndex = 1;
  for(unsigned layerIndex = 0; layerIndex < layers.size(); ++layerIndex ) {
    Material material = layers[layerIndex];
    translateAndMapModelObject(material);
    construction.setString(fieldIndex++, material.name().get());
  }

  return boost::optional<IdfObject>(construction);
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACEquipmentList( ZoneHVACEquipmentList & modelObject )
{
  OptionalString s;
  OptionalDouble d;
  OptionalModelObject temp;

  std::vector<ModelObject> objects = modelObject.equipment();

  if (objects.empty()){
    // do not write out this object
    return boost::none;
  }

  IdfObject idfObject(IddObjectType::ZoneHVAC_EquipmentList);

  // Name
  s = modelObject.name();
  if(s)
  {
    idfObject.setName(*s);
  }

  for( std::vector<ModelObject>::iterator it = objects.begin();
       it != objects.end();
       it++ )
  {
    unsigned coolingPriority = modelObject.coolingPriority(*it);
    unsigned heatingPriority = modelObject.heatingPriority(*it);

    boost::optional<IdfObject> _equipment = translateAndMapModelObject(*it);

    if( _equipment )
    {
      IdfExtensibleGroup eg = idfObject.pushExtensibleGroup();

      eg.setString(ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipmentObjectType,_equipment->iddObject().name()); 
      eg.setString(ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipmentName,_equipment->name().get()); 
      eg.setUnsigned(ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipmentCoolingSequence,coolingPriority); 
      eg.setUnsigned(ZoneHVAC_EquipmentListExtensibleFields::ZoneEquipmentHeatingorNoLoadSequence,heatingPriority); 
    }
  }

  m_idfObjects.push_back(idfObject);

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translatePlantEquipmentOperationOutdoorWetBulbDifference( PlantEquipmentOperationOutdoorWetBulbDifference & modelObject )
{
    IdfObject idfObject(IddObjectType::PlantEquipmentOperation_OutdoorWetBulbDifference);
    m_idfObjects.push_back(idfObject);

    // Name
    auto name = modelObject.name().get();
    idfObject.setName(name);

    // ReferenceTemperatureNodeName
    if( const auto & node = modelObject.referenceTemperatureNode() ) {
        idfObject.setString(PlantEquipmentOperation_OutdoorWetBulbDifferenceFields::ReferenceTemperatureNodeName,node->name().get());
    }

    double lowerLimit = modelObject.minimumLowerLimit();
    int i = 1;
    for( auto upperLimit : modelObject.loadRangeUpperLimits() ) {
        auto equipment = modelObject.equipment(upperLimit);
        if( ! equipment.empty() ) {
            auto eg = idfObject.pushExtensibleGroup();
            eg.setDouble(PlantEquipmentOperation_OutdoorWetBulbDifferenceExtensibleFields::WetBulbTemperatureDifferenceRangeLowerLimit,lowerLimit);
            eg.setDouble(PlantEquipmentOperation_OutdoorWetBulbDifferenceExtensibleFields::WetBulbTemperatureDifferenceRangeUpperLimit,upperLimit);

            IdfObject equipmentList(IddObjectType::PlantEquipmentList);
            m_idfObjects.push_back(equipmentList);
            auto equipmentListName = name + " equipment list " + std::to_string(i);
            equipmentList.setName(equipmentListName);
            eg.setString(PlantEquipmentOperation_OutdoorWetBulbDifferenceExtensibleFields::RangeEquipmentListName,equipmentListName);

            for( auto component : equipment ) {
                auto eg2 = equipmentList.pushExtensibleGroup();
                auto idf_component = translateAndMapModelObject(component);
                OS_ASSERT(idf_component);
                eg2.setString(PlantEquipmentListExtensibleFields::EquipmentObjectType,idf_component->iddObject().name());
                eg2.setString(PlantEquipmentListExtensibleFields::EquipmentName,idf_component->name().get());
            }
        }

        lowerLimit = upperLimit;
        ++i;
    }

    return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateSite( Site & modelObject )
{
  IdfObject site = createAndRegisterIdfObject(openstudio::IddObjectType::Site_Location,
                                              modelObject);

  OptionalString optS = modelObject.name();
  if(optS) {
    site.setName(*optS);
  }

  OptionalDouble od = modelObject.latitude();
  if(od) {
    site.setDouble(Site_LocationFields::Latitude, *od);
  }

  od = modelObject.longitude();
  if(od) {
    site.setDouble(Site_LocationFields::Longitude, *od);
  }

  od = modelObject.timeZone();
  if(od) {
    site.setDouble(Site_LocationFields::TimeZone, *od);
  }

  od = modelObject.elevation();
  if(od) {
    site.setDouble(Site_LocationFields::Elevation, *od);
  }

  // translate shading groups
  ShadingSurfaceGroupVector shadingSurfaceGroups = modelObject.shadingSurfaceGroups();
  std::sort(shadingSurfaceGroups.begin(), shadingSurfaceGroups.end(), WorkspaceObjectNameLess());
  for (ShadingSurfaceGroup& shadingSurfaceGroup : shadingSurfaceGroups){
    translateAndMapModelObject(shadingSurfaceGroup);
  }

  return boost::optional<IdfObject>(site);
}
boost::optional<IdfObject> ForwardTranslator::translateCoilHeatingDXSingleSpeed( CoilHeatingDXSingleSpeed & modelObject )
{
  IdfObject _coilSystemHeatingDX(IddObjectType::CoilSystem_Heating_DX);
    
  m_idfObjects.push_back(_coilSystemHeatingDX);

  boost::optional<IdfObject> _coilHeatingDXSingleSpeed = translateCoilHeatingDXSingleSpeedWithoutUnitary(modelObject);

  OS_ASSERT(_coilHeatingDXSingleSpeed);

  OptionalString s;

  s = modelObject.name();

  // Type and Name

  if( s )
  {
    _coilSystemHeatingDX.setString(CoilSystem_Heating_DXFields::HeatingCoilObjectType,_coilHeatingDXSingleSpeed->iddObject().name());

    _coilSystemHeatingDX.setString(CoilSystem_Heating_DXFields::HeatingCoilName,*s);

    _coilSystemHeatingDX.setName(*s + " CoilSystem");
  }

  // Availability Schedule

  Schedule schedule = modelObject.availabilitySchedule();

  boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule);

  if( _schedule )
  {
    _coilSystemHeatingDX.setString(CoilSystem_Heating_DXFields::AvailabilityScheduleName,_schedule->name().get());
  }

  return _coilSystemHeatingDX;
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACBaseboardConvectiveElectric(
    ZoneHVACBaseboardConvectiveElectric & modelObject )
{
  // Makes sure the modelObject gets put in the map, and that the new idfObject gets put in 
  // the final file. Also set's the idfObject's name.
  IdfObject idfObject = createRegisterAndNameIdfObject(IddObjectType::ZoneHVAC_Baseboard_Convective_Electric,modelObject);

  boost::optional<std::string> s;
  boost::optional<double> value;
  boost::optional<ModelObject> temp;
  
  // AvailabilityScheduleName
  Schedule availabilitySchedule = modelObject.availabilitySchedule();
  translateAndMapModelObject(availabilitySchedule);
  idfObject.setString(ZoneHVAC_Baseboard_Convective_ElectricFields::AvailabilityScheduleName,
                      availabilitySchedule.name().get() );

  // NominalCapacity

  if( modelObject.isNominalCapacityAutosized() )
  {
    idfObject.setString(ZoneHVAC_Baseboard_Convective_ElectricFields::NominalCapacity,"Autosize");
  }
  else if( value = modelObject.nominalCapacity() )
  {
    idfObject.setDouble(ZoneHVAC_Baseboard_Convective_ElectricFields::NominalCapacity,value.get());
  }

  // Efficiency

  if( value = modelObject.efficiency() )
  {
    idfObject.setDouble(ZoneHVAC_Baseboard_Convective_ElectricFields::Efficiency,value.get());
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateSolarCollectorPerformancePhotovoltaicThermalSimple(model::SolarCollectorPerformancePhotovoltaicThermalSimple & modelObject)
{
  IdfObject idfObject(openstudio::IddObjectType::SolarCollectorPerformance_PhotovoltaicThermal_Simple);

  m_idfObjects.push_back(idfObject);

  boost::optional<double> d;

  idfObject.setString(SolarCollectorPerformance_PhotovoltaicThermal_SimpleFields::Name, modelObject.name().get());

  idfObject.setDouble(SolarCollectorPerformance_PhotovoltaicThermal_SimpleFields::FractionofSurfaceAreawithActiveThermalCollector, modelObject.fractionOfSurfaceAreaWithActiveThermalCollector());

  std::string thermalConversionEfficiencyInputModeType = modelObject.thermalConversionEfficiencyInputModeType();
  idfObject.setString(SolarCollectorPerformance_PhotovoltaicThermal_SimpleFields::ThermalConversionEfficiencyInputModeType, thermalConversionEfficiencyInputModeType);

  if (istringEqual("Scheduled", thermalConversionEfficiencyInputModeType)){
    boost::optional<Schedule> schedule = modelObject.thermalConversionEfficiencySchedule();
    if (schedule){
      boost::optional<IdfObject> relatedIdfObject = translateAndMapModelObject(*schedule);
      OS_ASSERT(relatedIdfObject);
      idfObject.setString(SolarCollectorPerformance_PhotovoltaicThermal_SimpleFields::ThermalConversionEfficiencyScheduleName, relatedIdfObject->name().get());
    } else{
      LOG(Error, "SolarCollectorPerformance:PhotovoltaicThermal:Simple object named '" << modelObject.name().get() << "' has ThermalConversionEfficiencyInputModeType of Scheduled but has no schedule attached.");
    }
  } else {
    d = modelObject.thermalConversionEfficiency();
    if (d){
      idfObject.setDouble(SolarCollectorPerformance_PhotovoltaicThermal_SimpleFields::ValueforThermalConversionEfficiencyifFixed, *d);
    }else{
      LOG(Error, "SolarCollectorPerformance:PhotovoltaicThermal:Simple object named '" << modelObject.name().get() << "' has ThermalConversionEfficiencyInputModeType of Fixed but has no value set.");
    }
  }

  idfObject.setDouble(SolarCollectorPerformance_PhotovoltaicThermal_SimpleFields::FrontSurfaceEmittance, modelObject.frontSurfaceEmittance());

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateScheduleCompact( ScheduleCompact & modelObject )
{
  IdfObject scheduleCompact = createAndRegisterIdfObject(openstudio::IddObjectType::Schedule_Compact,
                                                         modelObject);

  OptionalString os;
  if ((os = modelObject.name())) {
    scheduleCompact.setName(*os);
  }

  boost::optional<ScheduleTypeLimits> scheduleTypeLimits = modelObject.scheduleTypeLimits();
  if (scheduleTypeLimits){
    boost::optional<IdfObject> idfScheduleTypeLimits = translateAndMapModelObject(*scheduleTypeLimits);
    if (idfScheduleTypeLimits){
      scheduleCompact.setString(Schedule_CompactFields::ScheduleTypeLimitsName, idfScheduleTypeLimits->name().get());
    }
  }

  for (const IdfExtensibleGroup& eg : modelObject.extensibleGroups()) {
    scheduleCompact.pushExtensibleGroup(eg.fields());
  }

  return boost::optional<IdfObject>(scheduleCompact);
}
boost::optional<IdfObject> ForwardTranslator::translateCoilHeatingElectric( CoilHeatingElectric & modelObject )
{
  boost::optional<std::string> s;
  boost::optional<double> value;

  IdfObject idfObject(IddObjectType::Coil_Heating_Electric);

  m_idfObjects.push_back(idfObject);

  // Name

  s = modelObject.name();
  if( s )
  {
    idfObject.setName(*s);
  }

  // AvailabilityScheduleName

  if( boost::optional<Schedule> schedule = modelObject.availabilitySchedule() )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
    {
      idfObject.setString(Coil_Heating_ElectricFields::AvailabilityScheduleName,_schedule->name().get());
    }
  }

  // Efficiency

  if( (value = modelObject.efficiency()) )
  {
    idfObject.setDouble(Coil_Heating_ElectricFields::Efficiency,value.get());
  }

  // Nominal Capacity 
  
  if( modelObject.isNominalCapacityAutosized() )
  {
    idfObject.setString(Coil_Heating_ElectricFields::NominalCapacity,"Autosize");
  }
  else if( (value = modelObject.nominalCapacity()) )
  {
    idfObject.setDouble(Coil_Heating_ElectricFields::NominalCapacity,value.get());
  }

  // Air Inlet Node Name
 
  if( boost::optional<ModelObject> mo = modelObject.inletModelObject() )
  {
    if( boost::optional<Node> node = mo->optionalCast<Node>() )
    {
      idfObject.setString(Coil_Heating_ElectricFields::AirInletNodeName,node->name().get());
    }
  }

  // Air Outlet Node Name
 
  if( boost::optional<ModelObject> mo = modelObject.outletModelObject() )
  {
    if( boost::optional<Node> node = mo->optionalCast<Node>() )
    {
      idfObject.setString(Coil_Heating_ElectricFields::AirOutletNodeName,node->name().get());
    }
  }

  // Temperature Setpoint Node Name 
 
  if( boost::optional<Node> node = modelObject.temperatureSetpointNode() )
  {
    idfObject.setString(Coil_Heating_ElectricFields::TemperatureSetpointNodeName,node->name().get());
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACIdealLoadsAirSystem(ZoneHVACIdealLoadsAirSystem & modelObject)
{
  
  IdfObject zoneHVACIdealLoadsAirSystem = createRegisterAndNameIdfObject(openstudio::IddObjectType::ZoneHVAC_IdealLoadsAirSystem,modelObject);
  
  // availability schedule name
  boost::optional<Schedule> schedule = modelObject.availabilitySchedule();
  if(schedule){
    boost::optional<IdfObject> idfSchedule = translateAndMapModelObject(*schedule);
    if(idfSchedule){
      zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::AvailabilityScheduleName,idfSchedule->name().get());
    }
  }

  // zone supply air node name
  boost::optional<std::string> zoneSupplyAirNodeName;
  if(boost::optional<Node> node = modelObject.outletNode()){
    if(boost::optional<std::string> s = node->name()){
      zoneSupplyAirNodeName = s;
      zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::ZoneSupplyAirNodeName,s.get());
    }
  }

  // zone exhaust air node name
  boost::optional<std::string> zoneExhaustAirNodeName;
  if(boost::optional<Node> node = modelObject.inletNode()){
    if(boost::optional<std::string> s = node->name()){
      zoneExhaustAirNodeName = s;
      zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::ZoneExhaustAirNodeName,s.get());
    }
  }

  // maximum heating supply air temperature
  if(! modelObject.isMaximumHeatingSupplyAirTemperatureDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingSupplyAirTemperature,modelObject.maximumHeatingSupplyAirTemperature());
  }

  // minimum cooling supply air temperature
  if (! modelObject.isMinimumCoolingSupplyAirTemperatureDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MinimumCoolingSupplyAirTemperature,modelObject.minimumCoolingSupplyAirTemperature());
  }

  // maximum heating supply air humidity ratio
  if (! modelObject.isMaximumHeatingSupplyAirHumidityRatioDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingSupplyAirHumidityRatio,modelObject.maximumHeatingSupplyAirHumidityRatio());
  }

  // minimum cooling supply air humidity ratio
  if (! modelObject.isMinimumCoolingSupplyAirHumidityRatioDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MinimumCoolingSupplyAirHumidityRatio,modelObject.minimumCoolingSupplyAirHumidityRatio());
  }

  // heating limit
  if (! modelObject.isHeatingLimitDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::HeatingLimit,modelObject.heatingLimit());
  }

  // maximum heating air flow rate
  if(modelObject.isMaximumHeatingAirFlowRateAutosized()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingAirFlowRate,"Autosize");
  }else if(boost::optional<double> d = modelObject.maximumHeatingAirFlowRate()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumHeatingAirFlowRate,d.get());
  }

  // maximum sensible heating capacity
  if(modelObject.isMaximumSensibleHeatingCapacityAutosized()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::MaximumSensibleHeatingCapacity,"Autosize");
  }else if(boost::optional<double> d = modelObject.maximumSensibleHeatingCapacity()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumSensibleHeatingCapacity,d.get());
  }

  // cooling limit
  if (! modelObject.isCoolingLimitDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::CoolingLimit,modelObject.coolingLimit());
  }

  // maximum cooling air flow rate
  if(modelObject.isMaximumCoolingAirFlowRateAutosized()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::MaximumCoolingAirFlowRate,"Autosize");
  }else if(boost::optional<double> d = modelObject.maximumCoolingAirFlowRate()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumCoolingAirFlowRate,d.get());
  }

  // maximum total cooling capacity
  if(modelObject.isMaximumTotalCoolingCapacityAutosized()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::MaximumTotalCoolingCapacity,"Autosize");
  }else if(boost::optional<double> d = modelObject.maximumTotalCoolingCapacity()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::MaximumTotalCoolingCapacity,d.get());
  }

  // heating availability schedule name
  if(boost::optional<Schedule> schedule = modelObject.heatingAvailabilitySchedule()){
    if(boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get())){
      zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::HeatingAvailabilityScheduleName,_schedule->name().get());
    }
  }

 // cooling availability schedule name
  if(boost::optional<Schedule> schedule = modelObject.coolingAvailabilitySchedule()){
    if(boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get())){
      zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::CoolingAvailabilityScheduleName,_schedule->name().get());
    }
  }

  // dehumidification control type
  if (! modelObject.isDehumidificationControlTypeDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::DehumidificationControlType,modelObject.dehumidificationControlType());
  }

  // cooling sensible heat ratio
  if (! modelObject.isCoolingSensibleHeatRatioDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::CoolingSensibleHeatRatio,modelObject.coolingSensibleHeatRatio());
  }

  // humidification control type
  if (! modelObject.isHumidificationControlTypeDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::HumidificationControlType,modelObject.humidificationControlType());
  }

  // forward translate design specification outdoor air object name
  // get the zone that this piece of equipment is connected to
  boost::optional<ThermalZone> zone = modelObject.thermalZone();
  if (zone) {
    // get this zone's space
    std::vector<Space> spaces = zone->spaces();
    // get the space's design specification outdoor air, if one exists
    if ( ! spaces.empty() ){
      boost::optional<DesignSpecificationOutdoorAir> designSpecificationOutdoorAir;
      designSpecificationOutdoorAir = spaces[0].designSpecificationOutdoorAir();
      if (designSpecificationOutdoorAir){
        // translate the design specification outdoor air to idf      
        boost::optional<IdfObject> designSpecificationOutdoorAirIdf;
        designSpecificationOutdoorAirIdf = translateAndMapModelObject(*designSpecificationOutdoorAir);
        // the translation should complete successfully
        OS_ASSERT(designSpecificationOutdoorAirIdf);      
        // set the field to reference the design specification outdoor air
        zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::DesignSpecificationOutdoorAirObjectName,designSpecificationOutdoorAirIdf->name().get()); 
      }
    }
  }

  // demand controlled ventilation type
  if (! modelObject.isDemandControlledVentilationTypeDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::DemandControlledVentilationType,modelObject.demandControlledVentilationType());
  }

  // outdoor air economizer type
  if (! modelObject.isOutdoorAirEconomizerTypeDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::OutdoorAirEconomizerType,modelObject.outdoorAirEconomizerType());
  }

  // heat recovery type
  if (! modelObject.isHeatRecoveryTypeDefaulted()){
    zoneHVACIdealLoadsAirSystem.setString(ZoneHVAC_IdealLoadsAirSystemFields::HeatRecoveryType,modelObject.heatRecoveryType());
  }

  // sensible heat recovery effectiveness
  if (! modelObject.isSensibleHeatRecoveryEffectivenessDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::SensibleHeatRecoveryEffectiveness,modelObject.sensibleHeatRecoveryEffectiveness());
  }

  // latent heat recovery effectiveness
  if (! modelObject.isLatentHeatRecoveryEffectivenessDefaulted()){
    zoneHVACIdealLoadsAirSystem.setDouble(ZoneHVAC_IdealLoadsAirSystemFields::LatentHeatRecoveryEffectiveness,modelObject.latentHeatRecoveryEffectiveness());
  }

  return zoneHVACIdealLoadsAirSystem;
}
boost::optional<IdfObject> ForwardTranslator::translateAirTerminalSingleDuctVAVReheat( AirTerminalSingleDuctVAVReheat & modelObject )
{
  OptionalModelObject temp;
  OptionalString optS;
  boost::optional<std::string> s;

  std::string baseName = modelObject.name().get();

  IdfObject _airDistributionUnit(openstudio::IddObjectType::ZoneHVAC_AirDistributionUnit);
  _airDistributionUnit.setName("ADU " + baseName ); //ADU: Air Distribution Unit

  IdfObject idfObject(openstudio::IddObjectType::AirTerminal_SingleDuct_VAV_Reheat);

  idfObject.setName(baseName);

  HVACComponent coil = modelObject.reheatCoil();

  m_idfObjects.push_back(_airDistributionUnit);

  m_idfObjects.push_back(idfObject);

  boost::optional<IdfObject> _reheatCoil = translateAndMapModelObject(coil);

  if( _reheatCoil && _reheatCoil->name() )
  {
    std::string damperOutletNodeName = modelObject.name().get() + " Damper Outlet";

    boost::optional<std::string> inletNodeName;
    boost::optional<std::string> outletNodeName;

    if( boost::optional<ModelObject> inletModelObject = modelObject.inletModelObject() )
    {
      if( boost::optional<Node> inletNode = inletModelObject->optionalCast<Node>() )
      {
        inletNodeName = inletNode->name().get();
      }
    }

    if( boost::optional<ModelObject> outletModelObject = modelObject.outletModelObject() )
    {
      if( boost::optional<Node> outletNode = outletModelObject->optionalCast<Node>() )
      {
        outletNodeName = outletNode->name().get();
      }
    }

    // Reheat Coil Name
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::ReheatCoilName,_reheatCoil->name().get());

    // Reheat Coil Type
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::ReheatCoilObjectType,_reheatCoil->iddObject().name());

    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::DamperAirOutletNodeName,damperOutletNodeName);

    if( outletNodeName && inletNodeName )
    {
      if( _reheatCoil->iddObject().type() == IddObjectType::Coil_Heating_Gas )
      {
        _reheatCoil->setString(Coil_Heating_GasFields::AirInletNodeName,damperOutletNodeName);
        _reheatCoil->setString(Coil_Heating_GasFields::AirOutletNodeName,outletNodeName.get());
      }
      else if( _reheatCoil->iddObject().type() == IddObjectType::Coil_Heating_Electric )
      {
        _reheatCoil->setString(Coil_Heating_ElectricFields::AirInletNodeName,damperOutletNodeName);
        _reheatCoil->setString(Coil_Heating_ElectricFields::AirOutletNodeName,outletNodeName.get());
      }
      else if( _reheatCoil->iddObject().type() == IddObjectType::Coil_Heating_Water )
      {
        _reheatCoil->setString(Coil_Heating_WaterFields::AirInletNodeName,damperOutletNodeName);
        _reheatCoil->setString(Coil_Heating_WaterFields::AirOutletNodeName,outletNodeName.get());
      }

      idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::AirOutletNodeName,outletNodeName.get());
      idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::AirInletNodeName,inletNodeName.get());
    }
  }
  else
  {
    LOG(Error,modelObject.briefDescription() << ": Could not translate heating coil");

    return boost::none;
  }

  // AvailabilityScheduleName
  Schedule availabilitySchedule = modelObject.availabilitySchedule();

  boost::optional<IdfObject> _availabilitySchedule = translateAndMapModelObject(availabilitySchedule);

  if( _availabilitySchedule && _availabilitySchedule->name() )
  {
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::AvailabilityScheduleName,_availabilitySchedule->name().get());
  }

  // MaximumAirFlowRate
  boost::optional<double> value = modelObject.maximumAirFlowRate();
  if( value )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumAirFlowRate,value.get());
  }
  else if( modelObject.isMaximumAirFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumAirFlowRate,"Autosize");
  }

  // ZoneMinimumAirFlowMethod
  s = modelObject.zoneMinimumAirFlowMethod();
  if( s )
  {
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::ZoneMinimumAirFlowInputMethod,s.get());
  }

  // ConstantMinimumAirFlowFraction
  value = modelObject.constantMinimumAirFlowFraction();
  idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::ConstantMinimumAirFlowFraction,value.get());

  // FixedMinimumAirFlowRate
  value = modelObject.fixedMinimumAirFlowRate();
  idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::FixedMinimumAirFlowRate,value.get());

  // MinimumAirFlowFractionScheduleName
  boost::optional<Schedule> minAirFlowFractionSchedule = modelObject.minimumAirFlowFractionSchedule();

  if( minAirFlowFractionSchedule )
  {
    boost::optional<IdfObject> _minAirFlowFractionSchedule = translateAndMapModelObject(minAirFlowFractionSchedule.get());

    if( _minAirFlowFractionSchedule && _minAirFlowFractionSchedule->name() )
    {
      idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::MinimumAirFlowFractionScheduleName,_minAirFlowFractionSchedule->name().get());
    }
  }

  // MaximumHotWaterOrSteamFlowRate
  value = modelObject.maximumHotWaterOrSteamFlowRate();
  
  if( value )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumHotWaterorSteamFlowRate,value.get());
  }
  else if( modelObject.isMaximumHotWaterOrSteamFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumHotWaterorSteamFlowRate,"Autosize");
  }

  // MinimumHotWaterOrSteamFlowRate
  value = modelObject.minimumHotWaterOrSteamFlowRate();
  idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MinimumHotWaterorSteamFlowRate,value.get());

  // ConvergenceTolerance
  value = modelObject.convergenceTolerance();
  idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::ConvergenceTolerance,value.get());

  // DamperHeatingAction
  s = modelObject.damperHeatingAction();
  idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::DamperHeatingAction,s.get());

  // MaximumFlowPerZoneFloorAreaDuringReheat
  value = modelObject.maximumFlowPerZoneFloorAreaDuringReheat();
  
  if( value )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowperZoneFloorAreaDuringReheat,value.get());
  }
  else if ( modelObject.isMaximumFlowPerZoneFloorAreaDuringReheatAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowperZoneFloorAreaDuringReheat,"Autocalculate");
  }

  // MaximumFlowFractionDuringReheat
  value = modelObject.maximumFlowFractionDuringReheat();

  if( value )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowFractionDuringReheat,value.get());
  }
  else if( modelObject.isMaximumFlowFractionDuringReheatAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowFractionDuringReheat,"Autocalculate");
  }

  // MaximumReheatAirTemperature
  value = modelObject.maximumReheatAirTemperature();
  idfObject.setDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumReheatAirTemperature,value.get());

  // Populate fields for AirDistributionUnit
  if( boost::optional<ModelObject> outletNode = modelObject.outletModelObject() )
  {
    _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirDistributionUnitOutletNodeName,outletNode->name().get());
  }
  _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirTerminalObjectType,idfObject.iddObject().name());
  _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirTerminalName,idfObject.name().get());

  // ControlForOutdoorAir
  {
    if( modelObject.controlForOutdoorAir() ) {
      if( auto airLoopHVAC = modelObject.airLoopHVAC() ) {
        auto zones = airLoopHVAC->demandComponents(modelObject,airLoopHVAC->demandOutletNode(),model::ThermalZone::iddObjectType());
        if( ! zones.empty() ) {
          auto zone = zones.front();
          auto spaces = zone.cast<model::ThermalZone>().spaces();
          if( ! spaces.empty() ) {
            if( auto designSpecificationOutdoorAir = spaces.front().designSpecificationOutdoorAir() ) {
              idfObject.setString(AirTerminal_SingleDuct_VAV_ReheatFields::DesignSpecificationOutdoorAirObjectName,
                designSpecificationOutdoorAir->name().get()); 
            }
          }
        }
      }  
    }
  }

  return _airDistributionUnit;
}
boost::optional<IdfObject> ForwardTranslator::translateAirTerminalSingleDuctConstantVolumeFourPipeInduction( AirTerminalSingleDuctConstantVolumeFourPipeInduction & modelObject )
{
  OptionalString s;
  OptionalDouble d;
  OptionalModelObject temp;

  IdfObject _airDistributionUnit(openstudio::IddObjectType::ZoneHVAC_AirDistributionUnit);
  _airDistributionUnit.setName(modelObject.name().get() + " Air Distribution Unit");
  m_idfObjects.push_back(_airDistributionUnit);

  // Name
  IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::AirTerminal_SingleDuct_ConstantVolume_FourPipeInduction, modelObject);

  // Availability Schedule Name
  if( (temp = modelObject.availabilitySchedule()) )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(temp.get()) )
    {
      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::AvailabilityScheduleName,_schedule->name().get());
    }
  }

  // Maximum Total Air Flow Rate
  if( (d = modelObject.maximumTotalAirFlowRate()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MaximumTotalAirFlowRate,d.get());
  }
  else if( modelObject.isMaximumTotalAirFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MaximumTotalAirFlowRate,"Autosize");
  }

  // Induction Ratio
  if( (d = modelObject.inductionRatio()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::InductionRatio,d.get());
  }

  // Supply Air Inlet Node Name
  if( auto node = modelObject.inletModelObject() ) {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::SupplyAirInletNodeName,node->name().get());
  }

  // Induced Air Inlet Node Name
  if( auto node = modelObject.inducedAirInletNode() ) {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::InducedAirInletNodeName,node->name().get());
  }

  // Air Outlet Node Name
  if( auto node = modelObject.outletModelObject() ) {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::AirOutletNodeName,node->name().get());
  }

  // Hot Water Inlet Node Name
  // deprecated

  // Cold Water Inlet Node Name
  //deprecated

  // Heating Coil Object Type
  // Heating Coil Name
  boost::optional<IdfObject> _heatingCoil;
  {
    auto heatingCoil = modelObject.heatingCoil();
    if( (_heatingCoil = translateAndMapModelObject(heatingCoil)) ) {
      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::HeatingCoilObjectType,_heatingCoil->iddObject().name());
      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::HeatingCoilName,_heatingCoil->name().get());
    }
  }

  // Maximum Hot Water Flow Rate
  if( (d = modelObject.maximumHotWaterFlowRate()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MaximumHotWaterFlowRate,d.get());
  }
  else if( modelObject.isMaximumHotWaterFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MaximumHotWaterFlowRate,"Autosize");
  }

  // Minimum Hot Water Flow Rate
  if( (d = modelObject.minimumHotWaterFlowRate()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MinimumHotWaterFlowRate,d.get());
  }

  // Heating Convergence Tolerance
  if( (d = modelObject.heatingConvergenceTolerance()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::HeatingConvergenceTolerance,d.get());
  }

  // Cooling Coil Object Type
  // Cooling Coil Name
  boost::optional<IdfObject> _coolingCoil;
  if( auto coolingCoil = modelObject.coolingCoil() ) {
    if( (_coolingCoil = translateAndMapModelObject(coolingCoil.get())) ) {
      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::CoolingCoilObjectType,_coolingCoil->iddObject().name());
      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::CoolingCoilName,_coolingCoil->name().get());
    }
  }
  
  // Maximum Cold Water Flow Rate
  if( (d = modelObject.maximumColdWaterFlowRate()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MaximumColdWaterFlowRate,d.get());
  }
  else if( modelObject.isMaximumColdWaterFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MaximumColdWaterFlowRate,"Autosize");
  }

  // Minimum Cold Water Flow Rate
  if( (d = modelObject.minimumColdWaterFlowRate()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::MinimumColdWaterFlowRate,d.get());
  }

  // Cooling Convergence Tolerance
  if( (d = modelObject.coolingConvergenceTolerance()) )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::CoolingConvergenceTolerance,d.get());
  }

  // Zone Mixer Name
  IdfObject _mixer(IddObjectType::AirLoopHVAC_ZoneMixer);
  _mixer.setName(modelObject.name().get() + " Mixer");
  m_idfObjects.push_back(_mixer);
  _mixer.clearExtensibleGroups();
  idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_FourPipeInductionFields::ZoneMixerName,_mixer.name().get());

  std::string baseName = modelObject.name().get();

  std::string heatingCoilInlet;
  std::string heatingCoilOutlet;
  std::string coolingCoilInlet;
  std::string coolingCoilOutlet;
  std::string mixerAirSystemInlet;
  std::string mixerInducedInlet;
  std::string mixerOutlet;

  if( auto inducedAirInletNode = modelObject.inducedAirInletNode() ) {
    heatingCoilInlet = inducedAirInletNode->name().get();
  }
  heatingCoilOutlet = baseName + " Heating Coil Outlet";

  if( _coolingCoil ) {
    coolingCoilInlet = heatingCoilOutlet;
    coolingCoilOutlet = baseName + " Cooling Coil Outlet";

    mixerInducedInlet = coolingCoilOutlet;
  } else {
    mixerInducedInlet = heatingCoilOutlet;
  }

  if( auto node = modelObject.inletModelObject() ) {
    mixerAirSystemInlet = node->name().get();
  }
  if( auto node = modelObject.outletModelObject() ) {
    mixerOutlet = node->name().get();
  }

  if( _heatingCoil && (_heatingCoil->iddObject().type() == IddObjectType::Coil_Heating_Water) ) {
    _heatingCoil->setString(Coil_Heating_WaterFields::AirInletNodeName,heatingCoilInlet);
    _heatingCoil->setString(Coil_Heating_WaterFields::AirOutletNodeName,heatingCoilOutlet);
  }

  if( _coolingCoil && (_coolingCoil->iddObject().type() == IddObjectType::Coil_Cooling_Water) ) {
    _coolingCoil->setString(Coil_Cooling_WaterFields::AirInletNodeName,coolingCoilInlet);
    _coolingCoil->setString(Coil_Cooling_WaterFields::AirOutletNodeName,coolingCoilOutlet);
  }

  _mixer.setString(AirLoopHVAC_ZoneMixerFields::OutletNodeName,mixerOutlet);
  IdfExtensibleGroup eg = _mixer.pushExtensibleGroup();
  eg.setString(AirLoopHVAC_ZoneMixerExtensibleFields::InletNodeName,mixerAirSystemInlet);
  eg = _mixer.pushExtensibleGroup();
  eg.setString(AirLoopHVAC_ZoneMixerExtensibleFields::InletNodeName,mixerInducedInlet);

  if( auto node = modelObject.outletModelObject() ) {
    _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirDistributionUnitOutletNodeName,node->name().get());
  }
  _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirTerminalObjectType,idfObject.iddObject().name());
  _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirTerminalName,idfObject.name().get());

  return _airDistributionUnit;
}
boost::optional<IdfObject> ForwardTranslator::translatePlantEquipmentOperationSchemes( PlantLoop & plantLoop )
{
  IdfObject operationSchemes(IddObjectType::PlantEquipmentOperationSchemes);
  m_idfObjects.push_back(operationSchemes);

  operationSchemes.setName(plantLoop.name().get() + " Operation Schemes");

  // Lambda does what the name suggests, create setpoint operation schemes.
  // This is for any component that has a setpoint manager on its outlet node
  auto createSetpointOperationScheme = [&](PlantLoop & plantLoop) {
    const auto & t_setpointComponents = setpointComponents(plantLoop);
    if( ! t_setpointComponents.empty() ) {
      Schedule alwaysOn = plantLoop.model().alwaysOnDiscreteSchedule();

      IdfObject setpointOperation(IddObjectType::PlantEquipmentOperation_ComponentSetpoint);
      setpointOperation.setName(plantLoop.name().get() + " Setpoint Operation Scheme");
      m_idfObjects.push_back(setpointOperation);
      setpointOperation.clearExtensibleGroups();

      IdfExtensibleGroup eg = operationSchemes.pushExtensibleGroup();
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,setpointOperation.iddObject().name());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,setpointOperation.name().get());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

      for( auto setpointComponent : t_setpointComponents )
      {
        boost::optional<IdfObject> _idfObject = translateAndMapModelObject(setpointComponent);
        OS_ASSERT(_idfObject);

        IdfExtensibleGroup eg = setpointOperation.pushExtensibleGroup();
        eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::EquipmentObjectType,_idfObject->iddObject().name());
        eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::EquipmentName,_idfObject->name().get());
        if( const auto & t_inletNode = inletNode(plantLoop,setpointComponent) ) {
          eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::DemandCalculationNodeName,t_inletNode->name().get());
        }
        if( const auto & t_outletNode = outletNode(plantLoop,setpointComponent) ) {
          eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::SetpointNodeName,t_outletNode->name().get());
        }
        if( auto value = flowrate(setpointComponent) ) {
          eg.setDouble(PlantEquipmentOperation_ComponentSetpointExtensibleFields::ComponentFlowRate,value.get());
        } else {
          eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::ComponentFlowRate,"Autosize");
        }
        auto t_componentType = componentType(setpointComponent);
        switch(t_componentType)
        {
          case ComponentType::HEATING :
            eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::OperationType,"Heating");
            break;
          case ComponentType::COOLING :
            eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::OperationType,"Cooling");
            break;
          default :
            eg.setString(PlantEquipmentOperation_ComponentSetpointExtensibleFields::OperationType,"Dual");
            break;
        }
      }
    }
  };

  Schedule alwaysOn = plantLoop.model().alwaysOnDiscreteSchedule();
  bool applyDefault = true;

  // If any operation schemes are defined in the model then don't apply default operation schemes

  if( auto coolingLoadScheme = plantLoop.plantEquipmentOperationCoolingLoad() ) {
    auto _scheme = translateAndMapModelObject(coolingLoadScheme.get());
    OS_ASSERT(_scheme);
    auto eg = operationSchemes.pushExtensibleGroup();
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,_scheme->iddObject().name());
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,_scheme->name().get());
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

    applyDefault = false;
  } 

  if( auto heatingLoadScheme = plantLoop.plantEquipmentOperationHeatingLoad() ) {
    auto _scheme = translateAndMapModelObject(heatingLoadScheme.get());
    OS_ASSERT(_scheme);
    auto eg = operationSchemes.pushExtensibleGroup();
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,_scheme->iddObject().name());
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,_scheme->name().get());
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

    applyDefault = false;
  }

  if( auto primaryScheme = plantLoop.primaryPlantEquipmentOperationScheme() ) {
    auto _scheme = translateAndMapModelObject(primaryScheme.get());
    OS_ASSERT(_scheme);
    auto eg = operationSchemes.pushExtensibleGroup();
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,_scheme->iddObject().name());
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,_scheme->name().get());
    eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

    createSetpointOperationScheme(plantLoop);
    applyDefault = false;
  }

  if( applyDefault ) {
    // If we get here then there must not be any operation schemes defined in the model 
    // and we should go ahead and create default schemes.
    const auto & t_heatingComponents = heatingComponents( plantLoop );
    if( ! t_heatingComponents.empty() ) {
      IdfObject heatingOperation(IddObjectType::PlantEquipmentOperation_HeatingLoad);
      heatingOperation.setName(plantLoop.name().get() + " Heating Operation Scheme");
      m_idfObjects.push_back(heatingOperation);
      heatingOperation.clearExtensibleGroups();

      IdfObject plantEquipmentList(IddObjectType::PlantEquipmentList);
      plantEquipmentList.setName(plantLoop.name().get() + " Heating Equipment List");
      plantEquipmentList.clearExtensibleGroups();
      m_idfObjects.push_back(plantEquipmentList);

      IdfExtensibleGroup eg = heatingOperation.pushExtensibleGroup();
      eg.setDouble(PlantEquipmentOperation_HeatingLoadExtensibleFields::LoadRangeLowerLimit,0.0);
      eg.setDouble(PlantEquipmentOperation_HeatingLoadExtensibleFields::LoadRangeUpperLimit,1E9);
      eg.setString(PlantEquipmentOperation_HeatingLoadExtensibleFields::RangeEquipmentListName,plantEquipmentList.name().get());

      eg = operationSchemes.pushExtensibleGroup();
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,heatingOperation.iddObject().name());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,heatingOperation.name().get());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

      for( auto heatingComponent : t_heatingComponents ) {
        if( const auto & idfObject = translateAndMapModelObject(heatingComponent) ) {
          IdfExtensibleGroup eg = plantEquipmentList.pushExtensibleGroup();
          eg.setString(PlantEquipmentListExtensibleFields::EquipmentObjectType,idfObject->iddObject().name());
          eg.setString(PlantEquipmentListExtensibleFields::EquipmentName,idfObject->name().get());
        }
      }
    }

    const auto & t_coolingComponents = coolingComponents( plantLoop );
    if( ! t_coolingComponents.empty() ) {
      IdfObject coolingOperation(IddObjectType::PlantEquipmentOperation_CoolingLoad);
      coolingOperation.setName(plantLoop.name().get() + " Cooling Operation Scheme");
      m_idfObjects.push_back(coolingOperation);
      coolingOperation.clearExtensibleGroups();

      IdfObject plantEquipmentList(IddObjectType::PlantEquipmentList);
      plantEquipmentList.setName(plantLoop.name().get() + " Cooling Equipment List");
      plantEquipmentList.clearExtensibleGroups();
      m_idfObjects.push_back(plantEquipmentList);

      IdfExtensibleGroup eg = coolingOperation.pushExtensibleGroup();
      eg.setDouble(PlantEquipmentOperation_CoolingLoadExtensibleFields::LoadRangeLowerLimit,0.0);
      eg.setDouble(PlantEquipmentOperation_CoolingLoadExtensibleFields::LoadRangeUpperLimit,1E9);
      eg.setString(PlantEquipmentOperation_CoolingLoadExtensibleFields::RangeEquipmentListName,plantEquipmentList.name().get());

      eg = operationSchemes.pushExtensibleGroup();
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,coolingOperation.iddObject().name());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,coolingOperation.name().get());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

      for( auto coolingComponent : t_coolingComponents ) {
        if( const auto & idfObject = translateAndMapModelObject(coolingComponent) ) {
          IdfExtensibleGroup eg = plantEquipmentList.pushExtensibleGroup();
          eg.setString(PlantEquipmentListExtensibleFields::EquipmentObjectType,idfObject->iddObject().name());
          eg.setString(PlantEquipmentListExtensibleFields::EquipmentName,idfObject->name().get());
        }
      }
    }

    const auto & t_uncontrolledComponents = uncontrolledComponents( plantLoop );
    if( ! t_uncontrolledComponents.empty() ) {

      IdfObject uncontrolledOperation(IddObjectType::PlantEquipmentOperation_Uncontrolled);
      uncontrolledOperation.setName(plantLoop.name().get() + " Uncontrolled Operation Scheme");
      m_idfObjects.push_back(uncontrolledOperation);
      uncontrolledOperation.clearExtensibleGroups();

      IdfObject plantEquipmentList(IddObjectType::PlantEquipmentList);
      plantEquipmentList.setName(plantLoop.name().get() + " Uncontrolled Equipment List");
      plantEquipmentList.clearExtensibleGroups();
      m_idfObjects.push_back(plantEquipmentList);

      uncontrolledOperation.setString(PlantEquipmentOperation_UncontrolledFields::EquipmentListName,plantEquipmentList.name().get());

      auto eg = operationSchemes.pushExtensibleGroup();
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType,uncontrolledOperation.iddObject().name());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName,uncontrolledOperation.name().get());
      eg.setString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeScheduleName,alwaysOn.name().get());

      for( auto uncontrolledComponent : t_uncontrolledComponents ) {
        if( const auto & idfObject = translateAndMapModelObject(uncontrolledComponent) ) {
          IdfExtensibleGroup eg = plantEquipmentList.pushExtensibleGroup();
          eg.setString(PlantEquipmentListExtensibleFields::EquipmentObjectType,idfObject->iddObject().name());
          eg.setString(PlantEquipmentListExtensibleFields::EquipmentName,idfObject->name().get());
        }
      }
    }

    createSetpointOperationScheme(plantLoop);
  }

  return operationSchemes;
}
boost::optional<IdfObject> ForwardTranslator::translateFanConstantVolume( FanConstantVolume& modelObject )
{
  OptionalString s;
  OptionalDouble d;
  OptionalModelObject temp;

  // Create a new IddObjectType::Fan_ConstantVolume
  IdfObject idfObject(IddObjectType::Fan_ConstantVolume);

  ///////////////////////////////////////////////////////////////////////////
  // Field: Name ////////////////////////////////////////////////////////////
  s = modelObject.name();
  if(s)
  {
    idfObject.setName(*s);
  }
  ///////////////////////////////////////////////////////////////////////////


  // hook up required objects
  try {
    if( boost::optional<model::AirLoopHVAC> airLoopHVAC = modelObject.airLoopHVAC() )
    {
      Schedule sched = airLoopHVAC->availabilitySchedule();
      boost::optional<IdfObject> schedIdf = translateAndMapModelObject(sched);
      if( schedIdf )
      {
        idfObject.setString(Fan_ConstantVolumeFields::AvailabilityScheduleName,schedIdf->name().get());
      }
    }
    else
    {
      Schedule sched = modelObject.availabilitySchedule();
      translateAndMapModelObject(sched);
      idfObject.setString(Fan_ConstantVolumeFields::AvailabilityScheduleName,sched.name().get());
    }
  }
  catch (std::exception& e) {
    LOG(Error,"Could not translate " << modelObject.briefDescription() << ", because " 
        << e.what() << ".");
    return boost::none;
  }


  ///////////////////////////////////////////////////////////////////////////
  // Fan Efficiency /////////////////////////////////////////////////////////
  idfObject.setDouble(openstudio::Fan_ConstantVolumeFields::FanTotalEfficiency,modelObject.fanEfficiency());
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // Pressure Rise //////////////////////////////////////////////////////////
  idfObject.setDouble(openstudio::Fan_ConstantVolumeFields::PressureRise,modelObject.pressureRise());
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // Maximum Flow Rate //////////////////////////////////////////////////////
  d = modelObject.maximumFlowRate();
  if(d)
  {
    idfObject.setDouble(openstudio::Fan_ConstantVolumeFields::MaximumFlowRate,*d);
  }
  else
  {
    idfObject.setString(openstudio::Fan_ConstantVolumeFields::MaximumFlowRate,"AutoSize");
  }
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // Motor Efficiency ///////////////////////////////////////////////////////
  idfObject.setDouble(openstudio::Fan_ConstantVolumeFields::MotorEfficiency,modelObject.motorEfficiency());
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // Motor In Airstream Fraction ////////////////////////////////////////////
  idfObject.setDouble(openstudio::Fan_ConstantVolumeFields::MotorInAirstreamFraction,modelObject.motorInAirstreamFraction());
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // Air Inlet Node Name ////////////////////////////////////////////////////
  temp = modelObject.inletModelObject();
  if(temp)
  {
    s = temp->name();
    if(s)
    {
      idfObject.setString(openstudio::Fan_ConstantVolumeFields::AirInletNodeName,*s);
    }
  }
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // Air Outlet Node Name ///////////////////////////////////////////////////
  temp = modelObject.outletModelObject();
  if(temp)
  {
    s = temp->name();
    if(s)
    {
      idfObject.setString(openstudio::Fan_ConstantVolumeFields::AirOutletNodeName,*s);
    }
  }
  ///
  ////////////////////////////////////////////////////////////////////////

  m_idfObjects.push_back(idfObject);
  return boost::optional<IdfObject>(idfObject);
}
boost::optional<IdfObject> ForwardTranslator::translateDesignDay( DesignDay & modelObject )
{

  IdfObject idfObject( openstudio::IddObjectType::SizingPeriod_DesignDay);

  // Name
  idfObject.setName(modelObject.name().get());

  // Month
  idfObject.setInt(SizingPeriod_DesignDayFields::Month, modelObject.month());

  // Day of Month
  idfObject.setInt(SizingPeriod_DesignDayFields::DayofMonth, modelObject.dayOfMonth());

  // Day Type
  idfObject.setString(SizingPeriod_DesignDayFields::DayType, modelObject.dayType());

  // Maximum Dry-Bulb Temperature
  idfObject.setDouble(SizingPeriod_DesignDayFields::MaximumDryBulbTemperature, modelObject.maximumDryBulbTemperature());

  // Dry-Bulb Temperature Range Modifier Type
  std::string dryBulbTemperatureRangeModifierType = modelObject.dryBulbTemperatureRangeModifierType();
  idfObject.setString(SizingPeriod_DesignDayFields::DryBulbTemperatureRangeModifierType, dryBulbTemperatureRangeModifierType);

  // Daily Dry-Bulb Temperature Range
  if (!istringEqual(dryBulbTemperatureRangeModifierType, "DifferenceSchedule")){
    idfObject.setDouble(SizingPeriod_DesignDayFields::DailyDryBulbTemperatureRange, modelObject.dailyDryBulbTemperatureRange());
  }

  // Dry-Bulb Temperature Range Modifier Day Schedule Name
  if (istringEqual(dryBulbTemperatureRangeModifierType, "MultiplierSchedule") || istringEqual(dryBulbTemperatureRangeModifierType, "DifferenceSchedule")){
    boost::optional<IdfObject> idfSchedule;
    if( boost::optional<ScheduleDay> schedule = modelObject.dryBulbTemperatureRangeModifierSchedule() ){
      idfSchedule = translateAndMapModelObject(*schedule);
    }
    if (idfSchedule){
      idfObject.setString(SizingPeriod_DesignDayFields::DryBulbTemperatureRangeModifierDayScheduleName, idfSchedule->name().get());
    }else{
      LOG(Error, "Dry Bulb Temperature Range Modifier Day Schedule Name field is required but not found");
    }
  }

  // Humidity Condition Type
  std::string humidityIndicatingType = modelObject.humidityIndicatingType();
  if (istringEqual(humidityIndicatingType, "Schedule")){
    humidityIndicatingType = "RelativeHumiditySchedule";
  }
  idfObject.setString(SizingPeriod_DesignDayFields::HumidityConditionType, humidityIndicatingType);

  // Wetbulb or DewPoint at Maximum Dry-Bulb
  if (istringEqual(humidityIndicatingType, "Wetbulb") ||
      istringEqual(humidityIndicatingType, "Dewpoint") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileMultiplierSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDifferenceSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDefaultMultipliers")){
    // units for this field are C
    idfObject.setDouble(SizingPeriod_DesignDayFields::WetbulborDewPointatMaximumDryBulb, modelObject.humidityIndicatingConditionsAtMaximumDryBulb());
  }

  // Humidity Condition Day Schedule Name
  if (istringEqual(humidityIndicatingType, "RelativeHumiditySchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileMultiplierSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDifferenceSchedule") ||
      istringEqual(humidityIndicatingType, "RelativeHumiditySchedule")){
    if( boost::optional<ScheduleDay> schedule = modelObject.humidityIndicatingDaySchedule() ){
      idfObject.setString(SizingPeriod_DesignDayFields::HumidityConditionDayScheduleName, schedule->name().get());
    }else{
      LOG(Error, "Humidity Condition Day Schedule Name field is required but not found");
    }
  }

  // Humidity Ratio at Maximum Dry-Bulb
  if (istringEqual(humidityIndicatingType, "HumidityRatio")){
    // units for this field are kgWater/kgDryAir
    idfObject.setDouble(SizingPeriod_DesignDayFields::HumidityRatioatMaximumDryBulb, modelObject.humidityIndicatingConditionsAtMaximumDryBulb());
  }

  // Enthalpy at Maximum Dry-Bulb
  if (istringEqual(humidityIndicatingType, "Enthalpy")){
    // units for this field are J/kg
    idfObject.setDouble(SizingPeriod_DesignDayFields::EnthalpyatMaximumDryBulb, modelObject.humidityIndicatingConditionsAtMaximumDryBulb());
  }

  // Daily Wet-Bulb Temperature Range
  if (istringEqual(humidityIndicatingType, "WetbulbProfileMultiplierSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDefaultMultipliers")){
    if ( OptionalDouble d = modelObject.dailyWetBulbTemperatureRange()) {
      idfObject.setDouble(SizingPeriod_DesignDayFields::DailyWetBulbTemperatureRange, *d);
    }else{
      LOG(Error, "Daily Wet Bulb Temperature Range field is required but not found");
    }
  }

  // Barometric Pressure
  idfObject.setDouble(SizingPeriod_DesignDayFields::BarometricPressure, modelObject.barometricPressure());

  // Site Wind Speed
  idfObject.setDouble(SizingPeriod_DesignDayFields::WindSpeed, modelObject.windSpeed());

  // Site Wind Direction
  idfObject.setDouble(SizingPeriod_DesignDayFields::WindDirection,modelObject.windDirection());

  // Rain Indicator
  if( modelObject.rainIndicator() ){
    idfObject.setString(SizingPeriod_DesignDayFields::RainIndicator, "Yes");
  }else{
    idfObject.setString(SizingPeriod_DesignDayFields::RainIndicator, "No");
  }

  // Snow Indicator
  if( modelObject.snowIndicator() ){
    idfObject.setString(SizingPeriod_DesignDayFields::SnowIndicator, "Yes");
  }else{
    idfObject.setString(SizingPeriod_DesignDayFields::SnowIndicator, "No");
  }

  // Site Daylight Saving Time Status
  if( modelObject.daylightSavingTimeIndicator() ){
    idfObject.setString(SizingPeriod_DesignDayFields::DaylightSavingTimeIndicator, "Yes");
  }else{
    idfObject.setString(SizingPeriod_DesignDayFields::DaylightSavingTimeIndicator, "No");
  }

  // Solar Model Indicator
  std::string solarModelIndicator = modelObject.solarModelIndicator();
  idfObject.setString(SizingPeriod_DesignDayFields::SolarModelIndicator, solarModelIndicator);

  // Beam Solar Day Schedule Name and Site Diffuse Solar Radiation Rate per Area Day Schedule Name
  if (istringEqual(solarModelIndicator, "Schedule")){

    boost::optional<IdfObject> idfSchedule;

    // Beam Solar Day Schedule Name
    if( boost::optional<ScheduleDay> schedule = modelObject.beamSolarDaySchedule() ){
      idfSchedule = translateAndMapModelObject(*schedule);
    }
    if (idfSchedule){
      idfObject.setString(SizingPeriod_DesignDayFields::BeamSolarDayScheduleName, idfSchedule->name().get());
    }else{
      LOG(Error, "Beam Solar Day Schedule Name field is required but not found");
    }

    idfSchedule.reset();

    // Site Diffuse Solar Radiation Rate per Area Day Schedule Name
    if( boost::optional<ScheduleDay> schedule = modelObject.diffuseSolarDaySchedule() ){
      idfSchedule = translateAndMapModelObject(*schedule);
    }
    if (idfSchedule) {
      idfObject.setString(SizingPeriod_DesignDayFields::DiffuseSolarDayScheduleName, idfSchedule->name().get());
    }else{
      LOG(Error, "Site Diffuse Solar Radiation Rate per Area Day Schedule Name field is required but not found");
    }
  }

  if (istringEqual(solarModelIndicator, "ASHRAETau")){
    // ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
    idfObject.setDouble(SizingPeriod_DesignDayFields::ASHRAEClearSkyOpticalDepthforBeamIrradiance_taub_ , modelObject.ashraeTaub());

    // ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
    idfObject.setDouble(SizingPeriod_DesignDayFields::ASHRAEClearSkyOpticalDepthforDiffuseIrradiance_taud_, modelObject.ashraeTaud());
  }

  // Sky Clearness
  if (istringEqual(solarModelIndicator, "ASHRAEClearSky") || istringEqual(solarModelIndicator, "ZhangHuang")){
    idfObject.setDouble(SizingPeriod_DesignDayFields::SkyClearness, modelObject.skyClearness());
  }

  m_idfObjects.push_back(idfObject);

  return boost::optional<IdfObject>(idfObject);
}
boost::optional<IdfObject> ForwardTranslator::translateSizingZone( SizingZone & modelObject )
{
  boost::optional<std::string> s;
  boost::optional<double> value;

  IdfObject idfObject(IddObjectType::Sizing_Zone);

  m_idfObjects.push_back(idfObject);

  // ZoneorZoneListName

  model::ThermalZone thermalZone = modelObject.thermalZone();

  boost::optional<IdfObject> _thermalZone = translateAndMapModelObject(thermalZone);

  boost::optional<std::string> name;

  if( _thermalZone )
  {
    name = _thermalZone->name();
  }

  if( name )
  {
    idfObject.setString(Sizing_ZoneFields::ZoneorZoneListName,name.get());
  }

  // ZoneCoolingDesignSupplyAirTemperatureInputMethod

  idfObject.setString(Sizing_ZoneFields::ZoneCoolingDesignSupplyAirTemperatureInputMethod,"SupplyAirTemperature");

  // ZoneCoolingDesignSupplyAirTemperature

  value = modelObject.zoneCoolingDesignSupplyAirTemperature();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::ZoneCoolingDesignSupplyAirTemperature,value.get());
  }

  // ZoneHeatingDesignSupplyAirTemperatureInputMethod

  idfObject.setString(Sizing_ZoneFields::ZoneHeatingDesignSupplyAirTemperatureInputMethod,"SupplyAirTemperature");

  // ZoneHeatingDesignSupplyAirTemperature

  value = modelObject.zoneHeatingDesignSupplyAirTemperature();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::ZoneHeatingDesignSupplyAirTemperature,value.get());
  }

  // ZoneCoolingDesignSupplyAirHumidityRatio
  
  value = modelObject.zoneCoolingDesignSupplyAirHumidityRatio();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::ZoneCoolingDesignSupplyAirHumidityRatio,value.get());
  }

  // ZoneHeatingDesignSupplyAirHumidityRatio
  
  value = modelObject.zoneHeatingDesignSupplyAirHumidityRatio();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::ZoneHeatingDesignSupplyAirHumidityRatio,value.get());
  }

  //  ((DesignSpecificationOutdoorAirObjectName)(Design Specification Outdoor Air Object Name))
  
  // ZoneHeatingSizingFactor 
  
  value = modelObject.zoneHeatingSizingFactor();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::ZoneHeatingSizingFactor,value.get());
  }
  
  // ZoneCoolingSizingFactor 
  
  value = modelObject.zoneCoolingSizingFactor();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::ZoneCoolingSizingFactor,value.get());
  }

  // CoolingDesignAirFlowMethod
  
  s = modelObject.coolingDesignAirFlowMethod();
  if( s )
  {
    idfObject.setString(Sizing_ZoneFields::CoolingDesignAirFlowMethod,s.get());
  }

  // CoolingDesignAirFlowRate
  
  value = modelObject.coolingDesignAirFlowRate();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::CoolingDesignAirFlowRate,value.get());
  }

  // CoolingMinimumAirFlowperZoneFloorArea

  value = modelObject.coolingMinimumAirFlowperZoneFloorArea();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::CoolingMinimumAirFlowperZoneFloorArea,value.get());
  }

  // CoolingMinimumAirFlow

  value = modelObject.coolingMinimumAirFlow();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::CoolingMinimumAirFlow,value.get());
  }

  // CoolingMinimumAirFlowFraction

  value = modelObject.coolingMinimumAirFlowFraction();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::CoolingMinimumAirFlowFraction,value.get());
  }

  // HeatingDesignAirFlowMethod
  
  s = modelObject.heatingDesignAirFlowMethod();
  if( s )
  {
    idfObject.setString(Sizing_ZoneFields::HeatingDesignAirFlowMethod,s.get());
  }

  // HeatingDesignAirFlowRate
  
  value = modelObject.heatingDesignAirFlowRate();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::HeatingDesignAirFlowRate,value.get());
  }

  // HeatingMaximumAirFlowperZoneFloorArea

  value = modelObject.heatingMaximumAirFlowperZoneFloorArea();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::HeatingMaximumAirFlowperZoneFloorArea,value.get());
  }

  // HeatingMaximumAirFlow

  value = modelObject.heatingMaximumAirFlow();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::HeatingMaximumAirFlow,value.get());
  }

  // HeatingMaximumAirFlowFraction

  value = modelObject.heatingMaximumAirFlowFraction();
  if( value )
  {
    idfObject.setDouble(Sizing_ZoneFields::HeatingMaximumAirFlowFraction,value.get());
  }

  // DesignZoneAirDistributionEffectivenessinCoolingMode
  // DesignZoneAirDistributionEffectivenessinHeatingMode

  boost::optional<double> designZoneAirDistributionEffectivenessinCoolingMode = 
                            modelObject.designZoneAirDistributionEffectivenessinCoolingMode();
  boost::optional<double> designZoneAirDistributionEffectivenessinHeatingMode = 
                            modelObject.designZoneAirDistributionEffectivenessinHeatingMode();

  std::string designSpecificationZoneAirDistributionName;
  if( name )
  {
    designSpecificationZoneAirDistributionName = name.get() +  " Design Spec Zone Air Dist";
  }

  if( designZoneAirDistributionEffectivenessinCoolingMode ||
      designZoneAirDistributionEffectivenessinHeatingMode )
  {
    IdfObject _designSpecification_ZoneAirDistribution(IddObjectType::DesignSpecification_ZoneAirDistribution);

    if( name )
    {
      _designSpecification_ZoneAirDistribution.setName(designSpecificationZoneAirDistributionName);
    }

    m_idfObjects.push_back(_designSpecification_ZoneAirDistribution);

    if( designZoneAirDistributionEffectivenessinCoolingMode )
    {
      _designSpecification_ZoneAirDistribution.setDouble(
        DesignSpecification_ZoneAirDistributionFields::ZoneAirDistributionEffectivenessinCoolingMode,
        designZoneAirDistributionEffectivenessinCoolingMode.get() );
    }

    if( designZoneAirDistributionEffectivenessinHeatingMode )
    {
      _designSpecification_ZoneAirDistribution.setDouble(
        DesignSpecification_ZoneAirDistributionFields::ZoneAirDistributionEffectivenessinHeatingMode,
        designZoneAirDistributionEffectivenessinHeatingMode.get() );
    }

    idfObject.setString(Sizing_ZoneFields::DesignSpecificationZoneAirDistributionObjectName,_designSpecification_ZoneAirDistribution.name().get());
  }


  // Add ThermalZone and associated design objects to ControllerMechanicalVentilation.
  // This would be done in forwardTranslateControllerMechanicalVentilation except doing it here maintains proper order of the idf file.
  
  boost::optional<model::ControllerMechanicalVentilation> controllerMechanicalVentilation;
  boost::optional<IdfObject> _controllerMechanicalVentilation;

  if( boost::optional<model::AirLoopHVAC> airLoopHVAC = thermalZone.airLoopHVAC() )
  {
    if( boost::optional<model::AirLoopHVACOutdoorAirSystem> oaSystem = airLoopHVAC->airLoopHVACOutdoorAirSystem() )
    {
      model::ControllerOutdoorAir controllerOutdoorAir = oaSystem->getControllerOutdoorAir(); 

      controllerMechanicalVentilation = controllerOutdoorAir.controllerMechanicalVentilation();
    }
  }
  
  if( controllerMechanicalVentilation )
  {
    _controllerMechanicalVentilation = translateAndMapModelObject(controllerMechanicalVentilation.get());
  }

  if( _controllerMechanicalVentilation && _thermalZone )
  {
    IdfExtensibleGroup eg = _controllerMechanicalVentilation->pushExtensibleGroup();

    // Thermal Zone Name
    eg.setString(Controller_MechanicalVentilationExtensibleFields::ZoneName,_thermalZone->name().get());

    // DesignSpecificationOutdoorAir
    std::vector<model::Space> spaces = thermalZone.spaces();

    if( spaces.size() > 0 )
    {
      if( boost::optional<model::DesignSpecificationOutdoorAir> designOASpec = spaces.front().designSpecificationOutdoorAir()  )
      {
        if( boost::optional<IdfObject> _designOASpec = translateAndMapModelObject(designOASpec.get()) )
        {
          eg.setString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationOutdoorAirObjectName,_designOASpec->name().get());
        }
      }
    }

    // DesignSpecificationZoneAirDistributionObjectName
    if( _thermalZone )
    {
      eg.setString(Controller_MechanicalVentilationExtensibleFields::DesignSpecificationZoneAirDistributionObjectName,
                   designSpecificationZoneAirDistributionName);
    }
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACPackagedTerminalAirConditioner( 
    ZoneHVACPackagedTerminalAirConditioner & modelObject )
{
  boost::optional<std::string> s;
  boost::optional<double> value;

  IdfObject idfObject(IddObjectType::ZoneHVAC_PackagedTerminalAirConditioner);

  m_idfObjects.push_back(idfObject);

  // Name

  std::string baseName = modelObject.name().get();

  idfObject.setName(baseName);

  std::string mixedAirNodeName = baseName + " Mixed Air Node";

  std::string coolingCoilOutletNodeName = baseName + " Cooling Coil Outlet Node";

  std::string heatingCoilOutletNodeName = baseName + " Heating Coil Outlet Node";

  std::string reliefAirNodeName = baseName + " Relief Air Node";

  std::string oaNodeName = baseName + " OA Node";

  // AvailabilityScheduleName

  if( boost::optional<Schedule> schedule = modelObject.availabilitySchedule() )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
    {
      idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::AvailabilityScheduleName,_schedule->name().get());
    }
  }

  // AirInletNodeName

  boost::optional<std::string> airInletNodeName;

  if( boost::optional<Node> node = modelObject.inletNode() )
  {
    if( (s = node->name()) )
    {
      airInletNodeName = s;

      idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::AirInletNodeName,s.get());
    }
  }

  // AirOutletNodeName

  boost::optional<std::string> airOutletNodeName;

  if( boost::optional<Node> node = modelObject.outletNode() )
  {
    if( (s = node->name()) )
    {
      airOutletNodeName = s;

      idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::AirOutletNodeName,s.get());
    }
  }

  // OutdoorAirMixerObjectType
  idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirMixerObjectType,
                      modelObject.outdoorAirMixerObjectType());

  // OutdoorAirMixerName
  
  std::string oaMixerName = modelObject.name().get() + " OA Mixer";  

  idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirMixerName,oaMixerName);

  IdfObject _outdoorAirMixer(IddObjectType::OutdoorAir_Mixer);
  _outdoorAirMixer.setName(oaMixerName);
  m_idfObjects.push_back(_outdoorAirMixer);

  _outdoorAirMixer.setString(OutdoorAir_MixerFields::MixedAirNodeName,mixedAirNodeName);

  _outdoorAirMixer.setString(OutdoorAir_MixerFields::OutdoorAirStreamNodeName,oaNodeName);

  IdfObject _oaNodeList(openstudio::IddObjectType::OutdoorAir_NodeList);
  _oaNodeList.setString(0,oaNodeName);
  m_idfObjects.push_back(_oaNodeList);

  _outdoorAirMixer.setString(OutdoorAir_MixerFields::ReliefAirStreamNodeName,reliefAirNodeName);

  if(airInletNodeName)
  {
    _outdoorAirMixer.setString(OutdoorAir_MixerFields::ReturnAirStreamNodeName,airInletNodeName.get());
  }

  // SupplyAirFlowRateDuringCoolingOperation

  if( modelObject.isSupplyAirFlowRateDuringCoolingOperationAutosized() )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFlowRateDuringCoolingOperation,"Autosize");
  }
  else if( (value = modelObject.supplyAirFlowRateDuringCoolingOperation()) )
  {
    idfObject.setDouble(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFlowRateDuringCoolingOperation,value.get());
  }

  // SupplyAirFlowRateDuringHeatingOperation

  if( modelObject.isSupplyAirFlowRateDuringHeatingOperationAutosized() )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFlowRateDuringHeatingOperation,"Autosize");
  }
  else if( (value = modelObject.supplyAirFlowRateDuringHeatingOperation()) )
  {
    idfObject.setDouble(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFlowRateDuringHeatingOperation,value.get());
  }

  // SupplyAirFlowRateWhenNoCoolingorHeatingisNeeded

  if( modelObject.isSupplyAirFlowRateWhenNoCoolingorHeatingisNeededAutosized() )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFlowRateWhenNoCoolingorHeatingisNeeded,"Autosize");
  } 
  else if( (value = modelObject.supplyAirFlowRateWhenNoCoolingorHeatingisNeeded()) )
  {
    idfObject.setDouble(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFlowRateWhenNoCoolingorHeatingisNeeded,value.get());
  }

  // OutdoorAirFlowRateDuringCoolingOperation
  
  if( modelObject.isOutdoorAirFlowRateDuringCoolingOperationAutosized() )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirFlowRateDuringCoolingOperation,"Autosize");
  }
  else if( (value = modelObject.outdoorAirFlowRateDuringCoolingOperation()) )
  {
    idfObject.setDouble(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirFlowRateDuringCoolingOperation,value.get());
  }

  // OutdoorAirFlowRateDuringHeatingOperation
  
  if( modelObject.isOutdoorAirFlowRateDuringHeatingOperationAutosized() )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirFlowRateDuringHeatingOperation,"Autosize");
  }
  else if( (value = modelObject.outdoorAirFlowRateDuringHeatingOperation()) )
  {
    idfObject.setDouble(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirFlowRateDuringHeatingOperation,value.get());
  }

  // OutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded
  
  if( modelObject.isOutdoorAirFlowRateWhenNoCoolingorHeatingisNeededAutosized() )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded,"Autosize");
  }
  else if( (value = modelObject.outdoorAirFlowRateWhenNoCoolingorHeatingisNeeded()) )
  {
    idfObject.setDouble(ZoneHVAC_PackagedTerminalAirConditionerFields::OutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded,value.get());
  }

  // SupplyAirFanObjectType

  HVACComponent supplyAirFan = modelObject.supplyAirFan();

  if( boost::optional<IdfObject> _supplyAirFan = translateAndMapModelObject(supplyAirFan) )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFanObjectType,_supplyAirFan->iddObject().name() );

    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFanName,_supplyAirFan->name().get());

    if( airOutletNodeName )
    {
      if( _supplyAirFan->iddObject().type() == IddObjectType::Fan_ConstantVolume )
      {
        _supplyAirFan->setString(Fan_ConstantVolumeFields::AirInletNodeName,heatingCoilOutletNodeName);

        _supplyAirFan->setString(Fan_ConstantVolumeFields::AirOutletNodeName,airOutletNodeName.get());
      }
    }
  }

  // HeatingCoilObjectType
  
  HVACComponent heatingCoil = modelObject.heatingCoil();

  if( boost::optional<IdfObject> _heatingCoil = translateAndMapModelObject(heatingCoil) )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::HeatingCoilObjectType,_heatingCoil->iddObject().name() );

    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::HeatingCoilName,_heatingCoil->name().get() ); 

    if( _heatingCoil->iddObject().type() == IddObjectType::Coil_Heating_Water )
    {
      _heatingCoil->setString(Coil_Heating_WaterFields::AirInletNodeName,coolingCoilOutletNodeName);

      _heatingCoil->setString(Coil_Heating_WaterFields::AirOutletNodeName,heatingCoilOutletNodeName);
    }
    else if( _heatingCoil->iddObject().type() == IddObjectType::Coil_Heating_Gas )
    {
      _heatingCoil->setString(Coil_Heating_GasFields::AirInletNodeName,coolingCoilOutletNodeName);

      _heatingCoil->setString(Coil_Heating_GasFields::AirOutletNodeName,heatingCoilOutletNodeName);
    }
    else if( _heatingCoil->iddObject().type() == IddObjectType::Coil_Heating_Electric )
    {
      _heatingCoil->setString(Coil_Heating_ElectricFields::AirInletNodeName,coolingCoilOutletNodeName);

      _heatingCoil->setString(Coil_Heating_ElectricFields::AirOutletNodeName,heatingCoilOutletNodeName);
    }
  }

  // CoolingCoilObjectType

  HVACComponent coolingCoil = modelObject.coolingCoil();

  boost::optional<IdfObject> _coolingCoil;

  if( boost::optional<CoilCoolingDXSingleSpeed> dxCoil = coolingCoil.optionalCast<CoilCoolingDXSingleSpeed>() )
  {
    _coolingCoil = translateCoilCoolingDXSingleSpeedWithoutUnitary(dxCoil.get());

    m_map.insert(std::make_pair(coolingCoil.handle(),_coolingCoil.get()));
  }
  else
  {
    _coolingCoil = translateAndMapModelObject(coolingCoil);
  }

  if( _coolingCoil )
  {
    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::CoolingCoilObjectType,_coolingCoil->iddObject().name() );

    idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::CoolingCoilName,_coolingCoil->name().get() );

    if( _coolingCoil->iddObject().type() == IddObjectType::Coil_Cooling_DX_SingleSpeed )
    {
      _coolingCoil->setString(Coil_Cooling_DX_SingleSpeedFields::AirInletNodeName,mixedAirNodeName);

      _coolingCoil->setString(Coil_Cooling_DX_SingleSpeedFields::AirOutletNodeName,coolingCoilOutletNodeName);
    }
  }

  // FanPlacement

  idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::FanPlacement,modelObject.fanPlacement());

  // SupplyAirFanOperatingModeScheduleName
  
  if( boost::optional<Schedule> schedule = modelObject.supplyAirFanOperatingModeSchedule() )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
    {
      idfObject.setString(ZoneHVAC_PackagedTerminalAirConditionerFields::SupplyAirFanOperatingModeScheduleName,_schedule->name().get());
    }
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateAirTerminalSingleDuctConstantVolumeReheat( AirTerminalSingleDuctConstantVolumeReheat& modelObject )
{
  OptionalModelObject temp;
  OptionalString optS;
  boost::optional<std::string> s;

  std::string baseName = modelObject.name().get();

  IdfObject _airDistributionUnit(openstudio::IddObjectType::ZoneHVAC_AirDistributionUnit);
  _airDistributionUnit.setName("ADU " + baseName ); //ADU: Air Distribution Unit

  IdfObject idfObject(openstudio::IddObjectType::AirTerminal_SingleDuct_ConstantVolume_Reheat);

  idfObject.setName(baseName);

  HVACComponent coil = modelObject.reheatCoil();

  m_idfObjects.push_back(_airDistributionUnit);

  m_idfObjects.push_back(idfObject);

  boost::optional<IdfObject> _reheatCoil = translateAndMapModelObject(coil);

  if( _reheatCoil && _reheatCoil->name() )
  {
    boost::optional<std::string> inletNodeName;
    boost::optional<std::string> outletNodeName;

    if( boost::optional<ModelObject> inletModelObject = modelObject.inletModelObject() )
    {
      if( boost::optional<Node> inletNode = inletModelObject->optionalCast<Node>() )
      {
        inletNodeName = inletNode->name().get();
      }
    }

    if( boost::optional<ModelObject> outletModelObject = modelObject.outletModelObject() )
    {
      if( boost::optional<Node> outletNode = outletModelObject->optionalCast<Node>() )
      {
        outletNodeName = outletNode->name().get();
      }
    }

    // Reheat Coil Name
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::ReheatCoilName,_reheatCoil->name().get());

    // Reheat Coil Type
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::ReheatCoilObjectType,_reheatCoil->iddObject().name());

    if( outletNodeName && inletNodeName )
    {
      if( _reheatCoil->iddObject().type() == IddObjectType::Coil_Heating_Gas )
      {
        _reheatCoil->setString(Coil_Heating_GasFields::AirInletNodeName,inletNodeName.get());
        _reheatCoil->setString(Coil_Heating_GasFields::AirOutletNodeName,outletNodeName.get());
      }
      else if( _reheatCoil->iddObject().type() == IddObjectType::Coil_Heating_Electric )
      {
        _reheatCoil->setString(Coil_Heating_ElectricFields::AirInletNodeName,inletNodeName.get());
        _reheatCoil->setString(Coil_Heating_ElectricFields::AirOutletNodeName,outletNodeName.get());
      }
      else if( _reheatCoil->iddObject().type() == IddObjectType::Coil_Heating_Water )
      {
        _reheatCoil->setString(Coil_Heating_WaterFields::AirInletNodeName,inletNodeName.get());
        _reheatCoil->setString(Coil_Heating_WaterFields::AirOutletNodeName,outletNodeName.get());
      }

      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::AirOutletNodeName,outletNodeName.get());
      idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::AirInletNodeName,inletNodeName.get());
    }
  }
  else
  {
    LOG(Error,modelObject.briefDescription() << ": Could not translate heating coil");

    return boost::none;
  }

  // AvailabilityScheduleName
  Schedule availabilitySchedule = modelObject.availabilitySchedule();

  boost::optional<IdfObject> _availabilitySchedule = translateAndMapModelObject(availabilitySchedule);

  if( _availabilitySchedule && _availabilitySchedule->name() )
  {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::AvailabilityScheduleName,_availabilitySchedule->name().get());
  }

  // MaximumAirFlowRate
  boost::optional<double> value = modelObject.maximumAirFlowRate();
  if( value )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumAirFlowRate,value.get());
  }
  else if( modelObject.isMaximumAirFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumAirFlowRate,"Autosize");
  }

  // HotWaterorSteamInletNodeName
  idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::HotWaterorSteamInletNodeName,"");

  // MaximumHotWaterOrSteamFlowRate
  value = modelObject.maximumHotWaterorSteamFlowRate();
  
  if( value )
  {
    idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumHotWaterorSteamFlowRate,value.get());
  }
  else if( modelObject.isMaximumHotWaterorSteamFlowRateAutosized() )
  {
    idfObject.setString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumHotWaterorSteamFlowRate,"Autosize");
  }

  // MinimumHotWaterOrSteamFlowRate
  value = modelObject.minimumHotWaterorSteamFlowRate();
  idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MinimumHotWaterorSteamFlowRate,value.get());

  // ConvergenceTolerance
  value = modelObject.convergenceTolerance();
  idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::ConvergenceTolerance,value.get());

  // MaximumReheatAirTemperature
  value = modelObject.maximumReheatAirTemperature();
  idfObject.setDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumReheatAirTemperature,value.get());

  // Populate fields for AirDistributionUnit
  if( boost::optional<ModelObject> outletNode = modelObject.outletModelObject() )
  {
    _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirDistributionUnitOutletNodeName,outletNode->name().get());
  }
  _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirTerminalObjectType,idfObject.iddObject().name());
  _airDistributionUnit.setString(ZoneHVAC_AirDistributionUnitFields::AirTerminalName,idfObject.name().get());

  return _airDistributionUnit;
}
boost::optional<IdfObject> ForwardTranslator::translateCoilHeatingWater( CoilHeatingWater & modelObject )
{
  boost::optional<std::string> s;
  boost::optional<double> value;

  IdfObject idfObject(IddObjectType::Coil_Heating_Water);

  m_idfObjects.push_back(idfObject);

  s = modelObject.name();
  if( s )
  {
    idfObject.setName(*s);
  }

  Schedule sched = modelObject.availableSchedule();
  boost::optional<IdfObject> _sched = translateAndMapModelObject(sched);
  if( _sched )
  {
    idfObject.setString(Coil_Heating_WaterFields::AvailabilityScheduleName,
                        _sched->name().get() );
  }

  // UFactorTimesAreaValue

  if( modelObject.isUFactorTimesAreaValueAutosized() )
  {
    idfObject.setString(Coil_Heating_WaterFields::UFactorTimesAreaValue,"Autosize");
  }
  else if( (value = modelObject.uFactorTimesAreaValue()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::UFactorTimesAreaValue,value.get());
  }

  // MaximumWaterFlowRate

  if( modelObject.isMaximumWaterFlowRateAutosized() )
  {
    idfObject.setString(Coil_Heating_WaterFields::MaximumWaterFlowRate,"Autosize");
  }
  else if( (value = modelObject.maximumWaterFlowRate()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::MaximumWaterFlowRate,value.get());
  }

  // WaterInletNodeName

  if( boost::optional<ModelObject> mo = modelObject.waterInletModelObject() )
  {
    if( boost::optional<Node> node = mo->optionalCast<Node>() )
    {
      idfObject.setString(Coil_Heating_WaterFields::WaterInletNodeName,node->name().get());
    }
  }

  // WaterOutletNodeName

  if( boost::optional<ModelObject> mo = modelObject.waterOutletModelObject() )
  {
    if( boost::optional<Node> node = mo->optionalCast<Node>() )
    {
      idfObject.setString(Coil_Heating_WaterFields::WaterOutletNodeName,node->name().get());
    }
  }

  // AirInletNodeName

  if( boost::optional<ModelObject> mo = modelObject.airInletModelObject() )
  {
    if( boost::optional<Node> node = mo->optionalCast<Node>() )
    {
      idfObject.setString(Coil_Heating_WaterFields::AirInletNodeName,node->name().get());
    }
  }

  // AirOutletNodeName

  if( boost::optional<ModelObject> mo = modelObject.airOutletModelObject() )
  {
    if( boost::optional<Node> node = mo->optionalCast<Node>() )
    {
      idfObject.setString(Coil_Heating_WaterFields::AirOutletNodeName,node->name().get());
    }
  }

  // PerformanceInputMethod

  s = modelObject.performanceInputMethod();
  if( s )
  {
    idfObject.setString(Coil_Heating_WaterFields::PerformanceInputMethod,s.get());
  }

  // RatedCapacity

  if( modelObject.isRatedCapacityAutosized() )
  {
    idfObject.setString(Coil_Heating_WaterFields::RatedCapacity,"Autosize");
  }
  else if( (value = modelObject.ratedCapacity()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::RatedCapacity,value.get());
  }

  // RatedInletWaterTemperature

  if( (value = modelObject.ratedInletWaterTemperature()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::RatedInletWaterTemperature,value.get());
  }

  // RatedInletAirTemperature

  if( (value = modelObject.ratedInletAirTemperature()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::RatedInletAirTemperature,value.get());
  }

  // RatedOutletWaterTemperature

  if( (value = modelObject.ratedOutletWaterTemperature()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::RatedOutletWaterTemperature,value.get());
  }

  // RatedOutletAirTemperature

  if( (value = modelObject.ratedOutletAirTemperature()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::RatedOutletAirTemperature,value.get());
  }

  // RatedRatioforAirandWaterConvection

  if( (value = modelObject.ratedRatioForAirAndWaterConvection()) )
  {
    idfObject.setDouble(Coil_Heating_WaterFields::RatedRatioforAirandWaterConvection,value.get());
  }
  
  return boost::optional<IdfObject>(idfObject);
}
boost::optional<IdfObject> ForwardTranslator::translateCoilWaterHeatingAirToWaterHeatPump( 
    CoilWaterHeatingAirToWaterHeatPump & modelObject)
{
  IdfObject idfObject(IddObjectType::Coil_WaterHeating_AirToWaterHeatPump);
  m_idfObjects.push_back(idfObject);

  // Name
  if( auto s = modelObject.name() ) {
    idfObject.setName(*s);
  }

  {
    auto value = modelObject.ratedHeatingCapacity();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedHeatingCapacity,value);
  }

  {
    auto value = modelObject.ratedCOP();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedCOP,value);
  }

  {
    auto value = modelObject.ratedSensibleHeatRatio();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedSensibleHeatRatio,value);
  }

  {
    auto value = modelObject.ratedEvaporatorInletAirDryBulbTemperature();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedEvaporatorInletAirDryBulbTemperature,value);
  }

  {
    auto value = modelObject.ratedEvaporatorInletAirWetBulbTemperature();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedEvaporatorInletAirWetBulbTemperature,value);
  }

  {
    auto value = modelObject.ratedCondenserInletWaterTemperature();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedCondenserInletWaterTemperature,value);
  }

  if( modelObject.isRatedEvaporatorAirFlowRateAutosized() ) {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedEvaporatorAirFlowRate,"Autosize");
  } else if( auto value = modelObject.ratedEvaporatorAirFlowRate() ) {
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedEvaporatorAirFlowRate,value.get());
  }

  if( modelObject.isRatedCondenserWaterFlowRateAutosized() ) {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedCondenserWaterFlowRate,"Autosize");
  } else if( auto value = modelObject.ratedCondenserWaterFlowRate() ) {
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::RatedCondenserWaterFlowRate,value.get());
  }

  if( modelObject.evaporatorFanPowerIncludedinRatedCOP() ) {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::EvaporatorFanPowerIncludedinRatedCOP,"Yes"); 
  } else {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::EvaporatorFanPowerIncludedinRatedCOP,"No");
  }

  if( modelObject.condenserPumpPowerIncludedinRatedCOP() ) {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::CondenserPumpPowerIncludedinRatedCOP,"Yes");
  } else {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::CondenserPumpPowerIncludedinRatedCOP,"No");
  }

  if( modelObject.condenserPumpHeatIncludedinRatedHeatingCapacityandRatedCOP() ) {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::CondenserPumpHeatIncludedinRatedHeatingCapacityandRatedCOP,"Yes");
  } else {
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::CondenserPumpHeatIncludedinRatedHeatingCapacityandRatedCOP,"No");
  }

  {
    auto value = modelObject.condenserWaterPumpPower();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::CondenserWaterPumpPower,value);
  }

  {
    auto value = modelObject.fractionofCondenserPumpHeattoWater();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::FractionofCondenserPumpHeattoWater,value);
  }

  {
    auto value = modelObject.crankcaseHeaterCapacity();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::CrankcaseHeaterCapacity,value);
  }

  {
    auto value = modelObject.maximumAmbientTemperatureforCrankcaseHeaterOperation();
    idfObject.setDouble(Coil_WaterHeating_AirToWaterHeatPumpFields::MaximumAmbientTemperatureforCrankcaseHeaterOperation,value);
  }

  {
    auto value = modelObject.evaporatorAirTemperatureTypeforCurveObjects();
    idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::EvaporatorAirTemperatureTypeforCurveObjects,value);
  }

  {
    auto curve = modelObject.heatingCapacityFunctionofTemperatureCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::HeatingCapacityFunctionofTemperatureCurveName,idf->name().get());
    }
  }

  {
    auto curve = modelObject.heatingCapacityFunctionofAirFlowFractionCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::HeatingCapacityFunctionofAirFlowFractionCurveName,idf->name().get());
    }
  }

  {
    auto curve = modelObject.heatingCapacityFunctionofWaterFlowFractionCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::HeatingCapacityFunctionofWaterFlowFractionCurveName,idf->name().get());
    }
  }

  {
    auto curve = modelObject.heatingCOPFunctionofTemperatureCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::HeatingCOPFunctionofTemperatureCurveName,idf->name().get());
    }
  }

  {
    auto curve = modelObject.heatingCOPFunctionofAirFlowFractionCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::HeatingCOPFunctionofAirFlowFractionCurveName,idf->name().get());
    }
  }

  {
    auto curve = modelObject.heatingCOPFunctionofWaterFlowFractionCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::HeatingCOPFunctionofWaterFlowFractionCurveName,idf->name().get());
    }
  }

  {
    auto curve = modelObject.partLoadFractionCorrelationCurve();
    if( auto idf = translateAndMapModelObject(curve) ) {
      idfObject.setString(Coil_WaterHeating_AirToWaterHeatPumpFields::PartLoadFractionCorrelationCurveName,idf->name().get());
    }
  }

  return idfObject;
}