From 83aa015ad66cc8210eb7dda06b42fc8f4393a72c Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 21 Jun 2026 11:34:26 -0600 Subject: [PATCH 01/12] Fix release notes for next RC --- .github/workflows/release_notes.yml | 6 +- developer/ruby/GitHubIssueStats.rb | 102 +++++++++++----------------- 2 files changed, 44 insertions(+), 64 deletions(-) diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml index 8b31283f1..3693dc961 100644 --- a/.github/workflows/release_notes.yml +++ b/.github/workflows/release_notes.yml @@ -11,6 +11,10 @@ jobs: release-notes: name: Create changelog runs-on: ubuntu-latest + permissions: + contents: write + issues: read + pull-requests: read steps: - uses: actions/checkout@v5 @@ -28,7 +32,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gem install github_api + gem install octokit pip install requests ruby ./developer/ruby/GitHubIssueStats.rb > changelog.txt diff --git a/developer/ruby/GitHubIssueStats.rb b/developer/ruby/GitHubIssueStats.rb index 4d792e576..a7827c1b0 100644 --- a/developer/ruby/GitHubIssueStats.rb +++ b/developer/ruby/GitHubIssueStats.rb @@ -1,4 +1,4 @@ -require 'github_api' +require 'octokit' require 'date' require 'yaml' @@ -6,20 +6,18 @@ # that is at least a week old def get_begin_date_and_previous_tag() a_week_ago = Time.now - (60*60*24*7) - - @github.repos.releases.list(owner: @repo_owner, - repo: @repo).each_page do |page| - page.each do |release| - next if release.tag_name !~ /^v\d\.\d\.0$/ - release_date = Time.parse(release.created_at) - next if release_date > a_week_ago - # This is perhaps unecessary since we match to a tag in vX.Y.0 format - # already but it doesn't hurt - next if release.prerelease - next if release.draft - STDERR.puts "Found previous major/minor release: #{release.tag_name}, #{release_date}" - return release_date, " (#{release.tag_name})" - end + repo = "#{@repo_owner}/#{@repo}" + + @github.releases(repo, per_page: 100).each do |release| + next if release.tag_name !~ /^v\d+\.\d+\.0$/ + release_date = release.created_at.to_time + next if release_date > a_week_ago + # This is perhaps unecessary since we match to a tag in vX.Y.0 format + # already but it doesn't hurt + next if release.prerelease + next if release.draft + STDERR.puts "Found previous major/minor release: #{release.tag_name}, #{release_date}" + return release_date, " (#{release.tag_name})" end STDERR.puts "Cannot find previous release, setting time to 2005" return Time.new(2005, 01, 01), "" @@ -60,16 +58,18 @@ def print_issue(issue) if !ENV['GITHUB_TOKEN'].nil? token = ENV['GITHUB_TOKEN'] - @github = Github.new oauth_token: token + @github = Octokit::Client.new(access_token: token) elsif File.exist?(Dir.home + '/github_config.yml') github_options = YAML.load_file(Dir.home + '/github_config.yml') token = github_options['oauth_token'] - @github = Github.new oauth_token: token + @github = Octokit::Client.new(access_token: token) else STDERR.puts "Github Token not found" - @github = Github.new + @github = Octokit::Client.new end +@github.auto_paginate = true + @begin_date, @prev_tag = get_begin_date_and_previous_tag() totalOpenIssues = Array.new @@ -78,59 +78,35 @@ def print_issue(issue) closedIssues = Array.new acceptedPullRequests = Array.new +repo = "#{@repo_owner}/#{@repo}" + # Process Open Issues -results = -1 -page = 1 -while (results != 0) - resp = @github.issues.list user: @repo_owner, repo: @repo, - :sort => 'created', - :direction => 'asc', - :state => 'open', - :per_page => 100, - :page => page - results = resp.length - resp.env[:body].each do |issue, index| - created = Time.parse(issue.created_at) - if !issue.has_key?(:pull_request) - totalOpenIssues << issue - if created >= @begin_date && created <= @end_date - newIssues << issue - end - else - totalOpenPullRequests << issue +@github.issues(repo, sort: 'created', direction: 'asc', state: 'open', per_page: 100).each do |issue| + created = issue.created_at.to_time + if issue.pull_request.nil? + totalOpenIssues << issue + if created >= @begin_date && created <= @end_date + newIssues << issue end + else + totalOpenPullRequests << issue end - - page = page + 1 end # Process Closed Issues -results = -1 -page = 1 -while (results != 0) - resp = @github.issues.list user: @repo_owner, repo: @repo, - :sort => 'created', - :direction => 'asc', - :state => 'closed', - :per_page => 100, - :page => page - results = resp.length - resp.env[:body].each do |issue, index| - created = Time.parse(issue.created_at) - closed = Time.parse(issue.closed_at) - if !issue.has_key?(:pull_request) - if created >= @begin_date && created <= @end_date - newIssues << issue - end - if closed >= @begin_date && closed <= @end_date - closedIssues << issue - end - elsif closed >= @begin_date && closed <= @end_date - acceptedPullRequests << issue +@github.issues(repo, sort: 'created', direction: 'asc', state: 'closed', per_page: 100).each do |issue| + created = issue.created_at.to_time + closed = issue.closed_at.to_time + if issue.pull_request.nil? + if created >= @begin_date && created <= @end_date + newIssues << issue end + if closed >= @begin_date && closed <= @end_date + closedIssues << issue + end + elsif closed >= @begin_date && closed <= @end_date + acceptedPullRequests << issue end - - page = page + 1 end closedIssues.sort! {|x,y| get_num(x) <=> get_num(y)} From 2161112f89b285f724f03708d94d82e928917532 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 19 Jul 2026 12:32:32 -0600 Subject: [PATCH 02/12] Fix CI to run on prs from external forks --- .github/workflows/app_build.yml | 19 ++++++++++++++++--- .github/workflows/docker-ci.yml | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index 0a0bd8bb8..4f8fd988b 100644 --- a/.github/workflows/app_build.yml +++ b/.github/workflows/app_build.yml @@ -156,6 +156,14 @@ jobs: echo "There are $N threads available" echo "N=$N" >> $GITHUB_ENV + # Detect fork PRs — secrets are unavailable for external forks, so + # codesigning and artifact upload steps must be skipped for them. + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then + echo "IS_FORK_PR=true" >> $GITHUB_ENV + else + echo "IS_FORK_PR=false" >> $GITHUB_ENV + fi + if [ "$RUNNER_OS" == "Linux" ]; then echo "Install needed system dependencies for OPENGL (due to Qt) for Linux" sudo apt update -qq @@ -232,7 +240,7 @@ jobs: - name: "Configure for codesigning" id: codesigning - if: runner.os == 'macOS' + if: runner.os == 'macOS' && env.IS_FORK_PR != 'true' run: | set -x cd $RUNNER_TEMP @@ -605,6 +613,7 @@ jobs: -WaitForCompletion -Force - name: Archive binary artifacts + if: env.IS_FORK_PR != 'true' uses: actions/upload-artifact@v4 # build/_CPack_Packages/win64/IFW/*.exe # build/_CPack_Packages/Linux/DEB/*.deb @@ -614,13 +623,14 @@ jobs: path: build/${{ matrix.BINARY_PKG_PATH }}/*.${{ env.BINARY_EXT }} - name: Archive TGZ or ZIP artifacts + if: env.IS_FORK_PR != 'true' uses: actions/upload-artifact@v4 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}.${{ env.COMPRESSED_EXT }} path: build/${{ matrix.COMPRESSED_PKG_PATH }}/*.${{ env.COMPRESSED_EXT }} - name: Full Test Package signing for IFW and TGZ - if: runner.os == 'macOS' + if: runner.os == 'macOS' && env.IS_FORK_PR != 'true' working-directory: ./build shell: bash run: | @@ -632,7 +642,7 @@ jobs: echo "::endgroup::" - name: Upload otool info as artifact - if: runner.os == 'macOS' + if: runner.os == 'macOS' && env.IS_FORK_PR != 'true' uses: actions/upload-artifact@v4 with: name: otool_infos_cpack_${{ matrix.os }}_${{ matrix.arch }} @@ -656,6 +666,7 @@ jobs: $XVFBCMD ctest -j -T test --output-on-failure --no-compress-output -C $BUILD_TYPE || true - name: Archive test results? + if: env.IS_FORK_PR != 'true' uses: actions/upload-artifact@v4 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}-Test.xml @@ -678,6 +689,7 @@ jobs: $XVFBCMD Products/SpacesSurfaces_Benchmark --benchmark_out_format=csv --benchmark_out='bench_results_SpacesSurfaces.csv' || true - name: Archive benchmark results? + if: env.IS_FORK_PR != 'true' uses: actions/upload-artifact@v4 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}-bench_results.csv @@ -723,6 +735,7 @@ jobs: test_package_macos: name: Test Built Package on macOS needs: build + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository runs-on: ${{ matrix.os }} strategy: # fail-fast: Default is true, switch to false to allow one platform to fail and still run others diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index adab56ce8..a584d9276 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -37,7 +37,7 @@ jobs: load: true tags: osapp-build:latest cache-from: type=gha,scope=osapp-build - cache-to: type=gha,scope=osapp-build,mode=max + cache-to: ${{ github.event.pull_request.head.repo.full_name == github.repository && 'type=gha,scope=osapp-build,mode=max' || '' }} - name: Configure (Conan install + CMake configure) run: make configure From bc8f72f68c8397f7c15f4b0a9f3d970c60739325 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 19 Jul 2026 16:22:54 -0600 Subject: [PATCH 03/12] Install QtIFW even for forks --- .github/workflows/app_build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index 4f8fd988b..6371db270 100644 --- a/.github/workflows/app_build.yml +++ b/.github/workflows/app_build.yml @@ -282,8 +282,13 @@ jobs: cd .. && rm -Rf codesigning + - name: "Install patched QtIFW (macOS)" + if: runner.os == 'macOS' + run: | + set -x + brew list aria2 || brew install aria2 + mkdir $RUNNER_TEMP/QtIFW && cd $RUNNER_TEMP/QtIFW # Download my patched QtIFW - mkdir QtIFW && cd QtIFW aria2c https://github.com/jmarrec/QtIFW-fixup/releases/download/v5.0.0-dev-with-fixup/QtIFW-5.0.0-${{ matrix.arch }}.zip xattr -r -d com.apple.quarantine ./QtIFW-5.0.0-${{ matrix.arch }}.zip unzip QtIFW-5.0.0-${{ matrix.arch }}.zip From de88fad4aa56ecae9ef776cde9ace4aaca11ca60 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 19 Jul 2026 19:30:05 -0600 Subject: [PATCH 04/12] Don't set code signing args for external prs --- .github/workflows/app_build.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index 6371db270..de5fe8409 100644 --- a/.github/workflows/app_build.yml +++ b/.github/workflows/app_build.yml @@ -553,15 +553,21 @@ jobs: fi if [ "$RUNNER_OS" == "macOS" ]; then + CODESIGNING_ARGS=() + if [ "$IS_FORK_PR" != "true" ]; then + CODESIGNING_ARGS=( + "-DCPACK_CODESIGNING_DEVELOPPER_ID_APPLICATION:STRING=Developer ID Application: The Energy Coalition (UG9S5ZLM34)" + "-DCPACK_CODESIGNING_NOTARY_PROFILE_NAME:STRING=OpenStudioApplication" + "-DCPACK_CODESIGNING_MACOS_IDENTIFIER:STRING=org.openstudiocoalition.OpenStudioApplication" + ) + fi cmake --preset conan-release -DQT_INSTALL_DIR:PATH=${{ env.QT_INSTALL_DIR }} \ -DBUILD_DOCUMENTATION:BOOL=${{ env.BUILD_DOCUMENTATION }} \ -DBUILD_PACKAGE:BOOL=${{ env.BUILD_PACKAGE }} \ -DCPACK_BINARY_TGZ:BOOL=ON \ -DANALYTICS_API_SECRET:STRING=${{ secrets.ANALYTICS_API_SECRET }} \ -DANALYTICS_MEASUREMENT_ID:STRING=${{ secrets.ANALYTICS_MEASUREMENT_ID }} \ - -DCPACK_CODESIGNING_DEVELOPPER_ID_APPLICATION:STRING="Developer ID Application: The Energy Coalition (UG9S5ZLM34)" \ - -DCPACK_CODESIGNING_NOTARY_PROFILE_NAME:STRING=OpenStudioApplication \ - -DCPACK_CODESIGNING_MACOS_IDENTIFIER:STRING=org.openstudiocoalition.OpenStudioApplication + "${CODESIGNING_ARGS[@]}" else cmake --preset conan-release -DQT_INSTALL_DIR:PATH=${{ env.QT_INSTALL_DIR }} \ -DBUILD_DOCUMENTATION:BOOL=${{ env.BUILD_DOCUMENTATION }} \ From d9415ade7d8d5ff17f9137ed8e5219e5d3594773 Mon Sep 17 00:00:00 2001 From: Mike Lovejoy <69771412+Ski90Moo@users.noreply.github.com> Date: Mon, 20 Jul 2026 06:04:14 +0300 Subject: [PATCH 05/12] fix: address misc hvac_library.osm issues (#888) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: autosize resistive defrost heater capacity in hvac_library.osm Hardcoded values (1e-07 for the VRF system, 2000 for the four DX heating coils) prevented EnergyPlus from sizing defrost heater power to the equipment capacity. Fixes #770 * fix: rename mislabeled gas/electric unitary system, add genuine VRF-free DX heat pump "Multi Speed HP AirToAir" was categorized as a heat pump but used a 2-stage gas heating coil with electric supplemental heat, so it is renamed to "Multi Speed DX_Clg Gas_Htg" to reflect its actual coils. A new AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed object named "Multi Speed HP AirToAir" is added with a genuine 2-stage DX heating coil (reverse-cycle defrost, autosized resistive defrost heater) and 2-stage DX cooling coil, matching the example provided in the issue. Fixes #834 * feat: add 2-speed DX cycling unitary system with CoolReheat dehumidification to hvac library Adds 'Unitary - 2-Speed DX Elec heat - Cycling - Dehumidify' to the default HVAC library, using OS:Coil:Cooling:DX:TwoStageWithHumidityControlMode and CoolReheat dehumidification control — a common high-humidity climate system not previously buildable from the OpenStudio App UI. All five referenced objects (fan, heating coil, cooling coil, supplemental coil, availability schedule) reuse existing library entries. Co-Authored-By: Claude Sonnet 4.6 * fix: correct NaturalGas fuel type to Electricity on two DX multispeed cooling coils Both OS:Coil:Cooling:DX:MultiSpeed objects in hvac_library.osm had their Fuel Type field incorrectly set to NaturalGas. DX cooling coils are electrically driven; this was a copy-paste error. Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Dan Macumber --- .../Resources/default/hvac_library.osm | 534 +++++++++++++++++- 1 file changed, 527 insertions(+), 7 deletions(-) diff --git a/src/openstudio_app/Resources/default/hvac_library.osm b/src/openstudio_app/Resources/default/hvac_library.osm index 54c76447f..7758f37ac 100644 --- a/src/openstudio_app/Resources/default/hvac_library.osm +++ b/src/openstudio_app/Resources/default/hvac_library.osm @@ -1866,7 +1866,7 @@ OS:Coil:Heating:DX:SingleSpeed, Resistive, !- Defrost Strategy Timed, !- Defrost Control 0.16666700000000001, !- Defrost Time Period Fraction - 2000; !- Resistive Defrost Heater Capacity {W} + autosize; !- Resistive Defrost Heater Capacity {W} OS:Curve:Biquadratic, {b43603f6-17c3-480d-9004-483799d9253d}, ! Handle @@ -4054,7 +4054,7 @@ OS:Coil:Heating:DX:SingleSpeed, Resistive, !- Defrost Strategy Timed, !- Defrost Control 0.16666700000000001, !- Defrost Time Period Fraction - 2000; !- Resistive Defrost Heater Capacity {W} + autosize; !- Resistive Defrost Heater Capacity {W} OS:Curve:Biquadratic, {af3d4a9f-eefe-47f5-917b-3db4f451b2d4}, ! Handle @@ -5327,7 +5327,7 @@ OS:AirConditioner:VariableRefrigerantFlow, Timed, !- Defrost Control , !- Defrost Energy Input Ratio Modifier Function of Temperature Curve Name 0.058333, !- Defrost Time Period Fraction {dimensionless} - 1e-07, !- Resistive Defrost Heater Capacity {W} + autosize, !- Resistive Defrost Heater Capacity {W} 7, !- Maximum Outdoor Dry-bulb Temperature for Defrost Operation {C} , !- Condenser Type , !- Condenser Inlet Node @@ -6095,7 +6095,7 @@ OS:Coil:Heating:DX:SingleSpeed, Resistive, !- Defrost Strategy Timed, !- Defrost Control 0.166667, !- Defrost Time Period Fraction - 2000; !- Resistive Defrost Heater Capacity {W} + autosize; !- Resistive Defrost Heater Capacity {W} OS:Curve:Cubic, {82a69b59-67f5-4e1e-bd60-460390e4890a}, !- Handle @@ -6357,7 +6357,7 @@ OS:Coil:Heating:DX:SingleSpeed, Resistive, !- Defrost Strategy Timed, !- Defrost Control 0.166667, !- Defrost Time Period Fraction - 2000; !- Resistive Defrost Heater Capacity {W} + autosize; !- Resistive Defrost Heater Capacity {W} OS:Curve:Cubic, {9c9999d6-4027-41c1-808f-f1f41f3715c6}, !- Handle @@ -8394,6 +8394,54 @@ OS:Coil:Heating:Gas, , !- Part Load Fraction Correlation Curve Name 0; !- Off Cycle Parasitic Gas Load {W} +OS:AirLoopHVAC:UnitarySystem, + {e6f7a8b9-c0d1-4234-e5f6-a7b8c9d01234}, !- Handle + Unitary - 2-Speed DX Elec heat - Cycling - Dehumidify, !- Name + Load, !- Control Type + , !- Controlling Zone or Thermostat Location + CoolReheat, !- Dehumidification Control Type + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + , !- Air Inlet Node Name + , !- Air Outlet Node Name + {d89faefb-347b-43f3-a961-af51209d1bda}, !- Supply Fan Name + BlowThrough, !- Fan Placement + , !- Supply Air Fan Operating Mode Schedule Name + {be648e1c-0822-4f10-b08a-dcb96f541f34}, !- Heating Coil Name + 1, !- DX Heating Coil Sizing Ratio + {5c5f1024-b1dc-4198-bc1e-9aa6b2695b48}, !- Cooling Coil Name + No, !- Use DOAS DX Cooling Coil + 2, !- DOAS DX Cooling Coil Leaving Minimum Air Temperature {C} + LatentOrSensibleLoadControl, !- Latent Load Control + {85d6c234-b976-4655-8f65-58ed0a4daced}, !- Supplemental Heating Coil Name + SupplyAirFlowRate, !- Supply Air Flow Rate Method During Cooling Operation + autosize, !- Supply Air Flow Rate During Cooling Operation {m3/s} + , !- Supply Air Flow Rate Per Floor Area During Cooling Operation {m3/s-m2} + , !- Fraction of Autosized Design Cooling Supply Air Flow Rate + , !- Design Supply Air Flow Rate Per Unit of Capacity During Cooling Operation {m3/s-W} + SupplyAirFlowRate, !- Supply Air Flow Rate Method During Heating Operation + autosize, !- Supply Air Flow Rate During Heating Operation {m3/s} + , !- Supply Air Flow Rate Per Floor Area during Heating Operation {m3/s-m2} + , !- Fraction of Autosized Design Heating Supply Air Flow Rate + , !- Design Supply Air Flow Rate Per Unit of Capacity During Heating Operation {m3/s-W} + None, !- Supply Air Flow Rate Method When No Cooling or Heating is Required + , !- Supply Air Flow Rate When No Cooling or Heating is Required {m3/s} + , !- Supply Air Flow Rate Per Floor Area When No Cooling or Heating is Required {m3/s-m2} + , !- Fraction of Autosized Design Cooling Supply Air Flow Rate When No Cooling or Heating is Required + , !- Fraction of Autosized Design Heating Supply Air Flow Rate When No Cooling or Heating is Required + , !- Design Supply Air Flow Rate Per Unit of Capacity During Cooling Operation When No Cooling or Heating is Required {m3/s-W} + , !- Design Supply Air Flow Rate Per Unit of Capacity During Heating Operation When No Cooling or Heating is Required {m3/s-W} + Yes, !- No Load Supply Air Flow Rate Control Set To Low Speed + 80, !- Maximum Supply Air Temperature {C} + 21, !- Maximum Outdoor Dry-Bulb Temperature for Supplemental Heater Operation {C} + , !- Outdoor Dry-Bulb Temperature Sensor Node Name + 0, !- Ancilliary On-Cycle Electric Power {W} + 0, !- Ancilliary Off-Cycle Electric Power {W} + , !- Design Heat Recovery Water Flow Rate {m3/s} + 80, !- Maximum Temperature for Heat Recovery {C} + , !- Heat Recovery Water Inlet Node Name + , !- Heat Recovery Water Outlet Node Name + ; !- Design Specification Multispeed Object Name + OS:HeatExchanger:FluidToFluid, {fd491069-c3a7-456d-ae6b-203361d31084}, !- Handle Fluid-to-Fluid HX, !- Name @@ -13152,7 +13200,7 @@ OS:Coil:Cooling:DX:MultiSpeed, 0, !- Basin Heater Capacity {W/K} 2, !- Basin Heater Setpoint Temperature {C} , !- Basin Heater Operating Schedule - NaturalGas, !- Fuel Type + Electricity, !- Fuel Type {50738a90-85a3-4703-9820-56af03d8c2e0}, !- Stage 1 {8d55def1-6653-4fd2-a9f9-f1454b277cd2}; !- Stage 2 @@ -13353,7 +13401,7 @@ OS:Coil:Heating:Electric, OS:AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed, {a7721ec5-6942-4f79-8594-cb2cf9d88264}, !- Handle - Multi Speed HP AirToAir, !- Name + Multi Speed DX_Clg Gas_Htg, !- Name {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule , !- Air Inlet Node , !- Air Outlet Node @@ -13385,6 +13433,478 @@ OS:AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed, autosize, !- Speed 3 Supply Air Flow Rate During Cooling Operation {m3/s} autosize; !- Speed 4 Supply Air Flow Rate During Cooling Operation {m3/s} +OS:AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed, + {303ae054-fd55-49c1-9eb0-788d9d9170bb}, !- Handle + Multi Speed HP AirToAir, !- Name + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule + , !- Air Inlet Node + , !- Air Outlet Node + , !- Controlling Zone or Thermostat Location + {18bc2dc3-1126-48b0-8420-8a6d8304086e}, !- Supply Air Fan + DrawThrough, !- Supply Air Fan Placement + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Supply Air Fan Operating Mode Schedule + {6f2b7423-8dae-4fe5-bf4c-3fd223c8d642}, !- Heating Coil + 1, !- DX Heating Coil Sizing Ratio + {a2f8741c-9852-4d40-ae35-b7d5f1c1fadc}, !- Cooling Coil + {bc309939-178c-4d32-b3b9-239f7fcaf2d9}, !- Supplemental Heating Coil + autosize, !- Maximum Supply Air Temperature from Supplemental Heater {C} + 21, !- Maximum Outdoor Dry-Bulb Temperature for Supplemental Heater Operation {C} + 0, !- Auxiliary On-Cycle Electric Power {W} + 0, !- Auxiliary Off-Cycle Electric Power {W} + 0, !- Design Heat Recovery Water Flow Rate {m3/s} + 80, !- Maximum Temperature for Heat Recovery {C} + , !- Heat Recovery Water Inlet Node + , !- Heat Recovery Water Outlet Node + autosize, !- Supply Air Flow Rate When No Cooling or Heating is Needed {m3/s} + 2, !- Number of Speeds for Heating + 2, !- Number of Speeds for Cooling + autosize, !- Speed 1 Supply Air Flow Rate During Heating Operation {m3/s} + autosize, !- Speed 2 Supply Air Flow Rate During Heating Operation {m3/s} + autosize, !- Speed 3 Supply Air Flow Rate During Heating Operation {m3/s} + autosize, !- Speed 4 Supply Air Flow Rate During Heating Operation {m3/s} + autosize, !- Speed 1 Supply Air Flow Rate During Cooling Operation {m3/s} + autosize, !- Speed 2 Supply Air Flow Rate During Cooling Operation {m3/s} + autosize, !- Speed 3 Supply Air Flow Rate During Cooling Operation {m3/s} + autosize; !- Speed 4 Supply Air Flow Rate During Cooling Operation {m3/s} + +OS:Fan:ConstantVolume, + {18bc2dc3-1126-48b0-8420-8a6d8304086e}, !- Handle + Multi Speed HP AirToAir Fan CV, !- Name + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + , !- Fan Total Efficiency + , !- Pressure Rise {Pa} + AutoSize, !- Maximum Flow Rate {m3/s} + , !- Motor Efficiency + , !- Motor In Airstream Fraction + , !- Air Inlet Node Name + , !- Air Outlet Node Name + ; !- End-Use Subcategory + +OS:Coil:Cooling:DX:MultiSpeed, + {a2f8741c-9852-4d40-ae35-b7d5f1c1fadc}, !- Handle + Multi Speed HP AirToAir DX Clg Coil, !- Name + , !- Availability Schedule + , !- Air Inlet Node + , !- Air Outlet Node + , !- Condenser Air Inlet Node + AirCooled, !- Condenser Type + -25, !- Minimum Outdoor Dry-Bulb Temperature for Compressor Operation {C} + , !- Supply Water Storage Tank + , !- Condensate Collection Water Storage Tank + No, !- Apply Part Load Fraction to Speeds Greater than 1 + , !- Apply Latent Degradation to Speeds Greater than 1 + 0, !- Crankcase Heater Capacity {W} + , !- Crankcase Heater Capacity Function of Temperature Curve Name + 10, !- Maximum Outdoor Dry-Bulb Temperature for Crankcase Heater Operation {C} + 0, !- Basin Heater Capacity {W/K} + 2, !- Basin Heater Setpoint Temperature {C} + , !- Basin Heater Operating Schedule + Electricity, !- Fuel Type + {f6b6ceb0-f987-4d07-8388-d055b404d9f6}, !- Stage 1 + {80406cd2-d15b-4ddd-b8c0-ced6646c7b45}; !- Stage 2 + +OS:Coil:Cooling:DX:MultiSpeed:StageData, + {f6b6ceb0-f987-4d07-8388-d055b404d9f6}, !- Handle + Coil Cooling DX Multi Speed Stage Data 3, !- Name + autosize, !- Gross Rated Total Cooling Capacity {W} + autosize, !- Gross Rated Sensible Heat Ratio + 3, !- Gross Rated Cooling COP {W/W} + autosize, !- Rated Air Flow Rate {m3/s} + 773.3, !- Rated Evaporator Fan Power Per Volume Flow Rate 2017 {W/(m3/s)} + 934.4, !- Rated Evaporator Fan Power Per Volume Flow Rate 2023 {W/(m3/s)} + {71bb6022-0caf-4c23-8554-a7202399e94a}, !- Total Cooling Capacity Function of Temperature Curve + {76c82d34-1037-4964-a0c9-4672deaf963e}, !- Total Cooling Capacity Function of Flow Fraction Curve + {7a560774-aead-429d-968e-20f4c4bbab5c}, !- Energy Input Ratio Function of Temperature Curve + {6c4e5ca2-6c07-4e79-80c4-d7be8985e982}, !- Energy Input Ratio Function of Flow Fraction Curve + {e995e1c7-c477-4228-ba6c-78b1e6f45eba}, !- Part Load Fraction Correlation Curve + 0, !- Nominal Time for Condensate Removal to Begin {s} + 0, !- Ratio of Initial Moisture Evaporation Rate and Steady State Latent Capacity {dimensionless} + 0, !- Maximum Cycling Rate {cycles/hr} + 0, !- Latent Capacity Time Constant {s} + 0.5, !- Rated Waste Heat Fraction of Power Input {dimensionless} + {d2fb72d4-b4e1-4c0c-ad81-0b16de4868d3}, !- Waste Heat Function of Temperature Curve + 0.9, !- Evaporative Condenser Effectiveness {dimensionless} + autosize, !- Evaporative Condenser Air Flow Rate {m3/s} + autosize; !- Rated Evaporative Condenser Pump Power Consumption {W} + +OS:Curve:Biquadratic, + {71bb6022-0caf-4c23-8554-a7202399e94a}, !- Handle + Curve Biquadratic 49, !- Name + 0.766956, !- Coefficient1 Constant + 0.0107756, !- Coefficient2 x + -4.14703e-05, !- Coefficient3 x**2 + 0.00134961, !- Coefficient4 y + -0.000261144, !- Coefficient5 y**2 + 0.000457488, !- Coefficient6 x*y + 17, !- Minimum Value of x + 22, !- Maximum Value of x + 13, !- Minimum Value of y + 46; !- Maximum Value of y + +OS:Curve:Quadratic, + {76c82d34-1037-4964-a0c9-4672deaf963e}, !- Handle + Curve Quadratic 62, !- Name + 0.8, !- Coefficient1 Constant + 0.2, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Biquadratic, + {7a560774-aead-429d-968e-20f4c4bbab5c}, !- Handle + Curve Biquadratic 50, !- Name + 0.297145, !- Coefficient1 Constant + 0.0430933, !- Coefficient2 x + -0.000748766, !- Coefficient3 x**2 + 0.00597727, !- Coefficient4 y + 0.000482112, !- Coefficient5 y**2 + -0.000956448, !- Coefficient6 x*y + 17, !- Minimum Value of x + 22, !- Maximum Value of x + 13, !- Minimum Value of y + 46; !- Maximum Value of y + +OS:Curve:Quadratic, + {6c4e5ca2-6c07-4e79-80c4-d7be8985e982}, !- Handle + Curve Quadratic 63, !- Name + 1.156, !- Coefficient1 Constant + -0.1816, !- Coefficient2 x + 0.0256, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Quadratic, + {e995e1c7-c477-4228-ba6c-78b1e6f45eba}, !- Handle + Curve Quadratic 64, !- Name + 0.75, !- Coefficient1 Constant + 0.25, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Biquadratic, + {d2fb72d4-b4e1-4c0c-ad81-0b16de4868d3}, !- Handle + Curve Biquadratic 51, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Coefficient4 y + 0, !- Coefficient5 y**2 + 0, !- Coefficient6 x*y + 0, !- Minimum Value of x + 0, !- Maximum Value of x + 0, !- Minimum Value of y + 0; !- Maximum Value of y + +OS:Coil:Cooling:DX:MultiSpeed:StageData, + {80406cd2-d15b-4ddd-b8c0-ced6646c7b45}, !- Handle + Coil Cooling DX Multi Speed Stage Data 4, !- Name + autosize, !- Gross Rated Total Cooling Capacity {W} + autosize, !- Gross Rated Sensible Heat Ratio + 3, !- Gross Rated Cooling COP {W/W} + autosize, !- Rated Air Flow Rate {m3/s} + 773.3, !- Rated Evaporator Fan Power Per Volume Flow Rate 2017 {W/(m3/s)} + 934.4, !- Rated Evaporator Fan Power Per Volume Flow Rate 2023 {W/(m3/s)} + {71a3c55a-21dd-48bb-afb8-59cf497e4c9d}, !- Total Cooling Capacity Function of Temperature Curve + {b92b4502-1ef7-4efc-9f20-af5d99ea49c5}, !- Total Cooling Capacity Function of Flow Fraction Curve + {9e748d1b-c774-4764-81b7-72c4fe3af97d}, !- Energy Input Ratio Function of Temperature Curve + {7385ba32-fdd3-4c2d-b0a0-32f74cb10e7e}, !- Energy Input Ratio Function of Flow Fraction Curve + {3e6ac138-3ca6-4ad9-88e8-594bcb57db2c}, !- Part Load Fraction Correlation Curve + 0, !- Nominal Time for Condensate Removal to Begin {s} + 0, !- Ratio of Initial Moisture Evaporation Rate and Steady State Latent Capacity {dimensionless} + 0, !- Maximum Cycling Rate {cycles/hr} + 0, !- Latent Capacity Time Constant {s} + 0.5, !- Rated Waste Heat Fraction of Power Input {dimensionless} + {957a78ee-f15d-4dc7-b22d-0eb2a0377deb}, !- Waste Heat Function of Temperature Curve + 0.9, !- Evaporative Condenser Effectiveness {dimensionless} + autosize, !- Evaporative Condenser Air Flow Rate {m3/s} + autosize; !- Rated Evaporative Condenser Pump Power Consumption {W} + +OS:Curve:Biquadratic, + {71a3c55a-21dd-48bb-afb8-59cf497e4c9d}, !- Handle + Curve Biquadratic 52, !- Name + 0.766956, !- Coefficient1 Constant + 0.0107756, !- Coefficient2 x + -4.14703e-05, !- Coefficient3 x**2 + 0.00134961, !- Coefficient4 y + -0.000261144, !- Coefficient5 y**2 + 0.000457488, !- Coefficient6 x*y + 17, !- Minimum Value of x + 22, !- Maximum Value of x + 13, !- Minimum Value of y + 46; !- Maximum Value of y + +OS:Curve:Quadratic, + {b92b4502-1ef7-4efc-9f20-af5d99ea49c5}, !- Handle + Curve Quadratic 65, !- Name + 0.8, !- Coefficient1 Constant + 0.2, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Biquadratic, + {9e748d1b-c774-4764-81b7-72c4fe3af97d}, !- Handle + Curve Biquadratic 53, !- Name + 0.297145, !- Coefficient1 Constant + 0.0430933, !- Coefficient2 x + -0.000748766, !- Coefficient3 x**2 + 0.00597727, !- Coefficient4 y + 0.000482112, !- Coefficient5 y**2 + -0.000956448, !- Coefficient6 x*y + 17, !- Minimum Value of x + 22, !- Maximum Value of x + 13, !- Minimum Value of y + 46; !- Maximum Value of y + +OS:Curve:Quadratic, + {7385ba32-fdd3-4c2d-b0a0-32f74cb10e7e}, !- Handle + Curve Quadratic 66, !- Name + 1.156, !- Coefficient1 Constant + -0.1816, !- Coefficient2 x + 0.0256, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Quadratic, + {3e6ac138-3ca6-4ad9-88e8-594bcb57db2c}, !- Handle + Curve Quadratic 67, !- Name + 0.75, !- Coefficient1 Constant + 0.25, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Biquadratic, + {957a78ee-f15d-4dc7-b22d-0eb2a0377deb}, !- Handle + Curve Biquadratic 54, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Coefficient4 y + 0, !- Coefficient5 y**2 + 0, !- Coefficient6 x*y + 0, !- Minimum Value of x + 0, !- Maximum Value of x + 0, !- Minimum Value of y + 0; !- Maximum Value of y + +OS:Coil:Heating:DX:MultiSpeed, + {6f2b7423-8dae-4fe5-bf4c-3fd223c8d642}, !- Handle + Multi Speed HP AirToAir DX Htg Coil, !- Name + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + , !- Air Inlet Node Name + , !- Air Outlet Node Name + -8, !- Minimum Outdoor Dry-Bulb Temperature for Compressor Operation {C} + -8, !- Outdoor Dry-Bulb Temperature to Turn On Compressor {C} + 0, !- Crankcase Heater Capacity {W} + , !- Crankcase Heater Capacity Function of Temperature Curve Name + 10, !- Maximum Outdoor Dry-Bulb Temperature for Crankcase Heater Operation {C} + {4fa588e1-d220-4fcf-8d9d-8f8cae1abf9d}, !- Defrost Energy Input Ratio Function of Temperature Curve Name + 5, !- Maximum Outdoor Dry-Bulb Temperature for Defrost Operation {C} + ReverseCycle, !- Defrost Strategy + Timed, !- Defrost Control + 0.058333, !- Defrost Time Period Fraction + autosize, !- Resistive Defrost Heater Capacity {W} + No, !- Apply Part Load Fraction to Speeds Greater than 1 + Electricity, !- Fuel Type + 4, !- Region number for Calculating HSPF + {4285e995-bcaf-41a0-a14f-02689a66ee86}, !- Stage 1 + {a25bf692-bd23-49ac-9b7d-b135ffe6e2a4}; !- Stage 2 + +OS:Curve:Biquadratic, + {4fa588e1-d220-4fcf-8d9d-8f8cae1abf9d}, !- Handle + Curve Biquadratic 55, !- Name + 0.297145, !- Coefficient1 Constant + 0.0430933, !- Coefficient2 x + -0.000748766, !- Coefficient3 x**2 + 0.00597727, !- Coefficient4 y + 0.000482112, !- Coefficient5 y**2 + -0.000956448, !- Coefficient6 x*y + -23, !- Minimum Value of x + 29, !- Maximum Value of x + -23, !- Minimum Value of y + 29; !- Maximum Value of y + +OS:Coil:Heating:DX:MultiSpeed:StageData, + {4285e995-bcaf-41a0-a14f-02689a66ee86}, !- Handle + Coil Heating DX Multi Speed Stage Data 1, !- Name + autosize, !- Gross Rated Heating Capacity {W} + 3, !- Gross Rated Heating COP {W/W} + autosize, !- Rated Air Flow Rate {m3/s} + 773.3, !- Rated Supply Air Fan Power Per Volume Flow Rate 2017 {W/(m3/s)} + 934.4, !- Rated Supply Air Fan Power Per Volume Flow Rate 2023 {W/(m3/s)} + {d1da59d1-eafe-4f1d-89a6-edce1fc3004f}, !- Heating Capacity Function of Temperature Curve Name + {9a8de2dc-c26f-4f52-960a-5c788f1d9992}, !- Heating Capacity Function of Flow Fraction Curve Name + {0b321aaf-dcd2-4a82-8ffb-96d31e1187de}, !- Energy Input Ratio Function of Temperature Curve Name + {0cb36c1a-8cb6-45b3-bbeb-4f25d4cec590}, !- Energy Input Ratio Function of Flow Fraction Curve Name + {422dd867-b5d9-4164-b17a-81e1bfcd7f49}, !- Part Load Fraction Correlation Curve Name + 0.2, !- Rated Waste Heat Fraction of Power Input {dimensionless} + {89a37d2a-22bf-4c93-82e4-0baba4583575}; !- Waste Heat Function of Temperature Curve Name + +OS:Curve:Biquadratic, + {d1da59d1-eafe-4f1d-89a6-edce1fc3004f}, !- Handle + Curve Biquadratic 56, !- Name + 0.84077409, !- Coefficient1 Constant + 0.02818929, !- Coefficient2 x + 0.00075381, !- Coefficient3 x**2 + -0.00031819, !- Coefficient4 y + 9.69e-06, !- Coefficient5 y**2 + -0.00011147, !- Coefficient6 x*y + -20, !- Minimum Value of x + 20, !- Maximum Value of x + -20, !- Minimum Value of y + 20; !- Maximum Value of y + +OS:Curve:Quadratic, + {9a8de2dc-c26f-4f52-960a-5c788f1d9992}, !- Handle + Curve Quadratic 68, !- Name + 0.8, !- Coefficient1 Constant + 0.2, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Biquadratic, + {0b321aaf-dcd2-4a82-8ffb-96d31e1187de}, !- Handle + Curve Biquadratic 57, !- Name + 0.97241112, !- Coefficient1 Constant + -0.03066568, !- Coefficient2 x + 0.00074606, !- Coefficient3 x**2 + 0.00792652, !- Coefficient4 y + 0.00022958, !- Coefficient5 y**2 + -0.00040468, !- Coefficient6 x*y + -20, !- Minimum Value of x + 20, !- Maximum Value of x + -20, !- Minimum Value of y + 20; !- Maximum Value of y + +OS:Curve:Quadratic, + {0cb36c1a-8cb6-45b3-bbeb-4f25d4cec590}, !- Handle + Curve Quadratic 69, !- Name + 1.156, !- Coefficient1 Constant + -0.1816, !- Coefficient2 x + 0.0256, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Quadratic, + {422dd867-b5d9-4164-b17a-81e1bfcd7f49}, !- Handle + Curve Quadratic 70, !- Name + 0.75, !- Coefficient1 Constant + 0.25, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Biquadratic, + {89a37d2a-22bf-4c93-82e4-0baba4583575}, !- Handle + Curve Biquadratic 58, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Coefficient4 y + 0, !- Coefficient5 y**2 + 0, !- Coefficient6 x*y + 0, !- Minimum Value of x + 0, !- Maximum Value of x + 0, !- Minimum Value of y + 0; !- Maximum Value of y + +OS:Coil:Heating:DX:MultiSpeed:StageData, + {a25bf692-bd23-49ac-9b7d-b135ffe6e2a4}, !- Handle + Coil Heating DX Multi Speed Stage Data 2, !- Name + autosize, !- Gross Rated Heating Capacity {W} + 3, !- Gross Rated Heating COP {W/W} + autosize, !- Rated Air Flow Rate {m3/s} + 773.3, !- Rated Supply Air Fan Power Per Volume Flow Rate 2017 {W/(m3/s)} + 934.4, !- Rated Supply Air Fan Power Per Volume Flow Rate 2023 {W/(m3/s)} + {a6f3c4cc-ef33-4340-9810-7a53a305f1a7}, !- Heating Capacity Function of Temperature Curve Name + {454fdd98-741b-48c0-a0bc-a7adbace6f3d}, !- Heating Capacity Function of Flow Fraction Curve Name + {37447b60-e746-442e-8ac4-11d3e803c80e}, !- Energy Input Ratio Function of Temperature Curve Name + {2c6cd8fc-f2f4-4bd2-b898-67e43a7a9829}, !- Energy Input Ratio Function of Flow Fraction Curve Name + {e9517e4c-fec8-4d5e-ba54-cc7d3e4b2b36}, !- Part Load Fraction Correlation Curve Name + 0.2, !- Rated Waste Heat Fraction of Power Input {dimensionless} + {9bda2e5e-75cc-4731-b5ac-c53dceda34d1}; !- Waste Heat Function of Temperature Curve Name + +OS:Curve:Biquadratic, + {a6f3c4cc-ef33-4340-9810-7a53a305f1a7}, !- Handle + Curve Biquadratic 59, !- Name + 0.84077409, !- Coefficient1 Constant + 0.02818929, !- Coefficient2 x + 0.00075381, !- Coefficient3 x**2 + -0.00031819, !- Coefficient4 y + 9.69e-06, !- Coefficient5 y**2 + -0.00011147, !- Coefficient6 x*y + -20, !- Minimum Value of x + 20, !- Maximum Value of x + -20, !- Minimum Value of y + 20; !- Maximum Value of y + +OS:Curve:Quadratic, + {454fdd98-741b-48c0-a0bc-a7adbace6f3d}, !- Handle + Curve Quadratic 71, !- Name + 0.8, !- Coefficient1 Constant + 0.2, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Biquadratic, + {37447b60-e746-442e-8ac4-11d3e803c80e}, !- Handle + Curve Biquadratic 60, !- Name + 0.97241112, !- Coefficient1 Constant + -0.03066568, !- Coefficient2 x + 0.00074606, !- Coefficient3 x**2 + 0.00792652, !- Coefficient4 y + 0.00022958, !- Coefficient5 y**2 + -0.00040468, !- Coefficient6 x*y + -20, !- Minimum Value of x + 20, !- Maximum Value of x + -20, !- Minimum Value of y + 20; !- Maximum Value of y + +OS:Curve:Quadratic, + {2c6cd8fc-f2f4-4bd2-b898-67e43a7a9829}, !- Handle + Curve Quadratic 72, !- Name + 1.156, !- Coefficient1 Constant + -0.1816, !- Coefficient2 x + 0.0256, !- Coefficient3 x**2 + 0.5, !- Minimum Value of x + 1.5; !- Maximum Value of x + +OS:Curve:Quadratic, + {e9517e4c-fec8-4d5e-ba54-cc7d3e4b2b36}, !- Handle + Curve Quadratic 73, !- Name + 0.75, !- Coefficient1 Constant + 0.25, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Minimum Value of x + 1; !- Maximum Value of x + +OS:Curve:Biquadratic, + {9bda2e5e-75cc-4731-b5ac-c53dceda34d1}, !- Handle + Curve Biquadratic 61, !- Name + 1, !- Coefficient1 Constant + 0, !- Coefficient2 x + 0, !- Coefficient3 x**2 + 0, !- Coefficient4 y + 0, !- Coefficient5 y**2 + 0, !- Coefficient6 x*y + 0, !- Minimum Value of x + 0, !- Maximum Value of x + 0, !- Minimum Value of y + 0; !- Maximum Value of y + +OS:Coil:Heating:Electric, + {bc309939-178c-4d32-b3b9-239f7fcaf2d9}, !- Handle + Multi Speed HP AirToAir Sup Elec Htg Coil, !- Name + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + , !- Efficiency + , !- Nominal Capacity {W} + , !- Air Inlet Node Name + ; !- Air Outlet Node Name + OS:Coil:Cooling:DX:VariableSpeed, {3ef4dbff-bbcf-4645-93ab-fcd09507f984}, !- Handle Coil Cooling DX Variable Speed - Two Speeds, !- Name From f53bf8095dc4bdd4404a5610c9a1fc46742b5b71 Mon Sep 17 00:00:00 2001 From: Mike Lovejoy <69771412+Ski90Moo@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:47:05 +0300 Subject: [PATCH 06/12] Add hot-gas reheat unitary system (Coil:Heating:Desuperheater) (#891) * feat: add hot-gas reheat unitary system with Coil:Heating:Desuperheater Adds 'Unitary - Single Speed DX cooling - Elec heat - CAV - Hotgas reheat' to the default HVAC library, using CoolReheat dehumidification control with a Coil:Heating:Desuperheater as the reheat coil. The desuperheater reclaims waste heat from the unitary system's own DX cooling coil rather than using resistance reheat, avoiding the extra energy cost of electric reheat. Also fixes AirLoopHVACUnitarySystem cloning (drag-from-library and "copy system"): ModelObject::clone() only remaps parent-child ownership, but CoilHeatingDesuperheater::heatingSource() is a lateral reference to a sibling coil, so it was left unset on the clone. fixupClonedDesuperheaterHeatingSource() re-links the cloned desuperheater coil to the cloned cooling coil. Co-Authored-By: Claude Sonnet 5 * refactor: generalize clone lateral-reference fixup beyond desuperheater ModelObject::clone() clones each child individually and reattaches it via setParent(), so parent-child edges are correctly remapped, but lateral (sibling-to-sibling) object-list references within the cloned subtree are left pointing at the original objects. The previous fix special-cased just CoilHeatingDesuperheater::heatingSource(), but the same bug applies to any lateral reference (e.g. SetpointManager:MixedAir's node fields). Replace it with a generic fixup: build an old-handle -> clone map by walking the original and cloned subtrees in parallel, then use IddObject::objectLists()/WorkspaceObject::getTarget()/setPointer() to rewrite any object-list field in the clone that still points into the original subtree. This fixes the desuperheater case as before, and any future lateral-reference case, without needing a new special case per object type. Co-Authored-By: Claude Sonnet 5 * fix: correct clone fixup for fields clone() clears instead of staling Rename the library template to "Desuperheater reheat" for clarity. Testing the generalized fixup against the real SDK (loading hvac_library.osm and cloning the new UnitarySystem both within the same model and across models) showed remapLateralReferences was a no-op for CoilHeatingDesuperheater::heatingSource(): clone() doesn't leave that field pointing at the stale original object, it clears it outright. The previous implementation only rewrote fields that already had some target set on the clone, so it silently did nothing for this case -- reproduced live in the app as a fatal EnergyPlus error (missing heating_source_name / heating_source_object_type) when simulating the new system. Fixed by reading the field to remap from `original` instead of from the clone, and unconditionally forcing the clone's field to match whenever the original's target was itself part of the cloned subtree. Verified against the SDK directly (Ruby) for both same-model ("copy system") and cross-model (drag-from-library) clone scenarios, and confirmed the simulation now runs cleanly in the rebuilt app. Co-Authored-By: Claude Sonnet 5 * fix: reach loop-branch equipment when fixing up cloned references ParentObject::children() is true ownership (e.g. a UnitarySystem's own fan/coils) but does not include HVACComponents placed on a Loop's supply/demand branches. A UnitarySystem sitting on an AirLoopHVAC's supply branch is only reachable via supplyComponents(), not children(). Cloning a whole loop ("Copy System") therefore never visited the branch equipment at all -- including the desuperheater's heating source inside it -- because buildCloneHandleMap/remapLateralReferences only recursed via children(). Reproduced live: copying an existing (working) desuperheater airloop and reassigning zones still hit the same fatal EnergyPlus error, since the copy's desuperheater was never touched by the fixup. Fixed by walking children(), supplyComponents(), and demandComponents() as independently size-matched groups rather than one combined list -- demand-side counts legitimately differ between original and clone (connected thermal zones are never duplicated by clone(), by design), so that mismatch must not block fixing up the supply side, where the equipment lines up 1:1 with the original. Verified against the SDK directly (Ruby) by reproducing the actual AirLoopHVAC topology (OA system, unitary system, zone, terminal) and confirmed live in the app: both "drag from library" and "Copy System" on a whole airloop now correctly re-wire the desuperheater's heating source and a manually-added SetpointManager:MixedAir's node references. Co-Authored-By: Claude Sonnet 5 * fix: reach outdoor-air/relief branch equipment in clone fixup too Same traversal gap as loop supply/demand branches, one level deeper: equipment placed on an AirLoopHVACOutdoorAirSystem's outdoor-air or relief branch (e.g. an evaporative cooler, or a SetpointManager:MixedAir sitting on one of those nodes) is reachable only via oaComponents()/reliefComponents(), not children() and not the loop's own supplyComponents()/demandComponents() (which only see the OA system as a single opaque object on the main branch). Reproduced with the stock File > Examples > Example Model: its airloop's outdoor air system has an evaporative cooler with a SetpointManager: MixedAir on it. Copying that airloop left the copy's setpoint manager's node fields empty, since the fixup never visited that branch at all. Fixed by adding oaComponents()/reliefComponents() as two more independently size-matched groups in childSubtreeObjectGroups(). Verified against the SDK directly (Ruby, using OpenStudio::Model.exampleModel) -- all 4 of the example model's SetpointManagerMixedAir objects correctly resolve their node references on the clone -- and confirmed live in the rebuilt app. Co-Authored-By: Claude Sonnet 5 * style: fix clang-format indentation in HVACSystemsController.cpp CI flagged the anonymous namespace body as over-indented; clang-format doesn't indent namespace contents under this repo's style. * perf: pass clonedObject by const reference in fixupClonedReferences cppcheck flagged the unnecessary copy -- the parameter is only forwarded into buildCloneHandleMap/remapLateralReferences, both of which already accept it by const reference. * refactor: address review feedback on clone lateral-reference fixup Move fixupClonedReferences and its helpers from HVACSystemsController.cpp into src/utilities/CloneFixup.{hpp,cpp} and add unit tests covering the CoilHeatingDesuperheater::heatingSource() case. Also, per review: merge the duplicated tree-walk in buildCloneHandleMap/remapLateralReferences into a single recursive pass (collectClonedObjectPairs), pass clonedObject by reference instead of copying it down the recursion, and switch the handle map to std::unordered_map (ordering was never relied on). Co-Authored-By: Claude Sonnet 5 * fix: silence cppcheck constParameter finding on fixupClonedReferences clonedObject is only ever forwarded into collectClonedObjectPairs's const-ref parameter here; the actual mutation happens later on the copy stored in the pairs vector, which is safe since ModelObject shares its underlying object via shared_ptr regardless of wrapper constness. Co-Authored-By: Claude Sonnet 5 --------- Co-authored-by: Claude Sonnet 5 --- .../Resources/default/hvac_library.osm | 58 +++++++++ src/openstudio_lib/HVACSystemsController.cpp | 8 ++ src/utilities/CMakeLists.txt | 3 + src/utilities/CloneFixup.cpp | 123 ++++++++++++++++++ src/utilities/CloneFixup.hpp | 29 +++++ src/utilities/test/CloneFixup_GTest.cpp | 83 ++++++++++++ 6 files changed, 304 insertions(+) create mode 100644 src/utilities/CloneFixup.cpp create mode 100644 src/utilities/CloneFixup.hpp create mode 100644 src/utilities/test/CloneFixup_GTest.cpp diff --git a/src/openstudio_app/Resources/default/hvac_library.osm b/src/openstudio_app/Resources/default/hvac_library.osm index 7758f37ac..c928b4305 100644 --- a/src/openstudio_app/Resources/default/hvac_library.osm +++ b/src/openstudio_app/Resources/default/hvac_library.osm @@ -6999,6 +6999,64 @@ OS:AirLoopHVAC:UnitarySystem, , !- Heat Recovery Water Outlet Node Name ; !- Design Specification Multispeed Object Name +OS:Coil:Heating:Desuperheater, + {bdb03443-be0a-4953-8eb4-e6014bbced83}, !- Handle + Desuperheater Reheat Coil, !- Name + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + 0.25, !- Heat Reclaim Recovery Efficiency + , !- Air Inlet Node Name + , !- Air Outlet Node Name + {983b9c20-5a9e-49eb-9770-5c1545cc6b65}, !- Heating Source Name + 0; !- On Cycle Parasitic Electric Load {W} + +OS:AirLoopHVAC:UnitarySystem, + {dbc42aea-40d9-42a6-9016-87cc16813b11}, !- Handle + Unitary - Single Speed DX cooling - Elec heat - CAV - Desuperheater reheat, !- Name + Load, !- Control Type + , !- Controlling Zone or Thermostat Location + CoolReheat, !- Dehumidification Control Type + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Availability Schedule Name + , !- Air Inlet Node Name + , !- Air Outlet Node Name + {0c016d27-85cb-4254-8b85-9a16c4586de5}, !- Supply Fan Name + BlowThrough, !- Fan Placement + {9f54092d-a4a8-41b8-a381-c4c332ecb843}, !- Supply Air Fan Operating Mode Schedule Name + {ca3b3fed-58b0-4f64-85f9-257f45d033a5}, !- Heating Coil Name + 1, !- DX Heating Coil Sizing Ratio + {983b9c20-5a9e-49eb-9770-5c1545cc6b65}, !- Cooling Coil Name + No, !- Use DOAS DX Cooling Coil + 2, !- DOAS DX Cooling Coil Leaving Minimum Air Temperature {C} + LatentOrSensibleLoadControl, !- Latent Load Control + {bdb03443-be0a-4953-8eb4-e6014bbced83}, !- Supplemental Heating Coil Name + SupplyAirFlowRate, !- Supply Air Flow Rate Method During Cooling Operation + autosize, !- Supply Air Flow Rate During Cooling Operation {m3/s} + , !- Supply Air Flow Rate Per Floor Area During Cooling Operation {m3/s-m2} + , !- Fraction of Autosized Design Cooling Supply Air Flow Rate + , !- Design Supply Air Flow Rate Per Unit of Capacity During Cooling Operation {m3/s-W} + SupplyAirFlowRate, !- Supply Air Flow Rate Method During Heating Operation + autosize, !- Supply Air Flow Rate During Heating Operation {m3/s} + , !- Supply Air Flow Rate Per Floor Area during Heating Operation {m3/s-m2} + , !- Fraction of Autosized Design Heating Supply Air Flow Rate + , !- Design Supply Air Flow Rate Per Unit of Capacity During Heating Operation {m3/s-W} + None, !- Supply Air Flow Rate Method When No Cooling or Heating is Required + , !- Supply Air Flow Rate When No Cooling or Heating is Required {m3/s} + , !- Supply Air Flow Rate Per Floor Area When No Cooling or Heating is Required {m3/s-m2} + , !- Fraction of Autosized Design Cooling Supply Air Flow Rate When No Cooling or Heating is Required + , !- Fraction of Autosized Design Heating Supply Air Flow Rate When No Cooling or Heating is Required + , !- Design Supply Air Flow Rate Per Unit of Capacity During Cooling Operation When No Cooling or Heating is Required {m3/s-W} + , !- Design Supply Air Flow Rate Per Unit of Capacity During Heating Operation When No Cooling or Heating is Required {m3/s-W} + Yes, !- No Load Supply Air Flow Rate Control Set To Low Speed + 80, !- Maximum Supply Air Temperature {C} + 21, !- Maximum Outdoor Dry-Bulb Temperature for Supplemental Heater Operation {C} + , !- Outdoor Dry-Bulb Temperature Sensor Node Name + 0, !- Ancilliary On-Cycle Electric Power {W} + 0, !- Ancilliary Off-Cycle Electric Power {W} + , !- Design Heat Recovery Water Flow Rate {m3/s} + 80, !- Maximum Temperature for Heat Recovery {C} + , !- Heat Recovery Water Inlet Node Name + , !- Heat Recovery Water Outlet Node Name + ; !- Design Specification Multispeed Object Name + OS:Fan:OnOff, {e803e540-d848-409c-9bad-0c0dc9c7b50a}, !- Handle Fan On Off 14, !- Name diff --git a/src/openstudio_lib/HVACSystemsController.cpp b/src/openstudio_lib/HVACSystemsController.cpp index 279399050..61a96d05e 100644 --- a/src/openstudio_lib/HVACSystemsController.cpp +++ b/src/openstudio_lib/HVACSystemsController.cpp @@ -22,6 +22,7 @@ #include "HorizontalTabWidget.hpp" #include "MainRightColumnController.hpp" #include "../shared_gui_components/OSViewSwitcher.hpp" +#include "../utilities/CloneFixup.hpp" #include #include @@ -92,6 +93,8 @@ #include #include #include +#include +#include #include #include #include @@ -126,6 +129,8 @@ #include #include +#include + #include #include #include @@ -527,7 +532,9 @@ void HVACLayoutController::addLibraryObjectToModelNode(const OSItemId& itemId, m object = doc->getModelObject(itemId); if (object) { if (!doc->fromModel(itemId)) { + model::ModelObject original = object.get(); object = object->clone(comp.model()); + fixupClonedReferences(original, object.get()); remove = true; } } @@ -893,6 +900,7 @@ void HVACSystemsController::onCopySystemClicked() { auto loop = currentLoop(); if (loop) { auto clone = loop->clone(loop->model()); + fixupClonedReferences(loop.get(), clone); setCurrentHandle(toQString(clone.handle())); } } diff --git a/src/utilities/CMakeLists.txt b/src/utilities/CMakeLists.txt index ff46c78ac..d0b45c13f 100644 --- a/src/utilities/CMakeLists.txt +++ b/src/utilities/CMakeLists.txt @@ -10,6 +10,8 @@ set(${target_name}_src ${CMAKE_CURRENT_BINARY_DIR}/OpenStudioApplicationPathHelpers.cxx RemoteBCLNLR.hpp RemoteBCLNLR.cpp + CloneFixup.hpp + CloneFixup.cpp ) # set up groups of source files for Visual Studio @@ -17,6 +19,7 @@ source_group(${target_name} FILES ${target_name}_src) set(${target_name}_test_src test/OpenStudioApplicationPathHelpers_GTest.cpp + test/CloneFixup_GTest.cpp ) set(${target_name}_depends diff --git a/src/utilities/CloneFixup.cpp b/src/utilities/CloneFixup.cpp new file mode 100644 index 000000000..572469c1e --- /dev/null +++ b/src/utilities/CloneFixup.cpp @@ -0,0 +1,123 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "CloneFixup.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace openstudio { + +namespace { +// ParentObject::children() is true ownership (e.g. a UnitarySystem's fan/coils) but does *not* +// include HVACComponents placed on a Loop's supply/demand branches -- a UnitarySystem sitting on +// an AirLoopHVAC's supply branch is reachable via supplyComponents(), not children(). The same +// gap exists one level deeper: equipment on an AirLoopHVACOutdoorAirSystem's outdoor-air/relief +// branches (e.g. an evaporative cooler, or a SetpointManager:MixedAir sitting on one of those +// nodes) is reachable only via oaComponents()/reliefComponents(), not children() either. Cloning +// a whole loop (the "copy system" toolbar action) needs all of these, or the branch equipment -- +// and anything lateral-referenced from inside it -- is never visited at all. +// +// Returned as separate groups, each matched and recursed into independently: cloning a loop does +// not carry over the connected thermal zones on the demand side, so original vs. clone +// demandComponents() can legitimately have different sizes. Treating everything as one combined +// list would let a benign demand-side mismatch abort recursion into the other groups too, where +// the equipment (and any lateral references inside it) *does* line up 1:1 with the original. +std::vector> childSubtreeObjectGroups(const model::ModelObject& object) { + std::vector> groups; + if (boost::optional loop = object.optionalCast()) { + groups.push_back(loop->supplyComponents()); + groups.push_back(loop->demandComponents()); + } + if (boost::optional oaSystem = object.optionalCast()) { + groups.push_back(oaSystem->oaComponents()); + groups.push_back(oaSystem->reliefComponents()); + } + if (boost::optional parent = object.optionalCast()) { + groups.push_back(parent->children()); + } + return groups; +} + +// Walks `original` and `clone` in parallel -- childSubtreeObjectGroups() order is deterministic +// and preserved by clone() -- appending every matched (original, clone) pair in the subtree, +// including the roots themselves, to `pairs`. A single recursive walk here is shared by both the +// handle-map build and the reference remap below, rather than each re-implementing it. +void collectClonedObjectPairs(const model::ModelObject& original, const model::ModelObject& clone, + std::vector>& pairs) { + pairs.emplace_back(original, clone); + + std::vector> originalGroups = childSubtreeObjectGroups(original); + std::vector> cloneGroups = childSubtreeObjectGroups(clone); + for (size_t g = 0; g < originalGroups.size() && g < cloneGroups.size(); ++g) { + const std::vector& originalChildren = originalGroups[g]; + const std::vector& cloneChildren = cloneGroups[g]; + if (originalChildren.size() == cloneChildren.size()) { + for (size_t i = 0; i < originalChildren.size(); ++i) { + collectClonedObjectPairs(originalChildren[i], cloneChildren[i], pairs); + } + } + } +} + +// For every lateral object-list field on `original` whose target was itself cloned (i.e. has an +// entry in handleMap), forces the corresponding field on `clonedObject` to point at that clone. +// Fields are read from `original`, not from `clonedObject`: clone() doesn't necessarily leave a +// lateral reference pointing at the stale original object -- for CoilHeatingDesuperheater's +// heatingSource() it clears the field outright -- so the only reliable source of "what this +// field is supposed to point at" is the original. +void remapLateralReferences(const model::ModelObject& original, model::ModelObject& clonedObject, + const std::unordered_map>& handleMap) { + IddObject iddObject = original.iddObject(); + for (unsigned index = 0; index < original.numFields(); ++index) { + if (iddObject.objectLists(index).empty()) { + continue; + } + boost::optional originalTarget = original.getTarget(index); + if (!originalTarget) { + continue; + } + auto it = handleMap.find(originalTarget->handle()); + if (it == handleMap.end()) { + continue; + } + boost::optional clonedTarget = clonedObject.getTarget(index); + if (clonedTarget && clonedTarget->handle() == it->second.handle()) { + continue; + } + clonedObject.setPointer(index, it->second.handle()); + } +} +} // namespace + +void fixupClonedReferences(const model::ModelObject& original, const model::ModelObject& clonedObject) { + std::vector> pairs; + collectClonedObjectPairs(original, clonedObject, pairs); + + std::unordered_map> handleMap; + for (const auto& pair : pairs) { + handleMap.emplace(pair.first.handle(), pair.second); + } + + for (auto& pair : pairs) { + remapLateralReferences(pair.first, pair.second, handleMap); + } +} + +} // namespace openstudio diff --git a/src/utilities/CloneFixup.hpp b/src/utilities/CloneFixup.hpp new file mode 100644 index 000000000..3cf30ad02 --- /dev/null +++ b/src/utilities/CloneFixup.hpp @@ -0,0 +1,29 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OSAPP_UTILITIES_CLONEFIXUP_HPP +#define OSAPP_UTILITIES_CLONEFIXUP_HPP + +#include + +namespace openstudio { + +/// ModelObject::clone() clones each child individually and reattaches it via setParent(), so true +/// parent-child edges are correctly remapped to point within the new subtree. But children are +/// cloned one at a time rather than as a batch sharing a single old->new handle table, so any +/// *lateral* object-list reference between two siblings in the cloned subtree (e.g. +/// CoilHeatingDesuperheater::heatingSource() pointing at a sibling cooling coil, or a +/// SetpointManager's node references) is left broken on the clone -- for some field/type +/// combinations still pointing at the original object, for others (empirically, heatingSource()) +/// cleared to empty outright. This fixes that up generically, for any object type, rather than +/// special casing each lateral-reference field as it's discovered. +/// +/// `original` is the subtree that was cloned; `clonedObject` is its clone. Re-links any lateral +/// reference in the clone that still points into (or should point into) the original subtree. +void fixupClonedReferences(const model::ModelObject& original, const model::ModelObject& clonedObject); + +} // namespace openstudio + +#endif // OSAPP_UTILITIES_CLONEFIXUP_HPP diff --git a/src/utilities/test/CloneFixup_GTest.cpp b/src/utilities/test/CloneFixup_GTest.cpp new file mode 100644 index 000000000..b94d55f43 --- /dev/null +++ b/src/utilities/test/CloneFixup_GTest.cpp @@ -0,0 +1,83 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include + +#include "../CloneFixup.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openstudio; +using namespace openstudio::model; + +TEST(CloneFixup, RemapsLateralReferenceOnCloneSubtree) { + Model model; + AirLoopHVAC airLoopHVAC(model); + CoilCoolingDXSingleSpeed coolingCoil(model); + CoilHeatingDesuperheater desuperheaterCoil(model); + ASSERT_TRUE(desuperheaterCoil.setHeatingSource(coolingCoil)); + + Node coolingCoilNode = airLoopHVAC.supplyOutletNode(); + ASSERT_TRUE(coolingCoil.addToNode(coolingCoilNode)); + Node desuperheaterNode = airLoopHVAC.supplyOutletNode(); + ASSERT_TRUE(desuperheaterCoil.addToNode(desuperheaterNode)); + + ModelObject original = airLoopHVAC; + ModelObject clonedObject = airLoopHVAC.clone(model); + fixupClonedReferences(original, clonedObject); + + auto clonedLoop = clonedObject.cast(); + boost::optional clonedDesuperheater; + boost::optional clonedCoolingCoil; + for (const ModelObject& comp : clonedLoop.supplyComponents()) { + if (auto d = comp.optionalCast()) { + clonedDesuperheater = d; + } else if (auto c = comp.optionalCast()) { + clonedCoolingCoil = c; + } + } + ASSERT_TRUE(clonedDesuperheater); + ASSERT_TRUE(clonedCoolingCoil); + + // The clone's heatingSource must point at the *cloned* cooling coil, not the original. + boost::optional clonedHeatingSource = clonedDesuperheater->heatingSource(); + ASSERT_TRUE(clonedHeatingSource); + EXPECT_EQ(clonedHeatingSource->handle(), clonedCoolingCoil->handle()); + EXPECT_NE(clonedHeatingSource->handle(), coolingCoil.handle()); + + // The original must be untouched. + boost::optional originalHeatingSource = desuperheaterCoil.heatingSource(); + ASSERT_TRUE(originalHeatingSource); + EXPECT_EQ(originalHeatingSource->handle(), coolingCoil.handle()); +} + +TEST(CloneFixup, NoLateralReferenceIsANoOp) { + Model model; + AirLoopHVAC airLoopHVAC(model); + CoilCoolingDXSingleSpeed coolingCoil(model); + + Node coolingCoilNode = airLoopHVAC.supplyOutletNode(); + ASSERT_TRUE(coolingCoil.addToNode(coolingCoilNode)); + + ModelObject original = airLoopHVAC; + ModelObject clonedObject = airLoopHVAC.clone(model); + EXPECT_NO_THROW(fixupClonedReferences(original, clonedObject)); + + auto clonedLoop = clonedObject.cast(); + bool foundClonedCoil = false; + for (const ModelObject& comp : clonedLoop.supplyComponents()) { + if (comp.optionalCast()) { + foundClonedCoil = true; + } + } + EXPECT_TRUE(foundClonedCoil); +} From 5417f34ccf7bc34b8fa38820cd58b4c68ee9fc5c Mon Sep 17 00:00:00 2001 From: Mike Lovejoy <69771412+Ski90Moo@users.noreply.github.com> Date: Sun, 26 Jul 2026 18:58:10 +0300 Subject: [PATCH 07/12] feat: add Apply to Selected to Facility Shading tab Construction and Transmittance Schedule columns (#883) Closes #882 Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Dan Macumber --- src/openstudio_lib/FacilityShadingGridView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openstudio_lib/FacilityShadingGridView.cpp b/src/openstudio_lib/FacilityShadingGridView.cpp index e5ef9c88b..8acc77655 100644 --- a/src/openstudio_lib/FacilityShadingGridView.cpp +++ b/src/openstudio_lib/FacilityShadingGridView.cpp @@ -484,7 +484,7 @@ void FacilityShadingGridController::addColumns(const QString& category, std::vec DataSource(allShadingSurfaces, true) // t_source ); } else if (field == tr("Construction Name")) { - addDropZoneColumn(Heading(tr("Construction Name"), true, false), CastNullAdapter(&model::ShadingSurface::construction), + addDropZoneColumn(Heading(tr("Construction Name"), true, true), CastNullAdapter(&model::ShadingSurface::construction), CastNullAdapter(&model::ShadingSurface::setConstruction), boost::optional>(NullAdapter(&model::ShadingSurface::resetConstruction)), boost::optional>(NullAdapter(&model::ShadingSurface::isConstructionDefaulted)), @@ -497,7 +497,7 @@ void FacilityShadingGridController::addColumns(const QString& category, std::vec return t_shadingSurface->setTransmittanceSchedule(copy); }); - addDropZoneColumn(Heading(tr("Transmittance Schedule Name"), true, false), + addDropZoneColumn(Heading(tr("Transmittance Schedule Name"), true, true), CastNullAdapter(&model::ShadingSurface::transmittanceSchedule), setter, boost::optional>( CastNullAdapter(&model::ShadingSurface::resetTransmittanceSchedule)), From 28ad539eac3d0118c24b91063c43c6f2feec3aa3 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 26 Jul 2026 10:56:06 -0600 Subject: [PATCH 08/12] Bump to RC2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6ea949d2..4885eaacc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,7 +274,7 @@ endif() # TODO: Modify the more specific variables as needed to indicate prerelease, etc # Keep in beta in-between release cycles. Set to empty string (or comment out) for official) -set(PROJECT_VERSION_PRERELEASE "rc1") +set(PROJECT_VERSION_PRERELEASE "rc2") # OpenStudio version: Only include Major.Minor.Patch, eg "3.0.0", even if you have a prerelease tag set(OPENSTUDIOAPPLICATION_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") From d8c1854d3136bf4c80fdffa8bbd5722c65a67483 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 26 Jul 2026 10:56:32 -0600 Subject: [PATCH 09/12] More updates nrel -> nlr --- .github/workflows/app_build.yml | 2 +- BUILDING.md | 4 ++-- developer/doc/architecture.md | 8 ++++---- .../doc/libraries/shared_gui_components.md | 2 +- docker/configure.sh | 20 +++++++++---------- src/openstudio_app/OpenStudioApp.rc.in | 2 +- .../BCLMeasureDialog.hpp | 2 +- .../BuildingComponentDialog.hpp | 2 +- src/utilities/RemoteBCLNLR.cpp | 2 +- translations/gui_string_definitions.json | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index de5fe8409..6a9ae7cbf 100644 --- a/.github/workflows/app_build.yml +++ b/.github/workflows/app_build.yml @@ -342,7 +342,7 @@ jobs: echo "::endgroup::" begin_group "Remotes" - conan remote add --force nrel-v2 https://conan.openstudio.net/artifactory/api/conan/conan-v2 + conan remote add --force nlr-v2 https://conan.openstudio.net/artifactory/api/conan/conan-v2 conan remote list echo "::endgroup::" diff --git a/BUILDING.md b/BUILDING.md index 5fb23eecb..645121e11 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,10 +1,10 @@ # Building with conan v2 -Check you have `conan >= 2`, and add the `nrel-v2` remote to grab `ruby`. +Check you have `conan >= 2`, and add the `nlr-v2` remote to grab `ruby`. ```shell conan --version -conan remote add -f nrel-v2 http://conan.openstudio.net/artifactory/api/conan/conan-v2 +conan remote add -f nlr-v2 http://conan.openstudio.net/artifactory/api/conan/conan-v2 ``` ## Install the conan dependencies into a build folder diff --git a/developer/doc/architecture.md b/developer/doc/architecture.md index 193f98519..7fdce2a11 100644 --- a/developer/doc/architecture.md +++ b/developer/doc/architecture.md @@ -19,7 +19,7 @@ ## 1. Project Purpose -The **OpenStudio Application** is a cross-platform (Windows, macOS, Linux) graphical user interface for whole-building energy modeling. It provides a Qt 6 GUI on top of the **[OpenStudio SDK](https://github.com/NREL/OpenStudio)** (NREL), which in turn drives EnergyPlus simulations and Radiance daylighting analysis. +The **OpenStudio Application** is a cross-platform (Windows, macOS, Linux) graphical user interface for whole-building energy modeling. It provides a Qt 6 GUI on top of the **[OpenStudio SDK](https://github.com/NatLabRockies/OpenStudio)** (NLR), which in turn drives EnergyPlus simulations and Radiance daylighting analysis. Users model building envelopes, thermal zones, HVAC systems, loads, schedules, geometry, and OpenStudio Measures through a tab-based interface. The application is maintained by the **OpenStudio Coalition** and is fully open source. @@ -40,7 +40,7 @@ C4Context Person(user, "Energy Modeler", "Uses the GUI to create, configure, and simulate building energy models") System(app, "OpenStudio Application", "Qt 6 GUI for whole-building energy modeling (.osm files)") - System_Ext(bcl, "Building Component Library (BCL)", "Remote library of measures and components (NREL hosted)") + System_Ext(bcl, "Building Component Library (BCL)", "Remote library of measures and components (NLR hosted)") System_Ext(sdk, "OpenStudio SDK", "C++ library providing the model layer, geometry, HVAC objects, workflow execution, Ruby/Python bindings") System_Ext(eplus, "EnergyPlus", "Whole-building energy simulation engine (launched as a subprocess by the SDK)") System_Ext(radiance, "Radiance", "Daylight simulation engine (optional, invoked by SDK workflows)") @@ -144,8 +144,8 @@ The project uses **CMake Presets** with **Conan 2** for reproducible builds acro ```bash # 1. Install Conan 2, CMake ≥3.10.2, Qt 6.5.2 (via aqtinstall), compiler -# 2. Add NREL Conan remote -conan remote add -f nrel-v2 http://conan.openstudio.net/artifactory/api/conan/conan-v2 +# 2. Add NLR Conan remote +conan remote add -f nlr-v2 http://conan.openstudio.net/artifactory/api/conan/conan-v2 # 3. Install dependencies conan install . --output-folder=../OSApp-build-release --build=missing \ diff --git a/developer/doc/libraries/shared_gui_components.md b/developer/doc/libraries/shared_gui_components.md index 704b3c733..b20f8dcc2 100644 --- a/developer/doc/libraries/shared_gui_components.md +++ b/developer/doc/libraries/shared_gui_components.md @@ -12,7 +12,7 @@ Key responsibilities: - **Grid system** — a generic multi-column tabular view for OpenStudio model objects - **Form controls** — typed input widgets for real, integer, boolean, and string model fields - **Measure management** — discovery, update-checking, and execution of OpenStudio Measures -- **BCL integration** — browse and download building components and measures from NREL's BCL +- **BCL integration** — browse and download building components and measures from NLR's BCL - **Common dialogs** — network proxy, progress bars, wait dialogs --- diff --git a/docker/configure.sh b/docker/configure.sh index b41654cec..6c7745764 100644 --- a/docker/configure.sh +++ b/docker/configure.sh @@ -82,8 +82,8 @@ if [ ! -f "${CONAN_HOME}/profiles/default" ]; then sed -i 's/build_type=.*$/build_type=Release/' "${CONAN_HOME}/profiles/default" echo " Profile after edits:" cat "${CONAN_HOME}/profiles/default" - # NREL custom remote (hosts ruby/3.2.2 and other project packages). - conan remote add --force nrel-v2 \ + # NLR custom remote (hosts ruby/3.2.2 and other project packages). + conan remote add --force nlr-v2 \ https://conan.openstudio.net/artifactory/api/conan/conan-v2 echo " Conan profile created." else @@ -91,16 +91,16 @@ else cat "${CONAN_HOME}/profiles/default" fi -# -- Ensure nrel-v2 remote is registered ------------------------------------- -echo " Checking for nrel-v2 remote ..." -if conan remote list 2>/dev/null | grep -q 'nrel-v2'; then - echo " nrel-v2 remote found - ensuring it is enabled ..." - conan remote enable nrel-v2 - conan remote update nrel-v2 \ +# -- Ensure nlr-v2 remote is registered -------------------------------------- +echo " Checking for nlr-v2 remote ..." +if conan remote list 2>/dev/null | grep -q 'nlr-v2'; then + echo " nlr-v2 remote found - ensuring it is enabled ..." + conan remote enable nlr-v2 + conan remote update nlr-v2 \ --url https://conan.openstudio.net/artifactory/api/conan/conan-v2 else - echo " nrel-v2 remote not registered - adding ..." - conan remote add nrel-v2 \ + echo " nlr-v2 remote not registered - adding ..." + conan remote add nlr-v2 \ https://conan.openstudio.net/artifactory/api/conan/conan-v2 fi echo " Active Conan remotes:" diff --git a/src/openstudio_app/OpenStudioApp.rc.in b/src/openstudio_app/OpenStudioApp.rc.in index 95d50701c..a2344e50c 100644 --- a/src/openstudio_app/OpenStudioApp.rc.in +++ b/src/openstudio_app/OpenStudioApp.rc.in @@ -16,7 +16,7 @@ BEGIN VALUE "FileVersion", "${OPENSTUDIOAPPLICATION_VERSION}\0" VALUE "InternalName", "OpenStudioApp\0" VALUE "LegalCopyright", "Copyright (c) 2020-${CURRENT_YEAR}, OpenStudio Coalition and other contributors. All rights reserved..\0" - VALUE "LegalTrademarks", "OpenStudio (TM) is a trademark of NREL\0" + VALUE "LegalTrademarks", "OpenStudio (TM) is a trademark of the National Laboratory of the Rockies\0" VALUE "OriginalFilename", "OpenStudioApp.exe\0" VALUE "ProductName", "OpenStudioApplication\0" VALUE "ProductVersion", "${OPENSTUDIOAPPLICATION_LONG_VERSION}\0" diff --git a/src/shared_gui_components/BCLMeasureDialog.hpp b/src/shared_gui_components/BCLMeasureDialog.hpp index 0257ca028..2ca4cd2c8 100644 --- a/src/shared_gui_components/BCLMeasureDialog.hpp +++ b/src/shared_gui_components/BCLMeasureDialog.hpp @@ -21,7 +21,7 @@ namespace openstudio { /** * BCLMeasureDialog is a modal dialog that lets users search and download OpenStudio Measures from - * the NREL Building Component Library (BCL). It queries the BCL REST API, displays results with + * the NLR Building Component Library (BCL). It queries the BCL REST API, displays results with * taxonomy filtering and free-text search, and downloads selected measures to the local BCL cache. */ class BCLMeasureDialog : public OSDialog diff --git a/src/shared_gui_components/BuildingComponentDialog.hpp b/src/shared_gui_components/BuildingComponentDialog.hpp index 1fd277dbb..449c7bf07 100644 --- a/src/shared_gui_components/BuildingComponentDialog.hpp +++ b/src/shared_gui_components/BuildingComponentDialog.hpp @@ -28,7 +28,7 @@ class Component; /** * BuildingComponentDialog is a modal dialog for browsing and downloading building components - * (constructions, materials, schedules, etc.) from the NREL Building Component Library (BCL). + * (constructions, materials, schedules, etc.) from the NLR Building Component Library (BCL). * Once downloaded, the component is available for drag-and-drop into the relevant model views. * It is analogous to BCLMeasureDialog but targets BCL components rather than measures. */ diff --git a/src/utilities/RemoteBCLNLR.cpp b/src/utilities/RemoteBCLNLR.cpp index 6458b4fc2..931ffa435 100644 --- a/src/utilities/RemoteBCLNLR.cpp +++ b/src/utilities/RemoteBCLNLR.cpp @@ -36,7 +36,7 @@ pugi::xml_node RemoteQueryResponse::root() const { // as it will allow us to change http_client_config (SSL settings etc) in only one place web::http::client::http_client RemoteBCLNLR::getClient(const std::string& url, unsigned timeOutSeconds) { web::http::client::http_client_config config; - // bcl.nrel.gov can be slow to respond to client requests so bump the default of 30 seconds to 60 to account for lengthy response time. + // bcl can be slow to respond to client requests so bump the default of 30 seconds to 60 to account for lengthy response time. // this is timeout is for each send and receive operation on the client and not the entire client session. config.set_timeout(std::chrono::seconds(timeOutSeconds)); config.set_validate_certificates(false); diff --git a/translations/gui_string_definitions.json b/translations/gui_string_definitions.json index 39a3dcc52..295d0cf6a 100644 --- a/translations/gui_string_definitions.json +++ b/translations/gui_string_definitions.json @@ -345,7 +345,7 @@ }, "Online BCL": { "category": "openstudio_specific", - "definition": "The online Building Component Library (BCL) is a web-based repository hosted by NREL where users can search for, download, and manage pre-built energy modeling components (constructions, schedules, Measures, etc.) and add them to their OpenStudio models." + "definition": "The online Building Component Library (BCL) is a web-based repository hosted by NLR where users can search for, download, and manage pre-built energy modeling components (constructions, schedules, Measures, etc.) and add them to their OpenStudio models." }, "Local Library": { "category": "openstudio_specific", From 96bda2470867905483a73f5a26943300d88ee5e6 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 26 Jul 2026 11:35:15 -0600 Subject: [PATCH 10/12] Fix CI actions warnings --- .github/workflows/app_build.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index 6a9ae7cbf..d2b802461 100644 --- a/.github/workflows/app_build.yml +++ b/.github/workflows/app_build.yml @@ -213,6 +213,7 @@ jobs: sudo xcode-select -s "/Applications/Xcode_16.4.app/Contents/Developer/" echo "Untapping unused taps to avoid Homebrew trust warnings" + brew uninstall --force bicep || true brew untap aws/tap azure/bicep || true echo "Using brew to install ninja" @@ -625,7 +626,7 @@ jobs: - name: Archive binary artifacts if: env.IS_FORK_PR != 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 # build/_CPack_Packages/win64/IFW/*.exe # build/_CPack_Packages/Linux/DEB/*.deb # build/_CPack_Packages/Darwin/IFW/*.dmg @@ -635,7 +636,7 @@ jobs: - name: Archive TGZ or ZIP artifacts if: env.IS_FORK_PR != 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}.${{ env.COMPRESSED_EXT }} path: build/${{ matrix.COMPRESSED_PKG_PATH }}/*.${{ env.COMPRESSED_EXT }} @@ -654,7 +655,7 @@ jobs: - name: Upload otool info as artifact if: runner.os == 'macOS' && env.IS_FORK_PR != 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: otool_infos_cpack_${{ matrix.os }}_${{ matrix.arch }} path: build/otool*json @@ -678,7 +679,7 @@ jobs: - name: Archive test results? if: env.IS_FORK_PR != 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}-Test.xml path: build/Testing/**/*.xml @@ -701,7 +702,7 @@ jobs: - name: Archive benchmark results? if: env.IS_FORK_PR != 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}-bench_results.csv path: build/bench_results_*.csv @@ -773,13 +774,13 @@ jobs: path: checkout #- name: Gather Test Package from Artifacts - # uses: actions/download-artifact@v4 + # uses: actions/download-artifact@v5 # with: # name: OpenStudioApplication-${{ needs.build.outputs.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.binary_os }}.${{ matrix.COMPRESSED_EXT }} # path: package - name: Gather Dmg Package from Artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: OpenStudioApplication-${{ needs.build.outputs.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.binary_os }}.${{ matrix.BINARY_EXT }} path: dmg @@ -834,7 +835,7 @@ jobs: hdiutil detach ./temp_mount/ - name: Upload otool info as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: otool_info_dmg_${{ matrix.os }}_${{ matrix.arch }} path: dmg/otool*json From 999e2c7448ba829e7360d432d5ae1c10d8de2348 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 26 Jul 2026 17:06:34 -0600 Subject: [PATCH 11/12] Update action to use current runners --- .github/workflows/manual_cli_test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/manual_cli_test.yml b/.github/workflows/manual_cli_test.yml index f5730ed0a..a66c0585d 100644 --- a/.github/workflows/manual_cli_test.yml +++ b/.github/workflows/manual_cli_test.yml @@ -19,9 +19,9 @@ jobs: # fail-fast: Default is true, switch to false to allow one platform to fail and still run others fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-22.04, windows-2022, macos-13, macos-arm64] + os: [ubuntu-24.04, ubuntu-22.04, windows-2022, macos-15-intel, macos-arm64] include: - - os: ubuntu-20.04 + - os: ubuntu-24.04 SELF_HOSTED: false PLATFORM_NAME: Linux BINARY_EXT: deb @@ -36,12 +36,12 @@ jobs: PLATFORM_NAME: Windows BINARY_EXT: exe COMPRESSED_EXT: zip - - os: macos-13 + - os: macos-15-intel SELF_HOSTED: false PLATFORM_NAME: Darwin BINARY_EXT: dmg COMPRESSED_EXT: tar.gz - MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_DEPLOYMENT_TARGET: 13.0 - os: macos-arm64 SELF_HOSTED: true PLATFORM_NAME: Darwin @@ -51,7 +51,7 @@ jobs: steps: - name: Download binary installer - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 id: downloader with: pattern: OpenStudioApplication-*-${{ matrix.os }}.${{ matrix.COMPRESSED_EXT }} From 4dec040e8c6b11f8ae73827f3f0957f354d7cf26 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sun, 26 Jul 2026 17:10:15 -0600 Subject: [PATCH 12/12] Use github runners --- .github/workflows/manual_cli_test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/manual_cli_test.yml b/.github/workflows/manual_cli_test.yml index a66c0585d..ac779dc45 100644 --- a/.github/workflows/manual_cli_test.yml +++ b/.github/workflows/manual_cli_test.yml @@ -19,7 +19,7 @@ jobs: # fail-fast: Default is true, switch to false to allow one platform to fail and still run others fail-fast: false matrix: - os: [ubuntu-24.04, ubuntu-22.04, windows-2022, macos-15-intel, macos-arm64] + os: [ubuntu-24.04, ubuntu-22.04, windows-2022, macos-15-intel, macos-15] include: - os: ubuntu-24.04 SELF_HOSTED: false @@ -42,12 +42,12 @@ jobs: BINARY_EXT: dmg COMPRESSED_EXT: tar.gz MACOSX_DEPLOYMENT_TARGET: 13.0 - - os: macos-arm64 - SELF_HOSTED: true + - os: macos-15 + SELF_HOSTED: false PLATFORM_NAME: Darwin BINARY_EXT: dmg COMPRESSED_EXT: tar.gz - MACOSX_DEPLOYMENT_TARGET: 12.1 + MACOSX_DEPLOYMENT_TARGET: 13.0 steps: - name: Download binary installer