Skip to content

Wrap ZoneMRTCalculation - #232

Merged
jmarrec merged 5 commits into
developfrom
ZoneMRTCalc
Sep 10, 2026
Merged

Wrap ZoneMRTCalculation#232
jmarrec merged 5 commits into
developfrom
ZoneMRTCalc

Conversation

@joseph-robertson

Copy link
Copy Markdown
Contributor

Pull request overview

Companion PR:

Link to relevant GitHub Issue(s) if appropriate:

Link to the Ubuntu 24.04 .deb installer to use for CI Testing. If not set, it will default to latest official release.
[OpenStudio Installer]: http://

This Pull Request is concerning:

  • Case 1 - NewTest: a new test for a new model API class,
  • Case 2 - TestFix: a fix for an existing test. The GitHub issue should be referenced in the PR description
  • Case 3 - NewTestForExisting: a new test for an already-existing model API class
  • Case 4 - Other: Something else, like maintenance of the repo, or just committing test results with a new OpenStudio version.

Depending on your answer, please fill out the required section below, and delete the three others.
Leave the review checklist in place.


Case 1: New test for a new model API class

Please include which class(es) you are adding a test to specifically test for.
Include a link to the OpenStudio Pull Request in which you are adding the new classes, or the class itself if already on develop.

Work Checklist

The following has been checked to ensure compliance with the guidelines:

  • Tests pass either:

    • with official OpenStudio release (include version):

      • A matching OSM test has been added from the successful run of the Ruby one with the official OpenStudio release
      • The label AddedOSM has been added to this PR
      • All new out.osw have been committed
    • with current develop (incude SHA):

      • The label PendingOSM has been added to this PR
      • A matching OSM test has not yet been added because the official release is pending, but model_tests.rb has a TODO.
        def test_airterminal_cooledbeam_rb
          result = sim_test('airterminal_cooledbeam.rb')
        end
        
        # TODO: To be added in the next official release after: 2.5.0
        # def test_airterminal_fourpipebeam_osm
        #   result = sim_test('airterminal_fourpipebeam.osm')
        # end
      • No out.osw have been committed as they need to be run with an official OpenStudio version
  • Ruby test is stable: when run multiple times on the same machine, it produces the same total site kBTU.
    Please paste the heatmap png generated after running the following commands:

    • I ensured that I assign systems/loads/etc in a repeatable manner (eg: if I assign stuff to thermalZones, I do model.getThermalZones.sort_by{|z| z.name.to_s}.each do ... so I am sure I put the same ZoneHVAC systems to the same zones regardless of their order)
    • I tested stability using process_results.py (see python process_results.py --help for usage).
      Please paste the text output or heatmap png generated after running the following commands:
      # Clean up all custom-tagged OSWs
      python process_results.py test-stability clean
      # Run your test 5 times in a row. Replace `testname_rb` (eg `airterminal_fourpipebeam_rb`)
      python process_results.py test-stability run -n testname_rb
      # Check that they all passed
      python process_results.py test-status --tagged
      # Check site kBTU differences
      python process_results.py heatmap --tagged
      
  • Object has been added to autosize_hvac.rb to ensure the autosizedXXX values methods do work


Case 2: Fix for an existing test

Please include a link to the specific test you are modifying, and a description of the changes you have made and why they are required.

Work Checklist

The change:

  • affects site kBTU results
  • does not affect total site kBTU results

If it affects total site kBTU:

  • Test has been run backwards (see Instructions for Running Docker) for all OpenStudio versions to update numbers
  • Changes did not make the test fail in older OpenStudio versions where it used to pass
  • Matching OSM has been replaced with the output of the ruby test for the oldest OpenStudio release where it passes.
  • All new/changed out.osw have been committed for official OpenStudio versions only

Either way:

  • Ruby test is still stable: when run multiple times on the same machine, it produces the same total site kBTU.

    • I ensured that I assign systems/loads/etc in a repeatable manner (eg: if I assign Terminals to thermalZones, I do model.getThermalZones.sort_by{|z| z.name.to_s}.each do ... so I am sure I put the same ZoneHVAC systems to the same zones regardless of their order)
    • I tested stability using process_results.py (see python process_results.py --help for usage).
      Please paste the text output or heatmap png generated after running the following commands:
      # Clean up all custom-tagged OSWs
      python process_results.py test-stability clean
      # Run your test 5 times in a row. Replace `testname_rb` (eg `airterminal_fourpipebeam_rb`)
      python process_results.py test-stability run -n testname_rb
      # Check that they all passed
      python process_results.py test-status --tagged
      # Check site kBTU differences
      python process_results.py heatmap --tagged
      
  • If relevant, new fields that can be autosized have been added to autosize_hvac.rb to ensure the autosizedXXX values methods do work


Case 3: New test for an already-existing model API class

Please include which class(es) you are adding a test to specifically test for as it was currently not being tested for.
Include a link to the OpenStudio model classes themselves.

eg:

This pull request adds missing tests for the following classes:

Work Checklist

The following has been checked to ensure compliance with the guidelines:

  • Test has been run backwards (see Instructions for Running Docker) for all OpenStudio versions

  • A Matching OSM test has been added with the output of the ruby test for the oldest OpenStudio release where it passes (include OpenStudio Version)

  • Ruby test is stable in the last OpenStudio version: when run multiple times on the same machine, it produces the same total site kBTU.

    • I ensured that I assign systems/loads/etc in a repeatable manner (eg: if I assign stuff to thermalZones, I do model.getThermalZones.sort_by{|z| z.name.to_s}.each do ... so I am sure I put the same ZoneHVAC systems to the same zones regardless of their order)
    • I tested stability using process_results.py (see python process_results.py --help for usage).
      Please paste the text output or heatmap png generated after running the following commands:
      # Clean up all custom-tagged OSWs
      python process_results.py test-stability clean
      # Run your test 5 times in a row. Replace `testname_rb` (eg `airterminal_fourpipebeam_rb`)
      python process_results.py test-stability run -n testname_rb
      # Check that they all passed
      python process_results.py test-status --tagged
      # Check site kBTU differences
      python process_results.py heatmap --tagged
      
  • Object has been added to autosize_hvac.rb to ensure the autosizedXXX values methods do work


Case 4: Other

Please be as explicit as possible about the changes you have made, and why they are warranted.


Review Checklist

  • Code style (indentation, variable names, strip trailing spaces)
  • Functional code review (it has to work!)
  • Matching OSM test has been added or # TODO added to model_tests.rb
  • Appropriate out.osw have been committed
  • Test is stable
  • Object is tested in autosize_hvac as appropriate
  • The appropriate labels have been added to this PR:
    • One of: NewTest, TestFix, NewTestForExisting, Other
    • If NewTest: add PendingOSM or AddedOSM

@joseph-robertson joseph-robertson self-assigned this Sep 4, 2026
@joseph-robertson joseph-robertson added NewTest PR type: a new test for a new model API class PendingOSM A matching OSM test has yet to be added with the next official OpenStudio Release labels Sep 4, 2026
…51014f9c0) [x86_64-linux], Rubocop 1.50.0)

definition2 = OpenStudio::Model::PeopleDefinition.new(model)
definition2.setNumberofPeople(1.0)
definition2.setMeanRadiantTemperatureCalculationType('EnclosureAveraged') # SurfaceWeighted, AngleFactor not supported?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines +8 to +14
# make a 1 story, 100m X 50m, 5 zone core/perimeter building
model.add_geometry({ 'length' => 100,
'width' => 50,
'num_floors' => 1,
'floor_to_floor_height' => 4,
'plenum_height' => 1,
'perimeter_zone_depth' => 3 })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think you only need one zone here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I recan't what I just said. For demo purposes, I think we'd actually like two spaces (if you use 5 that's fine), but assign them to the same thermal zone.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So here we'd do.

# Get spaces, ordered by name to ensure consistency
spaces = model.getSpaces.sort_by { |s| s.name.to_s }

# collapse all spaces into one thermal zone
thermalZone = nil
spaces.each do |space|
  if thermalZone
    temp = space.thermalZone.get
    space.setThermalZone(thermalZone)
    temp.remove
  else
    thermalZone = space.thermalZone.get
  end
end
thermalZone.setName("5 spaces zone")

and we'd add a comment explaining the preconditions you need: people must be unique per space, etc.

Comment on lines +46 to +47
# get all spaces in the zone
spaces = thermal_zone.spaces.sort_by { |s| s.name.to_s }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's only one space per zone by design.

Comment on lines +21 to +26
# add ASHRAE System type 01, PTAC, Residential
model.add_hvac({ 'ashrae_sys_num' => '01' })

# add thermostats
model.add_thermostats({ 'heating_setpoint' => 24,
'cooling_setpoint' => 28 })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Don't think you need HVAC for your test?

Comment on lines +85 to +89
# mrt weighting factors for people
peoples = peoples.uniq.sort_by { |p| p.name.to_s }
peoples.each do |people|
zonemrtcalc.addMRTWeightingFactor(people, 1.0 / peoples.size)
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Explain that the sum can't exceed 1.

And probably do a proper demo of the the extensible methods, for example

# To add a group, you can use the convenience method
# bool addMonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType = "SumOrAverage");
groups = [
# variableOrMeterName, SumOrAverage
['Fan Electricity Energy', 'SumOrAverage'],
['Fan Rise in Air Temperature', 'SumOrAverage'],
['Fan Electricity Rate', 'Maximum'],
['Fan Rise in Air Temperature', 'ValueWhenMaximumOrMinimum']
]
output_table_monthly.addMonthlyVariableGroup(groups[0][0], groups[0][1])
# This will in turn actually use the helper class MonthlyVariableGroup
output_table_monthly.addMonthlyVariableGroup(OpenStudio::Model::MonthlyVariableGroup.new(groups[1][0], groups[1][1]))
raise unless output_table_monthly.numberofMonthlyVariableGroups == 2
# This is a vector of MonthlyVariableGroup
raise unless output_table_monthly.monthlyVariableGroups.size == 2
first_monthly_group = output_table_monthly.monthlyVariableGroups.first
# This returns an OptionalMonthlyVariableGroup
first_monthly_group_ = output_table_monthly.getMonthlyVariableGroup(0)
raise unless first_monthly_group_.is_initialized
# The equality operator is defined to check for both variableOrMeterName and
# aggregationType
raise unless first_monthly_group == first_monthly_group_.get
raise unless first_monthly_group.variableOrMeterName == 'Fan Electricity Energy'
raise unless first_monthly_group.aggregationType == 'SumOrAverage'
output_table_monthly.removeMonthlyVariableGroup(1)
output_table_monthly.removeAllMonthlyVariableGroups
# There is also a batch add
monthly_groups = groups.map { |g| OpenStudio::Model::MonthlyVariableGroup.new(g[0], g[1]) }
output_table_monthly.addMonthlyVariableGroups(monthly_groups)
raise unless output_table_monthly.numberofMonthlyVariableGroups == 4

Comment thread model_tests.rb
Comment on lines +2310 to +2312
def test_zonemrtcalculation_rb
result = sim_test('zonemrtcalculation.rb')
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Once the ruby one is good, you'll need a py equivalent.


definition2 = openstudio.model.PeopleDefinition(model)
definition2.setNumberofPeople(1.0)
definition2.setMeanRadiantTemperatureCalculationType("EnclosureAveraged") # SurfaceWeighted, AngleFactor not supported?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Supporting SurfaceWeighted and AngleFactor in NatLabRockies/OpenStudio#5650

Comment on lines +72 to +94
definition1 = OpenStudio::Model::PeopleDefinition.new(model)
definition1.setNumberofPeople(1.0)
definition1.setMeanRadiantTemperatureCalculationType('EnclosureAveraged')
definition1.setThermalComfortModelType(0, 'Fanger')

people1 = OpenStudio::Model::People.new(definition1)
people1.setWorkEfficiencySchedule(workeffsch)
people1.setClothingInsulationSchedule(cloinssch)
people1.setAirVelocitySchedule(airvelsch)
people1.setSpace(space)
peoples << people1

definition2 = OpenStudio::Model::PeopleDefinition.new(model)
definition2.setNumberofPeople(1.0)
definition2.setMeanRadiantTemperatureCalculationType('EnclosureAveraged') # SurfaceWeighted, AngleFactor not supported?
definition2.setThermalComfortModelType(0, 'Pierce')

people2 = OpenStudio::Model::People.new(definition2)
people2.setWorkEfficiencySchedule(workeffsch)
people2.setClothingInsulationSchedule(cloinssch)
people2.setAirVelocitySchedule(airvelsch)
people2.setSpace(space)
peoples << people2

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Using NatLabRockies/OpenStudio#5650, test all mean radiant temperature calculation types:

Suggested change
definition1 = OpenStudio::Model::PeopleDefinition.new(model)
definition1.setNumberofPeople(1.0)
definition1.setMeanRadiantTemperatureCalculationType('EnclosureAveraged')
definition1.setThermalComfortModelType(0, 'Fanger')
people1 = OpenStudio::Model::People.new(definition1)
people1.setWorkEfficiencySchedule(workeffsch)
people1.setClothingInsulationSchedule(cloinssch)
people1.setAirVelocitySchedule(airvelsch)
people1.setSpace(space)
peoples << people1
definition2 = OpenStudio::Model::PeopleDefinition.new(model)
definition2.setNumberofPeople(1.0)
definition2.setMeanRadiantTemperatureCalculationType('EnclosureAveraged') # SurfaceWeighted, AngleFactor not supported?
definition2.setThermalComfortModelType(0, 'Pierce')
people2 = OpenStudio::Model::People.new(definition2)
people2.setWorkEfficiencySchedule(workeffsch)
people2.setClothingInsulationSchedule(cloinssch)
people2.setAirVelocitySchedule(airvelsch)
people2.setSpace(space)
peoples << people2
surfaces = space.surfaces.sort_by { |s| s.name.to_s }
comfortview = OpenStudio::Model::ComfortViewFactorAngles.new(model)
surfaces.each do |surface|
comfortview.addAngleFactor(surface, 1.0 / surfaces.size)
end
definition1 = OpenStudio::Model::PeopleDefinition.new(model)
definition1.setNumberofPeople(1.0)
definition1.setMeanRadiantTemperatureCalculationType('EnclosureAveraged')
definition1.setThermalComfortModelType(0, 'Fanger')
people1 = OpenStudio::Model::People.new(definition1)
people1.setWorkEfficiencySchedule(workeffsch)
people1.setClothingInsulationSchedule(cloinssch)
people1.setAirVelocitySchedule(airvelsch)
people1.setSpace(space)
peoples << people1
definition2 = OpenStudio::Model::PeopleDefinition.new(model)
definition2.setNumberofPeople(1.0)
definition2.setMeanRadiantTemperatureCalculationType('SurfaceWeighted')
definition2.setSurfaceNameAngleFactorListName(surfaces[0])
definition2.setThermalComfortModelType(0, 'Pierce')
people2 = OpenStudio::Model::People.new(definition2)
people2.setWorkEfficiencySchedule(workeffsch)
people2.setClothingInsulationSchedule(cloinssch)
people2.setAirVelocitySchedule(airvelsch)
people2.setSpace(space)
peoples << people2
definition3 = OpenStudio::Model::PeopleDefinition.new(model)
definition3.setNumberofPeople(1.0)
definition3.setMeanRadiantTemperatureCalculationType('AngleFactor')
definition3.setSurfaceNameAngleFactorListName(comfortview)
definition3.setThermalComfortModelType(0, 'KSU')
people3 = OpenStudio::Model::People.new(definition3)
people3.setWorkEfficiencySchedule(workeffsch)
people3.setClothingInsulationSchedule(cloinssch)
people3.setAirVelocitySchedule(airvelsch)
people3.setSpace(space)
peoples << people3

@joseph-robertson joseph-robertson changed the title Wrap ZoneMRTCalculation Wrap ZoneMRTCalculation + ComfortViewFactorAngles Sep 9, 2026
@jmarrec

jmarrec commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

@joseph-robertson I'm going to merge this one without your last commit, which I extracted to another branch: ComfortViewFactorAngles

@jmarrec jmarrec changed the title Wrap ZoneMRTCalculation + ComfortViewFactorAngles Wrap ZoneMRTCalculation Sep 10, 2026
@jmarrec
jmarrec marked this pull request as ready for review September 10, 2026 08:47
@jmarrec
jmarrec merged commit e402eb6 into develop Sep 10, 2026
4 of 6 checks passed
@jmarrec
jmarrec deleted the ZoneMRTCalc branch September 10, 2026 08:47
@jmarrec jmarrec mentioned this pull request Sep 10, 2026
26 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

NewTest PR type: a new test for a new model API class PendingOSM A matching OSM test has yet to be added with the next official OpenStudio Release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants