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<std::string> UtilityCost_Computation_Impl::computeStep(unsigned index) const {
  IdfExtensibleGroup eg = getExtensibleGroup(index);
  if (!eg.empty()) {
    return eg.getString(OS_UtilityCost_ComputationExtensibleFields::ComputeStep,true);
  }
  return boost::none;
}
Beispiel #3
0
TEST_F(ModelFixture, ComponentWatcher_InComponent) {
  // create Component. ComponentWatcher should work here too (since Component is Model)
  Model model;
  DesignDay designDay(model);
  EXPECT_EQ(1u,model.numObjects());
  Component designDayComponent = designDay.createComponent();
  EXPECT_EQ(2u,designDayComponent.numObjects());
  ComponentData cd = designDayComponent.componentData();
  UUID versionUUID = cd.versionUUID();
  designDay = designDayComponent.primaryObject().cast<DesignDay>();

  // data changes ok--changes version
  EXPECT_TRUE(designDay.setName("My Design Day"));
  EXPECT_NE(versionUUID,cd.versionUUID());
  versionUUID = cd.versionUUID();

  // trying to assign contents results in refresh of contents
  IdfExtensibleGroup eg = cd.getExtensibleGroup(0);
  ASSERT_FALSE(eg.empty());
  std::string originalValue = eg.getString(0).get();
  EXPECT_FALSE(eg.setString(0,"Material"));
  EXPECT_TRUE(cd.initialized());
  EXPECT_EQ(2u,designDayComponent.numObjects());
  EXPECT_EQ(originalValue,eg.getString(0).get());
}
boost::optional<std::string> UtilityCost_Charge_Block_Impl::blockCostPerUnitValueOrVariableName(unsigned index) const {
  IdfExtensibleGroup eg = getExtensibleGroup(index);
  if (!eg.empty()) {
    return eg.getString(OS_UtilityCost_Charge_BlockExtensibleFields::BlockCostperUnitValueorVariableName,true);
  }
  return boost::none;
}
  bool LayeredConstruction_Impl::insertLayer(unsigned layerIndex, 
                                             const Material& material) 
  {
    OS_ASSERT(material.model() == model());
    layerIndex = mf_clearNullLayers(layerIndex);

    unsigned n = numLayers();
    MaterialVector layers = this->layers();
    MaterialVector::iterator layersBegin = layers.begin();
    MaterialVector::iterator layersEnd = layers.end();
    MaterialVector::iterator insertAtIt = layersBegin;
    while ((static_cast<unsigned>(insertAtIt - layersBegin) < layerIndex) &&
           (insertAtIt != layersEnd)) 
    { ++insertAtIt; }
    layers.insert(insertAtIt, material);
    OS_ASSERT(layers.size() == ++n);
    if ((model().strictnessLevel() < StrictnessLevel::Final) || 
        LayeredConstruction::layersAreValid(layers)) 
    {
      IdfExtensibleGroup idfGroup = insertExtensibleGroup(layerIndex,StringVector());
      OS_ASSERT(!idfGroup.empty());
      ModelExtensibleGroup group = idfGroup.cast<ModelExtensibleGroup>();
      bool ok = group.setPointer(0,material.handle());
      OS_ASSERT(ok);
      return true;
    }

    return false;
  }
boost::optional<double> LifeCycleCostUsePriceEscalation_Impl::yearEscalation(unsigned index) const 
{
  IdfExtensibleGroup eg = getExtensibleGroup(index);
  if (!eg.empty()) {
    return eg.getDouble(OS_LifeCycleCost_UsePriceEscalationExtensibleFields::YearEscalation,true);
  }
  return boost::none;
}
Beispiel #7
0
 ClimateZone ClimateZones_Impl::getClimateZone(unsigned index) const {
   IdfExtensibleGroup eg = getExtensibleGroup(index);
   if (eg.empty()) {
     std::shared_ptr<ClimateZones_Impl> p;
     return ClimateZone(p,numFields());
   }
   return eg.cast<ClimateZone>();
 }      
 boost::optional<std::string> PeopleDefinition_Impl::getThermalComfortModelType(int i) const {
   OptionalString result;
   if (i < numThermalComfortModelTypes()) {
     IdfExtensibleGroup eg = getExtensibleGroup(i);
     OS_ASSERT(!eg.empty());
     result = eg.getString(OS_People_DefinitionExtensibleFields::ThermalComfortModelType,true);
   }
   return result;
 }
Beispiel #9
0
 bool ComponentData_Impl::registerObject(const ModelObject& object) {
   IdfExtensibleGroup eg = pushExtensibleGroup(StringVector());
   bool result = !eg.empty();
   if (result) {
     ModelExtensibleGroup meg = eg.cast<ModelExtensibleGroup>();
     result = result && meg.setPointer(OS_ComponentDataExtensibleFields::NameofObject,
                                       object.handle());
   }
   return result;
 }
bool UtilityCost_Computation_Impl::setComputeStep(unsigned index, const std::string& str) {
  IdfExtensibleGroup eg = getExtensibleGroup(index);
  if (!eg.empty()) {
    return eg.setString(OS_UtilityCost_ComputationExtensibleFields::ComputeStep,str);
  }
  else {
    StringVector values(1u,str);
    return !insertExtensibleGroup(index,values).empty();
  }
  OS_ASSERT(false);
  return false;
}
bool UtilityCost_Charge_Block_Impl::setBlockCostPerUnitValueOrVariableName(unsigned index, const std::string& str) {
  IdfExtensibleGroup eg = getExtensibleGroup(index);
  if (!eg.empty()) {
    return eg.setString(OS_UtilityCost_Charge_BlockExtensibleFields::BlockCostperUnitValueorVariableName,str);
  }
  else {
    StringVector values(2u);
    values[OS_UtilityCost_Charge_BlockExtensibleFields::BlockCostperUnitValueorVariableName] = str;
    return !insertExtensibleGroup(index,values).empty();
  }
  OS_ASSERT(false);
  return false;
}
 bool EnergyManagementSystemProgramCallingManager_Impl::setProgram(const EnergyManagementSystemProgram& program, unsigned index) {
   //add program to {index} of vector of programs
   bool result = false;
   auto groups = extensibleGroups();
   unsigned sizeOfGroup = numExtensibleGroups();
   if (index <= sizeOfGroup) {
     IdfExtensibleGroup idfGroup = insertExtensibleGroup(index, StringVector());
     OS_ASSERT(!idfGroup.empty());
     ModelExtensibleGroup group = idfGroup.cast<ModelExtensibleGroup>();
     result = group.setPointer(0, program.handle());
   }
   return result;
 }
Beispiel #13
0
TEST_F(ModelFixture, ComponentWatcher_FromScratch) {
  // create schedule component
  Model justASchedule;
  ScheduleTypeLimits typeLimits(justASchedule);
  typeLimits.setName("Fraction");
  typeLimits.setLowerLimitValue(0.0);
  typeLimits.setUpperLimitValue(1.0);
  typeLimits.setNumericType("Continuous");
  ScheduleCompact schedule(justASchedule);
  EXPECT_TRUE(schedule.setPointer(OS_Schedule_CompactFields::ScheduleTypeLimitsName,
                                  typeLimits.handle()));
  Component scheduleComponent = schedule.createComponent();

  // create model with Lights objects and insert schedule component
  Model justLights;
  LightsDefinition lightsDefinition(justLights);
  Lights light1(lightsDefinition);
  OptionalComponentData ocd = justLights.insertComponent(scheduleComponent);

  // get ComponentData object
  ASSERT_TRUE(ocd);
  ComponentData componentData = *ocd;
  UUID versionUUID = componentData.versionUUID();

  // setting lighting schedule does not invalidate schedule component, or change its version UUID
  OptionalScheduleCompact oSchedule = componentData.primaryComponentObject().optionalCast<ScheduleCompact>();
  ASSERT_TRUE(oSchedule);
  schedule = *oSchedule;
  EXPECT_TRUE(light1.setSchedule(schedule));
  ASSERT_TRUE(componentData.initialized());
  EXPECT_EQ(versionUUID,componentData.versionUUID());

  // changing data field in componentObject does not invalidate schedule component, but does change version UUID
  StringVector values;
  values.push_back("Through: 12/31");
  IdfExtensibleGroup eg = componentData.primaryComponentObject().pushExtensibleGroup(values);
  EXPECT_FALSE(eg.empty());
  ASSERT_TRUE(componentData.initialized());
  EXPECT_NE(versionUUID,componentData.versionUUID());
  versionUUID = componentData.versionUUID();

  // changing type limits used by schedule in component invalidates the component
  Handle h = componentData.handle();
  EXPECT_TRUE(justLights.isMember(h));
  ScheduleTypeLimits newTypeLimits = typeLimits.clone(justLights).cast<ScheduleTypeLimits>();
  EXPECT_FALSE(newTypeLimits.handle() == typeLimits.handle());
  EXPECT_TRUE(schedule.setPointer(OS_Schedule_CompactFields::ScheduleTypeLimitsName,newTypeLimits.handle()));
  EXPECT_FALSE(componentData.initialized());
  EXPECT_FALSE(justLights.isMember(h));
}
 bool PeopleDefinition_Impl::setThermalComfortModelType(
     int i, const std::string& thermalComfortModelType)
 {
   int n = numThermalComfortModelTypes();
   if (i == n) {
     return pushThermalComfortModelType(thermalComfortModelType);
   }
   if (i < n) {
     IdfExtensibleGroup eg = getExtensibleGroup(i);
     OS_ASSERT(!eg.empty());
     return eg.setString(OS_People_DefinitionExtensibleFields::ThermalComfortModelType,
                         thermalComfortModelType);
   }
   return false;
 }
Beispiel #15
0
TEST_F(ModelFixture, ComponentWatcher_ComponentData_Interactions) {

  // create osm file that contains ComponentData
  Model model;
  DesignDay designDay(model);
  EXPECT_EQ(1u,model.numObjects());
  Component designDayComponent = designDay.createComponent();
  ComponentData componentData = designDayComponent.componentData();
  UUID uuid = componentData.uuid();
  UUID versionUUID = componentData.versionUUID();
  EXPECT_FALSE(versionUUID.isNull());
  model = Model();
  EXPECT_EQ(0u,model.numObjects());
  OptionalComponentData ocd = model.insertComponent(designDayComponent);
  ASSERT_TRUE(ocd);
  EXPECT_EQ(2u,model.numObjects());

  // simple insert should not change component UUIDs.
  componentData = *ocd;
  EXPECT_EQ(uuid,componentData.uuid());
  EXPECT_EQ(versionUUID,componentData.versionUUID());

  // changing data fields of component objects causes version id to change
  componentData.primaryComponentObject().cast<DesignDay>().setMaximumDryBulbTemperature(50.0);
  EXPECT_EQ(uuid,componentData.uuid());
  EXPECT_NE(versionUUID,componentData.versionUUID());
  versionUUID = componentData.versionUUID();

  // trying to change contents field to an invalid value directly is not allowed
  IdfExtensibleGroup eg = componentData.getExtensibleGroup(0);
  ASSERT_FALSE(eg.empty());
  std::string originalValue = eg.getString(0).get();
  EXPECT_EQ("Sizing Period Design Day 1",originalValue);
  EXPECT_FALSE(eg.setString(0,"My Material"));
  EXPECT_TRUE(componentData.initialized());
  EXPECT_EQ(2u,model.numObjects());
  EXPECT_EQ(originalValue,eg.getString(0).get());

  // ComponentData.remove() is okay (should automatically delete the ComponentWatcher)
  ocd = model.insertComponent(designDayComponent);
  ASSERT_TRUE(ocd);
  // should find original DesignDay and reinstantiate ComponentData
  EXPECT_EQ(2u,model.numObjects());
  componentData = *ocd;
  ASSERT_TRUE(componentData.initialized());
  EXPECT_FALSE(componentData.remove().empty());
  EXPECT_EQ(static_cast<unsigned>(1),model.numObjects());
}
Beispiel #16
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;
  }
Beispiel #17
0
 ClimateZone ClimateZones_Impl::appendClimateZone(const std::string& institution, 
                                                  const std::string documentName, 
                                                  unsigned year, 
                                                  const std::string& value) 
 {
   StringVector values;
   values.push_back(institution);
   values.push_back(documentName);
   std::stringstream ss;
   ss << year;
   values.push_back(ss.str());
   values.push_back(value);
   IdfExtensibleGroup eg = pushExtensibleGroup(values);
   if (eg.empty()) {
     std::shared_ptr<ClimateZones_Impl> p;
     return ClimateZone(p,numFields());
   }
   return eg.cast<ClimateZone>();
 }
Beispiel #18
0
  ModelObject ComponentData_Impl::getComponentObject(unsigned objectIndex) const {
    if (objectIndex >= numComponentObjects()) {
      LOG_AND_THROW("objectIndex = " << objectIndex << " >= numComponentObjects() = " << numComponentObjects() << ".");
    }

    IdfExtensibleGroup eg = getExtensibleGroup(objectIndex);
    if (eg.empty()) {
      LOG_AND_THROW("Cannot retrieve IdfExtensibleGroup at objectIndex = " << objectIndex);
    }

    ModelExtensibleGroup componentObjectData = eg.cast<ModelExtensibleGroup>();
    OptionalModelObject omo = componentObjectData.getModelObjectTarget<ModelObject>(
        OS_ComponentDataExtensibleFields::NameofObject);
    if (!omo) {
      LOG_AND_THROW("Cannot retrieve ModelObject at objectIndex = " << objectIndex);
    }

    return *omo;
  }
Beispiel #19
0
TEST_F(ModelFixture, ComponentWatcher_BadComponentDataFromWorkspace) {
  Workspace ws;
  OptionalWorkspaceObject owo = ws.addObject(IdfObject(IddObjectType::OS_ComponentData));
  ASSERT_TRUE(owo);
  // make component data ok except points to non-existent object
  WorkspaceObject cd = *owo;
  OptionalString oName = cd.name(); // should have been set by constructor
  ASSERT_TRUE(oName);
  EXPECT_FALSE(oName->empty());
  cd.setString(OS_ComponentDataFields::UUID,toString(createUUID()));
  cd.setString(OS_ComponentDataFields::VersionUUID,toString(createUUID()));
  StringVector values;
  values.push_back("My Material");
  IdfExtensibleGroup eg = cd.pushExtensibleGroup(values);
  EXPECT_TRUE(eg.empty()); // Cannot register a bad pointer.

  EXPECT_EQ(1u,ws.numObjects());
  Model model(ws);
  // expect ComponentWatcher creation to kick out ComponentData
  EXPECT_EQ(0u,model.numObjects());
}
boost::optional<IdfObject> ForwardTranslator::translateScheduleRuleset( ScheduleRuleset & modelObject )
{

  IdfObject scheduleYear( openstudio::IddObjectType::Schedule_Year );

  std::string scheduleYearName = modelObject.name().get();
  scheduleYear.setName(scheduleYearName);

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

  try {
    ScheduleDay defaultDaySchedule = modelObject.defaultDaySchedule();
    ScheduleDay summerDesignDaySchedule = modelObject.summerDesignDaySchedule();
    ScheduleDay winterDesignDaySchedule = modelObject.winterDesignDaySchedule();

    // initialize day of week schedules
    ScheduleDay sundaySchedule = defaultDaySchedule;
    ScheduleDay mondaySchedule = defaultDaySchedule;
    ScheduleDay tuesdaySchedule = defaultDaySchedule;
    ScheduleDay wednesdaySchedule = defaultDaySchedule;
    ScheduleDay thursdaySchedule = defaultDaySchedule;
    ScheduleDay fridaySchedule = defaultDaySchedule;
    ScheduleDay saturdaySchedule = defaultDaySchedule;

    // these are not yet exposed
    ScheduleDay holidayDaySchedule = defaultDaySchedule;
    ScheduleDay customDay1Schedule = defaultDaySchedule;
    ScheduleDay customDay2Schedule = defaultDaySchedule;

    // loop over entire year
    model::YearDescription yd = modelObject.model().getUniqueModelObject<model::YearDescription>();
    openstudio::Date jan1 = yd.makeDate(MonthOfYear::Jan, 1);
    openstudio::Date dec31 = yd.makeDate(MonthOfYear::Dec, 31);

    // this is the current date we are at in the year
    openstudio::Date date = jan1;

    // this is the week schedule for the week ending on the last saturday before the current date
    boost::optional<WeekScheduleStruct> weekSchedule;

    // this is the week schedule for the week before weekSchedule
    boost::optional<WeekScheduleStruct> lastWeekSchedule;

    // this is the saturday on which lastWeekSchedule ends
    openstudio::Date lastDate;

    // iterate over the schedule for each day of the year
    std::vector<ScheduleDay> daySchedules = modelObject.getDaySchedules(jan1, dec31);
    for (ScheduleDay& daySchedule : daySchedules){

      // translate the day schedule
      translateAndMapModelObject(daySchedule);

      // set day of week schedule
      switch(date.dayOfWeek().value()){
        case DayOfWeek::Sunday:
          sundaySchedule = daySchedule;
          break;
        case DayOfWeek::Monday:
          mondaySchedule = daySchedule;
          break;
        case DayOfWeek::Tuesday:
          tuesdaySchedule = daySchedule;
          break;
        case DayOfWeek::Wednesday:
          wednesdaySchedule = daySchedule;
          break;
        case DayOfWeek::Thursday:
          thursdaySchedule = daySchedule;
          break;
        case DayOfWeek::Friday:
          fridaySchedule = daySchedule;
          break;
        case DayOfWeek::Saturday:
          saturdaySchedule = daySchedule;
          break;
        default:
          OS_ASSERT(false);
      }

      // update week schedules each saturday
      if((date.dayOfWeek().value() == DayOfWeek::Saturday)){

        // set last week schedule before we overwrite week schedule
        lastDate = date - Time(7);
        lastWeekSchedule = weekSchedule;

        // set the week schedule
        weekSchedule = WeekScheduleStruct();
        weekSchedule->sundaySchedule = sundaySchedule.name().get();
        weekSchedule->mondaySchedule = mondaySchedule.name().get();
        weekSchedule->tuesdaySchedule = tuesdaySchedule.name().get();
        weekSchedule->wednesdaySchedule = wednesdaySchedule.name().get();
        weekSchedule->thursdaySchedule = thursdaySchedule.name().get();
        weekSchedule->fridaySchedule = fridaySchedule.name().get();
        weekSchedule->saturdaySchedule = saturdaySchedule.name().get();
        weekSchedule->holidayDaySchedule = holidayDaySchedule.name().get();
        weekSchedule->summerDesignDaySchedule = summerDesignDaySchedule.name().get();
        weekSchedule->winterDesignDaySchedule = winterDesignDaySchedule.name().get();
        weekSchedule->customDay1Schedule = customDay1Schedule.name().get();
        weekSchedule->customDay2Schedule = customDay2Schedule.name().get();

        // check if this schedule is equal to last week schedule
        if (weekSchedule && lastWeekSchedule && ( !(weekSchedule.get() == lastWeekSchedule.get()) )){
          // if not write out last week schedule

          // get last extensible group, if any, to find start date otherwise use jan1
          openstudio::Date startDate;
          std::vector<IdfExtensibleGroup> extensibleGroups = scheduleYear.extensibleGroups();
          if (extensibleGroups.empty()){
            startDate = jan1;
          }else{
            // day after last end date
            boost::optional<int> startMonth = extensibleGroups.back().getInt(3,true);
            OS_ASSERT(startMonth);
            boost::optional<int> startDay = extensibleGroups.back().getInt(4,true);
            OS_ASSERT(startDay);
            startDate = yd.makeDate(*startMonth, *startDay) + Time(1);
          }

          OS_ASSERT(startDate <= lastDate);

          // Name the schedule week
          lastWeekSchedule->setName(scheduleYearName, startDate, lastDate);

          // add the values
          std::vector<std::string> values;
          values.push_back(lastWeekSchedule->name);
          values.push_back(boost::lexical_cast<std::string>(startDate.monthOfYear().value()));
          values.push_back(boost::lexical_cast<std::string>(startDate.dayOfMonth()));
          values.push_back(boost::lexical_cast<std::string>(lastDate.monthOfYear().value()));
          values.push_back(boost::lexical_cast<std::string>(lastDate.dayOfMonth()));
          IdfExtensibleGroup test = scheduleYear.pushExtensibleGroup(values);
          OS_ASSERT(!test.empty());

          // Write the schedule
          m_idfObjects.push_back(lastWeekSchedule->toIdfObject());

        }
      }

      // if last day of year update and write out current week schedule
      if (date == dec31){

        // we may now think of date as being the next saturday on or after 12/31

        // set last week schedule before we overwrite week schedule
        lastDate = date - Time(1);
        while (lastDate.dayOfWeek().value() != DayOfWeek::Saturday){
          lastDate = lastDate - Time(1);
          lastWeekSchedule = weekSchedule;
        }

        // set the week schedule, some of these dates may extend past 12/31
        weekSchedule = WeekScheduleStruct();
        weekSchedule->sundaySchedule = sundaySchedule.name().get();
        weekSchedule->mondaySchedule = mondaySchedule.name().get();
        weekSchedule->tuesdaySchedule = tuesdaySchedule.name().get();
        weekSchedule->wednesdaySchedule = wednesdaySchedule.name().get();
        weekSchedule->thursdaySchedule = thursdaySchedule.name().get();
        weekSchedule->fridaySchedule = fridaySchedule.name().get();
        weekSchedule->saturdaySchedule = saturdaySchedule.name().get();
        weekSchedule->holidayDaySchedule = holidayDaySchedule.name().get();
        weekSchedule->summerDesignDaySchedule = summerDesignDaySchedule.name().get();
        weekSchedule->winterDesignDaySchedule = winterDesignDaySchedule.name().get();
        weekSchedule->customDay1Schedule = customDay1Schedule.name().get();
        weekSchedule->customDay2Schedule = customDay2Schedule.name().get();

        // check if this schedule is equal to last week schedule
        if (weekSchedule && lastWeekSchedule && ( !(weekSchedule.get() == lastWeekSchedule.get()) )){
          // if not write out last week schedule

          // get last extensible group, if any, to find start date otherwise use jan1
          openstudio::Date startDate;
          std::vector<IdfExtensibleGroup> extensibleGroups = scheduleYear.extensibleGroups();
          if (extensibleGroups.empty()){
            startDate = jan1;
          }else{
            // day after last end date
            boost::optional<int> startMonth = extensibleGroups.back().getInt(3,true);
            OS_ASSERT(startMonth);
            boost::optional<int> startDay = extensibleGroups.back().getInt(4,true);
            OS_ASSERT(startDay);
            startDate = yd.makeDate(*startMonth, *startDay) + Time(1);
          }

          OS_ASSERT(startDate <= lastDate);

          // Name the schedule week
          lastWeekSchedule->setName(scheduleYearName, startDate, lastDate);

          // add the values
          std::vector<std::string> values;
          values.push_back(lastWeekSchedule->name);
          values.push_back(boost::lexical_cast<std::string>(startDate.monthOfYear().value()));
          values.push_back(boost::lexical_cast<std::string>(startDate.dayOfMonth()));
          values.push_back(boost::lexical_cast<std::string>(lastDate.monthOfYear().value()));
          values.push_back(boost::lexical_cast<std::string>(lastDate.dayOfMonth()));
          IdfExtensibleGroup test = scheduleYear.pushExtensibleGroup(values);
          OS_ASSERT(!test.empty());

          // Write the schedule
          m_idfObjects.push_back(lastWeekSchedule->toIdfObject());

        }

        // write out the last week schedule

        // get last extensible group, if any, to find start date otherwise use jan1
        openstudio::Date startDate;
        std::vector<IdfExtensibleGroup> extensibleGroups = scheduleYear.extensibleGroups();
        if (extensibleGroups.empty()){
          startDate = jan1;
        }else{
          // day after last end date
          boost::optional<int> startMonth = extensibleGroups.back().getInt(3,true);
          OS_ASSERT(startMonth);
          boost::optional<int> startDay = extensibleGroups.back().getInt(4,true);
          OS_ASSERT(startDay);
          startDate = yd.makeDate(*startMonth, *startDay) + Time(1);
        }

        OS_ASSERT(startDate <= date);

        // Name the schedule week
        weekSchedule->setName(scheduleYearName, startDate, date);

        // add the values
        std::vector<std::string> values;
        values.push_back(weekSchedule->name);
        values.push_back(boost::lexical_cast<std::string>(startDate.monthOfYear().value()));
        values.push_back(boost::lexical_cast<std::string>(startDate.dayOfMonth()));
        values.push_back(boost::lexical_cast<std::string>(date.monthOfYear().value()));
        values.push_back(boost::lexical_cast<std::string>(date.dayOfMonth()));
        IdfExtensibleGroup test = scheduleYear.pushExtensibleGroup(values);
        OS_ASSERT(!test.empty());

        // Write the schedule
        m_idfObjects.push_back(weekSchedule->toIdfObject());

      }

      // increment date
      date += Time(1);
    }

  }
  catch (std::exception& e) {
    LOG(Error,"Unable to translate " << modelObject.briefDescription()
        << " to EnergyPlus IDF, because " << e.what() << ".");
    return boost::none;
  }

  m_idfObjects.push_back(scheduleYear);

  m_map.insert(std::make_pair(modelObject.handle(), scheduleYear));

  // translate day schedules

  ScheduleDay defaultDaySchedule = modelObject.defaultDaySchedule();
  ScheduleDay summerDesignDaySchedule = modelObject.summerDesignDaySchedule();
  ScheduleDay winterDesignDaySchedule = modelObject.winterDesignDaySchedule();

  translateAndMapModelObject(defaultDaySchedule);
  translateAndMapModelObject(summerDesignDaySchedule);
  translateAndMapModelObject(winterDesignDaySchedule);

  // translate schedule rules, these are returned in order
  for (ScheduleRule scheduleRule : modelObject.scheduleRules()){
    ScheduleDay daySchedule = scheduleRule.daySchedule();
    translateAndMapModelObject(daySchedule);
  }

  return scheduleYear;
}
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::translateAirLoopHVACOutdoorAirSystem( AirLoopHVACOutdoorAirSystem & modelObject )
{
  OptionalString s;
  IdfObject idfObject(IddObjectType::AirLoopHVAC_OutdoorAirSystem);

  m_idfObjects.push_back(idfObject);

  // Name
  std::string name = modelObject.name().get();
  idfObject.setString(openstudio::AirLoopHVAC_OutdoorAirSystemFields::Name,name);
 

  // Controller List
  IdfObject controllerListIdf(IddObjectType::AirLoopHVAC_ControllerList);
  controllerListIdf.setName(name + " Controller List");
  controllerListIdf.clearExtensibleGroups();

  m_idfObjects.push_back(controllerListIdf);

  ControllerOutdoorAir controllerOutdoorAir = modelObject.getControllerOutdoorAir();
  boost::optional<IdfObject> temp = translateAndMapModelObject(controllerOutdoorAir);

  s = controllerListIdf.name();
  if(s)
  {
    idfObject.setString(openstudio::AirLoopHVAC_OutdoorAirSystemFields::ControllerListName,*s);
  }

  s = temp->iddObject().name();
  StringVector groupFields(2u);
  bool addGroup(false);
  if(s)
  {
    groupFields[0] = *s;
    addGroup = true;
  }

  s = temp->name();
  if(s)
  {
    groupFields[1] = *s;
    addGroup = true;
  }

  if (addGroup) {
    IdfExtensibleGroup eg = controllerListIdf.pushExtensibleGroup(groupFields);
    OS_ASSERT(!eg.empty());
  }

  // Field: Availability Manager List Name //////////////////////////////////
  IdfObject availabilityManagerListIdf(IddObjectType::AvailabilityManagerAssignmentList);
  availabilityManagerListIdf.setName(name + " Availability Manager");
  m_idfObjects.push_back(availabilityManagerListIdf);

  IdfObject availabilityManagerScheduledIdf = IdfObject(openstudio::IddObjectType::AvailabilityManager_Scheduled);
  availabilityManagerScheduledIdf.createName();
  m_idfObjects.push_back(availabilityManagerScheduledIdf);

  Schedule alwaysOn = modelObject.model().alwaysOnDiscreteSchedule();
  IdfObject alwaysOnIdf = translateAndMapModelObject(alwaysOn).get();

  s = availabilityManagerListIdf.getString(openstudio::AvailabilityManagerAssignmentListFields::Name);
  if(s)
  {
    idfObject.setString(openstudio::AirLoopHVAC_OutdoorAirSystemFields::AvailabilityManagerListName,*s);
  }

  availabilityManagerListIdf.setString(1 + openstudio::AvailabilityManagerAssignmentListExtensibleFields::AvailabilityManagerObjectType,
                                       availabilityManagerScheduledIdf.iddObject().name());
  availabilityManagerListIdf.setString(1 + openstudio::AvailabilityManagerAssignmentListExtensibleFields::AvailabilityManagerName,
                                       availabilityManagerScheduledIdf.name().get());
  availabilityManagerScheduledIdf.setString(openstudio::AvailabilityManager_ScheduledFields::ScheduleName,alwaysOnIdf.name().get());

  // OA Node List
  s = modelObject.outboardOANode()->name();
  IdfObject oaNodeListIdf(openstudio::IddObjectType::OutdoorAir_NodeList);
  if(s)
  {
    oaNodeListIdf.setString(0,*s);
  }
  m_idfObjects.push_back(oaNodeListIdf);

  ///////////////////////////////////////////////////////////////////////////
  // Field: Outdoor Air Equipment List Name /////////////////////////////////
  IdfObject equipmentListIdf(IddObjectType::AirLoopHVAC_OutdoorAirSystem_EquipmentList);
  equipmentListIdf.setName(name + " Equipment List");

  m_idfObjects.push_back(equipmentListIdf);

  IdfObject outdoorAirMixerIdf(IddObjectType::OutdoorAir_Mixer);
  outdoorAirMixerIdf.setName(name + " Outdoor Air Mixer");
  m_idfObjects.push_back(outdoorAirMixerIdf);

  s = modelObject.mixedAirModelObject()->name();
  if(s)
  {
    outdoorAirMixerIdf.setString(OutdoorAir_MixerFields::MixedAirNodeName,*s);
  }
  s = modelObject.outdoorAirModelObject()->name();
  if(s)
  {
    outdoorAirMixerIdf.setString(OutdoorAir_MixerFields::OutdoorAirStreamNodeName,*s);
  }

  s = modelObject.reliefAirModelObject()->name();
  if(s)
  {
    outdoorAirMixerIdf.setString(OutdoorAir_MixerFields::ReliefAirStreamNodeName,*s);
  }

  s = modelObject.returnAirModelObject()->name();
  if(s)
  {
    outdoorAirMixerIdf.setString(OutdoorAir_MixerFields::ReturnAirStreamNodeName,*s);
  }

  s = outdoorAirMixerIdf.iddObject().name();
  equipmentListIdf.setString(1,*s);
  s = outdoorAirMixerIdf.name();
  equipmentListIdf.setString(2,*s);

  ModelObjectVector oaModelObjects = modelObject.oaComponents();
  ModelObjectVector::iterator oaIt;
  unsigned i = 3;
  for( oaIt = oaModelObjects.begin();
       oaIt != oaModelObjects.end();
       ++oaIt )
  {
    if( ! oaIt->optionalCast<Node>() )
    {
      s = stripOS2(oaIt->iddObject().name());
      if(s)
        equipmentListIdf.setString(i,*s);
      i++;
      s = oaIt->name();
      if(s)
        equipmentListIdf.setString(i,*s);
      i++;
    }
  }

  s = equipmentListIdf.name();
  if(s)
  {
    idfObject.setString(openstudio::AirLoopHVAC_OutdoorAirSystemFields::OutdoorAirEquipmentListName,*s);
  }

  for( oaIt = oaModelObjects.begin();
       oaIt != oaModelObjects.end();
       ++oaIt )
  {
    translateAndMapModelObject(*oaIt);
  }

  return boost::optional<IdfObject>(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;

}