Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 23 additions & 17 deletions resources/model/OpenStudio.idd
Original file line number Diff line number Diff line change
Expand Up @@ -28483,8 +28483,8 @@ OS:ZoneHVAC:CoolingPanel:RadiantConvective:Water,
\maximum 1

OS:ZoneMixing,
\memo ZoneMixing is a simple air exchange from one zone to another. Note that this statement
\memo only affects the energy balance of the "receiving" zone and will not produce
\memo ZoneMixing is a simple air exchange from one zone or space to another. Note that this statement
\memo only affects the energy balance of the "receiving" zone or space and will not produce
\memo any effect on the "source" zone. Mixing statements can be complementary and include
\memo multiple zones, but the balancing of flows between zones is left to the user's
\memo discretion.
Expand All @@ -28495,11 +28495,12 @@ OS:ZoneMixing,
A2 , \field Name
\required-field
\type alpha
A3 , \field Zone Name
\note This is the receiving zone
A3 , \field Zone or Space Name
\note This is the receiving zone or space
\required-field
\type object-list
\object-list ThermalZoneNames
\object-list SpaceNames
A4 , \field Schedule Name
\required-field
\type object-list
Expand All @@ -28522,7 +28523,7 @@ OS:ZoneMixing,
\units m3/s
\type real
\minimum 0
N2 , \field Flow Rate per Zone Floor Area
N2 , \field Flow Rate per Floor Area
\type real
\minimum 0
\units m3/s-m2
Expand All @@ -28534,38 +28535,43 @@ OS:ZoneMixing,
\units 1/hr
\type real
\minimum 0
A6 , \field Source Zone Name
A6 , \field Source Zone or Space Name
\type object-list
\object-list ThermalZoneNames
\object-list SpaceNames
N5 , \field Delta Temperature
\units deltaC
\type real
\note This field contains the constant temperature differential between source and
\note receiving zones below which mixing is shutoff.
\note receiving zone or space below which mixing is shutoff. If a source zone is
\note specified and it contains more than one space, the average source zone temperature
\note will be used for control.
A7 , \field Delta Temperature Schedule Name
\type object-list
\object-list ScheduleNames
\note This schedule contains the temperature differential between source and receiving
\note zones versus time below which mixing is shutoff.
A8 , \field Minimum Zone Temperature Schedule Name
\note This schedule contains the temperature differential between source and
\note receiving zone or space below which mixing is shutoff. If a source zone is
\note specified and it contains more than one space, the average source zone temperature
\note will be used for control.
A8 , \field Minimum Receiving Temperature Schedule Name
\type object-list
\object-list ScheduleNames
\note This schedule contains the zone dry-bulb temperature versus time below which
\note This schedule contains the receiving zone or space temperature versus time below which
\note mixing is shutoff.
A9 , \field Maximum Zone Temperature Schedule Name
A9 , \field Maximum Receiving Temperature Schedule Name
\type object-list
\object-list ScheduleNames
\note This schedule contains the zone dry-bulb temperature versus time above which
\note This schedule contains the receiving zone or space temperature versus time above which
\note mixing is shutoff.
A10 , \field Minimum Source Zone Temperature Schedule Name
A10 , \field Minimum Source Temperature Schedule Name
\type object-list
\object-list ScheduleNames
\note This schedule contains the source zone dry-bulb temperature versus time below
\note This schedule contains the source zone or space temperature versus time below
\note which mixing is shutoff.
A11, \field Maximum Source Zone Temperature Schedule Name
A11, \field Maximum Source Temperature Schedule Name
\type object-list
\object-list ScheduleNames
\note This schedule contains the source zone dry-bulb temperature versus time above
\note This schedule contains the source zone or space temperature versus time above
\note which mixing is shutoff.
A12, \field Minimum Outdoor Temperature Schedule Name
\type object-list
Expand Down
1 change: 1 addition & 0 deletions src/energyplus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,7 @@ set(${target_name}_test_src
Test/ZoneHVACTerminalUnitVariableRefrigerantFlow_GTest.cpp
Test/ZoneHVACUnitHeater_GTest.cpp
Test/ZoneHVACWaterToAirHeatPump_GTest.cpp
Test/ZoneMixing_GTest.cpp
Test/ZonePropertyUserViewFactorsBySurfaceName_GTest.cpp
Test/ZoneVentilationWindandStackOpenArea_GTest.cpp
)
Expand Down
84 changes: 63 additions & 21 deletions src/energyplus/ForwardTranslator/ForwardTranslateZoneMixing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include "../../model/ZoneMixing_Impl.hpp"
#include "../../model/ThermalZone.hpp"
#include "../../model/ThermalZone_Impl.hpp"
#include "../../model/Space.hpp"
#include "../../model/Space_Impl.hpp"

#include <utilities/idd/ZoneMixing_FieldEnums.hxx>
#include "../../utilities/idd/IddEnums.hpp"
Expand All @@ -50,21 +52,64 @@ namespace openstudio {
namespace energyplus {

boost::optional<IdfObject> ForwardTranslator::translateZoneMixing(ZoneMixing& modelObject) {

ModelObject zoneOrSpace = modelObject.zoneOrSpace();
boost::optional<ModelObject> sourceZoneOrSpace = modelObject.sourceZoneOrSpace();

auto getParentObjectName = [this](const ModelObject& mo) {
if (!m_excludeSpaceTranslation) {
return mo.nameString();
}

if (auto space_ = mo.optionalCast<Space>()) {
if (auto thermalZone_ = space_->thermalZone()) {
return thermalZone_->nameString();
} else {
OS_ASSERT(false); // This shouldn't happen, since we removed all orphaned spaces earlier in the FT
}
}

return mo.nameString();
};

if (!sourceZoneOrSpace) {
if (m_excludeSpaceTranslation && modelObject.space()) {
LOG(Warn, modelObject.briefDescription()
<< " doesn't have a Source Zone or Space, it will not be translated. As you were using Space-Level ZoneMixing, and you are not "
"translating to Spaces, it's possible it was pointing to two spaces inside the same zone");
} else {
LOG(Warn, modelObject.briefDescription() << " doesn't have a Source Zone or Space, it will not be translated.");
}
return boost::none;
}

if (zoneOrSpace == sourceZoneOrSpace.get()) {
// This isn't going to happen, because zm.setSourceSpace(newSpace) in ThermalZone::combineSpaces will be rejected
// Let's play it safe though
LOG(Warn, modelObject.briefDescription() << " has the same Receiving and Source Zone or Space, it will not be translated.");
if (!m_excludeSpaceTranslation) {
// We don't allow this at model time, the only reason we expect this to happen is when m_excludeSpaceTranslation is true, we call
// combineSpaces, and if the user has a ZoneMixing pointing to two spaces from the same ThermalZone, you end up with matching Receiving and
// Source Spaces
OS_ASSERT(false);
}
return boost::none;
}
Comment on lines +56 to +97
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ft adjust + warnings in case the user had a ZoneMixing pointing to two spaces inside the same zone AND opting out of space translation. Yep, that's the state of all the corner cases we have to test for nowadays...


// Makes sure the modelObject gets put in the map, and that the new idfObject gets put in
// the final file. Also set's the idfObject's name.
IdfObject idfObject = createRegisterAndNameIdfObject(IddObjectType::ZoneMixing, modelObject);

boost::optional<double> value;

// ZoneorSpaceName
ThermalZone zone = modelObject.zone();
translateAndMapModelObject(zone);
idfObject.setString(ZoneMixingFields::ZoneorSpaceName, zone.name().get());
translateAndMapModelObject(zoneOrSpace);
idfObject.setString(ZoneMixingFields::ZoneorSpaceName, getParentObjectName(zoneOrSpace));

// ScheduleName
Schedule schedule = modelObject.schedule();
translateAndMapModelObject(schedule);
idfObject.setString(ZoneMixingFields::ScheduleName, schedule.name().get());
idfObject.setString(ZoneMixingFields::ScheduleName, schedule.nameString());

// DesignFlowRateCalculationMethod
idfObject.setString(ZoneMixingFields::DesignFlowRateCalculationMethod, modelObject.designFlowRateCalculationMethod());
Expand All @@ -76,7 +121,7 @@ namespace energyplus {
}

// FlowRateperFloorArea
value = modelObject.flowRateperZoneFloorArea();
value = modelObject.flowRateperFloorArea();
if (value) {
idfObject.setDouble(ZoneMixingFields::FlowRateperFloorArea, *value);
}
Expand All @@ -94,11 +139,8 @@ namespace energyplus {
}

// SourceZoneorSpaceName
boost::optional<ThermalZone> sourceZone = modelObject.sourceZone();
if (sourceZone) {
// DLM: do not translate source zone now, it will be translated at the right time
idfObject.setString(ZoneMixingFields::SourceZoneorSpaceName, sourceZone->name().get());
}
// DLM: do not translate source zone now, it will be translated at the right time
idfObject.setString(ZoneMixingFields::SourceZoneorSpaceName, getParentObjectName(sourceZoneOrSpace.get()));

// DeltaTemperature
value = modelObject.deltaTemperature();
Expand All @@ -110,49 +152,49 @@ namespace energyplus {
boost::optional<Schedule> optSchedule = modelObject.deltaTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::DeltaTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::DeltaTemperatureScheduleName, optSchedule->nameString());
}

// MinimumReceivingTemperatureScheduleName
optSchedule = modelObject.minimumZoneTemperatureSchedule();
optSchedule = modelObject.minimumReceivingTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::MinimumReceivingTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::MinimumReceivingTemperatureScheduleName, optSchedule->nameString());
}

// MaximumReceivingTemperatureScheduleName
optSchedule = modelObject.maximumZoneTemperatureSchedule();
optSchedule = modelObject.maximumReceivingTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::MaximumReceivingTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::MaximumReceivingTemperatureScheduleName, optSchedule->nameString());
}

// MinimumSourceTemperatureScheduleName
optSchedule = modelObject.minimumSourceZoneTemperatureSchedule();
optSchedule = modelObject.minimumSourceTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::MinimumSourceTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::MinimumSourceTemperatureScheduleName, optSchedule->nameString());
}

// MaximumSourceTemperatureScheduleName
optSchedule = modelObject.maximumSourceZoneTemperatureSchedule();
optSchedule = modelObject.maximumSourceTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::MaximumSourceTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::MaximumSourceTemperatureScheduleName, optSchedule->nameString());
}

// MinimumOutdoorTemperatureScheduleName
optSchedule = modelObject.minimumOutdoorTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::MinimumOutdoorTemperatureScheduleName, optSchedule->nameString());
}

// MaximumOutdoorTemperatureScheduleName
optSchedule = modelObject.maximumOutdoorTemperatureSchedule();
if (optSchedule) {
translateAndMapModelObject(*optSchedule);
idfObject.setString(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName, optSchedule->name().get());
idfObject.setString(ZoneMixingFields::MaximumOutdoorTemperatureScheduleName, optSchedule->nameString());
}

return idfObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ namespace energyplus {
} else if (istringEqual("Flow/Area", *s)) {
d = workspaceObject.getDouble(openstudio::ZoneCrossMixingFields::FlowRateperFloor);
if (d) {
mixing.setFlowRateperZoneFloorArea(*d);
reverseMixing.setFlowRateperZoneFloorArea(*d);
mixing.setFlowRateperFloorArea(*d);
reverseMixing.setFlowRateperFloorArea(*d);
} else {
LOG(Error, "Flow/Area value not found for workspace object " << workspaceObject);
}
Expand Down Expand Up @@ -181,8 +181,8 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMinimumZoneTemperatureSchedule(s.get());
reverseMixing.setMinimumZoneTemperatureSchedule(s.get());
mixing.setMinimumReceivingTemperatureSchedule(s.get());
reverseMixing.setMinimumReceivingTemperatureSchedule(s.get());
}
}
}
Expand All @@ -192,8 +192,8 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMaximumZoneTemperatureSchedule(s.get());
reverseMixing.setMaximumZoneTemperatureSchedule(s.get());
mixing.setMaximumReceivingTemperatureSchedule(s.get());
reverseMixing.setMaximumReceivingTemperatureSchedule(s.get());
}
}
}
Expand All @@ -203,8 +203,8 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMinimumSourceZoneTemperatureSchedule(s.get());
reverseMixing.setMinimumSourceZoneTemperatureSchedule(s.get());
mixing.setMinimumSourceTemperatureSchedule(s.get());
reverseMixing.setMinimumSourceTemperatureSchedule(s.get());
}
}
}
Expand All @@ -214,8 +214,8 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMaximumSourceZoneTemperatureSchedule(s.get());
reverseMixing.setMaximumSourceZoneTemperatureSchedule(s.get());
mixing.setMaximumSourceTemperatureSchedule(s.get());
reverseMixing.setMaximumSourceTemperatureSchedule(s.get());
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/energyplus/ReverseTranslator/ReverseTranslateZoneMixing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ namespace energyplus {
} else if (istringEqual("Flow/Area", *s)) {
d = workspaceObject.getDouble(openstudio::ZoneMixingFields::FlowRateperFloorArea);
if (d) {
mixing.setFlowRateperZoneFloorArea(*d);
mixing.setFlowRateperFloorArea(*d);
} else {
LOG(Error, "Flow/Area value not found for workspace object " << workspaceObject);
}
Expand Down Expand Up @@ -167,7 +167,7 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMinimumZoneTemperatureSchedule(s.get());
mixing.setMinimumReceivingTemperatureSchedule(s.get());
}
}
}
Expand All @@ -177,7 +177,7 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMaximumZoneTemperatureSchedule(s.get());
mixing.setMaximumReceivingTemperatureSchedule(s.get());
}
}
}
Expand All @@ -187,7 +187,7 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMinimumSourceZoneTemperatureSchedule(s.get());
mixing.setMinimumSourceTemperatureSchedule(s.get());
}
}
}
Expand All @@ -197,7 +197,7 @@ namespace energyplus {
OptionalModelObject modelObject = translateAndMapWorkspaceObject(*target);
if (modelObject) {
if (auto s = modelObject->optionalCast<Schedule>()) {
mixing.setMaximumSourceZoneTemperatureSchedule(s.get());
mixing.setMaximumSourceTemperatureSchedule(s.get());
}
}
}
Expand Down
Loading