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

  Workspace workspace = workspaceObject.workspace();

  boost::optional<std::string> coolingCoilObjectType;
  boost::optional<std::string> coolingCoilName;

  coolingCoilObjectType = workspaceObject.getString(CoilSystem_Cooling_DXFields::CoolingCoilObjectType);
  coolingCoilName = workspaceObject.getString(CoilSystem_Cooling_DXFields::CoolingCoilName);

  boost::optional<WorkspaceObject> wo;

  if( coolingCoilObjectType && coolingCoilName )
  {
    wo = workspace.getObjectByTypeAndName(IddObjectType(coolingCoilObjectType.get()),coolingCoilName.get());
  }

  if( wo )
  {
    return translateAndMapWorkspaceObject(wo.get());
  }
  else
  {
    return boost::none;
  }
}
OptionalModelObject ReverseTranslator::translateEnergyManagementSystemOutputVariable(const WorkspaceObject & workspaceObject)
{
  if (workspaceObject.iddObject().type() != IddObjectType::EnergyManagementSystem_OutputVariable) {
    LOG(Error, "WorkspaceObject is not IddObjectType: EnergyManagementSystem_OutputVariable");
    return boost::none;
  }

  //make sure all other objects are translated first except below
  for (const WorkspaceObject& workspaceObject : m_workspace.objects()) {
    if ((workspaceObject.iddObject().type() != IddObjectType::EnergyManagementSystem_Program)
      && (workspaceObject.iddObject().type() != IddObjectType::EnergyManagementSystem_Subroutine)
      && (workspaceObject.iddObject().type() != IddObjectType::EnergyManagementSystem_ProgramCallingManager)
      && (workspaceObject.iddObject().type() != IddObjectType::EnergyManagementSystem_MeteredOutputVariable)
      && (workspaceObject.iddObject().type() != IddObjectType::EnergyManagementSystem_OutputVariable)) {
      translateAndMapWorkspaceObject(workspaceObject);
    }
  }

  OptionalString s = workspaceObject.getString(EnergyManagementSystem_OutputVariableFields::Name);
  if (!s) {
    LOG(Error, "EnergyManagementSystem_OutputVariable has no Name");
    return boost::none;
  }
  openstudio::model::EnergyManagementSystemOutputVariable emsOutputVariable(m_model);
  emsOutputVariable.setName(*s);

  s = workspaceObject.getString(EnergyManagementSystem_OutputVariableFields::EMSVariableName);
  if (!s) {
    LOG(Error, emsOutputVariable.nameString() + ": EMSVariableName not set");
    return boost::none;
  } else {
    Workspace workspace = workspaceObject.workspace();
    //look for GlobalVariables, translate and check if there is a name match since GV's dont have name field.
    boost::optional<WorkspaceObject> wsObject = workspace.getObjectByTypeAndName(IddObjectType::EnergyManagementSystem_GlobalVariable, *s);
    //for (WorkspaceObject& wsObject : workspace.getObjectsByType(IddObjectType::EnergyManagementSystem_GlobalVariable)) {
    if (wsObject) {
      boost::optional<model::ModelObject> modelObject = translateAndMapWorkspaceObject(wsObject.get());
      if (modelObject) {
        if (modelObject.get().cast<EnergyManagementSystemGlobalVariable>().name() == s) {
          emsOutputVariable.setEMSVariableName(*s);
        }
      }
    }
    //look for name match on other (EMS) objects.
    for (WorkspaceObject& wsObject : workspace.getObjectsByName(*s)) {
      boost::optional<model::ModelObject> modelObject = translateAndMapWorkspaceObject(wsObject);
      if (modelObject) {
        emsOutputVariable.setEMSVariableName(*s);
        break;
      }
    }
  }

  s = workspaceObject.getString(EnergyManagementSystem_OutputVariableFields::UpdateFrequency);
  if (!s) {
    LOG(Error, emsOutputVariable.nameString() + ": UpdateFrequency not set");
    return boost::none;
  } else {
    emsOutputVariable.setUpdateFrequency(*s);
  }

  s = workspaceObject.getString(EnergyManagementSystem_OutputVariableFields::TypeofDatainVariable);
  if (!s) {
    LOG(Error, emsOutputVariable.nameString() + ": TypeofDatainVariable not set");
    return boost::none;
  } else {
    emsOutputVariable.setTypeOfDataInVariable(*s);
  }

  s = workspaceObject.getString(EnergyManagementSystem_OutputVariableFields::Units);
  if (s) {
    emsOutputVariable.setUnits(*s);
  }

  s = workspaceObject.getString(EnergyManagementSystem_OutputVariableFields::EMSProgramorSubroutineName);
  if (s) {
    Workspace workspace = workspaceObject.workspace();
    for (WorkspaceObject& wsObject : workspace.getObjectsByName(*s)) {
      boost::optional<model::ModelObject> modelObject = translateAndMapWorkspaceObject(wsObject);
      if (modelObject) {
        if (modelObject.get().iddObjectType() == IddObjectType::OS_EnergyManagementSystem_Program) {
          emsOutputVariable.setEMSProgramOrSubroutineName(modelObject.get().cast<EnergyManagementSystemProgram>());
        } else if (modelObject.get().iddObjectType() == IddObjectType::OS_EnergyManagementSystem_Subroutine) {
          emsOutputVariable.setEMSProgramOrSubroutineName(modelObject.get().cast<EnergyManagementSystemSubroutine>());
        }
        return emsOutputVariable;
      }
    }
  }

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

  // this function creates a space and a thermal zone, it returns the space.  If you want the 
  // thermal zone you can reliably dereference the result of space.thermalZone().

  openstudio::model::ThermalZone thermalZone( m_model );

  openstudio::model::Space space( m_model );
  space.setThermalZone(thermalZone);

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

  OptionalString s = workspaceObject.name();
  if(s){
    space.setName(*s);
    thermalZone.setName(*s + " Thermal Zone");
    idfZoneName = *s;
  }

  OptionalDouble d = workspaceObject.getDouble(ZoneFields::DirectionofRelativeNorth);
  if(d){
    space.setDirectionofRelativeNorth(*d);
  }

  d=workspaceObject.getDouble(ZoneFields::XOrigin);
  if(d){
    space.setXOrigin(*d);
  }

  d=workspaceObject.getDouble(ZoneFields::YOrigin);
  if(d){
    space.setYOrigin(*d);
  }

  d=workspaceObject.getDouble(ZoneFields::ZOrigin);
  if(d){
    space.setZOrigin(*d);
  }

  OptionalInt i = workspaceObject.getInt(ZoneFields::Type);
  if(i){
    // no-op
  }

  i = workspaceObject.getInt(ZoneFields::Multiplier);
  if(i){
    thermalZone.setMultiplier(*i);
  }

  d = workspaceObject.getDouble(ZoneFields::CeilingHeight);
  if(d){
    thermalZone.setCeilingHeight(*d);
  }

  d=workspaceObject.getDouble(ZoneFields::Volume);
  if(d){
    thermalZone.setVolume(*d);
  }

  s = workspaceObject.getString(ZoneFields::ZoneInsideConvectionAlgorithm);
  if(s){
    thermalZone.setZoneInsideConvectionAlgorithm(*s);
  }

  s = workspaceObject.getString(ZoneFields::ZoneOutsideConvectionAlgorithm);
  if(s){
    thermalZone.setZoneOutsideConvectionAlgorithm(*s);
  }

  s = workspaceObject.getString(ZoneFields::PartofTotalFloorArea);
  if(s){
    if(istringEqual("Yes",*s))
    {
      space.setPartofTotalFloorArea(true);
    }
    else
    {
      space.setPartofTotalFloorArea(false);
    }
  }

  // Thermostat

  // If the zone in the idf file does not have a name, there is no use in even trying to find a thermostat
  if( idfZoneName )
  {
    Workspace workspace = workspaceObject.workspace();
    
    std::vector<WorkspaceObject> _zoneControlThermostats;
    _zoneControlThermostats = workspace.getObjectsByType(IddObjectType::ZoneControl_Thermostat);

    for( const auto & _zoneControlThermostat : _zoneControlThermostats )
    {
      if( boost::optional<std::string> zoneName = _zoneControlThermostat.getString( ZoneControl_ThermostatFields::ZoneorZoneListName ) )
      {
        bool zoneControlThermostatfound = false;

        if( zoneName.get() == idfZoneName ) 
        { 
          zoneControlThermostatfound = true; 
        }
        else if( boost::optional<WorkspaceObject> _zoneList = workspace.getObjectByTypeAndName(IddObjectType::ZoneList,zoneName.get()) )
        {
          std::vector<IdfExtensibleGroup> zoneListGroup = _zoneList->extensibleGroups();

          for( const auto & zoneListElem : zoneListGroup )
          {
            boost::optional<std::string> zoneListZoneName = zoneListElem.getString(ZoneListExtensibleFields::ZoneName);
            if( zoneListZoneName )
            {
              if( zoneListZoneName.get() == idfZoneName ) { zoneControlThermostatfound = true; }
              break;
            }
          }
        }
        if( zoneControlThermostatfound )
        {
          std::vector<IdfExtensibleGroup> extensibleGroups = _zoneControlThermostat.extensibleGroups();
          for( const auto & extensibleGroup : extensibleGroups )
          {
            boost::optional<std::string> thermostatType = extensibleGroup.getString(ZoneControl_ThermostatExtensibleFields::ControlObjectType);
            boost::optional<std::string> thermostatName = extensibleGroup.getString(ZoneControl_ThermostatExtensibleFields::ControlName);

            if( thermostatName && thermostatType )
            {
              boost::optional<WorkspaceObject> _thermostat
               = workspace.getObjectByTypeAndName(IddObjectType(thermostatType.get()),thermostatName.get());
              
              if( _thermostat )
              {
                boost::optional<ModelObject> thermostat = translateAndMapWorkspaceObject(_thermostat.get());
                if( thermostat )
                {
                  if( boost::optional<ThermostatSetpointDualSetpoint> thermostatSetpointDualSetpoint
                      = thermostat->optionalCast<ThermostatSetpointDualSetpoint>() )
                  {
                    thermalZone.setThermostatSetpointDualSetpoint(thermostatSetpointDualSetpoint.get());
                  }
                }
              }
            }
          }
          break;
        }
      }
    }
  }

  // Zone Equipment
/*
  if( idfZoneName )
  {
    std::vector<WorkspaceObject> zoneHVACEquipmentConnections;
    zoneHVACEquipmentConnections = workspaceObject.workspace().getObjectsByType(IddObjectType::ZoneHVAC_EquipmentConnections);

    for( std::vector<WorkspaceObject>::iterator it = zoneHVACEquipmentConnections.begin();
         it != zoneHVACEquipmentConnections.end();
         it++ )
    {
      s = it->getString(ZoneHVAC_EquipmentConnectionsFields::ZoneName);

      if( s && istringEqual(s.get(),idfZoneName.get()) )
      {
        boost::optional<WorkspaceObject> _zoneEquipmentList = it->getTarget(ZoneHVAC_EquipmentConnectionsFields::ZoneConditioningEquipmentListName);

        if( _zoneEquipmentList )
        {
          translateAndMapWorkspaceObject(_zoneEquipmentList.get());
        }

        break;
      }
    }
  }
*/

  return space;
}
/* This tests ensures that the CentralHeatPumpSystem ends up in a PlantEquipmentOperation:HeatingLoad for the heating loop
 * and PlantEquipmentOperation:CoolingLoad for the cooling loop. For the source loop, it's on the demand side so it shouldn't
 * be part of the plant equipment list used
 */
TEST_F(EnergyPlusFixture,ForwardTranslatorCentralHeatPumpSystem_PlantEquipmentOperation) {

  boost::optional<WorkspaceObject> _wo;
  ForwardTranslator ft;

  Model m;

  CentralHeatPumpSystem central_hp(m);

  // Add a Module
  CentralHeatPumpSystemModule central_hp_module(m);
  central_hp.addModule(central_hp_module);
  EXPECT_EQ(1, central_hp_module.numberofChillerHeaterModules());

  // Connect the CentralHP to three plant loops
  // CoolingLoop: on the supply side
  PlantLoop coolingPlant(m);
  EXPECT_TRUE(coolingPlant.addSupplyBranchForComponent(central_hp));

  // SourceLoop: on the demand side
  PlantLoop sourcePlant(m);
  EXPECT_TRUE(sourcePlant.addDemandBranchForComponent(central_hp));
  // Also add a CT to the sourcePlant
  CoolingTowerSingleSpeed ct(m);
  sourcePlant.addSupplyBranchForComponent(ct);

  // HeatingLoop: on the supply side
  PlantLoop heatingPlant(m);
  // Workaround to be able to use addToTertiaryNode directly
  // (addSupplyBranchForComponent should work directly, but this is tested in model GTest, so here we just make sure we call
  // addToTertiaryNode directly)
  BoilerHotWater temp_b(m);
  EXPECT_TRUE(heatingPlant.addSupplyBranchForComponent(temp_b));

  ASSERT_TRUE(temp_b.inletModelObject());
  auto node = temp_b.inletModelObject().get().cast<Node>();
  EXPECT_TRUE(central_hp.addToTertiaryNode(node));
  temp_b.remove();



  Workspace w = ft.translateModel(m);

  EXPECT_EQ(0u, ft.errors().size());
  EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::CentralHeatPumpSystem).size());

  IdfObject i_central_hp = w.getObjectsByType(IddObjectType::CentralHeatPumpSystem)[0];



  // Get the Loops, and find their plant operation scheme

  // supply = Cooling
  {
    _wo = w.getObjectByTypeAndName(IddObjectType::PlantLoop, coolingPlant.name().get());
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_coolingPlant = _wo.get();
    WorkspaceObject idf_plant_op = idf_coolingPlant.getTarget(PlantLoopFields::PlantEquipmentOperationSchemeName).get();

    // Should have created a Cooling Load one only
    ASSERT_EQ(1u, idf_plant_op.extensibleGroups().size());
    WorkspaceExtensibleGroup w_eg = idf_plant_op.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();
    ASSERT_EQ("PlantEquipmentOperation:CoolingLoad", w_eg.getString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType).get());

    // Get the Operation Scheme
    _wo = w_eg.getTarget(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName);
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_op_scheme = _wo.get();

    // Get the Plant Equipment List of this CoolingLoad scheme
    // There should only be one Load Range
    ASSERT_EQ(1u, idf_op_scheme.extensibleGroups().size());
    // Load range 1
    w_eg = idf_op_scheme.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();
    _wo = w_eg.getTarget(PlantEquipmentOperation_CoolingLoadExtensibleFields::RangeEquipmentListName);
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_peq_list = _wo.get();

    // Should have one equipment on it: CentralHeatPumpSystem
    ASSERT_EQ(1u, idf_peq_list.extensibleGroups().size());
    IdfExtensibleGroup idf_eg(idf_peq_list.extensibleGroups()[0]);
    ASSERT_EQ(central_hp.name().get(), idf_eg.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  }


  // tertiary = Heating
  {
    _wo = w.getObjectByTypeAndName(IddObjectType::PlantLoop, heatingPlant.name().get());
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_heatingPlant = _wo.get();
    WorkspaceObject idf_plant_op = idf_heatingPlant.getTarget(PlantLoopFields::PlantEquipmentOperationSchemeName).get();

    // Should have created a Heating Load one only
    ASSERT_EQ(1u, idf_plant_op.extensibleGroups().size());
    WorkspaceExtensibleGroup w_eg = idf_plant_op.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();
    ASSERT_EQ("PlantEquipmentOperation:HeatingLoad", w_eg.getString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType).get());

    // Get the Operation Scheme
    _wo = w_eg.getTarget(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName);
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_op_scheme = _wo.get();

    // Get the Plant Equipment List of this HeatingLoad scheme
    // There should only be one Load Range
    ASSERT_EQ(1u, idf_op_scheme.extensibleGroups().size());
    // Load range 1
    w_eg = idf_op_scheme.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();
    _wo = w_eg.getTarget(PlantEquipmentOperation_HeatingLoadExtensibleFields::RangeEquipmentListName);
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_peq_list = _wo.get();

    // Should have one equipment on it: CentralHeatPumpSystem
    ASSERT_EQ(1u, idf_peq_list.extensibleGroups().size());
    IdfExtensibleGroup idf_eg(idf_peq_list.extensibleGroups()[0]);
    ASSERT_EQ(central_hp.name().get(), idf_eg.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  }



  // SourceLoop: on the demand side. So it shouldn't be on it
  {
    _wo = w.getObjectByTypeAndName(IddObjectType::PlantLoop, sourcePlant.name().get());
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_sourcePlant = _wo.get();
    WorkspaceObject idf_plant_op = idf_sourcePlant.getTarget(PlantLoopFields::PlantEquipmentOperationSchemeName).get();

    // Should have created a Cooling Load one only
    ASSERT_EQ(1u, idf_plant_op.extensibleGroups().size());
    WorkspaceExtensibleGroup w_eg = idf_plant_op.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();
    ASSERT_EQ("PlantEquipmentOperation:CoolingLoad", w_eg.getString(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeObjectType).get());

    // Get the Operation Scheme
    _wo = w_eg.getTarget(PlantEquipmentOperationSchemesExtensibleFields::ControlSchemeName);
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_op_scheme = _wo.get();

    // Get the Plant Equipment List of this HeatingLoad scheme
    // There should only be one Load Range
    ASSERT_EQ(1u, idf_op_scheme.extensibleGroups().size());
    // Load range 1
    w_eg = idf_op_scheme.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();
    _wo = w_eg.getTarget(PlantEquipmentOperation_CoolingLoadExtensibleFields::RangeEquipmentListName);
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_peq_list = _wo.get();

    // Should have one equipment on it: Cooling Tower
    ASSERT_EQ(1u, idf_peq_list.extensibleGroups().size());
    IdfExtensibleGroup idf_eg(idf_peq_list.extensibleGroups()[0]);
    ASSERT_EQ(ct.name().get(), idf_eg.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  }

  m.save(toPath("./ft_central_hp.osm"), true);
  w.save(toPath("./ft_central_hp.idf"), true);

}
/* Ensures that the nodes that translated correctly
 * that means correct node names in the CentralHeatPumpSystem but also
 * on the Branches
 */
TEST_F(EnergyPlusFixture,ForwardTranslatorCentralHeatPumpSystem_Nodes) {

  boost::optional<WorkspaceObject> _wo;
  ForwardTranslator ft;

  Model m;

  CentralHeatPumpSystem central_hp(m);
  ASSERT_TRUE(central_hp.setAncillaryPower(0.7));
  EXPECT_FALSE(central_hp.ancillaryOperationSchedule());
  Schedule schedule = m.alwaysOnDiscreteSchedule();

  // Return type: bool
  ASSERT_TRUE(central_hp.setAncillaryOperationSchedule(schedule));
  EXPECT_TRUE(central_hp.ancillaryOperationSchedule());

  // First module has one
  CentralHeatPumpSystemModule central_hp_module(m);
  central_hp.addModule(central_hp_module);
  EXPECT_EQ(1, central_hp_module.numberofChillerHeaterModules());

  // Second has 2
  CentralHeatPumpSystemModule central_hp_module2(m);
  central_hp.addModule(central_hp_module2);
  ASSERT_TRUE(central_hp_module2.setNumberofChillerHeaterModules(2));


  ASSERT_EQ( (unsigned)2, central_hp.modules().size() );


  // Connect the CentralHP to three plant loops
  // CoolingLoop: on the supply side
  PlantLoop coolingPlant(m);
  EXPECT_TRUE(coolingPlant.addSupplyBranchForComponent(central_hp));

  // SourceLoop: on the demand side
  PlantLoop sourcePlant(m);
  EXPECT_TRUE(sourcePlant.addDemandBranchForComponent(central_hp));
  // Also add a CT to the sourcePlant
  CoolingTowerSingleSpeed ct(m);
  sourcePlant.addSupplyBranchForComponent(ct);

  // HeatingLoop: on the supply side
  PlantLoop heatingPlant(m);
  // Workaround to be able to use addToTertiaryNode directly
  // (addSupplyBranchForComponent should work directly, but this is tested in model GTest, so here we just make sure we call
  // addToTertiaryNode directly)
  BoilerHotWater temp_b(m);
  EXPECT_TRUE(heatingPlant.addSupplyBranchForComponent(temp_b));

  ASSERT_TRUE(temp_b.inletModelObject());
  auto node = temp_b.inletModelObject().get().cast<Node>();
  EXPECT_TRUE(central_hp.addToTertiaryNode(node));
  temp_b.remove();


  // Translate
  Workspace w = ft.translateModel(m);

  EXPECT_EQ(0u, ft.errors().size());
  EXPECT_EQ(1u, w.getObjectsByType(IddObjectType::CentralHeatPumpSystem).size());

  IdfObject i_central_hp = w.getObjectsByType(IddObjectType::CentralHeatPumpSystem)[0];

  // supply = Cooling
  ASSERT_EQ(i_central_hp.getString(CentralHeatPumpSystemFields::CoolingLoopInletNodeName).get(),
            central_hp.supplyInletModelObject().get().name());

  ASSERT_EQ(i_central_hp.getString(CentralHeatPumpSystemFields::CoolingLoopOutletNodeName).get(),
            central_hp.supplyOutletModelObject().get().name());

  // demand = Source
  ASSERT_EQ(i_central_hp.getString(CentralHeatPumpSystemFields::SourceLoopInletNodeName).get(),
            central_hp.demandInletModelObject().get().name());

  ASSERT_EQ(i_central_hp.getString(CentralHeatPumpSystemFields::SourceLoopOutletNodeName).get(),
            central_hp.demandOutletModelObject().get().name());

  // tertiary = Heating
  ASSERT_EQ(i_central_hp.getString(CentralHeatPumpSystemFields::HeatingLoopInletNodeName).get(),
            central_hp.tertiaryInletModelObject().get().name());

  ASSERT_EQ(i_central_hp.getString(CentralHeatPumpSystemFields::HeatingLoopOutletNodeName).get(),
            central_hp.tertiaryOutletModelObject().get().name());


  // Check node names on supply/demand branches
  // Checks that the special case implemented in ForwardTranslatePlantLoop::populateBranch does the right job

  // supply = Cooling (on supply)
  {
    _wo = w.getObjectByTypeAndName(IddObjectType::PlantLoop, coolingPlant.name().get());
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_plant = _wo.get();
    WorkspaceObject idf_brlist = idf_plant.getTarget(PlantLoopFields::PlantSideBranchListName).get();

    // Should have three branches: supply inlet, the one with the centralHP, supply outlet
    ASSERT_EQ(3u, idf_brlist.extensibleGroups().size());
    // Get the Central HP one
    WorkspaceExtensibleGroup w_eg = idf_brlist.extensibleGroups()[1].cast<WorkspaceExtensibleGroup>();
    WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get();

    // There should be only one equipment on the branch
    ASSERT_EQ(1u, idf_branch.extensibleGroups().size());
    WorkspaceExtensibleGroup w_eg2 = idf_branch.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentName).get(),
        central_hp.name());

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(),
        central_hp.supplyInletModelObject().get().name());

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(),
        central_hp.supplyOutletModelObject().get().name());
  }

  // tertiary = Heating (on supply)
  {
    _wo = w.getObjectByTypeAndName(IddObjectType::PlantLoop, heatingPlant.name().get());
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_plant = _wo.get();
    WorkspaceObject idf_brlist = idf_plant.getTarget(PlantLoopFields::PlantSideBranchListName).get();

    // Should have three branches: supply inlet, the one with the centralHP, supply outlet
    ASSERT_EQ(3u, idf_brlist.extensibleGroups().size());
    // Get the Central HP one
    WorkspaceExtensibleGroup w_eg = idf_brlist.extensibleGroups()[1].cast<WorkspaceExtensibleGroup>();
    WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get();

    // There should be only one equipment on the branch
    ASSERT_EQ(1u, idf_branch.extensibleGroups().size());
    WorkspaceExtensibleGroup w_eg2 = idf_branch.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentName).get(),
        central_hp.name());

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(),
        central_hp.tertiaryInletModelObject().get().name());

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(),
        central_hp.tertiaryOutletModelObject().get().name());
  }

  // demand = Source (on demand)
  {
    _wo = w.getObjectByTypeAndName(IddObjectType::PlantLoop, sourcePlant.name().get());
    ASSERT_TRUE(_wo.is_initialized());
    WorkspaceObject idf_plant = _wo.get();
    // Demand Side Branch List Name
    WorkspaceObject idf_brlist = idf_plant.getTarget(PlantLoopFields::DemandSideBranchListName).get();

    // Should have four branches: supply inlet, the one with the centralHP, a bypass, supply outlet
    ASSERT_EQ(4u, idf_brlist.extensibleGroups().size());
    // Get the Central HP one, which should be the second one
    WorkspaceExtensibleGroup w_eg = idf_brlist.extensibleGroups()[1].cast<WorkspaceExtensibleGroup>();
    WorkspaceObject idf_branch = w_eg.getTarget(BranchListExtensibleFields::BranchName).get();

    // There should be only one equipment on the branch
    ASSERT_EQ(1u, idf_branch.extensibleGroups().size());
    WorkspaceExtensibleGroup w_eg2 = idf_branch.extensibleGroups()[0].cast<WorkspaceExtensibleGroup>();

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentName).get(),
        central_hp.name());

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentInletNodeName).get(),
        central_hp.demandInletModelObject().get().name());

    ASSERT_EQ(w_eg2.getString(BranchExtensibleFields::ComponentOutletNodeName).get(),
        central_hp.demandOutletModelObject().get().name());
  }

  // m.save(toPath("./ft_central_hp.osm"), true);
  // w.save(toPath("./ft_central_hp.idf"), true);

}
/**
 * Tests whether the ForwarTranslator will handle the name of the GeneratorMicroTurbine correctly in the PlantEquipmentOperationHeatingLoad
 **/
TEST_F(EnergyPlusFixture,ForwardTranslatorGeneratorMicroTurbine_ELCD_PlantLoop)
{

  // TODO: Temporarily output the Log in the console with the Trace (-3) level
  // for debug
  // openstudio::Logger::instance().standardOutLogger().enable();
  // openstudio::Logger::instance().standardOutLogger().setLogLevel(Trace);


  // Create a model, a mchp, a mchpHR, a plantLoop and an electricalLoadCenter
  Model model;

  GeneratorMicroTurbine mchp = GeneratorMicroTurbine(model);
  GeneratorMicroTurbineHeatRecovery mchpHR = GeneratorMicroTurbineHeatRecovery(model, mchp);
  ASSERT_EQ(mchpHR, mchp.generatorMicroTurbineHeatRecovery().get());

  PlantLoop plantLoop(model);
  // Add a supply branch for the mchpHR
  ASSERT_TRUE(plantLoop.addSupplyBranchForComponent(mchpHR));

  // Create a WaterHeater:Mixed
  WaterHeaterMixed waterHeater(model);
  // Add it on the same branch as the chpHR, right after it
  Node mchpHROutletNode = mchpHR.outletModelObject()->cast<Node>();
  ASSERT_TRUE(waterHeater.addToNode(mchpHROutletNode));

  // Create a plantEquipmentOperationHeatingLoad
  PlantEquipmentOperationHeatingLoad operation(model);
  operation.setName(plantLoop.name().get() + " PlantEquipmentOperationHeatingLoad");
  ASSERT_TRUE(plantLoop.setPlantEquipmentOperationHeatingLoad(operation));
  ASSERT_TRUE(operation.addEquipment(mchpHR));
  ASSERT_TRUE(operation.addEquipment(waterHeater));

  // Create an ELCD
  ElectricLoadCenterDistribution elcd = ElectricLoadCenterDistribution(model);
  elcd.setName("Capstone C65 ELCD");
  elcd.setElectricalBussType("AlternatingCurrent");
  elcd.addGenerator(mchp);

  // Forward Translates
  ForwardTranslator forwardTranslator;
  Workspace workspace = forwardTranslator.translateModel(model);

  EXPECT_EQ(0u, forwardTranslator.errors().size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::WaterHeater_Mixed).size());
  ASSERT_EQ(1u, workspace.getObjectsByType(IddObjectType::ElectricLoadCenter_Distribution).size());
  // The MicroTurbine should have been forward translated since there is an ELCD

  WorkspaceObjectVector microTurbineObjects(workspace.getObjectsByType(IddObjectType::Generator_MicroTurbine));
  EXPECT_EQ(1u, microTurbineObjects.size());
  // Check that the HR nodes have been set
  WorkspaceObject idf_mchp(microTurbineObjects[0]);
  EXPECT_EQ(mchpHR.inletModelObject()->name().get(), idf_mchp.getString(Generator_MicroTurbineFields::HeatRecoveryWaterInletNodeName).get());
  EXPECT_EQ(mchpHR.outletModelObject()->name().get(), idf_mchp.getString(Generator_MicroTurbineFields::HeatRecoveryWaterOutletNodeName).get());

  OptionalWorkspaceObject idf_operation(workspace.getObjectByTypeAndName(IddObjectType::PlantEquipmentOperation_HeatingLoad,*(operation.name())));
  ASSERT_TRUE(idf_operation);
  // Get the extensible
  ASSERT_EQ(1u, idf_operation->numExtensibleGroups());
  // IdfExtensibleGroup eg = idf_operation.getExtensibleGroup(0);
   // idf_operation.targets[0]
  ASSERT_EQ(1u, idf_operation->targets().size());
  WorkspaceObject plantEquipmentList(idf_operation->targets()[0]);
  ASSERT_EQ(2u, plantEquipmentList.extensibleGroups().size());

  IdfExtensibleGroup eg(plantEquipmentList.extensibleGroups()[0]);
  ASSERT_EQ("Generator:MicroTurbine", eg.getString(PlantEquipmentListExtensibleFields::EquipmentObjectType).get());
  // This fails
  EXPECT_EQ(mchp.name().get(), eg.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  IdfExtensibleGroup eg2(plantEquipmentList.extensibleGroups()[1]);
  ASSERT_EQ("WaterHeater:Mixed", eg2.getString(PlantEquipmentListExtensibleFields::EquipmentObjectType).get());
  EXPECT_EQ(waterHeater.name().get(), eg2.getString(PlantEquipmentListExtensibleFields::EquipmentName).get());

  model.save(toPath("./ForwardTranslatorGeneratorMicroTurbine_ELCD_PlantLoop.osm"), true);
  workspace.save(toPath("./ForwardTranslatorGeneratorMicroTurbine_ELCD_PlantLoop.idf"), true);

}