Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
848afbb
Add ComfortViewFactorAngles model schema
joseph-robertson Sep 8, 2026
6c9beaa
Add ComfortViewFactorAngles model object
joseph-robertson Sep 8, 2026
2fe1fb5
Translate ComfortViewFactorAngles for People
joseph-robertson Sep 8, 2026
f674777
Import ComfortViewFactorAngles from EnergyPlus
joseph-robertson Sep 8, 2026
54530d7
Translate People MRT target schema
joseph-robertson Sep 8, 2026
1c97302
Test ComfortViewFactorAngles translation
joseph-robertson Sep 8, 2026
eb7fe45
Register ComfortViewFactorAngles translation
joseph-robertson Sep 8, 2026
96adc29
Clean up angle factor name.
joseph-robertson Sep 8, 2026
d536120
Update ComfortViewFactorAngles schema
joseph-robertson Sep 8, 2026
75bd4dd
Refine ComfortViewFactorAngles model API
joseph-robertson Sep 8, 2026
cef6abb
Refine ComfortViewFactorAngles translation
joseph-robertson Sep 8, 2026
9a9d173
Fix ComfortViewFactorAngles model integration
joseph-robertson Sep 8, 2026
25b1e1a
Add direct dependencies to ComfortViewFactorAngles translator tests
joseph-robertson Sep 8, 2026
85e090a
Correct ComfortViewFactorAngles translation test paths and People fie…
joseph-robertson Sep 8, 2026
b0f4b5c
Update ComfortViewFactorAngles EnergyPlus IDD metadata
joseph-robertson Sep 9, 2026
2a92c96
Require ComfortViewFactorAngles surfaces in one thermal zone
joseph-robertson Sep 9, 2026
25785c8
Reject cross-zone ComfortViewFactorAngles during translation
joseph-robertson Sep 9, 2026
b857c7b
Harden ComfortViewFactorAngles and MRT target validation
joseph-robertson Sep 9, 2026
24d1ad7
Migrate legacy People MRT targets to definitions
joseph-robertson Sep 9, 2026
f940f2e
Support all heat-transfer surfaces in ComfortViewFactorAngles
joseph-robertson Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions resources/energyplus/ProposedEnergy+.idd
Original file line number Diff line number Diff line change
Expand Up @@ -12158,16 +12158,16 @@ People:Definition,
\note optional (seventh thermal comfort model and report type)

ComfortViewFactorAngles,
\min-fields 3
\memo Used to specify radiant view factors for thermal comfort calculations.
\memo Note that the following angle factor fractions must sum up to 1.0
\memo The number of surfaces can be expanded beyond 100, if necessary, by adding more
\memo groups to the end of the list.
\extensible:2
\extensible:2 - repeat last two fields, remembering to remove ; from "inner" fields.
\memo All surfaces must be in the same enclosure.
A1 , \field Name
\required-field
\type alpha
\reference AllHeatTranAngFacNames
\object-list ZoneNames
A3 , \field Surface 1 Name
A2 , \field Surface 1 Name
\type object-list
\object-list AllHeatTranSurfNames
\begin-extensible
Expand Down
44 changes: 32 additions & 12 deletions resources/model/OpenStudio.idd
Original file line number Diff line number Diff line change
Expand Up @@ -3895,7 +3895,7 @@ OS:InternalMass:Definition,
OS:People:Definition,
\extensible:1
\min-fields 1
\max-fields 16
\max-fields 17
A1, \field Handle
\type handle
\required-field
Expand Down Expand Up @@ -3960,7 +3960,10 @@ OS:People:Definition,
\key EnclosureAveraged
\key SurfaceWeighted
\key AngleFactor
A6; \field Thermal Comfort Model Type
A6, \field Surface Name/Angle Factor List Name
\type object-list
\object-list AllHeatTranAngFacNames
A7; \field Thermal Comfort Model Type
\note optional (only needed for people thermal comfort results reporting)
\type choice
\begin-extensible
Expand All @@ -3972,6 +3975,27 @@ OS:People:Definition,
\key CoolingEffectASH55
\key AnkleDraftASH55

OS:ComfortViewFactorAngles,
\min-fields 4
\extensible:2
A1, \field Handle
\type handle
\required-field
A2, \field Name
\type alpha
\required-field
\reference AllHeatTranAngFacNames
A3, \field Surface 1 Name
\type object-list
\object-list AllHeatTranSurfNames
\begin-extensible
\required-field
N1; \field Angle Factor 1
\type real
\minimum 0.0
\maximum 1.0
\required-field

OS:Lights:Definition,
\min-fields 1
A1, \field Handle
Expand Down Expand Up @@ -7079,31 +7103,27 @@ OS:People,
\note units in schedule should be fraction applied to number of people (0.0 - 1.0)
\type object-list
\object-list ScheduleNames
A7, \field Surface Name/Angle Factor List Name
\note optional (only required for thermal comfort runs)
\type object-list
\object-list AllHeatTranAngFacNames
A8, \field Work Efficiency Schedule Name
A7, \field Work Efficiency Schedule Name
\note units in schedule are 0.0 to 1.0
\note optional (only required for thermal comfort runs)
\type object-list
\object-list ScheduleNames
A9, \field Clothing Insulation Calculation Method
A8, \field Clothing Insulation Calculation Method
\type choice
\key ClothingInsulationSchedule
\key DynamicClothingModelASHRAE55
\key CalculationMethodSchedule
\required-field
A10, \field Clothing Insulation Calculation Method Schedule Name
A9, \field Clothing Insulation Calculation Method Schedule Name
\type object-list
\object-list ScheduleNames
\note a schedule value of 1 for the Scheduled method, and 2 for the DynamicClothingModelASHRAE55 method
A11, \field Clothing Insulation Schedule Name
A10, \field Clothing Insulation Schedule Name
\note use "Clo" from ASHRAE or Thermal Comfort guides
\note optional (only required for thermal comfort runs)
\type object-list
\object-list ScheduleNames
A12, \field Air Velocity Schedule Name
A11, \field Air Velocity Schedule Name
\note units in the schedule are m/s
\note optional (only required for thermal comfort runs)
\type object-list
Expand All @@ -7112,7 +7132,7 @@ OS:People,
\type real
\minimum 0.0
\default 1.0
A13, \field Ankle Level Air Velocity Schedule Name
A12, \field Ankle Level Air Velocity Schedule Name
\type object-list
\object-list ScheduleNames
\note units in the schedule are m/s
Expand Down
3 changes: 3 additions & 0 deletions src/energyplus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ set(${target_name}_src
ForwardTranslator/ForwardTranslateZoneHVACUnitVentilator.cpp
ForwardTranslator/ForwardTranslateZoneHVACWaterToAirHeatPump.cpp
ForwardTranslator/ForwardTranslateZoneMixing.cpp
ForwardTranslator/ForwardTranslateComfortViewFactorAngles.cpp
ForwardTranslator/ForwardTranslateZoneMRTCalculation.cpp
ForwardTranslator/ForwardTranslateZonePropertyUserViewFactorsBySurfaceName.cpp
ForwardTranslator/ForwardTranslateZoneVentilationDesignFlowRate.cpp
Expand Down Expand Up @@ -651,6 +652,7 @@ set(${target_name}_src
ReverseTranslator/ReverseTranslateZoneInfiltrationFlowCoefficient.cpp
ReverseTranslator/ReverseTranslateZoneList.cpp
ReverseTranslator/ReverseTranslateZoneMixing.cpp
ReverseTranslator/ReverseTranslateComfortViewFactorAngles.cpp
ReverseTranslator/ReverseTranslateZoneMRTCalculation.cpp
ReverseTranslator/ReverseTranslateZonePropertyUserViewFactorsBySurfaceName.cpp
ReverseTranslator/ReverseTranslateZoneVentilationDesignFlowRate.cpp
Expand Down Expand Up @@ -737,6 +739,7 @@ set(${target_name}_test_src
Test/CoilWaterHeatingAirToWaterHeatPumpWrapped_GTest.cpp
Test/CoilWaterHeatingAirToWaterHeatPumpVariableSpeed_GTest.cpp
Test/CoilWaterHeatingDesuperheater_GTest.cpp
Test/ComfortViewFactorAngles_GTest.cpp
Test/Construction_GTest.cpp
Test/ConstructionWithInternalSource_GTest.cpp
Test/ControllerOutdoorAir_GTest.cpp
Expand Down
5 changes: 5 additions & 0 deletions src/energyplus/ForwardTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3348,6 +3348,11 @@ namespace energyplus {
retVal = translateZoneMixing(mo);
break;
}
case openstudio::IddObjectType::OS_ComfortViewFactorAngles: {
auto mo = modelObject.cast<ComfortViewFactorAngles>();
retVal = translateComfortViewFactorAngles(mo);
break;
}
case openstudio::IddObjectType::OS_ZoneMRTCalculation: {
auto mo = modelObject.cast<ZoneMRTCalculation>();
retVal = translateZoneMRTCalculation(mo);
Expand Down
3 changes: 3 additions & 0 deletions src/energyplus/ForwardTranslator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ namespace model {
class ZoneHVACWaterToAirHeatPump;
class ZoneMixing;
class ZoneMRTCalculation;
class ComfortViewFactorAngles;
class ZonePropertyUserViewFactorsBySurfaceName;
class ZoneVentilationDesignFlowRate;
class ZoneVentilationWindandStackOpenArea;
Expand Down Expand Up @@ -1619,6 +1620,8 @@ namespace energyplus {

boost::optional<IdfObject> translateZoneMRTCalculation(model::ZoneMRTCalculation& modelObject);

boost::optional<IdfObject> translateComfortViewFactorAngles(model::ComfortViewFactorAngles& modelObject);

boost::optional<IdfObject> translateZonePropertyUserViewFactorsBySurfaceName(model::ZonePropertyUserViewFactorsBySurfaceName& modelObject);

boost::optional<IdfObject> createAirLoopHVACSupplyPath(const model::Node& demandInletNode);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/***********************************************************************************************************************
* OpenStudio(R), Copyright (c) Alliance for Energy Innovation, LLC.
* See also https://openstudio.net/license
***********************************************************************************************************************/

#include "../ForwardTranslator.hpp"

#include "../../model/Model.hpp"
#include "../../model/Surface.hpp"
#include "../../model/Surface_Impl.hpp"
#include "../../model/InternalMass.hpp"
#include "../../model/InternalMass_Impl.hpp"
#include "../../model/PlanarSurface.hpp"
#include "../../model/Space.hpp"
#include "../../model/ThermalZone.hpp"
#include "../../model/ComfortViewFactorAngles.hpp"
#include "../../model/ComfortViewFactorAngles_Impl.hpp"

#include "../../utilities/idf/IdfExtensibleGroup.hpp"
#include "../../utilities/math/FloatCompare.hpp"

#include <utilities/idd/IddEnums.hxx>
#include <utilities/idd/ComfortViewFactorAngles_FieldEnums.hxx>

using namespace openstudio::model;

namespace openstudio {
namespace energyplus {

boost::optional<IdfObject> ForwardTranslator::translateComfortViewFactorAngles(ComfortViewFactorAngles& modelObject) {
std::vector<AngleFactor> angleFactors = modelObject.angleFactors();
if (angleFactors.empty()) {
LOG(Warn, modelObject.briefDescription() << " does not have any angle factors and will not be translated.");
return boost::none;
}

std::vector<std::pair<std::string, double>> translatedAngleFactors;
double sum = 0.0;
constexpr double tolerance = 0.000001;
boost::optional<ThermalZone> thermalZone;

for (const AngleFactor& angleFactor : angleFactors) {
ModelObject surface = angleFactor.surface();
boost::optional<Space> space;
if (auto planarSurface = surface.optionalCast<PlanarSurface>()) {
space = planarSurface->space();
} else if (auto internalMass = surface.optionalCast<InternalMass>()) {
space = internalMass->space();
}
const auto surfaceThermalZone = space ? space->thermalZone() : boost::none;
if (!surfaceThermalZone || (thermalZone && (surfaceThermalZone->handle() != thermalZone->handle()))) {
LOG(Error, modelObject.briefDescription()
<< " has surfaces that are not all assigned to the same ThermalZone and will not be translated.");
return boost::none;
}
thermalZone = surfaceThermalZone;
if (auto idfSurface = translateAndMapModelObject(surface)) {
const double value = angleFactor.angleFactor();
sum += value;
translatedAngleFactors.emplace_back(idfSurface->nameString(), value);
} else {
LOG(Error, "Could not translate an AngleFactor group for " << modelObject.briefDescription() << ". Continuing with the rest.");
}
}

if (translatedAngleFactors.empty()) {
LOG(Error, modelObject.briefDescription() << " does not have any valid angle factors and will not be translated.");
return boost::none;
}

if (!equal(sum, 1.0, tolerance)) {
LOG(Error, modelObject.briefDescription() << " has valid angle factors that sum to " << sum << " instead of 1.0 and will not be translated.");
return boost::none;
}

IdfObject idfObject = createRegisterAndNameIdfObject(IddObjectType::ComfortViewFactorAngles, modelObject);
for (const auto& [surfaceName, angleFactor] : translatedAngleFactors) {
auto group = idfObject.pushExtensibleGroup();
group.setString(ComfortViewFactorAnglesExtensibleFields::SurfaceName, surfaceName);
group.setDouble(ComfortViewFactorAnglesExtensibleFields::AngleFactor, angleFactor);
}
return idfObject;
}

} // namespace energyplus
} // namespace openstudio
32 changes: 29 additions & 3 deletions src/energyplus/ForwardTranslator/ForwardTranslatePeople.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../../model/People_Impl.hpp"
#include "../../model/PeopleDefinition.hpp"
#include "../../model/PeopleDefinition_Impl.hpp"
#include "../../model/ComfortViewFactorAngles.hpp"
#include "../../model/Space.hpp"
#include "../../model/Space_Impl.hpp"
#include "../../model/SpaceType.hpp"
Expand All @@ -18,6 +19,10 @@
#include "../../model/ThermalZone_Impl.hpp"
#include "../../model/Schedule.hpp"
#include "../../model/Schedule_Impl.hpp"
#include "../../model/Surface.hpp"
#include "../../model/Surface_Impl.hpp"
#include "../../model/ComfortViewFactorAngles.hpp"
#include "../../model/ComfortViewFactorAngles_Impl.hpp"
#include "../../model/LifeCycleCost.hpp"

#include "../../utilities/idf/IdfExtensibleGroup.hpp"
Expand Down Expand Up @@ -102,11 +107,32 @@ namespace energyplus {
}
}

if (!definition.isMeanRadiantTemperatureCalculationTypeDefaulted()) {
idfObject.setString(PeopleFields::MeanRadiantTemperatureCalculationType, definition.meanRadiantTemperatureCalculationType());
std::string mrtType = definition.meanRadiantTemperatureCalculationType();
if (auto target = definition.surfaceNameAngleFactorListName()) {
if (target->optionalCast<Surface>()) {
if (auto idfTarget = translateAndMapModelObject(*target)) {
mrtType = "SurfaceWeighted";
idfObject.setString(PeopleFields::SurfaceName_AngleFactorListName, idfTarget->nameString());
} else {
mrtType = "EnclosureAveraged";
}
} else if (target->optionalCast<ComfortViewFactorAngles>()) {
if (auto idfTarget = translateAndMapModelObject(*target)) {
mrtType = "AngleFactor";
idfObject.setString(PeopleFields::SurfaceName_AngleFactorListName, idfTarget->nameString());
} else {
mrtType = "EnclosureAveraged";
}
} else {
mrtType = "EnclosureAveraged";
}
} else if (!istringEqual(mrtType, "EnclosureAveraged")) {
mrtType = "EnclosureAveraged";
}

// TODO: Surface Name/Angle Factor List Name
if (!definition.isMeanRadiantTemperatureCalculationTypeDefaulted() || !istringEqual(mrtType, "EnclosureAveraged")) {
idfObject.setString(PeopleFields::MeanRadiantTemperatureCalculationType, mrtType);
}

if (boost::optional<Schedule> schedule_ = modelObject.workEfficiencySchedule()) {
if (auto idf_schedule_ = translateAndMapModelObject(schedule_.get())) {
Expand Down
4 changes: 4 additions & 0 deletions src/energyplus/ReverseTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,10 @@ namespace energyplus {
modelObject = translateZoneMixing(workspaceObject);
break;
}
case openstudio::IddObjectType::ComfortViewFactorAngles: {
modelObject = translateComfortViewFactorAngles(workspaceObject);
break;
}
case openstudio::IddObjectType::ZoneMRTCalculation: {
modelObject = translateZoneMRTCalculation(workspaceObject);
break;
Expand Down
2 changes: 2 additions & 0 deletions src/energyplus/ReverseTranslator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ namespace energyplus {

boost::optional<model::ModelObject> translateZoneMixing(const WorkspaceObject& workspaceObject);

boost::optional<model::ModelObject> translateComfortViewFactorAngles(const WorkspaceObject& workspaceObject);

boost::optional<model::ModelObject> translateZoneMRTCalculation(const WorkspaceObject& workspaceObject);

boost::optional<model::ModelObject> translateZonePropertyUserViewFactorsBySurfaceName(const WorkspaceObject& workspaceObject);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/***********************************************************************************************************************
* OpenStudio(R), Copyright (c) Alliance for Energy Innovation, LLC.
* See also https://openstudio.net/license
***********************************************************************************************************************/

#include "../ReverseTranslator.hpp"

#include "../../model/ComfortViewFactorAngles.hpp"
#include "../../model/ComfortViewFactorAngles_Impl.hpp"
#include "../../model/Surface.hpp"
#include "../../model/Surface_Impl.hpp"

#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp"

#include <utilities/idd/ComfortViewFactorAngles_FieldEnums.hxx>
#include "../../utilities/idd/IddEnums.hpp"
#include <utilities/idd/IddEnums.hxx>

#include "../../utilities/core/Assert.hpp"

using namespace openstudio::model;

namespace openstudio {
namespace energyplus {

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

ComfortViewFactorAngles comfortViewFactorAngles(m_model);
if (auto name = workspaceObject.name()) {
comfortViewFactorAngles.setName(*name);
}

for (const auto& idfGroup : workspaceObject.extensibleGroups()) {
auto group = idfGroup.cast<WorkspaceExtensibleGroup>();
auto surfaceTarget = group.getTarget(ComfortViewFactorAnglesExtensibleFields::SurfaceName);
auto angleFactor = group.getDouble(ComfortViewFactorAnglesExtensibleFields::AngleFactor);
if (!surfaceTarget || !angleFactor) {
continue;
}
if (auto modelObject = translateAndMapWorkspaceObject(*surfaceTarget)) {
comfortViewFactorAngles.addAngleFactor(*modelObject, *angleFactor);
}
}

return comfortViewFactorAngles;
}

} // namespace energyplus
} // namespace openstudio
Loading
Loading