diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index 0a0bd8bb8..d2b802461 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 @@ -205,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" @@ -232,7 +241,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 @@ -274,8 +283,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 @@ -329,7 +343,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::" @@ -540,15 +554,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 }} \ @@ -605,7 +625,8 @@ jobs: -WaitForCompletion -Force - name: Archive binary artifacts - uses: actions/upload-artifact@v4 + if: env.IS_FORK_PR != 'true' + uses: actions/upload-artifact@v5 # build/_CPack_Packages/win64/IFW/*.exe # build/_CPack_Packages/Linux/DEB/*.deb # build/_CPack_Packages/Darwin/IFW/*.dmg @@ -614,13 +635,14 @@ jobs: path: build/${{ matrix.BINARY_PKG_PATH }}/*.${{ env.BINARY_EXT }} - name: Archive TGZ or ZIP artifacts - uses: actions/upload-artifact@v4 + if: env.IS_FORK_PR != 'true' + 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 }} - 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,8 +654,8 @@ jobs: echo "::endgroup::" - name: Upload otool info as artifact - if: runner.os == 'macOS' - uses: actions/upload-artifact@v4 + if: runner.os == 'macOS' && env.IS_FORK_PR != 'true' + uses: actions/upload-artifact@v5 with: name: otool_infos_cpack_${{ matrix.os }}_${{ matrix.arch }} path: build/otool*json @@ -656,7 +678,8 @@ jobs: $XVFBCMD ctest -j -T test --output-on-failure --no-compress-output -C $BUILD_TYPE || true - name: Archive test results? - uses: actions/upload-artifact@v4 + if: env.IS_FORK_PR != 'true' + uses: actions/upload-artifact@v5 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}-Test.xml path: build/Testing/**/*.xml @@ -678,7 +701,8 @@ jobs: $XVFBCMD Products/SpacesSurfaces_Benchmark --benchmark_out_format=csv --benchmark_out='bench_results_SpacesSurfaces.csv' || true - name: Archive benchmark results? - uses: actions/upload-artifact@v4 + if: env.IS_FORK_PR != 'true' + uses: actions/upload-artifact@v5 with: name: OpenStudioApplication-${{ env.OS_APP_VERSION }}.${{ github.sha }}-${{ matrix.os }}-bench_results.csv path: build/bench_results_*.csv @@ -723,6 +747,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 @@ -749,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 @@ -810,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 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 diff --git a/.github/workflows/manual_cli_test.yml b/.github/workflows/manual_cli_test.yml index f5730ed0a..ac779dc45 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-15] include: - - os: ubuntu-20.04 + - os: ubuntu-24.04 SELF_HOSTED: false PLATFORM_NAME: Linux BINARY_EXT: deb @@ -36,22 +36,22 @@ 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 - - os: macos-arm64 - SELF_HOSTED: true + MACOSX_DEPLOYMENT_TARGET: 13.0 + - 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 - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 id: downloader with: pattern: OpenStudioApplication-*-${{ matrix.os }}.${{ matrix.COMPRESSED_EXT }} 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/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/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}") 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/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)} 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/openstudio_app/Resources/default/hvac_library.osm b/src/openstudio_app/Resources/default/hvac_library.osm index 54c76447f..c928b4305 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 @@ -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 @@ -8394,6 +8452,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 +13258,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 +13459,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 +13491,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 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)), 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/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/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/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/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); +} 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",