bool LifeCycleCostUsePriceEscalation_Impl::setYearEscalation(unsigned index, double num) {
  IdfExtensibleGroup eg = getExtensibleGroup(index);
  if (!eg.empty()) {
    return eg.setDouble(OS_LifeCycleCost_UsePriceEscalationExtensibleFields::YearEscalation,num);
  }
  else {
    StringVector values(1u);
    eg = insertExtensibleGroup(index,values);
    if (!eg.empty()) { 
      return eg.setDouble(OS_LifeCycleCost_UsePriceEscalationExtensibleFields::YearEscalation,num);
    }
  }
  return false;
}
boost::optional<IdfObject> ForwardTranslator::translateMaterialPropertyGlazingSpectralData( MaterialPropertyGlazingSpectralData & modelObject )
{
  IdfObject idfObject(openstudio::IddObjectType::MaterialProperty_GlazingSpectralData);

  m_idfObjects.push_back(idfObject);

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

  idfObject.clearExtensibleGroups();
  for (const auto& spectralDataField : modelObject.spectralDataFields()){
    IdfExtensibleGroup group = idfObject.pushExtensibleGroup();
    OS_ASSERT(group.numFields() == 4);
    group.setDouble(MaterialProperty_GlazingSpectralDataExtensibleFields::Wavelength, spectralDataField.wavelength());
    group.setDouble(MaterialProperty_GlazingSpectralDataExtensibleFields::transmittance, spectralDataField.transmittance());
    group.setDouble(MaterialProperty_GlazingSpectralDataExtensibleFields::frontreflectance, spectralDataField.frontReflectance());
    group.setDouble(MaterialProperty_GlazingSpectralDataExtensibleFields::backreflectance, spectralDataField.backReflectance());
  }

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

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

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

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

  scheduleDay.clearExtensibleGroups();

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

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

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

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

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

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

  return scheduleDay;
}
Beispiel #4
0
  bool ScheduleDay_Impl::addValue(const openstudio::Time& untilTime, double value) {
    if (untilTime.totalMinutes() <= 0.5 || untilTime.totalDays() > 1.0) {
      return false;
    }

    int untilHours = untilTime.hours() + 24*untilTime.days();
    int untilMinutes = untilTime.minutes() + floor((untilTime.seconds()/60.0) + 0.5);

    if (untilMinutes >= 60){
      untilHours += 1;
      untilMinutes += -60;
    }

    // use set to determine whether to overwrite or insert, and where
    std::set<openstudio::Time> times;
    std::pair<std::set<openstudio::Time>::const_iterator,bool> insertResult;
    for (const openstudio::Time& time : this->times()) {
      insertResult = times.insert(time);
      OS_ASSERT(insertResult.second);
    }

    insertResult = times.insert(untilTime);
    unsigned index = std::distance<std::set<openstudio::Time>::const_iterator>(times.begin(),insertResult.first);
    OS_ASSERT(index <= numExtensibleGroups());
    bool result(true);
    if (insertResult.second) {
      // new time--insert an extensible group
      std::vector<std::string> groupValues;
      groupValues.push_back(boost::lexical_cast<std::string>(untilHours));
      groupValues.push_back(boost::lexical_cast<std::string>(untilMinutes));
      groupValues.push_back(toString(value));

      IdfExtensibleGroup group = insertExtensibleGroup(index, groupValues);
      OS_ASSERT(!group.empty());
    }
    else {
      // time already exists, overwrite value
      IdfExtensibleGroup group = getExtensibleGroup(index);
      result = group.setDouble(OS_Schedule_DayExtensibleFields::ValueUntilTime,value);
    }

    return result;
  }
void makeUsePriceEscalation(boost::optional<double> rate, const std::string& fuelName, const std::string& baseDateMonth, int baseDateYear, int lengthOfStudyPeriodInYears, std::vector<IdfObject>& idfObjects)
{
  if (!rate || rate.get() == 0){
    return;
  }

  IdfObject idfObject(IddObjectType::LifeCycleCost_UsePriceEscalation);
  idfObject.setName(fuelName + " Use Price Escalation");
  idfObject.setString(LifeCycleCost_UsePriceEscalationFields::Resource, fuelName);
  idfObject.setInt(LifeCycleCost_UsePriceEscalationFields::EscalationStartYear, baseDateYear);
  idfObject.setString(LifeCycleCost_UsePriceEscalationFields::EscalationStartMonth, baseDateMonth);

  for (int i = 0; i < lengthOfStudyPeriodInYears; ++i){
    IdfExtensibleGroup group = idfObject.pushExtensibleGroup();
    double value = pow(1.0+rate.get(), i);
    group.setDouble(LifeCycleCost_UsePriceEscalationExtensibleFields::YearEscalation, value);
  }

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

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

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

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

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

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

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

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

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

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

    applyDefault = false;
  } 

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

    applyDefault = false;
  }

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

    createSetpointOperationScheme(plantLoop);
    applyDefault = false;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    createSetpointOperationScheme(plantLoop);
  }

  return operationSchemes;
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACBaseboardRadiantConvectiveElectric(ZoneHVACBaseboardRadiantConvectiveElectric& modelObject)
{
  boost::optional<std::string> s;
  boost::optional<double> value;
  boost::optional<ModelObject> temp;

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

  // AvailabilityScheduleName
  {
    auto schedule = modelObject.availabilitySchedule();
    if( auto _schedule = translateAndMapModelObject(schedule) ) {
      idfObject.setString(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::AvailabilityScheduleName,_schedule->name().get());
    }
  }

  // HeatingDesignCapacityMethod
  if( (s = modelObject.heatingDesignCapacityMethod()) ) {
    idfObject.setString(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::HeatingDesignCapacityMethod,s.get());
  }

  // HeatingDesignCapacity
  if( modelObject.isHeatingDesignCapacityAutosized() ) {
    idfObject.setString(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::HeatingDesignCapacity,"AutoSize");
  } else if( (value = modelObject.heatingDesignCapacity()) ) {
    idfObject.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::HeatingDesignCapacity,value.get());
  }

  // HeatingDesignCapacityPerFloorArea
  if( (value = modelObject.heatingDesignCapacityPerFloorArea()) ) {
    idfObject.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::HeatingDesignCapacityPerFloorArea,value.get());
  }

  // FractionofAutosizedHeatingDesignCapacity
  if( (value = modelObject.fractionofAutosizedHeatingDesignCapacity()) ) {
    idfObject.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::FractionofAutosizedHeatingDesignCapacity,value.get());
  }

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

  // FractionRadiant
  if( (value = modelObject.fractionRadiant()) ) {
    idfObject.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::FractionRadiant,value.get());
  }

  // FractionofRadiantEnergyIncidentonPeople
  double fractionofRadiantEnergyIncidentonPeople = modelObject.fractionofRadiantEnergyIncidentonPeople();
  {
    idfObject.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricFields::FractionofRadiantEnergyIncidentonPeople,fractionofRadiantEnergyIncidentonPeople);
  }

  //get rid of any existing surface (just to be safe)
  idfObject.clearExtensibleGroups();  
 
  //aggregator for total area; will be used to create weighted area
  double totalAreaOfWallSurfaces = 0;
  double totalAreaOfCeilingSurfaces = 0;
  double totalAreaOfFloorSurfaces = 0;

  //loop through all surfaces, adding up their area
  auto const& surfaces = modelObject.getImpl<model::detail::ZoneHVACBaseboardRadiantConvectiveElectric_Impl>()->surfaces();

  for ( auto const& surface : surfaces ) {
    if( istringEqual(surface.surfaceType(),"Floor") ) {
      totalAreaOfFloorSurfaces += surface.grossArea();
    } else if( istringEqual(surface.surfaceType(),"RoofCeiling") ) {
      totalAreaOfCeilingSurfaces += surface.grossArea();
    } else {
      totalAreaOfWallSurfaces += surface.grossArea();
    }
  }

  // Assume that 5% of what is not on people is on the floor
  double fractionOnFloor = (1.0 - fractionofRadiantEnergyIncidentonPeople) * 0.05;
  // Assume that 55% of what is not on people is on the walls
  double fractionOnWall = (1.0 - fractionofRadiantEnergyIncidentonPeople) * 0.55;
  // Assume that 40% of what is not on people is on the ceiling
  double fractionOnCeiling = (1.0 - fractionofRadiantEnergyIncidentonPeople) * 0.40;
  //loop through all the surfaces, adding them and their flow fractions (weighted per-area)
  for ( auto const& surface : surfaces ) {
    IdfExtensibleGroup group = idfObject.pushExtensibleGroup();
    group.setString(ZoneHVAC_Baseboard_RadiantConvective_ElectricExtensibleFields::SurfaceName, surface.name().get());        
    if( istringEqual(surface.surfaceType(),"Floor") ) {
      group.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricExtensibleFields::FractionofRadiantEnergytoSurface, (surface.grossArea()/totalAreaOfFloorSurfaces * fractionOnFloor) );     
    } else if( istringEqual(surface.surfaceType(),"RoofCeiling") ) {
      group.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricExtensibleFields::FractionofRadiantEnergytoSurface, (surface.grossArea()/totalAreaOfCeilingSurfaces * fractionOnCeiling) );     
    } else {
      group.setDouble(ZoneHVAC_Baseboard_RadiantConvective_ElectricExtensibleFields::FractionofRadiantEnergytoSurface, (surface.grossArea()/totalAreaOfWallSurfaces * fractionOnWall) );     
    }
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateRefrigerationWalkIn( RefrigerationWalkIn & modelObject )
{
  OptionalModelObject temp;
  boost::optional<std::string> s;
  boost::optional<double> d;

// Name
  IdfObject refrigerationWalkIn = createRegisterAndNameIdfObject(openstudio::IddObjectType::Refrigeration_WalkIn,
                                                          modelObject);

//AvailabilityScheduleName
  boost::optional<Schedule> availabilitySchedule = modelObject.availabilitySchedule();

  if( availabilitySchedule )
  {
    boost::optional<IdfObject> _availabilitySchedule = translateAndMapModelObject(availabilitySchedule.get());

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

//RatedCoilCoolingCapacity
  d = modelObject.ratedCoilCoolingCapacity();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCoilCoolingCapacity,d.get());
  }

//OperatingTemperature
  d = modelObject.operatingTemperature();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::OperatingTemperature,d.get());
  }

//RatedCoolingSourceTemperature
  d = modelObject.ratedCoolingSourceTemperature();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCoolingSourceTemperature,d.get());
  }

//RatedTotalHeatingPower
  d = modelObject.ratedTotalHeatingPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedTotalHeatingPower,d.get());
  }

//HeatingPowerScheduleName
  boost::optional<Schedule> heatingPowerSchedule = modelObject.heatingPowerSchedule();

  if( heatingPowerSchedule )
  {
    boost::optional<IdfObject> _heatingPowerSchedule = translateAndMapModelObject(heatingPowerSchedule.get());

    if( _heatingPowerSchedule && _heatingPowerSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::HeatingPowerScheduleName,_heatingPowerSchedule->name().get());
    }
  }

//RatedCoolingCoilFanPower
  d = modelObject.ratedCoolingCoilFanPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCoolingCoilFanPower,d.get());
  }

//RatedCirculationFanPower
  d = modelObject.ratedCirculationFanPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCirculationFanPower,d.get());
  }

//RatedTotalLightingPower
  d = modelObject.ratedTotalLightingPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedTotalLightingPower,d.get());
  }

//LightingScheduleName
  boost::optional<Schedule> lightingSchedule = modelObject.lightingSchedule();

  if( lightingSchedule )
  {
    boost::optional<IdfObject> _lightingSchedule = translateAndMapModelObject(lightingSchedule.get());

    if( _lightingSchedule && _lightingSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::LightingScheduleName,_lightingSchedule->name().get());
    }
  }

//DefrostType
  s = modelObject.defrostType();
  if (s) {
    refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostType,s.get());
  }

//DefrostControlType
  s = modelObject.defrostControlType();
  if (s) {
    refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostControlType,s.get());
  }

//DefrostCycleParameters
  boost::optional<int> durationofDefrostCycle = modelObject.durationofDefrostCycle();
  boost::optional<int> dripDownTime = modelObject.dripDownTime();
  std::vector<openstudio::Time> defrostStartTimes = modelObject.getImpl<model::detail::RefrigerationWalkIn_Impl>()->defrostStartTimes();

  if( durationofDefrostCycle && dripDownTime && !defrostStartTimes.empty() ) {
    int defrostTimeHour = *durationofDefrostCycle / 60;
    int defrostTimeMin = *durationofDefrostCycle % 60;
    int dripDownTimeHour = *dripDownTime / 60;
    int dripDownTimeMin = *dripDownTime % 60;

    std::vector< std::pair<openstudio::Time, double> > defrostDefaultDay;
    std::vector< std::pair<openstudio::Time, double> > dripDownDefaultDay;
    for( auto _defrostStartTime = defrostStartTimes.begin();
       _defrostStartTime != defrostStartTimes.end();
       ++_defrostStartTime )
    {
      defrostDefaultDay.push_back(std::make_pair(*_defrostStartTime, 0)); // defrost off
      openstudio::Time defrostEndTime(0, _defrostStartTime->hours() + defrostTimeHour, _defrostStartTime->minutes() + defrostTimeMin);
      defrostDefaultDay.push_back(std::make_pair(defrostEndTime, 1)); // defrost on

      dripDownDefaultDay.push_back(std::make_pair(*_defrostStartTime, 0)); // drip down off
      openstudio::Time dripDownEndTime(0, _defrostStartTime->hours() + defrostTimeHour + dripDownTimeHour, _defrostStartTime->minutes() + defrostTimeMin + dripDownTimeMin);
      dripDownDefaultDay.push_back(std::make_pair(dripDownEndTime, 1)); // drip down on
    }

    if( (defrostStartTimes.front().hours() != 0 && defrostStartTimes.front().minutes() != 0) || defrostStartTimes.back().hours() < 24) {
      openstudio::Time defrostDayEnd(0, 24, 0);
      defrostDefaultDay.push_back(std::make_pair(defrostDayEnd, 0)); // defrost off
      dripDownDefaultDay.push_back(std::make_pair(defrostDayEnd, 0)); // drip down off
    }

    //DefrostScheduleName
    std::string defrostName(modelObject.name().get() + " Defrost Schedule");
    boost::optional<IdfObject> defrostSchedule = this->createSimpleSchedule(defrostName, defrostDefaultDay);
    if( defrostSchedule ) {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostScheduleName, defrostName);
    }

    //DefrostDripDownScheduleName
    std::string dripDownName(modelObject.name().get() + " Defrost Drip Down Schedule");
    boost::optional<IdfObject> defrostDripDownSchedule = this->createSimpleSchedule(dripDownName, dripDownDefaultDay);
    if( defrostDripDownSchedule ) {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostDripDownScheduleName, dripDownName);
    }
  } else {
  //DefrostScheduleName
    boost::optional<Schedule> defrostSchedule = modelObject.defrostSchedule();

    if( defrostSchedule )
    {
      boost::optional<IdfObject> _defrostSchedule = translateAndMapModelObject(defrostSchedule.get());

      if( _defrostSchedule && _defrostSchedule->name() )
      {
        refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostScheduleName,_defrostSchedule->name().get());
      }
    }

  //DefrostDripDownScheduleName
    boost::optional<Schedule> defrostDripDownSchedule = modelObject.defrostDripDownSchedule();

    if( defrostDripDownSchedule )
    {
      boost::optional<IdfObject> _defrostDripDownSchedule = translateAndMapModelObject(defrostDripDownSchedule.get());

      if( _defrostDripDownSchedule && _defrostDripDownSchedule->name() )
      {
        refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostDripDownScheduleName,_defrostDripDownSchedule->name().get());
      }
    }
  }

//DefrostPower
  d = modelObject.defrostPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::DefrostPower,d.get());
  }

//TemperatureTerminationDefrostFractiontoIce
  d = modelObject.temperatureTerminationDefrostFractiontoIce();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::TemperatureTerminationDefrostFractiontoIce,d.get());
  }

//RestockingScheduleName
  boost::optional<Schedule> restockingSchedule = modelObject.restockingSchedule();

  if( restockingSchedule )
  {
    boost::optional<IdfObject> _restockingSchedule = translateAndMapModelObject(restockingSchedule.get());

    if( _restockingSchedule && _restockingSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::RestockingScheduleName,_restockingSchedule->name().get());
    }
  }

//AverageRefrigerantChargeInventory
  d = modelObject.averageRefrigerantChargeInventory();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::AverageRefrigerantChargeInventory,d.get());
  }

//InsulatedFloorSurfaceArea
  d = modelObject.insulatedFloorSurfaceArea();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::InsulatedFloorSurfaceArea,d.get());
  }

//InsulatedFloorUValue
  d = modelObject.insulatedFloorUValue();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::InsulatedFloorUValue,d.get());
  }

//ZoneBoundaries
  std::vector<RefrigerationWalkInZoneBoundary> zoneBoundaries = modelObject.zoneBoundaries();

  if( !zoneBoundaries.empty() )
  {
    for( const auto & _zoneBoundary : zoneBoundaries )
    {
      IdfExtensibleGroup eg = refrigerationWalkIn.pushExtensibleGroup();

      //ZoneName
        boost::optional<ThermalZone> thermalZone = _zoneBoundary.thermalZone();

        if( thermalZone )
        {
          boost::optional<IdfObject> _thermalZone = translateAndMapModelObject(thermalZone.get());

          if( _thermalZone && _thermalZone->name() )
          {
            eg.setString(Refrigeration_WalkInExtensibleFields::ZoneName,_thermalZone->name().get());
          }
        }

      //TotalInsulatedSurfaceAreaFacingZone
        d = _zoneBoundary.totalInsulatedSurfaceAreaFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::TotalInsulatedSurfaceAreaFacingZone,d.get());
        }

      //InsulatedSurfaceUValueFacingZone
        d = _zoneBoundary.insulatedSurfaceUValueFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::InsulatedSurfaceUValueFacingZone,d.get());
        }

      //AreaofGlassReachInDoorsFacingZone
        d = _zoneBoundary.areaofGlassReachInDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::AreaofGlassReachInDoorsFacingZone,d.get());
        }

      //HeightofGlassReachInDoorsFacingZone
        d = _zoneBoundary.heightofGlassReachInDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::HeightofGlassReachInDoorsFacingZone,d.get());
        }

      //GlassReachInDoorUValueFacingZone
        d = _zoneBoundary.glassReachInDoorUValueFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::GlassReachInDoorUValueFacingZone,d.get());
        }

      //GlassReachInDoorOpeningScheduleNameFacingZone
        boost::optional<Schedule> glassReachInDoorOpeningScheduleFacingZone = _zoneBoundary.glassReachInDoorOpeningScheduleFacingZone();

        if( glassReachInDoorOpeningScheduleFacingZone )
        {
          boost::optional<IdfObject> _glassReachInDoorOpeningScheduleFacingZone = translateAndMapModelObject(glassReachInDoorOpeningScheduleFacingZone.get());

          if( _glassReachInDoorOpeningScheduleFacingZone && _glassReachInDoorOpeningScheduleFacingZone->name() )
          {
            eg.setString(Refrigeration_WalkInExtensibleFields::GlassReachInDoorOpeningScheduleNameFacingZone,_glassReachInDoorOpeningScheduleFacingZone->name().get());
          }
        }

      //AreaofStockingDoorsFacingZone
        d = _zoneBoundary.areaofStockingDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::AreaofStockingDoorsFacingZone,d.get());
        }

      //HeightofStockingDoorsFacingZone
        d = _zoneBoundary.heightofStockingDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::HeightofStockingDoorsFacingZone,d.get());
        }

      //StockingDoorUValueFacingZone
        d = _zoneBoundary.stockingDoorUValueFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::StockingDoorUValueFacingZone,d.get());
        }

      //StockingDoorOpeningScheduleNameFacingZone
        boost::optional<Schedule> stockingDoorOpeningScheduleFacingZone = _zoneBoundary.stockingDoorOpeningScheduleFacingZone();

        if( stockingDoorOpeningScheduleFacingZone )
        {
          boost::optional<IdfObject> _stockingDoorOpeningScheduleFacingZone = translateAndMapModelObject(stockingDoorOpeningScheduleFacingZone.get());

          if( _stockingDoorOpeningScheduleFacingZone && _stockingDoorOpeningScheduleFacingZone->name() )
          {
            eg.setString(Refrigeration_WalkInExtensibleFields::StockingDoorOpeningScheduleNameFacingZone,_stockingDoorOpeningScheduleFacingZone->name().get());
          }
        }

      //StockingDoorOpeningProtectionTypeFacingZone
        s = _zoneBoundary.stockingDoorOpeningProtectionTypeFacingZone();
        if (s) {
          eg.setString(Refrigeration_WalkInExtensibleFields::StockingDoorOpeningProtectionTypeFacingZone,s.get());
        }
    }
  }

  return refrigerationWalkIn;

}
boost::optional<IdfObject> ForwardTranslator::translateGroundHeatExchangerVertical( GroundHeatExchangerVertical & modelObject )
{
  boost::optional<std::string> s;
  boost::optional<double> value;
  boost::optional<ModelObject> temp;

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

  // Inlet Node Name
  if( (temp = modelObject.inletModelObject()) )
  {
    idfObject.setString(GroundHeatExchanger_VerticalFields::InletNodeName,temp->name().get());
  }

  // Outlet Node Name
  if( (temp = modelObject.outletModelObject()) )
  {
    idfObject.setString(GroundHeatExchanger_VerticalFields::OutletNodeName,temp->name().get());
  }

  // Maximum Flow Rate
  if( (value = modelObject.maximumFlowRate()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::DesignFlowRate,value.get());
  }

  // Number of Bore Holes
  if( (value = modelObject.numberofBoreHoles()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::NumberofBoreHoles,value.get());
  }

  // Bore Hole Length
  if( (value = modelObject.boreHoleLength()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::BoreHoleLength,value.get());
  }

  // Bore Hole Radius
  if( (value = modelObject.boreHoleRadius()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::BoreHoleRadius,value.get());
  }

  // Ground Thermal Conductivity
  if( (value = modelObject.groundThermalConductivity()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::GroundThermalConductivity,value.get());
  }

  // Ground Thermal Heat Capacity
  if( (value = modelObject.groundThermalHeatCapacity()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::GroundThermalHeatCapacity,value.get());
  }

  // Ground Temperature
  if( (value = modelObject.groundTemperature()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::GroundTemperature,value.get());
  }

  // Design Flow Rate
  if( (value = modelObject.designFlowRate()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::DesignFlowRate,value.get());
  }

  // Grout Thermal Conductivity
  if( (value = modelObject.groutThermalConductivity()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::GroutThermalConductivity,value.get());
  }

  // Pipe Thermal Conductivity
  if( (value = modelObject.pipeThermalConductivity()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::PipeThermalConductivity,value.get());
  }

  // Pipe Out Diameter
  if( (value = modelObject.pipeOutDiameter()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::PipeOutDiameter,value.get());
  }

  // U-Tube Distance
  if( (value = modelObject.uTubeDistance()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::UTubeDistance,value.get());
  }

  // Pipe Thickness
  if( (value = modelObject.pipeThickness()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::PipeThickness,value.get());
  }

  // Maximum Length of Simulation
  if( (value = modelObject.maximumLengthofSimulation()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::MaximumLengthofSimulation,value.get());
  }

  // G-Function Reference Ratio
  if( (value = modelObject.gFunctionReferenceRatio()) )
  {
    idfObject.setDouble(GroundHeatExchanger_VerticalFields::GFunctionReferenceRatio,value.get());
  }

  std::vector< std::pair<double,double> > gFunctions = modelObject.gFunctions();

  // Number of Data Pairs of the G Function
  idfObject.setInt(GroundHeatExchanger_VerticalFields::NumberofDataPairsoftheGFunction,gFunctions.size());

  if( !gFunctions.empty() )
  {
    for( const auto & gFunction : gFunctions )
    {
      IdfExtensibleGroup eg = idfObject.pushExtensibleGroup();

      eg.setDouble(GroundHeatExchanger_VerticalExtensibleFields::GFunctionLn_T_Ts_Value,gFunction.first); 
      eg.setDouble(GroundHeatExchanger_VerticalExtensibleFields::GFunctionGValue,gFunction.second); 
    }
  }
  else {
    LOG(Error,modelObject.briefDescription() << ": At least one pair of GFunction data required");
    return boost::none;
  }

  return boost::optional<IdfObject>(idfObject);
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACLowTempRadiantConstFlow(ZoneHVACLowTempRadiantConstFlow& modelObject)
{
  boost::optional<std::string> s;
  boost::optional<double> value;
  boost::optional<ModelObject> temp;

  IdfObject idfObject(IddObjectType::ZoneHVAC_LowTemperatureRadiant_ConstantFlow);
  m_idfObjects.push_back(idfObject);

  //Name
  std::string baseName = modelObject.name().get();
  idfObject.setName(baseName);

  // AvailabilityScheduleName
  if( boost::optional<Schedule> schedule = modelObject.availabilitySchedule() )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
    {
      idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::AvailabilityScheduleName,_schedule->name().get());
    }
  }
  
  //field Zone Name
  boost::optional<std::string> thermalZoneName;
  if( boost::optional<ThermalZone> zone = modelObject.thermalZone() )
  {
    if( (s = zone->name()) )
    {
      thermalZoneName = s;

      idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::ZoneName,thermalZoneName.get());
    }
  }
 
  //field Surface Name or Radiant Surface Type

  //create a new surface group object
  IdfObject _surfaceGroup(IddObjectType::ZoneHVAC_LowTemperatureRadiant_SurfaceGroup);

  //create a name for the surface group
  std::string sname = baseName + "" + modelObject.radiantSurfaceType().get();
  _surfaceGroup.setName(sname);
  
  //attach the surface group to the zone low temp radiant object
  idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::SurfaceNameorRadiantSurfaceGroupName,sname);

  //get rid of any existing surface (just to be safe)
  idfObject.clearExtensibleGroups();  
 
  //aggregator for total area; will be used to create weighted area
  double totalAreaOfSurfaces = 0;

  //loop through all surfaces, adding up their area
  for (const Surface& surface : modelObject.surfaces()){
    totalAreaOfSurfaces = totalAreaOfSurfaces + surface.grossArea();
  }

  //loop through all the surfaces, adding them and their flow fractions (weighted per-area)
  for (const Surface& surface : modelObject.surfaces()){
    IdfExtensibleGroup group = _surfaceGroup.pushExtensibleGroup();
    OS_ASSERT(group.numFields() == 2);
    group.setString(0, surface.name().get());        
    group.setDouble(1, (surface.grossArea()/totalAreaOfSurfaces) );     
  }
 
  //add the surface group to the list of idf objects
  m_idfObjects.push_back(_surfaceGroup);

 //field Hydronic Tubing Inside Diameter
 if( (value = modelObject.hydronicTubingInsideDiameter()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HydronicTubingInsideDiameter,value.get());
  }

  //field Hydronic Tubing Length
 if( (value = modelObject.hydronicTubingLength()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HydronicTubingLength,value.get());
  }

  //field Temperature Control Type
 if(boost::optional<std::string> tempCtrlType= modelObject.temperatureControlType() )
  {
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::TemperatureControlType,tempCtrlType.get());
  }

  //field Rated Flow Rate
  if( (value = modelObject.ratedFlowRate()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::RatedFlowRate,value.get());
  }

  //field Pump Flow Rate Schedule Name
  if( boost::optional<Schedule> schedule = modelObject.pumpFlowRateSchedule() )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
    {
      idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::PumpFlowRateScheduleName,_schedule->name().get());
    }
  }

  //field Rated Pump Head
  if( (value = modelObject.ratedPumpHead()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::RatedPumpHead,value.get());
  }

  //field Rated Power Consumption
  if( (value = modelObject.ratedPowerConsumption()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::RatedPowerConsumption,value.get());
  }

  //field Motor Efficiency
  if( (value = modelObject.motorEfficiency()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::MotorEfficiency,value.get());
  }

  //field Fraction of Motor Inefficiencies to Fluid Stream
  if( (value = modelObject.fractionofMotorInefficienciestoFluidStream()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::FractionofMotorInefficienciestoFluidStream,value.get());
  }

  HVACComponent heatingCoil = modelObject.heatingCoil();
  boost::optional<CoilHeatingLowTempRadiantConstFlow>  coilOptionalHeating = heatingCoil.optionalCast<CoilHeatingLowTempRadiantConstFlow>();

  // field Heating Water Inlet Node Name 
  if (coilOptionalHeating){
    CoilHeatingLowTempRadiantConstFlow coilHeat = *coilOptionalHeating;

    temp = coilHeat.inletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HeatingWaterInletNodeName,*s);
      }
    }

    //field Heating Water Outlet Node Name 
    temp = coilHeat.outletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HeatingWaterOutletNodeName,*s);
      }
    }
  
    //field Heating High Water Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilHeat.heatingHighWaterTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HeatingHighWaterTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Heating Low Water Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilHeat.heatingLowWaterTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HeatingLowWaterTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Heating High Control Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilHeat.heatingHighControlTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HeatingHighControlTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Heating Low Control Temperature Schedule Name    
    if( boost::optional<Schedule> schedule = coilHeat.heatingLowControlTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::HeatingLowControlTemperatureScheduleName,_schedule->name().get());
      }
    }
  }

  HVACComponent coolingCoil = modelObject.coolingCoil();
  boost::optional<CoilCoolingLowTempRadiantConstFlow>  coilOptionalCooling = coolingCoil.optionalCast<CoilCoolingLowTempRadiantConstFlow>();

  // field Cooling Water Inlet Node Name 
  if (coilOptionalCooling){
    CoilCoolingLowTempRadiantConstFlow coilCool = *coilOptionalCooling;

    temp = coilCool.inletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CoolingWaterInletNodeName,*s);
      }
    }

    //field Cooling Water Outlet Node Name 
    temp = coilCool.outletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CoolingWaterOutletNodeName,*s);
      }
    }

    //field Cooling High Water Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilCool.coolingHighWaterTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CoolingHighWaterTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Cooling Low Water Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilCool.coolingLowWaterTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CoolingLowWaterTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Cooling High Control Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilCool.coolingHighControlTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CoolingHighControlTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Cooling Low Control Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilCool.coolingLowControlTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CoolingLowControlTemperatureScheduleName,_schedule->name().get());
      }
    }
 
    //field Condensation Control Type
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CondensationControlType,coilCool.condensationControlType());

    //field Condensation Control Dewpoint Offset
    if( (value = coilCool.condensationControlDewpointOffset()) )
    {
      idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CondensationControlDewpointOffset,value.get());
    }
    
  }
  
  //field Number of Circuits
  idfObject.setString(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::NumberofCircuits,modelObject.numberofCircuits());

  //field Circuit Length
  idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_ConstantFlowFields::CircuitLength,modelObject.circuitLength());

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateSubSurface( model::SubSurface & modelObject )
{
  IdfObject idfObject(openstudio::IddObjectType::FenestrationSurface_Detailed);
  
  idfObject.setString(FenestrationSurface_DetailedFields::Name, modelObject.name().get());

  std::string subSurfaceType = modelObject.subSurfaceType();
  if (istringEqual("FixedWindow", subSurfaceType)){
    subSurfaceType = "Window";
  }else if (istringEqual("OperableWindow", subSurfaceType)){
    subSurfaceType = "Window";
  }else if (istringEqual("OverheadDoor", subSurfaceType)){
    subSurfaceType = "Door";
  }else if (istringEqual("Skylight", subSurfaceType)){
    subSurfaceType = "Window";
  }

  boost::optional<ConstructionBase> construction = modelObject.construction();
  if (construction){
    idfObject.setString(FenestrationSurface_DetailedFields::ConstructionName, construction->name().get());

    if (subSurfaceType == "Door" && construction->isFenestration()){
      LOG(Warn, "SubSurface '" << modelObject.name().get() << "' uses fenestration construction, changing SubSurfaceType to Door");
      subSurfaceType = "GlassDoor";
    } else if (subSurfaceType == "GlassDoor" && !construction->isFenestration()){
      LOG(Warn, "SubSurface '" << modelObject.name().get() << "' uses non-fenestration construction, changing SubSurfaceType to GlassDoor");
      subSurfaceType = "Door";
    }
  }

  idfObject.setString(FenestrationSurface_DetailedFields::SurfaceType, subSurfaceType);

  boost::optional<Surface> surface = modelObject.surface();
  if (surface){
    idfObject.setString(FenestrationSurface_DetailedFields::BuildingSurfaceName, surface->name().get());
  }

  boost::optional<SubSurface> adjacentSubSurface = modelObject.adjacentSubSurface();
  if (adjacentSubSurface){
    idfObject.setString(FenestrationSurface_DetailedFields::OutsideBoundaryConditionObject, adjacentSubSurface->name().get());
  }

  boost::optional<double> viewFactortoGround = modelObject.viewFactortoGround();
  if (viewFactortoGround){
    idfObject.setDouble(FenestrationSurface_DetailedFields::ViewFactortoGround, *viewFactortoGround);
  }

  boost::optional<ShadingControl> shadingControl = modelObject.shadingControl();
  if (shadingControl){
    idfObject.setString(FenestrationSurface_DetailedFields::ShadingControlName, shadingControl->name().get());
  }

  boost::optional<WindowPropertyFrameAndDivider> frameAndDivider = modelObject.windowPropertyFrameAndDivider();
  openstudio::Vector3d offset(0, 0, 0);
  if (frameAndDivider){
    if (!frameAndDivider->isOutsideRevealDepthDefaulted()){
      offset = -frameAndDivider->outsideRevealDepth() * modelObject.outwardNormal();
    }
    idfObject.setString(FenestrationSurface_DetailedFields::FrameandDividerName, frameAndDivider->name().get());
  }

  if(!modelObject.isMultiplierDefaulted()){
    idfObject.setDouble(FenestrationSurface_DetailedFields::Multiplier, modelObject.multiplier());
  }

  idfObject.clearExtensibleGroups();
  for (const Point3d& point : modelObject.vertices()){
    IdfExtensibleGroup group = idfObject.pushExtensibleGroup();
    if (group.empty()) {
      LOG(Error,"Currently unable to translate " << modelObject.briefDescription() 
          << ", because it has more vertices than allowed by EnergyPlus.");
      return boost::none;
    }

    Point3d newPoint = point + offset;

    group.setDouble(0, newPoint.x());
    group.setDouble(1, newPoint.y());
    group.setDouble(2, newPoint.z());
  }
  
  m_idfObjects.push_back(idfObject);

  return idfObject;

}
boost::optional<IdfObject> ForwardTranslator::translateRefrigerationWalkIn( RefrigerationWalkIn & modelObject )
{
  OptionalModelObject temp;
  boost::optional<std::string> s;
  boost::optional<double> d;

// Name
  IdfObject refrigerationWalkIn = createRegisterAndNameIdfObject(openstudio::IddObjectType::Refrigeration_WalkIn,
                                                          modelObject);

//AvailabilityScheduleName
  boost::optional<Schedule> availabilitySchedule = modelObject.availabilitySchedule();

  if( availabilitySchedule )
  {
    boost::optional<IdfObject> _availabilitySchedule = translateAndMapModelObject(availabilitySchedule.get());

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

//RatedCoilCoolingCapacity
  d = modelObject.ratedCoilCoolingCapacity();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCoilCoolingCapacity,d.get());
  }

//OperatingTemperature
  d = modelObject.operatingTemperature();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::OperatingTemperature,d.get());
  }

//RatedCoolingSourceTemperature
  d = modelObject.ratedCoolingSourceTemperature();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCoolingSourceTemperature,d.get());
  }

//RatedTotalHeatingPower
  d = modelObject.ratedTotalHeatingPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedTotalHeatingPower,d.get());
  }

//HeatingPowerScheduleName
  boost::optional<Schedule> heatingPowerSchedule = modelObject.heatingPowerSchedule();

  if( heatingPowerSchedule )
  {
    boost::optional<IdfObject> _heatingPowerSchedule = translateAndMapModelObject(heatingPowerSchedule.get());

    if( _heatingPowerSchedule && _heatingPowerSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::HeatingPowerScheduleName,_heatingPowerSchedule->name().get());
    }
  }

//RatedCoolingCoilFanPower
  d = modelObject.ratedCoolingCoilFanPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCoolingCoilFanPower,d.get());
  }

//RatedCirculationFanPower
  d = modelObject.ratedCirculationFanPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedCirculationFanPower,d.get());
  }

//RatedTotalLightingPower
  d = modelObject.ratedTotalLightingPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::RatedTotalLightingPower,d.get());
  }

//LightingScheduleName
  boost::optional<Schedule> lightingSchedule = modelObject.lightingSchedule();

  if( lightingSchedule )
  {
    boost::optional<IdfObject> _lightingSchedule = translateAndMapModelObject(lightingSchedule.get());

    if( _lightingSchedule && _lightingSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::LightingScheduleName,_lightingSchedule->name().get());
    }
  }

//DefrostType
  s = modelObject.defrostType();
  if (s) {
    refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostType,s.get());
  }

//DefrostControlType
  s = modelObject.defrostControlType();
  if (s) {
    refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostControlType,s.get());
  }

//DefrostScheduleName
  boost::optional<Schedule> defrostSchedule = modelObject.defrostSchedule();

  if( defrostSchedule )
  {
    boost::optional<IdfObject> _defrostSchedule = translateAndMapModelObject(defrostSchedule.get());

    if( _defrostSchedule && _defrostSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostScheduleName,_defrostSchedule->name().get());
    }
  }

//DefrostDripDownScheduleName
  boost::optional<Schedule> defrostDripDownSchedule = modelObject.defrostDripDownSchedule();

  if( defrostDripDownSchedule )
  {
    boost::optional<IdfObject> _defrostDripDownSchedule = translateAndMapModelObject(defrostDripDownSchedule.get());

    if( _defrostDripDownSchedule && _defrostDripDownSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::DefrostDripDownScheduleName,_defrostDripDownSchedule->name().get());
    }
  }

//DefrostPower
  d = modelObject.defrostPower();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::DefrostPower,d.get());
  }

//TemperatureTerminationDefrostFractiontoIce
  d = modelObject.temperatureTerminationDefrostFractiontoIce();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::TemperatureTerminationDefrostFractiontoIce,d.get());
  }

//RestockingScheduleName
  boost::optional<Schedule> restockingSchedule = modelObject.restockingSchedule();

  if( restockingSchedule )
  {
    boost::optional<IdfObject> _restockingSchedule = translateAndMapModelObject(restockingSchedule.get());

    if( _restockingSchedule && _restockingSchedule->name() )
    {
      refrigerationWalkIn.setString(Refrigeration_WalkInFields::RestockingScheduleName,_restockingSchedule->name().get());
    }
  }

//AverageRefrigerantChargeInventory
  d = modelObject.averageRefrigerantChargeInventory();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::AverageRefrigerantChargeInventory,d.get());
  }

//InsulatedFloorSurfaceArea
  d = modelObject.insulatedFloorSurfaceArea();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::InsulatedFloorSurfaceArea,d.get());
  }

//InsulatedFloorUValue
  d = modelObject.insulatedFloorUValue();
  if (d) {
    refrigerationWalkIn.setDouble(Refrigeration_WalkInFields::InsulatedFloorUValue,d.get());
  }

//ZoneBoundaries
  std::vector<RefrigerationWalkInZoneBoundary> zoneBoundaries = modelObject.zoneBoundaries();

  if( !zoneBoundaries.empty() )
  {
    for( std::vector<RefrigerationWalkInZoneBoundary>::iterator _zoneBoundary = zoneBoundaries.begin();
       _zoneBoundary != zoneBoundaries.end();
       _zoneBoundary++ )
    {
      IdfExtensibleGroup eg = refrigerationWalkIn.pushExtensibleGroup();

      //ZoneName
        boost::optional<ThermalZone> thermalZone = _zoneBoundary->thermalZone();

        if( thermalZone )
        {
          boost::optional<IdfObject> _thermalZone = translateAndMapModelObject(thermalZone.get());

          if( _thermalZone && _thermalZone->name() )
          {
            eg.setString(Refrigeration_WalkInExtensibleFields::ZoneName,_thermalZone->name().get());
          }
        }

      //TotalInsulatedSurfaceAreaFacingZone
        d = _zoneBoundary->totalInsulatedSurfaceAreaFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::TotalInsulatedSurfaceAreaFacingZone,d.get());
        }

      //InsulatedSurfaceUValueFacingZone
        d = _zoneBoundary->insulatedSurfaceUValueFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::InsulatedSurfaceUValueFacingZone,d.get());
        }

      //AreaofGlassReachInDoorsFacingZone
        d = _zoneBoundary->areaofGlassReachInDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::AreaofGlassReachInDoorsFacingZone,d.get());
        }

      //HeightofGlassReachInDoorsFacingZone
        d = _zoneBoundary->heightofGlassReachInDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::HeightofGlassReachInDoorsFacingZone,d.get());
        }

      //GlassReachInDoorUValueFacingZone
        d = _zoneBoundary->glassReachInDoorUValueFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::GlassReachInDoorUValueFacingZone,d.get());
        }

      //GlassReachInDoorOpeningScheduleNameFacingZone
        boost::optional<Schedule> glassReachInDoorOpeningScheduleFacingZone = _zoneBoundary->glassReachInDoorOpeningScheduleFacingZone();

        if( glassReachInDoorOpeningScheduleFacingZone )
        {
          boost::optional<IdfObject> _glassReachInDoorOpeningScheduleFacingZone = translateAndMapModelObject(glassReachInDoorOpeningScheduleFacingZone.get());

          if( _glassReachInDoorOpeningScheduleFacingZone && _glassReachInDoorOpeningScheduleFacingZone->name() )
          {
            eg.setString(Refrigeration_WalkInExtensibleFields::GlassReachInDoorOpeningScheduleNameFacingZone,_glassReachInDoorOpeningScheduleFacingZone->name().get());
          }
        }

      //AreaofStockingDoorsFacingZone
        d = _zoneBoundary->areaofStockingDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::AreaofStockingDoorsFacingZone,d.get());
        }

      //HeightofStockingDoorsFacingZone
        d = _zoneBoundary->heightofStockingDoorsFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::HeightofStockingDoorsFacingZone,d.get());
        }

      //StockingDoorUValueFacingZone
        d = _zoneBoundary->stockingDoorUValueFacingZone();
        if (d) {
          eg.setDouble(Refrigeration_WalkInExtensibleFields::StockingDoorUValueFacingZone,d.get());
        }

      //StockingDoorOpeningScheduleNameFacingZone
        boost::optional<Schedule> stockingDoorOpeningScheduleFacingZone = _zoneBoundary->stockingDoorOpeningScheduleFacingZone();

        if( stockingDoorOpeningScheduleFacingZone )
        {
          boost::optional<IdfObject> _stockingDoorOpeningScheduleFacingZone = translateAndMapModelObject(stockingDoorOpeningScheduleFacingZone.get());

          if( _stockingDoorOpeningScheduleFacingZone && _stockingDoorOpeningScheduleFacingZone->name() )
          {
            eg.setString(Refrigeration_WalkInExtensibleFields::StockingDoorOpeningScheduleNameFacingZone,_stockingDoorOpeningScheduleFacingZone->name().get());
          }
        }

      //StockingDoorOpeningProtectionTypeFacingZone
        s = _zoneBoundary->stockingDoorOpeningProtectionTypeFacingZone();
        if (s) {
          eg.setString(Refrigeration_WalkInExtensibleFields::StockingDoorOpeningProtectionTypeFacingZone,s.get());
        }
    }
  }

  return refrigerationWalkIn;

}
boost::optional<IdfObject> ForwardTranslator::translateShadingSurface( model::ShadingSurface & modelObject )
{
  boost::optional<IdfObject> idfObject;

  boost::optional<Schedule> transmittanceSchedule = modelObject.transmittanceSchedule();
  Transformation transformation;
  Point3dVector points;

  boost::optional<ShadingSurfaceGroup> shadingSurfaceGroup = modelObject.shadingSurfaceGroup();
  if (shadingSurfaceGroup){

    transformation = shadingSurfaceGroup->transformation();
    points = transformation * modelObject.vertices();

    if (istringEqual("Space", shadingSurfaceGroup->shadingSurfaceType())){

      idfObject = IdfObject(openstudio::IddObjectType::Shading_Zone_Detailed);
      idfObject->setString(Shading_Zone_DetailedFields::Name, modelObject.name().get());

      boost::optional<Space> space = shadingSurfaceGroup->space();
      if (space){
        
        boost::optional<Surface> baseSurface;
        double minDistance = std::numeric_limits<double>::max();

        // at this point zone has one space and internal surfaces have already been combined
        for (const Surface& surface : space->surfaces()){
          if (istringEqual(surface.outsideBoundaryCondition(), "Outdoors")){
            Point3dVector surfaceVertices = surface.vertices();
            for (const Point3d& point : points){
              for (const Point3d& surfaceVertex : surfaceVertices){
                double distance = getDistance(point, surfaceVertex);
                if (distance < minDistance){
                  baseSurface = surface;
                  minDistance = distance;
                }
              }
            }
          }
        }

        if (!baseSurface){
          LOG(Error, "Cannot find appropriate base surface for shading surface '" << modelObject.name().get() << 
                     "', the shading surface will not be translated");
          return boost::none;
        }

        idfObject->setString(Shading_Zone_DetailedFields::BaseSurfaceName, baseSurface->name().get());
      }

      if (transmittanceSchedule){
        idfObject->setString(Shading_Zone_DetailedFields::TransmittanceScheduleName, transmittanceSchedule->name().get());
      }

    }else if (istringEqual("Site", shadingSurfaceGroup->shadingSurfaceType())){

      idfObject = IdfObject(openstudio::IddObjectType::Shading_Site_Detailed);
      idfObject->setString(Shading_Site_DetailedFields::Name, modelObject.name().get());
      
      if (transmittanceSchedule){
        idfObject->setString(Shading_Site_DetailedFields::TransmittanceScheduleName, transmittanceSchedule->name().get());
      }

    }else{
      boost::optional<Building> building = modelObject.model().getUniqueModelObject<Building>();
      if (building){
        transformation = building->transformation().inverse()*transformation;
      }

      idfObject = IdfObject(openstudio::IddObjectType::Shading_Building_Detailed);
      idfObject->setString(Shading_Building_DetailedFields::Name, modelObject.name().get());
      
      if (transmittanceSchedule){
        idfObject->setString(Shading_Building_DetailedFields::TransmittanceScheduleName, transmittanceSchedule->name().get());
      }
    }

  }else{
    idfObject = IdfObject(openstudio::IddObjectType::Shading_Building_Detailed);
    idfObject->setString(Shading_Building_DetailedFields::Name, modelObject.name().get());
    
    if (transmittanceSchedule){
      idfObject->setString(Shading_Building_DetailedFields::TransmittanceScheduleName, transmittanceSchedule->name().get());
    }
  }

  m_idfObjects.push_back(*idfObject);

  idfObject->clearExtensibleGroups();

  for (const Point3d& point : points){
    IdfExtensibleGroup group = idfObject->pushExtensibleGroup();
    OS_ASSERT(group.numFields() == 3);
    group.setDouble(0, point.x());
    group.setDouble(1, point.y());
    group.setDouble(2, point.z());
  }

  // get reflectance properties from construction if possible
  bool addShadingPropertyObject = false;

  IdfObject shadingPropertyObject = IdfObject(openstudio::IddObjectType::ShadingProperty_Reflectance);
  shadingPropertyObject.setString(ShadingProperty_ReflectanceFields::ShadingSurfaceName, modelObject.name().get());

  boost::optional<model::ConstructionBase> constructionBase = modelObject.construction();
  if (constructionBase){
    if (constructionBase->isFenestration()){

      shadingPropertyObject.setDouble(ShadingProperty_ReflectanceFields::FractionofShadingSurfaceThatIsGlazed, 1.0);
      shadingPropertyObject.setString(ShadingProperty_ReflectanceFields::GlazingConstructionName, constructionBase->name().get());
      addShadingPropertyObject = true;

    }else{

      shadingPropertyObject.setDouble(ShadingProperty_ReflectanceFields::FractionofShadingSurfaceThatIsGlazed, 0.0);

      boost::optional<model::Construction> construction = constructionBase->optionalCast<model::Construction>();
      if (construction){

        std::vector<model::Material> layers = construction->layers();

        // we want the outer layer
        if (!layers.empty()){

          if (layers[0].optionalCast<model::StandardOpaqueMaterial>()){
            model::StandardOpaqueMaterial outerMaterial = layers[0].cast<model::StandardOpaqueMaterial>();

            boost::optional<double> solRefl = outerMaterial.solarReflectance();
            if (solRefl){
              shadingPropertyObject.setDouble(ShadingProperty_ReflectanceFields::DiffuseSolarReflectanceofUnglazedPartofShadingSurface, *solRefl);
              addShadingPropertyObject = true;
            }

            boost::optional<double> visRefl = outerMaterial.visibleReflectance();
            if (visRefl){
              shadingPropertyObject.setDouble(ShadingProperty_ReflectanceFields::DiffuseVisibleReflectanceofUnglazedPartofShadingSurface, *visRefl);
              addShadingPropertyObject = true;
            }
          }

          
          if (layers[0].optionalCast<model::MasslessOpaqueMaterial>()){
            model::MasslessOpaqueMaterial outerMaterial = layers[0].cast<model::MasslessOpaqueMaterial>();

            boost::optional<double> solRefl = outerMaterial.solarReflectance();
            if (solRefl){
              shadingPropertyObject.setDouble(ShadingProperty_ReflectanceFields::DiffuseSolarReflectanceofUnglazedPartofShadingSurface, *solRefl);
              addShadingPropertyObject = true;
            }

            boost::optional<double> visRefl = outerMaterial.visibleReflectance();
            if (visRefl){
              shadingPropertyObject.setDouble(ShadingProperty_ReflectanceFields::DiffuseVisibleReflectanceofUnglazedPartofShadingSurface, *visRefl);
              addShadingPropertyObject = true;
            }
          }

        }
      }
    }
  }

  if (addShadingPropertyObject){
    m_idfObjects.push_back(shadingPropertyObject);
  }

  return idfObject;
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACLowTempRadiantVarFlow(ZoneHVACLowTempRadiantVarFlow& modelObject)
{
  boost::optional<std::string> s;
  boost::optional<double> value;
  boost::optional<ModelObject> temp;

  IdfObject idfObject(IddObjectType::ZoneHVAC_LowTemperatureRadiant_VariableFlow);
  m_idfObjects.push_back(idfObject);

  //Name
  std::string baseName = modelObject.name().get();
  idfObject.setName(baseName);

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

  //field Zone Name
  boost::optional<std::string> thermalZoneName;
  if( boost::optional<ThermalZone> zone = modelObject.thermalZone() )
  {
    if( (s = zone->name()) )
    {
      thermalZoneName = s;

      idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::ZoneName,thermalZoneName.get());
    }
  }

  //field Surface Name or Radiant Surface Type

  //create a new surface group object
  IdfObject _surfaceGroup(IddObjectType::ZoneHVAC_LowTemperatureRadiant_SurfaceGroup);

  //create a name for the surface group
  std::string sname = baseName + "" + modelObject.radiantSurfaceType().get();
  _surfaceGroup.setName(sname);

  //attach the surface group to the zone low temp radiant object
  idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::SurfaceNameorRadiantSurfaceGroupName,sname);

  //get rid of any existing surface (just to be safe)
  idfObject.clearExtensibleGroups();

  //aggregator for total area; will be used to create weighted area
  double totalAreaOfSurfaces = 0;

  //loop through all surfaces, adding up their area
  for (const Surface& surface : modelObject.surfaces()){
    totalAreaOfSurfaces = totalAreaOfSurfaces + surface.grossArea();
  }

  //loop through all the surfaces, adding them and their flow fractions (weighted per-area)
  for (const Surface& surface : modelObject.surfaces()){
    IdfExtensibleGroup group = _surfaceGroup.pushExtensibleGroup();
    OS_ASSERT(group.numFields() == 2);
    group.setString(0, surface.name().get());
    group.setDouble(1, (surface.grossArea()/totalAreaOfSurfaces) );
  }

  //add the surface group to the list of idf objects
  m_idfObjects.push_back(_surfaceGroup);


  //field Hydronic Tubing Inside Diameter
  if( (value = modelObject.hydronicTubingInsideDiameter()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HydronicTubingInsideDiameter,value.get());
  }

  //field Hydronic Tubing Length
  if( modelObject.isHydronicTubingLengthAutosized() )
  {
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HydronicTubingLength,"Autosize");
  }
  else if( (value = modelObject.hydronicTubingLength()) )
  {
    idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HydronicTubingLength,value.get());
  }

  //field Temperature Control Type
  if(boost::optional<std::string> tempCtrlType= modelObject.temperatureControlType() )
  {
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::TemperatureControlType,tempCtrlType.get());
  }

  // Heating Coil
  HVACComponent heatingCoil = modelObject.heatingCoil();
  boost::optional<CoilHeatingLowTempRadiantVarFlow>  coilOptionalHeating = heatingCoil.optionalCast<CoilHeatingLowTempRadiantVarFlow>();

  if (coilOptionalHeating)
  {
    CoilHeatingLowTempRadiantVarFlow coilHeat = *coilOptionalHeating;

    // Heating Design Capacity Method - introduced in 8.2.0 and not yet supported in OS
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HeatingDesignCapacityMethod,"HeatingDesignCapacity");

    // Heating Design Capacity - introduced in 8.2.0 and not yet supported in OS
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HeatingDesignCapacity,"Autosize");
    
    // field Maximum Hot Water Flow
    if( coilHeat.isMaximumHotWaterFlowAutosized() )
    {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::MaximumHotWaterFlow,"Autosize");
    }
    else if( (value = coilHeat.maximumHotWaterFlow()) )
    {
        idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::MaximumHotWaterFlow,value.get());
    }

    // field Heating Water Inlet Node Name
    temp = coilHeat.inletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HeatingWaterInletNodeName,*s);
      }
    }

    //field Heating Water Outlet Node Name
    temp = coilHeat.outletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HeatingWaterOutletNodeName,*s);
      }
    }

    //field Heating Control Throttling Range
    if( (value = coilHeat.heatingControlThrottlingRange()) )
    {
        idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HeatingControlThrottlingRange,value.get());
    }

    //field Heating Control Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilHeat.heatingControlTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::HeatingControlTemperatureScheduleName,_schedule->name().get());
      }
    }
  }

  // Cooling Coil
  HVACComponent coolingCoil = modelObject.coolingCoil();
  boost::optional<CoilCoolingLowTempRadiantVarFlow>  coilOptionalCooling = coolingCoil.optionalCast<CoilCoolingLowTempRadiantVarFlow>();

  if (coilOptionalCooling)
  {
    CoilCoolingLowTempRadiantVarFlow coilCool = *coilOptionalCooling;

    // Cooling Design Capacity Method - introduced in 8.2.0 and not yet supported in OS
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CoolingDesignCapacityMethod,"CoolingDesignCapacity");

    // Cooling Design Capacity - introduced in 8.2.0 and not yet supported in OS
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CoolingDesignCapacity,"Autosize");

    // Field Maximum Cold Water Flow
    if( coilCool.isMaximumColdWaterFlowAutosized() )
    {
      idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::MaximumColdWaterFlow,"Autosize");
    }
    else if( (value = coilCool.maximumColdWaterFlow()) )
    {
      idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::MaximumColdWaterFlow,value.get());
    }

    // field Cooling Water Inlet Node Name
    temp = coilCool.inletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CoolingWaterInletNodeName,*s);
      }
    }

    //field Cooling Water Outlet Node Name
    temp = coilCool.outletModelObject();
    if(temp)
    {
      s = temp->name();
      if(s)
      {
        idfObject.setString(openstudio::ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CoolingWaterOutletNodeName,*s);
      }
    }

    //field Cooling Control Throttling Range

    if( (value = coilCool.coolingControlThrottlingRange()) )
    {
      idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CoolingControlThrottlingRange,value.get());
    }

    //field Cooling Control Temperature Schedule Name
    if( boost::optional<Schedule> schedule = coilCool.coolingControlTemperatureSchedule() )
    {
      if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
      {
        idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CoolingControlTemperatureScheduleName,_schedule->name().get());
      }
    }

    //field Condensation Control Type
    idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CondensationControlType,coilCool.condensationControlType());

    //field Condensation Control Dewpoint Offset
    if( (value = coilCool.condensationControlDewpointOffset()) )
    {
      idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CondensationControlDewpointOffset,value.get());
    }
  }

  //field Number of Circuits
  idfObject.setString(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::NumberofCircuits,modelObject.numberofCircuits());

  //field Circuit Length
  idfObject.setDouble(ZoneHVAC_LowTemperatureRadiant_VariableFlowFields::CircuitLength,modelObject.circuitLength());

  return idfObject;
}
TEST_F(IdfFixture,WorkspaceObjectWatcher_RelationshipFieldChanges)
{
  IdfObject object(IddObjectType::DaylightingDevice_Tubular);
  Workspace workspace(StrictnessLevel::Draft, IddFileType::EnergyPlus);

  OptionalWorkspaceObject owo = workspace.addObject(object);
  ASSERT_TRUE(owo);
  WorkspaceObject tdd = *owo;
  WorkspaceObjectWatcher watcher(tdd);
  EXPECT_FALSE(watcher.dirty());

  // add zone objects
  object = IdfObject(IddObjectType::Zone);
  owo = workspace.addObject(object);
  ASSERT_TRUE(owo);
  EXPECT_EQ("Zone 1",owo->name().get());
  owo = workspace.addObject(object);
  ASSERT_TRUE(owo);
  EXPECT_EQ("Zone 2",owo->name().get());
  owo = workspace.addObject(object);
  ASSERT_TRUE(owo);
  EXPECT_EQ("Zone 3",owo->name().get());
  EXPECT_FALSE(watcher.dirty());

  vector<string> vals;
  vals.push_back("Zone 1");
  vals.push_back("1.0");
  IdfExtensibleGroup eg = tdd.pushExtensibleGroup(vals);
  ASSERT_FALSE(eg.empty());
  EXPECT_TRUE(watcher.dirty());
  EXPECT_TRUE(watcher.dataChanged());
  EXPECT_FALSE(watcher.nameChanged());
  EXPECT_TRUE(watcher.relationshipChanged());
  watcher.clearState();
  EXPECT_FALSE(watcher.dirty());
  EXPECT_FALSE(watcher.dataChanged());
  EXPECT_FALSE(watcher.nameChanged());
  EXPECT_FALSE(watcher.relationshipChanged());

  EXPECT_TRUE(eg.setString(0,"Zone 2"));
  EXPECT_TRUE(watcher.dirty());
  EXPECT_FALSE(watcher.dataChanged());
  EXPECT_FALSE(watcher.nameChanged());
  EXPECT_TRUE(watcher.relationshipChanged());
  watcher.clearState();
  EXPECT_FALSE(watcher.dirty());
  EXPECT_FALSE(watcher.dataChanged());
  EXPECT_FALSE(watcher.nameChanged());
  EXPECT_FALSE(watcher.relationshipChanged());

  EXPECT_TRUE(eg.setDouble(1,2.0));
  EXPECT_TRUE(watcher.dirty());
  EXPECT_TRUE(watcher.dataChanged());
  EXPECT_FALSE(watcher.nameChanged());
  EXPECT_FALSE(watcher.relationshipChanged());
  watcher.clearState();
  EXPECT_FALSE(watcher.dirty());
  EXPECT_FALSE(watcher.dataChanged());
  EXPECT_FALSE(watcher.nameChanged());
  EXPECT_FALSE(watcher.relationshipChanged());
}
boost::optional<IdfObject> ForwardTranslator::translateZoneHVACHighTemperatureRadiant(ZoneHVACHighTemperatureRadiant& modelObject)
{
  boost::optional<std::string> s;
  boost::optional<double> value;
  boost::optional<ModelObject> temp;

  IdfObject idfObject(IddObjectType::ZoneHVAC_HighTemperatureRadiant);
  m_idfObjects.push_back(idfObject);

  //Name
  std::string baseName = modelObject.name().get();
  idfObject.setName(baseName);

  // AvailabilityScheduleName
  if( boost::optional<Schedule> schedule = modelObject.availabilitySchedule() )
  {
    if( boost::optional<IdfObject> _schedule = translateAndMapModelObject(schedule.get()) )
    {
      idfObject.setString(ZoneHVAC_HighTemperatureRadiantFields::AvailabilityScheduleName,_schedule->name().get());
    }
  }
  
  //field Zone Name
  boost::optional<std::string> thermalZoneName;
  if( boost::optional<ThermalZone> zone = modelObject.thermalZone() )
  {
    if( (s = zone->name()) )
    {
      thermalZoneName = s;
      idfObject.setString(ZoneHVAC_HighTemperatureRadiantFields::ZoneName,thermalZoneName.get());
    }
  }

  //MaximumPowerInput
  if( modelObject.isMaximumPowerInputAutosized() )
  {
    idfObject.setString(ZoneHVAC_HighTemperatureRadiantFields::HeatingDesignCapacity,"AutoSize");
  }
  else if( (value = modelObject.maximumPowerInput()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::HeatingDesignCapacity,value.get());
  } 

  //FuelType
  if( (s = modelObject.fuelType()) )
  {
    idfObject.setString(ZoneHVAC_HighTemperatureRadiantFields::FuelType,s.get());
  }

  //CombustionEfficiency
  if( (value = modelObject.combustionEfficiency()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::CombustionEfficiency,value.get());
  }

  //FractionofInputConvertedtoRadiantEnergy
  if( (value = modelObject.fractionofInputConvertedtoRadiantEnergy()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::FractionofInputConvertedtoRadiantEnergy,value.get());
  }

  //FractionofInputConvertedtoLatentEnergy
  if( (value = modelObject.fractionofInputConvertedtoLatentEnergy()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::FractionofInputConvertedtoLatentEnergy,value.get());
  }

  //FractionofInputthatIsLost
  if( (value = modelObject.fractionofInputthatIsLost()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::FractionofInputthatIsLost,value.get());
  }

  //TemperatureControlType
  if( (s = modelObject.temperatureControlType()) )
  {
    idfObject.setString(ZoneHVAC_HighTemperatureRadiantFields::TemperatureControlType,s.get());
  }
  
  //HeatingThrottlingRange
  if( (value = modelObject.heatingThrottlingRange()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::HeatingThrottlingRange,value.get());
  }

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

  //FractionofRadiantEnergyIncidentonPeople
  double fractionofRadiantEnergyIncidentonPeople = 0.04;
  if( (value = modelObject.fractionofRadiantEnergyIncidentonPeople()) )
  {
    idfObject.setDouble(ZoneHVAC_HighTemperatureRadiantFields::FractionofRadiantEnergyIncidentonPeople,value.get());
    fractionofRadiantEnergyIncidentonPeople = value.get();
  }

  //get rid of any existing surface (just to be safe)
  idfObject.clearExtensibleGroups();  
 
  //aggregator for total area; will be used to create weighted area
  double totalAreaOfNonFloorSurfaces = 0;
  double totalAreaOfFloorSurfaces = 0;

  //loop through all surfaces, adding up their area
  for (const Surface& surface : modelObject.getImpl<model::detail::ZoneHVACHighTemperatureRadiant_Impl>()->surfaces()){
    if( istringEqual(surface.surfaceType(),"Floor") ) {
      totalAreaOfFloorSurfaces = totalAreaOfFloorSurfaces + surface.grossArea();
    } else {
      totalAreaOfNonFloorSurfaces = totalAreaOfNonFloorSurfaces + surface.grossArea();
    }
  }

  // Assume that 70% of what is not on people is on the floor
  double fractionOnFloor = (1.0 - fractionofRadiantEnergyIncidentonPeople) * 0.70;
  // Assume that 30% of what is not on people is on the non floor surfaces
  double fractionOnNonFloor = (1.0 - fractionofRadiantEnergyIncidentonPeople) * 0.30;
  //loop through all the surfaces, adding them and their flow fractions (weighted per-area)
  for (const Surface& surface : modelObject.getImpl<model::detail::ZoneHVACHighTemperatureRadiant_Impl>()->surfaces()){
    IdfExtensibleGroup group = idfObject.pushExtensibleGroup();
    group.setString(ZoneHVAC_HighTemperatureRadiantExtensibleFields::SurfaceName, surface.name().get());        
    if( istringEqual(surface.surfaceType(),"Floor") ) {
      group.setDouble(ZoneHVAC_HighTemperatureRadiantExtensibleFields::FractionofRadiantEnergytoSurface, (surface.grossArea()/totalAreaOfFloorSurfaces * fractionOnFloor) );     
    } else {
      group.setDouble(ZoneHVAC_HighTemperatureRadiantExtensibleFields::FractionofRadiantEnergytoSurface, (surface.grossArea()/totalAreaOfNonFloorSurfaces * fractionOnNonFloor) );     
    }
  }

  return idfObject;
}