diff --git a/resources/energyplus/ProposedEnergy+.idd b/resources/energyplus/ProposedEnergy+.idd index 46842f52c5..0047118a5b 100644 --- a/resources/energyplus/ProposedEnergy+.idd +++ b/resources/energyplus/ProposedEnergy+.idd @@ -11313,9 +11313,7 @@ People, \memo If a ZoneList, SpaceList, or a Zone comprised of more than one Space is specified \memo then this definition applies to all applicable spaces, and each instance will \memo be named with the Space Name plus this Object Name. - \extensible:1 \min-fields 10 - \max-fields 27 A1 , \field Name \required-field \type alpha @@ -11422,33 +11420,92 @@ People, \object-list ScheduleNames \note units in the schedule are m/s \note optional (only required for runs of thermal comfort models: Fanger, Pierce, KSU, CoolingEffectASH55 and AnkleDraftASH55) - A14, \field Ankle Level Air Velocity Schedule Name + A14, \field Thermal Comfort Model 1 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (only needed for people thermal comfort results reporting) + A15, \field Thermal Comfort Model 2 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (second type of thermal comfort model and results reporting) + A16, \field Thermal Comfort Model 3 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (third thermal comfort model and report type) + A17, \field Thermal Comfort Model 4 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (fourth thermal comfort model and report type) + A18, \field Thermal Comfort Model 5 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (fifth thermal comfort model and report type) + A19, \field Thermal Comfort Model 6 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (sixth thermal comfort model and report type) + A20, \field Thermal Comfort Model 7 Type + \type choice + \key Fanger + \key Pierce + \key KSU + \key AdaptiveASH55 + \key AdaptiveCEN15251 + \key CoolingEffectASH55 + \key AnkleDraftASH55 + \note optional (seventh thermal comfort model and report type) + A21, \field Ankle Level Air Velocity Schedule Name \type object-list \object-list ScheduleNames \note units in the schedule are m/s \note this is the schedule of the air speed at the 0.1 m above the floor \note optional (only required for runs of thermal comfort models AnkleDraftASH55) - N7 , \field Cold Stress Temperature Threshold + N7, \field Cold Stress Temperature Threshold \type real \units C \note this is the indoor safe temperature threshold for cold stress \default 15.56 - N8 , \field Heat Stress Temperature Threshold + N8; \field Heat Stress Temperature Threshold \type real \units C \note this is the indoor safe temperature threshold for heat stress \default 30 - A15; \field Thermal Comfort Model 1 Type - \type choice - \key Fanger - \key Pierce - \key KSU - \key AdaptiveASH55 - \key AdaptiveCEN15251 - \key CoolingEffectASH55 - \key AnkleDraftASH55 - \note optional (only needed for people thermal comfort results reporting) - \begin-extensible ComfortViewFactorAngles, \memo Used to specify radiant view factors for thermal comfort calculations. @@ -22262,7 +22319,7 @@ Sizing:Zone, \note Use SupplyAirHumidityRatio to enter the humidity ratio when zone dehumidification \note is required. The supply air humidity ratio should be less than the zone humidity \note ratio at the zone thermostat and humidistat set point condition. - \note Use HumidityRatioDifference to enter the difference in humidity ratio from the + \note Use HumidityRatioDifference to enter the difference in humidity ratio from the \note zone thermostat and humidistat set point condition. \type choice \key SupplyAirHumidityRatio @@ -22281,7 +22338,7 @@ Sizing:Zone, \note Zone Latent Cooling Design Supply Air Humidity Ratio Input Method = HumidityRatioDifference. \note This input is a positive value and defines the difference between the zone humidity \note ratio at the thermostat and humidistat set point condition and the supply air - \note humidity ratio entering the zone. + \note humidity ratio entering the zone. \minimum> 0.0 \type real \default 0.005 @@ -22290,7 +22347,7 @@ Sizing:Zone, \note Use SupplyAirHumidityRatio to enter the humidity ratio when zone humidification \note is required. The supply air humidity ratio should be greater than the zone humidity \note ratio at the zone thermostat and humidistat set point condition. - \note Use HumidityRatioDifference to enter the difference in humidity ratio from the + \note Use HumidityRatioDifference to enter the difference in humidity ratio from the \note zone thermostat and humidistat set point condition. \type choice \key SupplyAirHumidityRatio @@ -22309,7 +22366,7 @@ Sizing:Zone, \note Heating Design Supply Air Humidity Ratio Input Method = HumidityRatioDifference. \note This input is a positive value and defines the difference between the zone humidity \note ratio at the thermostat and humidistat set point condition and the supply air - \note humidity ratio entering the zone. + \note humidity ratio entering the zone. \minimum 0.0 \type real \default 0.005 @@ -28504,13 +28561,13 @@ ZoneHVAC:AirDistributionUnit, \object-list DesignSpecificationAirTerminalSizingName ZoneHVAC:ExhaustControl, - \memo Defines a controlled exhaust flow from a zone which finally feeds into + \memo Defines a controlled exhaust flow from a zone which finally feeds into \memo one of AirLoopHVAC:ZoneMixer's inlets, which are part of an AirLoopHVAC:ExhaustSystem. A1 , \field Name \required-field A2 , \field Availability Schedule Name \note Availability schedule name for this exhaust system. Schedule value > 0 means it is available. - \note If this field is blank, the exhaust system is always available. If the attached + \note If this field is blank, the exhaust system is always available. If the attached \note AirLoopHVAC:ExhaustSystem is off, then the flow will be zero. \type object-list \object-list ScheduleNames @@ -31998,7 +32055,7 @@ Coil:Heating:DX:SingleSpeed, \note should be between 0.00004027 m3/s and .00006041 m3/s per watt of rated heating capacity N4 , \field Rated Supply Fan Power Per Volume Flow Rate 2017 \note Enter the supply fan power per air volume flow rate at the rated test conditions. - \note as defined in the 2017 version of ANSI/AHRI Standard 210/240. + \note as defined in the 2017 version of ANSI/AHRI Standard 210/240. \note The test conditions vary external static pressure based on heating capacity. \note This value is only used to calculate Heating Seasonal Performance Factor(HSPF). \note This value is not used for modeling the supply (condenser) fan during simulations. @@ -32009,7 +32066,7 @@ Coil:Heating:DX:SingleSpeed, \default 773.3 N5 , \field Rated Supply Fan Power Per Volume Flow Rate 2023 \note Enter the supply fan power per air volume flow rate at the rated test conditions. - \note as defined in the 2017 version of ANSI/AHRI Standard 210/240. + \note as defined in the 2017 version of ANSI/AHRI Standard 210/240. \note The test conditions vary external static pressure based on heating capacity. \note This value is only used to calculate Heating Seasonal Performance Factor(HSPF). \note This value is not used for modeling the supply (condenser) fan during simulations. @@ -33237,7 +33294,7 @@ Coil:Heating:WaterToAirHeatPump:EquationFit, N7, \field Ratio of Rated Heating Capacity to Rated Cooling Capacity \note Ratio of rated heating capacity to rated cooling capacity. This \note input is used to calculate the heating or cooling capacity when autosizing. - \note This input is only used if a companion cooling coil of the same type + \note This input is only used if a companion cooling coil of the same type \note (Coil:Cooling:WaterToAirHeatPump:EquationFit) is used. This input is only \note used when a sizing run for the system which uses this object is requested \note and when the coil capacity is autosized. diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 3c6eba5cda..313054175e 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -6842,10 +6842,26 @@ OS:People, \note optional (only required for thermal comfort runs) \type object-list \object-list ScheduleNames - N1; \field Multiplier + N1, \field Multiplier \type real \minimum 0.0 \default 1.0 + A11, \field Ankle Level Air Velocity Schedule Name + \type object-list + \object-list ScheduleNames + \note units in the schedule are m/s + \note this is the schedule of the air speed at the 0.1 m above the floor + \note optional (only required for runs of thermal comfort models AnkleDraftASH55) + N2, \field Cold Stress Temperature Threshold + \type real + \units C + \note this is the indoor safe temperature threshold for cold stress + \default 15.56 + N3; \field Heat Stress Temperature Threshold + \type real + \units C + \note this is the indoor safe temperature threshold for heat stress + \default 30 OS:Lights, \min-fields 1 diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePeople.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePeople.cpp index e11cc2ff9f..37bdfd8134 100644 --- a/src/energyplus/ForwardTranslator/ForwardTranslatePeople.cpp +++ b/src/energyplus/ForwardTranslator/ForwardTranslatePeople.cpp @@ -150,13 +150,35 @@ namespace energyplus { } } - for (int i = 0, n = definition.numThermalComfortModelTypes(); i < n; ++i) { + // As of 22.2.0, this is no longer possible to make this an extensible field + // because E+ added 3 regular fields at the end (eg: Ankle Level Velocity Schedule Name) + for (int i = 0, numComfortModelTypes = 0; i < definition.numThermalComfortModelTypes(); ++i) { OptionalString s = definition.getThermalComfortModelType(i); if (s) { - idfObject.pushExtensibleGroup(StringVector(1U, *s)); + ++numComfortModelTypes; + if (numComfortModelTypes > 7) { + LOG(Warn, "For " << definition.briefDescription() << ", only 7 Thermal Confort Model Types are supported by EnergyPlus, number " + << numComfortModelTypes << " [=" << *s << "] will be ignored."); + } else { + idfObject.setString(PeopleFields::ThermalComfortModel1Type + i, *s); + } } } + if (boost::optional schedule_ = modelObject.ankleLevelAirVelocitySchedule()) { + if (auto idf_schedule_ = translateAndMapModelObject(schedule_.get())) { + idfObject.setString(PeopleFields::AnkleLevelAirVelocityScheduleName, idf_schedule_->nameString()); + } + } + + if (!modelObject.isColdStressTemperatureThresholdDefaulted()) { + idfObject.setDouble(PeopleFields::ColdStressTemperatureThreshold, modelObject.coldStressTemperatureThreshold()); + } + + if (!modelObject.isHeatStressTemperatureThresholdDefaulted()) { + idfObject.setDouble(PeopleFields::HeatStressTemperatureThreshold, modelObject.heatStressTemperatureThreshold()); + } + return idfObject; } diff --git a/src/energyplus/ReverseTranslator/ReverseTranslatePeople.cpp b/src/energyplus/ReverseTranslator/ReverseTranslatePeople.cpp index 00f18efc5c..24a0f9dabc 100644 --- a/src/energyplus/ReverseTranslator/ReverseTranslatePeople.cpp +++ b/src/energyplus/ReverseTranslator/ReverseTranslatePeople.cpp @@ -131,12 +131,12 @@ namespace energyplus { LOG(Error, "SurfaceName_AngleFactorListName not currently imported"); } - for (unsigned i = 0, n = workspaceObject.numExtensibleGroups(); i < n; ++i) { - IdfExtensibleGroup eg = workspaceObject.getExtensibleGroup(i); - OS_ASSERT(!eg.empty()); - s = eg.getString(openstudio::PeopleExtensibleFields::ThermalComfortModelType); + // As of 22.2.0, this is no longer possible to make this an extensible field + // because E+ added 3 regular fields at the end (eg: Ankle Level Velocity Schedule Name) + for (unsigned i = PeopleFields::ThermalComfortModel1Type, k = 0; i <= PeopleFields::ThermalComfortModel7Type; ++i) { + s = workspaceObject.getString(i, false, true); if (s) { - definition.setThermalComfortModelType(i, *s); + definition.setThermalComfortModelType(k++, *s); } } @@ -235,6 +235,31 @@ namespace energyplus { } } + target = workspaceObject.getTarget(openstudio::PeopleFields::AnkleLevelAirVelocityScheduleName); + if (target) { + OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target); + if (modelObject) { + if (OptionalSchedule intermediate = modelObject->optionalCast()) { + Schedule schedule(*intermediate); + bool ok = people.setAnkleLevelAirVelocitySchedule(schedule); + if (!ok) { + LOG(Error, "Unable to set " << people.briefDescription() << "'s Ankle Level Air Velocity schedule to " << schedule.briefDescription() + << ", likely because of a ScheduleTypeLimits conflict."); + } + } + } + } + + d = workspaceObject.getDouble(openstudio::PeopleFields::ColdStressTemperatureThreshold); + if (d) { + people.setColdStressTemperatureThreshold(*d); + } + + d = workspaceObject.getDouble(openstudio::PeopleFields::HeatStressTemperatureThreshold); + if (d) { + people.setHeatStressTemperatureThreshold(*d); + } + return people; } diff --git a/src/energyplus/Test/People_GTest.cpp b/src/energyplus/Test/People_GTest.cpp index e8c2fc9312..cf56a5b1d1 100644 --- a/src/energyplus/Test/People_GTest.cpp +++ b/src/energyplus/Test/People_GTest.cpp @@ -40,6 +40,7 @@ #include "../../model/People_Impl.hpp" #include "../../model/PeopleDefinition.hpp" #include "../../model/PeopleDefinition_Impl.hpp" +#include "../../model/ScheduleConstant.hpp" #include #include @@ -53,21 +54,201 @@ using namespace openstudio::model; using namespace openstudio; TEST_F(EnergyPlusFixture, ForwardTranslator_People) { - Model model; + Model m; - ThermalZone zone(model); + ThermalZone zone(m); - Space space(model); + Space space(m); space.setThermalZone(zone); - PeopleDefinition def(model); + PeopleDefinition pd(m); + EXPECT_TRUE(pd.setSpaceFloorAreaperPerson(10.0)); + EXPECT_TRUE(pd.setThermalComfortModelType(0, "Fanger")); + EXPECT_TRUE(pd.setThermalComfortModelType(1, "Pierce")); + EXPECT_TRUE(pd.setFractionRadiant(0.35)); + EXPECT_TRUE(pd.setSensibleHeatFraction(0.2)); + EXPECT_TRUE(pd.setCarbonDioxideGenerationRate(3.6e-8)); + EXPECT_TRUE(pd.setEnableASHRAE55ComfortWarnings(true)); + EXPECT_TRUE(pd.setMeanRadiantTemperatureCalculationType("SurfaceWeighted")); - People people(def); - people.setSpace(space); + People p(pd); + EXPECT_TRUE(p.setSpace(space)); + + { + ScheduleConstant numPeopleSch(m); + numPeopleSch.setName("NumberofPeopleSchedule"); + numPeopleSch.setValue(1); + EXPECT_TRUE(p.setNumberofPeopleSchedule(numPeopleSch)); + } + { + ScheduleConstant activitySch(m); + activitySch.setName("ActivitySchedule"); + activitySch.setValue(131.8); + EXPECT_TRUE(p.setActivityLevelSchedule(activitySch)); + } + { + + ScheduleConstant workEffSch(m); + workEffSch.setName("WorkEfficiencySchedule"); + workEffSch.setValue(0.0); + EXPECT_TRUE(p.setWorkEfficiencySchedule(workEffSch)); + } + { + ScheduleConstant cloSch(m); + cloSch.setName("ClothingInsulationSchedule"); + cloSch.setValue(1.0); + EXPECT_TRUE(p.setClothingInsulationSchedule(cloSch)); + } + + { + ScheduleConstant airSch(m); + airSch.setName("AirVelocitySchedule"); + airSch.setValue(0.137); + EXPECT_TRUE(p.setAirVelocitySchedule(airSch)); + } + + EXPECT_TRUE(p.setMultiplier(2)); + { + ScheduleConstant ankleSch(m); + ankleSch.setName("AnkleLevelAirVelocitySchedule"); + ankleSch.setValue(0.127); + EXPECT_TRUE(p.setAnkleLevelAirVelocitySchedule(ankleSch)); + } + + EXPECT_TRUE(p.setColdStressTemperatureThreshold(15.0)); + EXPECT_TRUE(p.setHeatStressTemperatureThreshold(31.0)); ForwardTranslator ft; - Workspace workspace = ft.translateModel(model); + Workspace workspace = ft.translateModel(m); std::vector peopleObjects = workspace.getObjectsByType(IddObjectType::People); ASSERT_EQ(1u, peopleObjects.size()); + + auto& peopleObject = peopleObjects.front(); + + EXPECT_EQ("Space 1", peopleObject.getString(PeopleFields::ZoneorZoneListorSpaceorSpaceListName).get()); + EXPECT_EQ("NumberofPeopleSchedule", peopleObject.getString(PeopleFields::NumberofPeopleScheduleName).get()); + EXPECT_EQ("Area/Person", peopleObject.getString(PeopleFields::NumberofPeopleCalculationMethod).get()); + EXPECT_TRUE(peopleObject.isEmpty(PeopleFields::NumberofPeople)); + EXPECT_TRUE(peopleObject.isEmpty(PeopleFields::PeopleperFloorArea)); + EXPECT_EQ(20.0, peopleObject.getDouble(PeopleFields::FloorAreaperPerson).get()); + EXPECT_EQ(0.35, peopleObject.getDouble(PeopleFields::FractionRadiant).get()); + EXPECT_EQ(0.2, peopleObject.getDouble(PeopleFields::SensibleHeatFraction).get()); + EXPECT_EQ("ActivitySchedule", peopleObject.getString(PeopleFields::ActivityLevelScheduleName).get()); + EXPECT_EQ(3.6e-08, peopleObject.getDouble(PeopleFields::CarbonDioxideGenerationRate).get()); + EXPECT_EQ("Yes", peopleObject.getString(PeopleFields::EnableASHRAE55ComfortWarnings).get()); + EXPECT_EQ("SurfaceWeighted", peopleObject.getString(PeopleFields::MeanRadiantTemperatureCalculationType).get()); + EXPECT_TRUE(peopleObject.isEmpty(PeopleFields::SurfaceName_AngleFactorListName)); + EXPECT_EQ("WorkEfficiencySchedule", peopleObject.getString(PeopleFields::WorkEfficiencyScheduleName).get()); + + EXPECT_TRUE(peopleObject.isEmpty(PeopleFields::ClothingInsulationCalculationMethod)); + EXPECT_EQ("ClothingInsulationSchedule", peopleObject.getString(PeopleFields::ClothingInsulationCalculationMethod, true).get()); + EXPECT_TRUE(peopleObject.isEmpty(PeopleFields::ClothingInsulationCalculationMethodScheduleName)); + + EXPECT_EQ("ClothingInsulationSchedule", peopleObject.getString(PeopleFields::ClothingInsulationScheduleName).get()); + EXPECT_EQ("AirVelocitySchedule", peopleObject.getString(PeopleFields::AirVelocityScheduleName).get()); + EXPECT_EQ("Fanger", peopleObject.getString(PeopleFields::ThermalComfortModel1Type).get()); + EXPECT_EQ("Pierce", peopleObject.getString(PeopleFields::ThermalComfortModel2Type).get()); + + EXPECT_EQ("AnkleLevelAirVelocitySchedule", peopleObject.getString(PeopleFields::AnkleLevelAirVelocityScheduleName).get()); + EXPECT_EQ(15.0, peopleObject.getDouble(PeopleFields::ColdStressTemperatureThreshold).get()); + EXPECT_EQ(31.0, peopleObject.getDouble(PeopleFields::HeatStressTemperatureThreshold).get()); +} + +TEST_F(EnergyPlusFixture, ReverseTranslator_People) { + + ReverseTranslator rt; + + Workspace w(StrictnessLevel::Minimal, IddFileType::EnergyPlus); + + OptionalWorkspaceObject _i_zone = w.addObject(IdfObject(IddObjectType::Zone)); + ASSERT_TRUE(_i_zone); + _i_zone->setName("Space1"); + + OptionalWorkspaceObject _i_people = w.addObject(IdfObject(IddObjectType::People)); + ASSERT_TRUE(_i_people); + + EXPECT_TRUE(_i_people->setPointer(PeopleFields::ZoneorZoneListorSpaceorSpaceListName, _i_zone->handle())); + + auto assignSchedule = [&_i_people, &w](unsigned index, const std::string& scheduleName) { + OptionalWorkspaceObject _i_sch = w.addObject(IdfObject(IddObjectType::Schedule_Constant)); + ASSERT_TRUE(_i_sch); + + _i_sch->setName(scheduleName); + EXPECT_TRUE(_i_people->setPointer(index, _i_sch->handle())); + }; + + assignSchedule(PeopleFields::NumberofPeopleScheduleName, "NumberofPeopleSchedule"); + EXPECT_TRUE(_i_people->setString(PeopleFields::NumberofPeopleCalculationMethod, "Area/Person")); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::NumberofPeople, 0.0)); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::PeopleperFloorArea, 0.0)); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::FloorAreaperPerson, 10.0)); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::FractionRadiant, 0.35)); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::SensibleHeatFraction, 0.2)); + + assignSchedule(PeopleFields::ActivityLevelScheduleName, "ActivitySchedule"); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::CarbonDioxideGenerationRate, 3.6e-08)); + EXPECT_TRUE(_i_people->setString(PeopleFields::EnableASHRAE55ComfortWarnings, "Yes")); + EXPECT_TRUE(_i_people->setString(PeopleFields::MeanRadiantTemperatureCalculationType, "SurfaceWeighted")); + EXPECT_TRUE(_i_people->setString(PeopleFields::SurfaceName_AngleFactorListName, "")); + assignSchedule(PeopleFields::WorkEfficiencyScheduleName, "WorkEfficiencySchedule"); + EXPECT_TRUE(_i_people->setString(PeopleFields::ClothingInsulationCalculationMethod, "")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ClothingInsulationCalculationMethodScheduleName, "")); + assignSchedule(PeopleFields::ClothingInsulationScheduleName, "ClothingInsulationSchedule"); + assignSchedule(PeopleFields::AirVelocityScheduleName, "AirVelocitySchedule"); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel1Type, "Fanger")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel2Type, "Pierce")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel3Type, "")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel4Type, "")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel5Type, "")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel6Type, "")); + EXPECT_TRUE(_i_people->setString(PeopleFields::ThermalComfortModel7Type, "")); + assignSchedule(PeopleFields::AnkleLevelAirVelocityScheduleName, "AnkleLevelAirVelocitySchedule"); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::ColdStressTemperatureThreshold, 15.0)); + EXPECT_TRUE(_i_people->setDouble(PeopleFields::HeatStressTemperatureThreshold, 31.0)); + + Model m = rt.translateWorkspace(w); + + ASSERT_EQ(1, m.getConcreteModelObjects().size()); + ASSERT_EQ(1, m.getConcreteModelObjects().size()); + + auto p = m.getConcreteModelObjects()[0]; + + EXPECT_EQ("People 1", p.nameString()); + ASSERT_TRUE(p.space()); + ASSERT_TRUE(p.numberofPeopleSchedule()); + EXPECT_EQ("NumberofPeopleSchedule", p.numberofPeopleSchedule()->nameString()); + ASSERT_TRUE(p.activityLevelSchedule()); + EXPECT_EQ("ActivitySchedule", p.activityLevelSchedule()->nameString()); + ASSERT_TRUE(p.workEfficiencySchedule()); + EXPECT_EQ("WorkEfficiencySchedule", p.workEfficiencySchedule()->nameString()); + ASSERT_TRUE(p.clothingInsulationSchedule()); + EXPECT_EQ("ClothingInsulationSchedule", p.clothingInsulationSchedule()->nameString()); + ASSERT_TRUE(p.airVelocitySchedule()); + EXPECT_EQ("AirVelocitySchedule", p.airVelocitySchedule()->nameString()); + + // E+ object doesn't have one + EXPECT_EQ(1.0, p.multiplier()); + ASSERT_TRUE(p.ankleLevelAirVelocitySchedule()); + EXPECT_EQ("AnkleLevelAirVelocitySchedule", p.ankleLevelAirVelocitySchedule()->nameString()); + EXPECT_EQ(15.0, p.coldStressTemperatureThreshold()); + EXPECT_EQ(31.0, p.heatStressTemperatureThreshold()); + + auto pd = p.definition().cast(); + EXPECT_EQ("Area/Person", pd.numberofPeopleCalculationMethod()); + EXPECT_FALSE(pd.numberofPeople()); + EXPECT_FALSE(pd.peopleperSpaceFloorArea()); + ASSERT_TRUE(pd.spaceFloorAreaperPerson()); + EXPECT_EQ(10.0, pd.spaceFloorAreaperPerson().get()); + EXPECT_EQ(0.35, pd.fractionRadiant()); + ASSERT_TRUE(pd.sensibleHeatFraction()); + EXPECT_EQ(0.2, pd.sensibleHeatFraction().get()); + EXPECT_EQ(3.6e-08, pd.carbonDioxideGenerationRate()); + EXPECT_TRUE(pd.enableASHRAE55ComfortWarnings()); + EXPECT_EQ("SurfaceWeighted", pd.meanRadiantTemperatureCalculationType()); + + ASSERT_EQ(2, pd.numThermalComfortModelTypes()); + ASSERT_EQ(2, pd.numExtensibleGroups()); + EXPECT_EQ("Fanger", pd.getThermalComfortModelType(0).get()); + EXPECT_EQ("Pierce", pd.getThermalComfortModelType(1).get()); } diff --git a/src/epjson/test/epJSONFixture.cpp b/src/epjson/test/epJSONFixture.cpp index 548284a6e7..0d794c2eba 100644 --- a/src/epjson/test/epJSONFixture.cpp +++ b/src/epjson/test/epJSONFixture.cpp @@ -29,17 +29,21 @@ #include "epJSONFixture.hpp" -#include "../../model/Model.hpp" -#include "../../model/Component.hpp" -#include "../../model/Construction.hpp" -#include "../../model/Construction_Impl.hpp" +#include "../epJSONTranslator.hpp" -#include +#include "../../utilities/core/ApplicationPathHelpers.hpp" #include "../../utilities/core/Compare.hpp" +#include "../../utilities/core/Filesystem.hpp" +#include "../../utilities/core/PathHelpers.hpp" +#include "../../utilities/idf/IdfFile.hpp" #include #include +#include +#include +#include + using namespace openstudio; void epJSONFixture::SetUp() {} @@ -57,5 +61,158 @@ void epJSONFixture::TearDownTestSuite() { logFile->disable(); } +void epJSONFixture::makeDoubles(Json::Value& value) { + if (value.isNumeric()) { + value = value.asDouble(); + } else { + for (auto& child : value) { + makeDoubles(child); + } + } +} + +void epJSONFixture::compareJSONS(const Json::Value& lhs, const Json::Value& rhs) { + auto doubledLhs = lhs; + makeDoubles(doubledLhs); + + auto doubledRhs = rhs; + makeDoubles(doubledRhs); + + if (doubledLhs != doubledRhs) { + compareJSONS_detailed(doubledLhs, doubledRhs, "root"); + constexpr bool debug = false; + if constexpr (debug) { + { + std::ofstream file_id; + file_id.open("lhs.json"); + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; + std::unique_ptr writer(builder.newStreamWriter()); + std::ofstream outputFileStream("/tmp/lhs.json"); + writer->write(doubledLhs, &outputFileStream); + } + { + std::ofstream file_id; + file_id.open("lhs.json"); + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; + std::unique_ptr writer(builder.newStreamWriter()); + std::ofstream outputFileStream("/tmp/rhs.json"); + writer->write(doubledRhs, &outputFileStream); + } + } + } + ASSERT_TRUE(doubledLhs == doubledRhs); +} + +void epJSONFixture::compareJSONS_detailed(Json::Value& lhs, Json::Value& rhs, const std::string& currentPath) { + if (lhs.isNumeric()) { + ASSERT_TRUE(rhs.isNumeric()) << "At " << currentPath << ", lhs is a numeric while rhs is not"; + ASSERT_EQ(lhs.asDouble(), rhs.asDouble()) << "At " << currentPath; + } else if (lhs.isString()) { + ASSERT_EQ(lhs, rhs) << "At " << currentPath; + } else if (lhs.isArray()) { + ASSERT_TRUE(rhs.isArray()) << "At " << currentPath << ", lhs is an arrary while rhs is not"; + ASSERT_EQ(lhs.size(), rhs.size()) << "At " << currentPath << ", size of array object differs"; + if (lhs != rhs) { + for (int i = 0; i < static_cast(lhs.size()); ++i) { + compareJSONS_detailed(lhs[i], rhs[i], currentPath + "[" + std::to_string(i) + "]"); + } + ASSERT_TRUE(false) << "Terminating due to array mismatch."; + } + } else { + ASSERT_TRUE(lhs.isObject()) << "At " << currentPath << ", lhs is not an object? did I miss a type? [developer error]"; + ASSERT_TRUE(rhs.isObject()) << "At " << currentPath << ", lhs is an object but rhs is not."; + if (lhs != rhs) { + std::vector lhsMemberNames = lhs.getMemberNames(); + std::vector rhsMemberNames = rhs.getMemberNames(); + if (lhsMemberNames != rhsMemberNames) { + for (auto& s : lhsMemberNames) { + EXPECT_TRUE(rhs.isMember(s)) << "At " << currentPath << ", rhs is missing member named '" << s << "'"; + } + for (auto& s : rhsMemberNames) { + EXPECT_TRUE(lhs.isMember(s)) << "At " << currentPath << ", lhs is missing member named '" << s << "'"; + } + ASSERT_TRUE(false) << "At " << currentPath << ", Terminating due to member name mismatch."; + } + + for (auto& s : lhsMemberNames) { + compareJSONS_detailed(lhs[s], rhs[s], currentPath + ">" + s); + } + ASSERT_TRUE(false) << "At " << currentPath << ", Terminating due to object mismatch."; + } + } +} + +openstudio::path setupIdftoEPJSONTest(const openstudio::path& location) { + const auto basename = openstudio::toPath(openstudio::filesystem::basename(location)); + const auto working_directory = openstudio::filesystem::complete(openstudio::toPath("epjson_tests") / basename); + auto idf_path = working_directory / openstudio::toPath("eplus.idf"); + openstudio::filesystem::create_directories(working_directory); + openstudio::filesystem::copy_file(location, idf_path, openstudio::filesystem::copy_option::overwrite_if_exists); + return idf_path; +} + +Json::Value translateIdfToEPJSONWithEP(const openstudio::path& location) { + + // In case for some reason the energyplus CLI cannot do the conversion, we do not want to pick up an old artifact + const auto epJSONFile = openstudio::setFileExtension(location, "epJSON", true); + if (openstudio::filesystem::exists(epJSONFile)) { + openstudio::filesystem::remove(epJSONFile); + } + + std::string cmd = fmt::format("{} --output-directory {} --convert-only {}", openstudio::toString(openstudio::getEnergyPlusExecutable().native()), + openstudio::toString(location.parent_path().native()), openstudio::toString(location.native())); + + [[maybe_unused]] auto result = std::system(cmd.c_str()); + if (!openstudio::filesystem::exists(epJSONFile)) { + throw openstudio::Exception( + fmt::format("Error during the E+ CLI call, epJSON wasn't created at {}\ncmd = '{}'\n", openstudio::toString(epJSONFile), cmd)); + } + + auto root = openstudio::epJSON::loadJSON(epJSONFile); + + const auto outputLocation = location.parent_path() / openstudio::toPath("eplus-rewritten.epJSON"); + std::ofstream ofs(openstudio::toString(outputLocation), std::ofstream::trunc); + ofs << root.toStyledString() << '\n'; + + return root; +} + +openstudio::path epJSONFixture::completeIDFPath(const openstudio::path& idf) { + return openstudio::getEnergyPlusDirectory() / openstudio::toPath("ExampleFiles") / idf; +} + +Json::Value translateIdfToEPJSONWithOS(const openstudio::path& location) { + auto idf = openstudio::IdfFile::load(location); + + if (!idf) { + return Json::Value{}; + } + + auto result = openstudio::epJSON::toJSON(*idf); + + const auto outputLocation = location.parent_path() / openstudio::toPath("os.epJSON"); + std::ofstream ofs(openstudio::toString(outputLocation), std::ofstream::trunc); + ofs << result.toStyledString() << '\n'; + return result; +} + +std::pair epJSONFixture::doEPJSONTranslations(const std::string& idfname) { + + const auto setupIdf = setupIdftoEPJSONTest(epJSONFixture::completeIDFPath(openstudio::toString(idfname))); + return {translateIdfToEPJSONWithEP(setupIdf), translateIdfToEPJSONWithOS(setupIdf)}; +} + +void epJSONFixture::compareEPJSONTranslations(const std::string& idfname) { + const auto [epTranslation, osTranslation] = epJSONFixture::doEPJSONTranslations("WCE_DoubleClear_BSDF.idf"); + ASSERT_TRUE(epTranslation) << "E+ translation failed for " << idfname; + ASSERT_TRUE(osTranslation) << "OS translation failed for " << idfname; + + compareJSONS(epTranslation, osTranslation); +} + // static variables boost::optional epJSONFixture::logFile; diff --git a/src/epjson/test/epJSONFixture.hpp b/src/epjson/test/epJSONFixture.hpp index d297bbf69e..d5e55944ee 100644 --- a/src/epjson/test/epJSONFixture.hpp +++ b/src/epjson/test/epJSONFixture.hpp @@ -37,8 +37,17 @@ #include +namespace Json { +class Value; +} + class epJSONFixture : public ::testing::Test { + public: + void compareEPJSONTranslations(const std::string& idfname); + static openstudio::path completeIDFPath(const openstudio::path& idf); + static void makeDoubles(Json::Value& value); + protected: /// initialize for each test virtual void SetUp() override; @@ -56,6 +65,13 @@ class epJSONFixture : public ::testing::Test // static variables static boost::optional logFile; + + private: + void compareJSONS(const Json::Value& lhs, const Json::Value& rhs); + + void compareJSONS_detailed(Json::Value& lhs, Json::Value& rhs, const std::string& currentPath); + + static std::pair doEPJSONTranslations(const std::string& idfname); }; #endif // EPJSON_TEST_EPJSONFIXTURE_HPP diff --git a/src/epjson/test/epJSONTranslator_GTest.cpp b/src/epjson/test/epJSONTranslator_GTest.cpp index 03c2c145df..b780a63342 100644 --- a/src/epjson/test/epJSONTranslator_GTest.cpp +++ b/src/epjson/test/epJSONTranslator_GTest.cpp @@ -55,345 +55,129 @@ #include #include -openstudio::path setupIdftoEPJSONTest(const openstudio::path& location) { - const auto basename = openstudio::toPath(openstudio::filesystem::basename(location)); - const auto working_directory = openstudio::filesystem::complete(openstudio::toPath("epjson_tests") / basename); - const auto idf_path = working_directory / openstudio::toPath("eplus.idf"); - openstudio::filesystem::create_directories(working_directory); - openstudio::filesystem::copy_file(location, idf_path, openstudio::filesystem::copy_option::overwrite_if_exists); - return idf_path; -} - -Json::Value translateIdfToEPJSONWithEP(const openstudio::path& location) { - - // In case for some reason the energyplus CLI cannot do the conversion, we do not want to pick up an old artifact - const auto epJSONFile = openstudio::setFileExtension(location, "epJSON", true); - if (openstudio::filesystem::exists(epJSONFile)) { - openstudio::filesystem::remove(epJSONFile); - } - - std::string cmd = fmt::format("{} --output-directory {} --convert-only {}", openstudio::toString(openstudio::getEnergyPlusExecutable().native()), - openstudio::toString(location.parent_path().native()), openstudio::toString(location.native())); - - [[maybe_unused]] auto result = std::system(cmd.c_str()); - if (!openstudio::filesystem::exists(epJSONFile)) { - throw openstudio::Exception( - fmt::format("Error during the E+ CLI call, epJSON wasn't created at {}\ncmd = '{}'\n", openstudio::toString(epJSONFile), cmd)); - } - - const auto root = openstudio::epJSON::loadJSON(epJSONFile); - - const auto outputLocation = location.parent_path() / openstudio::toPath("eplus-rewritten.epJSON"); - std::ofstream ofs(openstudio::toString(outputLocation), std::ofstream::trunc); - ofs << root.toStyledString() << '\n'; - - return root; -} - -openstudio::path completeIDFPath(const openstudio::path& idf) { - return openstudio::getEnergyPlusDirectory() / openstudio::toPath("ExampleFiles") / idf; -} - -Json::Value translateIdfToEPJSONWithOS(const openstudio::path& location) { - auto idf = openstudio::IdfFile::load(location); - - if (!idf) { - return Json::Value{}; - } - - auto result = openstudio::epJSON::toJSON(*idf); - - const auto outputLocation = location.parent_path() / openstudio::toPath("os.epJSON"); - std::ofstream ofs(openstudio::toString(outputLocation), std::ofstream::trunc); - ofs << result.toStyledString() << '\n'; - return result; -} - -void makeDoubles(Json::Value& value) { - if (value.isNumeric()) { - value = value.asDouble(); - } else { - for (auto& child : value) { - makeDoubles(child); - } - } -} - -bool equal(const Json::Value& lhs, const Json::Value& rhs) { - auto doubledLhs = lhs; - makeDoubles(doubledLhs); - - auto doubledRhs = rhs; - makeDoubles(doubledRhs); - - return doubledLhs == doubledRhs; -} - -std::pair doEPJSONTranslations(const std::string& idfname) { - - const auto setupIdf = setupIdftoEPJSONTest(completeIDFPath(openstudio::toString(idfname))); - return {translateIdfToEPJSONWithEP(setupIdf), translateIdfToEPJSONWithOS(setupIdf)}; -} - TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgMediumOfficeNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgMediumOfficeNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgMediumOfficeNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_1ZoneEvapCooler) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("1ZoneEvapCooler.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("1ZoneEvapCooler.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_WCE_DoubleClear_BSDF) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("WCE_DoubleClear_BSDF.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("WCE_DoubleClear_BSDF.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_SupermarketSecondary) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("SupermarketSecondary.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("SupermarketSecondary.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_5ZoneWaterLoopHeatPump) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("5ZoneWaterLoopHeatPump.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("5ZoneWaterLoopHeatPump.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ZoneCoupledGroundHTSlabInGrade) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ZoneCoupledGroundHTSlabInGrade.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ZoneCoupledGroundHTSlabInGrade.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_CmplxGlz_MeasuredDeflectionAndShading) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("CmplxGlz_MeasuredDeflectionAndShading.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("CmplxGlz_MeasuredDeflectionAndShading.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_HospitalLowEnergy) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("HospitalLowEnergy.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("HospitalLowEnergy.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_HospitalBaselineReheatReportEMS) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("HospitalBaselineReheatReportEMS.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("HospitalBaselineReheatReportEMS.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_HospitalBaseline) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("HospitalBaseline.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("HospitalBaseline.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_CmplxGlz_Daylighting_SouthVB45deg) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("CmplxGlz_Daylighting_SouthVB45deg.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("CmplxGlz_Daylighting_SouthVB45deg.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_CmplxGlz_Daylighting_SouthVerticalVB45deg) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("CmplxGlz_Daylighting_SouthVerticalVB45deg.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("CmplxGlz_Daylighting_SouthVerticalVB45deg.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_CmplxGlz_InShadeGasMix) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("CmplxGlz_InShadeGasMix.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("CmplxGlz_InShadeGasMix.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_CmplxGlz_SchedSurfGains) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("CmplxGlz_SchedSurfGains.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("CmplxGlz_SchedSurfGains.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_ApartmentHighRise_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_ApartmentHighRise_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_ApartmentHighRise_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_OutPatientHealthCare_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_OutPatientHealthCare_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_OutPatientHealthCare_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgOutPatientNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgOutPatientNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgOutPatientNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_SchoolSecondary_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_SchoolSecondary_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_SchoolSecondary_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_Hospital_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_Hospital_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_Hospital_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_UnitaryHybridAC_DedicatedOutsideAir) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("UnitaryHybridAC_DedicatedOutsideAir.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("UnitaryHybridAC_DedicatedOutsideAir.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgSmallHotelNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgSmallHotelNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgSmallHotelNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgHospitalNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgHospitalNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgHospitalNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgSecondarySchoolNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgSecondarySchoolNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgSecondarySchoolNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_SchoolPrimary_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_SchoolPrimary_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_SchoolPrimary_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_OfficeLarge_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_OfficeLarge_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_OfficeLarge_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_HotelLarge_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_HotelLarge_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_HotelLarge_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_ApartmentMidRise_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_ApartmentMidRise_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_ApartmentMidRise_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgMidriseApartmentNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgMidriseApartmentNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgMidriseApartmentNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefrigeratedWarehouse) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefrigeratedWarehouse.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefrigeratedWarehouse.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_CmplxGlz_SmOff_IntExtShading) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("CmplxGlz_SmOff_IntExtShading.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("CmplxGlz_SmOff_IntExtShading.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgPrimarySchoolNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgPrimarySchoolNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgPrimarySchoolNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_ASHRAE901_RetailStripmall_STD2019_Denver) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("ASHRAE901_RetailStripmall_STD2019_Denver.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("ASHRAE901_RetailStripmall_STD2019_Denver.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_RefBldgLargeHotelNew2004_Chicago) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("RefBldgLargeHotelNew2004_Chicago.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("RefBldgLargeHotelNew2004_Chicago.idf"); } TEST_F(epJSONFixture, TranslateIDFToEPJSON_EMSDemandManager_LargeOffice) { - const auto [epTranslation, osTranslation] = doEPJSONTranslations("EMSDemandManager_LargeOffice.idf"); - ASSERT_TRUE(epTranslation); - ASSERT_TRUE(osTranslation); - - EXPECT_TRUE(equal(epTranslation, osTranslation)); + compareEPJSONTranslations("EMSDemandManager_LargeOffice.idf"); } TEST_F(epJSONFixture, toStringUilityWorksAsExpected) { - const auto location = completeIDFPath("RefBldgMediumOfficeNew2004_Chicago.idf"); + const auto location = epJSONFixture::completeIDFPath("RefBldgMediumOfficeNew2004_Chicago.idf"); auto idf = openstudio::IdfFile::load(location); ASSERT_TRUE(idf); diff --git a/src/model/People.cpp b/src/model/People.cpp index 30a3db842b..3042074a40 100644 --- a/src/model/People.cpp +++ b/src/model/People.cpp @@ -116,21 +116,25 @@ namespace model { std::vector People_Impl::getScheduleTypeKeys(const Schedule& schedule) const { std::vector result; UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); - UnsignedVector::const_iterator b(fieldIndices.begin()), e(fieldIndices.end()); + UnsignedVector::const_iterator b(fieldIndices.begin()); + UnsignedVector::const_iterator e(fieldIndices.end()); if (std::find(b, e, OS_PeopleFields::NumberofPeopleScheduleName) != e) { - result.push_back(ScheduleTypeKey("People", "Number of People")); + result.emplace_back("People", "Number of People"); } if (std::find(b, e, OS_PeopleFields::ActivityLevelScheduleName) != e) { - result.push_back(ScheduleTypeKey("People", "Activity Level")); + result.emplace_back("People", "Activity Level"); } if (std::find(b, e, OS_PeopleFields::WorkEfficiencyScheduleName) != e) { - result.push_back(ScheduleTypeKey("People", "Work Efficiency")); + result.emplace_back("People", "Work Efficiency"); } if (std::find(b, e, OS_PeopleFields::ClothingInsulationScheduleName) != e) { - result.push_back(ScheduleTypeKey("People", "Clothing Insulation")); + result.emplace_back("People", "Clothing Insulation"); } if (std::find(b, e, OS_PeopleFields::AirVelocityScheduleName) != e) { - result.push_back(ScheduleTypeKey("People", "Air Velocity")); + result.emplace_back("People", "Air Velocity"); + } + if (std::find(b, e, OS_PeopleFields::AnkleLevelAirVelocityScheduleName) != e) { + result.emplace_back("People", "Ankle Level Air Velocity"); } return result; } @@ -187,23 +191,30 @@ namespace model { result = false; } - // optional in EnergyPlus - schedule = this->workEfficiencySchedule(); - if (schedule) { - this->setWorkEfficiencySchedule(*schedule); - } + // None of these are part of the DefaultScheduleSet + // // optional in EnergyPlus + // schedule = this->workEfficiencySchedule(); + // if (schedule) { + // this->setWorkEfficiencySchedule(*schedule); + // } - // optional in EnergyPlus - schedule = this->clothingInsulationSchedule(); - if (schedule) { - this->setClothingInsulationSchedule(*schedule); - } + // // optional in EnergyPlus + // schedule = this->clothingInsulationSchedule(); + // if (schedule) { + // this->setClothingInsulationSchedule(*schedule); + // } - // optional in EnergyPlus - schedule = this->airVelocitySchedule(); - if (schedule) { - this->setAirVelocitySchedule(*schedule); - } + // // optional in EnergyPlus + // schedule = this->airVelocitySchedule(); + // if (schedule) { + // this->setAirVelocitySchedule(*schedule); + // } + + // // optional in EnergyPlus + // schedule = this->ankleLevelAirVelocitySchedule(); + // if (schedule) { + // this->setAnkleLevelAirVelocitySchedule(*schedule); + // } return result; } @@ -540,6 +551,83 @@ namespace model { return types; } + boost::optional People_Impl::ankleLevelAirVelocitySchedule() const { + return getObject().getModelObjectTarget(OS_PeopleFields::AnkleLevelAirVelocityScheduleName); + } + + bool People_Impl::setAnkleLevelAirVelocitySchedule(Schedule& schedule) { + return setSchedule(OS_PeopleFields::AnkleLevelAirVelocityScheduleName, "People", "Air Velocity", schedule); + } + + void People_Impl::resetAnkleLevelAirVelocitySchedule() { + bool result = setString(OS_PeopleFields::AnkleLevelAirVelocityScheduleName, ""); + OS_ASSERT(result); + } + + boost::optional People_Impl::ankleLevelAirVelocityScheduleAsModelObject() const { + OptionalModelObject result; + OptionalSchedule intermediate = ankleLevelAirVelocitySchedule(); + if (intermediate) { + result = *intermediate; + } + return result; + } + + bool People_Impl::setAnkleLevelAirVelocityScheduleAsModelObject(const boost::optional& modelObject) { + if (modelObject) { + OptionalSchedule intermediate = modelObject->optionalCast(); + if (intermediate) { + Schedule schedule(*intermediate); + return setAnkleLevelAirVelocitySchedule(schedule); + } else { + return false; + } + } else { + resetAnkleLevelAirVelocitySchedule(); + } + return true; + } + + double People_Impl::coldStressTemperatureThreshold() const { + boost::optional value = getDouble(OS_PeopleFields::ColdStressTemperatureThreshold, true); + OS_ASSERT(value); + return value.get(); + } + + bool People_Impl::isColdStressTemperatureThresholdDefaulted() const { + return isEmpty(OS_PeopleFields::ColdStressTemperatureThreshold); + } + + bool People_Impl::setColdStressTemperatureThreshold(double coldStressTemperatureThreshold) { + bool result = setDouble(OS_PeopleFields::ColdStressTemperatureThreshold, coldStressTemperatureThreshold); + return result; + } + + void People_Impl::resetColdStressTemperatureThreshold() { + bool result = setString(OS_PeopleFields::ColdStressTemperatureThreshold, ""); + OS_ASSERT(result); + } + + double People_Impl::heatStressTemperatureThreshold() const { + boost::optional value = getDouble(OS_PeopleFields::HeatStressTemperatureThreshold, true); + OS_ASSERT(value); + return value.get(); + } + + bool People_Impl::isHeatStressTemperatureThresholdDefaulted() const { + return isEmpty(OS_PeopleFields::HeatStressTemperatureThreshold); + } + + bool People_Impl::setHeatStressTemperatureThreshold(double heatStressTemperatureThreshold) { + bool result = setDouble(OS_PeopleFields::HeatStressTemperatureThreshold, heatStressTemperatureThreshold); + return result; + } + + void People_Impl::resetHeatStressTemperatureThreshold() { + bool result = setString(OS_PeopleFields::HeatStressTemperatureThreshold, ""); + OS_ASSERT(result); + } + } // namespace detail People::People(const PeopleDefinition& peopleDefinition) : SpaceLoadInstance(People::iddObjectType(), peopleDefinition) { @@ -662,6 +750,50 @@ namespace model { return getImpl()->getFloorAreaPerPerson(floorArea); } + boost::optional People::ankleLevelAirVelocitySchedule() const { + return getImpl()->ankleLevelAirVelocitySchedule(); + } + + bool People::setAnkleLevelAirVelocitySchedule(Schedule& schedule) { + return getImpl()->setAnkleLevelAirVelocitySchedule(schedule); + } + + void People::resetAnkleLevelAirVelocitySchedule() { + getImpl()->resetAnkleLevelAirVelocitySchedule(); + } + + double People::coldStressTemperatureThreshold() const { + return getImpl()->coldStressTemperatureThreshold(); + } + + bool People::isColdStressTemperatureThresholdDefaulted() const { + return getImpl()->isColdStressTemperatureThresholdDefaulted(); + } + + bool People::setColdStressTemperatureThreshold(double coldStressTemperatureThreshold) { + return getImpl()->setColdStressTemperatureThreshold(coldStressTemperatureThreshold); + } + + void People::resetColdStressTemperatureThreshold() { + getImpl()->resetColdStressTemperatureThreshold(); + } + + double People::heatStressTemperatureThreshold() const { + return getImpl()->heatStressTemperatureThreshold(); + } + + bool People::isHeatStressTemperatureThresholdDefaulted() const { + return getImpl()->isHeatStressTemperatureThresholdDefaulted(); + } + + bool People::setHeatStressTemperatureThreshold(double heatStressTemperatureThreshold) { + return getImpl()->setHeatStressTemperatureThreshold(heatStressTemperatureThreshold); + } + + void People::resetHeatStressTemperatureThreshold() { + getImpl()->resetHeatStressTemperatureThreshold(); + } + /// @cond People::People(std::shared_ptr impl) : SpaceLoadInstance(std::move(impl)) {} /// @endcond diff --git a/src/model/People.hpp b/src/model/People.hpp index b023104f75..49e3ed8129 100644 --- a/src/model/People.hpp +++ b/src/model/People.hpp @@ -91,6 +91,15 @@ namespace model { /** Returns the air velocity schedule. */ boost::optional airVelocitySchedule() const; + /** Returns the ankle-level air velocity schedule. */ + boost::optional ankleLevelAirVelocitySchedule() const; + + double coldStressTemperatureThreshold() const; + bool isColdStressTemperatureThresholdDefaulted() const; + + double heatStressTemperatureThreshold() const; + bool isHeatStressTemperatureThresholdDefaulted() const; + //@} /** @name Setters */ //@{ @@ -142,6 +151,15 @@ namespace model { void resetMultiplier(); + bool setAnkleLevelAirVelocitySchedule(Schedule& schedule); + void resetAnkleLevelAirVelocitySchedule(); + + bool setColdStressTemperatureThreshold(double coldStressTemperatureThreshold); + void resetColdStressTemperatureThreshold(); + + bool setHeatStressTemperatureThreshold(double heatStressTemperatureThreshold); + void resetHeatStressTemperatureThreshold(); + //@} /** @name Other */ //@{ @@ -170,7 +188,7 @@ namespace model { //@} protected: /// @cond - typedef detail::People_Impl ImplType; + using ImplType = detail::People_Impl; friend class Model; friend class openstudio::IdfObject; @@ -183,10 +201,10 @@ namespace model { }; /** \relates People*/ - typedef boost::optional OptionalPeople; + using OptionalPeople = boost::optional; /** \relates People*/ - typedef std::vector PeopleVector; + using PeopleVector = std::vector; } // namespace model } // namespace openstudio diff --git a/src/model/People_Impl.hpp b/src/model/People_Impl.hpp index 4b5af81d1e..41a1e07e5e 100644 --- a/src/model/People_Impl.hpp +++ b/src/model/People_Impl.hpp @@ -109,6 +109,14 @@ namespace model { /** Returns the air velocity schedule. */ boost::optional airVelocitySchedule() const; + boost::optional ankleLevelAirVelocitySchedule() const; + + double coldStressTemperatureThreshold() const; + bool isColdStressTemperatureThresholdDefaulted() const; + + double heatStressTemperatureThreshold() const; + bool isHeatStressTemperatureThresholdDefaulted() const; + //@} /** @name Setters */ //@{ @@ -155,6 +163,15 @@ namespace model { void resetMultiplier(); + bool setAnkleLevelAirVelocitySchedule(Schedule& schedule); + void resetAnkleLevelAirVelocitySchedule(); + + bool setColdStressTemperatureThreshold(double coldStressTemperatureThreshold); + void resetColdStressTemperatureThreshold(); + + bool setHeatStressTemperatureThreshold(double heatStressTemperatureThreshold); + void resetHeatStressTemperatureThreshold(); + //@} /** @name Other */ //@{ @@ -195,6 +212,7 @@ namespace model { boost::optional workEfficiencyScheduleAsModelObject() const; boost::optional clothingInsulationScheduleAsModelObject() const; boost::optional airVelocityScheduleAsModelObject() const; + boost::optional ankleLevelAirVelocityScheduleAsModelObject() const; bool setPeopleDefinitionAsModelObject(const boost::optional& modelObject); bool setNumberofPeopleScheduleAsModelObject(const boost::optional& modelObject); @@ -202,6 +220,7 @@ namespace model { bool setWorkEfficiencyScheduleAsModelObject(const boost::optional& modelObject); bool setClothingInsulationScheduleAsModelObject(const boost::optional& modelObject); bool setAirVelocityScheduleAsModelObject(const boost::optional& modelObject); + bool setAnkleLevelAirVelocityScheduleAsModelObject(const boost::optional& modelObject); }; } // namespace detail diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp index 062633e04f..1859c0843d 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -327,6 +327,7 @@ namespace model { {"People", "Work Efficiency", "workEfficiencySchedule", true, "", 0.0, 1.0}, {"People", "Clothing Insulation", "clothingInsulationSchedule", true, "ClothingInsulation", 0.0, OptionalDouble()}, {"People", "Air Velocity", "airVelocitySchedule", true, "Velocity", 0.0, OptionalDouble()}, + {"People", "Ankle Level Air Velocity", "ankleLevelAirVelocitySchedule", true, "Velocity", 0.0, OptionalDouble()}, {"PhotovoltaicPerformanceSimple", "Efficiency", "efficiencySchedule", true, "", 0.0, 1.0}, {"PlantComponentTemperatureSource", "Source Temperature", "sourceTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()}, {"PipeIndoor", "Ambient Temperature", "ambientTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()},