V26.2.0-IOFreeze: new ZoneMRTCalculation object - #5649
Conversation
🧪 Test Results DashboardSummary
❌ Significant Test Failures📊 Test Run Information
|
There was a problem hiding this comment.
🟡 Changes recommended
Weight-sum validation, stale references, non-finite values, and partial-add failure reporting can currently produce invalid behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds Model API and EnergyPlus translation support for ZoneMRTCalculation.
Changes:
- Adds the model object, MRT weighting-factor API, and ThermalZone integration.
- Adds forward/reverse translators and binding support.
- Updates IDDs, build registration, and tests.
File summaries
| File | Description |
|---|---|
src/model/ZoneMRTCalculation.hpp |
Declares the new API. |
src/model/ZoneMRTCalculation.cpp |
Implements weighting-factor behavior. |
src/model/ZoneMRTCalculation_Impl.hpp |
Declares implementation details. |
src/model/ThermalZone.hpp |
Exposes MRT calculation access. |
src/model/ThermalZone.cpp |
Implements creation and lookup. |
src/model/ThermalZone_Impl.hpp |
Adds implementation declaration. |
src/model/test/ZoneMRTCalculation_GTest.cpp |
Tests the model API. |
src/model/ModelHVAC.i |
Adds C# ThermalZone access. |
src/model/ModelGeometry.i |
Registers binding templates. |
src/model/Model.cpp |
Registers constructors. |
src/model/ConcreteModelObjects.hpp |
Includes the new object. |
src/model/CMakeLists.txt |
Adds model sources and tests. |
src/energyplus/Test/ZoneMRTCalculation_GTest.cpp |
Tests both translators. |
src/energyplus/ReverseTranslator/ReverseTranslateZoneMRTCalculation.cpp |
Implements reverse translation. |
src/energyplus/ReverseTranslator.hpp |
Declares reverse translation. |
src/energyplus/ReverseTranslator.cpp |
Dispatches reverse translation. |
src/energyplus/ForwardTranslator/ForwardTranslateZoneMRTCalculation.cpp |
Implements forward translation. |
src/energyplus/ForwardTranslator.hpp |
Declares forward translation. |
src/energyplus/ForwardTranslator.cpp |
Registers forward translation. |
src/energyplus/CMakeLists.txt |
Adds translator sources and tests. |
resources/model/OpenStudio.idd |
Defines the OpenStudio object. |
resources/energyplus/ProposedEnergy+.idd |
Converts EnergyPlus fields to extensible groups. |
Review details
Suppressed comments (1)
src/model/ZoneMRTCalculation.cpp:107
- Removing a referenced
Peopleobject clears this pointer but leaves the extensible group. A subsequent call toaddMRTWeightingFactorreaches this predicate and calls.get()on an empty optional, throwing instead of adding the new factor. Treat a missing field as a non-match (and add a removal regression test).
auto it = std::find_if(egs.begin(), egs.end(), [&](const WorkspaceExtensibleGroup& eg) {
return eg.getField(OS_ZoneMRTCalculationExtensibleFields::PeopleName).get() == peopleHandle;
});
- Files reviewed: 22/22 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
Thermal-zone deletion can leave an invalid ZoneMRTCalculation object that later causes assertions during access or translation.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
resources/model/OpenStudio.idd:6830
- This field declares two conflicting
\typevalues; all other OpenStudio object-list fields use only\type object-list(for example, lines 6808-6809 and 6837-6838). Remove the redundant alpha declaration so the IDD metadata has one unambiguous field type.
src/model/ZoneMRTCalculation.cpp:85 - These adjacent string literals concatenate as
andthat, making the thrown clone error malformed. Preserve a space at the literal boundary.
- Files reviewed: 25/25 changed files
- Comments generated: 1
- Review effort level: Balanced
| ZoneMRTCalculation ThermalZone_Impl::getZoneMRTCalculation() const { | ||
| auto thisThermalZone = getObject<ThermalZone>(); | ||
| std::vector<ZoneMRTCalculation> zoneMRTCalculations = | ||
| thisThermalZone.getModelObjectSources<ZoneMRTCalculation>(ZoneMRTCalculation::iddObjectType()); |
There was a problem hiding this comment.
I think we'd have the same issue when deleting a Space.
Removing a Space bulk-removes its child People through ParentObject_Impl::remove(), but it does not call People_Impl::remove(). So cleanup would require Space_Impl::remove() to inspect all child People, then find and prune any ZoneMRTCalculation groups for each one. That reaches across more ownership boundaries and is less consistent with the existing bulk child-removal mechanism.
So: zone removal is direct source-object cleanup; space removal is indirect child/reference cleanup.
jmarrec
left a comment
There was a problem hiding this comment.
Looking pretty good. A couple of things to change in the code review
And please fill out the PR checklist, most important item of it: we need a OpenStudio-resources matching test for this new object
| \note A People object assigned directly to a Space retains its input name. | ||
| \note A People object expanded across multiple Spaces must use an expanded instance name | ||
| \note formed as "<Space Name> <People Name>"; its original input name is not accepted. | ||
| \note People instances in automatically generated remainder Spaces are not supported. |
There was a problem hiding this comment.
Yeah, not totally sure what to do here. If the model has People object that references a SpaceType (instead of a Space), then the IDF's People object references a SpaceList. So I think we'd want to support this (somehow), and the resulting IDF's ZoneMRTCalculation object would look something like (?)
ZoneMRTCalculation,
[...]
Story 1 East Perimeter Space Baseline Model People, !- People 1 Name
0.25, !- MRT Weighting Factor 1
Story 1 West Perimeter Space Baseline Model People, !- People 2 Name
0.75, !- MRT Weighting Factor 2
There was a problem hiding this comment.
I think your current implementation which enforces things at model time plus FT time is fine
More importantly this is going to be a very niche object, so if you choose to use it you can also deal with the preconditions.
For eg I don't think we need to check is assigned to a spaceType and the sp has only one space
| std::vector<MRTWeightingFactor> mrtWeightingFactors() const; | ||
|
|
||
| unsigned int numberofMRTWeightingFactors() const; | ||
|
|
||
| boost::optional<unsigned> mrtWeightingFactorIndex(const People& people) const; | ||
|
|
||
| boost::optional<MRTWeightingFactor> getMRTWeightingFactor(unsigned groupIndex) const; | ||
|
|
||
| bool addMRTWeightingFactor(const MRTWeightingFactor& mrtWeightingFactor); | ||
|
|
||
| bool addMRTWeightingFactor(const People& people, double mrtWeightingFactor); | ||
|
|
||
| bool addMRTWeightingFactors(const std::vector<MRTWeightingFactor>& mrtWeightingFactors); | ||
|
|
||
| void removeMRTWeightingFactor(int groupIndex); | ||
|
|
||
| void removeAllMRTWeightingFactors(); |
There was a problem hiding this comment.
MRT is an accronym.
You use mrtW and MRTW here, which is an unusual way of casing.
I know mRTWeightingFactor looks weirdish, but that's the camelCase convention we're using everywhere else.
I actually rely on it sometimes when doing dynamic programming...
I suggest either:
- changing
- or motivating it by looking at how we handled other IDD fields with acronyms.
There was a problem hiding this comment.
I asked Claude to look into it
In the codebase, when an acronym leads a lowerCamelCase identifier (getter/local var, not prefixed by get/set/add), the established pattern only lowercases the first letter of the acronym, e.g.:
dXCoil,dXHeatingCoilSizingRatio(from IDD fieldDX...)eMSVariableName,eMSRuntimeLanguageDebugOutputLevel(fromEMS...)fMUFile,fMUFileName,fMUVariableName(fromFMU...)vFDControlType,vFDEfficiencyCurve(fromVFD...)fCAH,fCAuxHeat,fCStorage(fromFC..., Generator:FuelCell fields)
That's 49 distinct identifiers following this rule, vs. only 3 outliers (copFunctionofTemperatureCurve and friends, which fully lowercase COP) — likely older/manually-typed exceptions rather than the intended convention. So the dominant, established rule is: lowercase only the leading letter of the acronym, keep the rest capitalized — exactly the mRTWeightingFactor you described.
ZoneMRTCalculation currently uses mrtWeightingFactor/mrtWeightingFactors/mrtWeightingFactorIndex (member functions, local vars, and the private member m_mrtWeightingFactor), which breaks that convention. The class name MRTWeightingFactor itself and the prefixed methods (addMRTWeightingFactor, getMRTWeightingFactor, numberofMRTWeightingFactors, removeMRTWeightingFactor) are already correctly cased and don't need touching — only the un-prefixed lowerCamelCase spots do.
| /** This class implements an MRT weighting factor. */ | ||
| class MODEL_API MRTWeightingFactor | ||
| { | ||
| public: | ||
| MRTWeightingFactor(const People& people, double mrtWeightingFactor); | ||
|
|
||
| People people() const; | ||
| double mrtWeightingFactor() const; | ||
|
|
||
| private: | ||
| People m_people; | ||
| double m_mrtWeightingFactor; | ||
| REGISTER_LOGGER("openstudio.model.MRTWeightingFactor"); | ||
| }; |
There was a problem hiding this comment.
Should we add an equality operator?
If so, do we compare People only? (a people can only be listed once...)
Or on both People and factor? (which is semantically more correct/expected)
🤔
There was a problem hiding this comment.
Added an operator== that compares on People AND factor. be24456
| if (sum > 1.0) { | ||
| LOG(Error, "Cannot add " << people.briefDescription() << " to " << briefDescription() << " because the MRT Weighting Factors would sum to " | ||
| << sum << ", which is greater than 1."); | ||
| return result; | ||
| } |
There was a problem hiding this comment.
AH! While modifying the OpenStudio-resources tests to be more comprehensive, I got bit by this floating point comparison. We need a tolerance.
| MODELOBJECT_TEMPLATES(SurfacePropertyExposedFoundationPerimeter); | ||
| MODELOBJECT_TEMPLATES(ViewFactor); // Helper class defined in ZonePropertyUserViewFactorsBySurfaceName | ||
| MODELOBJECT_TEMPLATES(ZonePropertyUserViewFactorsBySurfaceName); | ||
| MODELOBJECT_TEMPLATES(MRTWeightingFactor); // Helper class defined in ZoneMRTCalculation |
There was a problem hiding this comment.
You have defined operator<< for that class, you need to extend str so Ruby/Python see it
…tWeightingFactorIndex to use just People, update model tests.
…ng on both People and Factor (
723ae70 to
ed3042d
Compare
…Zone::getZoneMRTCalculation
…expansive machinery like this for such a niche object.
|
@joseph-robertson I have updated the PR as well as the OpenStudio-resources test at NatLabRockies/OpenStudio-resources#232 Unfortunately the Ubuntu runners have a Github CLI EXPKEYSIG (I got it locally on Ubuntu 24.04 too), so we can have CI run the OS resources test with the deb installer from this PR, but I built locally and confirmed everything works fine. Can you take a quick pass at my changes? Then we can drop both. Thank you. |
Pull request overview
Pull Request Author
src/model/test)src/energyplus/Test)src/osversion/VersionTranslator.cpp)Labels:
IDDChangeAPIChangePull Request - Ready for CIso that CI builds your PRReview Checklist
This will not be exhaustively relevant to every PR.