Ejemplo n.º 1
0
TEST_F(EnergyPlusFixture,ForwardTranslator_Surface_Zone)
{
  Model model;

  ThermalZone thermalZone(model);

  Space space(model);
  space.setThermalZone(thermalZone);
  
  Point3dVector points;
  points.push_back(Point3d(0, 1, 0));
  points.push_back(Point3d(0, 0, 0));
  points.push_back(Point3d(1, 0, 0));
  points.push_back(Point3d(1, 1, 0));
  Surface surface(points, model);
  surface.setSpace(space);

  ForwardTranslator forwardTranslator;
  Workspace workspace = forwardTranslator.translateModel(model);
  EXPECT_EQ(0u, forwardTranslator.errors().size());
  EXPECT_EQ(0u, forwardTranslator.warnings().size());

  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::BuildingSurface_Detailed).size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::Zone).size());

  WorkspaceObject surfaceObject = workspace.getObjectsByType(IddObjectType::BuildingSurface_Detailed)[0];
  WorkspaceObject zoneObject = workspace.getObjectsByType(IddObjectType::Zone)[0];

  ASSERT_TRUE(surfaceObject.getTarget(BuildingSurface_DetailedFields::ZoneName));
  EXPECT_EQ(zoneObject.handle(), surfaceObject.getTarget(BuildingSurface_DetailedFields::ZoneName)->handle());
}
OptionalModelObject ReverseTranslator::translateZoneInfiltrationEffectiveLeakageArea( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::ZoneInfiltration_EffectiveLeakageArea ){
    LOG(Error, "WorkspaceObject " << workspaceObject.briefDescription()
        << " is not IddObjectType: SpaceInfiltration_EffectiveLeakageArea");
    return boost::none;
  }

  // create the instance
  SpaceInfiltrationEffectiveLeakageArea spaceInfiltrationEffectiveLeakageArea(m_model);

  OptionalString s = workspaceObject.name();
  if(s){
    spaceInfiltrationEffectiveLeakageArea.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(ZoneInfiltration_EffectiveLeakageAreaFields::ZoneName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<Space>()){
        spaceInfiltrationEffectiveLeakageArea.setSpace(modelObject->cast<Space>());
      }else if (modelObject->optionalCast<SpaceType>()){
        spaceInfiltrationEffectiveLeakageArea.setSpaceType(modelObject->cast<SpaceType>());
      }
    }
  }

  target = workspaceObject.getTarget(ZoneInfiltration_EffectiveLeakageAreaFields::ScheduleName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (OptionalSchedule intermediate = modelObject->optionalCast<Schedule>()){
        Schedule schedule(*intermediate);
        spaceInfiltrationEffectiveLeakageArea.setSchedule(schedule);
      }
    }
  }

  boost::optional<double> value = workspaceObject.getDouble(ZoneInfiltration_EffectiveLeakageAreaFields::EffectiveAirLeakageArea);
  if( value )
  {
    spaceInfiltrationEffectiveLeakageArea.setEffectiveAirLeakageArea(value.get());
  }

  value = workspaceObject.getDouble(ZoneInfiltration_EffectiveLeakageAreaFields::StackCoefficient);
  if( value )
  {
    spaceInfiltrationEffectiveLeakageArea.setStackCoefficient(value.get());
  }

  value = workspaceObject.getDouble(ZoneInfiltration_EffectiveLeakageAreaFields::WindCoefficient);
  if( value )
  {
    spaceInfiltrationEffectiveLeakageArea.setWindCoefficient(value.get());
  }
  
  return spaceInfiltrationEffectiveLeakageArea;
}
OptionalModelObject ReverseTranslator::translateInternalMass( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::InternalMass ){
    LOG(Error, "WorkspaceObject is not IddObjectType: InternalMass");
    return boost::none;
  }

  // create the definition
  openstudio::model::InternalMassDefinition definition(m_model);
  
  OptionalString s = workspaceObject.name();
  if(s){
    definition.setName(*s + " Definition");
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(openstudio::InternalMassFields::ConstructionName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ConstructionBase>()){
        definition.setConstruction(modelObject->cast<ConstructionBase>());
      }
    }
  }

  OptionalDouble d = workspaceObject.getDouble(openstudio::InternalMassFields::SurfaceArea);
  if(d){
    definition.setSurfaceArea(*d);
  }

  // create the instance
  InternalMass internalMass(definition);

  s = workspaceObject.name();
  if(s){
    internalMass.setName(*s);
  }

  target = workspaceObject.getTarget(openstudio::InternalMassFields::ZoneName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<Space>()){
        internalMass.setSpace(modelObject->cast<Space>());
      }else if (modelObject->optionalCast<SpaceType>()){
        internalMass.setSpaceType(modelObject->cast<SpaceType>());
      }
    }
  }

  return internalMass;
}
Ejemplo n.º 4
0
TEST_F(EnergyPlusFixture,ForwardTranslator_Surface_DefaultConstruction)
{
  Model model;

  Construction construction(model);

  DefaultSurfaceConstructions defaultSurfaceConstructions(model);
  defaultSurfaceConstructions.setRoofCeilingConstruction(construction);

  DefaultConstructionSet defaultConstructionSet(model);
  defaultConstructionSet.setDefaultExteriorSurfaceConstructions(defaultSurfaceConstructions);

  Building building = model.getUniqueModelObject<Building>();
  building.setDefaultConstructionSet(defaultConstructionSet);

  Space space(model);
  ThermalZone zone(model);
  EXPECT_TRUE(space.setThermalZone(zone));  

  Point3dVector points;
  points.push_back(Point3d(0, 1, 0));
  points.push_back(Point3d(0, 0, 0));
  points.push_back(Point3d(1, 0, 0));
  points.push_back(Point3d(1, 1, 0));
  Surface surface(points, model);
  surface.setSpace(space);

  EXPECT_EQ("RoofCeiling", surface.surfaceType());
  EXPECT_EQ("Outdoors", surface.outsideBoundaryCondition());
  EXPECT_FALSE(surface.adjacentSurface());
  ASSERT_TRUE(surface.construction());
  EXPECT_TRUE(surface.isConstructionDefaulted());
  EXPECT_EQ(construction.handle(), surface.construction()->handle());

  ForwardTranslator forwardTranslator;
  Workspace workspace = forwardTranslator.translateModel(model);
  EXPECT_EQ(0u, forwardTranslator.errors().size());
  EXPECT_EQ(0u, forwardTranslator.warnings().size());

  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::BuildingSurface_Detailed).size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::Construction).size());

  WorkspaceObject surfaceObject = workspace.getObjectsByType(IddObjectType::BuildingSurface_Detailed)[0];
  WorkspaceObject constructionObject = workspace.getObjectsByType(IddObjectType::Construction)[0];

  ASSERT_TRUE(surfaceObject.getTarget(BuildingSurface_DetailedFields::ConstructionName));
  EXPECT_EQ(constructionObject.handle(), surfaceObject.getTarget(BuildingSurface_DetailedFields::ConstructionName)->handle());
}
OptionalModelObject ReverseTranslator::translateScheduleDayHourly(const WorkspaceObject & workspaceObject){
  if (workspaceObject.iddObject().type() != IddObjectType::Schedule_Day_Hourly){
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Day:Hourly");
    return boost::none;
  }

  // create the schedule
  ScheduleDay scheduleDay(m_model);

  OptionalString s = workspaceObject.name();
  if (s){
    scheduleDay.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(Schedule_Day_HourlyFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits){
      scheduleDay.setPointer(OS_Schedule_DayFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }

  for(unsigned i = 0; i < 24; ++i){
    OptionalDouble d = workspaceObject.getDouble(Schedule_Day_HourlyFields::Hour1 + i, true);
    if (d){
      scheduleDay.addValue(openstudio::Time(0,i+1,0,0), *d);
    }
  }

  return scheduleDay;
}
TEST_F(EnergyPlusFixture,ForwardTranslator_ShadingSurface_Space)
{
  Model model;
  
  ThermalZone thermalZone(model);

  Point3dVector points;
  points.push_back(Point3d(0,1,0));
  points.push_back(Point3d(1,1,0));
  points.push_back(Point3d(1,0,0));
  points.push_back(Point3d(0,0,0));

  boost::optional<Space> space = Space::fromFloorPrint(points, 1, model);
  ASSERT_TRUE(space);
  space->setThermalZone(thermalZone);

  ShadingSurfaceGroup shadingSurfaceGroup(model);
  EXPECT_TRUE(shadingSurfaceGroup.setSpace(*space));

  ShadingSurface shadingSurface(points, model);
  EXPECT_TRUE(shadingSurface.setShadingSurfaceGroup(shadingSurfaceGroup));

  ForwardTranslator forwardTranslator;
  Workspace workspace = forwardTranslator.translateModel(model);
  
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::Zone).size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::Shading_Zone_Detailed).size());

  WorkspaceObject zoneObject = workspace.getObjectsByType(IddObjectType::Zone)[0];
  WorkspaceObject shadingSurfaceObject = workspace.getObjectsByType(IddObjectType::Shading_Zone_Detailed)[0];

  EXPECT_TRUE(shadingSurfaceObject.getTarget(Shading_Zone_DetailedFields::BaseSurfaceName));
}
OptionalModelObject ReverseTranslator::translateScheduleCompact( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Schedule_Compact )
  {
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Compact");
    return boost::none;
  }

  ScheduleCompact scheduleCompact(m_model);

  OptionalWorkspaceObject target = workspaceObject.getTarget(Schedule_CompactFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits){
      scheduleCompact.setPointer(OS_Schedule_CompactFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }

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

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

  return scheduleCompact;
}
OptionalModelObject ReverseTranslator::translateAirTerminalSingleDuctUncontrolled( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::AirTerminal_SingleDuct_Uncontrolled )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: AirTerminal_SingleDuct_Uncontrolled");
     return boost::none;
  }

  boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(AirTerminal_SingleDuct_UncontrolledFields::AvailabilityScheduleName);
  boost::optional<Schedule> schedule;
  boost::optional<AirTerminalSingleDuctUncontrolled> airTerminal;
  boost::optional<double> value;

  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! (schedule = mo->optionalCast<Schedule>()) )
      {
        LOG(Error, workspaceObject.briefDescription() << " does not have an associated availability schedule");

        return boost::none;
      }
    }
  }

  if( schedule )
  {
    airTerminal = AirTerminalSingleDuctUncontrolled(m_model,schedule.get());
  }

  if( airTerminal )
  {
    boost::optional<std::string> s = workspaceObject.getString(AirTerminal_SingleDuct_UncontrolledFields::Name);
    if( s )
    {
      airTerminal->setName(s.get());
    }

    s = workspaceObject.getString(AirTerminal_SingleDuct_UncontrolledFields::MaximumAirFlowRate);
    if( s && istringEqual(s.get(),"AutoSize") )
    {
      airTerminal->autosizeMaximumAirFlowRate();
    }
    else if( (value = workspaceObject.getDouble(AirTerminal_SingleDuct_UncontrolledFields::MaximumAirFlowRate)) )
    {
      airTerminal->setMaximumAirFlowRate(value.get());
    }

    return airTerminal.get();
  }
  else
  {
    LOG(Error, "Unknown error translating " << workspaceObject.briefDescription());

    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateThermostatSetpointDualSetpoint( const WorkspaceObject & workspaceObject )
{
OptionalModelObject result,temp;
  OptionalSchedule schedule;

  ThermostatSetpointDualSetpoint tsds(m_model);

  OptionalWorkspaceObject owo = workspaceObject.getTarget(ThermostatSetpoint_DualSetpointFields::HeatingSetpointTemperatureScheduleName);
  if(!owo)
  {
    LOG(Error, "Error importing object: "
             << workspaceObject.briefDescription()
             << " Can't find Schedule: ");
    return result;
  }
  temp = translateAndMapWorkspaceObject( *owo);
  if(temp)
  {
    schedule=temp->optionalCast<Schedule>();
    if(schedule){
      tsds.setHeatingSchedule( *schedule );
    }
  }

  owo = workspaceObject.getTarget(ThermostatSetpoint_DualSetpointFields::CoolingSetpointTemperatureScheduleName);
  if(!owo)
  {
    LOG(Error, "Error importing object: "
             << workspaceObject.briefDescription()
             << " Can't find Schedule: ");
    return result;
  }
  temp = translateAndMapWorkspaceObject( *owo);
  if(temp)
  {
    schedule=temp->optionalCast<Schedule>();
    if(schedule){
      tsds.setCoolingSchedule( *schedule );
    }
  }

  result = tsds;
  return result;
}
OptionalModelObject ReverseTranslator::translateShadingBuildingDetailed( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Shading_Building_Detailed ){
    LOG(Error, "WorkspaceObject is not IddObjectType: Shading:Building:Detailed");
    return boost::none;
  }

  openstudio::Point3dVector vertices = getVertices(Shading_Building_DetailedFields::NumberofVertices + 1, workspaceObject);
 
  boost::optional<ShadingSurface> shadingSurface;
  try{
    shadingSurface = ShadingSurface(vertices, m_model);
  }catch(const std::exception&){
    LOG(Error, "Cannot create ShadingSurface for object: " << workspaceObject);
    return boost::none;
  }

  OptionalString s = workspaceObject.name();
  if(s) {
    shadingSurface->setName(*s);
  }

  // look for first site level shading surface group
  OptionalShadingSurfaceGroup shadingSurfaceGroup;
  for (const ShadingSurfaceGroup& group : m_model.getConcreteModelObjects<ShadingSurfaceGroup>()){
    if (istringEqual("Building", group.shadingSurfaceType())){
      shadingSurfaceGroup = group;
      break;
    }
  }

  // if not found make one
  if (!shadingSurfaceGroup){
    shadingSurfaceGroup = ShadingSurfaceGroup(m_model);
    shadingSurfaceGroup->setShadingSurfaceType("Building");
  }

  shadingSurface->setShadingSurfaceGroup(*shadingSurfaceGroup);

  OptionalWorkspaceObject target = workspaceObject.getTarget(openstudio::Shading_Building_DetailedFields::TransmittanceScheduleName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (OptionalSchedule intermediate = modelObject->optionalCast<Schedule>()){
        Schedule schedule(*intermediate);
        shadingSurface->setTransmittanceSchedule(schedule);
      }
    }
  }

  return shadingSurface.get();
}
OptionalModelObject ReverseTranslator::translateSiteWaterMainsTemperature( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Site_WaterMainsTemperature )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: Site_WaterMainsTemperature");
     return boost::none;
  }

  SiteWaterMainsTemperature mo = m_model.getUniqueModelObject<SiteWaterMainsTemperature>();

  boost::optional<WorkspaceObject> wo;
  boost::optional<Schedule> schedule;

  wo = workspaceObject.getTarget(Site_WaterMainsTemperatureFields::TemperatureScheduleName);
  if( wo )
  {
    boost::optional<ModelObject> mo2 = translateAndMapWorkspaceObject(wo.get());
    if( mo2 )
    {
      if( (schedule = mo2->optionalCast<Schedule>()) )
      {
        mo.setTemperatureSchedule(schedule.get());
      }
    }
  }

  boost::optional<double> value = workspaceObject.getDouble(Site_WaterMainsTemperatureFields::AnnualAverageOutdoorAirTemperature);
  if( value )
  {
    mo.setAnnualAverageOutdoorAirTemperature(value.get());
  }

  value = workspaceObject.getDouble(Site_WaterMainsTemperatureFields::MaximumDifferenceInMonthlyAverageOutdoorAirTemperatures);
  if( value )
  {
    mo.setMaximumDifferenceInMonthlyAverageOutdoorAirTemperatures(value.get());
  }

  return mo;
}
OptionalModelObject ReverseTranslator::translateOutputVariable( const WorkspaceObject & workspaceObject )
{

  OptionalString s = workspaceObject.getString(Output_VariableFields::VariableName);
  if(!s){
    return boost::none;
  }

  openstudio::model::OutputVariable outputVariable( *s,  m_model );

  s = workspaceObject.getString(Output_VariableFields::KeyValue);
  if(s){
    outputVariable.setKeyValue(*s);
  }

  s = workspaceObject.getString(Output_VariableFields::VariableName);
  if(s){
    outputVariable.setVariableName(*s);
  }

  s = workspaceObject.getString(Output_VariableFields::ReportingFrequency);
  if(s){
    outputVariable.setReportingFrequency(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(Output_VariableFields::ScheduleName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject( *target );
    if (modelObject){
      OptionalSchedule schedule = modelObject->optionalCast<Schedule>();
      if (schedule){
        outputVariable.setSchedule(*schedule);
      }
    }
  }

  return outputVariable;
}
OptionalModelObject ReverseTranslator::translateScheduleConstant( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Schedule_Constant )
  {
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Constant");
    return boost::none;
  }

  ScheduleConstant scheduleConstant(m_model);

  boost::optional<WorkspaceObject> target = workspaceObject.getTarget(Schedule_ConstantFields::ScheduleTypeLimitsName);
  if (target){
    OptionalModelObject scheduleTypeLimits = translateAndMapWorkspaceObject(*target);
    if (scheduleTypeLimits) {
      scheduleConstant.setPointer(OS_Schedule_ConstantFields::ScheduleTypeLimitsName, scheduleTypeLimits->handle());
    }
  }
  if (OptionalDouble val = workspaceObject.getDouble(Schedule_ConstantFields::HourlyValue)) {
    scheduleConstant.setValue(*val);
  }

  return scheduleConstant;
}
OptionalModelObject ReverseTranslator::translateAirTerminalSingleDuctVAVReheat( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::AirTerminal_SingleDuct_VAV_Reheat )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: AirTerminal_SingleDuct_VAV_Reheat");
     return boost::none;
  }

  boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(AirTerminal_SingleDuct_VAV_ReheatFields::AvailabilityScheduleName);
  boost::optional<Schedule> schedule;
  boost::optional<HVACComponent> coil;
  boost::optional<AirTerminalSingleDuctVAVReheat> airTerminal;

  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! (schedule = mo->optionalCast<Schedule>()) )
      {
        LOG(Error, workspaceObject.briefDescription() << " does not have an associated availability schedule");

        return boost::none;
      }
    }
  }

  wo = workspaceObject.getTarget(AirTerminal_SingleDuct_VAV_ReheatFields::ReheatCoilName);
  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! coil )
      {
        coil = mo->optionalCast<CoilHeatingGas>();
      }
    }
  }

  if( schedule && coil )
  {
    airTerminal = AirTerminalSingleDuctVAVReheat( m_model,schedule.get(),coil.get() );
  }

  if( airTerminal )
  {
    boost::optional<double> value;
    boost::optional<std::string> s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::Name);

    if( s )
    {
      airTerminal->setName(s.get());
    }

    // MaximumAirFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumAirFlowRate);
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumAirFlowRate);
    if( value )
    {
      airTerminal->setMaximumAirFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      airTerminal->autosizeMaximumAirFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      airTerminal->autosizeMaximumAirFlowRate();
    }

    // ZoneMinimumAirFlowInputMethod
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::ZoneMinimumAirFlowInputMethod);
    if( s )
    {
      airTerminal->setZoneMinimumAirFlowMethod(s.get());
    }

    // ConstantMinimumAirFlowFraction
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::ConstantMinimumAirFlowFraction);
    if( value )
    {
      airTerminal->setConstantMinimumAirFlowFraction(value.get());
    }

    // FixedMinimumAirFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::FixedMinimumAirFlowRate);
    if( value )
    {
      airTerminal->setFixedMinimumAirFlowRate(value.get());
    }

    boost::optional<WorkspaceObject> _schedule;

    // MinimumAirFlowFractionScheduleName
    _schedule = workspaceObject.getTarget(AirTerminal_SingleDuct_VAV_ReheatFields::MinimumAirFlowFractionScheduleName);
    if( _schedule )
    {
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(_schedule.get());
      if( mo )
      {
        if( boost::optional<Schedule> schedule = mo->optionalCast<Schedule>() )
        {
          airTerminal->setMinimumAirFlowFractionSchedule(schedule.get());
        }
      }
    }
  
    // MaximumHotWaterorSteamFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumHotWaterorSteamFlowRate);
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumHotWaterorSteamFlowRate);
    if( value )
    {
      airTerminal->setMaximumHotWaterOrSteamFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      airTerminal->autosizeMaximumHotWaterOrSteamFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      airTerminal->autosizeMaximumHotWaterOrSteamFlowRate();
    }

    // MinimumHotWaterorSteamFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MinimumHotWaterorSteamFlowRate);
    if( value )
    {
      airTerminal->setMinimumHotWaterOrStreamFlowRate(value.get());
    }

    // ConvergenceTolerance
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::ConvergenceTolerance);
    if( value )
    {
      airTerminal->setConvergenceTolerance(value.get());
    }

    // DamperHeatingAction
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::DamperHeatingAction);
    if( s )
    {
      airTerminal->setDamperHeatingAction(s.get());
    }

    // MaximumFlowPerZoneFloorAreaDuringReheat
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowperZoneFloorAreaDuringReheat);
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowperZoneFloorAreaDuringReheat);
    if( value )
    {
      airTerminal->setMaximumFlowPerZoneFloorAreaDuringReheat(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      airTerminal->autosizeMaximumFlowPerZoneFloorAreaDuringReheat();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      airTerminal->autosizeMaximumFlowPerZoneFloorAreaDuringReheat();
    }
    
    // MaximumFlowFractionDuringReheat
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowFractionDuringReheat);
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumFlowFractionDuringReheat);
    if( value )
    {
      airTerminal->setMaximumFlowFractionDuringReheat(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      airTerminal->autosizeMaximumFlowFractionDuringReheat();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      airTerminal->autosizeMaximumFlowFractionDuringReheat();
    }

    // MaximumReheatAirTemperature
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_ReheatFields::MaximumReheatAirTemperature);
    if( value )
    {
      airTerminal->setMaximumReheatAirTemperature(value.get());
    }

    return airTerminal.get();
  }
  else
  {
    LOG(Error, "Unknown error translating " << workspaceObject.briefDescription());

    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateSizingPeriodDesignDay( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::SizingPeriod_DesignDay )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: SizingPeriod_DesignDay");
     return boost::none;
  }

  DesignDay designDay(m_model);

  // Name
  boost::optional<std::string> s = workspaceObject.getString(SizingPeriod_DesignDayFields::Name);
  if( s ){
    designDay.setName(s.get());
  }else{
    LOG(Error, "SizingPeriod:DesignDay missing required field Name");
  }

  // Month
  boost::optional<int> i = workspaceObject.getInt(SizingPeriod_DesignDayFields::Month);
  if( i ){
    designDay.setMonth( i.get() );
  }else{
    LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Month");
  }

  // Day of Month
  i = workspaceObject.getInt(SizingPeriod_DesignDayFields::DayofMonth);
  if( i ){
    designDay.setDayOfMonth(i.get());
  }else{
    LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Day of Month");
  }

  // Day Type
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::DayType);
  if( s ){
    designDay.setDayType(s.get());
  }else{
    LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Day Type");
  }

  // Maximum Dry-Bulb Temperature
  boost::optional<double> value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::MaximumDryBulbTemperature);
  if( value ){
    designDay.setMaximumDryBulbTemperature(value.get());
  }else{
    LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Maximum Dry Bulb Temperature");
  }

  // Dry-Bulb Temperature Range Modifier Type
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::DryBulbTemperatureRangeModifierType);
  if( s ){
    designDay.setDryBulbTemperatureRangeModifierType(s.get());
  }
  std::string dryBulbTemperatureRangeModifierType = designDay.dryBulbTemperatureRangeModifierType();

  // Daily Dry-Bulb Temperature Range
  if (!istringEqual(dryBulbTemperatureRangeModifierType, "DifferenceSchedule")){
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::DailyDryBulbTemperatureRange);
    if( value ){
      designDay.setDailyDryBulbTemperatureRange(value.get()); 
    }
  }

  // Dry-Bulb Temperature Range Modifier Schedule Name
  if (istringEqual(dryBulbTemperatureRangeModifierType, "MultiplierSchedule") || istringEqual(dryBulbTemperatureRangeModifierType, "DifferenceSchedule")){
    boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(SizingPeriod_DesignDayFields::DryBulbTemperatureRangeModifierDayScheduleName);
    if( wo ){
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
      if( mo ){
        if(boost::optional<ScheduleDay> schedule = mo->optionalCast<ScheduleDay>()){
          designDay.setDryBulbTemperatureRangeModifierSchedule(*schedule);
        }
      }
    }

    if (!designDay.dryBulbTemperatureRangeModifierSchedule()){
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Maximum Dry Bulb Temperature");
    }
  }

  // Humidity Condition Type
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::HumidityConditionType);
  if( s ){
    if (istringEqual(*s, "RelativeHumiditySchedule")){
      s = "Schedule";
    }
    designDay.setHumidityIndicatingType(s.get());
  }
  std::string humidityIndicatingType = designDay.humidityIndicatingType();

  // Wetbulb or DewPoint at Maximum Dry-Bulb
  if (istringEqual(humidityIndicatingType, "Wetbulb") ||
      istringEqual(humidityIndicatingType, "Dewpoint") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileMultiplierSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDifferenceSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDefaultMultipliers")){
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::WetbulborDewPointatMaximumDryBulb);
    if( value ){
      // units for this field are C
      designDay.setHumidityIndicatingConditionsAtMaximumDryBulb(value.get());
    }else{
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Wetbulb or Dew Point at Maximum Dry Bulb");
    }
  }

  // Humidity Condition Day Schedule Name
  if (istringEqual(humidityIndicatingType, "RelativeHumiditySchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileMultiplierSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDifferenceSchedule") ||
      istringEqual(humidityIndicatingType, "RelativeHumiditySchedule")){
    
    boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(SizingPeriod_DesignDayFields::HumidityConditionDayScheduleName);
    if( wo ){
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
      if( mo ){
        boost::optional<ScheduleDay> schedule = mo->optionalCast<ScheduleDay>();
        if( schedule ){
          designDay.setHumidityIndicatingDaySchedule(schedule.get());
        }
      }
    }

    if (!designDay.humidityIndicatingDaySchedule()){
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Humidity Indicating Day Schedule Name");
    }
  }

  // Humidity Ratio at Maximum Dry-Bulb
  if (istringEqual(humidityIndicatingType, "HumidityRatio")){
    // units for this field are kgWater/kgDryAir
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::HumidityRatioatMaximumDryBulb);
    if( value ){
      designDay.setHumidityIndicatingConditionsAtMaximumDryBulb(value.get());
    }else{
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Humidity Ratio at Maximum Dry Bulb");
    }
  }

  // Enthalpy at Maximum Dry-Bulb
  if (istringEqual(humidityIndicatingType, "Enthalpy")){
    // units for this field are J/kg
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::EnthalpyatMaximumDryBulb);
    if( value ){
      designDay.setHumidityIndicatingConditionsAtMaximumDryBulb(value.get());
    }else{
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Enthalpy at Maximum Dry Bulb");
    }
  }

  // Daily Wet-Bulb Temperature Range
  if (istringEqual(humidityIndicatingType, "WetbulbProfileMultiplierSchedule") ||
      istringEqual(humidityIndicatingType, "WetBulbProfileDefaultMultipliers")){
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::DailyWetBulbTemperatureRange);
    if (value) {
      designDay.setDailyWetBulbTemperatureRange(*value);
    }else{
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Daily Wet Bulb Temperature Range");
    }
  }

  // Barometric Pressure
  value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::BarometricPressure);
  if( value ){
    designDay.setBarometricPressure(value.get());
  }

  // Site Wind Speed
  value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::WindSpeed);
  if( value ){
    designDay.setWindSpeed(value.get());
  }else{
    LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Site Wind Speed");
  }

  // Site Wind Direction
  value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::WindDirection);
  if( value ){
    designDay.setWindDirection(value.get());
  }else{
    LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Site Wind Direction");
  }

  // Rain Indicator
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::RainIndicator);
  if( s ){
    if( istringEqual(*s, "Yes") ){
      designDay.setRainIndicator(true);
    }else{
      designDay.setRainIndicator(false);
    }
  }

  // Snow Indicator
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::SnowIndicator);
  if( s ){
    if( istringEqual(*s, "Yes") ){
      designDay.setSnowIndicator(true);
    }else{
      designDay.setSnowIndicator(false);
    }
  }

  // Site Daylight Saving Time Status
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::DaylightSavingTimeIndicator);
  if( s ){
    if( istringEqual(*s, "Yes") ){
      designDay.setDaylightSavingTimeIndicator(true);
    }else{
      designDay.setDaylightSavingTimeIndicator(false);
    }
  }

  // Solar Model Indicator
  s = workspaceObject.getString(SizingPeriod_DesignDayFields::SolarModelIndicator);
  if( s ){
    designDay.setSolarModelIndicator(s.get());
  }
  std::string solarModelIndicator = designDay.solarModelIndicator();

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

    // Beam Solar Day Schedule Name
    boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(SizingPeriod_DesignDayFields::BeamSolarDayScheduleName);
    if( wo ){
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
      if( mo ){
        boost::optional<ScheduleDay> schedule = mo->optionalCast<ScheduleDay>();
        if( schedule ){
          designDay.setBeamSolarDaySchedule(schedule.get());
        }
      }
    }
    if (!designDay.beamSolarDaySchedule()){
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Beam Solar Day Schedule Name");
    }

    // Site Diffuse Solar Radiation Rate per Area Radiation Rate per Area Day Schedule Name
    wo = workspaceObject.getTarget(SizingPeriod_DesignDayFields::DiffuseSolarDayScheduleName);
    if( wo ){
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
      if( mo ){
        boost::optional<ScheduleDay> schedule = mo->optionalCast<ScheduleDay>();
        if( schedule ){
          designDay.setDiffuseSolarDaySchedule(schedule.get());
        }
      }
    }
    if (!designDay.diffuseSolarDaySchedule()){
      LOG(Error, "SizingPeriod:DesignDay " << designDay.name().get() << " missing required field Diffuse Solar Schedule Name");
    }
  }

  if (istringEqual(solarModelIndicator, "ASHRAETau")){
    //  ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub)
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::ASHRAEClearSkyOpticalDepthforBeamIrradiance_taub_);
    if (value) {
      designDay.setAshraeTaub(*value);
    }

    // ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud)
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::ASHRAEClearSkyOpticalDepthforDiffuseIrradiance_taud_);
    if (value) {
      designDay.setAshraeTaud(*value);
    }
  }

  // Sky Clearness
  if (istringEqual(solarModelIndicator, "ASHRAEClearSky") || istringEqual(solarModelIndicator, "ZhangHuang")){
    value = workspaceObject.getDouble(SizingPeriod_DesignDayFields::SkyClearness);
    if( value ){
      designDay.setSkyClearness(value.get());
    }
  }

  return designDay;
}
OptionalModelObject ReverseTranslator::translateDesignSpecificationOutdoorAir( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::DesignSpecification_OutdoorAir ){
    LOG(Error, "WorkspaceObject is not IddObjectType: DesignSpecification:OutdoorAir");
    return boost::none;
  }

  OptionalString outdoorAirMethod = workspaceObject.getString(DesignSpecification_OutdoorAirFields::OutdoorAirMethod, true);
  if (!outdoorAirMethod){
    LOG(Error, "No OutdoorAirMethod specified for DesignSpecification:OutdoorAir named '" << workspaceObject.name().get() << "'");
    return boost::none;
  }

  DesignSpecificationOutdoorAir result(m_model);

  OptionalString name = workspaceObject.name();
  if(name){
    result.setName(*name);
  }

  result.setOutdoorAirMethod(*outdoorAirMethod);

  boost::optional<double> flowPerPerson = workspaceObject.getDouble(DesignSpecification_OutdoorAirFields::OutdoorAirFlowperPerson);
  boost::optional<double> flowPerArea = workspaceObject.getDouble(DesignSpecification_OutdoorAirFields::OutdoorAirFlowperZoneFloorArea);
  boost::optional<double> flowPerZone = workspaceObject.getDouble(DesignSpecification_OutdoorAirFields::OutdoorAirFlowperZone);
  boost::optional<double> ach = workspaceObject.getDouble(DesignSpecification_OutdoorAirFields::OutdoorAirFlowAirChangesperHour);

  if (istringEqual(*outdoorAirMethod, "Flow/Person")){
    if (flowPerPerson){
      result.setOutdoorAirFlowperPerson(*flowPerPerson);
    }
  }else if (istringEqual(*outdoorAirMethod, "Flow/Area")){
    if (flowPerArea){
      result.setOutdoorAirFlowperFloorArea(*flowPerArea);
    }
  }else if (istringEqual(*outdoorAirMethod, "Flow/Zone")){
    if (flowPerZone){
      result.setOutdoorAirFlowRate(*flowPerZone);
    }
  }else if (istringEqual(*outdoorAirMethod, "AirChanges/Hour")){
    if (ach){
      result.setOutdoorAirFlowRate(*ach);
    }
  }else if (istringEqual(*outdoorAirMethod, "Sum") || istringEqual(*outdoorAirMethod, "Maximum")){

    if (flowPerPerson){
      result.setOutdoorAirFlowperPerson(*flowPerPerson);
    }
    if (flowPerArea){
      result.setOutdoorAirFlowperFloorArea(*flowPerArea);
    }
    if (flowPerZone){
      result.setOutdoorAirFlowRate(*flowPerZone);
    }
    if (ach){
      result.setOutdoorAirFlowRate(*ach);
    }

  }else{
    LOG(Error, "Unknown OutdoorAirMethod '" << *outdoorAirMethod << "' specified for DesignSpecification:OutdoorAir named '" << workspaceObject.name().get() << "'");
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(DesignSpecification_OutdoorAirFields::OutdoorAirFlowRateFractionScheduleName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (OptionalSchedule intermediate = modelObject->optionalCast<Schedule>()){
        Schedule schedule = *intermediate;
        result.setOutdoorAirFlowRateFractionSchedule(schedule);
      }
    }
  }

  return result;
}
OptionalModelObject ReverseTranslator::translateGeneratorMicroTurbine( const WorkspaceObject & workspaceObject )
{

  OptionalModelObject result,temp;
  OptionalDouble d;
  boost::optional<WorkspaceObject> owo;
  OptionalString optS;
  
  
  // TODO: The availability schedule is in the ElectricLoadCenter:Generators (list) in E+, here it's carried by the generator itself
  // Should also get the Rated Thermal To Electrical Power Ratio in the list

  //Generator:MicroTurbine,
  //    Capstone C65,            !- Name

  openstudio::model::GeneratorMicroTurbine mchp( m_model );
  
  // Name
  optS = workspaceObject.name();
  if(optS)
  {
    mchp.setName(*optS);
  }

  
    
  //    65000,                   !- Reference Electrical Power Output {W}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceElectricalPowerOutput);
  if(d)
  {
    mchp.setReferenceElectricalPowerOutput(*d);
  }
  //    29900,                   !- Minimum Full Load Electrical Power Output {W}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::MinimumFullLoadElectricalPowerOutput);
  if(d)
  {
    mchp.setMinimumFullLoadElectricalPowerOutput(*d);
  }
  //    65000,                   !- Maximum Full Load Electrical Power Output {W} setMaximumFullLoadElectricalPowerOutput
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::MaximumFullLoadElectricalPowerOutput);
  if(d)
  {
    mchp.setMaximumFullLoadElectricalPowerOutput(*d);
  }

  //    0.29,                    !- Reference Electrical Efficiency Using Lower Heating Value
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceElectricalEfficiencyUsingLowerHeatingValue);
  if(d)
  {
    mchp.setReferenceElectricalEfficiencyUsingLowerHeatingValue(*d);
  }

  //    15.0,                    !- Reference Combustion Air Inlet Temperature {C}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceCombustionAirInletTemperature);
  if(d)
  {
    mchp.setReferenceCombustionAirInletTemperature(*d);
  }

  //    0.00638,                 !- Reference Combustion Air Inlet Humidity Ratio {kgWater/kgDryAir}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceCombustionAirInletHumidityRatio);
  if(d)
  {
    mchp.setReferenceCombustionAirInletHumidityRatio(*d);
  }

  //    0.0,                     !- Reference Elevation {m}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::MinimumFullLoadElectricalPowerOutput);
  if(d)
  {
    mchp.setMinimumFullLoadElectricalPowerOutput(*d);
  }

  //    Capstone C65 Power_vs_Temp_Elev,  !- Electrical Power Function of Temperature and Elevation Curve Name
  // BiquadraticCurves
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ElectricalPowerFunctionofTemperatureandElevationCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveBiquadratic> curve = mo->optionalCast<CurveBiquadratic>() )
        {
          mchp.setElectricalPowerFunctionofTemperatureandElevationCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Electrical Power Function of Temperature and Elevation Curve Name");
        }
      }
    }

  //    Capstone C65 Efficiency_vs_Temp,  !- Electrical Efficiency Function of Temperature Curve Name
  // QuadraticCubicCurves
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ElectricalEfficiencyFunctionofTemperatureCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setElectricalEfficiencyFunctionofTemperatureCurve(curve.get());
        }
        else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
        {
          mchp.setElectricalEfficiencyFunctionofTemperatureCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Electrical Efficiency Function of Temperature Curve Name");
        }
      }
    }
  

  //    Capstone C65 Efficiency_vs_PLR,  !- Electrical Efficiency Function of Part Load Ratio Curve Name
  // QuadraticCubicCurves
  // setElectricalEfficiencyFunctionofPartLoadRatioCurve
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ElectricalEfficiencyFunctionofPartLoadRatioCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setElectricalEfficiencyFunctionofPartLoadRatioCurve(curve.get());
        }
        else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
        {
          mchp.setElectricalEfficiencyFunctionofPartLoadRatioCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Electrical Efficiency Function of Part Load Ratio Curve Name");
        }
      }
    }

  //    NaturalGas,              !- Fuel Type
  optS = workspaceObject.getString(Generator_MicroTurbineFields::FuelType);
  if(optS)
  {
    mchp.setFuelType(*optS);
  }
    
  //    50000,                   !- Fuel Higher Heating Value {kJ/kg}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::FuelHigherHeatingValue);
  if(d)
  {
    mchp.setFuelHigherHeatingValue(*d);
  }

  //    45450,                   !- Fuel Lower Heating Value {kJ/kg}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::FuelLowerHeatingValue);
  if(d)
  {
    mchp.setFuelLowerHeatingValue(*d);
  }

  //    300,                     !- Standby Power {W}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::StandbyPower);
  if(d)
  {
    mchp.setStandbyPower(*d);
  }

  //    4500,                    !- Ancillary Power {W}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::AncillaryPower);
  if(d)
  {
    mchp.setAncillaryPower(*d);
  }

  //    ,                        !- Ancillary Power Function of Fuel Input Curve Name
  // QuadraticCurves
  // mchp.setAncillaryPowerFunctionofFuelInputCurve
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::AncillaryPowerFunctionofFuelInputCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setAncillaryPowerFunctionofFuelInputCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Ancillary Power Function of Fuel Input Curve Name");
        }
      }
    }

  
  // Fields in between (in E+.idd) are moved at the end in the Heat Recovery section

  //    Capstone C65 Combustion Air Inlet Node,  !- Combustion Air Inlet Node Name
  //    Capstone C65 Combustion Air Outlet Node,  !- Combustion Air Outlet Node Name


  //    0.489885,                !- Reference Exhaust Air Mass Flow Rate {kg/s}
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceExhaustAirMassFlowRate);
  if(d)
  {
    mchp.setReferenceExhaustAirMassFlowRate(*d);
  }

  //    Capstone C65 ExhAirFlowRate_vs_InletTemp,  !- Exhaust Air Flow Rate Function of Temperature Curve Name
  // QuadraticCubicCurves
  // mchp.setExhaustAirFlowRateFunctionofTemperatureCurve
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ExhaustAirFlowRateFunctionofTemperatureCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setExhaustAirFlowRateFunctionofTemperatureCurve(curve.get());
        }
        else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
        {
          mchp.setExhaustAirFlowRateFunctionofTemperatureCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Exhaust Air Flow Rate Function of Temperature Curve Name");
        }
      }
    }

  //    Capstone C65 ExhAirFlowRate_vs_PLR,  !- Exhaust Air Flow Rate Function of Part Load Ratio Curve Name
  // QuadraticCubicCurves
  // mchp.setExhaustAirFlowRateFunctionofPartLoadRatioCurve(curve)
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ExhaustAirFlowRateFunctionofPartLoadRatioCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setExhaustAirFlowRateFunctionofPartLoadRatioCurve(curve.get());
        }
        else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
        {
          mchp.setExhaustAirFlowRateFunctionofPartLoadRatioCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Exhaust Air Flow Rate Function of Part Load Ratio Curve Name");
        }
      }
    }

  //    308.9,                   !- Nominal Exhaust Air Outlet Temperature
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::NominalExhaustAirOutletTemperature);
  if(d)
  {
    mchp.setNominalExhaustAirOutletTemperature(*d);
  }

  //    Capstone C65 ExhaustTemp_vs_InletTemp,  !- Exhaust Air Temperature Function of Temperature Curve Name
  // QuadraticCubicCurves
  // mchp.setExhaustAirTemperatureFunctionofTemperatureCurve(curve)
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ExhaustAirTemperatureFunctionofTemperatureCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setExhaustAirTemperatureFunctionofTemperatureCurve(curve.get());
        }
        else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
        {
          mchp.setExhaustAirTemperatureFunctionofTemperatureCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Exhaust Air Temperature Function of Temperature Curve Name");
        }
      }
    }

  //    Capstone C65 ExhaustTemp_vs_PLR;  !- Exhaust Air Temperature Function of Part Load Ratio Curve Name
  // QuadraticCubicCurves
  // mchp.setExhaustAirTemperatureFunctionofPartLoadRatioCurve(curve)
  if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ExhaustAirTemperatureFunctionofPartLoadRatioCurveName)) )
    {
      if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
      {
        if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
        {
          mchp.setExhaustAirTemperatureFunctionofPartLoadRatioCurve(curve.get());
        }
        else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
        {
          mchp.setExhaustAirTemperatureFunctionofPartLoadRatioCurve(curve.get());
        }
        else
        {
          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Exhaust Air Temperature Function of Part Load Ratio Curve Name");
        }
      }
    }
    
    
  /// HEAT RECOVERY PORTION
  
  // Would need to add that to the plantLoop reverse translator
  //    Capstone C65 Heat Recovery Water Inlet Node,  !- Heat Recovery Water Inlet Node Name
  //    Capstone C65 Heat Recovery Water Outlet Node,  !- Heat Recovery Water Outlet Node Name
  

  
  
  // TODO: For now, I trigger the creation is the 'Reference Thermal Efficiency Using Lower Heat Value' is filled.
  // TODO: I should trigger it based on the `Rated Thermal To Electrical Power Ratio in the list`  in the ElectricLoadCenter:Generators (list)
  // TODO: But in order to do that, the ElectricLoadCenter:Distribution & ElectricLoadCenter:Generators need to have a reverse translator
  
  //    0.4975,                  !- Reference Thermal Efficiency Using Lower Heat Value
  d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceThermalEfficiencyUsingLowerHeatValue);
  
  if(d)
  {
   
    // Create a GeneratorMicroTurbineHeatRecovery module, and assign it to the MicroTurbine
    // I've Set the Name in the constructor
    openstudio::model::GeneratorMicroTurbineHeatRecovery mchpHR (m_model, mchp);
    
    // Assign the Reference Thermal Efficiency Using Lower Heat Value
    mchpHR.setReferenceThermalEfficiencyUsingLowerHeatValue(*d);
    

    //    60.0,                    !- Reference Inlet Water Temperature {C}
    d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceInletWaterTemperature);
    if(d)
    {
      mchpHR.setReferenceInletWaterTemperature(*d);
    }
    
    //    PlantControl,            !- Heat Recovery Water Flow Operating Mode
    optS = workspaceObject.getString(Generator_MicroTurbineFields::HeatRecoveryWaterFlowOperatingMode);
    if(optS)
    {
      mchpHR.setHeatRecoveryWaterFlowOperatingMode(*optS);
    }
    
    //    0.00252362,              !- Reference Heat Recovery Water Flow Rate {m3/s}
    d = workspaceObject.getDouble(Generator_MicroTurbineFields::ReferenceHeatRecoveryWaterFlowRate);
    if(d)
    {
      mchpHR.setReferenceHeatRecoveryWaterFlowRate(*d);
    }
 
    //    ,                        !- Heat Recovery Water Flow Rate Function of Temperature and Power Curve Name
    // BiquadraticCurves
    // mchpHR.setHeatRecoveryWaterFlowRateFunctionofTemperatureandPowerCurve();
    if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::HeatRecoveryWaterFlowRateFunctionofTemperatureandPowerCurveName)) )
      {
        if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
        {
          if( boost::optional<CurveBiquadratic> curve = mo->optionalCast<CurveBiquadratic>() )
          {
            mchpHR.setHeatRecoveryWaterFlowRateFunctionofTemperatureandPowerCurve(curve.get());
          }
          else
          {
            LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Heat Recovery Water Flow Rate Function of Temperature and Power Curve Name");
          }
        }
      }

    //    Capstone C65 ThermalEff_vs_Temp_Elev,  !- Thermal Efficiency Function of Temperature and Elevation Curve Name
    // BicubicBiquadraticCurves
    // mchpHR.setThermalEfficiencyFunctionofTemperatureandElevationCurve();
    if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::ThermalEfficiencyFunctionofTemperatureandElevationCurveName)) )
      {
        if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
        {
          if( boost::optional<CurveBicubic> curve = mo->optionalCast<CurveBicubic>() )
          {
            mchpHR.setThermalEfficiencyFunctionofTemperatureandElevationCurve(curve.get());
          }
          else if( boost::optional<CurveBiquadratic> curve = mo->optionalCast<CurveBiquadratic>() )
          {
            mchpHR.setThermalEfficiencyFunctionofTemperatureandElevationCurve(curve.get());
          }
          else
          {
            LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Thermal Efficiency Function of Temperature and Elevation Curve Name");
          }
        }
      }

    //    Capstone C65 HeatRecoveryRate_vs_PLR,  !- Heat Recovery Rate Function of Part Load Ratio Curve Name
    // QuadraticCubicCurves
    // mchpHR.setHeatRecoveryRateFunctionofPartLoadRatioCurve();
    if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::HeatRecoveryRateFunctionofPartLoadRatioCurveName)) )
      {
        if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
        {
          if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
          {
            mchpHR.setHeatRecoveryRateFunctionofPartLoadRatioCurve(curve.get());
          }
          else if( boost::optional<CurveCubic> curve = mo->optionalCast<CurveCubic>() )
          {
            mchpHR.setHeatRecoveryRateFunctionofPartLoadRatioCurve(curve.get());
          }
          else
          {
            LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Heat Recovery Rate Function of Part Load Ratio Curve Name");
          }
        }
      }

    //    Capstone C65 HeatRecoveryRate_vs_InletTemp,  !- Heat Recovery Rate Function of Inlet Water Temperature Curve Name
    // QuadraticCurves
    // mchpHR.setHeatRecoveryRateFunctionofInletWaterTemperatureCurve();
    if ((owo = workspaceObject.getTarget(Generator_MicroTurbineFields::HeatRecoveryRateFunctionofInletWaterTemperatureCurveName)))
      {
        if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
        {
          if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
          {
            mchpHR.setHeatRecoveryRateFunctionofInletWaterTemperatureCurve(curve.get());
          }
          else
          {
            LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Heat Recovery Rate Function of Part Load Ratio Curve Name");
          }
        }
      }

    //    Capstone C65 HeatRecoveryRate_vs_WaterFlow,  !- Heat Recovery Rate Function of Water Flow Rate Curve Name
    // QuadraticCurves
    // mchpHR.setHeatRecoveryRateFunctionofInletWaterFlowRateCurve();
    if( (owo = workspaceObject.getTarget(Generator_MicroTurbineFields::HeatRecoveryRateFunctionofWaterFlowRateCurveName)) )
      {
        if( boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(owo.get()) )
        {
          if( boost::optional<CurveQuadratic> curve = mo->optionalCast<CurveQuadratic>() )
          {
            mchpHR.setHeatRecoveryRateFunctionofWaterFlowRateCurve(curve.get());
          }
          else
          {
            LOG(Warn, workspaceObject.briefDescription() << " has a wrong type curve for field Heat Recovery Rate Function of Water Flow Rate Curve Name");
          }
        }
      }

    //    0.001577263,             !- Minimum Heat Recovery Water Flow Rate {m3/s}
    d = workspaceObject.getDouble(Generator_MicroTurbineFields::MinimumHeatRecoveryWaterFlowRate);
    if(d)
    {
      mchpHR.setMinimumHeatRecoveryWaterFlowRate(*d);
    }

    //    0.003785432,             !- Maximum Heat Recovery Water Flow Rate {m3/s}
    d = workspaceObject.getDouble(Generator_MicroTurbineFields::MaximumHeatRecoveryWaterFlowRate);
    if(d)
    {
      mchpHR.setMaximumHeatRecoveryWaterFlowRate(*d);
    }

    //    82.2,                    !- Maximum Heat Recovery Water Temperature {C}
    d = workspaceObject.getDouble(Generator_MicroTurbineFields::MaximumHeatRecoveryWaterTemperature);
    if(d)
    {
      mchpHR.setMaximumHeatRecoveryWaterTemperature(*d);
    }
    
    
  }
    
    
  result=mchp;
  return result;
}
OptionalModelObject ReverseTranslator::translateBuildingSurfaceDetailed( const WorkspaceObject & workspaceObject )
{
 if( workspaceObject.iddObject().type() != IddObjectType::BuildingSurface_Detailed ){
   LOG(Error, "WorkspaceObject is not IddObjectType: BuildingSurface:Detailed");
    return boost::none;
  }

  openstudio::Point3dVector vertices = getVertices(BuildingSurface_DetailedFields::NumberofVertices + 1, workspaceObject);
 
  boost::optional<Surface> surface;
  try{
    surface = Surface(vertices, m_model);
  }catch(const std::exception&){
    LOG(Error, "Cannot create Surface for object: " << workspaceObject);
    return boost::none;
  }

  OptionalString s = workspaceObject.name();
  if(s) {
    surface->setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(openstudio::BuildingSurface_DetailedFields::ConstructionName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ConstructionBase>()){
        surface->setConstruction(modelObject->cast<ConstructionBase>());
      }
    }
  }

  target = workspaceObject.getTarget(openstudio::BuildingSurface_DetailedFields::ZoneName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<Space>()){
        surface->setSpace(modelObject->cast<Space>());
      }
    }
  }

  s = workspaceObject.getString(BuildingSurface_DetailedFields::SurfaceType);
  if (s) {
    if (istringEqual("Roof", *s) || istringEqual("Ceiling", *s)){
      s = "RoofCeiling";
    }
    surface->setSurfaceType(*s);
  }
  //std::string surfaceType = surface->surfaceType();

  s = workspaceObject.getString(BuildingSurface_DetailedFields::SunExposure);
  if (s) {
    surface->setSunExposure(*s);
  }

  s = workspaceObject.getString(BuildingSurface_DetailedFields::WindExposure);
  if (s) {
    surface->setWindExposure(*s);
  }

  OptionalDouble d = workspaceObject.getDouble(BuildingSurface_DetailedFields::ViewFactortoGround);
  if (d) {
    surface->setViewFactortoGround(*d);
  }

  target = workspaceObject.getTarget(openstudio::BuildingSurface_DetailedFields::OutsideBoundaryConditionObject);
  if (target){

    if (target->iddObject().type() == IddObjectType::Zone){
      // Zone boundary condition

      OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
      if(modelObject->optionalCast<Space>()){
        Space adjacentSpace = modelObject->cast<Space>();

        if (surface->space()){
          // insert this surface in the map so subsurface translation can find it  
          m_workspaceToModelMap.insert(std::make_pair(workspaceObject.handle(), surface.get()));

          // need to translate all sub surfaces here so they will be in adjacent space
          for (const WorkspaceObject& workspaceSubSurface : workspaceObject.getSources(IddObjectType::FenestrationSurface_Detailed)){
            translateAndMapWorkspaceObject(workspaceSubSurface);
          }

          // create adjacent surface in other space
          surface->createAdjacentSurface(adjacentSpace);
          return surface.get();
        }
      }

    }else if (target->iddObject().type() == IddObjectType::BuildingSurface_Detailed){
      // Surface boundary condition

      // see if we have already mapped other surface, don't do it here because that is circular
      if (target->handle() == workspaceObject.handle() ){
        // these objects are the same, set boundary condition to adiabatic
        surface->setOutsideBoundaryCondition("Adiabatic");
        return surface.get();
      }else{
        auto it = m_workspaceToModelMap.find(target->handle());
        if( it !=  m_workspaceToModelMap.end()){
          if (it->second.optionalCast<Surface>()){
            // this will set other side boundary object on both surfaces
            Surface adjacentSurface = it->second.cast<Surface>();
            surface->setAdjacentSurface(adjacentSurface);
            return surface.get();
          }
        }
      }

    }else{  
      LOG(Error, "OutsideBoundaryConditionObject not yet mapped for object of type " << target->iddObject().name());
    }
  }

  s = workspaceObject.getString(BuildingSurface_DetailedFields::OutsideBoundaryCondition);
  if (s) {
    surface->setOutsideBoundaryCondition(*s);
  }

  return surface.get();
}
OptionalModelObject ReverseTranslator::translateEvaporativeFluidCoolerSingleSpeed( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::EvaporativeFluidCooler_SingleSpeed )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: EvaporativeFluidCooler_SingleSpeed");
     return boost::none;
  }

  boost::optional<EvaporativeFluidCoolerSingleSpeed> evapCooler;

  evapCooler = EvaporativeFluidCoolerSingleSpeed( m_model );

  if( evapCooler )
  {
    boost::optional<double> value;
    boost::optional<std::string> s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::Name);

    if( s )
    {
      evapCooler->setName(s.get());
    }

    // DesignAirFlowRate
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignAirFlowRate);
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::DesignAirFlowRate);
    if( value )
    {
      evapCooler->setDesignAirFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      evapCooler->autosizeDesignAirFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      evapCooler->autosizeDesignAirFlowRate();
    }

    // FanPoweratDesignAirFlowRate
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignAirFlowRateFanPower);
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::DesignAirFlowRateFanPower);
    if( value )
    {
      evapCooler->setFanPoweratDesignAirFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      evapCooler->autosizeFanPoweratDesignAirFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      evapCooler->autosizeFanPoweratDesignAirFlowRate();
    }

    // DesignSprayWaterFlowRate
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignSprayWaterFlowRate);
    if( value )
    {
      evapCooler->setDesignSprayWaterFlowRate(value.get());
    }

    // PerformanceInputMethod
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::PerformanceInputMethod);
    if( s )
    {
      evapCooler->setPerformanceInputMethod(s.get());
    }

    // StandardDesignCapacity
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::StandardDesignCapacity);
    if( value )
    {
      evapCooler->setStandardDesignCapacity(value.get());
    }

    // UfactorTimesAreaValueatDesignAirFlowRate
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignAirFlowRateUfactorTimesAreaValue);
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::DesignAirFlowRateUfactorTimesAreaValue);
    if( value )
    {
      evapCooler->setUfactorTimesAreaValueatDesignAirFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      evapCooler->autosizeUfactorTimesAreaValueatDesignAirFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      evapCooler->autosizeUfactorTimesAreaValueatDesignAirFlowRate();
    }

    // DesignWaterFlowRate
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignWaterFlowRate);
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::DesignWaterFlowRate);
    if( value )
    {
      evapCooler->setDesignWaterFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      evapCooler->autosizeDesignWaterFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      evapCooler->autosizeDesignWaterFlowRate();
    }

    // UserSpecifiedDesignCapacity
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::UserSpecifiedDesignCapacity);
    if( value )
    {
      evapCooler->setUserSpecifiedDesignCapacity(value.get());
    }

    // DesignEnteringWaterTemperature
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignEnteringWaterTemperature);
    if( value )
    {
      evapCooler->setDesignEnteringWaterTemperature(value.get());
    }

    // DesignEnteringAirTemperature
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignEnteringAirTemperature);
    if( value )
    {
      evapCooler->setDesignEnteringAirTemperature(value.get());
    }

    // DesignEnteringAirWetbulbTemperature
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DesignEnteringAirWetbulbTemperature);
    if( value )
    {
      evapCooler->setDesignEnteringAirWetbulbTemperature(value.get());
    }

    // CapacityControl
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::CapacityControl);
    if( s )
    {
      evapCooler->setCapacityControl(s.get());
    }

    // SizingFactor
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::SizingFactor);
    if( value )
    {
      evapCooler->setSizingFactor(value.get());
    }

    // EvaporationLossMode
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::EvaporationLossMode);
    if( s )
    {
      evapCooler->setEvaporationLossMode(s.get());
    }

    // EvaporationLossFactor
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::EvaporationLossFactor);
    if( value )
    {
      evapCooler->setEvaporationLossFactor(value.get());
    }

    // DriftLossPercent
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::DriftLossPercent);
    if( value )
    {
      evapCooler->setDriftLossPercent(value.get());
    }

    // BlowdownCalculationMode
    s = workspaceObject.getString(EvaporativeFluidCooler_SingleSpeedFields::BlowdownCalculationMode);
    if( s )
    {
      evapCooler->setBlowdownCalculationMode(s.get());
    }

    // BlowdownConcentrationRatio
    value = workspaceObject.getDouble(EvaporativeFluidCooler_SingleSpeedFields::BlowdownConcentrationRatio);
    if( value )
    {
      evapCooler->setBlowdownConcentrationRatio(value.get());
    }

    boost::optional<WorkspaceObject> _schedule;

    // BlowdownMakeupWaterUsageScheduleName
    _schedule = workspaceObject.getTarget(EvaporativeFluidCooler_SingleSpeedFields::BlowdownMakeupWaterUsageScheduleName);
    if( _schedule )
    {
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(_schedule.get());
      if( mo )
      {
        if( boost::optional<Schedule> schedule = mo->optionalCast<Schedule>() )
        {
          evapCooler->setBlowdownMakeupWaterUsageSchedule(schedule.get());
        }
      }
    }

    return evapCooler.get();
  }
  else
  {
    LOG(Error, "Unknown error translating " << workspaceObject.briefDescription());

    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateCoilHeatingGas( const WorkspaceObject & workspaceObject )
{
  OptionalModelObject result,temp;

  OptionalSchedule schedule;

 //get the Schedule
  OptionalWorkspaceObject owo = workspaceObject.getTarget(Coil_Heating_GasFields::AvailabilityScheduleName);
  if(!owo)
  {
    LOG(Error, "Error importing object: "
             << workspaceObject.briefDescription()
             << " Can't find Schedule.");
    return result;
  }
  temp = translateAndMapWorkspaceObject(*owo);
  if(temp)
  {
    schedule=temp->optionalCast<Schedule>();
  }

  if( !schedule  )
  {
    LOG(Error, "Error importing object: "
             << workspaceObject.briefDescription()
             <<"Failed to convert iddObject (schedule) into ModelObject. Maybe it does not exist in model yet");
    return result;
  }

  try {

    openstudio::model::CoilHeatingGas coil( m_model,
                                            *schedule );
    OptionalString optS = workspaceObject.name();
    if(optS)
    {
      coil.setName(*optS);
    }
    OptionalDouble d;
    d = workspaceObject.getDouble(openstudio::Coil_Heating_GasFields::GasBurnerEfficiency);
    if(d)
    {
      coil.setGasBurnerEfficiency(*d);
    }
    d = workspaceObject.getDouble(openstudio::Coil_Heating_GasFields::NominalCapacity);
    if(d)
    {
      coil.setNominalCapacity(*d);
    }
    //skip inlet and outlet node names. That should be done FOR us by the AirLoop Translator.
    d = workspaceObject.getDouble(openstudio::Coil_Heating_GasFields::GasBurnerEfficiency);
    if(d)
    {
      coil.setGasBurnerEfficiency(*d);
    }

    d = workspaceObject.getDouble(openstudio::Coil_Heating_GasFields::NominalCapacity);
    if(d)
    {
      coil.setNominalCapacity(*d);
    }

    d = workspaceObject.getDouble(openstudio::Coil_Heating_GasFields::ParasiticElectricLoad);
    if(d)
    {
      coil.setParasiticElectricLoad(*d);
    }
    d = workspaceObject.getDouble(openstudio::Coil_Heating_GasFields::ParasiticGasLoad);
    if(d)
    {
      coil.setParasiticGasLoad(*d);
    }
    result = coil;
  }
  catch (std::exception& e) {
    LOG(Error,"Unable to reverse translate " << workspaceObject.briefDescription() << ", because "
        << e.what() << ".");
    return boost::none;
  }

  return result;
}
OptionalModelObject ReverseTranslator::translateAirLoopHVACOutdoorAirSystem( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::AirLoopHVAC_OutdoorAirSystem )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: AirLoopHVAC_OutdoorAirSystem");
     return boost::none;
  }

  Workspace _workspace = workspaceObject.workspace();

  boost::optional<WorkspaceObject> _controllerList;
  boost::optional<WorkspaceObject> _controllerOutdoorAir;
  boost::optional<std::string> controllerName;
  boost::optional<std::string> controllerType;
  boost::optional<ControllerOutdoorAir> oaController;

  _controllerList = workspaceObject.getTarget(AirLoopHVAC_OutdoorAirSystemFields::ControllerListName);

  if( _controllerList )
  {
    for( int i = 1;
         _controllerList->getString(i);
         i = i + 2 )
    {
      controllerType = _controllerList->getString(i);
      controllerName = _controllerList->getString(i + 1);
      if( controllerType )
      {
        if( istringEqual(controllerType.get(),"Controller:OutdoorAir") )
        {
          break;
        }
      }
    }
  }

  if( controllerName && controllerType )
  {
    boost::optional<WorkspaceObject> wo = _workspace.getObjectByTypeAndName(IddObjectType(controllerType.get()),controllerName.get()); 
    if( wo )
    {
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
      if( mo )
      {
        oaController = mo->optionalCast<ControllerOutdoorAir>();
      }
    }
  }

  if( oaController )
  {
    AirLoopHVACOutdoorAirSystem mo(m_model,oaController.get());

    boost::optional<std::string> name = workspaceObject.getString(AirLoopHVAC_OutdoorAirSystemFields::Name);
    if( name ) { mo.setName(name.get()); }

    Node outboardOANode = mo.outboardOANode().get();

    boost::optional<WorkspaceObject> _oaEquipmentList;
    boost::optional<WorkspaceObject> _outdoorAirMixer;
    std::vector<WorkspaceObject> equipmentVector;
    _oaEquipmentList = workspaceObject.getTarget(AirLoopHVAC_OutdoorAirSystemFields::OutdoorAirEquipmentListName);

    if( _oaEquipmentList )
    {
      for( int i = 1;
           _oaEquipmentList->getString(i);
           i = i + 2 )
      {
        boost::optional<std::string> equipmentName;
        boost::optional<std::string> equipmentType;

        equipmentType = _oaEquipmentList->getString(i);
        equipmentName = _oaEquipmentList->getString(i + 1);

        if( equipmentName && equipmentType )
        {
          boost::optional<WorkspaceObject> wo = _workspace.getObjectByTypeAndName(IddObjectType(equipmentType.get()),equipmentName.get());
          if( wo )
          {
            equipmentVector.push_back(wo.get());

            if( wo->iddObject().type() == IddObjectType::OutdoorAir_Mixer )
            {
              _outdoorAirMixer = wo;
            }
          }
        }
      }
      if( _outdoorAirMixer )
      {
        boost::optional<std::string> mixerOAInletNodeName = _outdoorAirMixer->getString(OutdoorAir_MixerFields::OutdoorAirStreamNodeName);
        boost::optional<std::string> mixerOAReliefNodeName = _outdoorAirMixer->getString(OutdoorAir_MixerFields::ReliefAirStreamNodeName);
        if( mixerOAInletNodeName ) { mo.outdoorAirModelObject()->cast<Node>().setName(mixerOAInletNodeName.get()); }
        if( mixerOAReliefNodeName ) { mo.reliefAirModelObject()->cast<Node>().setName(mixerOAReliefNodeName.get()); }

        boost::optional<std::string> oaStreamInletNodeName;
        boost::optional<std::string> oaStreamOutletNodeName;

        oaStreamInletNodeName = _outdoorAirMixer->getString(OutdoorAir_MixerFields::OutdoorAirStreamNodeName);

        while( oaStreamInletNodeName )
        {
          boost::optional<ModelObject> oaComponentModelObject;
          boost::optional<std::string> newOAStreamInletNodeName;

          for( std::vector<WorkspaceObject>::iterator it = equipmentVector.begin();
               it < equipmentVector.end();
               it++ )
          {
            switch(it->iddObject().type().value())
            {
              case openstudio::IddObjectType::EvaporativeCooler_Direct_ResearchSpecial :
              {
                oaStreamOutletNodeName = it->getString(EvaporativeCooler_Direct_ResearchSpecialFields::AirOutletNodeName);
                if( oaStreamOutletNodeName )
                {
                  if( istringEqual(oaStreamOutletNodeName.get(),oaStreamInletNodeName.get()) )
                  {
                    newOAStreamInletNodeName = it->getString(EvaporativeCooler_Direct_ResearchSpecialFields::AirInletNodeName);

                    oaComponentModelObject = translateAndMapWorkspaceObject(*it);
                  }
                }
                break;
              }
              default :
              {
                break;
              }
            }
            if( newOAStreamInletNodeName )
            {
              break;
            }
          }

          oaStreamInletNodeName = newOAStreamInletNodeName;

          if( oaComponentModelObject )
          {
            bool success = false;

            if( boost::optional<HVACComponent> hvacComponent = oaComponentModelObject->optionalCast<HVACComponent>() )
            {
              success = hvacComponent->addToNode(outboardOANode);

              if( success )
              {
                if( boost::optional<StraightComponent> straightComponent = hvacComponent->optionalCast<StraightComponent>() )
                {
                  if( oaStreamInletNodeName )
                  {
                    straightComponent->inletModelObject()->cast<Node>().setName(oaStreamInletNodeName.get());
                  }
                  if( oaStreamOutletNodeName )
                  {
                    straightComponent->outletModelObject()->cast<Node>().setName(oaStreamOutletNodeName.get());
                  }
                }
              }
            }

            if( ! success )
            {
              oaComponentModelObject->remove();
            }
          }
        }
      }
    }

    return mo;
  }
  else
  {
    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateScheduleWeekDaily(const WorkspaceObject & workspaceObject){
  if (workspaceObject.iddObject().type() != IddObjectType::Schedule_Week_Daily){
    LOG(Error, "WorkspaceObject is not IddObjectType: Schedule:Week");
    return boost::none;
  }

  // create the schedule
  ScheduleWeek scheduleWeek(m_model);

  OptionalString s = workspaceObject.name();
  if (s){
    scheduleWeek.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(Schedule_Week_DailyFields::SundaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setSundaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::MondaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setMondaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::TuesdaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setTuesdaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::WednesdaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setWednesdaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }
  
  target = workspaceObject.getTarget(Schedule_Week_DailyFields::ThursdaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setThursdaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::FridaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setFridaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::SaturdaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setSaturdaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::HolidaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setHolidaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::SummerDesignDaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setSummerDesignDaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::WinterDesignDaySchedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setWinterDesignDaySchedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::CustomDay1Schedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setCustomDay1Schedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  target = workspaceObject.getTarget(Schedule_Week_DailyFields::CustomDay2Schedule_DayName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ScheduleDay>()){
        scheduleWeek.setCustomDay2Schedule(modelObject->cast<ScheduleDay>());
      }
    }
  }

  return scheduleWeek;
}
OptionalModelObject ReverseTranslator::translateFanConstantVolume( const WorkspaceObject & workspaceObject )
{
OptionalModelObject result,temp;
  OptionalSchedule schedule;

  OptionalWorkspaceObject owo = workspaceObject.getTarget(Fan_ConstantVolumeFields::AvailabilityScheduleName);
  if(!owo)
  {
    LOG(Error, "Error importing object: "
             << workspaceObject.briefDescription()
             << " Can't find Schedule: ");
    return result;
  }
  temp = translateAndMapWorkspaceObject( *owo);
  if(temp)
  {
    schedule = temp->optionalCast<Schedule>();
  }

  if( !schedule )
  {
    LOG(Error, "Error importing object: "
             << workspaceObject.name().get()
             <<"Failed to convert iddObjects into model Objects. Maybe they do not exist in model yet");

    return result;
  }

  openstudio::model::FanConstantVolume fan( m_model, *schedule );
  OptionalString optS = workspaceObject.name();
  if(optS)
  {
    fan.setName(*optS);
  }
  //inlet and outlet nodes are set my the HVACAirLoop
  OptionalDouble d;
  d = workspaceObject.getDouble(openstudio::Fan_ConstantVolumeFields::FanTotalEfficiency);
  if(d)
  {
    fan.setFanEfficiency(*d);
  }
  d = workspaceObject.getDouble(openstudio::Fan_ConstantVolumeFields::PressureRise);
  if(d)
  {
    fan.setPressureRise(*d);
  }
  d = workspaceObject.getDouble(openstudio::Fan_ConstantVolumeFields::MaximumFlowRate);
  if(d)
  {
    fan.setMaximumFlowRate(*d);
  }
  d = workspaceObject.getDouble(openstudio::Fan_ConstantVolumeFields::MotorEfficiency);
  if(d)
  {
    fan.setMotorEfficiency(*d);
  }
  d = workspaceObject.getDouble(openstudio::Fan_ConstantVolumeFields::MotorInAirstreamFraction);
  if(d)
  {
    fan.setMotorInAirstreamFraction(*d);
  }

  optS=workspaceObject.getString(openstudio::Fan_ConstantVolumeFields::EndUseSubcategory);
  if(optS)
  {
    fan.setEndUseSubcategory(*optS);
  }

  result=fan;
  return result;
}
OptionalModelObject ReverseTranslator::translateSetpointManagerScheduled( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::SetpointManager_Scheduled )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: SetpointManager_Scheduled");
     return boost::none;
  }

  boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(SetpointManager_ScheduledFields::ScheduleName);
  boost::optional<Schedule> schedule;

  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! (schedule = mo->optionalCast<Schedule>()) )
      {
        LOG(Error, workspaceObject.briefDescription() << " does not have an associated schedule");

        return boost::none;
      }
    }
  }

  bool nodeFound = false;

  if( boost::optional<std::string> setpointNodeName = workspaceObject.getString(SetpointManager_ScheduledFields::SetpointNodeorNodeListName) )
  {
    boost::optional<Node> setpointNode = m_model.getModelObjectByName<Node>(setpointNodeName.get());

    if( setpointNode ) { nodeFound = true; }
  }

  if( ! nodeFound )
  {
    LOG(Error, workspaceObject.briefDescription() << " is not attached to a node in the model");

    return boost::none;
  }

  if( schedule )
  {
    SetpointManagerScheduled mo(m_model,schedule.get());

    // Name
    boost::optional<std::string> s = workspaceObject.getString(SetpointManager_ScheduledFields::Name);
    if( s )
    {
      mo.setName(s.get());
    }

    // Setpoint Node
    s = workspaceObject.getString(SetpointManager_ScheduledFields::SetpointNodeorNodeListName);
    if( s )
    {
      boost::optional<Node> node = m_model.getModelObjectByName<Node>(s.get());
    
      if( node )
      {
        mo.addToNode(node.get());
      }
    }

    // Control Variable
    s = workspaceObject.getString(SetpointManager_ScheduledFields::ControlVariable);
    if( s )
    {
      mo.setControlVariable(s.get());
    }

    if( mo.setpointNode() )
    {
      return mo;
    }
    else
    {
      return boost::none;
    }
  }
  else
  {
    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateFenestrationSurfaceDetailed( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::FenestrationSurface_Detailed ){
    LOG(Error, "WorkspaceObject is not IddObjectType: Site:FenestrationSurface_Detailed");
    return boost::none;
  }

  openstudio::Point3dVector vertices = getVertices(FenestrationSurface_DetailedFields::NumberofVertices + 1, workspaceObject);
 
  boost::optional<SubSurface> subSurface;
  try{
    subSurface = SubSurface(vertices, m_model);
  }catch(const std::exception&){
    LOG(Error, "Cannot create SubSurface for object: " << workspaceObject);
    return boost::none;
  }

  OptionalString s = workspaceObject.name();
  if(s) {
    subSurface->setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(openstudio::FenestrationSurface_DetailedFields::ConstructionName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<ConstructionBase>()){
        subSurface->setConstruction(modelObject->cast<ConstructionBase>());
      }
    }
  }

  target = workspaceObject.getTarget(openstudio::FenestrationSurface_DetailedFields::BuildingSurfaceName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<Surface>()){
        subSurface->setSurface(modelObject->cast<Surface>());
      }
    }
  }

  // needs to be after .setSurface.
  s = workspaceObject.getString(FenestrationSurface_DetailedFields::SurfaceType);
  if (s) {
    if (istringEqual("Window", *s)){
      s = "FixedWindow";

      boost::optional<Surface> surface = subSurface->surface();
      if (surface){
        if ((surface->surfaceType() == "RoofCeiling") &&
            (surface->outsideBoundaryCondition() == "Outdoors")){
              s = "Skylight";
        }
      }
    }
    subSurface->setSubSurfaceType(*s);
  }

  target = workspaceObject.getTarget(openstudio::FenestrationSurface_DetailedFields::OutsideBoundaryConditionObject);
  if (target){
    if (target->iddObject().type() == IddObjectType::Zone){
      // Zone boundary condition

      OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
      if(modelObject->optionalCast<Space>()){
        Space adjacentSpace = modelObject->cast<Space>();

        OptionalSurface surface = subSurface->surface();
        if (surface && surface->space()){
          Space space = surface->space().get();

          if (surface->adjacentSurface()){
            Surface adjacentSurface = surface->adjacentSurface().get();

            if (adjacentSurface.space() && adjacentSpace.handle() == adjacentSurface.space()->handle()){
              Transformation transformation = adjacentSpace.transformation().inverse()*surface->space()->transformation();

              // duplicate subsurface in other space
              SubSurface adjacentSubSurface = subSurface->clone(m_model).cast<SubSurface>();
              adjacentSubSurface.setName(subSurface->name().get() + " Reversed");
              std::reverse(vertices.begin(), vertices.end());
              adjacentSubSurface.setVertices(transformation*vertices);
              adjacentSubSurface.setSurface(adjacentSurface);
              subSurface->setAdjacentSubSurface(adjacentSubSurface);

              return subSurface.get();
            }
          }
        }
      }

    }else if (target->iddObject().type() == IddObjectType::FenestrationSurface_Detailed){
      // SubSurface boundary condition

      // see if we have already mapped other sub surface, don't do it here because that is circular
      auto it = m_workspaceToModelMap.find(target->handle());
      if( it !=  m_workspaceToModelMap.end()){
        if (it->second.optionalCast<SubSurface>()){
          // this will set other side boundary object on both surfaces
          SubSurface adjacentSubSurface = it->second.cast<SubSurface>();
          subSurface->setAdjacentSubSurface(adjacentSubSurface);
          return subSurface.get();
        }
      }
    }else{  
      LOG(Error, "OutsideBoundaryConditionObject not yet mapped for object of type " << target->iddObject().name());
    }
  }

  // DLM: should these be before control paths that return above?
  OptionalDouble d = workspaceObject.getDouble(FenestrationSurface_DetailedFields::ViewFactortoGround);
  if (d) {
    subSurface->setViewFactortoGround(*d);
  }

  target = workspaceObject.getTarget(openstudio::FenestrationSurface_DetailedFields::ShadingControlName);
  if (target){
    LOG(Warn, "Shading Control Name not yet mapped for FenestrationSurface:Detailed");
  }

  target = workspaceObject.getTarget(openstudio::FenestrationSurface_DetailedFields::FrameandDividerName);
  if (target){
    LOG(Warn, "Frame and Divider Name not yet mapped for FenestrationSurface:Detailed");
  }

  OptionalInt i = workspaceObject.getInt(FenestrationSurface_DetailedFields::Multiplier);
  if (i) {
    subSurface->setMultiplier(*i);
  }

  return subSurface.get();
}
OptionalModelObject ReverseTranslator::translateGasEquipment( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::GasEquipment ){
    LOG(Error, "WorkspaceObject is not IddObjectType: GasEquipment");
    return boost::none;
  }

  // create the definition
  openstudio::model::GasEquipmentDefinition definition(m_model);
  
  OptionalString s = workspaceObject.name();
  if(s){
    definition.setName(*s + " Definition");
  }

  s = workspaceObject.getString(openstudio::GasEquipmentFields::DesignLevelCalculationMethod, true);
  OS_ASSERT(s);
 
  OptionalDouble d;
  if (istringEqual("EquipmentLevel", *s)){
    d = workspaceObject.getDouble(openstudio::GasEquipmentFields::DesignLevel);
    if (d){
      definition.setDesignLevel(*d);
    }else{
      LOG(Error, "EquipmentLevel value not found for workspace object " << workspaceObject);
    }
  }else if(istringEqual("Watts/Area", *s)){
    d = workspaceObject.getDouble(openstudio::GasEquipmentFields::PowerperZoneFloorArea);
    if (d){
      definition.setWattsperSpaceFloorArea(*d);
    }else{
      LOG(Error, "Watts/Area value not found for workspace object " << workspaceObject);
    }
  }else if(istringEqual("Watts/Person", *s)){
    d = workspaceObject.getDouble(openstudio::GasEquipmentFields::PowerperPerson);
    if (d){
      definition.setWattsperPerson(*d);
    }else{
      LOG(Error, "Watts/Person value not found for workspace object " << workspaceObject);
    }
  }else{
    LOG(Error, "Unknown DesignLevelCalculationMethod value for workspace object" << workspaceObject);
  }

  d = workspaceObject.getDouble(openstudio::GasEquipmentFields::FractionLatent);
  if (d){
    definition.setFractionLatent(*d);
  }

  d = workspaceObject.getDouble(openstudio::GasEquipmentFields::FractionRadiant);
  if (d){
    definition.setFractionRadiant(*d);
  }

  d = workspaceObject.getDouble(openstudio::GasEquipmentFields::FractionLost);
  if (d){
    definition.setFractionLost(*d);
  }

  d = workspaceObject.getDouble(openstudio::GasEquipmentFields::CarbonDioxideGenerationRate);
  if (d){
    definition.setCarbonDioxideGenerationRate(*d);
  }

  // create the instance
  GasEquipment gasEquipment(definition);

  s = workspaceObject.name();
  if(s){
    gasEquipment.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(openstudio::GasEquipmentFields::ZoneorZoneListName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<Space>()){
        gasEquipment.setSpace(modelObject->cast<Space>());
      }else if (modelObject->optionalCast<SpaceType>()){
        gasEquipment.setSpaceType(modelObject->cast<SpaceType>());
      }
    }
  }

  target = workspaceObject.getTarget(openstudio::GasEquipmentFields::ScheduleName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (OptionalSchedule intermediate = modelObject->optionalCast<Schedule>()){
        Schedule schedule(*intermediate);
        gasEquipment.setSchedule(schedule);
      }
    }
  }

  s = workspaceObject.getString(openstudio::GasEquipmentFields::EndUseSubcategory);
  if(s){
    gasEquipment.setEndUseSubcategory(*s);
  }
       
  return gasEquipment;
}
boost::optional<model::ModelObject> ReverseTranslator::translateExteriorLights(
    const WorkspaceObject& workspaceObject)
{
  if (workspaceObject.iddObject().type() != IddObjectType::Exterior_Lights) {
    LOG(Error,"WorkspaceObject " << workspaceObject.briefDescription()
        << " is not of IddObjectType::Exterior_Lights.");
    return boost::none;
  }

  model::ExteriorLightsDefinition definition(m_model);

  OptionalString s;
  OptionalDouble d;

  if ((s = workspaceObject.name())) {
    definition.setName(*s + " Definition");
  }

  if ((d = workspaceObject.getDouble(Exterior_LightsFields::DesignLevel))){
    definition.setDesignLevel(*d);
  }

  model::OptionalExteriorLights exteriorLights;
  model::OptionalSchedule schedule;

  if (OptionalWorkspaceObject target = workspaceObject.getTarget(Exterior_LightsFields::ScheduleName))
  {
    if (model::OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target)) {
      schedule = modelObject->optionalCast<model::Schedule>();
    }
  }

  if (schedule) {
    try {
      exteriorLights = model::ExteriorLights(definition,*schedule);
    }
    catch (std::exception& e) {
      LOG(Warn,"Could not reverse translate " << workspaceObject.briefDescription()
          << " in full, because " << e.what() << ".");
    }
  }
  if (!exteriorLights) {
    exteriorLights = model::ExteriorLights(definition);
  }

  OS_ASSERT(exteriorLights);

  if ((s = workspaceObject.name())) {
    exteriorLights->setName(*s);
  }

  if ((s = workspaceObject.getString(Exterior_LightsFields::ControlOption,false,true))) {
    exteriorLights->setControlOption(*s);
  }

  if ((s = workspaceObject.getString(Exterior_LightsFields::EndUseSubcategory,false,true))) {
    exteriorLights->setEndUseSubcategory(*s);
  }

  return *exteriorLights;
}
OptionalModelObject ReverseTranslator::translateAirTerminalSingleDuctConstantVolumeReheat( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::AirTerminal_SingleDuct_ConstantVolume_Reheat )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: AirTerminal_SingleDuct_ConstantVolume_Reheat");
     return boost::none;
  }

  boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::AvailabilityScheduleName);
  boost::optional<Schedule> schedule;
  boost::optional<HVACComponent> coil;
  boost::optional<AirTerminalSingleDuctConstantVolumeReheat> airTerminal;

  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! (schedule = mo->optionalCast<Schedule>()) )
      {
        LOG(Error, workspaceObject.briefDescription() << " does not have an associated availability schedule");

        return boost::none;
      }
    }
  }

  wo = workspaceObject.getTarget(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::ReheatCoilName);
  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! coil )
      {
        //TODO: Maybe try to cast this to different types depending on ReheatCoilType
        coil = mo->optionalCast<CoilHeatingElectric>();
      }
    }
  }

  if( schedule && coil )
  {
    airTerminal = AirTerminalSingleDuctConstantVolumeReheat( m_model,schedule.get(),coil.get() );
  }

  if( airTerminal )
  {
    boost::optional<double> value;
    boost::optional<std::string> s = workspaceObject.getString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::Name);

    if( s )
    {
      airTerminal->setName(s.get());
    }

    // MaximumAirFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumAirFlowRate);
    if( value )
    {
      airTerminal->setMaximumAirFlowRate(value.get());
    }
    else
    {
      s = workspaceObject.getString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumAirFlowRate);
      if( s && istringEqual(s.get(),"Autosize") )
      {
        airTerminal->autosizeMaximumAirFlowRate();
      }
      else if( s && istringEqual(s.get(),"Autocalculate") )
      {
        airTerminal->autosizeMaximumAirFlowRate();
      }
    }
  
    // MaximumHotWaterorSteamFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumHotWaterorSteamFlowRate);
    if( value )
    {
      airTerminal->setMaximumHotWaterorSteamFlowRate(value.get());
    }
    else
    {
      s = workspaceObject.getString(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumHotWaterorSteamFlowRate);
      if( s && istringEqual(s.get(),"Autosize") )
      {
        airTerminal->autosizeMaximumHotWaterorSteamFlowRate();
      }
      else if( s && istringEqual(s.get(),"Autocalculate") )
      {
        airTerminal->autosizeMaximumHotWaterorSteamFlowRate();
      }
    }

    // MinimumHotWaterorSteamFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MinimumHotWaterorSteamFlowRate);
    if( value )
    {
      airTerminal->setMinimumHotWaterorSteamFlowRate(value.get());
    }

    // ConvergenceTolerance
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::ConvergenceTolerance);
    if( value )
    {
      airTerminal->setConvergenceTolerance(value.get());
    }

    // MaximumReheatAirTemperature
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_ConstantVolume_ReheatFields::MaximumReheatAirTemperature);
    if( value )
    {
      airTerminal->setMaximumReheatAirTemperature(value.get());
    }

    return airTerminal.get();
  }
  else
  {
    LOG(Error, "Unknown error translating " << workspaceObject.briefDescription());

    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateAirTerminalSingleDuctVAVNoReheat( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::AirTerminal_SingleDuct_VAV_NoReheat )
  {
     LOG(Error, "WorkspaceObject is not IddObjectType: AirTerminal_SingleDuct_VAV_NoReheat");
     return boost::none;
  }

  boost::optional<WorkspaceObject> wo = workspaceObject.getTarget(AirTerminal_SingleDuct_VAV_NoReheatFields::AvailabilityScheduleName);
  boost::optional<Schedule> schedule;
  boost::optional<AirTerminalSingleDuctVAVNoReheat> airTerminal;

  if( wo )
  {
    boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(wo.get());
    if( mo )
    {
      if( ! (schedule = mo->optionalCast<Schedule>()) )
      {
        LOG(Error, workspaceObject.briefDescription() << " does not have an associated availability schedule");

        return boost::none;
      }
    }
  }

  if( schedule )
  {
    airTerminal = AirTerminalSingleDuctVAVNoReheat( m_model,schedule.get() );
  }

  if( airTerminal )
  {
    boost::optional<double> value;
    boost::optional<std::string> s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_NoReheatFields::Name);

    if( s )
    {
      airTerminal->setName(s.get());
    }

    // MaximumAirFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_NoReheatFields::MaximumAirFlowRate);
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_NoReheatFields::MaximumAirFlowRate);
    if( value )
    {
      airTerminal->setMaximumAirFlowRate(value.get());
    }
    else if( s && istringEqual(s.get(),"Autosize") )
    {
      airTerminal->autosizeMaximumAirFlowRate();
    }
    else if( s && istringEqual(s.get(),"Autocalculate") )
    {
      airTerminal->autosizeMaximumAirFlowRate();
    }

    // ZoneMinimumAirFlowInputMethod
    s = workspaceObject.getString(AirTerminal_SingleDuct_VAV_NoReheatFields::ZoneMinimumAirFlowInputMethod);
    if( s )
    {
      airTerminal->setZoneMinimumAirFlowInputMethod(s.get());
    }

    // ConstantMinimumAirFlowFraction
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_NoReheatFields::ConstantMinimumAirFlowFraction);
    if( value )
    {
      airTerminal->setConstantMinimumAirFlowFraction(value.get());
    }

    // FixedMinimumAirFlowRate
    value = workspaceObject.getDouble(AirTerminal_SingleDuct_VAV_NoReheatFields::FixedMinimumAirFlowRate);
    if( value )
    {
      airTerminal->setFixedMinimumAirFlowRate(value.get());
    }

    boost::optional<WorkspaceObject> _schedule;

    // MinimumAirFlowFractionScheduleName
    _schedule = workspaceObject.getTarget(AirTerminal_SingleDuct_VAV_NoReheatFields::MinimumAirFlowFractionScheduleName);
    if( _schedule )
    {
      boost::optional<ModelObject> mo = translateAndMapWorkspaceObject(_schedule.get());
      if( mo )
      {
        if( boost::optional<Schedule> schedule = mo->optionalCast<Schedule>() )
        {
          airTerminal->setMinimumAirFlowFractionSchedule(schedule.get());
        }
      }
    }

    return airTerminal.get();
  }
  else
  {
    LOG(Error, "Unknown error translating " << workspaceObject.briefDescription());

    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateOutputIlluminanceMap( const WorkspaceObject & workspaceObject )
{
  if( workspaceObject.iddObject().type() != IddObjectType::Output_IlluminanceMap ){
    LOG(Error, "WorkspaceObject is not IddObjectType: Output:IlluminanceMap");
    return boost::none;
  }

  IlluminanceMap illuminanceMap( m_model );

  OptionalString s = workspaceObject.name();
  if (s){
    illuminanceMap.setName(*s);
  }

  OptionalWorkspaceObject target = workspaceObject.getTarget(Output_IlluminanceMapFields::ZoneName);
  if (target){
    OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
    if (modelObject){
      if (modelObject->optionalCast<Space>()){
        illuminanceMap.setSpace(modelObject->cast<Space>());
      }
    }
  }

  OptionalDouble d = workspaceObject.getDouble(Output_IlluminanceMapFields::Zheight);
  if (d){
    illuminanceMap.setOriginZCoordinate(*d);
  }

  d = workspaceObject.getDouble(Output_IlluminanceMapFields::XMinimumCoordinate);
  if (d){
    illuminanceMap.setOriginXCoordinate(*d);

    OptionalDouble maxX = workspaceObject.getDouble(Output_IlluminanceMapFields::XMaximumCoordinate);
    if (maxX){
      illuminanceMap.setXLength(*maxX - *d);
    }
  }

  OptionalInt i = workspaceObject.getInt(Output_IlluminanceMapFields::NumberofXGridPoints);
  if (i){
    illuminanceMap.setNumberofXGridPoints(*i);
  }

  d = workspaceObject.getDouble(Output_IlluminanceMapFields::YMinimumCoordinate);
  if (d){
    illuminanceMap.setOriginYCoordinate(*d);

    OptionalDouble maxY = workspaceObject.getDouble(Output_IlluminanceMapFields::YMaximumCoordinate);
    if (maxY){
      illuminanceMap.setYLength(*maxY - *d);
    }
  }

  i = workspaceObject.getInt(Output_IlluminanceMapFields::NumberofYGridPoints);
  if (i){
    illuminanceMap.setNumberofYGridPoints(*i);
  }

  return illuminanceMap;
}