Repository: CalebBell/ht Branch: master Commit: 85e0ee686ec9 Files: 100 Total size: 1.2 MB Directory structure: gitextract_tl1dvoty/ ├── .github/ │ └── workflows/ │ ├── build-multiarch.yml │ ├── build.yml │ ├── build_multi_numpy_scipy.yml │ ├── build_third_party_packagers.yml │ ├── publish-pypi.yml │ └── quality.yml ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── AUTHORS ├── Changelog.md ├── Justfile ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── asv.conf.json ├── bench/ │ └── ht function performance comparison.ipynb ├── conftest.py ├── dev/ │ ├── basic_standalone_ht_check.py │ ├── cx_freeze/ │ │ └── cx_freeze_basic_standalone_check_builder.py │ └── prerelease.py ├── docs/ │ ├── conf.py │ ├── ht.air_cooler.rst │ ├── ht.boiling_flow.rst │ ├── ht.boiling_nucleic.rst │ ├── ht.boiling_plate.rst │ ├── ht.condensation.rst │ ├── ht.conduction.rst │ ├── ht.conv_external.rst │ ├── ht.conv_free_enclosed.rst │ ├── ht.conv_free_immersed.rst │ ├── ht.conv_internal.rst │ ├── ht.conv_jacket.rst │ ├── ht.conv_packed_bed.rst │ ├── ht.conv_plate.rst │ ├── ht.conv_supercritical.rst │ ├── ht.conv_tube_bank.rst │ ├── ht.conv_two_phase.rst │ ├── ht.core.rst │ ├── ht.hx.rst │ ├── ht.insulation.rst │ ├── ht.numba.rst │ ├── ht.radiation.rst │ ├── ht.vectorized.rst │ ├── index.rst │ ├── plots/ │ │ └── Nu_external_cylinder.py │ ├── test_documentation.py │ └── tutorial.rst ├── ht/ │ ├── air_cooler.py │ ├── boiling_flow.py │ ├── boiling_nucleic.py │ ├── boiling_plate.py │ ├── condensation.py │ ├── conduction.py │ ├── conv_external.py │ ├── conv_free_enclosed.py │ ├── conv_free_immersed.py │ ├── conv_internal.py │ ├── conv_jacket.py │ ├── conv_packed_bed.py │ ├── conv_plate.py │ ├── conv_supercritical.py │ ├── conv_tube_bank.py │ ├── conv_two_phase.py │ ├── core.py │ ├── data/ │ │ ├── square_C1s_Phadkeb.npy │ │ ├── square_Ns_Phadkeb.npy │ │ ├── triangular_C1s_Phadkeb.npy │ │ └── triangular_Ns_Phadkeb.npy │ ├── hx.py │ ├── insulation.py │ ├── numba.py │ ├── numba_vectorized.py │ ├── py.typed │ ├── radiation.py │ ├── units.py │ └── vectorized.py ├── pyproject.toml ├── requirements_security.txt └── tests/ ├── test_air_cooler.py ├── test_boiling_flow.py ├── test_boiling_nucleic.py ├── test_boiling_plate.py ├── test_condensation.py ├── test_conduction.py ├── test_conv_external.py ├── test_conv_free_enclosed.py ├── test_conv_free_immersed.py ├── test_conv_internal.py ├── test_conv_jacket.py ├── test_conv_packed_bed.py ├── test_conv_plate.py ├── test_conv_supercritical.py ├── test_conv_tube_bank.py ├── test_conv_two_phase.py ├── test_core.py ├── test_hx.py ├── test_numba.py ├── test_radiation.py ├── test_units.py └── test_vectorized.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build-multiarch.yml ================================================ name: Build Multiarch on: push: branches: [release] pull_request: branches: [master, release] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/release' }} jobs: multi-arch-test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: # Disabled: Docker localhost registry connection refused error on armv6/trixie # - arch: armv6 # distro: trixie - arch: armv7 distro: trixie - arch: aarch64 distro: trixie - arch: riscv64 distro: trixie - arch: s390x distro: trixie - arch: ppc64le distro: trixie - arch: armv7 distro: ubuntu_latest - arch: aarch64 distro: ubuntu_latest - arch: s390x distro: ubuntu_latest - arch: ppc64le distro: ubuntu_latest - arch: armv6 distro: alpine_latest - arch: armv7 distro: alpine_latest - arch: aarch64 distro: alpine_latest - arch: riscv64 distro: alpine_latest - arch: s390x distro: alpine_latest - arch: ppc64le distro: alpine_latest steps: - uses: actions/checkout@v4 - name: Install Just uses: extractions/setup-just@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: image: tonistiigi/binfmt:qemu-v8.1.5 - name: Run tests on ${{ matrix.arch }}/${{ matrix.distro }} run: just test-arch ${{ matrix.arch }} ${{ matrix.distro }} ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: branches: [master, release] pull_request: branches: [master, release] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/release' }} jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.13t', 'pypy3.11'] os: [windows-latest, ubuntu-latest, macos-15-intel, macos-latest] architecture: ['x86', 'x64'] exclude: # Only test pypy on Linux - os: windows-latest python-version: pypy3.11 - os: macos-latest python-version: pypy3.11 - os: macos-15-intel python-version: pypy3.11 # no python builds available on macos 32 bit, arm or x64 - os: macos-latest architecture: x86 - os: macos-15-intel architecture: x86 # no python builds available on linux 32 bit - os: ubuntu-latest architecture: x86 # scipy dropped 32 bit windows builds - os: windows-latest architecture: x86 python-version: 3.9 - os: windows-latest architecture: x86 python-version: 3.10 - os: windows-latest architecture: x86 python-version: 3.11 - os: windows-latest architecture: x86 python-version: 3.12 - os: windows-latest architecture: x86 python-version: 3.13 - os: windows-latest architecture: x86 python-version: 3.13t - os: windows-latest architecture: x86 python-version: 3.14 # pandas doesn't have wheels for 3.13t on Windows x64 - os: windows-latest architecture: x64 python-version: 3.13t # These are arm - old versions of Python are not supported - os: macos-latest python-version: 3.9 - os: macos-latest python-version: 3.10 steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} ${{ matrix.architecture }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - name: Install uv if: matrix.python-version != '3.13t' uses: astral-sh/setup-uv@v4 with: enable-cache: true cache-dependency-glob: "pyproject.toml" - name: Install Ubuntu dependencies if: startsWith(runner.os, 'Linux') run: | # Taken from scipy sudo apt-get update sudo apt-get install -y libopenblas-dev libatlas-base-dev liblapack-dev gfortran libgmp-dev libmpfr-dev libsuitesparse-dev ccache libmpc-dev libjpeg-dev zlib1g-dev libtiff-dev libfreetype6-dev liblcms2-dev libwebp-dev - name: Install dependencies run: | python -c "import platform; print(platform.platform()); print(platform.architecture())" if [[ "${{ matrix.python-version }}" == "3.13t" ]]; then # Use pip for 3.13t (free-threading) as uv may not fully support it yet python -m pip install --upgrade pip pip install -e .[test] else uv pip install --system -e .[test] fi shell: bash - name: Add numba if: ${{ !contains(fromJSON('["pypy3.11", "3.13t"]'), matrix.python-version) }} run: | uv pip install --system -e .[numba] - name: Test with pytest run: | pytest . -v --cov-report html --cov=ht --cov-report term-missing -m "not online and not thermo" coveralls || true env: COVERALLS_REPO_TOKEN: ${{ secrets.coveralls }} COVERALLS_PARALLEL: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PYTHON_GIL: ${{ matrix.python-version == '3.13t' && '0' || '' }} - name: Upload coverage HTML report if: always() uses: actions/upload-artifact@v4 with: name: coverage-html-${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.architecture }} path: htmlcov/ finish: needs: build runs-on: ubuntu-latest steps: - name: Coveralls Finished env: COVERALLS_REPO_TOKEN: ${{ secrets.coveralls }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | curl https://coveralls.io/webhook?repo_token=${{ secrets.coveralls }} -d "payload[build_num]=${{ github.sha }}&payload[status]=done" ================================================ FILE: .github/workflows/build_multi_numpy_scipy.yml ================================================ name: Build-Test-Multi-Scipy-Numpy on: push: branches: [release] pull_request: branches: [master, release] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/release' }} jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: numpy: ['2.0.1'] #['1.16.5', '1.18.5', '1.20.3', '1.22.4', '1.24.4', '1.26.4', '2.0.1'] scipy: ['1.14.0'] #['1.7.3', '1.8.1', '1.9.3', '1.10.1', '1.12.0', '1.14.0'] python-version: ['3.10'] #['3.7', '3.8', '3.9', '3.10'] os: [ubuntu-latest] architecture: ['x64'] include: - numpy: '1.24.4' scipy: '1.9.3' python-version: '3.10' - numpy: '1.24.4' scipy: '1.12.0' python-version: '3.9' - numpy: '1.26.4' scipy: '1.10.1' python-version: '3.9' - numpy: '1.26.4' scipy: '1.12.0' python-version: '3.9' - numpy: '1.26.4' scipy: '1.14.0' python-version: '3.10' - numpy: '2.0.1' scipy: '1.14.0' python-version: '3.10' steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v4 with: enable-cache: true cache-dependency-glob: "pyproject.toml" - name: Install Just uses: extractions/setup-just@v2 - name: Run tests with specific Python/NumPy/SciPy versions run: just test-multi-single ${{ matrix.python-version }} ${{ matrix.numpy }} ${{ matrix.scipy }} ================================================ FILE: .github/workflows/build_third_party_packagers.yml ================================================ name: Check Third-Party Packager Compatibility on: push: branches: [release] pull_request: branches: [master, release] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/release' }} jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: ['3.13'] os: [windows-latest, ubuntu-latest, macos-15-intel, macos-latest] packager: [pyinstaller, cxfreeze, nuitka] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "pyproject.toml" - name: Install just uses: extractions/setup-just@v2 # https://stackoverflow.com/questions/69250165/github-action-windows-runner-how-to-remove-all-sh-exe-from-path/69251820#69251820 # https://github.com/actions/runner-images/issues/7253#issuecomment-1500121978 # https://github.com/actions/runner-images/discussions/6049 - name: Remove windows bash if: runner.os == 'Windows' shell: bash run: rm 'C:/Windows/System32/bash.exe' - name: Test ${{ matrix.packager }} if: runner.os != 'Windows' run: | just test-${{ matrix.packager }} ================================================ FILE: .github/workflows/publish-pypi.yml ================================================ name: Publish to PyPI on: release: types: [published] permissions: id-token: write contents: read jobs: test: uses: ./.github/workflows/quality.yml publish: # This job publishes to PyPI when a GitHub release is created with a tag starting with 'v' on the release branch. # # Requirements: # - Repository admin must create a release with a tag starting with 'v' (e.g., v1.2.3) # - The tag must be created on the 'release' branch # - The release branch is protected by rulesets requiring all changes go through PR review # # Security notes: # - The tag and branch checks in this job are soft checks (can be bypassed by modifying workflow) # - Real security enforcement comes from the 'pypi' environment which requires manual approval by org admin # - This provides a final gate before any code is published to PyPI needs: test runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') environment: name: pypi steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 # Need full history to check branch ancestry - name: Check if tag is on release branch run: | if ! git branch -r --contains ${{ github.ref }} | grep -q 'origin/release'; then echo "Error: Tag is not on release branch" exit 1 fi echo "Tag verified to be on release branch" - name: Download distributions uses: actions/download-artifact@v4 with: name: distributions path: dist/ - name: Upload to PyPI uses: pypa/gh-action-pypi-publish@release/v1 ================================================ FILE: .github/workflows/quality.yml ================================================ name: Quality & Validation on: push: branches: [master, release] pull_request: branches: [master, release] workflow_call: outputs: distributions-artifact: description: "Name of the distributions artifact" value: ${{ jobs.quality-validation.outputs.distributions-artifact }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/release' }} jobs: quality-validation: runs-on: ubuntu-latest outputs: distributions-artifact: distributions steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: '3.13' - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "pyproject.toml" - name: Install just uses: extractions/setup-just@v3 - name: Install Pandoc (required for docs) uses: r-lib/actions/setup-pandoc@v2 with: pandoc-version: '3.1.11' - name: Install prek (required for just install) run: uv tool install prek - name: Install all dependencies run: just install # ==================== Pre-commit checks ==================== - name: Run pre-commit (prek) run: just precommit # ==================== Security scanning ==================== - name: Run security scans (pip-audit + bandit) run: just security # ==================== Distribution testing ==================== - name: Build distributions run: just build - name: Check distributions with twine run: just check-dist - name: Test wheel installation run: | WHEEL_TEST_DIR=$(mktemp -d) cd $WHEEL_TEST_DIR uv venv venv WHEEL_FILE=$(ls $GITHUB_WORKSPACE/dist/*.whl) uv pip install --python venv/bin/python "ht[test] @ file://${WHEEL_FILE}" cp -r $GITHUB_WORKSPACE/tests . cp $GITHUB_WORKSPACE/pyproject.toml . venv/bin/pytest tests/ -vv -m "not online and not thermo and not numba" - name: Test sdist installation run: | SDIST_TEST_DIR=$(mktemp -d) cd $SDIST_TEST_DIR uv venv venv SDIST_FILE=$(ls $GITHUB_WORKSPACE/dist/*.tar.gz) uv pip install --python venv/bin/python "ht[test] @ file://${SDIST_FILE}" cp -r $GITHUB_WORKSPACE/tests . cp $GITHUB_WORKSPACE/pyproject.toml . venv/bin/pytest tests/ -vv -m "not online and not thermo and not numba" # ==================== Documentation ==================== - name: Build Sphinx HTML documentation run: just docs # ==================== Upload artifacts ==================== - name: Upload distributions as artifacts uses: actions/upload-artifact@v4 with: name: distributions path: dist/ - name: Upload documentation as artifacts uses: actions/upload-artifact@v4 with: name: documentation-html path: _build/html/ ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. rev: v0.14.0 hooks: # Run the linter. - id: ruff-check args: [--fix] files: ^(ht)/ - repo: https://github.com/abravalheri/validate-pyproject rev: v0.24.1 hooks: - id: validate-pyproject - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-yaml - id: check-toml - id: check-json exclude: ^asv\.conf\.json$ - id: check-xml - id: check-merge-conflict - id: check-case-conflict - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable - id: check-illegal-windows-names - id: debug-statements - repo: https://github.com/hukkin/mdformat rev: 1.0.0 hooks: - id: mdformat ================================================ FILE: .readthedocs.yaml ================================================ # .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" apt_packages: - nodejs # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - method: pip path: . extra_requirements: - docs ================================================ FILE: AUTHORS ================================================ Caleb Bell ================================================ FILE: Changelog.md ================================================ # Changelog ## [Unreleased] ## [1.2.0] - 2025-10-26 ### Added - Project is now PEP 517 compliant and doesn't use deprecated setup.py commands anymore - GitHub Actions workflow for publishing to PyPI using environment protection - `uv` package manager integration across CI workflows for faster dependency resolution - New consolidated `quality.yml` workflow for linting and testing - New `build_third_party_packagers.yml` workflow consolidating cx_Freeze, PyInstaller, Nuitka, and py2exe testing - Pre-commit hooks configuration - Justfile with extensive development automation commands - github actions have been refactored to use this where possible, making them locally debuggable ### Changed - **Breaking**: Dropped Python 3.8 support; minimum Python version is now 3.9 - Migrated from setup.py to pyproject.toml-only configuration (PEP 517) - Consolidated multiarch CI workflows with reduced test matrix for improved performance - Optimized multi-numpy/scipy testing workflow using `uv` - Simplified third-party packager testing into single consolidated workflow - Moved coverage configuration to pyproject.toml - Moved pytest configuration from pytest.ini to pyproject.toml - Moved mypy configuration from mypy.ini to pyproject.toml - Moved Ruff configuration from .ruff.toml to pyproject.toml - Reorganized development requirements into pyproject.toml optional dependencies - Updated fluids dependency to >= 1.2.0 ### Removed - Removed setup.py (replaced by pyproject.toml) - Removed standalone configuration files: pytest.ini, mypy.ini, .ruff.toml - Removed separate workflow files: build_cxfreeze_library.yml, build_nuitka_library.yml, build_py2exe_library.yml, build_pyinstaller_library.yml - Removed pre-commit.yml and security.yml workflows (consolidated into quality.yml) - Removed separate requirements files (test, docs, multiarch) - now in pyproject.toml ### Security - Implemented PyPI publishing workflow with manual approval gate ## [1.1.0] - 2025-10-19 ### Added - Python 3.13 and 3.13t (free-threaded) support with PYTHON_GIL=0 configuration - Pre-commit configuration with Ruff, mdformat, and file validators - New GitHub Actions workflows for pre-commit checks and security scanning - Packaging compatibility workflows for cx_Freeze, PyInstaller, and py2exe - Standalone test scripts and demo builders for verifying packaged distributions - Coverage HTML artifact uploads to all test workflows - Concurrency controls to workflows to cancel redundant builds - Justfile for streamlined development tasks (setup, docs, test, typecheck, lint) - Security scanning with pip-audit and bandit ### Changed - Minimum Python version raised from 3.6 to 3.8 - Updated actions to latest versions (setup-qemu v3, run-on-arch v3) - Updated macOS CI runners (macos-13 → macos-15-intel, added macos-latest for ARM) - Extensive code quality improvements with Ruff linting across entire codebase: - String quote normalization to double quotes - Removed unused imports and variables - Improved code formatting and PEP 8 compliance - Better type hints compatibility - Merged type hints across the codebase with improved accuracy - Updated copyright year to 2025 - Fixed numerous typos across documentation files - Improved Sphinx configuration for Python 3.13 compatibility - Enhanced docstring and markdown formatting - Updated README to reflect Python 3.8+ requirement ### Removed - Dropped Python 3.6 and 3.7 support - Removed obsolete platform-specific exclusions ### Security - Added automated security scanning workflow documented in SECURITY.md ## [1.0.7] - 2024-11-10 ### Changed - Code cleanup and minor optimizations - Fix Issue #54 https://github.com/CalebBell/fluids/issues/54 - Fluids version dependency now >= 1.0.27 ## [1.0.6] - 2024-07-26 ### Changed - Compatibility with NumPy 2.0 and SciPy 1.14 - Fluids version dependency now >= 1.0.26 ## [1.0.5] - 2023-06-04 ### Changed - Code cleanup with ruff (experiment) ## [1.0.4] - 2023-04-23 ### Added - Nothing ### Changed - Internal cleanup - Fix to Nu_plate_Martin correlation (see https://github.com/CalebBell/ht/pull/8) ### Removed - Support for Python before 3.6 - Drop appveyor and Travis CI ### Fixed - Nothing ================================================ FILE: Justfile ================================================ # Use a strict shell for more predictable recipe execution. # The "-c" is crucial: it tells bash to treat the recipe lines as commands. set shell := ["bash", "-euo", "pipefail", "-c"] # --- Variables --- # Define paths to executables within the virtual environment for clarity. # This ensures we always use the tools installed in our project's venv. VENV_PYTHON := ".venv/bin/python" VENV_PYTEST := ".venv/bin/pytest" VENV_MYPY := ".venv/bin/mypy" VENV_RUFF := ".venv/bin/ruff" VENV_PIP_AUDIT := ".venv/bin/pip-audit" VENV_BANDIT := ".venv/bin/bandit" # Cross-platform variables for third-party packager tests VENV_BIN_DIR := if os_family() == "windows" { "Scripts" } else { "bin" } PYTHON_EXE := if os_family() == "windows" { "python.exe" } else { "python" } EXE_SUFFIX := if os_family() == "windows" { ".exe" } else { "" } # --- Main Recipes --- # The default recipe, run when you just type `just`. It lists available commands. default: @just --list ## ⚙️ install: Create a uv virtual environment and install all dependencies. install: @echo ">>> Creating virtual environment in ./.venv..." @uv venv @echo "\n>>> Installing 'ht' in editable mode with dev dependencies..." @uv pip install -e .[dev] @echo "\n>>> Installing prek hooks..." @prek install @echo "\n✅ Environment setup complete! You can now run other commands." ## 📚 docs: Build the Sphinx documentation. docs: @echo ">>> Building Sphinx docs..." # Note: -j auto (parallel build) is faster but less stable, can cause JSON decoding crashes @{{VENV_PYTHON}} -m sphinx -b html -d _build/doctrees docs _build/html @echo "✅ Docs built in _build/html" ## 🧪 test: Run the test suite with pytest. test *ARGS: @echo ">>> Running pytest..." @{{VENV_PYTEST}} -n auto {{ARGS}} ## 📊 test-cov: Run tests with coverage report. test-cov: @echo ">>> Running pytest with coverage..." @{{VENV_PYTEST}} -n auto --cov=ht --cov-report=html --cov-report=term @echo "✅ Coverage report generated in htmlcov/" ## 🧐 typecheck: Check static types with mypy. typecheck: @echo ">>> Running mypy..." @{{VENV_MYPY}} . ## ✨ lint: Check for code style issues and errors with Ruff. lint: @echo ">>> Running Ruff..." @{{VENV_RUFF}} check . ## 🏁 check: Run all checks (linting and type checking). check: lint typecheck ## 🔒 security: Run security scans with pip-audit and bandit. security: @echo ">>> Running pip-audit..." @{{VENV_PIP_AUDIT}} -r requirements_security.txt @echo ">>> Running bandit..." @{{VENV_BANDIT}} -r ht -ll @echo "✅ Security scans complete." ## 🪝 precommit: Run pre-commit hooks on all files. precommit: @echo ">>> Running pre-commit hooks..." @prek run --all-files ## 🔌 hooks-install: Install prek hooks. hooks-install: @echo ">>> Installing prek hooks..." @prek install @echo "✅ Hooks installed." ## 🗑️ hooks-remove: Remove prek hooks. hooks-remove: @echo ">>> Removing prek hooks..." @prek uninstall @echo "✅ Hooks removed." # asv is broken # ## ⚡ bench: Run performance benchmarks. # bench: # @echo ">>> Running benchmarks..." # @asv run ## 📦 build: Build wheel and source distributions. build: @echo ">>> Building distributions..." @{{VENV_PYTHON}} -m build @echo "✅ Distributions built in dist/" ## 🔍 check-dist: Check built distributions with twine. check-dist: @echo ">>> Checking distributions with twine..." @.venv/bin/twine check dist/* @echo "✅ Distributions are valid." ## 🚀 ci: Run all CI checks (lint, typecheck, test). ci: lint typecheck test @echo "✅ All CI checks passed!" ## 🧊 test-cxfreeze: Test cx_Freeze compatibility (build executable and run it). test-cxfreeze py="3.13": @echo ">>> Creating temporary virtual environment with Python {{py}}..." @uv venv .venv-cxfreeze-{{py}} --python {{py}} @echo "\n>>> Installing project and cx_Freeze in temporary environment..." @uv pip install --python .venv-cxfreeze-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} -e .[test] @uv pip install --python .venv-cxfreeze-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} cx_Freeze @echo "\n>>> Building cx_Freeze executable..." @cd dev/cx_freeze && ../../.venv-cxfreeze-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} cx_freeze_basic_standalone_check_builder.py build && cd ../.. @echo "\n>>> Testing executable..." @./dev/cx_freeze/build/exe.*/basic_standalone_ht_check{{EXE_SUFFIX}} @echo "\n>>> Cleaning up temporary environment..." @rm -rf .venv-cxfreeze-{{py}} @echo "✅ cx_Freeze test complete and cleaned up!" ## 🔥 test-nuitka: Test Nuitka compatibility (compile module and import it). test-nuitka py="3.13": @echo ">>> Creating temporary virtual environment with Python {{py}}..." @uv venv .venv-nuitka-{{py}} --python {{py}} @echo "\n>>> Installing project and Nuitka in temporary environment..." @uv pip install --python .venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} -e .[test,numba] @uv pip install --python .venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} nuitka @echo "\n>>> Preparing build directory..." @mkdir -p dev/nuitka/build @cp -r ht dev/nuitka/build/ @echo "\n>>> Building Nuitka module..." @cd dev/nuitka/build && ../../../.venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} -m nuitka --module ht --include-package=ht @echo "\n>>> Removing original ht folder from build directory..." @rm -rf dev/nuitka/build/ht/ht @echo "\n>>> Testing compiled module can be imported..." @cd dev/nuitka/build && ../../../.venv-nuitka-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} -c "import ht; print('Version:', ht.__version__)" @echo "\n>>> Cleaning up temporary environment..." @rm -rf .venv-nuitka-{{py}} @echo "✅ Nuitka test complete and cleaned up!" ## 📦 test-pyinstaller: Test PyInstaller compatibility (build executable and run it). test-pyinstaller py="3.13": @echo ">>> Creating temporary virtual environment with Python {{py}}..." @uv venv .venv-pyinstaller-{{py}} --python {{py}} @echo "\n>>> Installing project and PyInstaller in temporary environment..." @uv pip install --python .venv-pyinstaller-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} .[test] @uv pip install --python .venv-pyinstaller-{{py}}/{{VENV_BIN_DIR}}/{{PYTHON_EXE}} pyinstaller @rm -rf build @echo "\n>>> Preparing build directory..." @mkdir -p dev/pyinstaller/build @echo "\n>>> Building PyInstaller executable..." @.venv-pyinstaller-{{py}}/{{VENV_BIN_DIR}}/pyinstaller{{EXE_SUFFIX}} --onefile --name basic_standalone_ht_check --distpath dev/pyinstaller/build/dist --workpath dev/pyinstaller/build/build --specpath dev/pyinstaller/build dev/basic_standalone_ht_check.py @echo "\n>>> Testing executable..." @./dev/pyinstaller/build/dist/basic_standalone_ht_check{{EXE_SUFFIX}} @echo "\n>>> Cleaning up temporary environment..." @rm -rf .venv-pyinstaller-{{py}} @echo "✅ PyInstaller test complete and cleaned up!" ## 🌍 qemu-setup: Register QEMU interpreters for multi-arch container support. qemu-setup: @command -v podman >/dev/null 2>&1 || { echo "❌ Error: podman is not installed. Please install podman first."; exit 1; } @echo ">>> Registering QEMU interpreters with binfmt_misc..." @podman run --rm --privileged multiarch/qemu-user-static --reset -p yes @echo "✅ QEMU multi-arch support enabled." ## 🎯 prepare-multiarch-image: Build and cache a multiarch image with dependencies (use: just prepare-multiarch-image ). prepare-multiarch-image arch distro="trixie": #!/usr/bin/env bash set -euo pipefail # Check for podman command -v podman >/dev/null 2>&1 || { echo "❌ Error: podman is not installed. Please install podman first."; exit 1; } # Tag for cached image tag="ht-test-{{arch}}-{{distro}}:latest" # Check if image already exists if podman image exists "$tag" 2>/dev/null; then echo "✅ Image $tag already exists, skipping build." exit 0 fi echo ">>> Building cached image for {{arch}} with {{distro}}..." # Map architecture to platform case "{{arch}}" in armv6) platform="linux/arm/v6" ;; armv7) platform="linux/arm/v7" ;; aarch64) platform="linux/arm64" ;; riscv64) platform="linux/riscv64" ;; s390x) platform="linux/s390x" ;; ppc64le) platform="linux/ppc64le" ;; *) echo "Unknown architecture: {{arch}}"; exit 1 ;; esac # Map distro to base image (using slim variants for Debian/Ubuntu) case "{{distro}}" in trixie) image="debian:trixie-slim" ;; ubuntu_latest) image="ubuntu:latest" ;; ubuntu_devel) image="ubuntu:devel" ;; alpine_latest) image="alpine:latest" ;; *) echo "Unknown distro: {{distro}}"; exit 1 ;; esac echo "Platform: $platform, Image: $image" # Determine package manager and install commands if [[ "{{distro}}" == "alpine_latest" ]]; then install_cmd="apk update && apk add bash python3 py3-pip py3-scipy py3-matplotlib py3-numpy py3-pandas" else install_cmd="apt-get update && apt-get install -y liblapack-dev gfortran libgmp-dev libmpfr-dev libsuitesparse-dev ccache libmpc-dev python3 python3-pip python3-scipy python3-matplotlib python3-numpy python3-pandas" fi # Create a temporary Containerfile cat > /tmp/Containerfile.ht.{{arch}}.{{distro}} << EOF FROM $image RUN $install_cmd EOF # Build the image with the specified platform podman build --platform "$platform" -t "$tag" -f /tmp/Containerfile.ht.{{arch}}.{{distro}} # Clean up rm /tmp/Containerfile.ht.{{arch}}.{{distro}} echo "✅ Cached image $tag built successfully!" ## 🔄 prepare-all-multiarch-images: Build all cached images for multiarch testing in parallel. prepare-all-multiarch-images: #!/usr/bin/env bash set -euo pipefail # Check for GNU parallel command -v parallel >/dev/null 2>&1 || { echo "❌ Error: GNU parallel is not installed. Please install it (e.g., apt install parallel)."; exit 1; } echo ">>> Building all cached multiarch images in parallel (this will take a while)..." # Define all arch/distro combinations # riscv64 ubuntu_devel fails often, on github actions with Illegal Instruction combinations=( "armv6 trixie" "armv7 trixie" "aarch64 trixie" "riscv64 trixie" "s390x trixie" "ppc64le trixie" "armv7 ubuntu_latest" "aarch64 ubuntu_latest" "s390x ubuntu_latest" "ppc64le ubuntu_latest" # "riscv64 ubuntu_devel" "armv6 alpine_latest" "armv7 alpine_latest" "aarch64 alpine_latest" "riscv64 alpine_latest" "s390x alpine_latest" "ppc64le alpine_latest" ) # Get number of CPU cores ncores=$(nproc) echo ">>> Using $ncores parallel jobs" # Run all builds in parallel with line-buffered output and keep going on failures failed=0 printf '%s\n' "${combinations[@]}" | \ parallel --line-buffer --keep-order --jobs "$ncores" --colsep ' ' \ 'echo ">>> Starting {1}/{2}" && just prepare-multiarch-image {1} {2} && echo "✅ Completed {1}/{2}" || (echo "❌ Failed: {1}/{2}" && exit 1)' \ || failed=1 echo "" if [ $failed -eq 0 ]; then echo "✅ All cached multiarch images built successfully!" else echo "⚠️ Some images failed to build. Check output above for details." exit 1 fi ## 🏗️ test-arch: Run tests on a specific architecture (use: just test-arch ). ## Note: This uses cached images built with prepare-multiarch-image for faster execution. test-arch arch distro="trixie": #!/usr/bin/env bash set -euo pipefail # Check for podman command -v podman >/dev/null 2>&1 || { echo "❌ Error: podman is not installed. Please install podman first."; exit 1; } echo ">>> Running tests on {{arch}} with {{distro}}..." # Map architecture to platform case "{{arch}}" in armv6) platform="linux/arm/v6" ;; armv7) platform="linux/arm/v7" ;; aarch64) platform="linux/arm64" ;; riscv64) platform="linux/riscv64" ;; s390x) platform="linux/s390x" ;; ppc64le) platform="linux/ppc64le" ;; *) echo "Unknown architecture: {{arch}}"; exit 1 ;; esac # Use cached image image="localhost/ht-test-{{arch}}-{{distro}}:latest" echo "Platform: $platform, Image: $image" # Build image if it doesn't exist if ! podman image exists "$image" 2>/dev/null; then echo ">>> Image $image not found, building it now..." just prepare-multiarch-image {{arch}} {{distro}} fi # Determine pip flags if [[ "{{distro}}" == "alpine_latest" ]]; then pip_flags="--break-system-packages" else pip_flags="--break-system-packages" fi # Run the container with files copied (not mounted) # Note: Removed -it flag for CI compatibility, removed :Z flag for broader compatibility podman run --rm \ --platform "$platform" \ -v "$(pwd):/src:ro" \ "$image" \ bash -c " mkdir -p /workspace && \ cd /src && \ find . -mindepth 1 -maxdepth 1 ! -name '.*' -exec cp -r {} /workspace/ \; && \ cd /workspace && \ python3 -m pip install wheel $pip_flags && \ pip3 install -e .[test-multiarch] $pip_flags && \ python3 -m pytest . -v -m 'not online and not numba' " echo "✅ Tests on {{arch}} with {{distro}} complete!" ## 🌐 test-multiarch: Run tests on all architectures from CI (requires time!). test-multiarch: @echo ">>> Running multi-arch tests (this will take a while)..." @echo "\n=== Debian Trixie ===" @just test-arch armv6 trixie || echo "❌ armv6/trixie failed" @just test-arch armv7 trixie || echo "❌ armv7/trixie failed" @just test-arch aarch64 trixie || echo "❌ aarch64/trixie failed" @just test-arch riscv64 trixie || echo "❌ riscv64/trixie failed" @just test-arch s390x trixie || echo "❌ s390x/trixie failed" @just test-arch ppc64le trixie || echo "❌ ppc64le/trixie failed" @echo "\n=== Ubuntu Latest ===" @just test-arch armv7 ubuntu_latest || echo "❌ armv7/ubuntu_latest failed" @just test-arch aarch64 ubuntu_latest || echo "❌ aarch64/ubuntu_latest failed" @just test-arch s390x ubuntu_latest || echo "❌ s390x/ubuntu_latest failed" @just test-arch ppc64le ubuntu_latest || echo "❌ ppc64le/ubuntu_latest failed" # @echo "\n=== Ubuntu Devel ===" # @just test-arch riscv64 ubuntu_devel || echo "❌ riscv64/ubuntu_devel failed" @echo "\n=== Alpine Latest ===" @just test-arch armv6 alpine_latest || echo "❌ armv6/alpine_latest failed" @just test-arch armv7 alpine_latest || echo "❌ armv7/alpine_latest failed" @just test-arch aarch64 alpine_latest || echo "❌ aarch64/alpine_latest failed" @just test-arch riscv64 alpine_latest || echo "❌ riscv64/alpine_latest failed" @just test-arch s390x alpine_latest || echo "❌ s390x/alpine_latest failed" @just test-arch ppc64le alpine_latest || echo "❌ ppc64le/alpine_latest failed" @echo "\n✅ Multi-arch testing complete!" ## 🧬 test-multi-single: Test with specific Python/NumPy/SciPy versions (e.g., just test-multi-single 3.9 1.26.4 1.12.0). ## Set KEEP_VENV=1 to keep the virtual environment for debugging (e.g., KEEP_VENV=1 just test-multi-single 3.9 1.26.4 1.12.0). test-multi-single py="3.10" numpy="2.0.1" scipy="1.14.0": @echo ">>> Testing Python {{py}}, NumPy {{numpy}}, SciPy {{scipy}}..." @echo ">>> Installing Python {{py}} if needed..." @uv python install {{py}} || true @echo ">>> Creating temporary virtual environment..." @uv venv .venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}} --python {{py}} @echo ">>> Installing dependencies..." @uv pip install --python .venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}}/bin/python -e .[test] @uv pip install --python .venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}}/bin/python "numpy=={{numpy}}" "scipy=={{scipy}}" @echo ">>> Installing numba..." @uv pip install --python .venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}}/bin/python -e .[numba] || echo "⚠️ Numba install failed, continuing..." @echo ">>> Running tests (no coverage)..." @.venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}}/bin/pytest . -m "not online and not numba" @if [ -z "$${KEEP_VENV}" ]; then \ echo ">>> Cleaning up temporary environment..."; \ rm -rf .venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}}; \ else \ echo ">>> Keeping venv .venv-test-python{{py}}-numpy{{numpy}}-scipy{{scipy}} for debugging (KEEP_VENV is set)"; \ fi @echo "✅ Test complete for Python {{py}}, NumPy {{numpy}}, SciPy {{scipy}}!" ## 🧬 test-multi: Run all Python/NumPy/SciPy combinations from CI locally. test-multi: #!/usr/bin/env bash set -euo pipefail # Check for GNU parallel command -v parallel >/dev/null 2>&1 || { echo "❌ Error: GNU parallel is not installed. Please install it (e.g., apt install parallel)."; exit 1; } echo ">>> Running multi-version tests (this will take a while)..." echo ">>> This mirrors the CI matrix from build_multi_numpy_scipy.yml" # Define all Python/NumPy/SciPy combinations combinations=( "3.10 1.24.4 1.9.3" "3.10 1.24.4 1.12.0" "3.9 1.24.4 1.12.0" "3.9 1.26.4 1.10.1" "3.9 1.26.4 1.12.0" "3.10 1.26.4 1.14.0" "3.10 2.0.1 1.14.0" ) # Get number of CPU cores ncores=$(nproc) echo ">>> Using $ncores parallel jobs" echo "" # Run all tests in parallel with line-buffered output and keep going on failures failed=0 printf '%s\n' "${combinations[@]}" | \ parallel --line-buffer --keep-order --jobs "$ncores" --colsep ' ' \ 'echo ">>> Starting Python {1}, NumPy {2}, SciPy {3}" && just test-multi-single {1} {2} {3} && echo "✅ Completed Python {1}, NumPy {2}, SciPy {3}" || (echo "❌ Failed: Python {1}, NumPy {2}, SciPy {3}" && exit 1)' \ || failed=1 echo "" if [ $failed -eq 0 ]; then echo "✅ All multi-version tests passed!" else echo "⚠️ Some tests failed. Check output above for details." exit 1 fi ## 🧹 clean: Remove build artifacts and Python caches. clean: @echo ">>> Cleaning up build artifacts and cache files..." @rm -rf _build .mypy_cache .pytest_cache dist *.egg-info htmlcov prof dev/cx_freeze/build dev/nuitka/build dev/pyinstaller/build .venv-cxfreeze-* .venv-nuitka-* .venv-pyinstaller-* @rm -rf .venv-test-* @rm -f ht.*.so ht.*.pyd @find . -type d -name "__pycache__" -exec rm -rf {} + @echo "✅ Cleanup complete." ## 🐳 clean-multiarch-images: Remove all cached multiarch container images. clean-multiarch-images: #!/usr/bin/env bash set -euo pipefail # Check for podman command -v podman >/dev/null 2>&1 || { echo "❌ Error: podman is not installed. Please install podman first."; exit 1; } echo ">>> Removing cached multiarch container images..." # Find all images matching our naming pattern images=$(podman images --format "{{{{.Repository}}}}:{{{{.Tag}}}}" | grep "^ht-test-" || true) if [ -z "$images" ]; then echo "✅ No multiarch images found to remove." exit 0 fi removed=0 while IFS= read -r img; do echo " Removing $img..." podman rmi "$img" 2>/dev/null || echo " ⚠️ Failed to remove $img" ((removed++)) done <<< "$images" echo "" echo "✅ Removed $removed multiarch image(s)." ## 💣 nuke: Remove the virtual environment and all build artifacts. nuke: clean @echo ">>> Removing all virtual environments..." @rm -rf .venv* @echo "✅ Project completely cleaned." ================================================ FILE: LICENSE.txt ================================================ Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ include LICENSE.txt AUTHORS README.rst pytest.ini recursive-include tests * recursive-include docs * recursive-include _custom_build *.py global-exclude __pycache__ global-exclude *.py[co] global-exclude *.coverage global-exclude *.so global-exclude *.ipynb_checkpoints global-exclude ipynb_checkpoints global-exclude *-checkpoint.ipynb global-exclude *checkpoint* global-exclude .*ipynb_checkpoints* recursive-exclude docs *ipynb_checkpoints ================================================ FILE: README.rst ================================================ ================== Heat Transfer (ht) ================== .. image:: http://img.shields.io/pypi/v/ht.svg?style=flat :target: https://pypi.python.org/pypi/ht :alt: Version_status .. image:: http://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat :target: https://ht.readthedocs.io/en/latest/ :alt: Documentation .. image:: https://github.com/CalebBell/ht/workflows/Build/badge.svg :target: https://github.com/CalebBell/ht/actions :alt: Build_status .. image:: http://img.shields.io/badge/license-MIT-blue.svg?style=flat :target: https://github.com/CalebBell/ht/blob/release/LICENSE.txt :alt: license .. image:: https://img.shields.io/pypi/pyversions/ht.svg? :target: https://pypi.python.org/pypi/ht :alt: Supported_versions .. image:: https://zenodo.org/badge/48963057.svg? :alt: Zendo :target: https://zenodo.org/badge/latestdoi/48963057 .. contents:: What is ht? ----------- ht is open-source software for engineers and technicians working in the fields of chemical or mechanical engineering. It includes modules for various heat transfer functions. Among the tasks this library can be used for are: * Sizing a Shell & Tube heat exchanger using any of the Zukauskas, ESDU 73031, or Bell methods * Calculating pressure drop in a Hairpin heat exchanger * Calculating heat loss of objects, including insulated objects * Calculating heat loss from buried pipe * Performing radiative heat transfer calculations * Conderser and Reboiler rating * Detailed heat exchanger evaluation; finding fouling factors * Heat transfer in packed beds * Sizing a Plate and Frame heat exchanger * Modeling an Air Cooler * Supercritical CO2 or water heat transfer The ht library depends on the SciPy library to provide numerical constants, interpolation, integration, and numerical solving functionality. ht runs on all operating systems which support Python, is quick to install, and is free of charge. ht is designed to be easy to use while still providing powerful functionality. If you need to perform some heat transfer calculations, give ht a try. Installation ------------ Get the latest version of ht from https://pypi.python.org/pypi/ht/ If you have an installation of Python with pip, simple install it with: $ pip install ht Alternatively, if you are using `conda `_ as your package management, you can simply install ht in your environment from `conda-forge `_ channel with: $ conda install -c conda-forge ht To get the git version, run: $ git clone git://github.com/CalebBell/ht.git Documentation ------------- ht's documentation is available on the web: https://ht.readthedocs.io/en/latest/index.html Latest source code ------------------ The latest development version of ht's sources can be obtained at https://github.com/CalebBell/ht Bug reports ----------- To report bugs, please use the ht's Bug Tracker at: https://github.com/CalebBell/ht/issues License information ------------------- ht is MIT licensed. See ``LICENSE.txt`` for information on the terms & conditions for usage of this software, and a DISCLAIMER OF ALL WARRANTIES. Although not required by the ht license, if it is convenient for you, please cite ht if used in your work. Please also consider contributing any changes you make back, such that they may be incorporated into the main library and all of us will benefit from them. Citation -------- To cite ht in publications use:: Caleb Bell and Contributors (2016-2025). ht: Heat transfer component of Chemical Engineering Design Library (ChEDL) https://github.com/CalebBell/ht. ================================================ FILE: asv.conf.json ================================================ { // The version of the config file format. Do not change, unless // you know what you are doing. "version": 1, // The name of the project being benchmarked "project": "ht", // The project's homepage "project_url": "https://github.com/calebbell/ht/", // The URL or local path of the source code repository for the // project being benchmarked "repo": ".", // List of branches to benchmark. If not provided, defaults to "master" // (for git) or "default" (for mercurial). // "branches": ["master"], // for git // "branches": ["default"], // for mercurial // The DVCS being used. If not set, it will be automatically // determined from "repo" by looking at the protocol in the URL // (if remote), or by looking for special directories, such as // ".git" (if local). // "dvcs": "git", // The tool to use to create environments. May be "conda", // "virtualenv" or other value depending on the plugins in use. // If missing or the empty string, the tool will be automatically // determined by looking for tools on the PATH environment // variable. "environment_type": "virtualenv", // timeout in seconds for installing any dependencies in environment // defaults to 10 min "install_timeout": 600, // the base URL to show a commit for the project. "show_commit_url": "http://github.com/calebbell/ht/commit/", // The Pythons you'd like to test against. If not provided, defaults // to the current version of Python used to run `asv`. "pythons": ["2.7", "3.4"], // The matrix of dependencies to test. Each key is the name of a // package (in PyPI) and the values are version numbers. An empty // list or empty string indicates to just test against the default // (latest) version. null indicates that the package is to not be // installed. If the package to be tested is only available from // PyPi, and the 'environment_type' is conda, then you can preface // the package name by 'pip+', and the package will be installed via // pip (with all the conda available packages installed first, // followed by the pip installed packages). // "matrix": { "numpy": [], "scipy": [], }, // Combinations of libraries/python versions can be excluded/included // from the set to test. Each entry is a dictionary containing additional // key-value pairs to include/exclude. // // An exclude entry excludes entries where all values match. The // values are regexps that should match the whole string. // // An include entry adds an environment. Only the packages listed // are installed. The 'python' key is required. The exclude rules // do not apply to includes. // // In addition to package names, the following keys are available: // // - python // Python version, as in the *pythons* variable above. // - environment_type // Environment type, as above. // - sys_platform // Platform, as in sys.platform. Possible values for the common // cases: 'linux2', 'win32', 'cygwin', 'darwin'. // // "exclude": [ // {"python": "3.2", "sys_platform": "win32"}, // skip py3.2 on windows // {"environment_type": "conda", "six": null}, // don't run without six on conda // ], // // "include": [ // // additional env for python2.7 // {"python": "2.7", "numpy": "1.8"}, // // additional env if run on windows+conda // {"platform": "win32", "environment_type": "conda", "python": "2.7", "libpython": ""}, // ], // The directory (relative to the current directory) that benchmarks are // stored in. If not provided, defaults to "benchmarks" "benchmark_dir": "benchmarks", // The directory (relative to the current directory) to cache the Python // environments in. If not provided, defaults to "env" "env_dir": ".asv/env", // The directory (relative to the current directory) that raw benchmark // results are stored in. If not provided, defaults to "results". "results_dir": ".asv/results", // The directory (relative to the current directory) that the html tree // should be written to. If not provided, defaults to "html". "html_dir": ".asv/html", // The number of characters to retain in the commit hashes. // "hash_length": 8, // `asv` will cache wheels of the recent builds in each // environment, making them faster to install next time. This is // number of builds to keep, per environment. // "wheel_cache_size": 0 // The commits after which the regression search in `asv publish` // should start looking for regressions. Dictionary whose keys are // regexps matching to benchmark names, and values corresponding to // the commit (exclusive) after which to start looking for // regressions. The default is to start from the first commit // with results. If the commit is `null`, regression detection is // skipped for the matching benchmark. // // "regressions_first_commits": { // "some_benchmark": "352cdf", // Consider regressions only after this commit // "another_benchmark": null, // Skip regression detection altogether // } // The thresholds for relative change in results, after which `asv // publish` starts reporting regressions. Dictionary of the same // form as in ``regressions_first_commits``, with values // indicating the thresholds. If multiple entries match, the // maximum is taken. If no entry matches, the default is 5%. // // "regressions_thresholds": { // "some_benchmark": 0.01, // Threshold of 1% // "another_benchmark": 0.5, // Threshold of 50% // } } ================================================ FILE: bench/ht function performance comparison.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from ht import *\n", "import matplotlib.pyplot as plt\n", "from math import *\n", "import numpy as np\n", "from scipy.interpolate import *" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.93 µs ± 10 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "380 ns ± 5.37 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Rohsenow(Te=4.9, Cpl=4217., kl=0.680, mul=2.79E-4, sigma=0.0589, Hvap=2.257E6, rhol=957.854, rhog=0.595593, Csf=0.011, n=1.26)*4.9" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.07 µs ± 27.4 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Stephan_Abdelsalam(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, angle=35, correlation='hydrocarbon')" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "331 ns ± 7.01 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Serth_HEDH(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "342 ns ± 22.4 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Nusselt_laminar(Tsat=370, Tw=350, rhog=7.0, rhol=585., kl=0.091, mul=158.9E-6, Hvap=776900, L=0.1)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "425 ns ± 8.24 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Boyko_Kruzhilin(m=100, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=0.85)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "280 ns ± 6.99 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit S_isothermal_pipe_eccentric_to_isothermal_pipe(.1, .4, .05, 10)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "272 ns ± 3.99 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Nu_cylinder_Zukauskas(7992, 0.707, 0.69)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "213 ns ± 4.44 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Nu_cylinder_Whitaker(6071, 0.7)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "243 ns ± 3.85 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "219 ns ± 3.23 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Nu_vertical_cylinder(0.72, 1E7, Method='McAdams, Weiss & Saunders')\n", "%timeit Nu_vertical_cylinder(0.72, 1E7)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "209 ns ± 0.709 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "225 ns ± 1.61 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "326 ns ± 4.33 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Nu_horizontal_cylinder(0.72, 1E7)\n", "%timeit Nu_horizontal_cylinder(0.72, 1E7, Method='Morgan')\n", "%timeit Nu_horizontal_cylinder(0.72, 1E7, Method='Churchill-Chu')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "28.1 ns ± 0.347 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)\n", "5.93 ns ± 0.0134 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)\n" ] } ], "source": [ "%timeit laminar_T_const()\n", "%timeit 3.66" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 µs ± 19.5 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "393 ns ± 3.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "['Churchill-Zajic', 'Petukhov-Kirillov-Popov', 'Gnielinski', 'Bhatti-Shah', 'Dipprey-Sabersky', 'Sandall', 'Webb', 'Friend-Metzner', 'Prandtl', 'von-Karman', 'Gowen-Smith', 'Kawase-Ulbrecht', 'Kawase-De', 'Nunner', 'Dittus-Boelter', 'Sieder-Tate', 'Drexel-McAdams', 'Colburn', 'ESDU', 'Gnielinski smooth low Pr', 'Gnielinski smooth high Pr']\n" ] } ], "source": [ "%timeit Nu_conv_internal(Re=1E5, Pr=1.2, fd=0.0185, eD=1E-3)\n", "%timeit Nu_conv_internal_methods(Re=1E5, Pr=1.2, fd=0.0185, eD=1E-3)\n", "print(Nu_conv_internal_methods(Re=1E5, Pr=1.2, fd=0.0185, eD=1E-3))" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "638 ns ± 12.5 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "868 ns ± 54.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6)\n", "%timeit Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype='radial', isobaric_expansion=0.000303)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "683 ns ± 13 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit Nu_packed_bed_Gnielinski(dp=8E-4, voidage=0.4, vs=1, rho=1E3, mu=1E-3, Pr=0.7)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5.38 µs ± 186 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", "4.01 µs ± 188 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", "4.09 µs ± 112 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n" ] } ], "source": [ "%timeit dP_Kern(m=11., rho=995., mu=0.000803, mu_w=0.000657, DShell=0.584, LSpacing=0.1524, pitch=0.0254, Do=.019, NBaffles=22)\n", "%timeit dP_Zukauskas(Re=13943., n=7, ST=0.0313, SL=0.0343, D=0.0164, rho=1.217, Vmax=12.6)\n", "%timeit dP_Zukauskas(Re=13943., n=7, ST=0.0313, SL=0.0313, D=0.0164, rho=1.217, Vmax=12.6)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "225 ns ± 7.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "246 ns ± 12.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], "source": [ "%timeit LMTD(100., 60., 30., 40.2)\n", "%timeit LMTD(100., 60., 30., 40.2, counterflow=False)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7.61 µs ± 122 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", "18.7 µs ± 391 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", "24.8 µs ± 200 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n" ] } ], "source": [ "%timeit [[Ntubes_Perrys(DBundle=1.184, Ntp=i, Do=.028, angle=j) for i in [1,2,4,6]] for j in [30, 45, 60, 90]]\n", "%timeit [[Ntubes_VDI(DBundle=1.184, Ntp=i, Do=.028, pitch=.036, angle=j) for i in [1,2,4,8]] for j in [30, 45, 60, 90]]\n", "%timeit [Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=i, angle=45.) for i in [1,2,4,6,8]]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "24.9 µs ± 290 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", "223\n" ] } ], "source": [ "from ht.insulation import ASHRAE_k, ASHRAE, materials_dict\n", "%timeit [ASHRAE_k(ID) for ID in ASHRAE]\n", "print(len(ASHRAE))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Bitumen, pure\n", "261 µs ± 1.53 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" ] } ], "source": [ "a = 'Bitumen'\n", "print(nearest_material(a))\n", "%timeit nearest_material(a)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "204 ns ± 1.44 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", "145 ns ± 2.83 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)\n" ] } ], "source": [ "%timeit blackbody_spectral_radiance(800., 4E-6)\n", "%timeit q_rad(.85, 400, 305.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conclusion: From 30 ms to 100 ns, not that wide of a range; only 5 orders of magnitude.\n", "String matching is definitely slow, which I know. Definitely menu-driven selection is prefered.\n", "Some complicated functions take a decently long time to work, also not a surprise.\n", "A better alternative to inter1d and interp2d exists; and is equally easy to use. \n", "As a bonus, it provides a degree of smoothing and more control.\n", "The overhead of dealing with strings is not large, but it may still be worth moving out in favor of dictionary comparisons.\n", "By and large, the library is still quick, for Python anyway." ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 } ================================================ FILE: conftest.py ================================================ import platform import sys # Detect Python implementation and version is_pypy = "PyPy" in sys.version is_graal = "Graal" in sys.version is_free_threaded = hasattr(sys, "_is_gil_enabled") and not sys._is_gil_enabled() ver_tup = tuple(int(x) for x in platform.python_version_tuple()[:2]) is_x86_or_x86_64 = platform.machine().lower() in ("i386", "i686", "x86", "x86_64", "amd64") def pytest_ignore_collect( collection_path, config): """Determine which paths pytest should ignore during test collection.""" # Normalize path to string path = str(collection_path) # Skip virtual environments and ASV benchmark environments if "venv" in path or "site-packages" in path or ".asv" in path: return True # Skip utility and development directories skip_paths = ("cx_freeze", "py2exe", "manual_runner", "make_test_stubs", "plot", "prerelease", "benchmarks", "conf.py", "_custom_build") if any(skip_path in path for skip_path in skip_paths): return True # Skip notebook benchmarks if "ipynb" in path and "bench" in path: return True # PyPy/GraalVM compatibility exclusions if (is_pypy or is_graal) and "test_spa" in path: return True if is_graal and "units" in path: return True # Skip numba and .rst tests for unsupported configurations # Numba requires: CPython 3.9-3.13, x86/x86_64 architecture, GIL-enabled unsupported_for_numba = ( ver_tup < (3, 9) or ver_tup >= (3, 14) or is_pypy or is_graal or is_free_threaded or not is_x86_or_x86_64 ) if unsupported_for_numba: if "numba" in path: return True # Skip .rst tests due to rendering differences and missing NUMBER flag support if ".rst" in path: return True return False def pytest_configure(config): """Configure pytest options for doctest support.""" # Only configure for Python 3 if sys.version[0] != "3": return import pytest pytest_major_version = int(pytest.__version__.split(".")[0]) # Enable doctest modules for pytest >= 6 if pytest_major_version >= 6: config.addinivalue_line("addopts", "--doctest-modules") config.option.doctestmodules = True config.addinivalue_line("doctest_optionflags", "NUMBER") # Always normalize whitespace in doctests config.addinivalue_line("doctest_optionflags", "NORMALIZE_WHITESPACE") ================================================ FILE: dev/basic_standalone_ht_check.py ================================================ import ht from ht import * import numpy as np import scipy.integrate import scipy.interpolate import scipy.spatial import scipy.special import scipy.optimize def check_close(a, b, rtol=1e-7, atol=0): np.all(np.abs(a - b) <= (atol + rtol * np.abs(b))) return True def run_checks(): checks = [] # Check LMTD result = LMTD(Thi=100, Tho=60, Tci=30, Tco=40.2) checks.append(check_close(result, 43.200409294131525)) # Check radiation result = q_rad(emissivity=1, T=400) checks.append(check_close(result, 1451.613952)) # Check insulation material lookup wood = nearest_material('spruce') checks.append(k_material(wood) == 0.09) checks.append(rho_material(wood) == 400.0) return all(checks) if run_checks(): print("ht basic checks passed - NumPy and SciPy used successfully") else: print('Library not OK') exit(1) ================================================ FILE: dev/cx_freeze/cx_freeze_basic_standalone_check_builder.py ================================================ from cx_Freeze import setup, Executable build_exe_options = { "packages": ["numpy", "scipy", "ht", "fluids"], "excludes": ["cairo", "locket", "setproctitle", "bcrypt", "beniget", "concurrent", "curses", "et_xmlfile", "google", "imagesize", "olefile", "pyasn1_modules", "pytest", "tabulate", "tlz", "xxhash", "_pydevd_frame_eval", "astunparse", "backcall", "constantly", "cssselect", "greenlet", "html", "incremental", "iniconfig", "ipywidgets", "matplotlib_inline", "ply", "pydoc_data", "pygtkcompat", "pyximport", "tblib", "typed_ast", "xmlrpc", "yapf", "zope", "asgiref", "blib2to3", "certifi", "cloudpickle", "dbm", "jupyter_core", "kiwisolver", "lz4", "ptyprocess", "PySide2", "snappy", "sortedcontainers", "toml", "tomli", "tomllib", "zoneinfo", "blosc", "ephem", "exceptiongroup", "gast", "http", "jacobi", "lazy_object_proxy", "llvmlite", "mpi4py", "mpl_toolkits", "msgpack", "OpenSSL", "past", "pydevd_plugins", "smmap", "wrapt", "wsgiref", "xml", "argcomplete", "bs4", "executing", "ipython_genutils", "markupsafe", "mdurl", "pure_eval", "pyasn1", "PyQt5", "qtpy", "service_identity", "zstandard", "asttokens", "bytecode", "colorama", "contourpy", "idna", "numexpr", "PyQt6", "soupsieve", "stack_data", "wcwidth", "nacl", "pycparser", "traitlets", "alabaster", "cvxopt", "fastjsonschema", "pexpect", "pluggy", "simplejson", "tkinter", "torchgen", "defusedxml", "monkeytype", "av", "charset_normalizer", "IPython", "opt_einsum", "psutil", "sphinxcontrib", "toolz", "torchvision", "xlrd", "zict", "docutils", "girepository-1.0", "gitdb", "jedi", "jsonschema", "parso", "pyparsing", "wx", "html5lib", "lib2to3", "cffi", "dill", "partd", "tqdm", "babel", "click", "gi", "git", "pydev_ipython", "pyrsistent", "zmq", "nbformat", "odf", "torchaudio", "Cython", "fsspec", "pygments", "requests", "yaml", "django", "invoke", "markdown_it", "black", "graphviz", "jaxlib", "sqlalchemy", "hypothesis", "openpyxl", "attr", "_pydev_bundle", "fontTools", "jinja2", "jupyter_client", "pyglet", "joblib", "twisted", "patsy", "ipykernel", "pvlib", "statsmodels", "tornado", "pythran", "snowballstemmer", "asyncio", "tables", "h5py", "prompt_toolkit", "sphinx", "coverage", "dask", "jax", "setuptools", "numba", "sympy", "chardet", "paramiko", "distributed", "gevent", "rich", "torch", "matplotlib", "pyarrow", "PIL"], "include_files": [] } base = None executables = [ Executable("../basic_standalone_ht_check.py", base=base) ] setup( name="basic_ht_check", version="0.1", description="A simple cx_Freeze script that includes numpy and scipy", options={"build_exe": build_exe_options}, executables=executables ) ================================================ FILE: dev/prerelease.py ================================================ import os import shutil import sys if sys.version_info.major != 3 and sys.version_info.minor != 11: raise ValueError("""This prerelease script will only run on Python 3.11. Some parts of a library change the last few decimals numbers between releases, and other parts only have obsolete dependencies i.e. pint on Python 2. For that reason, while the pytest test suite runs everywhere, the notebooks and doctests only run on one paltform.""") import os import sys from datetime import datetime def set_file_modification_time(filename, mtime): atime = os.stat(filename).st_atime os.utime(filename, times=(atime, mtime.timestamp())) now = datetime.now() main_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) remove_folders = ('__pycache__', '.mypy_cache', '_build', '.cache', '.ipynb_checkpoints') bad_extensions = ('.pyc', '.nbi', '.nbc') paths = [main_dir] for p in paths: for (dirpath, dirnames, filenames) in os.walk(p): for bad_folder in remove_folders: if dirpath.endswith(bad_folder): shutil.rmtree(dirpath) continue for filename in filenames: full_path = os.path.join(dirpath, filename) if not os.path.exists(full_path): continue set_file_modification_time(full_path, now) for bad_extension in bad_extensions: if full_path.endswith(bad_extension): os.remove(full_path) main_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) test_dir = os.path.join(main_dir, 'tests') os.chdir(test_dir) #mod_spec = importlib.util.spec_from_file_location("make_test_stubs", os.path.join(test_dir, "make_test_stubs.py")) #make_test_stubs = importlib.util.module_from_spec(mod_spec) #mod_spec.loader.exec_module(make_test_stubs) import pytest os.chdir(main_dir) pytest.main(["--doctest-glob='*.rst'", "--doctest-modules", "--nbval", "-n", "8", "--dist", "loadscope", "-v"]) ================================================ FILE: docs/conf.py ================================================ # # Heat Transfer documentation build configuration file, created by # sphinx-quickstart on Sat Jan 2 17:15:23 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import time #import sys #from mock import Mock as MagicMock # # #class Mock(MagicMock): # @classmethod # def __getattr__(cls, name): # return Mock() # #MOCK_MODULES = ['scipy', 'scipy.interpolate', 'scipy.constants', 'argparse', #'numpy', 'pandas', 'scipy.optimize', 'fluids', 'costing', 'fluids.friction', #'fluids.piping', 'fluids.friction_factor'] #sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.coverage", #'sphinx.ext.mathjax', "sphinx.ext.viewcode", "sphinx.ext.autosummary", "numpydoc", "IPython.sphinxext.ipython_console_highlighting", "IPython.sphinxext.ipython_directive", "sphinx.ext.intersphinx", "nbsphinx", "matplotlib.sphinxext.plot_directive", "sphinxcontrib.katex", "sphinx_sitemap", "sphinxcontrib.googleanalytics", ] googleanalytics_id = "G-7YTWGWHN84" katex_css_path = \ "https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css" katex_js_path = "katex.min.js" katex_autorender_path = "auto-render.min.js" html_baseurl = "https://ht.readthedocs.io/" sitemap_url_scheme = "{link}" sitemap_filename = "sitemap2.xml" # readthedocs generates its own # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "Heat Transfer" import datetime build_date = datetime.datetime.utcfromtimestamp( int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) ) copyright = f"2016 - {build_date.year}, Caleb Bell and contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. import ht version = ht.__version__ # The full version, including alpha/beta/rc tags. release = ht.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build", "**.ipynb_checkpoints"] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "nature" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Custom CSS files html_css_files = ["custom.css"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "Heat Transferdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ("index", "Heat Transfer.tex", "Heat Transfer Documentation", "Caleb Bell", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ("index", "ht", "Heat Transfer Documentation", ["Caleb Bell"], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ("index", "Heat Transfer", "Heat Transfer Documentation", "Caleb Bell", "Heat Transfer", "One line description of project.", "Miscellaneous"), ] nbsphinx_requirejs_path = "" # fixes katex not working plot_rcparams = {"savefig.bbox": "tight"} plot_apply_rcparams = True # if context option is used numpydoc_xref_param_type = True # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False intersphinx_mapping = {"python": ("https://docs.python.org/3", None), "numpy": ("http://docs.scipy.org/doc/numpy", None), "scipy": ("http://docs.scipy.org/doc/scipy/reference", None), "matplotlib": ("http://matplotlib.org/stable/", None), "thermo": ("https://thermo.readthedocs.io/", None), "chemicals": ("https://chemicals.readthedocs.io/", None), "fluids": ("https://fluids.readthedocs.io/", None)} html_theme = "nature" katex_prerender = True from sphinx.ext.autodoc import between try: import ht.numba except: pass def setup(app): # Register a sphinx.ext.autodoc.between listener to ignore everything # between lines that contain the word IGNORE app.connect("autodoc-process-docstring", between("(^Chemical Engineering Design Library).*|(^SOFTWARE.$).*", exclude=True)) return app ================================================ FILE: docs/ht.air_cooler.rst ================================================ Air cooler sizing and rating (ht.air_cooler) ============================================ .. automodule:: ht.air_cooler :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.boiling_flow.rst ================================================ Flow boiling (ht.boiling_flow) ============================== .. automodule:: ht.boiling_flow :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.boiling_nucleic.rst ================================================ Nucleic boiling and critical heat flux (ht.boiling_nucleic) =========================================================== .. automodule:: ht.boiling_nucleic :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.boiling_plate.rst ================================================ Boiling in plate and frame exchangers (ht.boiling_plate) ======================================================== .. automodule:: ht.boiling_plate :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.condensation.rst ================================================ Condensation (ht.condensation) ============================== .. automodule:: ht.condensation :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conduction.rst ================================================ Conduction and shape factors (ht.conduction) ============================================ .. automodule:: ht.conduction :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_external.rst ================================================ External convection (ht.conv_external) ====================================== .. automodule:: ht.conv_external :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_free_enclosed.rst ================================================ Free convection to enclosed bodies (ht.conv_free_enclosed) ========================================================== .. automodule:: ht.conv_free_enclosed :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_free_immersed.rst ================================================ Free convection to immersed bodies (ht.conv_free_immersed) ========================================================== .. automodule:: ht.conv_free_immersed :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_internal.rst ================================================ Internal convection (ht.conv_internal) ====================================== .. automodule:: ht.conv_internal :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_jacket.rst ================================================ Convection to jacketed vessels (ht.conv_jacket) =============================================== .. automodule:: ht.conv_jacket :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_packed_bed.rst ================================================ Convection to packed beds (ht.conv_packed_bed) ============================================== .. automodule:: ht.conv_packed_bed :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_plate.rst ================================================ Convection to Plate Heat Exchangers (single-phase) (ht.conv_plate) ===================================================================== .. automodule:: ht.conv_plate :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_supercritical.rst ================================================ Convection with supercritical fluids (ht.conv_supercritical) ============================================================ .. automodule:: ht.conv_supercritical :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_tube_bank.rst ================================================ Heat transfer and pressure drop across tube bundles (ht.conv_tube_bank) ======================================================================= .. automodule:: ht.conv_tube_bank :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.conv_two_phase.rst ================================================ Non boiling and non condensing two-phase heat transfer (ht.conv_two_phase) ========================================================================== .. automodule:: ht.conv_two_phase :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.core.rst ================================================ Miscellaneous utilities (ht.core) ================================= .. automodule:: ht.core :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.hx.rst ================================================ Heat exchanger sizing and rating (ht.hx) ======================================== .. automodule:: ht.hx :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.insulation.rst ================================================ Database of insulating and refractory material properties (ht.insulation) ========================================================================= .. automodule:: ht.insulation :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.numba.rst ================================================ Support for Numba (ht.numba) ============================ Basic module which wraps most of ht functions and classes to be compatible with the `Numba `_ dynamic Python compiler. Support for Numba may require the latest version of Numba. Numba is rapidly evolving, and hopefully in the future it will support more of the functionality of ht. Using the numba-accelerated version of `ht` is easy; simply call functions and classes from the ht.numba namespace. The ht.numba module must be imported separately; it is not loaded automatically as part of ht. >>> import ht >>> import ht.numba >>> ht.numba.Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4) 0.55050936040 There is a delay while the code is compiled when using Numba; the speed is not quite free. Most, but not all compilations can be cached to save time in future loadings. It is easy to compare the speed of a function with and without Numba. >>> %timeit ht.numba.Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4) # doctest: +SKIP 1.22 µs ± 41.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) >>> %timeit ht.Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4) # doctest: +SKIP 5.89 µs ± 274 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) Not everything is faster in the numba interface. It is advisable to check that numba is indeed faster for your use case. Functions which take strings as inputs are also known to normally get slower; the numerical stuff is still being sped up but the string handling is slow: >>> %timeit ht.numba.baffle_correction_Bell(0.82, method='spline') # doctest: +SKIP 16.5 µs ± 538 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) >>> %timeit ht.baffle_correction_Bell(0.82, method='spline') # doctest: +SKIP 15.6 µs ± 457 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) Nevertheless, using the function from the numba interface may be preferably, to allow an even larger program to be completely compiled in njit mode. Today, the list of things known not to work is as follows: - :py:func:`~.dP_Zukauskas` (needs some spline work) - :py:func:`~.cylindrical_heat_transfer` (returns dictionaries) - :py:func:`~.effectiveness_NTU_method` (returns dictionaries) - :py:func:`~.P_NTU_method` (returns dictionaries) - :py:func:`~.NTU_from_effectiveness` (does string-to-int conversion) - :py:func:`~.DBundle_min` and :py:func:`~.shell_clearance` (needs work) - :py:func:`~.wall_factor_Nu` and :py:func:`~.wall_factor_fd` (dictionary lookups) - :py:func:`~.solar_spectrum` (external file reading) - :py:func:`~.NTU_from_P_basic` (used to work but broke in 0.57 numba release) - :py:func:`~.NTU_from_P_J` (used to work but broke in 0.57 numba release) - :py:func:`~.NTU_from_P_G` (used to work but broke in 0.57 numba release) - :py:func:`~.NTU_from_P_E` (used to work but broke in 0.57 numba release) - :py:func:`~.NTU_from_P_H` (used to work but broke in 0.57 numba release) - :py:func:`~.NTU_from_P_plate` (used to work but broke in 0.57 numba release) - Everything in :py:mod:`ht.insulation` Numpy Support ------------- Numba also allows ht to provide any of its supported functions as a numpy universal function. Numpy's wonderful broadcasting is implemented, so some arguments can be arrays and some can not. >>> import ht.numba_vectorized >>> import numpy as np >>> ht.numba_vectorized.Nu_Grimison_tube_bank(np.linspace(1e4, 1e5, 4), np.array([.708]), np.array([11]), np.array([.05]), np.array([.05]), np.array([.025])) array([3.39729780e+06, 3.74551216e+07, 9.86950909e+07, 1.83014426e+08]) Unfortunately, keyword-arguments are not supported by Numba. Also default arguments are not presently supported by Numba. Despite these limitations is is here that Numba really shines! Arrays are Numba's strength. Please note this interface is provided, but what works and what doesn't is mostly up to the numba project. This backend is not quite as polished as their normal engine. ================================================ FILE: docs/ht.radiation.rst ================================================ Heat transfer by radiation (ht.radiation) ========================================= .. automodule:: ht.radiation :members: :undoc-members: :show-inheritance: ================================================ FILE: docs/ht.vectorized.rst ================================================ Support for numpy arrays (ht.vectorized) ======================================== Basic module which wraps all ht functions with numpy's vectorize. All other object - dicts, classes, etc - are not wrapped. Supports star imports; so the same objects exported when importing from the main library will be imported from here. >>> from ht.vectorized import * Inputs do not need to be numpy arrays; they can be any iterable: >>> import ht.vectorized >>> ht.vectorized.LMTD([100, 101], 60., 30., 40.2) array([43.20040929, 43.60182765]) ================================================ FILE: docs/index.rst ================================================ ht: Heat Transfer component of Chemical Engineering Design Library (ChEDL) ========================================================================== .. meta:: :google-site-verification: wcmDZ88ikLzq1to6urRDGA6R7oIhzya2sN5hOeV1zZw Introduction ------------ ht is open-source software for engineers and technicians working in the fields of chemical or mechanical engineering. It includes modules for various heat transfer functions. Among the tasks this library can be used for are: * Sizing a Shell & Tube heat exchanger using any of the Zukauskas, ESDU 73031, or Bell methods * Calculating pressure drop in a Hairpin heat exchanger * Calculating heat loss of objects, including insulated objects * Calculating heat loss from buried pipe * Performing radiative heat transfer calculations * Conderser and Reboiler rating * Detailed heat exchanger evaluation; finding fouling factors * Heat transfer in packed beds * Sizing a Plate and Frame heat exchanger * Modeling an Air Cooler * Supercritical CO2 or water heat transfer .. image:: http://img.shields.io/pypi/v/ht.svg?style=flat :target: https://pypi.python.org/pypi/ht :alt: Version_status .. image:: http://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat :target: https://ht.readthedocs.io/en/latest/ :alt: Documentation .. image:: https://github.com/CalebBell/ht/workflows/Build/badge.svg :target: https://github.com/CalebBell/ht/actions :alt: Build_status .. image:: http://img.shields.io/badge/license-MIT-blue.svg?style=flat :target: https://github.com/CalebBell/ht/blob/master/LICENSE.txt :alt: license .. image:: https://img.shields.io/pypi/pyversions/ht.svg? :target: https://pypi.python.org/pypi/ht :alt: Supported_versions .. image:: https://zenodo.org/badge/48963057.svg? :alt: Zendo :target: https://zenodo.org/badge/latestdoi/48963057 Module Contents: .. toctree:: :maxdepth: 1 :caption: Tutorial tutorial .. toctree:: :maxdepth: 1 :caption: API ht.air_cooler ht.boiling_flow ht.boiling_nucleic ht.boiling_plate ht.condensation ht.conduction ht.conv_external ht.conv_free_immersed ht.conv_free_enclosed ht.conv_internal ht.conv_jacket ht.conv_packed_bed ht.conv_plate ht.conv_supercritical ht.conv_tube_bank ht.conv_two_phase ht.core ht.hx ht.insulation ht.numba ht.radiation ht.vectorized Installation ------------ Get the latest version of ht from https://pypi.python.org/pypi/ht/ If you have an installation of Python with pip, simple install it with: $ pip install ht To get the git version, run: $ git clone git://github.com/CalebBell/ht.git Latest source code ------------------ The latest development version of ht's sources can be obtained at https://github.com/CalebBell/ht Bug reports ----------- To report bugs, please use the ht's Bug Tracker at: https://github.com/CalebBell/ht/issues License information ------------------- ht is MIT licensed. See ``LICENSE.txt`` for information on the terms & conditions for usage of this software, and a DISCLAIMER OF ALL WARRANTIES. Although not required by the ht license, if it is convenient for you, please cite ht if used in your work. Please also consider contributing any changes you make back, such that they may be incorporated into the main library and all of us will benefit from them. Citation -------- To cite ht in publications use:: Caleb Bell and Contributors (2016-2025). ht: Heat transfer component of Chemical Engineering Design Library (ChEDL) https://github.com/CalebBell/ht. Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ================================================ FILE: docs/plots/Nu_external_cylinder.py ================================================ import matplotlib.pyplot as plt import numpy as np from ht.conv_external import Nu_external_cylinder, conv_external_cylinder_methods styles = ["--", "-.", "-", ":", ".", ",", "o", "v", "^", "<", ">", "1", "2", "3", "4"] Res = np.logspace(np.log10(10), np.log10(1E6), 1000) Prs = np.array([[.7, 2, 6], [.1, .2, .4], [25, 100, 1000]]) f, axarr = plt.subplots(3, 3) for Pr, axes in zip(Prs.ravel(), axarr.ravel()): for method, style in zip(conv_external_cylinder_methods, styles): Nus = [Nu_external_cylinder(Re=Re, Pr=Pr, Method=method) for Re in Res] axes.semilogx(Res, Nus, label=method) # + ', angle = ' + str(angle) axes.set_title(rf"Pr = {Pr:g}") for item in ([axes.title, axes.xaxis.label, axes.yaxis.label] + axes.get_xticklabels() + axes.get_yticklabels()): item.set_fontsize(6.5) ttl = axes.title.set_position([.5, .98]) plt.subplots_adjust(wspace=.35, hspace=.35) f.suptitle("Comparison of available methods for external convection\n Reynolds Number (x) vs. Nusselt Number (y)") plt.legend(loc="upper center", bbox_to_anchor=(1.5, 2.4)) plt.subplots_adjust(right=0.82, top=.85, bottom=.05) #plt.show() ================================================ FILE: docs/test_documentation.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2021 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import os import pytest """ Tests that run aspects of the documentation should go in here. The only bit included right now are the plots, which should run without an error; no contents checking is performed. """ plots_folder = os.path.join(os.path.dirname(__file__), "plots") plot_files = [i for i in os.listdir(plots_folder) if i.endswith(".py")] print(plot_files) @pytest.mark.parametrize("file", plot_files) def test_documentation_plots(file): import matplotlib as mpl mpl.use("Agg") exec(open(os.path.join(plots_folder, file)).read(), globals()) ================================================ FILE: docs/tutorial.rst ================================================ Tutorial ======== Introduction ------------ ht is the heat transfer component of the Chemical Engineering Design Library (ChEDL). Functions are provided to calculate heat transfer in a variety of situations, generally using dimensionless factors such as Reynolds and Prandtl number, and giving results in terms of dimensionless heat transfer coefficient, the Nusselt number. The 'dimensional' heat transfer coefficient may then be determined .. math:: h = \frac{k\cdot \text{Nu}}{L} Design Philosophy ----------------- Like all libraries, this was developed to scratch my own itches. Since its public release it has been found useful by many others, from students across the world to practicing engineers at some of the world's largest companies. The bulk of this library's API is considered stable; enhancements to functions and classes will still happen, and default methods when using a generic correlation interface may change to newer and more accurate correlations as they are published and reviewed. To the extent possible, correlations are implemented depending on the highest level parameters. The Nu_conv_internal correlation does not accept pipe diameter, velocity, viscosity, density, heat capacity, and thermal conductivity - it accepts Reynolds number and Prandtl number. This makes the API cleaner and encourages modular design. All functions are desiged to accept inputs in base SI units. However, any set of consistent units given to a function will return a consistent result; for instance, a function calculating volume doesn't care if given an input in inches or meters; the output units will be the cube of those given to it. The user is directed to unit conversion libraries such as `pint `_ to perform unit conversions if they prefer not to work in SI units. The standard math library is used in all functions except where special functions from numpy or scipy are necessary. SciPy is used for root finding, interpolation, scientific constants, ode integration, and its many special mathematical functions not present in the standard math library. The only other required library is the `fluids` library, a sister library for fluid dynamics. No other libraries will become required dependencies; anything else is optional. There are two ways to use numpy arrays with ht. Easiest to use is a `vectorized` module, which wraps all of the ht functions with np.vectorize. Instead of importing from ht, the user can import from :doc:`ht.vectorized `: >>> from ht.vectorized import * >>> LMTD([100, 101], 60., 30., 40.2) array([43.20040929, 43.60182765]) It is possible to switch back and forth between the namespaces with a subsequent import: >>> from ht import * The second way is `Numba `_. This optional dependency provides the speed you expect from NumPy arrays - or better. In some cases, much better. The tutorial for using it is at :doc:`ht.numba `, but in general use it the same way but with a different import. >>> from ht.numba_vectorized import * Insulation ---------- Insulating and refractory materials from the VDI Heat Transfer Handbook and the ASHRAE Handbook: Fundamentals have been digitized and are programatically available in ht. Density, heat capacity, and thermal conductivity are available although not all materials have all three. The actual data is stored in a series of dictionaries, building_materials, ASHRAE_board_siding, ASHRAE_flooring, ASHRAE_insulation, ASHRAE_roofing, ASHRAE_plastering, ASHRAE_masonry, ASHRAE_woods, and refractories. A total of 390 different materials are available. Functions have been written to make accessing this data much more convenient. To determine the correct string to look up a material by, one can use the function nearest_material: >>> from ht import * >>> nearest_material('stainless steel') 'Metals, stainless steel' >>> nearest_material('mineral fibre') 'Mineral fiber' Knowing a material's ID, the functions k_material, rho_material, and Cp_material can be used to obtain its properties. >>> wood = nearest_material('spruce') >>> k_material(wood) 0.09 >>> rho_material(wood) 400.0 >>> Cp_material(wood) 1630.0 Materials which are refractories, stored in the dictionary `refractories`, have temperature dependent heat capacity and thermal conductivity between 400 °C and 1200 °C. >>> C = nearest_material('graphite') >>> k_material(C) 67.0 >>> k_material(C, T=800) 62.9851975 The limiting values are returned outside of this range: >>> Cp_material(C, T=8000), Cp_material(C, T=1) (1588.0, 1108.0) Radiation --------- The Stefan-Boltzman law is implemented as `q_rad`. Optionally, a surrounding temperature may be specified as well. If the surrounding temperature is higher than the object, the calculated heat flux in W/m^2 will be negative, indicating the object is picking up heat not losing it. >>> q_rad(emissivity=1, T=400) 1451.613952 >>> q_rad(.85, T=400, T2=305.) 816.7821722650002 >>> q_rad(.85, T=400, T2=5000) # ouch -30122590.815640796 A blackbody's spectral radiance can also be calculated, in units of W/steradian/square metre/metre. This calculation requires the temperature of the object and the wavelength to be considered. >>> blackbody_spectral_radiance(T=800., wavelength=4E-6) 1311694129.7430933 Heat Exchanger Sizing --------------------- There are three popular methods of sizing heat exchangers. The log-mean temperature difference correction factor method, the ε-NTU method, and the P-NTU method. Each of those are cannot size a heat exchanger on their own - they do not care about heat transfer coefficients or area - but they must be used first to determine the thermal conditions of the heat exchanger. Sizing a heat exchanger is a very iterative process, and many designs should be attempted to determine the optimal one based on required performance and cost. The P-NTU method supports the most types of heat exchangers; its form always requires the UA term to be guessed however. LMTD Correction Factor Method ----------------------------- The simplest method, the log-mean temperature difference correction factor method, is as follows: .. math:: Q = UA\Delta T_{lm} F_t Knowing the outlet and inlet temperatures of a heat exchanger and `Q`, one could determine `UA` as follows: >>> dTlm = LMTD(Tci=15, Tco=85, Thi=130, Tho=110) >>> Ft = F_LMTD_Fakheri(Tci=15, Tco=85, Thi=130, Tho=110, shells=1) >>> Q = 1E6 # 1 MW >>> UA = Q/(dTlm*Ft) >>> UA 15833.566307803789 This method requires you to know all four temperatures before UA can be calculated. Fakheri developed a general expression for calculating `Ft`; it is valid for counterflow shell-and-tube exchangers with an even number of tube passes; the number of shell-side passes can be varied. `Ft` is always less than 1, approaching 1 with very high numbers of shells: >>> F_LMTD_Fakheri(Tci=15, Tco=85, Thi=130, Tho=110, shells=10) 0.9994785295070708 No other expressions are available to calculate `Ft` for different heat exchanger geometries; only the TEMA F and E exchanger types are really covered by this expression. However, with results from the other methods, `Ft` can always be back-calculated. Log mean temperature are available for both counterflow (by default) and co-current flow. This calculation does not depend on the units of temperature provided. >>> LMTD(Thi=100, Tho=60, Tci=30, Tco=40.2) 43.200409294131525 >>> LMTD(100, 60, 30, 40.2, counterflow=False) 39.75251118049003 Effectiveness-NTU Method ------------------------ This method uses the formula :math:`Q=\epsilon C_{min}(T_{h,i}-T_{c,i})`. The main complication of this method is calculating effectiveness `epsilon`, which is a function of the mass flows, heat capacities, and UA :math:`\epsilon = f(NTU, C_r)`. The effectiveness-NTU method is implemented in in `effectiveness_from_NTU` and `NTU_from_effectiveness`. The supported heat exchanger types are somewhat limited; they are: * Counterflow (ex. double-pipe) * Parallel (ex. double pipe inefficient configuration) * Shell and tube exchangers with even numbers of tube passes, one or more shells in series (TEMA E (one pass shell) only) * Crossflow, single pass, fluids unmixed * Crossflow, single pass, Cmax mixed, Cmin unmixed * Crossflow, single pass, Cmin mixed, Cmax unmixed * Boiler or condenser To illustrate the method, first the individual methods will be used to determine the outlet temperatures of a heat exchanger. After, the more convenient and flexible wrapper `effectiveness_NTU_method` is shown. Overall case of rating an existing heat exchanger where a known flowrate of steam and oil are contacted in crossflow, with the steam side mixed: >>> U = 275 # W/m^2/K >>> A = 10.82 # m^2 >>> Cp_oil = 1900 # J/kg/K >>> Cp_steam = 1860 # J/kg/K >>> m_steam = 5.2 # kg/s >>> m_oil = 0.725 # kg/s >>> Thi = 130 # °C >>> Tci = 15 # °C >>> Cmin = calc_Cmin(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> Cmax = calc_Cmax(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> Cr = calc_Cr(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> NTU = NTU_from_UA(UA=U*A, Cmin=Cmin) >>> eff = effectiveness_from_NTU(NTU=NTU, Cr=Cr, subtype='crossflow, mixed Cmax') >>> Q = eff*Cmin*(Thi - Tci) >>> Tco = Tci + Q/(m_oil*Cp_oil) >>> Tho = Thi - Q/(m_steam*Cp_steam) >>> Cmin, Cmax, Cr (1377.5, 9672.0, 0.14242142266335814) >>> NTU, eff, Q (2.160072595281307, 0.8312180361425988, 131675.32715043944) >>> Tco, Tho (110.59007415639887, 116.38592564614977) That was not very convenient. The more helpful wrapper `effectiveness_NTU_method` needs only the heat capacities and mass flows of each stream, the type of the heat exchanger, and one combination of the following inputs is required: * Three of the four inlet and outlet stream temperatures * Temperatures for the cold outlet and hot outlet and UA * Temperatures for the cold inlet and hot inlet and UA * Temperatures for the cold inlet and hot outlet and UA * Temperatures for the cold outlet and hot inlet and UA The function returns all calculated parameters for convenience as a dictionary. Solve a heat exchanger to determine UA and effectiveness given the configuration, flows, subtype, the cold inlet/outlet temperatures, and the hot stream inlet temperature. >>> from pprint import pprint >>> pprint(effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, ... subtype='crossflow, mixed Cmax', Tci=15, Tco=85, Thi=130)) {'Cmax': 9672.0, 'Cmin': 2755.0, 'Cr': 0.2848428453267163, 'NTU': 1.1040839095588, 'Q': 192850.0, 'Tci': 15, 'Tco': 85, 'Thi': 130, 'Tho': 110.06100082712986, 'UA': 3041.751170834494, 'effectiveness': 0.6086956521739131} Solve the same heat exchanger with the UA specified, and known inlet temperatures: >>> pprint(effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, ... subtype='crossflow, mixed Cmax', Tci=15, Thi=130, UA=3041.75)) {'Cmax': 9672.0, 'Cmin': 2755.0, 'Cr': 0.2848428453267163, 'NTU': 1.1040834845735028, 'Q': 192849.96310220254, 'Tci': 15, 'Tco': 84.99998660697007, 'Thi': 130, 'Tho': 110.06100464203861, 'UA': 3041.75, 'effectiveness': 0.6086955357127832} ================================================ FILE: ht/air_cooler.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import atan, log10, sin from fluids.constants import hp, minute from fluids.core import Prandtl, Reynolds from ht.conv_tube_bank import ESDU_tube_row_correction from ht.core import LMTD, WALL_FACTOR_PRANDTL, fin_efficiency_Kern_Kraus, wall_factor __all__: list[str] = [ "Ft_aircooler", "air_cooler_noise_GPSA", "air_cooler_noise_Mukherjee", "dP_ESDU_high_fin", "dP_ESDU_low_fin", "h_Briggs_Young", "h_ESDU_high_fin", "h_ESDU_low_fin", "h_Ganguli_VDI", ] fin_densities_inch = [7, 8, 9, 10, 11] # fins/inch fin_densities = [275.6, 315.0, 354.3, 393.7, 433.1] # [round(i/0.0254, 1) for i in fin_densities_inch] ODs = [1, 1.25, 1.5, 2] # Actually though, just use TEMA. API 661 says 1 inch min. fin_heights = [0.010, 0.012, 0.016] # m tube_orientations = ["vertical (inlet at bottom)", "vertical (inlet at top)", "horizontal", "inclined"] _fan_diameters = [0.71, 0.8, 0.9, 1.0, 1.2, 1.24, 1.385, 1.585, 1.78, 1.98, 2.22, 2.475, 2.775, 3.12, 3.515, 4.455, 4.95, 5.545, 6.24, 7.03, 7.92, 8.91, 9.9, 10.4, 11.1, 12.4, 13.85, 15.85] fan_ring_types = ["straight", "flanged", "bell", "15 degree cone", "30 degree cone"] fin_constructions = ["extruded", "embedded", "L-footed", "overlapped L-footed", "externally bonded", "knurled footed"] headers = ["plug", "removable cover", "removable bonnet", "welded bonnet"] configurations = ["forced draft", "natural draft", "induced-draft (top drive)", "induced-draft (bottom drive)"] # Coefs are from: Roetzel and Nicole - 1975 - Mean Temperature Difference for Heat Exchanger Design A General Approximate Explicit Equation # Checked twice. _crossflow_1_row_1_pass = [[-4.62E-1, -3.13E-2, -1.74E-1, -4.2E-2], [5.08E0, 5.29E-1, 1.32E0, 3.47E-1], [-1.57E1, -2.37E0, -2.93E0, -8.53E-1], [1.72E1, 3.18E0, 1.99E0, 6.49E-1]] _crossflow_2_rows_1_pass = [[-3.34E-1, -1.54E-1, -8.65E-2, 5.53E-2], [3.3E0, 1.28E0, 5.46E-1, -4.05E-1], [-8.7E0, -3.35E0, -9.29E-1, 9.53E-1], [8.7E0, 2.83E0, 4.71E-1, -7.17E-1]] _crossflow_3_rows_1_pass = [[-8.74E-2, -3.18E-2, -1.83E-2, 7.1E-3], [1.05E0, 2.74E-1, 1.23E-1, -4.99E-2], [-2.45E0, -7.46E-1, -1.56E-1, 1.09E-1], [3.21E0, 6.68E-1, 6.17E-2, -7.46E-2]] _crossflow_4_rows_1_pass = [[-4.14E-2, -1.39E-2, -7.23E-3, 6.1E-3], [6.15E-1, 1.23E-1, 5.66E-2, -4.68E-2], [-1.2E0, -3.45E-1, -4.37E-2, 1.07E-1], [2.06E0, 3.18E-1, 1.11E-2, -7.57E-2]] _crossflow_2_rows_2_pass = [[-2.35E-1, -7.73E-2, -5.98E-2, 5.25E-3], [2.28E0, 6.32E-1, 3.64E-1, -1.27E-2], [-6.44E0, -1.63E0, -6.13E-1, -1.14E-2], [6.24E0, 1.35E0, 2.76E-1, 2.72E-2]] _crossflow_3_rows_3_pass = [[-8.43E-1, 3.02E-2, 4.8E-1, 8.12E-2], [5.85E0, -9.64E-3, -3.28E0, -8.34E-1], [-1.28E1, -2.28E-1, 7.11E0, 2.19E0], [9.24E0, 2.66E-1, -4.9E0, -1.69E0]] _crossflow_4_rows_4_pass = [[-3.39E-1, 2.77E-2, 1.79E-1, -1.99E-2], [2.38E0, -9.99E-2, -1.21E0, 4E-2], [-5.26E0, 9.04E-2, 2.62E0, 4.94E-2], [3.9E0, -8.45E-4, -1.81E0, -9.81E-2]] _crossflow_4_rows_2_pass = [[-6.05E-1, 2.31E-2, 2.94E-1, 1.98E-2], [4.34E0, 5.9E-3, -1.99E0, -3.05E-1], [-9.72E0, -2.48E-1, 4.32, 8.97E-1], [7.54E0, 2.87E-1, -3E0, -7.31E-1]] def Ft_aircooler(Thi: float, Tho: float, Tci: float, Tco: float, Ntp: int=1, rows: int=1) -> float: r"""Calculates log-mean temperature difference correction factor for a crossflow heat exchanger, as in an Air Cooler. Method presented in [1]_, fit to other's nonexplicit work. Error is < 0.1%. Requires number of rows and tube passes as well as stream temperatures. .. math:: F_T = 1 - \sum_{i=1}^m \sum_{k=1}^n a_{i,k}(1-r_{1,m})^k\sin(2i\arctan R) .. math:: R = \frac{T_{hi} - T_{ho}}{T_{co}-T_{ci}} .. math:: r_{1,m} = \frac{\Delta T_{lm}}{T_{hi} - T_{ci}} Parameters ---------- Thi : float Temperature of hot fluid in [K] Tho : float Temperature of hot fluid out [K] Tci : float Temperature of cold fluid in [K] Tco : float Temperature of cold fluid out [K] Ntp : int Number of passes the tubeside fluid will flow through [-] rows : int Number of rows of tubes [-] Returns ------- Ft : float Log-mean temperature difference correction factor [-] Notes ----- This equation assumes that the hot fluid is tubeside, as in the case of air coolers. The model is not symmetric, so ensure to switch around the inputs if using this function for other purposes. This equation appears in [1]_. It has been verified. For some cases, approximations are made to match coefficients with the number of tube passes and rows provided. 16 coefficients are used for each case; 8 cases are considered: * 1 row 1 pass * 2 rows 1 pass * 2 rows 2 passes * 3 rows 1 pass * 3 rows 3 passes * 4 rows 1 pass * 4 rows 2 passes * 4 rows 4 passes Examples -------- >>> Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4) 0.550509360409 References ---------- .. [1] Roetzel, W., and F. J. L. Nicole. "Mean Temperature Difference for Heat Exchanger Design-A General Approximate Explicit Equation." Journal of Heat Transfer 97, no. 1 (February 1, 1975): 5-8. doi:10.1115/1.3450288 """ dTlm = LMTD(Thi=Thi, Tho=Tho, Tci=Tci, Tco=Tco) rlm = dTlm/(Thi-Tci) R = (Thi-Tho)/(Tco-Tci) # P = (Tco-Tci)/(Thi-Tci) if Ntp == 1 and rows == 1: coefs = _crossflow_1_row_1_pass elif Ntp == 1 and rows == 2: coefs = _crossflow_2_rows_1_pass elif Ntp == 1 and rows == 3: coefs = _crossflow_3_rows_1_pass elif Ntp == 1 and rows == 4: coefs = _crossflow_4_rows_1_pass elif Ntp == 1 and rows > 4: # A reasonable assumption coefs = _crossflow_4_rows_1_pass elif Ntp == 2 and rows == 2: coefs = _crossflow_2_rows_2_pass elif Ntp == 3 and rows == 3: coefs = _crossflow_3_rows_3_pass elif Ntp == 4 and rows == 4: coefs = _crossflow_4_rows_4_pass elif Ntp > 4 and rows > 4 and Ntp == rows: # A reasonable assumption coefs = _crossflow_4_rows_4_pass elif Ntp == 2 and rows == 4: coefs = _crossflow_4_rows_2_pass else: # A bad assumption, but hey, gotta pick something. coefs = _crossflow_4_rows_2_pass tot = 0.0 atanR2 = 2.0*atan(R) N = len(coefs) sine_terms = [0.0]*N for i in range(N): sine_terms[i] = sin((i + 1.)*atanR2) x0 = one_m_rlm_orig = 1.0 - rlm for k in range(N): coeffs_k = coefs[k] tot_i = 0.0 for i in range(N): tot_i += coeffs_k[i]*sine_terms[i] tot += tot_i*x0 x0 *= one_m_rlm_orig return 1. - tot def air_cooler_noise_GPSA(tip_speed: float, power: float) -> float: r"""Calculates the noise generated by an air cooler bay with one fan according to the GPSA handbook [1]_. .. math:: \text{PWL[dB(A)]} = 56 + 30\log_{10}\left( \frac{\text{tip speed} [m/min]}{304.8 [m/min]}\right) + 10\log_{10}( \text{power}[hp]) Parameters ---------- tip_speed : float Tip speed of the air cooler fan blades, [m/s] power : float Shaft power of single fan motor, [W] Returns ------- noise : float Sound pressure level at 1 m from source, [dB(A)] Notes ----- Internal units are in m/minute, and hp. Examples -------- Example problem from GPSA [1]_. >>> air_cooler_noise_GPSA(tip_speed=3177/minute, power=25.1*hp) 100.5368047795 References ---------- .. [1] GPSA. "Engineering Data Book, SI." 13th edition. Gas Processors Suppliers Association (2012). """ tip_speed = tip_speed*minute # convert tip speed to m/minute power = power/hp # convert power from W to hp return 56.0 + 30.0*log10(tip_speed/304.8) + 10.0*log10(power) def air_cooler_noise_Mukherjee(tip_speed: float, power: float, fan_diameter: float, induced: bool=False) -> float: r"""Calculates the noise generated by an air cooler bay with one fan according to [1]_. .. math:: \text{SPL[dB(A)]} = 46 + 30\log_{10}\text{(tip speed)}[m/s] + 10\log_{10}( \text{power}[hp]) - 20 \log_{10}(D_{fan}) Parameters ---------- tip_speed : float Tip speed of the air cooler fan blades, [m/s] power : float Shaft power of single fan motor, [W] fan_diameter : float Diameter of air cooler fan, [m] induced : bool Whether the air cooler is forced air (False) or induced air (True), [-] Returns ------- noise : float Sound pressure level at 1 m from source (p0=2E-5 Pa), [dB(A)] Notes ----- Internal units are in m/minute, hp, and m. If the air cooler is induced, the sound pressure level is reduced by 3 dB. Examples -------- >>> air_cooler_noise_Mukherjee(tip_speed=3177/minute, power=25.1*hp, fan_diameter=4.267) 99.1102632909 References ---------- .. [1] Mukherjee, R., and Geoffrey Hewitt. Practical Thermal Design of Air-Cooled Heat Exchangers. New York: Begell House Publishers Inc.,U.S., 2007. """ noise = 46.0 + 30.0*log10(tip_speed) + 10.0*log10(power/hp) - 20.0*log10(fan_diameter) if induced: noise -= 3.0 return noise def h_Briggs_Young(m: float, A: float, A_min: float, A_increase: float, A_fin: float, A_tube_showing: float, tube_diameter: float, fin_diameter: float, fin_thickness: float, bare_length: float, rho: float, Cp: float, mu: float, k: float, k_fin: float) -> float: r"""Calculates the air side heat transfer coefficient for an air cooler or other finned tube bundle with the formulas of Briggs and Young [1]_, [2]_ [3]_. .. math:: Nu = 0.134Re^{0.681} Pr^{0.33}\left(\frac{S}{h}\right)^{0.2} \left(\frac{S}{b}\right)^{0.1134} Parameters ---------- m : float Mass flow rate of air across the tube bank, [kg/s] A : float Surface area of combined finned and non-finned area exposed for heat transfer, [m^2] A_min : float Minimum air flow area, [m^2] A_increase : float Ratio of actual surface area to bare tube surface area :math:`A_{increase} = \frac{A_{tube}}{A_{bare, total/tube}}`, [-] A_fin : float Surface area of all fins in the bundle, [m^2] A_tube_showing : float Area of the bare tube which is exposed in the bundle, [m^2] tube_diameter : float Diameter of the bare tube, [m] fin_diameter : float Outer diameter of each tube after including the fin on both sides, [m] fin_thickness : float Thickness of the fins, [m] bare_length : float Length of bare tube between two fins :math:`\text{bare length} = \text{fin interval} - t_{fin}`, [m] rho : float Average (bulk) density of air across the tube bank, [kg/m^3] Cp : float Average (bulk) heat capacity of air across the tube bank, [J/kg/K] mu : float Average (bulk) viscosity of air across the tube bank, [Pa*s] k : float Average (bulk) thermal conductivity of air across the tube bank, [W/m/K] k_fin : float Thermal conductivity of the fin, [W/m/K] Returns ------- h_bare_tube_basis : float Air side heat transfer coefficient on a bare-tube surface area as if there were no fins present basis, [W/K/m^2] Notes ----- The limits on this equation are 1000 < Re < 8000 , 11.13 mm < D_o < 40.89 mm, 1.42 mm < fin height < 16.57 mm, 0.33 mm < fin thickness < 2.02 mm, 1.30 mm < fin pitch < 4.06 mm, and 24.49 mm < normal pitch < 111 mm. Examples -------- >>> from fluids.geometry import AirCooledExchanger >>> from scipy.constants import inch >>> AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=20, tube_length=3, ... tube_diameter=1*inch, fin_thickness=0.000406, fin_density=1/0.002309, ... pitch_normal=.06033, pitch_parallel=.05207, ... fin_height=0.0159, tube_thickness=(.0254-.0186)/2, ... bundles_per_bay=1, parallel_bays=1, corbels=True) >>> h_Briggs_Young(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, ... A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, ... fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, ... fin_thickness=AC.fin_thickness, ... rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205) 1422.872240323 References ---------- .. [1] Briggs, D.E., and Young, E.H., 1963, "Convection Heat Transfer and Pressure Drop of Air Flowing across Triangular Banks of Finned Tubes", Chemical Engineering Progress Symp., Series 41, No. 59. Chem. Eng. Prog. Symp. Series No. 41, "Heat Transfer - Houston". .. [2] Mukherjee, R., and Geoffrey Hewitt. Practical Thermal Design of Air-Cooled Heat Exchangers. New York: Begell House Publishers Inc.,U.S., 2007. .. [3] Kroger, Detlev. Air-Cooled Heat Exchangers and Cooling Towers: Thermal-Flow Performance Evaluation and Design, Vol. 1. Tulsa, Okl: PennWell Corp., 2004. """ fin_height = 0.5*(fin_diameter - tube_diameter) V_max = m/(A_min*rho) Re = Reynolds(V=V_max, D=tube_diameter, rho=rho, mu=mu) Pr = Prandtl(Cp=Cp, mu=mu, k=k) Nu = 0.134*Re**0.681*Pr**(1/3.)*(bare_length/fin_height)**0.2*(bare_length/fin_thickness)**0.1134 h = k/tube_diameter*Nu efficiency = fin_efficiency_Kern_Kraus(Do=tube_diameter, D_fin=fin_diameter, t_fin=fin_thickness, k_fin=k_fin, h=h) h_total_area_basis = (efficiency*A_fin + A_tube_showing)/A*h h_bare_tube_basis = h_total_area_basis*A_increase return h_bare_tube_basis def h_ESDU_high_fin(m: float, A: float, A_min: float, A_increase: float, A_fin: float, A_tube_showing: float, tube_diameter: float, fin_diameter: float, fin_thickness: float, bare_length: float, pitch_parallel: float, pitch_normal: float, tube_rows: int, rho: float, Cp: float, mu: float, k: float, k_fin: float, Pr_wall: None=None) -> float: r"""Calculates the air side heat transfer coefficient for an air cooler or other finned tube bundle with the formulas of [2]_ as presented in [1]_. .. math:: Nu = 0.242 Re^{0.658} \left(\frac{\text{bare length}} {\text{fin height}}\right)^{0.297} \left(\frac{P_1}{P_2}\right)^{-0.091} P_r^{1/3}\cdot F_1\cdot F_2 .. math:: h_{A,total} = \frac{\eta A_{fin} + A_{bare, showing}}{A_{total}} h .. math:: h_{bare,total} = A_{increase} h_{A,total} Parameters ---------- m : float Mass flow rate of air across the tube bank, [kg/s] A : float Surface area of combined finned and non-finned area exposed for heat transfer, [m^2] A_min : float Minimum air flow area, [m^2] A_increase : float Ratio of actual surface area to bare tube surface area :math:`A_{increase} = \frac{A_{tube}}{A_{bare, total/tube}}`, [-] A_fin : float Surface area of all fins in the bundle, [m^2] A_tube_showing : float Area of the bare tube which is exposed in the bundle, [m^2] tube_diameter : float Diameter of the bare tube, [m] fin_diameter : float Outer diameter of each tube after including the fin on both sides, [m] fin_thickness : float Thickness of the fins, [m] bare_length : float Length of bare tube between two fins :math:`\text{bare length} = \text{fin interval} - t_{fin}`, [m] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] tube_rows : int Number of tube rows per bundle, [-] rho : float Average (bulk) density of air across the tube bank, [kg/m^3] Cp : float Average (bulk) heat capacity of air across the tube bank, [J/kg/K] mu : float Average (bulk) viscosity of air across the tube bank, [Pa*s] k : float Average (bulk) thermal conductivity of air across the tube bank, [W/m/K] k_fin : float Thermal conductivity of the fin, [W/m/K] Pr_wall : float, optional Prandtl number at the wall temperature; provide if a correction with the defaults parameters is desired; otherwise apply the correction elsewhere, [-] Returns ------- h_bare_tube_basis : float Air side heat transfer coefficient on a bare-tube surface area as if there were no fins present basis, [W/K/m^2] Notes ----- The tube-row count correction factor is 1 for four or more rows, 0.92 for three rows, 0.84 for two rows, and 0.76 for one row according to [1]_. The property correction factor can be disabled by not specifying `Pr_wall`. A Prandtl number exponent of 0.26 is recommended in [1]_ for heating and cooling for both liquids and gases. Examples -------- >>> from fluids.geometry import AirCooledExchanger >>> from scipy.constants import inch >>> AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=20, tube_length=3, ... tube_diameter=1*inch, fin_thickness=0.000406, fin_density=1/0.002309, ... pitch_normal=.06033, pitch_parallel=.05207, ... fin_height=0.0159, tube_thickness=(.0254-.0186)/2, ... bundles_per_bay=1, parallel_bays=1, corbels=True) >>> h_ESDU_high_fin(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, ... A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, ... fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, ... fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, ... pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, ... rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205) 1390.88891804 References ---------- .. [1] Hewitt, G. L. Shires, T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1st edition. Boca Raton: CRC Press, 1994. .. [2] "High-Fin Staggered Tube Banks: Heat Transfer and Pressure Drop for Turbulent Single Phase Gas Flow." ESDU 86022 (October 1, 1986). .. [3] Rabas, T. J., and J. Taborek. "Survey of Turbulent Forced-Convection Heat Transfer and Pressure Drop Characteristics of Low-Finned Tube Banks in Cross Flow." Heat Transfer Engineering 8, no. 2 (January 1987): 49-62. """ fin_height = 0.5*(fin_diameter - tube_diameter) V_max = m/(A_min*rho) Re = Reynolds(V=V_max, D=tube_diameter, rho=rho, mu=mu) Pr = Prandtl(Cp=Cp, mu=mu, k=k) Nu = 0.242*Re**0.658*(bare_length/fin_height)**0.297*(pitch_normal/pitch_parallel)**-0.091*Pr**(1/3.) if tube_rows < 2: F2 = 0.76 elif tube_rows < 3: F2 = 0.84 elif tube_rows < 4: F2 = 0.92 else: F2 = 1.0 Nu *= F2 if Pr_wall is not None: F1 = wall_factor(Pr=Pr, Pr_wall=Pr_wall, Pr_heating_coeff=0.26, Pr_cooling_coeff=0.26, property_option=WALL_FACTOR_PRANDTL) Nu *= F1 h = k/tube_diameter*Nu efficiency = fin_efficiency_Kern_Kraus(Do=tube_diameter, D_fin=fin_diameter, t_fin=fin_thickness, k_fin=k_fin, h=h) h_total_area_basis = (efficiency*A_fin + A_tube_showing)/A*h h_bare_tube_basis = h_total_area_basis*A_increase return h_bare_tube_basis def h_ESDU_low_fin(m: float, A: float, A_min: float, A_increase: float, A_fin: float, A_tube_showing: float, tube_diameter: float, fin_diameter: float, fin_thickness: float, bare_length: float, pitch_parallel: float, pitch_normal: float, tube_rows: int, rho: float, Cp: float, mu: float, k: float, k_fin: float, Pr_wall: float | None=None) -> float: r"""Calculates the air side heat transfer coefficient for an air cooler or other finned tube bundle with low fins using the formulas of [1]_ as presented in [2]_ (and also [3]_). .. math:: Nu = 0.183Re^{0.7} \left(\frac{\text{bare length}}{\text{fin height}} \right)^{0.36}\left(\frac{p_1}{D_{o}}\right)^{0.06} \left(\frac{\text{fin height}}{D_o}\right)^{0.11} Pr^{0.36} \cdot F_1\cdot F_2 .. math:: h_{A,total} = \frac{\eta A_{fin} + A_{bare, showing}}{A_{total}} h .. math:: h_{bare,total} = A_{increase} h_{A,total} Parameters ---------- m : float Mass flow rate of air across the tube bank, [kg/s] A : float Surface area of combined finned and non-finned area exposed for heat transfer, [m^2] A_min : float Minimum air flow area, [m^2] A_increase : float Ratio of actual surface area to bare tube surface area :math:`A_{increase} = \frac{A_{tube}}{A_{bare, total/tube}}`, [-] A_fin : float Surface area of all fins in the bundle, [m^2] A_tube_showing : float Area of the bare tube which is exposed in the bundle, [m^2] tube_diameter : float Diameter of the bare tube, [m] fin_diameter : float Outer diameter of each tube after including the fin on both sides, [m] fin_thickness : float Thickness of the fins, [m] bare_length : float Length of bare tube between two fins :math:`\text{bare length} = \text{fin interval} - t_{fin}`, [m] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] tube_rows : int Number of tube rows per bundle, [-] rho : float Average (bulk) density of air across the tube bank, [kg/m^3] Cp : float Average (bulk) heat capacity of air across the tube bank, [J/kg/K] mu : float Average (bulk) viscosity of air across the tube bank, [Pa*s] k : float Average (bulk) thermal conductivity of air across the tube bank, [W/m/K] k_fin : float Thermal conductivity of the fin, [W/m/K] Pr_wall : float, optional Prandtl number at the wall temperature; provide if a correction with the defaults parameters is desired; otherwise apply the correction elsewhere, [-] Returns ------- h_bare_tube_basis : float Air side heat transfer coefficient on a bare-tube surface area as if there were no fins present basis, [W/K/m^2] Notes ----- The tube-row count correction factor `F2` can be disabled by setting `tube_rows` to 10. The property correction factor `F1` can be disabled by not specifying `Pr_wall`. A Prandtl number exponent of 0.26 is recommended in [1]_ for heating and cooling for both liquids and gases. There is a third correction factor in [1]_ for tube angles not 30, 45, or 60 degrees, but it is not fully explained and it is not shown in [2]_. Another correction factor is in [2]_ for flow at an angle; however it would not make sense to apply it to finned tube banks due to the blockage by the fins. Examples -------- >>> from fluids.geometry import AirCooledExchanger >>> AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=8, tube_length=0.5, ... tube_diameter=0.0164, fin_thickness=0.001, fin_density=1/0.003, ... pitch_normal=0.0313, pitch_parallel=0.0271, fin_height=0.0041, corbels=True) >>> h_ESDU_low_fin(m=0.914, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, ... A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, ... fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, ... fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, ... pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, ... rho=1.217, Cp=1007., mu=1.8E-5, k=0.0253, k_fin=15) 553.85383647 References ---------- .. [1] Hewitt, G. L. Shires, T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1st edition. Boca Raton: CRC Press, 1994. .. [2] "High-Fin Staggered Tube Banks: Heat Transfer and Pressure Drop for Turbulent Single Phase Gas Flow." ESDU 86022 (October 1, 1986). .. [3] Rabas, T. J., and J. Taborek. "Survey of Turbulent Forced-Convection Heat Transfer and Pressure Drop Characteristics of Low-Finned Tube Banks in Cross Flow." Heat Transfer Engineering 8, no. 2 (January 1987): 49-62. """ fin_height = 0.5*(fin_diameter - tube_diameter) V_max = m/(A_min*rho) Re = Reynolds(V=V_max, D=tube_diameter, rho=rho, mu=mu) Pr = Prandtl(Cp=Cp, mu=mu, k=k) Nu = (0.183*Re**0.7*(bare_length/fin_height)**0.36 *(pitch_normal/fin_diameter)**0.06 *(fin_height/fin_diameter)**0.11*Pr**0.36) staggered = abs(1 - pitch_normal/pitch_parallel) > 0.05 F2 = ESDU_tube_row_correction(tube_rows=tube_rows, staggered=staggered) Nu *= F2 if Pr_wall is not None: F1 = wall_factor(Pr=Pr, Pr_wall=Pr_wall, Pr_heating_coeff=0.26, Pr_cooling_coeff=0.26, property_option=WALL_FACTOR_PRANDTL) Nu *= F1 h = k/tube_diameter*Nu efficiency = fin_efficiency_Kern_Kraus(Do=tube_diameter, D_fin=fin_diameter, t_fin=fin_thickness, k_fin=k_fin, h=h) h_total_area_basis = (efficiency*A_fin + A_tube_showing)/A*h h_bare_tube_basis = h_total_area_basis*A_increase return h_bare_tube_basis def h_Ganguli_VDI(m: float, A: float, A_min: float, A_increase: float, A_fin: float, A_tube_showing: float, tube_diameter: float, fin_diameter: float, fin_thickness: float, bare_length: float, pitch_parallel: float, pitch_normal: float, tube_rows: int, rho: float, Cp: float, mu: float, k: float, k_fin: float) -> float: r"""Calculates the air side heat transfer coefficient for an air cooler or other finned tube bundle with the formulas of [1]_ as modified in [2]_. Inline: .. math:: Nu_d = 0.22Re_d^{0.6}\left(\frac{A}{A_{tube,only}}\right)^{-0.15}Pr^{1/3} Staggered: .. math:: Nu_d = 0.38 Re_d^{0.6}\left(\frac{A}{A_{tube,only}}\right)^{-0.15}Pr^{1/3} Parameters ---------- m : float Mass flow rate of air across the tube bank, [kg/s] A : float Surface area of combined finned and non-finned area exposed for heat transfer, [m^2] A_min : float Minimum air flow area, [m^2] A_increase : float Ratio of actual surface area to bare tube surface area :math:`A_{increase} = \frac{A_{tube}}{A_{bare, total/tube}}`, [-] A_fin : float Surface area of all fins in the bundle, [m^2] A_tube_showing : float Area of the bare tube which is exposed in the bundle, [m^2] tube_diameter : float Diameter of the bare tube, [m] fin_diameter : float Outer diameter of each tube after including the fin on both sides, [m] fin_thickness : float Thickness of the fins, [m] bare_length : float Length of bare tube between two fins :math:`\text{bare length} = \text{fin interval} - t_{fin}`, [m] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] tube_rows : int Number of tube rows per bundle, [-] rho : float Average (bulk) density of air across the tube bank, [kg/m^3] Cp : float Average (bulk) heat capacity of air across the tube bank, [J/kg/K] mu : float Average (bulk) viscosity of air across the tube bank, [Pa*s] k : float Average (bulk) thermal conductivity of air across the tube bank, [W/m/K] k_fin : float Thermal conductivity of the fin, [W/m/K] Returns ------- h_bare_tube_basis : float Air side heat transfer coefficient on a bare-tube surface area as if there were no fins present basis, [W/K/m^2] Notes ----- The VDI modifications were developed in comparison with HTFS and HTRI data according to [2]_. For cases where the tube row count is less than four, the coefficients are modified in [2]_. For the inline case, 0.2 replaces 0.22. For the stagered cases, the coefficient is 0.2, 0.33, 0.36 for 1, 2, or 3 tube rows respectively. The model is also showin in [4]_. Examples -------- Example 12.1 in [3]_: >>> from fluids.geometry import AirCooledExchanger >>> from scipy.constants import foot, inch >>> AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=56, tube_length=36*foot, ... tube_diameter=1*inch, fin_thickness=0.013*inch, fin_density=10/inch, ... angle=30, pitch_normal=2.5*inch, fin_height=0.625*inch, corbels=True) >>> h_Ganguli_VDI(m=130.70315, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, ... A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, ... fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, ... fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, ... pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, ... rho=1.2013848, Cp=1009.0188, mu=1.9304793e-05, k=0.027864828, k_fin=238) 969.285081857 References ---------- .. [1] Ganguli, A., S. S. Tung, and J. Taborek. "Parametric Study of Air-Cooled Heat Exchanger Finned Tube Geometry." In AIChE Symposium Series, 81:122-28, 1985. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. .. [3] Serth, Robert W., and Thomas Lestina. Process Heat Transfer: Principles, Applications and Rules of Thumb. Academic Press, 2014. .. [4] Kroger, Detlev. Air-Cooled Heat Exchangers and Cooling Towers: Thermal-Flow Performance Evaluation and Design, Vol. 1. Tulsa, Okl: PennWell Corp., 2004. """ V_max = m/(A_min*rho) Re = Reynolds(V=V_max, D=tube_diameter, rho=rho, mu=mu) Pr = Prandtl(Cp=Cp, mu=mu, k=k) if abs(1 - pitch_normal/pitch_parallel) < 0.05: # in-line, with a tolerance of 0.05 proximity if tube_rows < 4: coeff = 0.2 else: coeff = 0.22 else: # staggered if tube_rows == 1: coeff = 0.2 elif tube_rows == 2: coeff = 0.33 elif tube_rows == 3: coeff = 0.36 else: coeff = 0.38 # VDI example shows the ratio is of the total area, to the original bare tube area # Serth example would match Nu = 47.22 except for lazy rounding Nu = coeff*Re**0.6*Pr**(1/3.)*(A_increase)**-0.15 h = k/tube_diameter*Nu efficiency = fin_efficiency_Kern_Kraus(Do=tube_diameter, D_fin=fin_diameter, t_fin=fin_thickness, k_fin=k_fin, h=h) h_total_area_basis = (efficiency*A_fin + A_tube_showing)/A*h h_bare_tube_basis = h_total_area_basis*A_increase return h_bare_tube_basis def dP_ESDU_high_fin(m: float, A_min: float, A_increase: float, flow_area_contraction_ratio: float, tube_diameter: float, pitch_parallel: float, pitch_normal: float, tube_rows: int, rho: float, mu: float) -> float: r"""Calculates the air-side pressure drop for a high-finned tube bank according to the ESDU [1]_ method, as described in [2]_. This includes the effects of friction of the fin, and acceleration. .. math:: \Delta P = (K_{acc} + n_{rows} K_{f}) \frac{1}{2}\rho v_{max}^2 .. math:: K_{f} = 4.567 Re_D^{-0.242} \left(\frac{A}{A_{tube,only}} \right)^{0.504} \left(\frac{p_1}{D_o}\right)^{-0.376} \left(\frac{p_2}{D_{o}}\right)^{-0.546} .. math:: K_{acc} = 1 + \text{(flow area contraction ratio)}^2 Parameters ---------- m : float Mass flow rate of air across the tube bank, [kg/s] A_min : float Minimum air flow area, [m^2] A_increase : float Ratio of actual surface area to bare tube surface area :math:`A_{increase} = \frac{A_{tube}}{A_{bare, total/tube}}`, [-] flow_area_contraction_ratio : float Ratio of `A_min` to `A_face`, [-] tube_diameter : float Diameter of the bare tube, [m] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] tube_rows : int Number of tube rows per bundle, [-] rho : float Average (bulk) density of air across the tube bank, [kg/m^3] mu : float Average (bulk) viscosity of air across the tube bank, [Pa*s] Returns ------- dP : float Overall pressure drop across the finned tube bank, [Pa] Notes ----- The data used by the ESDU covered: * fin density 4 to 11/inch * tube outer diameters 3/8 to 2 inches * fin heights 1/3 to 5/8 inches * fin tip to fin root diameters 1.2 to 2.4 * Reynolds numbers 5000 to 50000 [1]_ claims 72% of experimental points were within 10% of the results of the correlation. The Reynolds number used in this equation is that based on `V_max`, calculated using the minimum flow area. Examples -------- >>> from fluids.geometry import AirCooledExchanger >>> AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=8, tube_length=0.5, ... tube_diameter=0.0164, fin_thickness=0.001, fin_density=1/0.003, ... pitch_normal=0.0313, pitch_parallel=0.0271, fin_height=0.0041, corbels=True) >>> dP_ESDU_high_fin(m=0.914, A_min=AC.A_min, A_increase=AC.A_increase, flow_area_contraction_ratio=AC.flow_area_contraction_ratio, tube_diameter=AC.tube_diameter, pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, tube_rows=AC.tube_rows, rho=1.217, mu=0.000018) 485.630768779 References ---------- .. [1] "High-Fin Staggered Tube Banks: Heat Transfer and Pressure Drop for Turbulent Single Phase Gas Flow." ESDU (October 1, 1986). .. [2] Hewitt, G. L. Shires T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1E. Boca Raton: CRC Press, 1994. """ Vmax = m/(A_min*rho) Re = Vmax*tube_diameter*rho/mu Kf = (4.567*Re**-0.242*(A_increase)**0.504 *(pitch_normal/tube_diameter)**-0.376 *(pitch_parallel/tube_diameter)**-0.546) Ka = 1.0 + flow_area_contraction_ratio*flow_area_contraction_ratio dP = (Ka + tube_rows*Kf)*0.5*rho*Vmax*Vmax return dP def dP_ESDU_low_fin(m: float, A_min: float, A_increase: float, flow_area_contraction_ratio: float, tube_diameter: float, fin_height: float, bare_length: float, pitch_parallel: float, pitch_normal: float, tube_rows: int, rho: float, mu: float) -> float: r"""Calculates the air-side pressure drop for a low-finned tube bank according to the ESDU [1]_ method, as described in [2]_. This includes the effects of friction of the fin, and acceleration. .. math:: \Delta P = (K_{acc} + n_{rows} K_{f}) \frac{1}{2}\rho v_{max}^2 .. math:: K_{f} = 4.71 Re_D^{-0.286} \left(\frac{\text{fin height}} {\text{bare length}}\right)^{0.51} \left(\frac{p_1 - D_o}{p_2 - D_o}\right)^{0.536} \left(\frac{D_o}{p_1 - D_o}\right)^{0.36} .. math:: K_{acc} = 1 + \text{(flow area contraction ratio)}^2 Parameters ---------- m : float Mass flow rate of air across the tube bank, [kg/s] A_min : float Minimum air flow area, [m^2] A_increase : float Ratio of actual surface area to bare tube surface area :math:`A_{increase} = \frac{A_{tube}}{A_{bare, total/tube}}`, [-] flow_area_contraction_ratio : float Ratio of `A_min` to `A_face`, [-] tube_diameter : float Diameter of the bare tube, [m] fin_height : float Height above bare tube of the tube fins, [m] bare_length : float Length of bare tube between two fins :math:`\text{bare length} = \text{fin interval} - t_{fin}`, [m] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] tube_rows : int Number of tube rows per bundle, [-] rho : float Average (bulk) density of air across the tube bank, [kg/m^3] mu : float Average (bulk) viscosity of air across the tube bank, [Pa*s] Returns ------- dP : float Overall pressure drop across the finned tube bank, [Pa] Notes ----- Low fins are fins which were formed on the tube outside wall, normally by the cold rolling process. The data used by the ESDU covered: * fin density 11 to 32/inch * tube outer diameters 0.5 to 1.25 inches * fin heights 0.03 to 0.1 inches * Reynolds numbers 1000 to 80000 [1]_ compared this correlation with 81 results and obtained a standard deviation of 7.7%. The Reynolds number used in this equation is that based on `V_max`, calculated using the minimum flow area. Examples -------- >>> from fluids.geometry import AirCooledExchanger >>> AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=8, tube_length=0.5, ... tube_diameter=0.0164, fin_thickness=0.001, fin_density=1/0.003, ... pitch_normal=0.0313, pitch_parallel=0.0271, fin_height=0.0041, corbels=True) >>> dP_ESDU_low_fin(m=0.914, A_min=AC.A_min, A_increase=AC.A_increase, ... flow_area_contraction_ratio=AC.flow_area_contraction_ratio, ... tube_diameter=AC.tube_diameter, fin_height=AC.fin_height, ... bare_length=AC.bare_length, pitch_parallel=AC.pitch_parallel, ... pitch_normal=AC.pitch_normal, tube_rows=AC.tube_rows, rho=1.217, ... mu=0.000018) 464.5433141865 References ---------- .. [1] "High-Fin Staggered Tube Banks: Heat Transfer and Pressure Drop for Turbulent Single Phase Gas Flow." ESDU (October 1, 1986). .. [2] Hewitt, G. L. Shires T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1E. Boca Raton: CRC Press, 1994. """ Vmax = m/(A_min*rho) Re = Vmax*tube_diameter*rho/mu Kf = (4.72*Re**-0.286*(fin_height/bare_length)**0.51 *((pitch_normal-tube_diameter)/(pitch_parallel-tube_diameter))**0.536 *(tube_diameter/(pitch_normal-tube_diameter))**0.36) Ka = 1.0 + flow_area_contraction_ratio*flow_area_contraction_ratio dP = (Ka + tube_rows*Kf)*0.5*rho*Vmax*Vmax return dP """Three more correlations - Heat Transfer and Pressure Drop Characteristics of Dry Tower Extended Surfaces: Data Analysis and Correlation. Pacific Northwest Laboratory, 1976. * said to be in common use in http://www.thermopedia.com/content/551/ Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. Kroger - Mirković """ ================================================ FILE: ht/boiling_flow.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import atan, exp, log10, pi from fluids.constants import g from fluids.core import Boiling, Bond, Prandtl, Weber from fluids.numerics import secant from fluids.two_phase_voidage import Lockhart_Martinelli_Xtt from ht.boiling_nucleic import Cooper, Forster_Zuber from ht.conv_internal import turbulent_Dittus_Boelter, turbulent_Gnielinski __all__: list[str] = [ "Chen_Bennett", "Chen_Edelstein", "Lazarek_Black", "Li_Wu", "Liu_Winterton", "Sun_Mishima", "Thome", "Yun_Heo_Kim", ] __numba_additional_funcs__ = ("to_solve_q_Thome",) def Lazarek_Black(m: float, D: float, mul: float, kl: float, Hvap: float, q: float | None=None, Te: float | None=None) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in vertical tubes for either upward or downward flow. Correlation is as shown in [1]_, and also reviewed in [2]_ and [3]_. Either the heat flux or excess temperature is required for the calculation of heat transfer coefficient. Quality independent. Requires no properties of the gas. Uses a Reynolds number assuming all the flow is liquid. .. math:: h_{tp} = 30 Re_{lo}^{0.857} Bg^{0.714} \frac{k_l}{D} .. math:: Re_{lo} = \frac{G_{tp}D}{\mu_l} Parameters ---------- m : float Mass flow rate [kg/s] D : float Diameter of the channel [m] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of liquid [J/kg] q : float, optional Heat flux to wall [W/m^2] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ has been reviewed. [2]_ claims it was developed for a range of quality 0-0.6, Relo 860-5500, mass flux 125-750 kg/m^2/s, q of 1.4-38 W/cm^2, and with a pipe diameter of 3.1 mm. Developed with data for R113 only. Examples -------- >>> Lazarek_Black(m=10, D=0.3, mul=1E-3, kl=0.6, Hvap=2E6, Te=100) 9501.932636079293 References ---------- .. [1] Lazarek, G. M., and S. H. Black. "Evaporative Heat Transfer, Pressure Drop and Critical Heat Flux in a Small Vertical Tube with R-113." International Journal of Heat and Mass Transfer 25, no. 7 (July 1982): 945-60. doi:10.1016/0017-9310(82)90070-9. .. [2] Fang, Xiande, Zhanru Zhou, and Dingkun Li. "Review of Correlations of Flow Boiling Heat Transfer Coefficients for Carbon Dioxide." International Journal of Refrigeration 36, no. 8 (December 2013): 2017-39. doi:10.1016/j.ijrefrig.2013.05.015. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ G = m/(pi/4*D**2) Relo = G*D/mul if q is not None: Bg = Boiling(G=G, q=q, Hvap=Hvap) return 30*Relo**0.857*Bg**0.714*kl/D elif Te is not None: # Solved with sympy return 27000*30**(71/143)*(1./(G*Hvap))**(357/143)*Relo**(857/286)*Te**(357/143)*kl**(500/143)/D**(500/143) else: raise ValueError("Either q or Te is needed for this correlation") def Li_Wu(m: float, x: float, D: float, rhol: float, rhog: float, mul: float, kl: float, Hvap: float, sigma: float, q: float | None=None, Te: float | None=None) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is as shown in [1]_, and also reviewed in [2]_ and [3]_. Either the heat flux or excess temperature is required for the calculation of heat transfer coefficient. Uses liquid Reynolds number, Bond number, and Boiling number. .. math:: h_{tp} = 334 Bg^{0.3}(Bo\cdot Re_l^{0.36})^{0.4}\frac{k_l}{D} .. math:: Re_{l} = \frac{G(1-x)D}{\mu_l} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] q : float, optional Heat flux to wall [W/m^2] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ has been reviewed. [1]_ used 18 sets of experimental data to derive the results, covering hydraulic diameters from 0.19 to 3.1 mm and 12 different fluids. Examples -------- >>> Li_Wu(m=1, x=0.2, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, q=1E5) 5345.409399239492 References ---------- .. [1] Li, Wei, and Zan Wu. "A General Correlation for Evaporative Heat Transfer in Micro/mini-Channels." International Journal of Heat and Mass Transfer 53, no. 9-10 (April 2010): 1778-87. doi:10.1016/j.ijheatmasstransfer.2010.01.012. .. [2] Fang, Xiande, Zhanru Zhou, and Dingkun Li. "Review of Correlations of Flow Boiling Heat Transfer Coefficients for Carbon Dioxide." International Journal of Refrigeration 36, no. 8 (December 2013): 2017-39. doi:10.1016/j.ijrefrig.2013.05.015. .. [3] Kim, Sung-Min, and Issam Mudawar. "Review of Databases and Predictive Methods for Pressure Drop in Adiabatic, Condensing and Boiling Mini/micro-Channel Flows." International Journal of Heat and Mass Transfer 77 (October 2014): 74-97. doi:10.1016/j.ijheatmasstransfer.2014.04.035. """ G = m/(pi/4*D**2) Rel = G*D*(1-x)/mul Bo = Bond(rhol=rhol, rhog=rhog, sigma=sigma, L=D) if q is not None: Bg = Boiling(G=G, q=q, Hvap=Hvap) return 334*Bg**0.3*(Bo*Rel**0.36)**0.4*kl/D elif Te is not None: A = 334*(Bo*Rel**0.36)**0.4*kl/D return A**(10/7.)*Te**(3/7.)/(G**(3/7.)*Hvap**(3/7.)) else: raise ValueError("Either q or Te is needed for this correlation") def Sun_Mishima(m: float, D: float, rhol: float, rhog: float, mul: float, kl: float, Hvap: float, sigma: float, q: float | None=None, Te: float | None=None) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is as shown in [1]_, and also reviewed in [2]_. Either the heat flux or excess temperature is required for the calculation of heat transfer coefficient. Uses liquid-only Reynolds number, Weber number, and Boiling number. Weber number is defined in terms of the velocity if all fluid were liquid. .. math:: h_{tp} = \frac{ 6 Re_{lo}^{1.05} Bg^{0.54}} {We_l^{0.191}(\rho_l/\rho_g)^{0.142}}\frac{k_l}{D} .. math:: Re_{lo} = \frac{G_{tp}D}{\mu_l} Parameters ---------- m : float Mass flow rate [kg/s] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] q : float, optional Heat flux to wall [W/m^2] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ has been reviewed. [1]_ used 2501 data points to derive the results, covering hydraulic diameters from 0.21 to 6.05 mm and 11 different fluids. Examples -------- >>> Sun_Mishima(m=1, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, Te=10) 507.6709168372167 References ---------- .. [1] Sun, Licheng, and Kaichiro Mishima. "An Evaluation of Prediction Methods for Saturated Flow Boiling Heat Transfer in Mini-Channels." International Journal of Heat and Mass Transfer 52, no. 23-24 (November 2009): 5323-29. doi:10.1016/j.ijheatmasstransfer.2009.06.041. .. [2] Fang, Xiande, Zhanru Zhou, and Dingkun Li. "Review of Correlations of Flow Boiling Heat Transfer Coefficients for Carbon Dioxide." International Journal of Refrigeration 36, no. 8 (December 2013): 2017-39. doi:10.1016/j.ijrefrig.2013.05.015. """ G = m/(pi/4*D**2) V = G/rhol Relo = G*D/mul We = Weber(V=V, L=D, rho=rhol, sigma=sigma) if q is not None: Bg = Boiling(G=G, q=q, Hvap=Hvap) return 6*Relo**1.05*Bg**0.54/(We**0.191*(rhol/rhog)**0.142)*kl/D elif Te is not None: A = 6*Relo**1.05/(We**0.191*(rhol/rhog)**0.142)*kl/D return A**(50/23.)*Te**(27/23.)/(G**(27/23.)*Hvap**(27/23.)) else: raise ValueError("Either q or Te is needed for this correlation") def Thome(m: float, x: float, D: float, rhol: float, rhog: float, mul: float, mug: float, kl: float, kg: float, Cpl: float, Cpg: float, Hvap: float, sigma: float, Psat: float, Pc: float, q: float | None=None, Te: float | None=None) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is as developed in [1]_ and [2]_, and also reviewed [3]_. This is a complicated model, but expected to have more accuracy as a result. Either the heat flux or excess temperature is required for the calculation of heat transfer coefficient. The solution for a specified excess temperature is solved numerically, making it slow. .. math:: h(z) = \frac{t_l}{\tau} h_l(z) +\frac{t_{film}}{\tau} h_{film}(z) + \frac{t_{dry}}{\tau} h_{g}(z) .. math:: h_{l/g}(z) = (Nu_{lam}^4 + Nu_{trans}^4)^{1/4} k/D .. math:: Nu_{laminar} = 0.91 {Pr}^{1/3} \sqrt{ReD/L(z)} .. math:: Nu_{trans} = \frac{ (f/8) (Re-1000)Pr}{1+12.7 (f/8)^{1/2} (Pr^{2/3}-1)} \left[ 1 + \left( \frac{D}{L(z)}\right)^{2/3}\right] .. math:: f = (1.82 \log_{10} Re - 1.64 )^{-2} .. math:: L_l = \frac{\tau G_{tp}}{\rho_l}(1-x) .. math:: L_{dry} = v_p t_{dry} .. math:: t_l = \frac{\tau}{1 + \frac{\rho_l}{\rho_g}\frac{x}{1-x}} .. math:: t_v = \frac{\tau}{1 + \frac{\rho_g}{\rho_l}\frac{1-x}{x}} .. math:: \tau = \frac{1}{f_{opt}} .. math:: f_{opt} = \left(\frac{q}{q_{ref}}\right)^{n_f} .. math:: q_{ref} = 3328\left(\frac{P_{sat}}{P_c}\right)^{-0.5} .. math:: t_{dry,film} = \frac{\rho_l \Delta H_{vap}}{q}[\delta_0(z) - \delta_{min}] .. math:: \frac{\delta_0}{D} = C_{\delta 0}\left(3\sqrt{\frac{\nu_l}{v_p D}} \right)^{0.84}\left[(0.07Bo^{0.41})^{-8} + 0.1^{-8}\right]^{-1/8} .. math:: Bo = \frac{\rho_l D}{\sigma} v_p^2 .. math:: v_p = G_{tp} \left[\frac{x}{\rho_g} + \frac{1-x}{\rho_l}\right] .. math:: h_{film}(z) = \frac{2 k_l}{\delta_0(z) + \delta_{min}(z)} .. math:: \delta_{min} = 0.3\cdot 10^{-6} \text{m} .. math:: C_{\delta,0} = 0.29 .. math:: n_f = 1.74 if t dry film > tv: .. math:: \delta_{end}(x) = \delta(z, t_v) .. math:: t_{film} = t_v .. math:: t_{dry} = 0 Otherwise: .. math:: \delta_{end}(z) = \delta_{min} .. math:: t_{film} = t_{dry,film} .. math:: t_{dry} = t_v - t_{film} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] mug : float Viscosity of gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] kg : float Thermal conductivity of gas [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Cpg : float Heat capacity of gas [J/kg/K] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] Psat : float Vapor pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] q : float, optional Heat flux to wall [W/m^2] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ and [2]_ have been reviewed, and are accurately reproduced in [3]_. [1]_ used data from 7 studies, covering 7 fluids and Dh from 0.7-3.1 mm, heat flux from 0.5-17.8 W/cm^2, x from 0.01-0.99, and G from 50-564 kg/m^2/s. Liquid and/or gas slugs are both considered, and are hydrodynamically developing. `Ll` is the calculated length of liquid slugs, and `L_dry` is the same for vapor slugs. Because of the complexity of the model and that there is some logic in this function, `Te` as an input may lead to a different solution that the calculated `q` will in return. Examples -------- >>> Thome(m=1, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, ... mul=156E-6, mug=1E-5, Cpl=2300, Cpg=1400, sigma=0.02, Hvap=9E5, ... Psat=1E5, Pc=22E6, q=1E5) 1633.008836502032 References ---------- .. [1] Thome, J. R., V. Dupont, and A. M. Jacobi. "Heat Transfer Model for Evaporation in Microchannels. Part I: Presentation of the Model." International Journal of Heat and Mass Transfer 47, no. 14-16 (July 2004): 3375-85. doi:10.1016/j.ijheatmasstransfer.2004.01.006. .. [2] Dupont, V., J. R. Thome, and A. M. Jacobi. "Heat Transfer Model for Evaporation in Microchannels. Part II: Comparison with the Database." International Journal of Heat and Mass Transfer 47, no. 14-16 (July 2004): 3387-3401. doi:10.1016/j.ijheatmasstransfer.2004.01.007. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ if q is None and Te is not None: q = secant(to_solve_q_Thome, 1E4, args=( m, x, D, rhol, rhog, kl, kg, mul, mug, Cpl, Cpg, sigma, Hvap, Psat, Pc, Te)) return Thome(m=m, x=x, D=D, rhol=rhol, rhog=rhog, kl=kl, kg=kg, mul=mul, mug=mug, Cpl=Cpl, Cpg=Cpg, sigma=sigma, Hvap=Hvap, Psat=Psat, Pc=Pc, q=q) elif q is None and Te is None: raise ValueError("Either q or Te is needed for this correlation") C_delta0 = 0.3E-6 G = m/(pi/4*D**2) Rel = G*D*(1-x)/mul Reg = G*D*x/mug qref = 3328*(Psat/Pc)**-0.5 if q is None: q = 1e4 # Make numba happy, their bug, never gets ran fopt = (q/qref)**1.74 tau = 1./fopt vp = G*(x/rhog + (1-x)/rhol) Bo = rhol*D/sigma*vp**2 # Not standard definition nul = mul/rhol delta0 = D*0.29*(3*(nul/vp/D)**0.5)**0.84*((0.07*Bo**0.41)**-8 + 0.1**-8)**(-1/8.) tl = tau/(1 + rhol/rhog*(x/(1.-x))) tv = tau/(1 + rhog/rhol*((1.-x)/x)) t_dry_film = rhol*Hvap/q*(delta0 - C_delta0) if t_dry_film > tv: t_film = tv delta_end = delta0 - q/rhol/Hvap*tv # what could time possibly be? t_dry = 0 else: t_film = t_dry_film delta_end = C_delta0 t_dry = tv-t_film Ll = tau*G/rhol*(1-x) Ldry = t_dry*vp Prg = Prandtl(Cp=Cpg, k=kg, mu=mug) Prl = Prandtl(Cp=Cpl, k=kl, mu=mul) fg = (1.82*log10(Reg) - 1.64)**-2 fl = (1.82*log10(Rel) - 1.64)**-2 Nu_lam_Zl = 2*0.455*(Prl)**(1/3.)*(D*Rel/Ll)**0.5 Nu_trans_Zl = turbulent_Gnielinski(Re=Rel, Pr=Prl, fd=fl)*(1 + (D/Ll)**(2/3.)) if Ldry == 0: Nu_lam_Zg, Nu_trans_Zg = 0, 0 else: Nu_lam_Zg = 2*0.455*(Prg)**(1/3.)*(D*Reg/Ldry)**0.5 Nu_trans_Zg = turbulent_Gnielinski(Re=Reg, Pr=Prg, fd=fg)*(1 + (D/Ldry)**(2/3.)) h_Zg = kg/D*(Nu_lam_Zg**4 + Nu_trans_Zg**4)**0.25 h_Zl = kl/D*(Nu_lam_Zl**4 + Nu_trans_Zl**4)**0.25 h_film = 2*kl/(delta0 + C_delta0) return tl/tau*h_Zl + t_film/tau*h_film + t_dry/tau*h_Zg def to_solve_q_Thome(q: float, m: float, x: float, D: float, rhol: float, rhog: float, kl: float, kg: float, mul: float, mug: float, Cpl: float, Cpg: float, sigma: float, Hvap: float, Psat: float, Pc: float, Te: float) -> float: err = q/Thome(m=m, x=x, D=D, rhol=rhol, rhog=rhog, kl=kl, kg=kg, mul=mul, mug=mug, Cpl=Cpl, Cpg=Cpg, sigma=sigma, Hvap=Hvap, Psat=Psat, Pc=Pc, q=q) - Te return err def Yun_Heo_Kim(m: float, x: float, D: float, rhol: float, mul: float, Hvap: float, sigma: float, q: float | None=None, Te: float | None=None) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is as shown in [1]_ and [2]_, and also reviewed in [3]_. Either the heat flux or excess temperature is required for the calculation of heat transfer coefficient. Uses liquid Reynolds number, Weber number, and Boiling number. Weber number is defined in terms of the velocity if all fluid were liquid. .. math:: h_{tp} = 136876(Bg\cdot We_l)^{0.1993} Re_l^{-0.1626} .. math:: Re_l = \frac{G D (1-x)}{\mu_l} .. math:: We_l = \frac{G^2 D}{\rho_l \sigma} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] mul : float Viscosity of liquid [Pa*s] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] q : float, optional Heat flux to wall [W/m^2] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ has been reviewed. Examples -------- >>> Yun_Heo_Kim(m=1, x=0.4, D=0.3, rhol=567., mul=156E-6, sigma=0.02, Hvap=9E5, q=1E4) 9479.313988550184 References ---------- .. [1] Yun, Rin, Jae Hyeok Heo, and Yongchan Kim. "Evaporative Heat Transfer and Pressure Drop of R410A in Microchannels." International Journal of Refrigeration 29, no. 1 (January 2006): 92-100. doi:10.1016/j.ijrefrig.2005.08.005. .. [2] Yun, Rin, Jae Hyeok Heo, and Yongchan Kim. "Erratum to 'Evaporative Heat Transfer and Pressure Drop of R410A in Microchannels; [Int. J. Refrigeration 29 (2006) 92-100]." International Journal of Refrigeration 30, no. 8 (December 2007): 1468. doi:10.1016/j.ijrefrig.2007.08.003. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ G = m/(pi/4*D**2) V = G/rhol Rel = G*D*(1-x)/mul We = Weber(V=V, L=D, rho=rhol, sigma=sigma) if q is not None: Bg = Boiling(G=G, q=q, Hvap=Hvap) return 136876*(Bg*We)**0.1993*Rel**-0.1626 elif Te is not None: A = 136876*(We)**0.1993*Rel**-0.1626*(Te/G/Hvap)**0.1993 return A**(10000/8007.) else: raise ValueError("Either q or Te is needed for this correlation") def Chen_Edelstein(m: float, x: float, D: float, rhol: float, rhog: float, mul: float, mug: float, kl: float, Cpl: float, Hvap: float, sigma: float, dPsat: float, Te: float) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is developed in [1]_ and [2]_, and reviewed in [3]_. This model is one of the most often used. It uses the Dittus-Boelter correlation for turbulent convection and the Forster-Zuber correlation for pool boiling, and combines them with two factors `F` and `S`. .. math:: h_{tp} = S\cdot h_{nb} + F \cdot h_{sp,l} .. math:: h_{sp,l} = 0.023 Re_l^{0.8} Pr_l^{0.4} k_l/D .. math:: Re_l = \frac{DG(1-x)}{\mu_l} .. math:: h_{nb} = 0.00122\left( \frac{\lambda_l^{0.79} c_{p,l}^{0.45} \rho_l^{0.49}}{\sigma^{0.5} \mu^{0.29} H_{vap}^{0.24} \rho_g^{0.24}} \right)\Delta T_{sat}^{0.24} \Delta p_{sat}^{0.75} .. math:: F = (1 + X_{tt}^{-0.5})^{1.78} .. math:: X_{tt} = \left( \frac{1-x}{x}\right)^{0.9} \left(\frac{\rho_g}{\rho_l} \right)^{0.5}\left( \frac{\mu_l}{\mu_g}\right)^{0.1} .. math:: S = 0.9622 - 0.5822\left(\tan^{-1}\left(\frac{Re_L\cdot F^{1.25}} {6.18\cdot 10^4}\right)\right) Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] mug : float Viscosity of gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] dPsat : float Difference in Saturation pressure of fluid at Te and T, [Pa] Te : float Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ and [2]_ have been reviewed, but the model is only put together in the review of [3]_. Many other forms of this equation exist with different functions for `F` and `S`. Examples -------- >>> Chen_Edelstein(m=0.106, x=0.2, D=0.0212, rhol=567, rhog=18.09, ... mul=156E-6, mug=7.11E-6, kl=0.086, Cpl=2730, Hvap=2E5, sigma=0.02, ... dPsat=1E5, Te=3) 3289.058731974052 See Also -------- turbulent_Dittus_Boelter Forster_Zuber References ---------- .. [1] Chen, J. C. "Correlation for Boiling Heat Transfer to Saturated Fluids in Convective Flow." Industrial & Engineering Chemistry Process Design and Development 5, no. 3 (July 1, 1966): 322-29. doi:10.1021/i260019a023. .. [2] Edelstein, Sergio, A. J. Pérez, and J. C. Chen. "Analytic Representation of Convective Boiling Functions." AIChE Journal 30, no. 5 (September 1, 1984): 840-41. doi:10.1002/aic.690300528. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ G = m/(pi/4*D**2) Rel = D*G*(1-x)/mul Prl = Prandtl(Cp=Cpl, mu=mul, k=kl) hl = turbulent_Dittus_Boelter(Re=Rel, Pr=Prl)*kl/D Xtt = Lockhart_Martinelli_Xtt(x=x, rhol=rhol, rhog=rhog, mul=mul, mug=mug) F = (1 + Xtt**-0.5)**1.78 Re = Rel*F**1.25 S = 0.9622 - 0.5822*atan(Re/6.18E4) hnb = Forster_Zuber(Te=Te, dPsat=dPsat, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog) return hnb*S + hl*F def Chen_Bennett(m: float, x: float, D: float, rhol: float, rhog: float, mul: float, mug: float, kl: float, Cpl: float, Hvap: float, sigma: float, dPsat: float, Te: float) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is developed in [1]_ and [2]_, and reviewed in [3]_. This model is one of the most often used, and replaces the `Chen_Edelstein` correlation. It uses the Dittus-Boelter correlation for turbulent convection and the Forster-Zuber correlation for pool boiling, and combines them with two factors `F` and `S`. .. math:: h_{tp} = S\cdot h_{nb} + F \cdot h_{sp,l} .. math:: h_{sp,l} = 0.023 Re_l^{0.8} Pr_l^{0.4} k_l/D .. math:: Re_l = \frac{DG(1-x)}{\mu_l} .. math:: h_{nb} = 0.00122\left( \frac{\lambda_l^{0.79} c_{p,l}^{0.45} \rho_l^{0.49}}{\sigma^{0.5} \mu^{0.29} H_{vap}^{0.24} \rho_g^{0.24}} \right)\Delta T_{sat}^{0.24} \Delta p_{sat}^{0.75} .. math:: F = \left(\frac{Pr_1+1}{2}\right)^{0.444}\cdot (1+X_{tt}^{-0.5})^{1.78} .. math:: S = \frac{1-\exp(-F\cdot h_{conv} \cdot X_0/k_l)} {F\cdot h_{conv}\cdot X_0/k_l} .. math:: X_{tt} = \left( \frac{1-x}{x}\right)^{0.9} \left(\frac{\rho_g}{\rho_l} \right)^{0.5}\left( \frac{\mu_l}{\mu_g}\right)^{0.1} .. math:: X_0 = 0.041 \left(\frac{\sigma}{g \cdot (\rho_l-\rho_v)}\right)^{0.5} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] mug : float Viscosity of gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] dPsat : float Difference in Saturation pressure of fluid at Te and T, [Pa] Te : float Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ and [2]_ have been reviewed, but the model is only put together in the review of [3]_. Many other forms of this equation exist with different functions for `F` and `S`. Examples -------- >>> Chen_Bennett(m=0.106, x=0.2, D=0.0212, rhol=567, rhog=18.09, ... mul=156E-6, mug=7.11E-6, kl=0.086, Cpl=2730, Hvap=2E5, sigma=0.02, ... dPsat=1E5, Te=3) 4938.275351219369 See Also -------- Chen_Edelstein turbulent_Dittus_Boelter Forster_Zuber References ---------- .. [1] Bennett, Douglas L., and John C. Chen. "Forced Convective Boiling in Vertical Tubes for Saturated Pure Components and Binary Mixtures." AIChE Journal 26, no. 3 (May 1, 1980): 454-61. doi:10.1002/aic.690260317. .. [2] Bennett, Douglas L., M.W. Davies and B.L. Hertzler, The Suppression of Saturated Nucleate Boiling by Forced Convective Flow, American Institute of Chemical Engineers Symposium Series, vol. 76, no. 199. 91-103, 1980. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ G = m/(pi/4*D**2) Rel = D*G*(1-x)/mul Prl = Prandtl(Cp=Cpl, mu=mul, k=kl) hl = turbulent_Dittus_Boelter(Re=Rel, Pr=Prl)*kl/D Xtt = Lockhart_Martinelli_Xtt(x=x, rhol=rhol, rhog=rhog, mul=mul, mug=mug) F = ((Prl+1)/2.)**0.444*(1 + Xtt**-0.5)**1.78 X0 = 0.041*(sigma/(g*(rhol-rhog)))**0.5 S = (1 - exp(-F*hl*X0/kl))/(F*hl*X0/kl) hnb = Forster_Zuber(Te=Te, dPsat=dPsat, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog) return hnb*S + hl*F def Liu_Winterton(m: float, x: float, D: float, rhol: float, rhog: float, mul: float, kl: float, Cpl: float, MW: float, P: float, Pc: float, Te: float) -> float: r"""Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is as developed in [1]_, also reviewed in [2]_ and [3]_. Excess wall temperature is required to use this correlation. .. math:: h_{tp} = \sqrt{ (F\cdot h_l)^2 + (S\cdot h_{nb})^2} .. math:: S = \left( 1+0.055F^{0.1} Re_{L}^{0.16}\right)^{-1} .. math:: h_{l} = 0.023 Re_L^{0.8} Pr_l^{0.4} k_l/D .. math:: Re_L = \frac{GD}{\mu_l} .. math:: F = \left[ 1+ xPr_{l}(\rho_l/\rho_g-1)\right]^{0.35} .. math:: h_{nb} = \left(55\Delta Te^{0.67} \frac{P}{P_c}^{(0.12 - 0.2\log_{10} R_p)}(-\log_{10} \frac{P}{P_c})^{-0.55} MW^{-0.5}\right)^{1/0.33} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] MW : float Molecular weight of the fluid, [g/mol] P : float Pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ has been reviewed, and is accurately reproduced in [3]_. Uses the `Cooper` and `turbulent_Dittus_Boelter` correlations. A correction for horizontal flow at low Froude numbers is available in [1]_ but has not been implemented and is not recommended in several sources. Examples -------- >>> Liu_Winterton(m=1, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, ... mul=156E-6, Cpl=2300, P=1E6, Pc=22E6, MW=44.02, Te=7) 4747.749477190532 References ---------- .. [1] Liu, Z., and R. H. S. Winterton. "A General Correlation for Saturated and Subcooled Flow Boiling in Tubes and Annuli, Based on a Nucleate Pool Boiling Equation." International Journal of Heat and Mass Transfer 34, no. 11 (November 1991): 2759-66. doi:10.1016/0017-9310(91)90234-6. .. [2] Fang, Xiande, Zhanru Zhou, and Dingkun Li. "Review of Correlations of Flow Boiling Heat Transfer Coefficients for Carbon Dioxide." International Journal of Refrigeration 36, no. 8 (December 2013): 2017-39. doi:10.1016/j.ijrefrig.2013.05.015. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ G = m/(pi/4*D**2) ReL = D*G/mul Prl = Prandtl(Cp=Cpl, mu=mul, k=kl) hl = turbulent_Dittus_Boelter(Re=ReL, Pr=Prl)*kl/D F = (1 + x*Prl*(rhol/rhog - 1))**0.35 S = (1 + 0.055*F**0.1*ReL**0.16)**-1 h_nb = Cooper(Te=Te, P=P, Pc=Pc, MW=MW) return ((F*hl)**2 + (S*h_nb)**2)**0.5 ================================================ FILE: ht/boiling_nucleic.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import log10 from fluids.constants import g __all__: list[str] = [ "Bier", "Cooper", "Forster_Zuber", "Gorenflo", "HEDH_Montinsky", "HEDH_Taborek", "McNelly", "Montinsky", "Rohsenow", "Serth_HEDH", "Stephan_Abdelsalam", "Zuber", "h0_Gorenflow_1993", "h0_VDI_2e", "h_nucleic", "h_nucleic_all_methods", "h_nucleic_methods", "qmax_boiling", "qmax_boiling_all_methods", "qmax_boiling_methods", ] def Rohsenow(rhol: float, rhog: float, mul: float, kl: float, Cpl: float, Hvap: float, sigma: float, Te: float | None=None, q: float | None=None, Csf: float=0.013, n: float=1.7) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [2]_ as presented in [1]_. Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = {{\mu }_{L}} \Delta H_{vap} \left[ \frac{g( \rho_L-\rho_v)} {\sigma } \right]^{0.5}\left[\frac{C_{p,L}\Delta T_e^{2/3}}{C_{sf} \Delta H_{vap} Pr_L^n}\right]^3 With `q` specified: .. math:: h = \left({{\mu }_{L}} \Delta H_{vap} \left[ \frac{g( \rho_L-\rho_v)} {\sigma } \right]^{0.5}\left[\frac{C_{p,L}\Delta T_e^{2/3}}{C_{sf} \Delta H_{vap} Pr_L^n}\right]^3\right)^{1/3}q^{2/3} Parameters ---------- rhol : float Density of the liquid [kg/m^3] rhog : float Density of the produced gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Hvap : float Heat of vaporization of the fluid at P, [J/kg] sigma : float Surface tension of liquid [N/m] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Csf : float Rohsenow coefficient specific to fluid and metal [-] n : float Constant, 1 for water, 1.7 (default) for other fluids usually [-] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- No further work is required on this correlation. Multiple sources confirm its form and rearrangement. Examples -------- h for water at atmospheric pressure on oxidized aluminum. >>> Rohsenow(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, ... Hvap=2.257E6, sigma=0.0589, Te=4.9, Csf=0.011, n=1.26) 3723.655267067467 References ---------- .. [1] Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. .. [2] Rohsenow, Warren M. "A Method of Correlating Heat Transfer Data for Surface Boiling of Liquids." Technical Report. Cambridge, Mass. : M.I.T. Division of Industrial Cooporation, 1951 """ if Te is not None: return mul*Hvap*(g*(rhol-rhog)/sigma)**0.5*(Cpl*Te**(2/3.)/Csf/Hvap/(Cpl*mul/kl)**n)**3 elif q is not None: A = mul*Hvap*(g*(rhol-rhog)/sigma)**0.5*(Cpl/Csf/Hvap/(Cpl*mul/kl)**n)**3 return A**(1/3.)*q**(2/3.) else: raise ValueError("Either q or Te is needed for this correlation") def McNelly(rhol: float, rhog: float, kl: float, Cpl: float, Hvap: float, sigma: float, P: float, Te: float | None=None, q: float | None=None) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [2]_ as presented in [1]_. Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = \left(0.225\left(\frac{\Delta T_e C_{p,l}}{H_{vap}}\right)^{0.69} \left(\frac{P k_L}{\sigma}\right)^{0.31} \left(\frac{\rho_L}{\rho_V}-1\right)^{0.33}\right)^{1/0.31} With `q` specified: .. math:: h = 0.225\left(\frac{q C_{p,l}}{H_{vap}}\right)^{0.69} \left(\frac{P k_L}{\sigma}\right)^{0.31}\left(\frac{\rho_L}{\rho_V}-1\right)^{0.33} Parameters ---------- rhol : float Density of the liquid [kg/m^3] rhog : float Density of the produced gas [kg/m^3] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Hvap : float Heat of vaporization of the fluid at P, [J/kg] sigma : float Surface tension of liquid [N/m] P : float Saturation pressure of fluid, [Pa] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Further examples for this function are desired. Examples -------- Water boiling, with excess temperature of 4.3 K. >>> McNelly(Te=4.3, P=101325, Cpl=4180., kl=0.688, sigma=0.0588, ... Hvap=2.25E6, rhol=958., rhog=0.597) 533.8056972951352 References ---------- .. [1] Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. .. [2] McNelly M. J.: "A correlation of the rates of heat transfer to n ucleate boiling liquids," J. Imp Coll. Chem Eng Soc 7:18, 1953. """ if Te is not None: return (0.225*(Te*Cpl/Hvap)**0.69*(P*kl/sigma)**0.31*(rhol/rhog-1.)**0.33 )**(1./0.31) elif q is not None: return 0.225*(q*Cpl/Hvap)**0.69*(P*kl/sigma)**0.31*(rhol/rhog-1.)**0.33 else: raise ValueError("Either q or Te is needed for this correlation") def Forster_Zuber(rhol: float, rhog: float, mul: float, kl: float, Cpl: float, Hvap: float, sigma: float, dPsat: float, Te: float | None=None, q: float | None=None) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [2]_ as presented in [1]_. Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = 0.00122\left(\frac{k_L^{0.79} C_{p,l}^{0.45}\rho_L^{0.49}} {\sigma^{0.5}\mu_L^{0.29} H_{vap}^{0.24} \rho_V^{0.24}}\right) \Delta T_e^{0.24} \Delta P_{sat}^{0.75} With `q` specified: .. math:: h = \left[0.00122\left(\frac{k_L^{0.79} C_{p,l}^{0.45}\rho_L^{0.49}} {\sigma^{0.5}\mu_L^{0.29} H_{vap}^{0.24} \rho_V^{0.24}}\right) \Delta P_{sat}^{0.75} q^{0.24}\right]^{\frac{1}{1.24}} Parameters ---------- rhol : float Density of the liquid [kg/m^3] rhog : float Density of the produced gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Hvap : float Heat of vaporization of the fluid at P, [J/kg] sigma : float Surface tension of liquid [N/m] dPsat : float Difference in saturation pressure of the fluid at Te and T, [Pa] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Examples have been found in [1]_ and [3]_ and match exactly. Examples -------- Water boiling, with excess temperature of 4.3K from [1]_. >>> Forster_Zuber(Te=4.3, dPsat=3906*4.3, Cpl=4180., kl=0.688, ... mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) 3519.9239897462644 References ---------- .. [1] Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. .. [2] Forster, H. K., and N. Zuber. "Dynamics of Vapor Bubbles and Boiling Heat Transfer." AIChE Journal 1, no. 4 (December 1, 1955): 531-35. doi:10.1002/aic.690010425. .. [3] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ if Te is not None: return 0.00122*(kl**0.79*Cpl**0.45*rhol**0.49/sigma**0.5/mul**0.29/Hvap**0.24/rhog**0.24)*Te**0.24*dPsat**0.75 elif q is not None: return (0.00122*(kl**0.79*Cpl**0.45*rhol**0.49/sigma**0.5/mul**0.29/Hvap**0.24/rhog**0.24)*q**0.24*dPsat**0.75)**(1/1.24) else: raise ValueError("Either q or Te is needed for this correlation") def Montinsky(P: float, Pc: float, Te: float | None=None, q: float | None=None) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [2]_ as presented in [1]_. Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = \left(0.00417P_c^{0.69} \Delta Te^{0.7}\left[1.8(P/P_c)^{0.17} + 4(P/P_c)^{1.2} + 10(P/P_c)^{10}\right]\right)^{1/0.3} With `q` specified: .. math:: h = 0.00417P_c^{0.69} q^{0.7}\left[1.8(P/P_c)^{0.17} + 4(P/P_c)^{1.2} + 10(P/P_c)^{10}\right] Parameters ---------- P : float Saturation pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Formulas has been found consistent in all cited sources. Examples have been found in [1]_ and [3]_. The equation for this function is sometimes given with a constant of 3.7E-5 instead of 0.00417 if critical pressure is not internally converted to kPa. [3]_ lists a constant of 3.596E-5. Examples -------- Water boiling at 1 atm, with excess temperature of 4.3K from [1]_. >>> Montinsky(P=101325, Pc=22048321, Te=4.3) 1185.0509770292663 References ---------- .. [1] Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. .. [2] Mostinsky I. L.: "Application of the rule of corresponding states for the calculation of heat transfer and critical heat flux," Teploenergetika 4:66, 1963 English Abstr. Br Chem Eng 8(8):586, 1963 .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [4] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ if Te is not None: return (0.00417*(Pc/1000.)**0.69*Te**0.7*(1.8*(P/Pc)**0.17 + 4*(P/Pc)**1.2 +10*(P/Pc)**10))**(1/0.3) elif q is not None: return (0.00417*(Pc/1000.)**0.69*q**0.7*(1.8*(P/Pc)**0.17 + 4*(P/Pc)**1.2 +10*(P/Pc)**10)) else: raise ValueError("Either q or Te is needed for this correlation") _angles_Stephan_Abdelsalam = {"general": 35, "water": 45, "hydrocarbon": 35, "cryogenic": 1, "refrigerant": 35} def Stephan_Abdelsalam(rhol: float, rhog: float, mul: float, kl: float, Cpl: float, Hvap: float, sigma: float, Tsat: float, Te: float | None=None, q: float | None=None, kw: float=401.0, rhow: float=8.96, Cpw: float=384.0, angle: float | None=None, correlation: str="general") -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [2]_ as presented in [1]_. Five variants are possible. Either heat flux or excess temperature is required. The forms for `Te` are not shown here, but are similar to those of the other functions. .. math:: h = 0.23X_1^{0.674} X_2^{0.35} X_3^{0.371} X_5^{0.297} X_8^{-1.73} k_L/d_B .. math:: X1 = \frac{q D_d}{K_L T_{sat}} .. math:: X2 = \frac{\alpha^2 \rho_L}{\sigma D_d} .. math:: X3 = \frac{C_{p,L} T_{sat} D_d^2}{\alpha^2} .. math:: X4 = \frac{H_{vap} D_d^2}{\alpha^2} .. math:: X5 = \frac{\rho_V}{\rho_L} .. math:: X6 = \frac{C_{p,l} \mu_L}{k_L} .. math:: X7 = \frac{\rho_W C_{p,W} k_W}{\rho_L C_{p,L} k_L} .. math:: X8 = \frac{\rho_L-\rho_V}{\rho_L} .. math:: D_b = 0.0146\theta\sqrt{\frac{2\sigma}{g(\rho_L-\rho_g)}} Respectively, the following four correlations are for water, hydrocarbons, cryogenic fluids, and refrigerants. .. math:: h = 0.246\times 10^7 X1^{0.673} X4^{-1.58} X3^{1.26}X8^{5.22}k_L/d_B .. math:: h = 0.0546 X5^{0.335} X1^{0.67} X8^{-4.33} X4^{0.248}k_L/d_B .. math:: h = 4.82 X1^{0.624} X7^{0.117} X3^{0.374} X4^{-0.329}X5^{0.257} k_L/d_B .. math:: h = 207 X1^{0.745} X5^{0.581} X6^{0.533} k_L/d_B Parameters ---------- rhol : float Density of the liquid [kg/m^3] rhog : float Density of the produced gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Hvap : float Heat of vaporization of the fluid at P, [J/kg] sigma : float Surface tension of liquid [N/m] Tsat : float Saturation temperature at operating pressure [Pa] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] kw : float, optional Thermal conductivity of wall (only for cryogenics) [W/m/K] rhow : float, optional Density of the wall (only for cryogenics) [kg/m^3] Cpw : float, optional Heat capacity of wall (only for cryogenics) [J/kg/K] angle : float, optional Contact angle of bubble with wall [degrees] correlation : str, optional Any of 'general', 'water', 'hydrocarbon', 'cryogenic', or 'refrigerant' Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- If cryogenic correlation is selected, metal properties are used. Default values are the properties of copper at STP. The angle is selected automatically if a correlation is selected; if angle is provided anyway, the automatic selection is ignored. A IndexError exception is raised if the correlation is not in the dictionary _angles_Stephan_Abdelsalam. Examples -------- Example is from [3]_ and matches. >>> Stephan_Abdelsalam(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, ... sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, angle=35) 26722.441071108373 References ---------- .. [1] Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. .. [2] Stephan, K., and M. Abdelsalam. "Heat-Transfer Correlations for Natural Convection Boiling." International Journal of Heat and Mass Transfer 23, no. 1 (January 1980): 73-87. doi:10.1016/0017-9310(80)90140-4. .. [3] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ if Te is None and q is None: raise ValueError("Either q or Te is needed for this correlation") if correlation == "water": angle = 45.0 elif correlation == "cryogenic": angle = 1.0 elif True: angle = 35.0 db = 0.0146*angle*(2*sigma/g/(rhol-rhog))**0.5 diffusivity_L = kl/rhol/Cpl if Te is not None: X1 = db/kl/Tsat*Te elif q is not None: X1 = db/kl/Tsat*q X2 = diffusivity_L**2*rhol/sigma/db X3 = Hvap*db**2/diffusivity_L**2 X4 = Hvap*db**2/diffusivity_L**2 X5 = rhog/rhol X6 = Cpl*mul/kl X7 = rhow*Cpw*kw/(rhol*Cpl*kl) X8 = (rhol-rhog)/rhol if correlation == "general": if Te is not None: h = (0.23*X1**0.674*X2**0.35*X3**0.371*X5**0.297*X8**-1.73*kl/db)**(1/0.326) else: h = (0.23*X1**0.674*X2**0.35*X3**0.371*X5**0.297*X8**-1.73*kl/db) elif correlation == "water": if Te is not None: h = (0.246E7*X1**0.673*X4**-1.58*X3**1.26*X8**5.22*kl/db)**(1/0.327) else: h = (0.246E7*X1**0.673*X4**-1.58*X3**1.26*X8**5.22*kl/db) elif correlation == "hydrocarbon": if Te is not None: h = (0.0546*X5**0.335*X1**0.67*X8**-4.33*X4**0.248*kl/db)**(1/0.33) else: h = (0.0546*X5**0.335*X1**0.67*X8**-4.33*X4**0.248*kl/db) elif correlation == "cryogenic": if Te is not None: h = (4.82*X1**0.624*X7**0.117*X3**0.374*X4**-0.329*X5**0.257*kl/db)**(1/0.376) else: h = (4.82*X1**0.624*X7**0.117*X3**0.374*X4**-0.329*X5**0.257*kl/db) else: if Te is not None: h = (207*X1**0.745*X5**0.581*X6**0.533*kl/db)**(1/0.255) else: h = (207*X1**0.745*X5**0.581*X6**0.533*kl/db) return h def HEDH_Taborek(P: float, Pc: float, Te: float | None=None, q: float | None=None) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to Taborek (1986) as described in [1]_ and as presented in [2]_. Modification of [3]_. Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = \left(0.00417P_c^{0.69} \Delta Te^{0.7}\left[2.1P_r^{0.27} + \left(9 + (1-Pr^2)^{-1}\right)P_r^2 \right]\right)^{1/0.3} With `q` specified: .. math:: h = 0.00417P_c^{0.69} q^{0.7}\left[2.1P_r^{0.27} + \left(9 + (1-Pr^2 )^{-1}\right)P_r^2\right] Parameters ---------- P : float Saturation pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Example is from [3]_ and matches to within the error of the algebraic manipulation rounding. Examples -------- >>> HEDH_Taborek(Te=16.2, P=310.3E3, Pc=2550E3) 1397.272486525486 References ---------- .. [1] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [2] Mostinsky I. L.: "Application of the rule of corresponding states for the calculation of heat transfer and critical heat flux," Teploenergetika 4:66, 1963 English Abstr. Br Chem Eng 8(8):586, 1963 .. [3] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ Pr = P/Pc if Te is not None: return (0.00417*(Pc/1000.)**0.69*Te**0.7*(2.1*Pr**0.27 + (9 + 1./(1-Pr**2))*Pr**2))**(1/0.3) elif q is not None: return (0.00417*(Pc/1000.)**0.69*q**0.7*(2.1*Pr**0.27 + (9 + 1./(1-Pr**2))*Pr**2)) else: raise ValueError("Either q or Te is needed for this correlation") def Bier(P: float, Pc: float, Te: float | None=None, q: float | None=None) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [1]_ . Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = \left(0.00417P_c^{0.69} \Delta Te^{0.7}\left[0.7 + 2P_r\left(4 + \frac{1}{1-P_r}\right) \right]\right)^{1/0.3} With `q` specified: .. math:: h = 0.00417P_c^{0.69} \Delta q^{0.7}\left[0.7 + 2P_r\left(4 + \frac{1}{1-P_r}\right) \right] Parameters ---------- P : float Saturation pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- No examples of this are known. Seems to give very different results than other correlations. Examples -------- Water boiling at 1 atm, with excess temperature of 4.3 K from [1]_. >>> Bier(101325., 22048321.0, Te=4.3) 1290.5349471503353 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ Pr = P/Pc if Te is not None: return (0.00417*(Pc/1000.)**0.69*Te**0.7*(0.7 + 2.*Pr*(4. + 1./(1.-Pr))))**(1./0.3) elif q is not None: return 0.00417*(Pc/1000.)**0.69*q**0.7*(0.7 + 2.*Pr*(4. + 1./(1. - Pr))) else: raise ValueError("Either q or Te is needed for this correlation") def Cooper(P: float, Pc: float, MW: float, Te: float | None=None, q: float | None=None, Rp: float=1E-6) -> float: r"""Calculates heat transfer coefficient for a evaporator operating in the nucleate boiling regime according to [2]_ as presented in [1]_. Either heat flux or excess temperature is required. With `Te` specified: .. math:: h = \left(55\Delta Te^{0.67} \frac{P}{P_c}^{(0.12 - 0.2\log_{10} R_p)} (-\log_{10} \frac{P}{P_c})^{-0.55} MW^{-0.5}\right)^{1/0.33} With `q` specified: .. math:: h = 55q^{0.67} \frac{P}{P_c}^{(0.12 - 0.2\log_{10} R_p)}(-\log_{10} \frac{P}{P_c})^{-0.55} MW^{-0.5} Parameters ---------- P : float Saturation pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] MW : float Molecular weight of fluid, [g/mol] Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Rp : float, optional Roughness parameter of the surface (1 micrometer default) used by `Cooper` method, [m] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Examples 1 and 2 are for water and benzene, from [1]_. Roughness parameter is with an old definition. Accordingly, it is not used by the h function. If unchanged, the roughness parameter's logarithm gives a value of 0.12 as an exponent of reduced pressure. Examples -------- Water boiling at 1 atm, with excess temperature of 4.3 K from [1]_. >>> Cooper(P=101325., Pc=22048321.0, MW=18.02, Te=4.3) 1558.1435442153575 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] M. G. Cooper, "Saturation and Nucleate Pool Boiling: A Simple Correlation," Inst. Chem. Eng. Syrup. Ser. (86/2): 785, 1984. .. [3] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ Rp*= 1E6 if Te is not None: return (55*Te**0.67*(P/Pc)**(0.12 - 0.2*log10(Rp))*( -log10(P/Pc))**-0.55*MW**-0.5)**(1/0.33) elif q is not None: return (55*q**0.67*(P/Pc)**(0.12 - 0.2*log10(Rp))*( -log10(P/Pc))**-0.55*MW**-0.5) else: raise ValueError("Either q or Te is needed for this correlation") h0_Gorenflow_1993 = {"74-82-8": 7000.0, "74-84-0": 4500.0, "74-98-6": 4000.0, "106-97-8": 3600.0, "109-66-0": 3400.0, "78-78-4": 2500.0, "110-54-3": 3300.0, "142-82-5": 3200.0, "71-43-2": 2900.0, "108-88-3": 2800.0, "92-52-4": 2100.0, "67-56-1": 5400.0, "64-17-5": 4400.0, "71-23-8": 3800.0, "67-63-0": 3000.0, "71-36-3": 2600.0, "78-83-1": 4500.0, "67-64-1": 3300.0, "75-69-4": 2800.0, "75-71-8": 4000.0, "75-72-9": 3900.0, "75-63-8": 3500.0, "75-45-6": 3900.0, "75-46-7": 4400.0, "76-13-1": 2650.0, "76-14-2": 3800.0, "76-15-3": 3200.0, "811-97-2": 4500.0, "28987-04-4": 3700.0, "431-89-0": 3800.0, "115-25-3": 4200.0, "74-87-3": 4400.0, "56-23-5": 3200.0, "75-73-0": 4750.0, "7732-18-5": 5600.0, "7664-41-7": 7000.0, "124-38-9": 5100.0, "2551-62-4": 3700.0, "7782-44-7": 9500.0, "7727-37-9": 10000.0, "7440-37-1": 8200.0, "7440-01-9": 20000.0, "1333-74-0": 24000.0, "7440-59-7": 2000.0} IS_NUMBA = "IS_NUMBA" in globals() if IS_NUMBA: h0_Gorenflow_1993_keys = tuple(h0_Gorenflow_1993.keys()) h0_Gorenflow_1993_values = tuple(h0_Gorenflow_1993.values()) def Gorenflo(P: float, Pc: float, q: float | None=None, Te: float | None=None, CASRN: str | None=None, h0: float | None=None, Ra: float=4E-7) -> float: r"""Calculates heat transfer coefficient for a pool boiling according to [1]_ and also presented in [2]_. Calculation is based on the corresponding states law, with a single regression constant per fluid. P and Pc are always required. Either `q` or `Te` may be specified. Either `CASRN` or `h0` may be specified as well. If `CASRN` is specified and the fluid is not in the list of those studied, an error is raises. .. math:: \frac{h}{h_0} = C_W F(p^*) \left(\frac{q}{q_0}\right)^n .. math:: C_W = \left(\frac{R_a}{R_{ao}}\right)^{0.133} .. math:: q_0 = 20 \;000 \frac{\text{W}}{\text{m}^{2}} .. math:: R_{ao} = 0.4 \mu\text{m} For fluids other than water: .. math:: n = 0.9 - 0.3 p^{*0.3} .. math:: f(p^*) = 1.2p^{*0.27} + \left(2.5 + \frac{1}{1-p^*}\right)p^* For water: .. math:: n = 0.9 - 0.3 p^{*0.15} .. math:: f(p^*) = 1.73p^{*0.27} + \left(6.1 + \frac{0.68}{1-p^*}\right)p^2 Parameters ---------- P : float Saturation pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] q : float, optional Heat flux, [W/m^2] Te : float, optional Excess wall temperature, [K] CASRN : str, optional CASRN of fluid h0 : float Reference heat transfer coefficient for Gorenflo method, [W/m^2/K] Ra : float, optional Roughness parameter of the surface (0.4 micrometer default) for Gorenflo method, [m] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- A more recent set of reference heat fluxes is available. Where a range of values was listed for reference heat fluxes in [1]_, values from the second edition of [1]_ were used instead. 44 values are available, all listed in the dictionary `h0_Gorenflow_1993`. Values range from 2000 to 24000 W/m^2/K. Examples -------- Water boiling at 3 bar and a heat flux of 2E4 W/m^2/K. >>> Gorenflo(3E5, 22048320., q=2E4, CASRN='7732-18-5') 3043.344595525422 References ---------- .. [1] Schlunder, Ernst U, VDI. VDI Heat Atlas. Dusseldorf: V.D.I. Verlag, 1993. http://digital.ub.uni-paderborn.de/hs/download/pdf/41898?originalFilename=true .. [2] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. """ Pr = P/Pc Ra0 = 0.4E-6 q0 = 2E4 if h0 is None: # NUMBA: DELETE try: h0 = h0_Gorenflow_1993[CASRN] except: raise ValueError("Reference heat transfer coefficient not known") if h0 is None: try: h0 = h0_Gorenflow_1993_values[h0_Gorenflow_1993_keys.index(CASRN)] except: raise ValueError("Reference heat transfer coefficient not known") if CASRN != "7732-18-5": # Case for not dealing with water n = 0.9 - 0.3*Pr**0.3 Fp = 1.2*Pr**0.27 + (2.5 + 1/(1-Pr))*Pr else: # Case for water n = 0.9 - 0.3*Pr**0.15 Fp = 1.73*Pr**0.27 + (6.1 + 0.68/(1-Pr))*Pr**2 CW = (Ra/Ra0)**0.133 if q is not None: return h0*CW*Fp*(q/q0)**n elif Te is not None: A = h0*CW*Fp*(Te/q0)**n return A**(-1./(n - 1.)) else: raise ValueError("Either q or Te is needed for this correlation") h0_VDI_2e = {"74-82-8": 7200.0, "74-85-1": 4200.0, "74-84-0": 4600.0, "115-07-1": 4200.0, "74-98-6": 4300.0, "106-97-8": 3600.0, "75-28-5": 3700.0, "109-66-0": 3300.0, "78-78-4": 3200.0, "110-54-3": 3200.0, "110-82-7": 3000.0, "142-82-5": 2900.0, "71-43-2": 2900.0, "108-88-3": 2800.0, "92-52-4": 2100.0, "67-56-1": 5400.0, "64-17-5": 4350.0, "71-23-8": 3750.0, "67-63-0": 4100.0, "71-36-3": 2600.0, "78-83-1": 4500.0, "78-92-2": 3400.0, "75-07-0": 3500.0, "67-64-1": 3300.0, "124-38-9": 5500.0, "75-46-7": 4800.0, "75-10-5": 5000.0, "354-33-6": 4400.0, "811-97-2": 4200.0, "420-46-2": 4700.0, "75-37-6": 4600.0, "754-12-1": 3000.0, "431-89-0": 4100.0, "115-25-3": 4200.0, "75-73-0": 4750.0, "306-83-2": 3000.0, "75-69-4": 2800.0, "75-71-8": 4000.0, "75-72-9": 3900.0, "75-63-8": 3500.0, "75-45-6": 3900.0, "76-13-1": 2650.0, "76-14-2": 3800.0, "76-15-3": 4200.0, "74-87-3": 4400.0, "56-23-5": 3200.0, "2551-62-4": 3700.0, "7732-18-5": 5600.0, "7664-41-7": 7000.0, "7782-44-7": 9500.0, "7727-37-9": 10000.0, "7440-37-1": 8200.0, "7440-01-9": 20000.0, "1333-74-0": 24000.0, "7440-59-7": 2000.0} cryogenics = {"132259-10-0": "Air", "7440-37-1": "Argon", "630-08-0": "carbon monoxide", "7782-39-0": "deuterium", "7782-41-4": "fluorine", "7440-59-7": "helium", "1333-74-0": "hydrogen", "7439-90-9": "krypton", "74-82-8": "methane", "7440-01-9": "neon", "7727-37-9": "nitrogen", "7782-44-7": "oxygen", "7440-63-3": "xenon"} h_nucleic_all_methods = ["Stephan-Abdelsalam", "Stephan-Abdelsalam water", "Stephan-Abdelsalam cryogenic", "HEDH-Taborek", "Forster-Zuber", "Rohsenow", "Cooper", "Bier", "Montinsky", "McNelly", "Gorenflo (1993)"] def h_nucleic_methods(Te: float | None=None, Tsat: float | None=None, P: float | None=None, dPsat: float | None=None, Cpl: float | None=None, kl: float | None=None, mul: float | None=None, rhol: float | None=None, sigma: float | None=None, Hvap: float | None=None, rhog: float | None=None, MW: float | None=None, Pc: float | None=None, CAS: str | None=None, check_ranges: bool=False) -> list[str]: r"""This function returns the names of correlations for nucleate boiling heat flux. Parameters ---------- Te : float, optional Excess wall temperature, [K] Tsat : float, optional Saturation temperature at operating pressure [Pa] P : float, optional Saturation pressure of fluid, [Pa] dPsat : float, optional Difference in saturation pressure of the fluid at Te and T, [Pa] Cpl : float, optional Heat capacity of liquid [J/kg/K] kl : float, optional Thermal conductivity of liquid [W/m/K] mul : float, optional Viscosity of liquid [Pa*s] rhol : float, optional Density of the liquid [kg/m^3] sigma : float, optional Surface tension of liquid [N/m] Hvap : float, optional Heat of vaporization of the fluid at P, [J/kg] rhog : float, optional Density of the produced gas [kg/m^3] MW : float, optional Molecular weight of fluid, [g/mol] Pc : float, optional Critical pressure of fluid, [Pa] CAS : str, optional CAS of fluid check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `h` with the given inputs Examples -------- >>> h_nucleic_methods(P=3E5, Pc=22048320., Te=4.0, CAS='7732-18-5') ['Gorenflo (1993)', 'HEDH-Taborek', 'Bier', 'Montinsky'] """ methods = [] if P is not None and Pc is not None: if CAS is not None and CAS in h0_Gorenflow_1993: # numba: delete # if CAS is not None and CAS in h0_Gorenflow_1993_keys: # numba: uncomment methods.append("Gorenflo (1993)") if (Te is not None and Tsat is not None and Cpl is not None and kl is not None and mul is not None and sigma is not None and Hvap is not None and rhol is not None and rhog is not None): if CAS is not None and CAS == "7732-18-5": methods.append("Stephan-Abdelsalam water") if CAS is not None and CAS in cryogenics: methods.append("Stephan-Abdelsalam cryogenic") methods.append("Stephan-Abdelsalam") if Te is not None and P is not None and Pc is not None: methods.append("HEDH-Taborek") if (Te is not None and dPsat is not None and Cpl is not None and kl is not None and mul is not None and sigma is not None and Hvap is not None and rhol is not None and rhog is not None): methods.append("Forster-Zuber") if (Te is not None and Cpl is not None and kl is not None and mul is not None and sigma is not None and Hvap is not None and rhol is not None and rhog is not None): methods.append("Rohsenow") if MW is not None and Te is not None and P is not None and Pc is not None: methods.append("Cooper") if Te is not None and P is not None and Pc is not None: methods.extend(["Bier", "Montinsky"]) if (Te is not None and P is not None and Cpl is not None and kl is not None and sigma is not None and Hvap is not None and rhol is not None and rhog is not None): methods.append("McNelly") return methods def h_nucleic(Te: float | None=None, q: float | None=None, Tsat: float | None=None, P: float | None=None, dPsat: float | None=None, Cpl: float | None=None, kl: float | None=None, mul: float | None=None, rhol: float | None=None, sigma: float | None=None, Hvap: float | None=None, rhog: float | None=None, MW: float | None=None, Pc: float | None=None, Csf: float=0.013, n: float=1.7, kw: float=401.0, rhow: float=8.96, Cpw: float=384.0, angle: float=35.0, Rp: float=1e-6, Ra: float=0.4e-6, h0: None=None, CAS: str | None=None, Method: str | None=None) -> float: r"""This function handles the calculation of nucleate boiling heat flux and chooses the best method for performing the calculation based on the provided information. One of `Te` and `q` are always required. Parameters ---------- Te : float, optional Excess wall temperature, [K] q : float, optional Heat flux, [W/m^2] Tsat : float, optional Saturation temperature at operating pressure [Pa] P : float, optional Saturation pressure of fluid, [Pa] dPsat : float, optional Difference in saturation pressure of the fluid at Te and T, [Pa] Cpl : float, optional Heat capacity of liquid [J/kg/K] kl : float, optional Thermal conductivity of liquid [W/m/K] mul : float, optional Viscosity of liquid [Pa*s] rhol : float, optional Density of the liquid [kg/m^3] sigma : float, optional Surface tension of liquid [N/m] Hvap : float, optional Heat of vaporization of the fluid at P, [J/kg] rhog : float, optional Density of the produced gas [kg/m^3] MW : float, optional Molecular weight of fluid, [g/mol] Pc : float, optional Critical pressure of fluid, [Pa] Csf : float, optional Rohsenow coefficient specific to fluid and metal [-] n : float, optional Rohsenow constant, 1 for water, 1.7 (default) for other fluids usually [-] kw : float, optional Thermal conductivity of wall (only for cryogenics) [W/m/K] rhow : float, optional Density of the wall (only for cryogenics) [kg/m^3] Cpw : float, optional Heat capacity of wall (only for cryogenics) [J/kg/K] angle : float, optional Contact angle of bubble with wall [degrees] Rp : float, optional Roughness parameter of the surface (1 micrometer default) used by `Cooper` method, [m] Ra : float, optional Roughness parameter of the surface (0.4 micrometer default) for Gorenflo method, [m] h0 : float Reference heat transfer coefficient for Gorenflo method, [W/m^2/K] CAS : str, optional CAS of fluid Returns ------- h : float Nucleate boiling heat flux [W/m^2] Other Parameters ---------------- Method : string, optional The name of the method to use; one of ['Gorenflo (1993)', 'Stephan-Abdelsalam water', 'Stephan-Abdelsalam cryogenic', 'Stephan-Abdelsalam', 'HEDH-Taborek', 'Forster-Zuber', 'Rohsenow', 'Cooper', 'Bier', 'Montinsky', 'McNelly'] Notes ----- The methods Stephan-Abdelsalam, Cooper, and Gorenflo all take other arguments as well such as surface roughness or the thermal properties of the wall material. See them for their documentation. These parameters can also be passed as keyword arguments. >>> h_nucleic(P=3E5, Pc=22048320., q=2E4, CAS='7732-18-5', Ra=1E-6) 3437.7726419934147 Examples -------- Water boiling at 3 bar and a heat flux of 2E4 W/m^2/K. >>> h_nucleic(P=3E5, Pc=22048320., q=2E4, CAS='7732-18-5') 3043.344595525422 Water, known excess temperature of 4.9 K, Rohsenow method >>> h_nucleic(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, ... Hvap=2.257E6, sigma=0.0589, Te=4.9, Csf=0.011, n=1.26, ... Method='Rohsenow') 3723.655267067467 """ if Method is None: methods = h_nucleic_methods(Te=Te, Tsat=Tsat, P=P, dPsat=dPsat, Cpl=Cpl, kl=kl, mul=mul, rhol=rhol, sigma=sigma, Hvap=Hvap, rhog=rhog, MW=MW, Pc=Pc, CAS=CAS) if not methods: raise ValueError("Insufficient property data for any method.") Method = methods[0] if Method == "Stephan-Abdelsalam"and Tsat is not None: return Stephan_Abdelsalam(Te=Te, q=q, Tsat=Tsat, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog, correlation="general", kw=kw, rhow=rhow, Cpw=Cpw, angle=angle) elif Method == "Stephan-Abdelsalam water" and Tsat is not None: return Stephan_Abdelsalam(Te=Te, q=q, Tsat=Tsat, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog, correlation="water", kw=kw, rhow=rhow, Cpw=Cpw, angle=angle) elif Method == "Stephan-Abdelsalam cryogenic" and Tsat is not None: return Stephan_Abdelsalam(Te=Te, q=q, Tsat=Tsat, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog, correlation="cryogenic", kw=kw, rhow=rhow, Cpw=Cpw, angle=angle) elif Method == "HEDH-Taborek" and P is not None and Pc is not None: return HEDH_Taborek(Te=Te, q=q, P=P, Pc=Pc) elif Method == "Forster-Zuber" and dPsat is not None: return Forster_Zuber(Te=Te, q=q, dPsat=dPsat, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog) elif Method == "Rohsenow": return Rohsenow(Te=Te, q=q, Cpl=Cpl, kl=kl, mul=mul, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog, Csf=Csf, n=n) elif Method == "Cooper": return Cooper(Te=Te, q=q, P=P, Pc=Pc, MW=MW, Rp=Rp) elif Method == "Bier" and P is not None and Pc is not None: return Bier(Te=Te, q=q, P=P, Pc=Pc) elif Method == "Montinsky" and P is not None and Pc is not None: return Montinsky(Te=Te, q=q, P=P, Pc=Pc) elif Method == "McNelly": return McNelly(Te=Te, q=q, P=P, Cpl=Cpl, kl=kl, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog) elif Method == "Gorenflo (1993)": return Gorenflo(P=P, q=q, Pc=Pc, Te=Te, CASRN=CAS, h0=h0, Ra=Ra) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") ### Critical Heat Flux def Zuber(sigma: float, Hvap: float, rhol: float, rhog: float, K: float=0.18) -> float: r"""Calculates critical heat flux for nucleic boiling of a flat plate or other shape as presented in various sources. K = pi/24 is believed to be the original [1]_ value for K, but 0.149 is now more widely used, a value claimed to be from [2]_ according to [5]_. Cao [4]_ lists a value of 0.18 for K. The Wolverine Tube data book also lists a value of 0.18, and so it is the default. .. math:: q_c = {KH}_{vap} \rho_g^{0.5}\left[\sigma g (\rho_L-\rho_g)\right]^{0.25} Parameters ---------- sigma : float Surface tension of liquid [N/m] Hvap : float Heat of vaporization of the fluid at P, [J/kg] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the produced gas [kg/m^3] K : float Constant [] Returns ------- q : float Critical heat flux [W/m^2] Notes ----- No further work is required on this correlation. Multiple sources confirm its form. Examples -------- Example from [3]_ >>> Zuber(sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09, K=0.149) 444307.22304342285 >>> Zuber(sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09, K=0.18) 536746.9808578263 References ---------- .. [1] Zuber N. "On the stability of boiling heat transfer". Trans ASME 1958 80:711-20. .. [2] Lienhard, J.H., and Dhir, V.K., 1973, Extended Hydrodynamic Theory of the Peak and Minimum Heat Fluxes, NASA CR-2270. .. [3] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. .. [4] Cao, Eduardo. Heat Transfer in Process Engineering. McGraw Hill Professional, 2009. .. [5] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer, 7E.Mason, OH: Cengage Learning, 2010. """ return K*Hvap*rhog**0.5*(g*sigma*(rhol-rhog))**0.25 def Serth_HEDH(D: float, sigma: float, Hvap: float, rhol: float, rhog: float) -> float: r"""Calculates critical heat flux for nucleic boiling of a tube bundle according to [2]_, citing [3]_, and using [1]_ as the original form. .. math:: q_c = KH_{vap} \rho_g^{0.5}\left[\sigma g (\rho_L-\rho_g)\right]^{0.25} .. math:: K = 0.123 (R^*)^{-0.25} \text{ for 0.12 < R* < 1.17} .. math:: K = 0.118 .. math:: R^* = \frac{D}{2} \left[\frac{g(\rho_L-\rho_G)}{\sigma}\right]^{0.5} Parameters ---------- D : float Diameter of tubes [m] sigma : float Surface tension of liquid [N/m] Hvap : float Heat of vaporization of the fluid at T, [J/kg] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the produced gas [kg/m^3] Returns ------- q : float Critical heat flux [W/m^2] Notes ----- A further source for this would be nice. Examples -------- >>> Serth_HEDH(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09) 351867.46522901946 References ---------- .. [1] Zuber N. "On the stability of boiling heat transfer". Trans ASME 1958 80:711-20. .. [2] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. .. [3] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. """ R = D/2*(g*(rhol-rhog)/sigma)**0.5 if 0.12 <= R <= 1.17: K = 0.125*R**-0.25 else: K = 0.118 return K*Hvap*rhog**0.5*(g*sigma*(rhol-rhog))**0.25 def HEDH_Montinsky(P: float, Pc: float) -> float: r"""Calculates critical heat flux in the nucleate boiling regime according to [3]_ as presented in [1]_, using an expression modified from [2]_. .. math:: q_c = 367 P_cP_r^{0.35}(1-P_r)^{0.9} Parameters ---------- P : float Saturation pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] Returns ------- q : float Critical heat flux [W/m^2] Notes ----- No further work is required. Units of Pc are kPa internally. Examples -------- Example is from [3]_ and matches to within the error of the algebraic manipulation rounding. >>> HEDH_Montinsky(P=310.3E3, Pc=2550E3) 398405.66545181436 References ---------- .. [1] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [2] Mostinsky I. L.: "Application of the rule of corresponding states for the calculation of heat transfer and critical heat flux," Teploenergetika 4:66, 1963 English Abstr. Br Chem Eng 8(8):586, 1963 .. [3] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ Pr = P/Pc return 367*(Pc/1000.)*Pr**0.35*(1-Pr)**0.9 qmax_boiling_all_methods = ["Serth-HEDH", "Zuber", "HEDH-Montinsky"] def qmax_boiling_methods(rhol: int | None=None, rhog: float | None=None, sigma: float | None=None, Hvap: float | None=None, D: float | None=None, P: float | None=None, Pc: float | None=None, check_ranges: bool=False) -> list[str]: r"""This function returns a list of methods names which can be used to calculate nucleate boiling critical heat flux. Preferred methods are 'Serth-HEDH' when a tube diameter is specified, and 'Zuber' otherwise. Parameters ---------- rhol : float, optional Density of the liquid [kg/m^3] rhog : float, optional Density of the produced gas [kg/m^3] sigma : float, optional Surface tension of liquid [N/m] Hvap : float, optional Heat of vaporization of the fluid at T, [J/kg] D : float, optional Diameter of tubes [m] P : float, optional Saturation pressure of fluid, [Pa] Pc : float, optional Critical pressure of fluid, [Pa] check_ranges : bool, optional Added for Future use only Returns ------- methods : list List of methods which can be used to calculate qmax with the given inputs Examples -------- >>> qmax_boiling_methods(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09) ['Serth-HEDH', 'Zuber'] """ methods = [] if (sigma is not None and Hvap is not None and rhol is not None and rhog is not None and D is not None): methods.append("Serth-HEDH") if (sigma is not None and Hvap is not None and rhol is not None and rhog is not None): methods.append("Zuber") if P is not None and Pc is not None: methods.append("HEDH-Montinsky") return methods def qmax_boiling(rhol: int | None=None, rhog: float | None=None, sigma: float | None=None, Hvap: float | None=None, D: float | None=None, P: float | None=None, Pc: float | None=None, Method: str | None=None) -> float: r"""This function handles the calculation of nucleate boiling critical heat flux and chooses the best method for performing the calculation. Preferred methods are 'Serth-HEDH' when a tube diameter is specified, and 'Zuber' otherwise. Parameters ---------- rhol : float, optional Density of the liquid [kg/m^3] rhog : float, optional Density of the produced gas [kg/m^3] sigma : float, optional Surface tension of liquid [N/m] Hvap : float, optional Heat of vaporization of the fluid at T, [J/kg] D : float, optional Diameter of tubes [m] P : float, optional Saturation pressure of fluid, [Pa] Pc : float, optional Critical pressure of fluid, [Pa] Returns ------- q : float Nucleate boiling critical heat flux [W/m^2] Other Parameters ---------------- Method : string, optional A string of the function name to use; one of ('Serth-HEDH', 'Zuber', or 'HEDH-Montinsky') Examples -------- >>> qmax_boiling(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09) 351867.46522901946 """ if Method is None: if (sigma is not None and Hvap is not None and rhol is not None and rhog is not None and D is not None): Method2 = "Serth-HEDH" elif (sigma is not None and Hvap is not None and rhol is not None and rhog is not None): Method2 = "Zuber" elif P is not None and Pc is not None: Method2 = "HEDH-Montinsky" else: raise ValueError("Insufficient property or geometry data for any " "method.") else: Method2 = Method if Method2 == "Serth-HEDH": return Serth_HEDH(D=D, sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog) elif Method2 == "Zuber": return Zuber(sigma=sigma, Hvap=Hvap, rhol=rhol, rhog=rhog) elif Method2 == "HEDH-Montinsky": return HEDH_Montinsky(P=P, Pc=Pc) else: raise ValueError("Correlation name not recognized; options are " "'Serth-HEDH', 'Zuber' and 'HEDH-Montinsky'") ================================================ FILE: ht/boiling_plate.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2017, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import radians from fluids.constants import g from fluids.core import Bond, Prandtl, thermal_diffusivity from fluids.two_phase_voidage import Lockhart_Martinelli_Xtt __all__: list[str] = [ "h_boiling_Amalfi", "h_boiling_Han_Lee_Kim", "h_boiling_Huang_Sheer", "h_boiling_Lee_Kang_Kim", "h_boiling_Yan_Lin", ] def h_boiling_Amalfi(m: float, x: float, Dh: float, rhol: float, rhog: float, mul: float, mug: float, kl: float, Hvap: float, sigma: float, q: float, A_channel_flow: float, chevron_angle: float=45.0) -> float: r"""Calculates the two-phase boiling heat transfer coefficient of a liquid and gas flowing inside a plate and frame heat exchanger, as developed in [1]_ from a wide range of existing correlations and data sets. Expected to be the most accurate correlation currently available. For Bond number < 4 (tiny channel case): .. math:: h= 982 \left(\frac{k_l}{D_h}\right)\left(\frac{\beta}{\beta_{max}}\right)^{1.101} \left(\frac{G^2 D_h}{\rho_m \sigma}\right)^{0.315} \left(\frac{\rho_l}{\rho_g}\right)^{-0.224} Bo^{0.320} For Bond number >= 4: .. math:: h = 18.495 \left(\frac{k_l}{D_h}\right) \left(\frac{\beta}{\beta_{max}} \right)^{0.248}\left(Re_g\right)^{0.135}\left(Re_{lo}\right)^{0.351} \left(\frac{\rho_l}{\rho_g}\right)^{-0.223} Bd^{0.235} Bo^{0.198} In the above equations, beta max is 45 degrees; Bo is Boiling number; and Bd is Bond number. Note that this model depends on the specific heat flux involved. Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific point in the plate exchanger [] Dh : float Hydraulic diameter of the plate, :math:`D_h = \frac{4\lambda}{\phi}` [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of the liquid [Pa*s] mug : float Viscosity of the gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of the fluid at the system pressure, [J/kg] sigma : float Surface tension of liquid [N/m] q : float Heat flux, [W/m^2] A_channel_flow : float The flow area for the fluid, calculated as :math:`A_{ch} = 2\cdot \text{width} \cdot \text{amplitude}` [m] chevron_angle : float, optional Angle of the plate corrugations with respect to the vertical axis (the direction of flow if the plates were straight), between 0 and 90. For exchangers with two angles, use the average value. [degrees] Returns ------- h : float Boiling heat transfer coefficient [W/m^2/K] Notes ----- Heat transfer correlation developed from 1903 datum. Fluids included R134a, ammonia, R236fa, R600a, R290, R1270, R1234yf, R410A, R507A, ammonia/water, and air/water mixtures. Wide range of operating conditions, plate geometries. Examples -------- >>> h_boiling_Amalfi(m=3E-5, x=.4, Dh=0.00172, rhol=567., rhog=18.09, ... kl=0.086, mul=156E-6, mug=7.11E-6, sigma=0.02, Hvap=9E5, q=1E5, ... A_channel_flow=0.0003) 776.0781179096225 References ---------- .. [1] Amalfi, Raffaele L., Farzad Vakili-Farahani, and John R. Thome. "Flow Boiling and Frictional Pressure Gradients in Plate Heat Exchangers. Part 2: Comparison of Literature Methods to Database and New Prediction Methods." International Journal of Refrigeration 61 (January 2016): 185-203. doi:10.1016/j.ijrefrig.2015.07.009. """ chevron_angle_max = 45. beta_s = chevron_angle/chevron_angle_max rho_s = (rhol/rhog) # rho start in model G = m/A_channel_flow # Calculating the area of the channel is normally specified well Bd = Bond(rhol=rhol, rhog=rhog, sigma=sigma, L=Dh) rho_h = 1./(x/rhog + (1-x)/rhol) # homogeneous holdup - mixture density calculation We_m = G*G*Dh/sigma/rho_h Bo = q/(G*Hvap) # Boiling number if Bd < 4: # Should occur normally for "microscale" conditions Nu_tp = 982*beta_s**1.101*We_m**0.315*Bo**0.320*rho_s**-0.224 else: Re_lo = G*Dh/mul Re_g = G*x*Dh/mug Nu_tp = 18.495*beta_s**0.135*Re_g**0.135*Re_lo**0.351*Bd**0.235*Bo**0.198*rho_s**-0.223 return kl/Dh*Nu_tp def h_boiling_Lee_Kang_Kim(m: float, x: float, D_eq: float, rhol: float, rhog: float, mul: float, mug: float, kl: float, Hvap: float, q: float, A_channel_flow: float) -> float: r"""Calculates the two-phase boiling heat transfer coefficient of a liquid and gas flowing inside a plate and frame heat exchanger, as shown in [1]_ and reviewed in [2]_. For :math:`Re_g/Re_l < 9`: .. math:: h = 98.7 \left(\frac{k_l}{D_h}\right)\left(\frac{Re_g}{Re_l} \right)^{-0.0848}Bo^{-0.0597} X_{tt}^{0.0973} For :math:`Re_g/Re_l \ge 9`: .. math:: h = 234.9 \left(\frac{k_l}{D_h}\right)\left(\frac{Re_g}{Re_l} \right)^{-0.576} Bo^{-0.275} X_{tt}^{0.66} .. math:: X_{tt} = \left(\frac{1-x}{x}\right)^{0.875} \left(\frac{\rho_g}{\rho_l} \right)^{0.5}\left(\frac{\mu_l}{\mu_g}\right)^{0.125} In the above equations, Bo is Boiling number. Note that this model depends on the specific heat flux involved. It also uses equivalent diameter, not hydraulic diameter. Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific point in the plate exchanger [] D_eq : float Equivalent diameter of the channels, :math:`D_{eq} = 4a` [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of the liquid [Pa*s] mug : float Viscosity of the gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of the fluid at the system pressure, [J/kg] q : float Heat flux, [W/m^2] A_channel_flow : float The flow area for the fluid, calculated as :math:`A_{ch} = 2\cdot \text{width} \cdot \text{amplitude}` [m] Returns ------- h : float Boiling heat transfer coefficient [W/m^2/K] Notes ----- This correlation was developed with mass fluxes from 14.5 to 33.6 kg/m^2/s, heat flux from 15 to 30 kW/m^2, qualities from 0.09 to 0.6, 200 < Re < 600, 2.3 < Re_g/Re_l < 32.1, 0.00019 < Bo < 0.001, 0.028 < Xtt < 0.3. Mean average deviation of 4.4%. Examples -------- >>> h_boiling_Lee_Kang_Kim(m=3E-5, x=.4, D_eq=0.002, rhol=567., rhog=18.09, ... kl=0.086, mul=156E-6, mug=9E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003) 1229.6271295086806 References ---------- .. [1] Lee, Eungchan, Hoon Kang, and Yongchan Kim. "Flow Boiling Heat Transfer and Pressure Drop of Water in a Plate Heat Exchanger with Corrugated Channels at Low Mass Flux Conditions." International Journal of Heat and Mass Transfer 77 (October 2014): 37-45. doi:10.1016/j.ijheatmasstransfer.2014.05.019. .. [2] Amalfi, Raffaele L., Farzad Vakili-Farahani, and John R. Thome. "Flow Boiling and Frictional Pressure Gradients in Plate Heat Exchangers. Part 1: Review and Experimental Database." International Journal of Refrigeration 61 (January 2016): 166-84. doi:10.1016/j.ijrefrig.2015.07.010. """ G = m/A_channel_flow Bo = q/(G*Hvap) Re_ratio = x/(1. - x)*mul/mug Xtt = Lockhart_Martinelli_Xtt(x, rhol, rhog, mul, mug, pow_x=0.875, pow_rho=0.5, pow_mu=0.125) if Re_ratio < 9: h = 98.7*kl/D_eq*Re_ratio**-0.0848*Bo**-0.0597*Xtt**0.0973 else: h = 234.9*kl/D_eq*Re_ratio**-0.576*Bo**-0.275*Xtt**0.66 return h def h_boiling_Han_Lee_Kim(m: float, x: float, Dh: float, rhol: float, rhog: float, mul: float, kl: float, Hvap: float, Cpl: float, q: float, A_channel_flow: float, wavelength: float, chevron_angle: int=45.0) -> float: r"""Calculates the two-phase boiling heat transfer coefficient of a liquid and gas flowing inside a plate and frame heat exchanger, as developed in [1]_ from experiments with three plate exchangers and the working fluids R410A and R22. A well-documented and tested correlation, reviewed in [2]_, [3]_, [4]_, [5]_, and [6]_. .. math:: h = Ge_1\left(\frac{k_l}{D_h}\right)Re_{eq}^{Ge_2} Pr^{0.4} Bo_{eq}^{0.3} .. math:: Ge_1 = 2.81\left(\frac{\lambda}{D_h}\right)^{-0.041}\left(\frac{\pi}{2} -\beta\right)^{-2.83} .. math:: Ge_2 = 0.746\left(\frac{\lambda}{D_h}\right)^{-0.082}\left(\frac{\pi} {2}-\beta\right)^{0.61} .. math:: Re_{eq} = \frac{G_{eq} D_h}{\mu_l} .. math:: Bo_{eq} = \frac{q}{G_{eq} H_{vap}} .. math:: G_{eq} = \frac{m}{A_{flow}}\left[1 - x + x\left(\frac{\rho_l}{\rho_g} \right)^{1/2}\right] In the above equations, lambda is the wavelength of the corrugations, and the flow area is specified to be (twice the corrugation amplitude times the width of the plate. The mass flow is that per channel. Radians is used in degrees, and the formulas are for the inclination angle not the chevron angle (it is converted internally). Note that this model depends on the specific heat flux involved. Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific point in the plate exchanger [] Dh : float Hydraulic diameter of the plate, :math:`D_h = \frac{4\lambda}{\phi}` [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of the liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of the fluid at the system pressure, [J/kg] Cpl : float Heat capacity of liquid [J/kg/K] q : float Heat flux, [W/m^2] A_channel_flow : float The flow area for the fluid, calculated as :math:`A_{ch} = 2\cdot \text{width} \cdot \text{amplitude}` [m] wavelength : float Distance between the bottoms of two of the ridges (sometimes called pitch), [m] chevron_angle : float, optional Angle of the plate corrugations with respect to the vertical axis (the direction of flow if the plates were straight), between 0 and 90. For exchangers with two angles, use the average value. [degrees] Returns ------- h : float Boiling heat transfer coefficient [W/m^2/K] Notes ----- Date regression was with the log mean temperature difference, uncorrected for geometry. Developed with three plate heat exchangers with angles of 45, 35, and 20 degrees. Mass fluxes ranged from 13 to 34 kg/m^2/s; evaporating temperatures of 5, 10, and 15 degrees, vapor quality 0.9 to 0.15, heat fluxes of 2.5-8.5 kW/m^2. Examples -------- >>> h_boiling_Han_Lee_Kim(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, ... kl=0.086, mul=156E-6, Hvap=9E5, Cpl=2200, q=1E5, A_channel_flow=0.0003, ... wavelength=3.7E-3, chevron_angle=45) 675.7322255419421 References ---------- .. [1] Han, Dong-Hyouck, Kyu-Jung Lee, and Yoon-Ho Kim. "Experiments on the Characteristics of Evaporation of R410A in Brazed Plate Heat Exchangers with Different Geometric Configurations." Applied Thermal Engineering 23, no. 10 (July 2003): 1209-25. doi:10.1016/S1359-4311(03)00061-9. .. [2] Amalfi, Raffaele L., Farzad Vakili-Farahani, and John R. Thome. "Flow Boiling and Frictional Pressure Gradients in Plate Heat Exchangers. Part 1: Review and Experimental Database." International Journal of Refrigeration 61 (January 2016): 166-84. doi:10.1016/j.ijrefrig.2015.07.010. .. [3] Eldeeb, Radia, Vikrant Aute, and Reinhard Radermacher. "A Survey of Correlations for Heat Transfer and Pressure Drop for Evaporation and Condensation in Plate Heat Exchangers." International Journal of Refrigeration 65 (May 2016): 12-26. doi:10.1016/j.ijrefrig.2015.11.013. .. [4] Solotych, Valentin, Donghyeon Lee, Jungho Kim, Raffaele L. Amalfi, and John R. Thome. "Boiling Heat Transfer and Two-Phase Pressure Drops within Compact Plate Heat Exchangers: Experiments and Flow Visualizations." International Journal of Heat and Mass Transfer 94 (March 2016): 239-253. doi:10.1016/j.ijheatmasstransfer.2015.11.037. .. [5] García-Cascales, J. R., F. Vera-García, J. M. Corberán-Salvador, and J. Gonzálvez-Maciá. "Assessment of Boiling and Condensation Heat Transfer Correlations in the Modelling of Plate Heat Exchangers." International Journal of Refrigeration 30, no. 6 (September 2007): 1029-41. doi:10.1016/j.ijrefrig.2007.01.004. .. [6] Huang, Jianchang. "Performance Analysis of Plate Heat Exchangers Used as Refrigerant Evaporators," 2011. Thesis. http://wiredspace.wits.ac.za/handle/10539/9779 """ chevron_angle = radians(chevron_angle) G = m/A_channel_flow # For once, clearly defined in the publication G_eq = G*((1. - x) + x*(rhol/rhog)**0.5) Re_eq = G_eq*Dh/mul Bo_eq = q/(G_eq*Hvap) Pr = Prandtl(Cp=Cpl, k=kl, mu=mul) Ge1 = 2.81*(wavelength/Dh)**-0.041*chevron_angle**-2.83 Ge2 = 0.746*(wavelength/Dh)**-0.082*chevron_angle**0.61 return Ge1*kl/Dh*Re_eq**Ge2*Bo_eq**0.3*Pr**0.4 def h_boiling_Huang_Sheer(rhol: float, rhog: float, mul: float, kl: float, Hvap: float, sigma: float, Cpl: float, q: float, Tsat: float, angle: float=35.) -> float: r"""Calculates the two-phase boiling heat transfer coefficient of a liquid and gas flowing inside a plate and frame heat exchanger, as developed in [1]_ and again in the thesis [2]_. Depends on the properties of the fluid and not the heat exchanger's geometry. .. math:: h = 1.87\times10^{-3}\left(\frac{k_l}{d_o}\right)\left(\frac{q d_o} {k_l T_{sat}}\right)^{0.56} \left(\frac{H_{vap} d_o^2}{\alpha_l^2}\right)^{0.31} Pr_l^{0.33} .. math:: d_o = 0.0146\theta\left[\frac{2\sigma}{g(\rho_l-\rho_g)}\right]^{0.5}\\ \theta = 35^\circ Note that this model depends on the specific heat flux involved and the saturation temperature of the fluid. Parameters ---------- rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of the liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of the fluid at the system pressure, [J/kg] sigma : float Surface tension of liquid [N/m] Cpl : float Heat capacity of liquid [J/kg/K] q : float Heat flux, [W/m^2] Tsat : float Actual saturation temperature of the fluid at the system pressure, [K] angle : float, optional Contact angle of the bubbles with the wall, assumed 35 for refrigerants in the development of the correlation [degrees] Returns ------- h : float Boiling heat transfer coefficient [W/m^2/K] Notes ----- Developed with 222 data points for R134a and R507A with only two of them for ammonia and R12. Chevron angles ranged from 28 to 60 degrees, heat fluxes from 1.85 kW/m^2 to 10.75 kW/m^2, mass fluxes 5.6 to 52.25 kg/m^2/s, qualities from 0.21 to 0.95, and saturation temperatures in degrees Celcius of 1.9 to 13.04. The inclusion of the saturation temperature makes this correlation have limited predictive power for other fluids whose saturation tempratures might be much higher or lower than those used in the development of the correlation. For this reason it should be regarded with caution. As first published in [1]_ a power of two was missing in the correlation for bubble diameter in the dimensionless group with a power of 0.31. That made the correlation non-dimensional. A second variant of this correlation was also published in [2]_ but with less accuracy because it was designed to mimick the standard pool boiling curve. The correlation is reviewed in [3]_, but without the corrected power. It was also changed there to use hydraulic diameter, not bubble diameter. It still ranked as one of the more accurate correlations reviewed. [4]_ also reviewed it without the corrected power but found it predicted the lowest results of those surveyed. Examples -------- >>> h_boiling_Huang_Sheer(rhol=567., rhog=18.09, kl=0.086, mul=156E-6, ... Hvap=9E5, sigma=0.02, Cpl=2200, q=1E4, Tsat=279.15) 4401.055635078054 References ---------- .. [1] Huang, Jianchang, Thomas J. Sheer, and Michael Bailey-McEwan. "Heat Transfer and Pressure Drop in Plate Heat Exchanger Refrigerant Evaporators." International Journal of Refrigeration 35, no. 2 (March 2012): 325-35. doi:10.1016/j.ijrefrig.2011.11.002. .. [2] Huang, Jianchang. "Performance Analysis of Plate Heat Exchangers Used as Refrigerant Evaporators," 2011. Thesis. http://wiredspace.wits.ac.za/handle/10539/9779 .. [3] Amalfi, Raffaele L., Farzad Vakili-Farahani, and John R. Thome. "Flow Boiling and Frictional Pressure Gradients in Plate Heat Exchangers. Part 1: Review and Experimental Database." International Journal of Refrigeration 61 (January 2016): 166-84. doi:10.1016/j.ijrefrig.2015.07.010. .. [4] Eldeeb, Radia, Vikrant Aute, and Reinhard Radermacher. "A Survey of Correlations for Heat Transfer and Pressure Drop for Evaporation and Condensation in Plate Heat Exchangers." International Journal of Refrigeration 65 (May 2016): 12-26. doi:10.1016/j.ijrefrig.2015.11.013. """ do = 0.0146*angle*(2.*sigma/(g*(rhol - rhog)))**0.5 Prl = Prandtl(Cp=Cpl, mu=mul, k=kl) alpha_l = thermal_diffusivity(k=kl, rho=rhol, Cp=Cpl) h = 1.87E-3*(kl/do)*(q*do/(kl*Tsat))**0.56*(Hvap*do**2/alpha_l**2)**0.31*Prl**0.33 return h def h_boiling_Yan_Lin(m: float, x: float, Dh: float, rhol: float, rhog: float, mul: float, kl: float, Hvap: float, Cpl: float, q: float, A_channel_flow: float) -> float: r"""Calculates the two-phase boiling heat transfer coefficient of a liquid and gas flowing inside a plate and frame heat exchanger, as developed in [1]_. Reviewed in [2]_, [3]_, [4]_, and [5]_. .. math:: h = 1.926\left(\frac{k_l}{D_h}\right) Re_{eq} Pr_l^{1/3} Bo_{eq}^{0.3} Re^{-0.5} .. math:: Re_{eq} = \frac{G_{eq} D_h}{\mu_l} .. math:: Bo_{eq} = \frac{q}{G_{eq} H_{vap}} .. math:: G_{eq} = \frac{m}{A_{flow}}\left[1 - x + x\left(\frac{\rho_l}{\rho_g} \right)^{1/2}\right] .. math:: Re = \frac{G D_h}{\mu_l} Claimed to be valid for :math:`2000 < Re_{eq} < 10000`. Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific point in the plate exchanger [] Dh : float Hydraulic diameter of the plate, :math:`D_h = \frac{4\lambda}{\phi}` [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of the liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Hvap : float Heat of vaporization of the fluid at the system pressure, [J/kg] Cpl : float Heat capacity of liquid [J/kg/K] q : float Heat flux, [W/m^2] A_channel_flow : float The flow area for the fluid, calculated as :math:`A_{ch} = 2\cdot \text{width} \cdot \text{amplitude}` [m] Returns ------- h : float Boiling heat transfer coefficient [W/m^2/K] Notes ----- Developed with R134a as the refrigerant in a PHD with 2 channels, chevron angle 60 degrees, quality from 0.1 to 0.8, heat flux 11-15 kW/m^2, and mass fluxes of 55 and 70 kg/m^2/s. Examples -------- >>> h_boiling_Yan_Lin(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, ... kl=0.086, Cpl=2200, mul=156E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003) 318.7228565961241 References ---------- .. [1] Yan, Y.-Y., and T.-F. Lin. "Evaporation Heat Transfer and Pressure Drop of Refrigerant R-134a in a Plate Heat Exchanger." Journal of Heat Transfer 121, no. 1 (February 1, 1999): 118-27. doi:10.1115/1.2825924. .. [2] Amalfi, Raffaele L., Farzad Vakili-Farahani, and John R. Thome. "Flow Boiling and Frictional Pressure Gradients in Plate Heat Exchangers. Part 1: Review and Experimental Database." International Journal of Refrigeration 61 (January 2016): 166-84. doi:10.1016/j.ijrefrig.2015.07.010. .. [3] Eldeeb, Radia, Vikrant Aute, and Reinhard Radermacher. "A Survey of Correlations for Heat Transfer and Pressure Drop for Evaporation and Condensation in Plate Heat Exchangers." International Journal of Refrigeration 65 (May 2016): 12-26. doi:10.1016/j.ijrefrig.2015.11.013. .. [4] García-Cascales, J. R., F. Vera-García, J. M. Corberán-Salvador, and J. Gonzálvez-Maciá. "Assessment of Boiling and Condensation Heat Transfer Correlations in the Modelling of Plate Heat Exchangers." International Journal of Refrigeration 30, no. 6 (September 2007): 1029-41. doi:10.1016/j.ijrefrig.2007.01.004. .. [5] Huang, Jianchang. "Performance Analysis of Plate Heat Exchangers Used as Refrigerant Evaporators," 2011. Thesis. http://wiredspace.wits.ac.za/handle/10539/9779 """ G = m/A_channel_flow G_eq = G*((1. - x) + x*(rhol/rhog)**0.5) Re_eq = G_eq*Dh/mul Re = G*Dh/mul # Not actually specified clearly but it is in another paper by them Bo_eq = q/(G_eq*Hvap) Pr_l = Prandtl(Cp=Cpl, k=kl, mu=mul) return 1.926*(kl/Dh)*Re_eq*Pr_l**(1/3.)*Bo_eq**0.3*Re**-0.5 ================================================ FILE: ht/condensation.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import pi, sin from fluids.constants import R, g from fluids.core import Prandtl, Reynolds from ht.conv_internal import turbulent_Dittus_Boelter __all__: list[str] = [ "Akers_Deans_Crosser", "Boyko_Kruzhilin", "Cavallini_Smith_Zecchin", "Nusselt_laminar", "Shah", "h_kinetic", ] def Nusselt_laminar(Tsat: float, Tw: float, rhog: float, rhol: float, kl: float, mul: float, Hvap: float, L: float, angle: float=90.) -> float: r"""Calculates heat transfer coefficient for laminar film condensation of a pure chemical on a flat plate, as presented in [1]_ according to an analysis performed by Nusselt in 1916. .. math:: h=0.943\left[\frac{g\sin(\theta)\rho_{liq}(\rho_l-\rho_v)k_{l}^3 \Delta H_{vap}}{\mu_l(T_{sat}-T_w)L}\right]^{0.25} Parameters ---------- Tsat : float Saturation temperature at operating pressure [K] Tw : float Wall temperature, [K] rhog : float Density of the gas [kg/m^3] rhol : float Density of the liquid [kg/m^3] kl : float Thermal conductivity of liquid [W/m/K] mul : float Viscosity of liquid [Pa*s] Hvap : float Heat of vaporization of the fluid at P, [J/kg] L : float Length of the plate [m] angle : float, optional Angle of inclination of the plate [degrees] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Optionally, the plate may be inclined. The constant 0.943 is actually: .. math:: 2\sqrt{2}/3 Examples -------- p. 578 in [1]_, matches exactly. >>> Nusselt_laminar(Tsat=370, Tw=350, rhog=7.0, rhol=585., kl=0.091, ... mul=158.9E-6, Hvap=776900, L=0.1) 1482.206403453679 References ---------- .. [1] Hewitt, G. L. Shires T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1E. Boca Raton: CRC Press, 1994. """ return 2.*2.**0.5/3.*(kl**3*rhol*(rhol - rhog)*g*sin(angle/180.*pi) *Hvap/(mul*(Tsat - Tw)*L))**0.25 def Boyko_Kruzhilin(m: float, rhog: float, rhol: float, kl: float, mul: float, Cpl: float, D: float, x: float) -> float: r"""Calculates heat transfer coefficient for condensation of a pure chemical inside a vertical tube or tube bundle, as presented in [2]_ according to [1]_. .. math:: h_f = h_{LO}\left[1 + x\left(\frac{\rho_L}{\rho_G} - 1\right)\right]^{0.5} .. math:: h_{LO} = 0.021 \frac{k_L}{L} Re_{LO}^{0.8} Pr^{0.43} Parameters ---------- m : float Mass flow rate [kg/s] rhog : float Density of the gas [kg/m^3] rhol : float Density of the liquid [kg/m^3] kl : float Thermal conductivity of liquid [W/m/K] mul : float Viscosity of liquid [Pa*s] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] D : float Diameter of the tubing [m] x : float Quality at the specific interval [-] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- To calculate overall heat transfer coefficient during condensation, simply average values at x = 1 and x = 0. Examples -------- Page 589 in [2]_, matches exactly. >>> Boyko_Kruzhilin(m=500*pi/4*.03**2, rhog=6.36, rhol=582.9, kl=0.098, ... mul=159E-6, Cpl=2520., D=0.03, x=0.85) 10598.657227479956 References ---------- .. [1] Boyko, L. D., and G. N. Kruzhilin. "Heat Transfer and Hydraulic Resistance during Condensation of Steam in a Horizontal Tube and in a Bundle of Tubes." International Journal of Heat and Mass Transfer 10, no. 3 (March 1, 1967): 361-73. doi:10.1016/0017-9310(67)90152-4. .. [2] Hewitt, G. L. Shires T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1E. Boca Raton: CRC Press, 1994. """ Vlo = m/rhol/(pi/4.*D**2) Relo = rhol*Vlo*D/mul Prl = mul*Cpl/kl hlo = 0.021*kl/D*Relo**0.8*Prl**0.43 return hlo*(1. + x*(rhol/rhog - 1.))**0.5 def Akers_Deans_Crosser(m: float, rhog: float, rhol: float, kl: float, mul: float, Cpl: float, D: float, x: float) -> float: r"""Calculates heat transfer coefficient for condensation of a pure chemical inside a vertical tube or tube bundle, as presented in [2]_ according to [1]_. .. math:: Nu = \frac{hD_i}{k_l} = C Re_e^n Pr_l^{1/3} .. math:: C = 0.0265, n=0.8 \text{ for } Re_e > 5\times10^4 .. math:: C = 5.03, n=\frac{1}{3} \text{ for } Re_e < 5\times10^4 .. math:: Re_e = \frac{D_i G_e}{\mu_l} .. math:: G_e = G\left[(1-x)+x(\rho_l/\rho_g)^{0.5}\right] Parameters ---------- m : float Mass flow rate [kg/s] rhog : float Density of the gas [kg/m^3] rhol : float Density of the liquid [kg/m^3] kl : float Thermal conductivity of liquid [W/m/K] mul : float Viscosity of liquid [Pa*s] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] D : float Diameter of the tubing [m] x : float Quality at the specific interval [-] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Examples -------- >>> Akers_Deans_Crosser(m=0.35, rhog=6.36, rhol=582.9, kl=0.098, ... mul=159E-6, Cpl=2520., D=0.03, x=0.85) 7117.24177265201 References ---------- .. [1] Akers, W. W., H. A. Deans, and O. K. Crosser. "Condensing Heat Transfer Within Horizontal Tubes." Chem. Eng. Progr. Vol: 55, Symposium Ser. No. 29 (January 1, 1959). .. [2] Kakaç, Sadik, ed. Boilers, Evaporators, and Condensers. 1st. Wiley-Interscience, 1991. """ G = m/(pi/4*D**2) Ge = G*((1-x) + x*(rhol/rhog)**0.5) Ree = D*Ge/mul Prl = mul*Cpl/kl if Ree > 5E4: C, n = 0.0265, 0.8 else: C, n = 5.03, 1/3. Nu = C*Ree**n*Prl**(1/3.) return Nu*kl/D #print([Akers_Deans_Crosser(m=0.01, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=0.85)]) def h_kinetic(T: float, P: float, MW: float, Hvap: float, f: float=1.0) -> float: r"""Calculates heat transfer coefficient for condensation of a pure chemical inside a vertical tube or tube bundle, as presented in [2]_ according to [1]_. .. math:: h = \left(\frac{2f}{2-f}\right)\left(\frac{MW}{1000\cdot 2\pi R T} \right)^{0.5}\left(\frac{H_{vap}^2 P \cdot MW}{1000\cdot RT^2}\right) Parameters ---------- T : float Vapor temperature, [K] P : float Vapor pressure, [Pa] MW : float Molecular weight of the gas, [g/mol] Hvap : float Heat of vaporization of the fluid at P, [J/kg] f : float Correction factor, [-] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- f is a correction factor for how the removal of gas particles affects the behavior of the ideal gas in diffusing to the condensing surface. It is quite close to one, and has not been well explored in the literature due to the rarity of the importance of the kinetic resistance. Examples -------- Water at 1 bar and 300 K: >>> h_kinetic(300, 1E5, 18.02, 2441674) 30788829.908851154 References ---------- .. [1] Berman, L. D. "On the Effect of Molecular-Kinetic Resistance upon Heat Transfer with Condensation." International Journal of Heat and Mass Transfer 10, no. 10 (October 1, 1967): 1463. doi:10.1016/0017-9310(67)90033-6. .. [2] Kakaç, Sadik, ed. Boilers, Evaporators, and Condensers. 1 edition. Wiley-Interscience, 1991. .. [3] Stephan, Karl. Heat Transfer in Condensation and Boiling. Translated by C. V. Green. Softcover reprint of the original 1st ed. 1992 edition. Berlin; New York: Springer, 2013. """ return (2*f)/(2-f)*(MW/(1000*2*pi*R*T))**0.5*(Hvap**2*P*MW)/(1000*R*T**2) def Cavallini_Smith_Zecchin(m: float, x: float, D: float, rhol: float, rhog: float, mul: float, mug: float, kl: float, Cpl: float) -> float: r"""Calculates heat transfer coefficient for condensation of a fluid inside a tube, as presented in [1]_, also given in [2]_ and [3]_. .. math:: Nu = \frac{hD_i}{k_l} = 0.05 Re_e^{0.8} Pr_l^{0.33} .. math:: Re_{eq} = Re_g(\mu_g/\mu_l)(\rho_l/\rho_g)^{0.5} + Re_l .. math:: v_{gs} = \frac{mx}{\rho_g \frac{\pi}{4}D^2} .. math:: v_{ls} = \frac{m(1-x)}{\rho_l \frac{\pi}{4}D^2} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific interval [-] D : float Diameter of the channel [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] mug : float Viscosity of gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Examples -------- >>> Cavallini_Smith_Zecchin(m=1, x=0.4, D=.3, rhol=800, rhog=2.5, mul=1E-5, mug=1E-3, kl=0.6, Cpl=2300) 5578.218369177804 References ---------- .. [1] A. Cavallini, J. R. Smith and R. Zecchin, A dimensionless correlation for heat transfer in forced convection condensation, 6th International Heat Transfer Conference., Tokyo, Japan (1974) 309-313. .. [2] Kakaç, Sadik, ed. Boilers, Evaporators, and Condensers. 1st. Wiley-Interscience, 1991. .. [3] Balcilar, Muhammet, Ahmet Selim Dalkiliç, Berna Bolat, and Somchai Wongwises. "Investigation of Empirical Correlations on the Determination of Condensation Heat Transfer Characteristics during Downward Annular Flow of R134a inside a Vertical Smooth Tube Using Artificial Intelligence Algorithms." Journal of Mechanical Science and Technology 25, no. 10 (October 12, 2011): 2683-2701. doi:10.1007/s12206-011-0618-2. """ Prl = Prandtl(Cp=Cpl, mu=mul, k=kl) Vl = m*(1-x)/(rhol*pi/4*D**2) Vg = m*x/(rhog*pi/4*D**2) Rel = Reynolds(V=Vl, D=D, rho=rhol, mu=mul) Reg = Reynolds(V=Vg, D=D, rho=rhog, mu=mug) """The following was coded, and may be used instead of the above lines, to check that the definitions of parameters here provide the same results as those defined in [1]_. G = m/(pi/4*D**2) Re = G*D/mul Rel = Re*(1-x) Reg = Re*x/(mug/mul)""" Reeq = Reg*(mug/mul)*(rhol/rhog)**0.5 + Rel Nul = 0.05*Reeq**0.8*Prl**0.33 return Nul*kl/D # confirmed to be with respect to the liquid def Shah(m: float, x: float, D: float, rhol: float, mul: float, kl: float, Cpl: float, P: float, Pc: float) -> float: r"""Calculates heat transfer coefficient for condensation of a fluid inside a tube, as presented in [1]_ and again by the same author in [2]_; also given in [3]_. Requires no properties of the gas. Uses the Dittus-Boelter correlation for single phase heat transfer coefficient, with a Reynolds number assuming all the flow is liquid. .. math:: h_{TP} = h_L\left[(1-x)^{0.8} +\frac{3.8x^{0.76}(1-x)^{0.04}} {P_r^{0.38}}\right] Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific interval [-] D : float Diameter of the channel [m] rhol : float Density of the liquid [kg/m^3] mul : float Viscosity of liquid [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] P : float Pressure of the fluid, [Pa] Pc : float Critical pressure of the fluid, [Pa] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ is well written an unambiguous as to how to apply this equation. Examples -------- >>> Shah(m=1, x=0.4, D=.3, rhol=800, mul=1E-5, kl=0.6, Cpl=2300, P=1E6, Pc=2E7) 2561.2593415479214 References ---------- .. [1] Shah, M. M. "A General Correlation for Heat Transfer during Film Condensation inside Pipes." International Journal of Heat and Mass Transfer 22, no. 4 (April 1, 1979): 547-56. doi:10.1016/0017-9310(79)90058-9. .. [2] Shah, M. M., Heat Transfer During Film Condensation in Tubes and Annuli: A Review of the Literature, ASHRAE Transactions, vol. 87, no. 3, pp. 1086-1100, 1981. .. [3] Kakaç, Sadik, ed. Boilers, Evaporators, and Condensers. 1st. Wiley-Interscience, 1991. """ VL = m/(rhol*pi/4*D**2) ReL = Reynolds(V=VL, D=D, rho=rhol, mu=mul) Prl = Prandtl(Cp=Cpl, k=kl, mu=mul) hL = turbulent_Dittus_Boelter(ReL, Prl)*kl/D Pr = P/Pc return hL*((1-x)**0.8 + 3.8*x**0.76*(1-x)**0.04/Pr**0.38) ================================================ FILE: ht/conduction.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import acosh, log, pi from fluids.constants import Btu, degree_Fahrenheit, foot, hour, inch __all__: list[str] = [ "R_cylinder", "R_to_k", "R_value_to_k", "S_isothermal_pipe_eccentric_to_isothermal_pipe", "S_isothermal_pipe_normal_to_plane", "S_isothermal_pipe_to_isothermal_pipe", "S_isothermal_pipe_to_plane", "S_isothermal_pipe_to_two_planes", "S_isothermal_sphere_to_plane", "cylindrical_heat_transfer", "k_to_R", "k_to_R_value", "k_to_thermal_resistivity", "thermal_resistivity_to_k", ] def R_to_k(R: float, t: float, A: float=1.) -> float: r"""Returns the thermal conductivity of a substance given its thickness and thermal resistance. .. math:: k = \frac{t}{RA} Parameters ---------- R : float Thermal resistance of a substance, (K/W) if A is 1 m^2, otherwise must be [m^2*K/W] t : float Thickness of the substance used in the measurement of R, [m] A : float, optional Area; normally 1, [m^2] Returns ------- k : float Thermal conductivity of a substance [W/m/K] Examples -------- >>> R_to_k(R=0.05, t=0.025) 0.5 Notes ----- When solving problems of changing areas, this value may be calculated with an area other than 1 m^2. Values in tables reported as properties of materials are often divided by area already; the conversion holds if A is 1. References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return t/(A*R) def k_to_R(k: float, t: float, A: float=1.) -> float: r"""Returns the thermal resistance of a substance given its thickness and thermal conductivity. .. math:: R = \frac{t}{kA} Parameters ---------- k : float Thermal conductivity of a substance [W/m/K] t : float Thickness of the substance for a given value of R, [m] A : float, optional Area; normally 1, [m^2] Returns ------- R : float Thermal resistance of a substance [K/W] Examples -------- >>> k_to_R(k=0.5, t=0.025) 0.05 Notes ----- When solving problems of changing areas, this value may be calculated with an area other than 1 m^2. Values in tables reported as properties of materials are often divided by area already; the conversion holds if A is 1. References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return t/(k*A) def k_to_thermal_resistivity(k: float) -> float: r"""Returns the thermal resistivity of a substance given its thermal conductivity. .. math:: r = \frac{1}{k} Parameters ---------- k : float Thermal conductivity of a substance [W/m/K] Returns ------- r : float Thermal resistivity of a substance [m*K/W] Examples -------- >>> k_to_thermal_resistivity(0.25) 4.0 Notes ----- Do not confuse this with thermal resistance! Often not introduced in heat transfer textbooks to avoid further confusion. Used almost exclusively as a description of solids. Thermal resistivity has different units than R-value, but is of the same dimensionality. References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ return 1./k def thermal_resistivity_to_k(r: float) -> float: r"""Returns the thermal resistivity of a substance given its thermal conductivity. .. math:: k = \frac{1}{r} Parameters ---------- r : float Thermal resistivity of a substance [m*K/W] Returns ------- k : float Thermal conductivity of a substance [W/m/K] Examples -------- >>> thermal_resistivity_to_k(4) 0.25 Notes ----- Do not confuse this with thermal resistance! Often not introduced in heat as a description of solids. Thermal resistivity has different units than R-value, but is of the same dimensionality. References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ return 1./r def R_value_to_k(R_value: float, SI: bool=True) -> float: r"""Returns the thermal conductivity of a substance given its R-value, which can be in either SI units of m^2 K/(W*inch) or the Imperial units of ft^2 deg F*h/(BTU*inch). Parameters ---------- R_value : float R-value of a substance [m^2 K/(W*inch) or ft^2 deg F*h/(BTU*inch)] SI : bool, optional Whether to use the SI conversion or not Returns ------- k : float Thermal conductivity of a substance [W/m/K] Notes ----- If given input is SI, it is divided by 0.0254 (multiplied by 39.37) and then inversed. Otherwise, it is multiplied by 6.93347 and then inversed. Examples -------- >>> R_value_to_k(0.12), R_value_to_k(0.71, SI=False) (0.2116666666, 0.2031378716) >>> R_value_to_k(1., SI=False)/R_value_to_k(1.) 5.6782633411 References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if SI: r = R_value/inch else: r = R_value*(foot*foot*degree_Fahrenheit*hour/Btu/inch) return thermal_resistivity_to_k(r) def k_to_R_value(k: float, SI: bool=True) -> float: r"""Returns the R-value of a substance given its thermal conductivity, Will return R-value in SI units unless SI is false. SI units are m^2 K/(W*inch); Imperial units of R-value are ft^2 deg F*h/(BTU*inch). Parameters ---------- k : float Thermal conductivity of a substance [W/m/K] SI : bool, optional Whether to use the SI conversion or not Returns ------- R_value : float R-value of a substance [m^2 K/(W*inch) or ft^2 deg F*h/(BTU*inch)] Notes ----- Provides the reverse conversion of R_value_to_k. Examples -------- >>> k_to_R_value(R_value_to_k(0.12)), k_to_R_value(R_value_to_k(0.71, SI=False), SI=False) (0.12, 0.71) References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ r = k_to_thermal_resistivity(k) if SI: return r*inch else: return r/(foot*foot*degree_Fahrenheit*hour/Btu/inch) def R_cylinder(Di: float, Do: float, k: float, L: float) -> float: r"""Returns the thermal resistance `R` of a cylinder of constant thermal conductivity `k`, of inner and outer diameter `Di` and `Do`, and with a length `L`. .. math:: (hA)_{\text{cylinder}}=\frac{k}{\ln(D_o/D_i)} \cdot 2\pi L\\ R_{\text{cylinder}}=\frac{1}{(hA)_{\text{cylinder}}}= \frac{\ln(D_o/D_i)}{2\pi Lk} Parameters ---------- Di : float Inner diameter of the cylinder, [m] Do : float Outer diameter of the cylinder, [m] k : float Thermal conductivity of the cylinder, [W/m/K] L : float Length of the cylinder, [m] Returns ------- R : float Thermal resistance [K/W] Examples -------- >>> R_cylinder(0.9, 1., 20, 10) 8.38432343682705e-05 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ hA = k*2*pi*L/log(Do/Di) return 1./hA ### Shape Factors def S_isothermal_sphere_to_plane(D: float, Z: float) -> float: r"""Returns the Shape factor `S` of a sphere of constant temperature and of outer diameter `D` which is `Z` distance from an infinite plane. .. math:: S = \frac{2\pi D}{1 - \frac{D}{4Z}} Parameters ---------- D : float Diameter of the sphere, [m] Z : float Distance from the middle of the sphere to the infinite plane, [m] Returns ------- S : float Shape factor [m] Examples -------- >>> S_isothermal_sphere_to_plane(1, 100) 6.298932638776527 Notes ----- No restrictions on the use of this equation. .. math:: Q = Sk(T_1 - T_2) \\ R_{\text{shape}}=\frac{1}{Sk} References ---------- .. [1] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 2*pi*D/(1. - D/(4.*Z)) def S_isothermal_pipe_to_plane(D: float, Z: float, L: float=1) -> float: r"""Returns the Shape factor `S` of a pipe of constant outer temperature and of outer diameter `D` which is `Z` distance from an infinite plane. Length `L` must be provided, but can be set to 1 to obtain a dimensionless shape factor used in some sources. .. math:: S = \frac{2\pi L}{\cosh^{-1}(2z/D)} Parameters ---------- D : float Diameter of the pipe, [m] Z : float Distance from the middle of the pipe to the infinite plane, [m] L : float, optional Length of the pipe, [m] Returns ------- S : float Shape factor [m] Examples -------- >>> S_isothermal_pipe_to_plane(1, 100, 3) 3.146071454894645 Notes ----- L should be much larger than D. .. math:: Q = Sk(T_1 - T_2) \\ R_{\text{shape}}=\frac{1}{Sk} References ---------- .. [1] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 2.*pi*L/acosh(2.*Z/D) def S_isothermal_pipe_normal_to_plane(D: float, L: float) -> float: r"""Returns the Shape factor `S` of a pipe of constant outer temperature and of outer diameter `D` which extends into an infinite medium below an an infinite plane. .. math:: S = \frac{2\pi L}{\ln(4L/D)} Parameters ---------- D : float Diameter of the pipe, [m] L : float Length of the pipe, [m] Returns ------- S : float Shape factor [m] Examples -------- >>> S_isothermal_pipe_normal_to_plane(1, 100) 104.86893910124888 Notes ----- L should be much larger than D. .. math:: Q = Sk(T_1 - T_2) \\ R_{\text{shape}}=\frac{1}{Sk} References ---------- .. [1] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 2.*pi*L/log(4.*L/D) def S_isothermal_pipe_to_isothermal_pipe(D1: float, D2: float, W: float, L: float=1.) -> float: r"""Returns the Shape factor `S` of a pipe of constant outer temperature and of outer diameter `D1` which is `w` distance from another infinite pipe of outer diameter`D2`. Length `L` must be provided, but can be set to 1 to obtain a dimensionless shape factor used in some sources. .. math:: S = \frac{2\pi L}{\cosh^{-1}\left(\frac{4w^2-D_1^2-D_2^2}{2D_1D_2}\right)} Parameters ---------- D1 : float Diameter of one pipe, [m] D2 : float Diameter of the other pipe, [m] W : float Distance from the middle of one pipe to the middle of the other, [m] L : float, optional Length of the pipe, [m] Returns ------- S : float Shape factor [m] Examples -------- >>> S_isothermal_pipe_to_isothermal_pipe(.1, .2, 1, 1) 1.188711034982268 Notes ----- L should be much larger than both diameters. L should be larger than W. .. math:: Q = Sk(T_1 - T_2) \\ R_{\text{shape}}=\frac{1}{Sk} References ---------- .. [1] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 2.*pi*L/acosh((4*W*W - D1*D1 - D2*D2)/(2.*D1*D2)) def S_isothermal_pipe_to_two_planes(D: float, Z: float, L: float=1.) -> float: r"""Returns the Shape factor `S` of a pipe of constant outer temperature and of outer diameter `D` which is `Z` distance from two infinite isothermal planes of equal temperatures, parallel to each other and enclosing the pipe. Length `L` must be provided, but can be set to 1 to obtain a dimensionless shape factor used in some sources. .. math:: S = \frac{2\pi L}{\ln\frac{8z}{\pi D}} Parameters ---------- D : float Diameter of the pipe, [m] Z : float Distance from the middle of the pipe to either of the planes, [m] L : float, optional Length of the pipe, [m] Returns ------- S : float Shape factor [m] Examples -------- >>> S_isothermal_pipe_to_two_planes(.1, 5, 1) 1.2963749299921428 Notes ----- L should be much larger than both diameters. L should be larger than W. .. math:: Q = Sk(T_1 - T_2) \\ R_{\text{shape}}=\frac{1}{Sk} References ---------- .. [1] Shape Factors for Heat Conduction Through Bodies with Isothermal or Convective Boundary Conditions, J. E. Sunderland, K. R. Johnson, ASHRAE Transactions, Vol. 70, 1964. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 2.*pi*L/log(8.*Z/(pi*D)) def S_isothermal_pipe_eccentric_to_isothermal_pipe(D1: float, D2: float, Z: float, L: float=1.) -> float: r"""Returns the Shape factor `S` of a pipe of constant outer temperature and of outer diameter `D1` which is `Z` distance from the center of another pipe of outer diameter`D2`. Length `L` must be provided, but can be set to 1 to obtain a dimensionless shape factor used in some sources. .. math:: S = \frac{2\pi L}{\cosh^{-1} \left(\frac{D_2^2 + D_1^2 - 4Z^2}{2D_1D_2}\right)} Parameters ---------- D1 : float Diameter of inner pipe, [m] D2 : float Diameter of outer pipe, [m] Z : float Distance from the middle of inner pipe to the center of the other, [m] L : float, optional Length of the pipe, [m] Returns ------- S : float Shape factor [m] Examples -------- >>> S_isothermal_pipe_eccentric_to_isothermal_pipe(.1, .4, .05, 10) 47.709841915608976 Notes ----- L should be much larger than both diameters. D2 should be larger than D1. .. math:: Q = Sk(T_1 - T_2) \\ R_{\text{shape}}=\frac{1}{Sk} References ---------- .. [1] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 2.*pi*L/acosh((D2*D2 + D1*D1 - 4.*Z*Z)/(2.*D1*D2)) # Specific heat transfer problems of conduction def cylindrical_heat_transfer(Ti: float, To: float, hi: float, ho: float, Di: float, ts: list[float], ks: list[float]) -> dict[str, float | list[float]]: r"""Calculation for the heat transfer through a cylindrical wall, as occurs in pipes and cylindrical vessels. This is the core method which calculates the temperatures of each layer - and allows an outer layer to iterate on temperature or duty to meet a fixed specification, or include things like temperature dependent thermal conductivities or radiation. Parameters ---------- Ti : float Temperature of the inside of the cylinder, [K] To : float External temperature outside the cylinder, away from the cylinder wall, [K] hi : float Inside heat transfer coefficient, [W/m^2/K] ho : float Outside heat transfer coefficient, [W/m^2/K] Di : float Inside diameter of cylinder, [m] ts : list[float] List of thicknesses of each layer of the cylinder, [m] ks : list[float] List of thermal conductivities of each layer of the cylinder, [w/m/K] Returns ------- results : dict * Q : Heat exchanged through the cylinder (per meter of length), [W/m] * Rs : Thermal resistances of each of the layers, [m*K/W] * Ts : Temperatures of the outside of each of the layers, [K] * UA : Heat transfer coefficient times area (on a per-meter of cylinder) basis, [W/K/m] * U_inner : Heat transfer coefficient with respect to the inside diameter, [W/K] * U_outer : Heat transfer coefficient with respect to the exterior diameter, [W/K] * q : Specific heat exchanged (per square meter) through the cylinder (per meter of length), [W/m^3] Examples -------- >>> from pprint import pprint >>> pprint(cylindrical_heat_transfer(Ti=453.15, To=301.15, hi=1e12, ho=22.697193, Di=0.0779272, ts=[0.0054864, .05], ks=[56.045, 0.0598535265])) {'Q': 73.12000884069367, 'Rs': [0.00022201030738405449, 1.189361782070256], 'Ts': [453.15, 453.1226455779877, 306.578530147744], 'UA': 0.48105268974140575, 'U_inner': 1.9649599487726137, 'U_outer': 0.8106078714663484, 'q': 123.21239646288495} """ length = 1.0 # basis # Note - fouling is just another layer, should be converted to a thickness/thermal conductivity external_diameter = Di + 2.0*sum(ts) A_external = pi*external_diameter*length A_internal = pi*Di*length Rs = [] Do_running = Di R_layers = 0.0 for i in range(len(ts)): Do_running, Di_running = 2.0*ts[i]+Do_running, Do_running Ri = 0.5*external_diameter*log(Do_running/Di_running)/ks[i] R_layers += Ri Rs.append(Ri) D_ratio = external_diameter/Di inv_term = D_ratio/hi + R_layers + 1.0/ho U_external = 1.0/inv_term UA = A_external*U_external dT = Ti - To Q = UA*dT q = Q/A_external # Compute the temperature profile Ts = [Ti] for Ri in Rs: Ts.append(Ts[-1] - q*Ri) # Convert heat transfer coefficient area basis = U_i*A_i = U_o*A_o, divide ans = {"Q": Q, "q": q, "UA": UA, "U_outer": U_external, "U_inner": UA/A_internal, "Ts": Ts, "Rs": Rs} return ans ================================================ FILE: ht/conv_external.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import exp __all__: list[str] = [ "LAMINAR_TRANSITION_HORIZONTAL_PLATE", "Nu_cylinder_Churchill_Bernstein", "Nu_cylinder_Fand", "Nu_cylinder_McAdams", "Nu_cylinder_Perkins_Leppert_1962", "Nu_cylinder_Perkins_Leppert_1964", "Nu_cylinder_Sanitjai_Goldstein", "Nu_cylinder_Whitaker", "Nu_cylinder_Zukauskas", "Nu_external_cylinder", "Nu_external_cylinder_methods", "Nu_external_horizontal_plate", "Nu_external_horizontal_plate_methods", "Nu_horizontal_plate_laminar_Baehr", "Nu_horizontal_plate_laminar_Churchill_Ozoe", "Nu_horizontal_plate_turbulent_Kreith", "Nu_horizontal_plate_turbulent_Schlichting", "conv_horizontal_plate_methods", ] ### Single Cylinders in Crossflow def Nu_cylinder_Zukauskas(Re: float, Pr: float, Prw: float | None=None) -> float: r"""Calculates Nusselt number for crossflow across a single tube at a specified Re. Method from [1]_, also shown without modification in [2]_. This method applies to both the laminar and turbulent regimes. .. math:: Nu_{D}=CRe^{m}Pr^{n}\left(\frac{Pr}{Pr_s}\right)^{1/4} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at free stream temperature [-] Prw : float, optional Prandtl number at wall temperature, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- If Prandtl number at wall are not provided, the Prandtl number correction is not used and left to an outside function. n is 0.37 if Pr <= 10; otherwise n is 0.36. C and m are from the following table. If Re is outside of the ranges shown, the nearest range is used blindly. +---------+-------+-----+ | Re | C | m | +=========+=======+=====+ | 1-40 | 0.75 | 0.4 | +---------+-------+-----+ | 40-1E3 | 0.51 | 0.5 | +---------+-------+-----+ | 1E3-2E5 | 0.26 | 0.6 | +---------+-------+-----+ | 2E5-1E6 | 0.076 | 0.7 | +---------+-------+-----+ Examples -------- Example 7.3 in [2]_, matches. >>> Nu_cylinder_Zukauskas(7992, 0.707, 0.69) 50.523612661934386 References ---------- .. [1] Zukauskas, A. Heat transfer from tubes in crossflow. In T.F. Irvine, Jr. and J. P. Hartnett, editors, Advances in Heat Transfer, volume 8, pages 93-160. Academic Press, Inc., New York, 1972. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ if Re <= 40: c, m = 0.75, 0.4 elif Re < 1E3: c, m = 0.51, 0.5 elif Re < 2E5: c, m = 0.26, 0.6 else: c, m = 0.076, 0.7 if Pr <= 10.0: n = 0.37 else: n = 0.36 Nu = c*Re**m*Pr**n if Prw is not None: Nu = Nu*(Pr/Prw)**0.25 return Nu def Nu_cylinder_Churchill_Bernstein(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for crossflow across a single tube at a specified `Re` and `Pr`, both evaluated at the film temperature. No other wall correction is necessary for this formulation. Method is shown without modification in [2]_ and many other texts. .. math:: Nu_D = 0.3 + \frac{0.62 Re_D^{0.5} Pr^{1/3}}{[1 + (0.4/Pr)^{2/3} ]^{0.25}}\left[1 + \left(\frac{Re_D}{282000}\right)^{5/8}\right]^{0.8} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at film temperature, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- May underestimate heat transfer in some cases, as it the formula is described in [1]_ as "appears to provide a lower bound for RePr > 0.4". An alternate exponent for a smaller range is also presented in [1]_. This method applies to both the laminar and turbulent regimes. Examples -------- Example 7.3 in [2]_, matches. >>> Nu_cylinder_Churchill_Bernstein(6071, 0.7) 40.63708594124974 References ---------- .. [1] Churchill, S. W., and M. Bernstein. "A Correlating Equation for Forced Convection From Gases and Liquids to a Circular Cylinder in Crossflow." Journal of Heat Transfer 99, no. 2 (May 1, 1977): 300-306. doi:10.1115/1.3450685. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return 0.3 + (0.62*Re**0.5*Pr**(1/3.))/(1 + (0.4/Pr)**(2/3.))**0.25*( 1 +(Re/282000.)**(0.625))**0.8 def Nu_cylinder_Sanitjai_Goldstein(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for crossflow across a single tube at a specified `Re` and `Pr`, both evaluated at the film temperature. No other wall correction is necessary for this formulation. Method is the most recent implemented here and believed to be more accurate than other formulations available. .. math:: Nu = 0.446Re^{0.5} Pr^{0.35} + 0.528\left[(6.5\exp(Re/5000))^{-5} + (0.031Re^{0.8})^{-5}\right]^{-1/5}Pr^{0.42} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at film temperature, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Developed with test results for water, mixtures of ethylene glycol and water, and air (Pr = 0.7 to 176). Re range from 2E3 to 9E4. Also presents results for local heat transfer coefficients. This method applies to both the laminar and turbulent regimes. Examples -------- >>> Nu_cylinder_Sanitjai_Goldstein(6071, 0.7) 40.38327083519522 References ---------- .. [1] Sanitjai, S., and R. J. Goldstein. "Forced Convection Heat Transfer from a Circular Cylinder in Crossflow to Air and Liquids." International Journal of Heat and Mass Transfer 47, no. 22 (October 2004): 4795-4805. doi:10.1016/j.ijheatmasstransfer.2004.05.012. """ # Interesting numerical issue: # The power of the -5 exp Re term is moved inside the exponential to # avoid overflow errors # This occurs easily with a large diameter cylinder (such as a vessel) return 0.446*Re**0.5*Pr**0.35 + 0.528*((6.5**-5*exp(-5*Re/5000.)) + (0.031*Re**0.8)**-5)**-0.2*Pr**0.42 def Nu_cylinder_Fand(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for crossflow across a single tube at a specified `Re` and `Pr`, both evaluated at the film temperature. No other wall correction is necessary for this formulation. Also shown in [2]_. .. math:: Nu = (0.35 + 0.34Re^{0.5} + 0.15Re^{0.58})Pr^{0.3} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at film temperature, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Developed with test results for water, and Re from 1E4 to 1E5, but also compared with other data in the literature. Claimed validity of Re from 1E-1 to 1E5. This method applies to both the laminar and turbulent regimes. Examples -------- >>> Nu_cylinder_Fand(6071, 0.7) 45.19984325481126 References ---------- .. [1] Fand, R. M. "Heat Transfer by Forced Convection from a Cylinder to Water in Crossflow." International Journal of Heat and Mass Transfer 8, no. 7 (July 1, 1965): 995-1010. doi:10.1016/0017-9310(65)90084-0. .. [2] Sanitjai, S., and R. J. Goldstein. "Forced Convection Heat Transfer from a Circular Cylinder in Crossflow to Air and Liquids." International Journal of Heat and Mass Transfer 47, no. 22 (October 2004): 4795-4805. doi:10.1016/j.ijheatmasstransfer.2004.05.012. """ return (0.35 + 0.34*Re**0.5 + 0.15*Re**0.58)*Pr**0.3 def Nu_cylinder_McAdams(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for crossflow across a single tube at a specified `Re` and `Pr`, both evaluated at the film temperature. No other wall correction is necessary for this formulation. Also shown in [2]_. .. math:: Nu = (0.35 + 0.56 Re^{0.52})Pr^{0.3} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at film temperature, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Developed with very limited test results for water only. This method applies to both the laminar and turbulent regimes. Examples -------- >>> Nu_cylinder_McAdams(6071, 0.7) 46.98179235867934 References ---------- .. [1] McAdams, William Henry. Heat Transmission. 3E. Malabar, Fla: Krieger Pub Co, 1985. .. [2] Fand, R. M. "Heat Transfer by Forced Convection from a Cylinder to Water in Crossflow." International Journal of Heat and Mass Transfer 8, no. 7 (July 1, 1965): 995-1010. doi:10.1016/0017-9310(65)90084-0. """ return (0.35 + 0.56*Re**0.52)*Pr**0.3 def Nu_cylinder_Whitaker(Re: float, Pr: float, mu: float | None=None, muw: float | None=None) -> float: r"""Calculates Nusselt number for crossflow across a single tube as shown in [1]_ at a specified `Re` and `Pr`, both evaluated at the free stream temperature. Recommends a viscosity exponent correction of 0.25, which is applied only if provided. Also shown in [2]_. .. math:: Nu_D = (0.4 Re_D^{0.5} + 0.06Re_D^{2/3})Pr^{0.4} \left(\frac{\mu}{\mu_w}\right)^{0.25} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at free stream temperature, [-] mu : float, optional Viscosity of fluid at the free stream temperature [Pa*s] muw : float, optional Viscosity of fluid at the wall temperature [Pa*s] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Developed considering data from 1 to 1E5 Re, 0.67 to 300 Pr, and range of viscosity ratios from 0.25 to 5.2. Found experimental data to generally agree with it within 25%. This method applies to both the laminar and turbulent regimes. Examples -------- >>> Nu_cylinder_Whitaker(6071, 0.7) 45.94527461589126 References ---------- .. [1] Whitaker, Stephen. "Forced Convection Heat Transfer Correlations for Flow in Pipes, Past Flat Plates, Single Cylinders, Single Spheres, and for Flow in Packed Beds and Tube Bundles." AIChE Journal 18, no. 2 (March 1, 1972): 361-371. doi:10.1002/aic.690180219. .. [2] Sanitjai, S., and R. J. Goldstein. "Forced Convection Heat Transfer from a Circular Cylinder in Crossflow to Air and Liquids." International Journal of Heat and Mass Transfer 47, no. 22 (October 2004): 4795-4805. doi:10.1016/j.ijheatmasstransfer.2004.05.012. """ Nu = (0.4*Re**0.5 + 0.06*Re**(2/3.))*Pr**0.3 if mu is not None and muw is not None: Nu *= (mu/muw)**0.25 return Nu def Nu_cylinder_Perkins_Leppert_1962(Re: float, Pr: float, mu: float | None=None, muw: float | None=None) -> float: r"""Calculates Nusselt number for crossflow across a single tube as shown in [1]_ at a specified `Re` and `Pr`, both evaluated at the free stream temperature. Recommends a viscosity exponent correction of 0.25, which is applied only if provided. Also shown in [2]_. .. math:: Nu = \left[0.30Re^{0.5} + 0.10Re^{0.67}\right]Pr^{0.4} \left(\frac{\mu}{\mu_w}\right)^{0.25} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at free stream temperature, [-] mu : float, optional Viscosity of fluid at the free stream temperature [Pa*s] muw : float, optional Viscosity of fluid at the wall temperature [Pa*s] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Considered results with Re from 40 to 1E5, Pr from 1 to 300; and viscosity ratios of 0.25 to 4. This method applies to both the laminar and turbulent regimes. Examples -------- >>> Nu_cylinder_Perkins_Leppert_1962(6071, 0.7) 49.97164291175499 References ---------- .. [1] Perkins, Jr., H. C., and G. Leppert. "Forced Convection Heat Transfer From a Uniformly Heated Cylinder." Journal of Heat Transfer 84, no. 3 (August 1, 1962): 257-261. doi:10.1115/1.3684359. .. [2] Sanitjai, S., and R. J. Goldstein. "Forced Convection Heat Transfer from a Circular Cylinder in Crossflow to Air and Liquids." International Journal of Heat and Mass Transfer 47, no. 22 (October 2004): 4795-4805. doi:10.1016/j.ijheatmasstransfer.2004.05.012. """ Nu = (0.30*Re**0.5 + 0.10*Re**0.67)*Pr**0.4 if mu is not None and muw is not None: Nu *= (mu/muw)**0.25 return Nu def Nu_cylinder_Perkins_Leppert_1964(Re: float, Pr: float, mu: float | None=None, muw: float | None=None) -> float: r"""Calculates Nusselt number for crossflow across a single tube as shown in [1]_ at a specified `Re` and `Pr`, both evaluated at the free stream temperature. Recommends a viscosity exponent correction of 0.25, which is applied only if provided. Also shown in [2]_. .. math:: Nu = \left[0.31Re^{0.5} + 0.11Re^{0.67}\right]Pr^{0.4} \left(\frac{\mu}{\mu_w}\right)^{0.25} Parameters ---------- Re : float Reynolds number with respect to cylinder diameter, [-] Pr : float Prandtl number at free stream temperature, [-] mu : float, optional Viscosity of fluid at the free stream temperature [Pa*s] muw : float, optional Viscosity of fluid at the wall temperature [Pa*s] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Considers new data since `Nu_cylinder_Perkins_Leppert_1962`, Re from 2E3 to 1.2E5, Pr from 1 to 7, and surface to bulk temperature differences of 11 to 66. This method applies to both the laminar and turbulent regimes. Examples -------- >>> Nu_cylinder_Perkins_Leppert_1964(6071, 0.7) 53.61767038619986 References ---------- .. [1] Perkins Jr., H. C., and G. Leppert. "Local Heat-Transfer Coefficients on a Uniformly Heated Cylinder." International Journal of Heat and Mass Transfer 7, no. 2 (February 1964): 143-158. doi:10.1016/0017-9310(64)90079-1. .. [2] Sanitjai, S., and R. J. Goldstein. "Forced Convection Heat Transfer from a Circular Cylinder in Crossflow to Air and Liquids." International Journal of Heat and Mass Transfer 47, no. 22 (October 2004): 4795-4805. doi:10.1016/j.ijheatmasstransfer.2004.05.012. """ Nu = (0.31*Re**0.5 + 0.11*Re**0.67)*Pr**0.4 if mu is not None and muw is not None: Nu *= (mu/muw)**0.25 return Nu conv_external_cylinder_turbulent_methods = { "Zukauskas": (Nu_cylinder_Zukauskas, ("Re", "Pr", "Prw")), "Churchill-Bernstein": (Nu_cylinder_Churchill_Bernstein, ("Re", "Pr")), "Sanitjai-Goldstein": (Nu_cylinder_Sanitjai_Goldstein, ("Re", "Pr")), "Fand": (Nu_cylinder_Fand, ("Re", "Pr")), "McAdams": (Nu_cylinder_McAdams, ("Re", "Pr")), "Whitaker": (Nu_cylinder_Whitaker, ("Re", "Pr", "mu", "muw")), "Perkins-Leppert 1962": (Nu_cylinder_Perkins_Leppert_1962, ("Re", "Pr", "mu", "muw")), "Perkins-Leppert 1964": (Nu_cylinder_Perkins_Leppert_1964, ("Re", "Pr", "mu", "muw")), } conv_external_cylinder_turbulent_methods_ranked = ["Sanitjai-Goldstein", "Churchill-Bernstein", "Zukauskas", "Whitaker", "Perkins-Leppert 1964", "McAdams", "Fand", "Perkins-Leppert 1962"] conv_external_cylinder_methods = conv_external_cylinder_turbulent_methods.copy() _missing_external_cylinder_method = f"Correlation name not recognized; the availble methods are {list(conv_external_cylinder_methods.keys())}." def Nu_external_cylinder_methods(Re: float, Pr: float, Prw: float | None=None, mu: float | None=None, muw: float | None=None, check_ranges: bool=True) -> list[str]: r"""This function returns a list of correlation names for forced convection over an external cylinder. The preferred method 'Sanitjai-Goldstein'. Parameters ---------- Re : float Reynolds number of fluid with respect to cylinder diameter, [-] Pr : float Prandtl number at either the free stream or wall temperature depending on the method, [-] Prw : float, optional Prandtl number at wall temperature, [-] mu : float, optional Viscosity of fluid at the free stream temperature [Pa*s] muw : float, optional Viscosity of fluid at the wall temperature [Pa*s] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `Nu` with the given inputs Examples -------- >>> Nu_external_cylinder_methods(0.72, 1E7)[0] 'Sanitjai-Goldstein' """ methods = ["Sanitjai-Goldstein", "Churchill-Bernstein", "Fand", "McAdams"] if Prw is not None: methods.append("Zukauskas") if mu is not None and muw is not None: methods.extend(["Whitaker", "Perkins-Leppert 1964", "Perkins-Leppert 1962"]) return methods def Nu_external_cylinder(Re: float, Pr: float, Prw: float | None=None, mu: float | None=None, muw: float | None=None, Method: str | None=None) -> float: r"""Calculates Nusselt number for crossflow across a single tube at a specified `Re` and `Pr` according to the specified method. Optional parameters are `Prw`, `mu`, and `muw`. This function has eight methods available. The 'Sanitjai-Goldstein' method is the default. The front of the cyliner is normally always in a laminar regime; whereas the back is turbulent. The proportions change with `Re`; all correlations take this into account. For this heat transfer case, there is no separation between laminar and turbulent methods. Parameters ---------- Re : float Reynolds number of fluid with respect to cylinder diameter, [-] Pr : float Prandtl number at either the free stream or wall temperature depending on the method, [-] Prw : float, optional Prandtl number at wall temperature, [-] mu : float, optional Viscosity of fluid at the free stream temperature [Pa*s] muw : float, optional Viscosity of fluid at the wall temperature [Pa*s] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use, as in the dictionary conv_external_cylinder_methods. Notes ----- A comparison of the methods for various Prandtl and Reynolds number ranges is plotted below. .. plot:: plots/Nu_external_cylinder.py Examples -------- >>> Nu_external_cylinder(6071, 0.7) 40.38327083519522 """ Method2 = "Sanitjai-Goldstein" if Method is None else Method if Method2 == "Sanitjai-Goldstein": return Nu_cylinder_Sanitjai_Goldstein(Re=Re, Pr=Pr) elif Method2 == "Churchill-Bernstein": return Nu_cylinder_Sanitjai_Goldstein(Re=Re, Pr=Pr) elif Method2 == "Fand": return Nu_cylinder_Fand(Re=Re, Pr=Pr) elif Method2 == "McAdams": return Nu_cylinder_McAdams(Re=Re, Pr=Pr) elif Method2 == "Zukauskas": return Nu_cylinder_Zukauskas(Re=Re, Pr=Pr, Prw=Prw) elif Method2 == "Whitaker": return Nu_cylinder_Whitaker(Re=Re, Pr=Pr, mu=mu, muw=muw) elif Method2 == "Perkins-Leppert 1962": return Nu_cylinder_Perkins_Leppert_1962(Re=Re, Pr=Pr, mu=mu, muw=muw) elif Method2 == "Perkins-Leppert 1964": return Nu_cylinder_Perkins_Leppert_1964(Re=Re, Pr=Pr, mu=mu, muw=muw) else: raise ValueError(_missing_external_cylinder_method) # Horizontal Plate in crossflow def Nu_horizontal_plate_laminar_Baehr(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for laminar flow across an **isothermal** flat plate at a specified `Re` and `Pr`, both evaluated at the bulk temperature. No other wall correction is necessary for this formulation. Four different equations are used for different Prandtl number ranges. The equation for the common Prandtl number range is also recommended in [2]_ and [3]_. if :math:`\text{Pr} < 0.005`: .. math:: \text{Nu}_L = 1.128\text{Re}^{0.5}\text{Pr}^{0.5} if :math:`0.005 < \text{Pr} < 0.05`: .. math:: \text{Nu}_L = 1.0\text{Re}^{0.5}\text{Pr}^{0.5} if :math:`0.6 < \text{Pr} < 10`: .. math:: \text{Nu}_L = 0.664\text{Re}^{0.5}\text{Pr}^{1/3} if :math:`\text{Pr} > 10`: .. math:: \text{Nu}_L = 0.678\text{Re}^{0.5}\text{Pr}^{1/3} Parameters ---------- Re : float Reynolds number with respect to plate length and bulk fluid properties, [-] Pr : float Prandtl number at bulk temperature, [-] Returns ------- Nu : float Nusselt number with respect to plate length and bulk temperature, [-] Notes ----- Does not take into account the impact of free convection, which can increase the convection substantially. Examples -------- >>> Nu_horizontal_plate_laminar_Baehr(1e5, 0.7) 186.4378528752262 References ---------- .. [1] Baehr, Hans Dieter, and Karl Stephan. Heat and Mass Transfer. Springer, 2013. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ if Pr < 0.005: return 1.128*(Re*Pr)**0.5 elif Pr < 0.05: return (Re*Pr)**0.5 elif Pr < 10.0: # Equation in VDI handbook, G4 as well return 0.664*Re**0.5*Pr**(1/3.) else: return 0.678*Re**0.5*Pr**(1/3.) def Nu_horizontal_plate_laminar_Churchill_Ozoe(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for laminar flow across an **isothermal** flat plate at a specified `Re` and `Pr`, both evaluated at the bulk temperature. No other wall correction is necessary for this formulation. A single equation covers all Prandtl number ranges. .. math:: Nu_L = \frac{0.6774Re_L^{1/2}Pr^{1/3}}{[1+(0.0468/Pr)^{2/3}]^{1/4}} Parameters ---------- Re : float Reynolds number with respect to plate length and bulk fluid properties, [-] Pr : float Prandtl number at bulk temperature, [-] Returns ------- Nu : float Nusselt number with respect to plate length and bulk temperature, [-] Notes ----- Does not take into account the impact of free convection, which can increase the convection substantially. Examples -------- >>> Nu_horizontal_plate_laminar_Churchill_Ozoe(1e5, 0.7) 183.08600782591418 References ---------- .. [1] Churchill, Stuart W., and Hiroyuki Ozoe. "Correlations for Laminar Forced Convection in Flow Over an Isothermal Flat Plate and in Developing and Fully Developed Flow in an Isothermal Tube." Journal of Heat Transfer 95, no. 3 (August 1, 1973): 416 https://doi.org/10.1115/1.3450078. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return (0.6774*Re**(0.5)*Pr**(1/3.) *(1.0 + (0.0468/Pr)**(2.0/3.0))**-0.25 ) def Nu_horizontal_plate_turbulent_Schlichting(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for turbulent flow across an **isothermal** flat plate at a specified `Re` and `Pr`, both evaluated at the bulk temperature. The formulation of Schlichting is used, which adds a surface friction term to a formulation from Petukhov and Popov. .. math:: \text{Nu}_L = \frac{0.037\text{Re}_L^{0.8} \text{Pr}} {1 + 2.443\text{Re}_L^{-0.1}(\text{Pr}^{2/3} - 1)} Parameters ---------- Re : float Reynolds number with respect to plate length and bulk fluid properties, [-] Pr : float Prandtl number at bulk temperature, [-] Returns ------- Nu : float Nusselt number with respect to plate length and bulk temperature, [-] Notes ----- Does not take into account the impact of free convection, which can increase the convection substantially. Examples -------- >>> Nu_horizontal_plate_turbulent_Schlichting(1e5, 0.7) 309.620048541267 References ---------- .. [1] Schlichting, H., and Klaus Gersten. Grenzschicht-Theorie. 9th ed. Berlin Heidelberg: Springer-Verlag, 1997. http://www.springer.com/de/book/9783662075548. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ num = 0.037*Re**0.8*Pr den = (1.0 + 2.443*Re**-0.1*(Pr**(2.0/3.0) - 1.0)) return num/den def Nu_horizontal_plate_turbulent_Kreith(Re: float, Pr: float) -> float: r"""Calculates Nusselt number for turbulent flow across an **isothermal** flat plate at a specified `Re` and `Pr`, both evaluated at the bulk temperature. The formulation of Kreith is used. .. math:: \text{Nu}_L = 0.036\text{Re}_L^{0.8} \text{Pr}^{1/3} Parameters ---------- Re : float Reynolds number with respect to plate length and bulk fluid properties, [-] Pr : float Prandtl number at bulk temperature, [-] Returns ------- Nu : float Nusselt number with respect to plate length and bulk temperature, [-] Notes ----- Does not take into account the impact of free convection, which can increase the convection substantially. Applies for turbulent flow only. Examples -------- >>> Nu_horizontal_plate_turbulent_Kreith(1.03e6, 0.71) 2074.8740070411122 References ---------- .. [1] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. """ return 0.036*Pr**(1.0/3.0)*Re**0.8 conv_horizontal_plate_laminar_methods = { "Baehr": (Nu_horizontal_plate_laminar_Baehr, ("Re", "Pr")), "Churchill Ozoe": (Nu_horizontal_plate_laminar_Churchill_Ozoe, ("Re", "Pr")), } conv_horizontal_plate_turbulent_methods = { "Schlichting": (Nu_horizontal_plate_turbulent_Schlichting, ("Re", "Pr")), "Kreith": (Nu_horizontal_plate_turbulent_Kreith, ("Re", "Pr")), } conv_horizontal_plate_methods = conv_horizontal_plate_laminar_methods.copy() conv_horizontal_plate_methods.update(conv_horizontal_plate_turbulent_methods) LAMINAR_TRANSITION_HORIZONTAL_PLATE = 5E5 def Nu_external_horizontal_plate_methods(Re: float, Pr: float, L: float | None=None, x: float | None=None, check_ranges: bool=True) -> list[str]: r"""Returns a list of correlation names for calculating Nusselt number for forced convection across a horizontal plate, supporting both laminar and turbulent regimes. Parameters ---------- Re : float Reynolds number with respect to bulk properties and plate length, [-] Pr : float Prandtl number with respect to bulk properties, [-] L : float, optional Length of horizontal plate, [m] x : float, optional Length of horizontal plate for specific calculation distance, [m] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `Nu` with the given inputs Examples -------- >>> Nu_external_horizontal_plate_methods(Re=1e7, Pr=.7)[0] 'Schlichting' """ turbulent = Re >= LAMINAR_TRANSITION_HORIZONTAL_PLATE if check_ranges: if turbulent: return ["Schlichting", "Kreith"] else: return ["Baehr", "Churchill Ozoe"] else: return ["Baehr", "Churchill Ozoe", "Schlichting", "Kreith"] def Nu_external_horizontal_plate(Re: float, Pr: float, L: None=None, x: None=None, Method: str | None=None, laminar_method: str="Baehr", turbulent_method: str="Schlichting", Re_transition: float=LAMINAR_TRANSITION_HORIZONTAL_PLATE) -> float: r"""This function calculates the heat transfer coefficient for external forced convection along a horizontal plate. Requires at a minimum a flow's Reynolds and Prandtl numbers `Re` and `Pr`. `L` and `x` are not used by any correlations presently, but are included for future support. If no correlation's name is provided as `Method`, the most accurate applicable correlation is selected. Parameters ---------- Re : float Reynolds number with respect to bulk properties and plate length, [-] Pr : float Prandtl number with respect to bulk properties, [-] L : float, optional Length of horizontal plate, [m] x : float, optional Length of horizontal plate for specific calculation distance, [m] Returns ------- Nu : float Nusselt number with respect to plate length, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use, as in the dictionary conv_horizontal_plate_methods laminar_method : str, optional The prefered method for laminar flow, [-] turbulent_method : str, optional The prefered method for turbulent flow, [-] Re_transition : float, optional The transition Reynolds number for laminar changing to turbulent flow, [-] Examples -------- Turbulent example >>> Nu_external_horizontal_plate(Re=1E7, Pr=.7) 11496.952599969829 """ turbulent = not Re < Re_transition if Method is None: Method2 = turbulent_method if turbulent else laminar_method else: Method2 = Method if Method2 == "Baehr": return Nu_horizontal_plate_laminar_Baehr(Re=Re, Pr=Pr) elif Method2 == "Churchill Ozoe": return Nu_horizontal_plate_laminar_Churchill_Ozoe(Re=Re, Pr=Pr) elif Method2 == "Schlichting": return Nu_horizontal_plate_turbulent_Schlichting(Re=Re, Pr=Pr) elif Method2 == "Kreith": return Nu_horizontal_plate_turbulent_Kreith(Re=Re, Pr=Pr) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") ================================================ FILE: ht/conv_free_enclosed.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import exp, log from fluids.numerics import bisplev, horner, implementation_optimize_tck, secant __all__: list[str] = [ "Nu_Nusselt_Rayleigh_Hollands", "Nu_Nusselt_Rayleigh_Holling_Herwig", "Nu_Nusselt_Rayleigh_Probert", "Nu_Nusselt_vertical_Thess", "Nu_vertical_helical_coil_Ali", "Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan", "Rac_Nusselt_Rayleigh", "Rac_Nusselt_Rayleigh_disk", ] __numba_additional_funcs__ = ["Nu_Nusselt_Rayleigh_Holling_Herwig_err"] def Nu_Nusselt_Rayleigh_Holling_Herwig_err(Nu: float, Ra: float, Ra_third: float, D2: float) -> float: err = Ra_third*(0.1/2.0*log(1.0/16.0*Ra*Nu) + D2)**(-4.0/3.0) - Nu return err def Nu_Nusselt_Rayleigh_Holling_Herwig(Pr: float, Gr: float, buoyancy: bool=True) -> float: r"""Calculates the Nusselt number for natural convection between two theoretical flat horizontal plates. The height between the plates is infinite, and one of the other dimensions of the plates is much larger than the other. This correlation is for the horizontal plate Rayleigh-Benard classic heat transfer problem, not for real finite geometry plates. This model is a non-linear equation which is solved numerically. The model can calculate `Nu` for `Ra` ranges between 350 and larger numbers; [1]_ recommends :math:`10^{5} < Ra < 10^{15}`. .. math:: \text{Nu} = \frac{{Ra}^{1/3}}{[0.05\ln(\frac{0.078}{16}{Ra}^{1.323}) + 2D]^{4/3}} .. math:: D = -\frac{14.94}{{Ra}^{0.25}} + 3.43 Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - plate temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] Returns ------- Nu : float Nusselt number with respect to height between the two plates, [-] Notes ----- A range of calculated values are provided in [1]_; they all match the results of this function. This model is recommended in [2]_. For :math:`Ra < 1708`, `Nu` = 1; for cases not assited by `buoyancy`, `Nu` is also 1. No success has been found finding an analytical solution in the major CAS packages, but the nonlinear function is in fact a function of one variable; this means a pade or chebyshev expansion could be performed. Examples -------- >>> Nu_Nusselt_Rayleigh_Holling_Herwig(5.54, 3.21e8, buoyancy=True) 77.54656801896913 References ---------- .. [1] Hölling, M., and H. Herwig. "Asymptotic Analysis of Heat Transfer in Turbulent Rayleigh-Bénard Convection." International Journal of Heat and Mass Transfer 49, no. 5 (March 1, 2006): 1129-36. https://doi.org/10.1016/j.ijheatmasstransfer.2005.09.002. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ if not buoyancy: return 1.0 Rac = 1708 # Constant Ra = Gr*Pr if Ra < Rac: return 1.0 Ra_third = Ra**(1.0/3.0) D2 = 2.0*(-14.94*Ra**-0.25 + 3.43) Nu_guess = Ra_third*(0.1/2.0*log(.078/16.0*Ra**1.323) + D2)**(-4.0/3.0) return secant(Nu_Nusselt_Rayleigh_Holling_Herwig_err, Nu_guess, args=(Ra, Ra_third, D2)) def Nu_Nusselt_Rayleigh_Probert(Pr: float, Gr: float, buoyancy: bool=True) -> float: r"""Calculates the Nusselt number for natural convection between two theoretical flat plates. The height between the plates is infinite, and one of the other dimensions of the plates is much larger than the other. This correlation is for the horizontal plate Rayleigh-Benard classic heat transfer problem, not for real finite geometry plates. Two sets of equations are used. For the laminar regime :math:`1708 < \text{Ra} \le 2.2\times 10^{4}`: .. math:: \text{Nu} = 0.208(\text{Ra})^{0.25} For the turbulent regime :math:`2.2\times 10^{4} < \text{Ra}`: .. math:: \text{Nu} = 0.092(\text{Ra})^{1/3} Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - plate temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] Returns ------- Nu : float Nusselt number with respect to height between the two plates, [-] Notes ----- This model is recommended in [2]_ as a rough model. For :math:`Ra < 1708`, `Nu` = 1; for cases not assited by `buoyancy`, `Nu` is also 1. Examples -------- >>> Nu_Nusselt_Rayleigh_Probert(5.54, 3.21e8, buoyancy=True) 111.46181048289132 References ---------- .. [1] Probert, SD, RG Brooks, and M Dixon. "Heat Transfer across Rectangular Cavities." CHEMICAL AND PROCESS ENGINEERING, 1970, 35. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ if not buoyancy: return 1.0 Rac = 1708 # Constant Ra = Gr*Pr if Ra < Rac: return 1.0 elif Ra < 2.2e4: return 0.208*Ra**0.25 else: return 0.092*Ra**(1.0/3.0) def Nu_Nusselt_Rayleigh_Hollands(Pr: float, Gr: float, buoyancy: bool=True, Rac: float=1708) -> float: r"""Calculates the Nusselt number for natural convection between two theoretical flat horizontal plates using the Hollands [1]_ correlation recommended in [2]_. This correlation supports different aspect ratios, so the plates can be real, finite objects and have their heat transfer accurately modeled. The influence comes from the `Rac` term, which should be calculated separately, using `Rac_Nusselt_Rayleigh` or `Rac_Nusselt_Rayleigh_disk`. .. math:: \text{Nu} = 1 + \left[1 - \frac{1708}{\text{Ra}} \right]^* \left[k_1 + 2 \left(\frac{\text{Ra}^{1/3}}{k_2} \right)^{1 - \ln({\text{Ra}}^{1/5}/k_2)} \right]^* + \left[\left(\frac{\text{Ra}}{5803}\right)^{1/3} - 1\right]^* .. math:: k_1 = \frac{1.44}{1 + 0.018/{Pr} + 0.00136/{Pr}^2} .. math:: k_2 = 75\exp(1.5\text{Pr}^{-0.5}) Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - plate temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] Rac : float, optional Critical Rayleigh number, [-] Returns ------- Nu : float Nusselt number with respect to height between the two plates, [-] Notes ----- For :math:`Ra < {Ra}_c`, `Nu` = 1; for cases not assited by `buoyancy`, `Nu` is also 1. Examples -------- >>> Nu_Nusselt_Rayleigh_Hollands(5.54, 3.21e8, buoyancy=True) 69.02668649510 Plates - 1 m height, 2 m long, 0.2 m long vs a 1 m^3 cube >>> Nu_Nusselt_Rayleigh_Hollands(.7, 3.21e6, buoyancy=True, Rac=Rac_Nusselt_Rayleigh(H=1, L=2, W=.2, insulated=False)) 4.666249131876 >>> Nu_Nusselt_Rayleigh_Hollands(.7, 3.21e6, buoyancy=True, Rac=Rac_Nusselt_Rayleigh(H=1, L=1, W=1, insulated=False)) 8.786362614129 References ---------- .. [1] Hollands, K. G. T. "Multi-Prandtl Number Correlation Equations for Natural Convection in Layers and Enclosures." International Journal of Heat and Mass Transfer 27, no. 3 (March 1, 1984): 466-68. https://doi.org/10.1016/0017-9310(84)90295-3. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ if not buoyancy: return 1.0 Ra = Gr*Pr if Ra < Rac: return 1.0 k1 = 1.44/(1.0 + 0.018/Pr + 0.00136/(Pr*Pr)) k2 = 75*exp(1.5*Pr**-0.5) t1 = (1.0 - Rac/Ra) t2 = k1 + 2.0*(Ra**(1.0/3.0)/k2)**(1.0 - log(Ra**(1.0/3.0)/k2)) t3 = (Ra/5803.0)**(1.0/3.0) - 1.0 if Rac != 1708: t4 = max(0.0, (Ra/Rac)**(1.0/3.0) - 1.0) t5 = (1.0 - exp(-0.95*t4)) else: t5 = 1.0 Nu = 1.0 + max(0.0, t1)*max(0.0, t2) + max(0.0, t3)*t5 return Nu def Nu_Nusselt_vertical_Thess(Pr: float, Gr: float, H: int | None=None, L: int | None=None) -> float: r"""Calculates the Nusselt number for natural convection between two theoretical vertical flat plates using the correlation by Thess [1] in [1]_. This is a variant on the horizontal Rayleigh-Benard classic heat transfer problem. This correlation supports different aspect ratios, so the plates can be real, finite objects and have their heat transfer accurately modeled. The recommended range of the correlation is H/L < 80. For 1e4 < Ra < 1e7: .. math:: \text{Nu} = 0.42{Pr}^{0.012} {Ra}^{0.25} \left(\frac{H}{L}\right)^{-0.25} For 1e7 < Ra > 1e9 (or when geometry is unknown): .. math:: \text{Nu} = 0.049{Ra}^{0.33} Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - plate temperature difference [-] H : float, optional Height of vertical plate, [m] L : float, optional Length of vertical plate, [m] Returns ------- Nu : float Nusselt number with respect to distance between the two plates, [-] Examples -------- >>> Nu_Nusselt_vertical_Thess(.7, 3.21e6) 6.112587569602785 >>> Nu_Nusselt_vertical_Thess(.7, 3.21e6, L=10, H=1) 28.79328626041646 References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ Ra = Gr*Pr if Ra < 1e7 and H is not None and L is not None: return 0.42*Pr**0.012*Ra**0.25*(L/H)**0.25 return 0.049*Ra**0.33 ratios_uninsulated_Catton = [0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6] Racs_uninstulated_Catton = [[9802960, 1554480, 606001, 469377, 444995, 444363, 457007, 473725, 494741], [1554480, 638754, 115596, 64270.8, 53529.7, 50816.4, 50136.1, 50088.7, 50410.1], [606001, 115596, 48178.9, 14615.3, 11374.5, 9831.6, 9312, 9099.4, 8980.2], [469377, 64270.8, 14615.3, 6974, 5138.2, 3906, 3633.6, 3446.2, 3358], [444995, 53529.7, 11374.5, 5137.9, 3773.6, 2753.6, 2530.5, 2359.5, 2285.7], [444363, 50816.4, 9831.6, 3906, 2753, 2557.4, 2337.2, 2174.44, 2101], [457007, 50136.1, 9311.9, 3633.6, 2530.5, 2337.2, 2270.2, 2110.9, 2037.2], [473725, 50088.6, 9099.4, 3446.2, 2359.5, 2174.4, 2110.9, 2081.7, 2007.8], [494742, 50410.1, 8980.2, 3357.9, 2285.7, 2100.9, 2037.2, 2007.8, 1991.9]] tck_uninstulated_Catton = implementation_optimize_tck([[0.125, 0.125, 0.125, 0.125, 0.41375910864088195, 0.5819413331927507, 1.9885569998423345, 2.8009586482973834, 3.922852887459219, 6.0, 6.0, 6.0, 6.0], [0.125, 0.125, 0.125, 0.125, 0.4180739258304788, 0.6521218159098487, 1.4270223336187269, 2.89426640315332, 3.9239774081390215, 6.0, 6.0, 6.0, 6.0], [16.098194938851986, 14.026983058722742, 13.35866942808268, 13.043296359953983, 13.008470795621905, 12.991279831677808, 13.040841344665466, 13.07803101947673, 13.111789672293794, 14.074352449019207, 14.878522936155216, 11.151352953023258, 11.096394321545977, 10.813773781060574, 10.796217122120712, 10.78189560829848, 10.774336865714089, 10.78004622910552, 13.400086198278455, 11.369928815173187, 11.82067779495709, 9.6860949637944, 9.686120336218499, 9.50952376562826, 9.444619552074945, 9.452058024482865, 9.441608909473647, 12.933722760010111, 10.873615956186896, 8.971126166473885, 8.520162104980807, 8.317346176887659, 7.837750498437191, 7.78951404473208, 7.690715685713949, 7.695209247397283, 13.025815591825872, 10.75723159025179, 9.734653433466208, 8.569056561731081, 8.77031704228521, 7.853798846698488, 7.939088236475908, 7.748880239519593, 7.785611785518214, 12.992898431724237, 10.728320934519346, 9.37520794405935, 8.247995842200584, 7.753730020752022, 7.937553314495094, 7.6598493250444255, 7.673199977054488, 7.63790748099515, 13.041869920313422, 10.713059500923494, 9.364505568407685, 8.18000143764639, 7.927764179244221, 7.660718938605501, 7.85174473958641, 7.5354388646400965, 7.614740168201775, 13.077057211283323, 10.706667262420716, 9.341451094646674, 8.122270764822368, 7.671593316397699, 7.697000470802994, 7.530680469875164, 7.720180133976149, 7.59900173760075, 13.111791693551362, 10.711047679739433, 9.339770955175847, 8.117021359757253, 7.727537757463738, 7.654072928976537, 7.607359118625173, 7.602197791148399, 7.596844236081228], 3, 3]) ratios_insulated_Catton = [0.125, 0.25, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 12] Racs_instulated_Catton = [[3011718, 333013, 70040, 37689, 39798, 36262, 37058, 35875, 36209, 35664, 35794, 35486, 35556, 35380, 35451, 35193], [333013, 203163, 28452, 11962, 12540, 11020, 11251, 10757, 10858, 10635, 10666, 10544, 10571, 10499, 10518, 10426], [70040, 28452, 17307, 5262, 5341, 4524, 4567, 4330, 4355, 4245, 4261, 4186, 4196, 4158, 4165, 4118], [37689, 11962, 5262, 3446, 3270, 2789, 2754, 2622, 2609, 2552, 2545, 2502, 2498, 2480, 2447, 2453], [39798, 12540, 5341, 3270, None, None, None, None, None, None, None, None, None, None, None, None], [36262, 11020, 4524, 2789, None, 2276, 2222, 2121, 2098, 2057, 2044, 2009, 2001, 1989, 1984, 1967], [37058, 11251, 4567, 2754, None, 2222, None, None, None, None, None, None, None, None, None, None], [35875, 10757, 4330, 2622, None, 2121, None, 2004, 1978, 1941, 1927, 1897, 1888, 1879, 1871, 1855], [36209, 10858, 4355, 2609, None, 2098, None, 1978, None, None, None, None, None, None, None, None], [35664, 10635, 4245, 2552, None, 2057, None, 1941, None, 1894, 1878, 1852, 1842, 1833, 1826, 1808], [35794, 10666, 4261, 2545, None, 2044, None, 1927, None, 1878, None, None, None, None, None, None], [35486, 10544, 4186, 2502, None, 2009, None, 1897, None, 1852, None, None, None, 1810, 1803, 1783], [35556, 10571, 4196, 2498, None, 2001, None, 1888, None, 1842, None, None, None, None, None, None], [35380, 10499, 4158, 2480, None, 1989, None, 1879, None, 1833, None, 1810, None, 1797, 1789, 1768], [35451, 10518, 4165, 2447, None, 1984, None, 1871, None, 1826, None, 1803, None, 1789, None, None], [35193, 10426, 4118, 2453, None, 1967, None, 1855, None, 1808, None, 1783, None, 1768, None, 1741]] tck_insulated_Catton = implementation_optimize_tck([[0.125, 0.125, 0.2165763979498294, 0.25, 0.4948545767149843, 0.8432690088415454, 2.297018168305444, 5.324310151069744, 12.0, 12.0], [0.125, 0.125, 0.125, 0.37135574365684176, 0.8160817162671293, 1.1103105500488575, 1.9000136398530074, 3.521092600950009, 12.0, 12.0, 12.0], [14.917942380813974, 12.196391449028951, 10.665084931671647, 10.531834082947338, 10.57637568816619, 10.486173564722383, 10.471864979770599, 10.468190753935556, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 12.715841947316376, 12.462417612931137, 9.174421085152083, 9.411191211042704, 9.409695481542864, 9.28122664900159, 9.249608368005552, 9.251639244971427, 11.165512470689693, 10.01308504970903, 9.75292707527754, 8.509349912597454, 8.566854764542974, 8.372517445356857, 8.32618713246236, 8.329704835832104, 10.56848779064929, 9.163970117017675, 8.369187019066972, 8.19799054440329, 8.087508877612247, 7.896372367041187, 7.806891615973793, 7.835687464634469, 10.509836235182163, 9.041210689705586, 8.118960504225761, 7.909354018896528, 7.735269232380504, 7.614379036546508, 7.4775491512154515, 7.529024952770015, 10.474423221467699, 8.98482837851057, 8.036532362247245, 7.822308882170893, 7.6362269726600065, 7.539826337638537, 7.459554042916101, 7.480930154132415, 10.469149134470264, 8.978694786931275, 8.024134988827441, 7.811393154091167, 7.627457342156321, 7.521833838146938, 7.4376750879045455, 7.462202956737165], 1, 2]) def Rac_Nusselt_Rayleigh(H: float, L: float, W: float, insulated: bool=True) -> float: r"""Calculates the critical Rayleigh number for free convection to begin in the Nusselt-Rayleigh parallel horizontal plate scenario. There are actually two cases - one for the top plate to be insulated (adiabatic) and the other where it has infinite thermal conductivity/is infinitely thin or not present (perfectly conducting). All real cases will lie between the two. Parameters ---------- H : float Distance between the two plates, [m] L : float Length of the plates, [m] W : float Width of the plates, [m] insulated : bool, optional Whether the top plate is insulated or uninsulated, [-] Returns ------- Rac : float Critical Rayleigh number, [-] Examples -------- >>> Rac_Nusselt_Rayleigh(1, .5, 2, False) 2530.500000000005 >>> Rac_Nusselt_Rayleigh(1, .5, 2, True) 2071.0089443385655 Notes ----- Splines have been fit to data in [1]_ for the uninsulated case and [2]_ for the insulated case. The data is presented in the original papers and in [3]_. References ---------- .. [1] Catton, Ivan. "Effect of Wall Conduction on the Stability of a Fluid in a Rectangular Region Heated from Below." Journal of Heat Transfer 94, no. 4 (November 1, 1972): 446-52. https://doi.org/10.1115/1.3449966. .. [2] Catton, Ivan. "Convection in a Closed Rectangular Region: The Onset of Motion." Journal of Heat Transfer 92, no. 1 (February 1, 1970): 186-88. https://doi.org/10.1115/1.3449626. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ H_L_ratio = min(max(H/L, 0.125), 12.0) W_L_ratio = min(max(W/L, 0.125), 12.0) if insulated: Rac = exp(bisplev(W_L_ratio, H_L_ratio, tck_insulated_Catton)) else: Rac = exp(bisplev(W_L_ratio, H_L_ratio, tck_uninstulated_Catton)) return Rac uninsulated_disk_coeffs = [1.3624571738082523, -0.24301326192178863, -6.152310426160362, 1.1950540229805053, 11.401090141352329, -2.405543860763877, -11.091871509655324, 2.519761389270987, 5.992609902331248, -1.4345227368881952, -1.7445130176764998, 0.42892571421446996, 0.22897205478499438, -0.042179780698649895, -0.01904413256783342, 0.006771075600246057, 0.13171026423861615] insulated_disk_coeffs = [0.2173851248644496, 0.09672312658254612, -1.0800494968302843, -0.3323452633903514, 2.1789014174652115, 0.43391756058946473, -2.275756526433769, -0.29309565826688255, 1.3153930583762103, 0.14707146242791974, -0.44891166228441826, -0.045070571352735386, 0.08693822836596571, 0.010343944709216, -0.01325209778273359, 0.0035707992137628142, 0.13258956599554672] def Rac_Nusselt_Rayleigh_disk(H: float, D: float, insulated: bool=True) -> float: r"""Calculates the critical Rayleigh number for free convection to begin in the parallel horizontal disk scenario. There are actually two cases - one for the top plate to be insulated (adiabatic) and the other where it has infinite thermal conductivity/is infinitely thin or not present (perfectly conducting). All real cases will lie between the two. Parameters ---------- H : float Distance between the two disks, [m] D : float Diameter of the two disks, [m] insulated : bool, optional Whether the top plate is insulated or uninsulated, [-] Returns ------- Rac : float Critical Rayleigh number, [-] Examples -------- >>> Rac_Nusselt_Rayleigh_disk(H=1, D=.4, insulated=False) 151199.9999999945 >>> Rac_Nusselt_Rayleigh_disk(H=1, D=4, insulated=False) 1891.520931853363 >>> Rac_Nusselt_Rayleigh_disk(2, 1, True) 24347.31479211917 Notes ----- The range of data covered by this function is `D`/`H` from 0.4 to infinity. As inifinity is not well suited to polynomial form, the upper limit is 6 in actuality. Values outside that range are rounded to the limits. This function provides 17-coefficient polynomial fits to interpolate in the table of values in [1]_. The source of the coefficients is cited as being from [2]_. References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Buell, J. C., and I. Catton. "The Effect of Wall Conduction on the Stability of a Fluid in a Right Circular Cylinder Heated From Below." Journal of Heat Transfer 105, no. 2 (May 1, 1983): 255-60. https://doi.org/10.1115/1.3245571. """ x = min(max(D/H, 0.4), 6.0) if insulated: coeffs = insulated_disk_coeffs else: coeffs = uninsulated_disk_coeffs return exp(1.0/horner(coeffs, 0.357142857142857151*(x - 3.2))) ### Free convection vertical helical coil def Nu_vertical_helical_coil_Ali(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a vertical helical coil inside a tank or other vessel according to the Ali [1]_ correlation. .. math:: Nu_L = 0.555Gr_L^{0.301} Pr^{0.314} Parameters ---------- Pr : float Prandtl number of the fluid surrounding the coil with properties evaluated at bulk conditions or as described in the notes [-] Gr : float Prandtl number of the fluid surrounding the coil with properties evaluated at bulk conditions or as described in the notes (for the two temperatures, use the average coil fluid temperature and the temperature of the fluid outside the coil) [-] Returns ------- Nu : float Nusselt number with respect to the total length of the helical coil (and bulk thermal conductivity), [-] Notes ----- In [1]_, the temperature at which the fluid surrounding the coil's properties were evaluated at was calculated in an unusual fashion. The average temperature of the fluid inside the coil :math:`(T_{in} + T_{out})/2` is averaged with the fluid outside the coil's temperature. The correlation is valid for Prandtl numbers between 4.4 and 345, and tank diameter/coil outer diameter ratios between 10 and 30. Examples -------- >>> Nu_vertical_helical_coil_Ali(4.4, 1E11) 1808.57749972 References ---------- .. [1] Ali, Mohamed E. "Natural Convection Heat Transfer from Vertical Helical Coils in Oil." Heat Transfer Engineering 27, no. 3 (April 1, 2006): 79-85. """ return 0.555*Gr**0.301*Pr**0.314 def Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a vertical helical coil inside a tank or other vessel according to the Prabhanjan, Rennie, and Raghavan [1]_ correlation. .. math:: Nu_H = 0.0749\text{Ra}_H^{0.3421} The range of Rayleigh numbers is as follows: .. math:: 9 \times 10^{9} < \text{Ra} < 4 \times 10^{11} Parameters ---------- Pr : float Prandtl number calculated with the film temperature - wall and temperature very far from the coil average, [-] Gr : float Grashof number calculated with the film temperature - wall and temperature very far from the coil average, and using the total height of the coil [-] Returns ------- Nu : float Nusselt number using the total height of the coil and the film temperature, [-] Notes ----- [1]_ also has several other equations using different characteristic lengths. Examples -------- >>> Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan(4.4, 1E11) 720.6211067718227 References ---------- .. [1] Prabhanjan, Devanahalli G., Timothy J. Rennie, and G. S. Vijaya Raghavan. "Natural Convection Heat Transfer from Helical Coiled Tubes." International Journal of Thermal Sciences 43, no. 4 (April 1, 2004): 359-65. """ Ra = Pr*Gr return 0.0749*Ra**0.3421 ================================================ FILE: ht/conv_free_immersed.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import log __all__: list[str] = [ "Nu_coil_Xin_Ebadian", "Nu_free_horizontal_plate", "Nu_free_horizontal_plate_methods", "Nu_free_vertical_plate", "Nu_free_vertical_plate_methods", "Nu_horizontal_cylinder", "Nu_horizontal_cylinder_Churchill_Chu", "Nu_horizontal_cylinder_Kuehn_Goldstein", "Nu_horizontal_cylinder_Morgan", "Nu_horizontal_cylinder_methods", "Nu_horizontal_plate_McAdams", "Nu_horizontal_plate_Rohsenow", "Nu_horizontal_plate_VDI", "Nu_sphere_Churchill", "Nu_vertical_cylinder", "Nu_vertical_cylinder_Al_Arabi_Khamis", "Nu_vertical_cylinder_Carne_Morgan", "Nu_vertical_cylinder_Eigenson_Morgan", "Nu_vertical_cylinder_Griffiths_Davis_Morgan", "Nu_vertical_cylinder_Hanesian_Kalish_Morgan", "Nu_vertical_cylinder_Jakob_Linke_Morgan", "Nu_vertical_cylinder_Kreith_Eckert", "Nu_vertical_cylinder_McAdams_Weiss_Saunders", "Nu_vertical_cylinder_Popiel_Churchill", "Nu_vertical_cylinder_Touloukian_Morgan", "Nu_vertical_cylinder_methods", "Nu_vertical_plate_Churchill", ] def Nu_vertical_plate_Churchill(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a vertical plate according to the Churchill-Chu [1]_ correlation, also presented in [2]_. Plate must be isothermal; an alternate expression exists for constant heat flux. .. math:: Nu_{L}=\left[0.825+\frac{0.387Ra_{L}^{1/6}} {[1+(0.492/Pr)^{9/16}]^{8/27}}\right]^2 Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] Returns ------- Nu : float Nusselt number with respect to height, [-] Notes ----- Although transition from laminar to turbulent is discrete in reality, this equation provides a smooth transition in value from laminar to turbulent. Checked with the original source. Can be applied to vertical cylinders as well, subject to the criteria below: .. math:: \frac{D}{L}\ge \frac{35}{Gr_L^{1/4}} Examples -------- From [2]_, Example 9.2, matches: >>> Nu_vertical_plate_Churchill(0.69, 2.63E9) 147.16185223770603 References ---------- .. [1] Churchill, Stuart W., and Humbert H. S. Chu. "Correlating Equations for Laminar and Turbulent Free Convection from a Vertical Plate." International Journal of Heat and Mass Transfer 18, no. 11 (November 1, 1975): 1323-29. doi:10.1016/0017-9310(75)90243-4. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Ra = Pr*Gr term = (0.825 + (0.387*Ra**(1/6.)*(1.0 + (Pr/0.492)**(-0.5625))**(-8.0/27.0))) return term*term Nu_free_vertical_plate_all_methods = ["Churchill"] def Nu_free_vertical_plate_methods(Pr: float, Gr: float, H: float | None=None, W: float | None=None, check_ranges: bool=True) -> list[str]: r"""This function returns a list of methods for calculating heat transfer coefficient for external free convection from a verical plate. Requires at a minimum a fluid's Prandtl number `Pr`, and the Grashof number `Gr` for the system fluid (which require T and P to obtain). `L` and `W` are not used by any correlations presently, but are included for future support. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] H : float, optional Height of vertical plate, [m] W : float, optional Width of the vertical plate, [m] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `Nu` with the given inputs, [-] Examples -------- >>> Nu_free_vertical_plate_methods(0.69, 2.63E9) ['Churchill'] """ return Nu_free_vertical_plate_all_methods def Nu_free_vertical_plate(Pr: float, Gr: float, buoyancy: None=None, H: float | None=None, W: float | None=None, Method: str | None=None) -> float: r"""This function calculates the heat transfer coefficient for external free convection from a verical plate. Requires at a minimum a fluid's Prandtl number `Pr`, and the Grashof number `Gr` for the system fluid (which require T and P to obtain). `L` and `W` are not used by any correlations presently, but are included for future support. If no correlation's name is provided as `Method`, the 'Churchill' correlation is selected. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] H : float, optional Height of vertical plate, [m] W : float, optional Width of the vertical plate, [m] Returns ------- Nu : float Nusselt number with respect to plate height, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use; one of ('Churchill', ). Examples -------- Turbulent example >>> Nu_free_vertical_plate(0.69, 2.63E9, False) 147.16185223770603 """ if Method is None: Method2 = "Churchill" else: Method2 = Method if Method2 == "Churchill": return Nu_vertical_plate_Churchill(Pr, Gr) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") def Nu_horizontal_plate_McAdams(Pr: float, Gr: float, buoyancy: bool=True) -> float: r"""Calculates the Nusselt number for natural convection above a horizontal plate according to the McAdams [1]_ correlations. The plate must be isothermal. Four different equations are used, two each for laminar and turbulent; the two sets of correlations are required because if the plate is hot, buoyancy lifts the fluid off the plate and enhances free convection whereas if the plate is cold, the cold fluid above it settles on it and decreases the free convection. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] Returns ------- Nu : float Nusselt number with respect to length, [-] Notes ----- Examples -------- >>> Nu_horizontal_plate_McAdams(5.54, 3.21e8, buoyancy=True) 181.73121274384457 >>> Nu_horizontal_plate_McAdams(5.54, 3.21e8, buoyancy=False) 55.44564799362829 >>> Nu_horizontal_plate_McAdams(.01, 3.21e8, buoyancy=True) 22.857041558492334 >>> Nu_horizontal_plate_McAdams(.01, 3.21e8, buoyancy=False) 11.428520779246167 References ---------- .. [1] McAdams, William Henry. Heat Transmission. 3E. Malabar, Fla: Krieger Pub Co, 1985. """ Ra = Pr*Gr if buoyancy: if Ra <= 1E7: Nu = .54*Ra**0.25 else: Nu = 0.15*Ra**(1.0/3.0) else: if Ra <= 1E10: Nu = .27*Ra**0.25 else: Nu = .15*Ra**(1.0/3.0) return Nu def Nu_horizontal_plate_VDI(Pr: float, Gr: float, buoyancy: bool=True) -> float: r"""Calculates the Nusselt number for natural convection above a horizontal plate according to the VDI [1]_ correlations. The plate must be isothermal. Three different equations are used, one each for laminar and turbulent for the heat transfer happening at upper surface case and one for the case of heat transfer happening at the lower surface. The lower surface correlation is recommened for the laminar flow regime. The two different sets of correlations are required because if the plate is hot, buoyancy lifts the fluid off the plate and enhances free convection whereas if the plate is cold, the cold fluid above it settles on it and decreases the free convection. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] Returns ------- Nu : float Nusselt number with respect to length, [-] Notes ----- The characteristic length suggested for use is as follows, with `a` and `b` being the length and width of the plate. .. math:: L = \frac{ab}{2(a+b)} The buoyancy enhanced cases are from [2]_; the other is said to be from [3]_, although the equations there not quite the same and do not include the Prandtl number correction. Examples -------- >>> Nu_horizontal_plate_VDI(5.54, 3.21e8, buoyancy=True) 203.89681224927565 >>> Nu_horizontal_plate_VDI(5.54, 3.21e8, buoyancy=False) 39.16864971535617 References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. .. [2] Stewartson, Keith. "On the Free Convection from a Horizontal Plate." Zeitschrift Für Angewandte Mathematik Und Physik ZAMP 9, no. 3 (September 1, 1958): 276-82. https://doi.org/10.1007/BF02033031. .. [3] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. """ Ra = Pr*Gr if buoyancy: f2 = (1.0 + (0.322/Pr)**(0.55))**(20.0/11.0) if Ra*f2 < 7e4: return 0.766*(Ra*f2)**0.2 else: return 0.15*(Ra*f2)**(1.0/3.0) else: f1 = (1.0 + (0.492/Pr)**(9.0/16.0))**(-16.0/9.0) return 0.6*(Ra*f1)**0.2 def Nu_horizontal_plate_Rohsenow(Pr: float, Gr: float, buoyancy: bool=True) -> float: r"""Calculates the Nusselt number for natural convection above a horizontal plate according to the Rohsenow, Hartnett, and Cho (1998) [1]_ correlations. The plate must be isothermal. Three different equations are used, one each for laminar and turbulent for the heat transfer happening at upper surface case and one for the case of heat transfer happening at the lower surface. The lower surface correlation is recommened for the laminar flow regime. The two different sets of correlations are required because if the plate is hot, buoyancy lifts the fluid off the plate and enhances free convection whereas if the plate is cold, the cold fluid above it settles on it and decreases the free convection. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] Returns ------- Nu : float Nusselt number with respect to length, [-] Notes ----- The characteristic length suggested for use is as follows, with `a` and `b` being the length and width of the plate. .. math:: L = \frac{ab}{2(a+b)} Examples -------- >>> Nu_horizontal_plate_Rohsenow(5.54, 3.21e8, buoyancy=True) 175.91054716322836 >>> Nu_horizontal_plate_Rohsenow(5.54, 3.21e8, buoyancy=False) 35.95799244863986 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ Ra = Pr*Gr if buoyancy: C_tU = 0.14*((1.0 + 0.01707*Pr)/(1.0 + 0.01*Pr)) C_tV = 0.13*Pr**0.22/(1.0 + 0.61*Pr**0.81)**0.42 t1 = 1.0 # Ah/A # Heated to non heated area ratio t2 = 0.0 # Lf*P/A # Lf vertical distance between lowest and highest point in body # P is perimiter, A is area Cl = (0.0972 - (0.0157 + 0.462*C_tV)*t1 + (0.615*C_tV - 0.0548 - 6e-6*Pr)*t2) Nu_T = 0.835*Cl*Ra**0.25 # average Cl Nu_l = 1.4/(log(1.0 + 1.4/Nu_T)) Nu_t = C_tU*Ra**(1.0/3.0) m = 10.0 Nu = ((Nu_l)**m + Nu_t**m)**(1.0/m) return Nu else: # No friction/C term Nu_T = 0.527*Ra**0.2/(1.0 + (1.9/Pr)**0.9)**(2.0/9.0) Nu_l = 2.5/(log(1.0 + 2.5/Nu_T)) return Nu_l conv_free_horizontal_plate_all_methods = { "McAdams": (Nu_horizontal_plate_McAdams, ("Pr", "Gr", "buoyancy")), "VDI": (Nu_horizontal_plate_VDI, ("Pr", "Gr", "buoyancy")), "Rohsenow": (Nu_horizontal_plate_Rohsenow, ("Pr", "Gr", "buoyancy")), } Nu_free_horizontal_plate_all_methods = ["VDI", "McAdams", "Rohsenow"] def Nu_free_horizontal_plate_methods(Pr: float, Gr: float, buoyancy: bool, L: float | None=None, W: float | None=None, check_ranges: bool=True) -> list[str]: r"""This function returns a list of methods for calculating heat transfer coefficient for external free convection from a verical plate. Requires at a minimum a fluid's Prandtl number `Pr`, and the Grashof number `Gr` for the system fluid, temperatures, and geometry. `L` and `W` are not used by any correlations presently, but are included for future support. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] L : float, optional Length of horizontal plate, [m] W : float, optional Width of the horizontal plate, [m] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `Nu` with the given inputs, [-] Examples -------- >>> Nu_free_horizontal_plate_methods(0.69, 2.63E9, True) ['VDI', 'McAdams', 'Rohsenow'] """ return Nu_free_horizontal_plate_all_methods def Nu_free_horizontal_plate(Pr: float, Gr: float, buoyancy: bool, L: float | None=None, W: float | None=None, Method: str | None=None) -> float: r"""This function calculates the heat transfer coefficient for external free convection from a horizontal plate. Requires at a minimum a fluid's Prandtl number `Pr`, and the Grashof number `Gr` for the system fluid, temperatures, and geometry. `L` and `W` are not used by any correlations presently, but are included for future support. If no correlation's name is provided as `Method`, the 'VDI' correlation is selected. Parameters ---------- Pr : float Prandtl number with respect to fluid properties [-] Gr : float Grashof number with respect to fluid properties and plate - fluid temperature difference [-] buoyancy : bool, optional Whether or not the plate's free convection is buoyancy assisted (hot plate) or not, [-] L : float, optional Length of horizontal plate, [m] W : float, optional Width of the horizontal plate, [m] Returns ------- Nu : float Nusselt number with respect to plate length, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use, as in the dictionary conv_free_horizontal_plate_methods Examples -------- Turbulent example >>> Nu_free_horizontal_plate(5.54, 3.21e8, buoyancy=True) 203.89681224927565 >>> Nu_free_horizontal_plate(5.54, 3.21e8, buoyancy=True, Method='McAdams') 181.73121274384457 """ if Method is None: Method2 = "VDI" else: Method2 = Method if Method2 == "VDI": return Nu_horizontal_plate_VDI(Pr=Pr, Gr=Gr, buoyancy=buoyancy) if Method2 == "McAdams": return Nu_horizontal_plate_McAdams(Pr=Pr, Gr=Gr, buoyancy=buoyancy) if Method2 == "Rohsenow": return Nu_horizontal_plate_Rohsenow(Pr=Pr, Gr=Gr, buoyancy=buoyancy) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") def Nu_sphere_Churchill(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a sphere according to the Churchill [1]_ correlation. Sphere must be isothermal. .. math:: Nu_D=2+\frac{0.589Ra_D^{1/4}} {\left[1+(0.469/Pr)^{9/16}\right]^{4/9}} \cdot\left\{1 + \frac{7.44\times 10^{-8}Ra} {[1+(0.469/Pr)^{9/16}]^{16/9}}\right\}^{1/12} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Although transition from laminar to turbulent is discrete in reality, this equation provides a smooth transition in value from laminar to turbulent. Checked with the original source. Good for Ra < 1E13. Limit of Nu is 2 at low Grashof numbers. Examples -------- >>> Nu_sphere_Churchill(.7, 1E7) 25.670869440317578 References ---------- .. [1] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. """ Ra = Pr*Gr Nu = 2 + (0.589*Ra**0.25/(1 + (0.469/Pr)**(9/16.))**(4/9.)*( 1 + 7.44E-8*Ra/(1 + (0.469/Pr)**(9/16.))**(16/9.))**(1/12.)) return Nu ### Vertical cylinders def Nu_vertical_cylinder_Griffiths_Davis_Morgan(Pr: float, Gr: float, turbulent: bool | None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, as presented in [3]_ and [4]_. .. math:: Nu_H = 0.67 Ra_H^{0.25},\; 10^{7} < Ra < 10^{9} .. math:: Nu_H = 0.0782 Ra_H^{0.357}, \; 10^{9} < Ra < 10^{11} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Cylinder of diameter 17.43 cm, length from 4.65 to 263.5 cm. Air as fluid. Transition between ranges is not smooth. If outside of range, no warning is given. Examples -------- >>> Nu_vertical_cylinder_Griffiths_Davis_Morgan(.7, 2E10) 327.6230596100138 References ---------- .. [1] Griffiths, Ezer, A. H. Davis, and Great Britain. The Transmission of Heat by Radiation and Convection. London: H. M. Stationery off., 1922. .. [2] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [3] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [4] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 1E9 and turbulent is None): Nu = 0.0782*Ra**0.357 else: Nu = 0.67*Ra**0.25 return Nu def Nu_vertical_cylinder_Jakob_Linke_Morgan(Pr: float, Gr: float, turbulent: bool | None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, as presented in [3]_ and [4]_. .. math:: Nu_H = 0.555 Ra_H^{0.25},\; 10^{4} < Ra < 10^{8} .. math:: Nu_H = 0.129 Ra_H^{1/3},\; 10^{8} < Ra < 10^{12} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Cylinder of diameter 3.5 cm, length from L/D = 4.3. Air as fluid. Transition between ranges is not smooth. If outside of range, no warning is given. Results are presented rounded in [4]_, and the second range is not shown in [3]_. Examples -------- >>> Nu_vertical_cylinder_Jakob_Linke_Morgan(.7, 2E10) 310.90835207860454 References ---------- .. [1] Jakob, M., and Linke, W., Warmeubergang beim Verdampfen von Flussigkeiten an senkrechten und waagerechten Flaschen, Phys. Z., vol. 36, pp. 267-280, 1935. .. [2] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [3] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [4] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 1E8 and turbulent is None): Nu = 0.129*Ra**(1/3.) else: Nu = 0.555*Ra**0.25 return Nu def Nu_vertical_cylinder_Carne_Morgan(Pr: float, Gr: float, turbulent: bool | None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, as presented in [3]_ and [4]_. .. math:: Nu_H = 1.07 Ra_H^{0.28},\; 2\times 10^{6} < Ra < 2\times 10^{8} .. math:: Nu_H = 0.152 Ra_H^{0.38},\; 2\times 10^{8} < Ra < 2\times 10^{11} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Cylinder of diameters 0.475 cm to 7.62 cm, L/D from 8 to 127. Isothermal boundary condition was assumed, but not verified. Transition between ranges is not smooth. If outside of range, no warning is given. The higher range of [1]_ is not shown in [3]_, and the formula for the first is actually for the second in [3]_. Examples -------- >>> Nu_vertical_cylinder_Carne_Morgan(.7, 2E8) 204.31470629065677 References ---------- .. [1] J. B. Carne. "LIX. Heat Loss by Natural Convection from Vertical Cylinders." The London, Edinburgh, and Dublin Philosophical Magazine and Journal of Science 24, no. 162 (October 1, 1937): 634-53. doi:10.1080/14786443708565140. .. [2] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [3] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [4] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 2E8 and turbulent is None): return 0.152*Ra**0.38 else: return 1.07*Ra**0.28 def Nu_vertical_cylinder_Eigenson_Morgan(Pr: float, Gr: float, turbulent: None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, presented in [3]_ and in more detail in [4]_. .. math:: Nu_H = 0.48 Ra_H^{0.25},\; 10^{9} < Ra .. math:: Nu_H = 51.5 + 0.0000726 Ra_H^{0.63},\; 10^{9} < Ra < 1.69 \times 10^{10} .. math:: Nu_H = 0.148 Ra_H^{1/3} - 127.6 ,\; 1.69 \times 10^{10} < Ra Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Author presents results as appropriate for both flat plates and cylinders. Height of 2.5 m with diameters of 2.4, 7.55, 15, 35, and 50 mm. Another experiment of diameter 58 mm and length of 6.5 m was considered. Cylinder of diameters 0.475 cm to 7.62 cm, L/D from 8 to 127.Transition between ranges is not smooth. If outside of range, no warning is given. Formulas are presented similarly in [3]_ and [4]_, but only [4]_ shows the transition formula. Examples -------- >>> Nu_vertical_cylinder_Eigenson_Morgan(0.7, 2E10) 230.55946525499715 References ---------- .. [1] Eigenson L (1940). Les lois gouvernant la transmission de la chaleur aux gaz biatomiques par les parois des cylindres verticaux dans le cas de convection naturelle. Dokl Akad Nauk SSSR 26:440-444 .. [2] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [3] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [4] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 1.69E10 and turbulent is None): return 0.148*Ra**(1/3.) - 127.6 elif 1E9 < Ra < 1.69E10 and turbulent is not False: return 51.5 + 0.0000726*Ra**0.63 else: return 0.48*Ra**0.25 def Nu_vertical_cylinder_Touloukian_Morgan(Pr: float, Gr: float, turbulent: bool | None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, as presented in [3]_ and [4]_. .. math:: Nu_H = 0.726 Ra_H^{0.25},\; 2\times 10^{8} < Ra < 4\times 10^{10} .. math:: Nu_H = 0.0674 (Gr_H Pr^{1.29})^{1/3},\; 4\times 10^{10} < Ra < 9\times 10^{11} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Cylinder of diameters 2.75 inch, with heights of 6, 18, and 36.25 inch. Temperature was controlled via multiple separately controlled heating sections. Fluids were water and ethylene-glycol. Transition between ranges is not smooth. If outside of range, no warning is given. [2]_, [3]_, and [4]_ are in complete agreement about this formulation. Examples -------- >>> Nu_vertical_cylinder_Touloukian_Morgan(.7, 2E10) 249.72879961097854 References ---------- .. [1] Touloukian, Y. S, George A Hawkins, and Max Jakob. Heat Transfer by Free Convection from Heated Vertical Surfaces to Liquids. Trans. ASME 70, 13-18 (1948). .. [2] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [3] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [4] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 4E10 and turbulent is None): return 0.0674*(Gr*Pr**1.29)**(1/3.) else: return 0.726*Ra**0.25 def Nu_vertical_cylinder_McAdams_Weiss_Saunders(Pr: float, Gr: float, turbulent: bool | None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ and [2]_ correlated by [3]_, as presented in [4]_, [5]_, and [6]_. .. math:: Nu_H = 0.59 Ra_H^{0.25},\; 10^{4} < Ra < 10^{9} .. math:: Nu_H = 0.13 Ra_H^{1/3.},\; 10^{9} < Ra < 10^{12} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Transition between ranges is not smooth. If outside of range, no warning is given. For ranges under 10^4, a graph is provided, not included here. Examples -------- >>> Nu_vertical_cylinder_McAdams_Weiss_Saunders(.7, 2E10) 313.31849434277973 References ---------- .. [1] Weise, Rudolf. "Warmeubergang durch freie Konvektion an quadratischen Platten." Forschung auf dem Gebiet des Ingenieurwesens A 6, no. 6 (November 1935): 281-92. doi:10.1007/BF02592565. .. [2] Saunders, O. A. "The Effect of Pressure Upon Natural Convection in Air." Proceedings of the Royal Society of London A: Mathematical, Physical and Engineering Sciences 157, no. 891 (November 2, 1936): 278-91. doi:10.1098/rspa.1936.0194. .. [3] McAdams, William Henry. Heat Transmission. 3E. Malabar, Fla: Krieger Pub Co, 1985. .. [4] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [5] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [6] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 1E9 and turbulent is None): return 0.13*Ra**(1/3.) else: return 0.59*Ra**0.25 def Nu_vertical_cylinder_Kreith_Eckert(Pr: float, Gr: float, turbulent: bool | None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, also as presented in [3]_, [4]_, and [5]_. .. math:: Nu_H = 0.555 Ra_H^{0.25},\; 10^{5} < Ra < 10^{9} .. math:: Nu_H = 0.021 Ra_H^{0.4},\; 10^{9} < Ra < 10^{12} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection Returns ------- Nu : float Nusselt number, [-] Notes ----- Transition between ranges is not smooth. If outside of range, no warning is given. Examples -------- >>> Nu_vertical_cylinder_Kreith_Eckert(.7, 2E10) 240.25393473033196 References ---------- .. [1] Eckert, E. R. G., Thomas W. Jackson, and United States. Analysis of Turbulent Free-Convection Boundary Layer on Flat Plate. National Advisory Committee for Aeronautics, no. 2207. Washington, D.C.: National Advisoty Committee for Aeronautics, 1950. .. [2] Kreith, Frank, Raj Manglik, and Mark Bohn. Principles of Heat Transfer. Cengage, 2010. .. [3] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [4] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [5] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if turbulent or (Ra > 1E9 and turbulent is None): return 0.021*Ra**0.4 else: return 0.555*Ra**0.25 def Nu_vertical_cylinder_Hanesian_Kalish_Morgan(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to the results of [1]_ correlated by [2]_, also as presented in [3]_ and [4]_. .. math:: Nu_H = 0.48 Ra_H^{0.23},\; 10^{6} < Ra < 10^{8} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- For air and fluoro-carbons. If outside of range, no warning is given. Laminar range only! Examples -------- >>> Nu_vertical_cylinder_Hanesian_Kalish_Morgan(.7, 1E7) 18.014150492696604 References ---------- .. [1] Hanesian, D. and Kalish, R. "Heat Transfer by Natural Convection with Fluorocarbon Gases." IEEE Transactions on Parts, Materials and Packaging 6, no. 4 (December 1970): 147-148. doi:10.1109/TPMP.1970.1136270. .. [2] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [3] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [4] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr return 0.48*Ra**0.23 ### Vertical cylinders, more complex correlations def Nu_vertical_cylinder_Al_Arabi_Khamis(Pr: float, Gr: float, L: float, D: float, turbulent: None=None) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to [1]_, also as presented in [2]_ and [3]_. .. math:: Nu_H = 2.9Ra_H^{0.25}/Gr_D^{1/12},\; 9.88 \times 10^7 \le Ra_H \le 2.7\times10^{9} .. math:: Nu_H = 0.47 Ra_H^{0.333}/Gr_D^{1/12},\; 2.7 \times 10^9 \le Ra_H \le 2.95\times10^{10} Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number with respect to cylinder height [-] L : float Length of vertical cylinder, [m] D : float Diameter of cylinder, [m] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False; leave as None for automatic selection, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- For air. Local Nusselt number results also given in [1]_. D from 12.75 to 51 mm; H from 300 to 2000 mm. Temperature kept constant by steam condensing. If outside of range, no warning is given. Applies for range of: .. math:: 1.08 \times 10^4 \le Gr_D \le 6.9 \times 10^5 Examples -------- >>> Nu_vertical_cylinder_Al_Arabi_Khamis(.71, 2E10, 10, 1) 280.39793209114765 References ---------- .. [1] Al-Arabi, M., and M. Khamis. "Natural Convection Heat Transfer from Inclined Cylinders." International Journal of Heat and Mass Transfer 25, no. 1 (January 1982): 3-15. doi:10.1016/0017-9310(82)90229-0. .. [2] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [3] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Gr_D = Gr/L**3*D**3 Ra = Pr*Gr if turbulent or (Ra > 2.6E9 and turbulent is None): return 0.47*Ra**(1/3.)*Gr_D**(-1/12.) else: return 2.9*Ra**0.25*Gr_D**(-1/12.) def Nu_vertical_cylinder_Popiel_Churchill(Pr: float, Gr: float, L: float, D: float) -> float: r"""Calculates Nusselt number for natural convection around a vertical isothermal cylinder according to [1]_, also presented in [2]_. .. math:: \frac{Nu}{Nu_{L,fp}} = 1 + B\left[32^{0.5}Gr_L^{-0.25}\frac{L}{D}\right]^C .. math:: B = 0.0571322 + 0.20305 Pr^{-0.43} .. math:: C = 0.9165 - 0.0043Pr^{0.5} + 0.01333\ln Pr + 0.0004809/Pr Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number with respect to cylinder height [-] L : float Length of vertical cylinder, [m] D : float Diameter of cylinder, [m] Returns ------- Nu : float Nusselt number, [-] Notes ----- For 0.01 < Pr < 100. Requires a vertical flat plate correlation. Both [2], [3] present a power of 2 instead of 0.5 on the 32 in the equation, but the original has the correct form. Examples -------- >>> Nu_vertical_cylinder_Popiel_Churchill(0.7, 1E10, 2.5, 1) 228.89790055149896 References ---------- .. [1] Popiel, C. O., J. Wojtkowiak, and K. Bober. "Laminar Free Convective Heat Transfer from Isothermal Vertical Slender Cylinder." Experimental Thermal and Fluid Science 32, no. 2 (November 2007): 607-613. doi:10.1016/j.expthermflusci.2007.07.003. .. [2] Popiel, Czeslaw O. "Free Convection Heat Transfer from Vertical Slender Cylinders: A Review." Heat Transfer Engineering 29, no. 6 (June 1, 2008): 521-36. doi:10.1080/01457630801891557. .. [3] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ B = 0.0571322 + 0.20305*Pr**-0.43 C = 0.9165 - 0.0043*Pr**0.5 + 0.01333*log(Pr) + 0.0004809/Pr Nu_fp = Nu_vertical_plate_Churchill(Pr, Gr) return Nu_fp*(1 + B*(32**0.5*Gr**-0.25*L/D)**C) # Nice Name : (function_call, does_turbulent, does_laminar, transition_Ra, is_only_Pr_Gr) vertical_cylinder_correlations = { "Churchill Vertical Plate": (Nu_vertical_plate_Churchill, True, True, None, True), "Griffiths, Davis, & Morgan": (Nu_vertical_cylinder_Griffiths_Davis_Morgan, True, True, 1.00E+009, True), "Jakob, Linke, & Morgan": (Nu_vertical_cylinder_Jakob_Linke_Morgan, True, True, 1.00E+008, True), "Carne & Morgan": (Nu_vertical_cylinder_Carne_Morgan, True, True, 2.00E+008, True), "Eigenson & Morgan": (Nu_vertical_cylinder_Eigenson_Morgan, True, True, 6.90E+011, True), "Touloukian & Morgan": (Nu_vertical_cylinder_Touloukian_Morgan, True, True, 4.00E+010, True), "McAdams, Weiss & Saunders": (Nu_vertical_cylinder_McAdams_Weiss_Saunders, True, True, 1.00E+009, True), "Kreith & Eckert": (Nu_vertical_cylinder_Kreith_Eckert, True, True, 1.00E+009, True), "Hanesian, Kalish & Morgan": (Nu_vertical_cylinder_Hanesian_Kalish_Morgan, False, True, 1.00E+008, True), "Al-Arabi & Khamis": (Nu_vertical_cylinder_Al_Arabi_Khamis, True, True, 2.60E+009, False), "Popiel & Churchill": (Nu_vertical_cylinder_Popiel_Churchill, False, True, 1.00E+009, False), } def Nu_vertical_cylinder_methods(Pr: float, Gr: float, L: float | None=None, D: float | None=None, check_ranges: bool=True) -> list[str]: r"""This function returns a list of correlation names for free convetion to a vertical cylinder. The functions returned are 'Popiel & Churchill' for fully defined geometries, and 'McAdams, Weiss & Saunders' otherwise. Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number with respect to cylinder height [-] L : float, optional Length of vertical cylinder, [m] D : float, optional Diameter of cylinder, [m] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `Nu` with the given inputs Examples -------- >>> Nu_vertical_cylinder_methods(0.72, 1E7)[0] 'McAdams, Weiss & Saunders' """ if L is None or D is None: return ["McAdams, Weiss & Saunders", "Churchill Vertical Plate", "Griffiths, Davis, & Morgan", "Jakob, Linke, & Morgan", "Carne & Morgan", "Eigenson & Morgan", "Touloukian & Morgan", "Kreith & Eckert", "Hanesian, Kalish & Morgan"] else: return ["Popiel & Churchill", "Churchill Vertical Plate", "Griffiths, Davis, & Morgan", "Jakob, Linke, & Morgan", "Carne & Morgan", "Eigenson & Morgan", "Touloukian & Morgan", "McAdams, Weiss & Saunders", "Kreith & Eckert", "Hanesian, Kalish & Morgan", "Al-Arabi & Khamis"] def Nu_vertical_cylinder(Pr: float, Gr: float, L: float | None=None, D: float | None=None, Method: str | None=None) -> float: r"""This function handles choosing which vertical cylinder free convection correlation is used. Generally this is used by a helper class, but can be used directly. Will automatically select the correlation to use if none is provided; returns None if insufficient information is provided. Preferred functions are 'Popiel & Churchill' for fully defined geometries, and 'McAdams, Weiss & Saunders' otherwise. Examples -------- >>> Nu_vertical_cylinder(0.72, 1E7) 30.562236756513943 Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number with respect to cylinder height [-] L : float, optional Length of vertical cylinder, [m] D : float, optional Diameter of cylinder, [m] Returns ------- Nu : float Nusselt number, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use, as in the dictionary vertical_cylinder_correlations """ if Method is None: if L is None or D is None: Method2 = "McAdams, Weiss & Saunders" else: Method2 = "Popiel & Churchill" else: Method2 = Method if Method2 == "Churchill Vertical Plate": return Nu_vertical_plate_Churchill(Pr=Pr, Gr=Gr) elif Method2 == "Griffiths, Davis, & Morgan": return Nu_vertical_cylinder_Griffiths_Davis_Morgan(Pr=Pr, Gr=Gr) elif Method2 == "Jakob, Linke, & Morgan": return Nu_vertical_cylinder_Jakob_Linke_Morgan(Pr=Pr, Gr=Gr) elif Method2 == "Carne & Morgan": return Nu_vertical_cylinder_Carne_Morgan(Pr=Pr, Gr=Gr) elif Method2 == "Eigenson & Morgan": return Nu_vertical_cylinder_Eigenson_Morgan(Pr=Pr, Gr=Gr) elif Method2 == "Touloukian & Morgan": return Nu_vertical_cylinder_Touloukian_Morgan(Pr=Pr, Gr=Gr) elif Method2 == "McAdams, Weiss & Saunders": return Nu_vertical_cylinder_McAdams_Weiss_Saunders(Pr=Pr, Gr=Gr) elif Method2 == "Kreith & Eckert": return Nu_vertical_cylinder_Kreith_Eckert(Pr=Pr, Gr=Gr) elif Method2 == "Hanesian, Kalish & Morgan": return Nu_vertical_cylinder_Hanesian_Kalish_Morgan(Pr=Pr, Gr=Gr) elif Method2 == "Al-Arabi & Khamis": return Nu_vertical_cylinder_Al_Arabi_Khamis(Pr=Pr, Gr=Gr, L=L, D=D) elif Method2 == "Popiel & Churchill": return Nu_vertical_cylinder_Popiel_Churchill(Pr=Pr, Gr=Gr, L=L, D=D) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") #import matplotlib.pyplot as plt #import numpy as np ##L, D = 1.5, 0.1 #Pr, Gr = 0.72, 1E8 #methods = Nu_vertical_cylinder_methods(Pr, Gr) #Grs = np.logspace(2, 12, 10000) # #for method in methods: # Nus = [Nu_vertical_cylinder(Pr=Pr, Gr=i, Method=method) for i in Grs] # plt.loglog(Grs, Nus, label=method) #plt.legend() #plt.show() ### Horizontal Cylinders def Nu_horizontal_cylinder_Churchill_Chu(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a horizontal cylinder according to the Churchill-Chu [1]_ correlation, also presented in [2]_. Cylinder must be isothermal; an alternate expression exists for constant heat flux. .. math:: Nu_{D}=\left[0.60+\frac{0.387Ra_{D}^{1/6}} {[1+(0.559/Pr)^{9/16}]^{8/27}}\right]^2 Parameters ---------- Pr : float Prandtl number [-] Gr : float Grashof number with respect to cylinder diameter, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Although transition from laminar to turbulent is discrete in reality, this equation provides a smooth transition in value from laminar to turbulent. Checked with the original source, which has its powers unsimplified but is equivalent. [1]_ recommends 1E-5 as the lower limit for Ra, but no upper limit. [2]_ suggests an upper limit of 1E12. Examples -------- From [2]_, Example 9.2, matches: >>> Nu_horizontal_cylinder_Churchill_Chu(0.69, 2.63E9) 139.13493970073597 References ---------- .. [1] Churchill, Stuart W., and Humbert H. S. Chu. "Correlating Equations for Laminar and Turbulent Free Convection from a Horizontal Cylinder." International Journal of Heat and Mass Transfer 18, no. 9 (September 1975): 1049-53. doi:10.1016/0017-9310(75)90222-7. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Ra = Pr*Gr return (0.6 + 0.387*Ra**(1/6.)/(1. + (0.559/Pr)**(9/16.))**(8/27.))**2 def Nu_horizontal_cylinder_Kuehn_Goldstein(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a horizontal cylinder according to the Kuehn-Goldstein [1]_ correlation, also shown in [2]_. Cylinder must be isothermal. .. math:: \frac{2}{Nu_D} = \ln\left[1 + \frac{2}{\left[\left\{0.518Ra_D^{0.25} \left[1 + \left(\frac{0.559}{Pr}\right)^{3/5}\right]^{-5/12} \right\}^{15} + (0.1Ra_D^{1/3})^{15}\right]^{1/15}}\right] Parameters ---------- Pr : float Prandtl number with respect to film temperature [-] Gr : float Grashof number with respect to cylinder diameter, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- [1]_ suggests this expression is valid for all cases except low-Pr fluids. [2]_ suggests no restrictions. Examples -------- >>> Nu_horizontal_cylinder_Kuehn_Goldstein(0.69, 2.63E9) 122.99323525628186 References ---------- .. [1] Kuehn, T. H., and R. J. Goldstein. "Correlating Equations for Natural Convection Heat Transfer between Horizontal Circular Cylinders." International Journal of Heat and Mass Transfer 19, no. 10 (October 1976): 1127-34. doi:10.1016/0017-9310(76)90145-9 .. [2] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr return 2./log(1 + 2./((0.518*Ra**0.25*(1. + (0.559/Pr)**0.6)**(-5/12.))**15 + (0.1*Ra**(1/3.))**15)**(1/15.)) def Nu_horizontal_cylinder_Morgan(Pr: float, Gr: float) -> float: r"""Calculates Nusselt number for natural convection around a horizontal cylinder according to the Morgan [1]_ correlations, a product of a very large review of the literature. Sufficiently common as to be shown in [2]_. Cylinder must be isothermal. .. math:: Nu_D = C Ra_D^n +----------+----------+-------+-------+ | Gr min | Gr max | C | n | +==========+==========+=======+=======+ | 10E-10 | 10E-2 | 0.675 | 0.058 | +----------+----------+-------+-------+ | 10E-2 | 10E2 | 1.02 | 0.148 | +----------+----------+-------+-------+ | 10E2 | 10E4 | 0.850 | 0.188 | +----------+----------+-------+-------+ | 10E4 | 10E7 | 0.480 | 0.250 | +----------+----------+-------+-------+ | 10E7 | 10E12 | 0.125 | 0.333 | +----------+----------+-------+-------+ Parameters ---------- Pr : float Prandtl number with respect to film temperature [-] Gr : float Grashof number with respect to cylinder diameter, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Notes ----- Most comprehensive review with a new proposed equation to date. Discontinuous among the jumps in range. Blindly runs outside if upper and lower limits without warning. Examples -------- >>> Nu_horizontal_cylinder_Morgan(0.69, 2.63E9) 151.3881997228419 References ---------- .. [1] Morgan, V.T., The Overall Convective Heat Transfer from Smooth Circular Cylinders, in Advances in Heat Transfer, eds. T.F. Irvin and J.P. Hartnett, V 11, 199-264, 1975. .. [2] Boetcher, Sandra K. S. "Natural Convection Heat Transfer From Vertical Cylinders." In Natural Convection from Circular Cylinders, 23-42. Springer, 2014. """ Ra = Pr*Gr if Ra < 1E-2: C, n = 0.675, 0.058 elif Ra < 1E2: C, n = 1.02, 0.148 elif Ra < 1E4: C, n = 0.850, 0.188 elif Ra < 1E7: C, n = 0.480, 0.250 else: # up to 1E12 C, n = 0.125, 0.333 return C*Ra**n horizontal_cylinder_correlations = { "Churchill-Chu": (Nu_horizontal_cylinder_Churchill_Chu), "Kuehn & Goldstein": (Nu_horizontal_cylinder_Kuehn_Goldstein), "Morgan": (Nu_horizontal_cylinder_Morgan) } def Nu_horizontal_cylinder_methods(Pr: float, Gr: float, check_ranges: bool=True) -> list[str]: r"""This function returns a list of correlation names for free convetion to a horizontal cylinder. Preferred functions are 'Morgan' when discontinuous results are acceptable and 'Churchill-Chu' otherwise. Parameters ---------- Pr : float Prandtl number with respect to film temperature [-] Gr : float Grashof number with respect to cylinder diameter, [-] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list[str] List of methods which can be used to calculate `Nu` with the given inputs Examples -------- >>> Nu_horizontal_cylinder_methods(0.72, 1E7)[0] 'Morgan' """ return ["Morgan", "Churchill-Chu", "Kuehn & Goldstein"] def Nu_horizontal_cylinder(Pr: float, Gr: float, Method: str | None=None) -> float: r"""This function handles choosing which horizontal cylinder free convection correlation is used. Generally this is used by a helper class, but can be used directly. Will automatically select the correlation to use if none is provided; returns None if insufficient information is provided. Preferred functions are 'Morgan' when discontinuous results are acceptable and 'Churchill-Chu' otherwise. Parameters ---------- Pr : float Prandtl number with respect to film temperature [-] Gr : float Grashof number with respect to cylinder diameter, [-] Returns ------- Nu : float Nusselt number with respect to cylinder diameter, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use, as in the dictionary horizontal_cylinder_correlations Notes ----- All fluid properties should be evaluated at the film temperature, the average between the outer surface temperature of the solid, and the fluid temperature far away from the heat transfer interface - normally the same as the temperature before any cooling or heating occurs. .. math:: T_f = (T_{\text{surface}} + T_\infty)/2 Examples -------- >>> Nu_horizontal_cylinder(0.72, 1E7) 24.864192615468973 """ if Method is None: Method2 = "Morgan" else: Method2 = Method if Method2 == "Churchill-Chu": return Nu_horizontal_cylinder_Churchill_Chu(Pr=Pr, Gr=Gr) elif Method2 == "Kuehn & Goldstein": return Nu_horizontal_cylinder_Kuehn_Goldstein(Pr=Pr, Gr=Gr) elif Method2 == "Morgan": return Nu_horizontal_cylinder_Morgan(Pr=Pr, Gr=Gr) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") #import matplotlib.pyplot as plt #import numpy as np #Pr, Gr = 0.72, 1E8 #methods = Nu_horizontal_cylinder_methods(Pr, Gr) #Grs = np.logspace(-2, 2.5, 10000) # #for method in methods: # Nus = [Nu_horizontal_cylinder(Pr=Pr, Gr=i, Method=method) for i in Grs] # plt.semilogx(Grs, Nus, label=method) #plt.legend() #plt.show() def Nu_coil_Xin_Ebadian(Pr: float, Gr: float, horizontal: bool=False) -> float: r"""Calculates Nusselt number for natural convection around a vertical or horizontal helical coil suspended in a fluid without forced convection. For horizontal cases: .. math:: Nu_D = 0.318 Ra_D^{0.293},\; 5 \times {10}^{3} < Ra < 1 \times {10}^5 For vertical cases: .. math:: Nu_D = 0.290 Ra_D^{0.293},\; 5 \times {10}^{3} < Ra < 1 \times {10}^5 Parameters ---------- Pr : float Prandtl number calculated with the film temperature - wall and temperature very far from the coil average, [-] Gr : float Grashof number calculated with the film temperature - wall and temperature very far from the coil average, and using the outer diameter of the coil [-] horizontal : bool, optional Whether the coil is horizontal or vertical, [-] Returns ------- Nu : float Nusselt number using the outer diameter of the coil and the film temperature, [-] Notes ----- This correlation is also reviewed in [2]_. Examples -------- >>> Nu_coil_Xin_Ebadian(0.7, 2E4, horizontal=False) 4.755689726250451 >>> Nu_coil_Xin_Ebadian(0.7, 2E4, horizontal=True) 5.2148597687849785 References ---------- .. [1] Xin, R. C., and M. A. Ebadian. "Natural Convection Heat Transfer from Helicoidal Pipes." Journal of Thermophysics and Heat Transfer 10, no. 2 (1996): 297-302. .. [2] Prabhanjan, Devanahalli G., Timothy J. Rennie, and G. S. Vijaya Raghavan. "Natural Convection Heat Transfer from Helical Coiled Tubes." International Journal of Thermal Sciences 43, no. 4 (April 1, 2004): 359-65. """ Ra = Pr*Gr if horizontal: return 0.318*Ra**0.293 else: return 0.290*Ra**0.293 ================================================ FILE: ht/conv_internal.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations __all__: list[str] = [ "Morimoto_Hotta", "Nu_conv_internal", "Nu_conv_internal_methods", "Nu_laminar_rectangular_Shan_London", "conv_tube_laminar_methods", "conv_tube_methods", "conv_tube_turbulent_methods", "helical_turbulent_Nu_Mori_Nakayama", "helical_turbulent_Nu_Schmidt", "helical_turbulent_Nu_Xin_Ebadian", "laminar_Q_const", "laminar_T_const", "laminar_entry_Baehr_Stephan", "laminar_entry_Seider_Tate", "laminar_entry_thermal_Hausen", "turbulent_Bhatti_Shah", "turbulent_Churchill_Zajic", "turbulent_Colburn", "turbulent_Dipprey_Sabersky", "turbulent_Dittus_Boelter", "turbulent_Drexel_McAdams", "turbulent_ESDU", "turbulent_Friend_Metzner", "turbulent_Gnielinski", "turbulent_Gnielinski_smooth_1", "turbulent_Gnielinski_smooth_2", "turbulent_Gowen_Smith", "turbulent_Kawase_De", "turbulent_Kawase_Ulbrecht", "turbulent_Martinelli", "turbulent_Nunner", "turbulent_Petukhov_Kirillov_Popov", "turbulent_Prandtl", "turbulent_Sandall", "turbulent_Sieder_Tate", "turbulent_Webb", "turbulent_entry_Hausen", "turbulent_von_Karman", ] from math import exp, log, tanh from fluids.friction import LAMINAR_TRANSITION_PIPE, Clamond ### Laminar def laminar_T_const() -> float: r"""Returns internal convection Nusselt number for laminar flows in pipe according to [1]_, [2]_ and [3]_. Wall temperature is assumed constant. This is entirely theoretically derived and reproduced experimentally. .. math:: Nu = 3.66 Returns ------- Nu : float Nusselt number, [-] Notes ----- This applies only for fully thermally and hydraulically developed and flows. References ---------- .. [1] Green, Don, and Robert Perry. Perry`s Chemical Engineers` Handbook, Eighth Edition. New York: McGraw-Hill Education, 2007. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ return 3.66 def laminar_Q_const() -> float: r"""Returns internal convection Nusselt number for laminar flows in pipe according to [1]_, [2]_, and [3]_. Heat flux is assumed constant. This is entirely theoretically derived and reproduced experimentally. .. math:: Nu = 4.354 Returns ------- Nu : float Nusselt number, [-] Notes ----- This applies only for fully thermally and hydraulically developed and flows. Many sources round to 4.36, but [3]_ does not. References ---------- .. [1] Green, Don, and Robert Perry. Perry`s Chemical Engineers` Handbook, Eighth Edition. New York: McGraw-Hill Education, 2007. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin ; New York: Springer, 2010. """ return 48/11. ### Laminar - entry region def laminar_entry_thermal_Hausen(Re: float, Pr: float, L: float, Di: float) -> float: r"""Calculates average internal convection Nusselt number for laminar flows in pipe during the thermal entry region according to [1]_ as shown in [2]_ and cited by [3]_. .. math:: Nu_D=3.66+\frac{0.0668\frac{D}{L}Re_{D}Pr}{1+0.04{(\frac{D}{L} Re_{D}Pr)}^{2/3}} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] L : float Length of pipe [m] Di : float Diameter of pipe [m] Returns ------- Nu : float Nusselt number, [-] Notes ----- If Pr >> 1, (5 is a common requirement) this equation also applies to flows with developing velocity profile. As L gets larger, this equation becomes the constant-temperature Nusselt number. Examples -------- >>> laminar_entry_thermal_Hausen(Re=100000, Pr=1.1, L=5, Di=.5) 39.01352358988535 References ---------- .. [1] Hausen, H. Darstellung des Warmeuberganges in Rohren durch verallgeminerte Potenzbeziehungen, Z. Ver deutsch. Ing Beih. Verfahrenstech., 4, 91-98, 1943 .. [2] W. M. Kays. 1953. Numerical Solutions for Laminar Flow Heat Transfer in Circular Tubes. .. [3] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Gz = Di/L*Re*Pr return 3.66 + (0.0668*Gz)/(1+0.04*(Gz)**(2/3.)) def laminar_entry_Seider_Tate(Re: float, Pr: float, L: float, Di: float, mu: float | None=None, mu_w: float | None=None) -> float: r"""Calculates average internal convection Nusselt number for laminar flows in pipe during the thermal entry region as developed in [1]_, also shown in [2]_. .. math:: Nu_D=1.86\left(\frac{D}{L}Re_DPr\right)^{1/3}\left(\frac{\mu_b} {\mu_s}\right)^{0.14} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] L : float Length of pipe [m] Di : float Diameter of pipe [m] mu : float, optional Viscosity of fluid, [Pa*s] mu_w : float, optional Viscosity of fluid at wall temperature, [Pa*s] Returns ------- Nu : float Nusselt number, [-] Notes ----- Reynolds number should be less than 10000. This should be calculated using pipe diameter. Prandlt number should be no less than air and no more than liquid metals; 0.7 < Pr < 16700 Viscosities should be the bulk and surface properties; they are optional. Outside the boundaries, this equation is provides very false results. Examples -------- >>> laminar_entry_Seider_Tate(Re=100000, Pr=1.1, L=5, Di=.5) 41.366029684589265 References ---------- .. [1] Sieder, E. N., and G. E. Tate. "Heat Transfer and Pressure Drop of Liquids in Tubes." Industrial & Engineering Chemistry 28, no. 12 (December 1, 1936): 1429-35. doi:10.1021/ie50324a027. .. [2] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ Nu = 1.86*(Di/L*Re*Pr)**(1/3.0) if mu_w is not None and mu is not None: Nu *= (mu/mu_w)**0.14 return Nu def laminar_entry_Baehr_Stephan(Re: float, Pr: float, L: float, Di: float) -> float: r"""Calculates average internal convection Nusselt number for laminar flows in pipe during the thermal and velocity entry region according to [1]_ as shown in [2]_. .. math:: Nu_D=\frac{\frac{3.657}{\tanh[2.264 Gz_D^{-1/3}+1.7Gz_D^{-2/3}]} +0.0499Gz_D\tanh(Gz_D^{-1})}{\tanh(2.432Pr^{1/6}Gz_D^{-1/6})} .. math:: Gz = \frac{D}{L}Re_D Pr Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] L : float Length of pipe [m] Di : float Diameter of pipe [m] Returns ------- Nu : float Nusselt number, [-] Notes ----- As L gets larger, this equation becomes the constant-temperature Nusselt number. Examples -------- >>> laminar_entry_Baehr_Stephan(Re=100000, Pr=1.1, L=5, Di=.5) 72.65402046550976 References ---------- .. [1] Baehr, Hans Dieter, and Karl Stephan. Heat and Mass Transfer. Springer, 2013. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Gz = Di/L*Re*Pr return (3.657/tanh(2.264*Gz**(-1/3.)+ 1.7*Gz**(-2/3.0)) + 0.0499*Gz*tanh(1./Gz))/tanh(2.432*Pr**(1/6.0)*Gz**(-1/6.0)) ### Turbulent - Equations with more complicated options def turbulent_Dittus_Boelter(Re: float, Pr: float, heating: bool=True, revised: bool=True) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [1]_, and [2]_, a reprint of [3]_. .. math:: Nu = m*Re_D^{4/5}Pr^n Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] heating : bool Indicates if the process is heating or cooling, optional revised : bool Indicates if revised coefficients should be used or not Returns ------- Nu : float Nusselt number, [-] Notes ----- The revised coefficient is m = 0.023. The original form of Dittus-Boelter has a linear coefficient of 0.0243 for heating and 0.0265 for cooling. These are sometimes rounded to 0.024 and 0.026 respectively. The default, heating, provides n = 0.4. Cooling makes n 0.3. 0.6 ≤ Pr ≤ 160 Re_{D} ≥ 10000 L/D ≥ 10 Examples -------- >>> turbulent_Dittus_Boelter(Re=1E5, Pr=1.2) 247.40036409449127 >>> turbulent_Dittus_Boelter(Re=1E5, Pr=1.2, heating=False) 242.9305927410295 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Dittus, F. W., and L. M. K. Boelter. "Heat Transfer in Automobile Radiators of the Tubular Type." International Communications in Heat and Mass Transfer 12, no. 1 (January 1985): 3-22. doi:10.1016/0735-1933(85)90003-X .. [3] Dittus, F. W., and L. M. K. Boelter, University of California Publications in Engineering, Vol. 2, No. 13, pp. 443-461, October 17, 1930. """ m = 0.023 if heating: power = 0.4 else: power = 0.3 if heating and not revised: m = 0.0243 elif not heating and not revised: m = 0.0265 else: m = 0.023 return m*Re**0.8*Pr**power def turbulent_Sieder_Tate(Re: float, Pr: float, mu: float | None=None, mu_w: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [1]_ and supposedly [2]_. .. math:: Nu = 0.027Re^{4/5}Pr^{1/3}\left(\frac{\mu}{\mu_s}\right)^{0.14} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] mu : float Viscosity of fluid, [Pa*s] mu_w : float Viscosity of fluid at wall temperature, [Pa*s] Returns ------- Nu : float Nusselt number, [-] Notes ----- A linear coefficient of 0.023 is often listed with this equation. The source of the discrepancy is not known. The equation is not present in the original paper, but is nevertheless the source usually cited for it. Examples -------- >>> turbulent_Sieder_Tate(Re=1E5, Pr=1.2) 286.9178136793052 >>> turbulent_Sieder_Tate(Re=1E5, Pr=1.2, mu=0.01, mu_w=0.067) 219.84016455766044 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Sieder, E. N., and G. E. Tate. "Heat Transfer and Pressure Drop of Liquids in Tubes." Industrial & Engineering Chemistry 28, no. 12 (December 1, 1936): 1429-35. doi:10.1021/ie50324a027. """ Nu = 0.027*Re**0.8*Pr**(1/3.) if mu_w is not None and mu is not None: Nu *= (mu/mu_w)**0.14 return Nu def turbulent_entry_Hausen(Re: float, Pr: float, Di: float, x: float) -> float: r"""Calculates internal convection Nusselt number for the entry region of a turbulent flow in pipe according to [2]_ as in [1]_. .. math:: Nu = 0.037(Re^{0.75} - 180)Pr^{0.42}[1+(x/D)^{-2/3}] Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] Di : float Inside diameter of pipe, [m] x : float Length inside of pipe for calculation, [m] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.7 < Pr ≤ 3 and 10^4 ≤ Re ≤ 5*10^6. Examples -------- >>> turbulent_entry_Hausen(Re=1E5, Pr=1.2, Di=0.154, x=0.05) 677.7228275901755 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] H. Hausen, "Neue Gleichungen fÜr die Wärmeübertragung bei freier oder erzwungener Stromung,"Allg. Warmetchn., (9): 75-79, 1959. """ return 0.037*(Re**0.75 - 180)*Pr**0.42*(1 + (x/Di)**(-2/3.)) ### Regular correlations, Re, Pr and fd only def turbulent_Colburn(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = 0.023Re^{0.8}Pr^{1/3} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5 < Pr < 3 and 10^4 < Re < 10^5. Examples -------- >>> turbulent_Colburn(Re=1E5, Pr=1.2) 244.41147091200068 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Colburn, Allan P. "A Method of Correlating Forced Convection Heat-Transfer Data and a Comparison with Fluid Friction." International Journal of Heat and Mass Transfer 7, no. 12 (December 1964): 1359-84. doi:10.1016/0017-9310(64)90125-5. """ return 0.023*Re**0.8*Pr**(1/3.) def turbulent_Drexel_McAdams(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = 0.021Re^{0.8}Pr^{0.4} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is Pr ≤ 0.7 and 10^4 ≤ Re ≤ 5*10^6. Examples -------- >>> turbulent_Drexel_McAdams(Re=1E5, Pr=0.6) 171.19055301724387 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Drexel, Rober E., and William H. Mcadams. "Heat-Transfer Coefficients for Air Flowing in Round Tubes, in Rectangular Ducts, and around Finned Cylinders," February 1, 1945. http://ntrs.nasa.gov/search.jsp?R=19930090924. """ return 0.021*Re**0.8*Pr**(0.4) def turbulent_von_Karman(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = \frac{(f/8)Re Pr}{1 + 5(f/8)^{0.5}\left[Pr-1+\ln\left(\frac{5Pr+1} {6}\right)\right]} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5 ≤ Pr ≤ 3 and 10^4 ≤ Re ≤ 10^5. Examples -------- >>> turbulent_von_Karman(Re=1E5, Pr=1.2, fd=0.0185) 255.7243541243272 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] T. von Karman, "The Analogy Between Fluid Friction and Heat Transfer," Trans. ASME, (61):705-710,1939. """ return (fd/8.0*Re*Pr/(1.0 + 5.0*(fd/8.0)**0.5 *(Pr - 1.0 + log((5.0*Pr + 1.0)/6.)))) def turbulent_Prandtl(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = \frac{(f/8)RePr}{1 + 8.7(f/8)^{0.5}(Pr-1)} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ 0.5 ≤ Pr ≤ 5 and 10^4 ≤ Re ≤ 5*10^6 Examples -------- >>> turbulent_Prandtl(Re=1E5, Pr=1.2, fd=0.0185) 256.073339689557 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] L. Prandt, Fuhrrer durch die Stomungslehre, Vieweg, Braunschweig, p. 359, 1944. """ return (fd/8.)*Re*Pr/(1.0 + 8.7*(fd/8.)**0.5*(Pr - 1.0)) def turbulent_Friend_Metzner(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = \frac{(f/8)RePr}{1.2 + 11.8(f/8)^{0.5}(Pr-1)Pr^{-1/3}} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ 50 < Pr ≤ 600 and 5*10^4 ≤ Re ≤ 5*10^6. The extreme limits on range should be considered! Examples -------- >>> turbulent_Friend_Metzner(Re=1E5, Pr=100., fd=0.0185) 1738.3356262055322 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Friend, W. L., and A. B. Metzner. “Turbulent Heat Transfer inside Tubes and the Analogy among Heat, Mass, and Momentum Transfer.” AIChE Journal 4, no. 4 (December 1, 1958): 393-402. doi:10.1002/aic.690040404. """ return (fd/8.)*Re*Pr/(1.2 + 11.8*(fd/8.)**0.5*(Pr - 1.)*Pr**(-1/3.)) def turbulent_Petukhov_Kirillov_Popov(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ and [3]_ as in [1]_. .. math:: Nu = \frac{(f/8)RePr}{C+12.7(f/8)^{1/2}(Pr^{2/3}-1)}\\ C = 1.07 + 900/Re - [0.63/(1+10Pr)] Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5 < Pr ≤ 10^6 and 4000 ≤ Re ≤ 5*10^6 Examples -------- >>> turbulent_Petukhov_Kirillov_Popov(Re=1E5, Pr=1.2, fd=0.0185) 250.11935088905105 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] B. S. Petukhov, and V. V. Kirillov, "The Problem of Heat Exchange in the Turbulent Flow of Liquids in Tubes," (Russian) Teploenergetika, (4): 63-68, 1958 .. [3] B. S. Petukhov and V. N. Popov, "Theoretical Calculation of Heat Exchange in Turbulent Flow in Tubes of an Incompressible Fluidwith Variable Physical Properties," High Temp., (111): 69-83, 1963. """ C = 1.07 + 900./Re - (0.63/(1. + 10.*Pr)) return (fd/8.)*Re*Pr/(C + 12.7*(fd/8.)**0.5*(Pr**(2/3.) - 1.)) def turbulent_Webb(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = \frac{(f/8)RePr}{1.07 + 9(f/8)^{0.5}(Pr-1)Pr^{1/4}} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5 < Pr ≤ 100 and 10^4 ≤ Re ≤ 5*10^6 Examples -------- >>> turbulent_Webb(Re=1E5, Pr=1.2, fd=0.0185) 239.10130376815872 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Webb, Dr R. L. “A Critical Evaluation of Analytical Solutions and Reynolds Analogy Equations for Turbulent Heat and Mass Transfer in Smooth Tubes.” Wärme - Und Stoffübertragung 4, no. 4 (December 1, 1971): 197-204. doi:10.1007/BF01002474. """ return (fd/8.)*Re*Pr/(1.07 + 9.*(fd/8.)**0.5*(Pr - 1.)*Pr**0.25) def turbulent_Sandall(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. .. math:: Nu = \frac{(f/8)RePr}{12.48Pr^{2/3} - 7.853Pr^{1/3} + 3.613\ln Pr + 5.8 + C}\\ C = 2.78\ln((f/8)^{0.5} Re/45) Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5< Pr ≤ 2000 and 10^4 ≤ Re ≤ 5*10^6. Examples -------- >>> turbulent_Sandall(Re=1E5, Pr=1.2, fd=0.0185) 229.0514352970239 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Sandall, O. C., O. T. Hanna, and P. R. Mazet. “A New Theoretical Formula for Turbulent Heat and Mass Transfer with Gases or Liquids in Tube Flow.” The Canadian Journal of Chemical Engineering 58, no. 4 (August 1, 1980): 443-47. doi:10.1002/cjce.5450580404. """ C = 2.78*log((fd/8.)**0.5*Re/45.) return (fd/8.)**0.5*Re*Pr/(12.48*Pr**(2/3.) - 7.853*Pr**(1/3.) + 3.613*log(Pr) + 5.8 + C) def turbulent_Gnielinski(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. This is the most recent general equation, and is strongly recommended. .. math:: Nu = \frac{(f/8)(Re-1000)Pr}{1+12.7(f/8)^{1/2}(Pr^{2/3}-1)} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5 < Pr ≤ 2000 and 2300 ≤ Re ≤ 5*10^6. Examples -------- >>> turbulent_Gnielinski(Re=1E5, Pr=1.2, fd=0.0185) 254.62682749359632 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Gnielinski, V. (1976). New Equation for Heat and Mass Transfer in Turbulent Pipe and Channel Flow, International Chemical Engineering, Vol. 16, pp. 359-368. """ return (fd/8.)*(Re - 1E3)*Pr/(1. + 12.7*(fd/8.)**0.5*(Pr**(2/3.) - 1.)) def turbulent_Gnielinski_smooth_1(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. This is a simplified case assuming smooth pipe. .. math:: Nu = 0.0214(Re^{0.8}-100)Pr^{0.4} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 0.5 < Pr ≤ 1.5 and 10^4 ≤ Re ≤ 5*10^6. Examples -------- >>> turbulent_Gnielinski_smooth_1(Re=1E5, Pr=1.2) 227.88800494373442 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Gnielinski, V. (1976). New Equation for Heat and Mass Transfer in Turbulent Pipe and Channel Flow, International Chemical Engineering, Vol. 16, pp. 359-368. """ return 0.0214*(Re**0.8 - 100.)*Pr**0.4 def turbulent_Gnielinski_smooth_2(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as in [1]_. This is a simplified case assuming smooth pipe. .. math:: Nu = 0.012(Re^{0.87}-280)Pr^{0.4} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Range according to [1]_ is 1.5 < Pr ≤ 500 and 3*10^3 ≤ Re ≤ 10^6. Examples -------- >>> turbulent_Gnielinski_smooth_2(Re=1E5, Pr=7.) 577.7692524513449 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Gnielinski, V. (1976). New Equation for Heat and Mass Transfer in Turbulent Pipe and Channel Flow, International Chemical Engineering, Vol. 16, pp. 359-368. """ return 0.012*(Re**0.87 - 280.)*Pr**0.4 def turbulent_Churchill_Zajic(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as developed in [1]_. Has yet to obtain popularity. .. math:: Nu = \left\{\left(\frac{Pr_T}{Pr}\right)\frac{1}{Nu_{di}} + \left[1-\left(\frac{Pr_T}{Pr}\right)^{2/3}\right]\frac{1}{Nu_{D\infty}} \right\}^{-1} .. math:: Nu_{di} = \frac{Re(f/8)}{1 + 145(8/f)^{-5/4}} .. math:: Nu_{D\infty} = 0.07343Re\left(\frac{Pr}{Pr_T}\right)^{1/3}(f/8)^{0.5} .. math:: Pr_T = 0.85 + \frac{0.015}{Pr} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- No restrictions on range. This is equation is developed with more theoretical work than others. Examples -------- >>> turbulent_Churchill_Zajic(Re=1E5, Pr=1.2, fd=0.0185) 260.5564907817961 References ---------- .. [1] Churchill, Stuart W., and Stefan C. Zajic. “Prediction of Fully Developed Turbulent Convection with Minimal Explicit Empiricism.” AIChE Journal 48, no. 5 (May 1, 2002): 927-40. doi:10.1002/aic.690480503. .. [2] Plawsky, Joel L. Transport Phenomena Fundamentals, Third Edition. CRC Press, 2014. """ Pr_T = 0.85 + 0.015/Pr Nu_di = Re*(fd/8.)/(1. + 145*(8./fd)**(-1.25)) Nu_dinf = 0.07343*Re*(Pr/Pr_T)**(1./3.0)*(fd/8.)**0.5 return 1./(Pr_T/Pr/Nu_di + (1. - (Pr_T/Pr)**(2/3.))/Nu_dinf) def turbulent_ESDU(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to the ESDU as shown in [1]_. .. math:: Nu = 0.0225Re^{0.795}Pr^{0.495}\exp(-0.0225\ln(Pr)^2) Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- 4000 < Re < 1E6, 0.3 < Pr < 3000 and L/D > 60. This equation has not been checked. It was developed by a commercial group. This function is a small part of a much larger series of expressions accounting for many factors. Examples -------- >>> turbulent_ESDU(Re=1E5, Pr=1.2) 232.3017143430645 References ---------- .. [1] Hewitt, G. L. Shires, T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1E. Boca Raton: CRC Press, 1994. """ return 0.0225*Re**0.795*Pr**0.495*exp(-0.0225*log(Pr)**2) ### Correlations for 'rough' turbulent pipe def turbulent_Martinelli(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. .. math:: Nu = \frac{RePr(f/8)^{0.5}}{5[Pr + \ln(1+5Pr) + 0.5\ln(Re(f/8)^{0.5}/60)]} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- No range is given for this equation. Liquid metals are probably its only applicability. Examples -------- >>> turbulent_Martinelli(Re=1E5, Pr=100., fd=0.0185) 887.1710686396347 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Martinelli, R. C. (1947). "Heat transfer to molten metals". Trans. ASME, 69, 947-959. """ return Re*Pr*(fd/8.)**0.5/5/(Pr + log(1. + 5.*Pr) + 0.5*log(Re*(fd/8.)**0.5/60.)) def turbulent_Nunner(Re: float, Pr: float, fd: float, fd_smooth: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. .. math:: Nu = \frac{RePr(f/8)}{1 + 1.5Re^{-1/8}Pr^{-1/6}[Pr(f/f_s)-1]} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] fd_smooth : float Darcy friction factor of a smooth pipe [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- Valid for Pr ≅ 0.7; bad results for Pr > 1. Examples -------- >>> turbulent_Nunner(Re=1E5, Pr=0.7, fd=0.0185, fd_smooth=0.005) 101.15841010919947 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] W. Nunner, "Warmeiibergang und Druckabfall in Rauhen Rohren," VDI-Forschungsheft 445, ser. B,(22): 5-39, 1956 """ return Re*Pr*fd/8./(1 + 1.5*Re**-0.125*Pr**(-1/6.)*(Pr*fd/fd_smooth - 1.)) def turbulent_Dipprey_Sabersky(Re: float, Pr: float, fd: float, eD: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. .. math:: Nu = \frac{RePr(f/8)}{1 + (f/8)^{0.5}[5.19Re_\epsilon^{0.2} Pr^{0.44} - 8.48]} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] eD : float Relative roughness, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- According to [1]_, the limits are: 1.2 ≤ Pr ≤ 5.94 and 1.4*10^4 ≤ Re ≤ 5E5 and 0.0024 ≤ eD ≤ 0.049. Examples -------- >>> turbulent_Dipprey_Sabersky(Re=1E5, Pr=1.2, fd=0.0185, eD=1E-3) 288.33365198566656 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Dipprey, D. F., and R. H. Sabersky. “Heat and Momentum Transfer in Smooth and Rough Tubes at Various Prandtl Numbers.” International Journal of Heat and Mass Transfer 6, no. 5 (May 1963): 329-53. doi:10.1016/0017-9310(63)90097-8 """ Re_e = Re*eD*(fd/8.)**0.5 return Re*Pr*fd/8./(1 + (fd/8.)**0.5*(5.19*Re_e**0.2*Pr**0.44 - 8.48)) def turbulent_Gowen_Smith(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. .. math:: Nu = \frac{Re Pr (f/8)^{0.5}} {4.5 + [0.155(Re(f/8)^{0.5})^{0.54} + (8/f)^{0.5}]Pr^{0.5}} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- 0.7 ≤ Pr ≤ 14.3 and 10^4 ≤ Re ≤ 5E4 and 0.0021 ≤ eD ≤ 0.095 Examples -------- >>> turbulent_Gowen_Smith(Re=1E5, Pr=1.2, fd=0.0185) 131.72530453824106 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Gowen, R. A., and J. W. Smith. “Turbulent Heat Transfer from Smooth and Rough Surfaces.” International Journal of Heat and Mass Transfer 11, no. 11 (November 1968): 1657-74. doi:10.1016/0017-9310(68)90046-X. """ return Re*Pr*(fd/8.)**0.5/(4.5 + (0.155*(Re*(fd/8.)**0.5)**0.54 + (8./fd)**0.5)*Pr**0.5) def turbulent_Kawase_Ulbrecht(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. .. math:: Nu = 0.0523RePr^{0.5}(f/4)^{0.5} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- No limits are provided. Examples -------- >>> turbulent_Kawase_Ulbrecht(Re=1E5, Pr=1.2, fd=0.0185) 389.6262247333975 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Kawase, Yoshinori, and Jaromir J. Ulbrecht. “Turbulent Heat and Mass Transfer in Dilute Polymer Solutions.” Chemical Engineering Science 37, no. 7 (1982): 1039-46. doi:10.1016/0009-2509(82)80134-6. """ return 0.0523*Re*Pr**0.5*(fd/4.)**0.5 def turbulent_Kawase_De(Re: float, Pr: float, fd: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. .. math:: Nu = 0.0471 RePr^{0.5}(f/4)^{0.5}(1.11 + 0.44Pr^{-1/3} - 0.7Pr^{-1/6}) Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- 5.1 ≤ Pr ≤ 390 and 5000 ≤ Re ≤ 5E5 and 0.0024 ≤ eD ≤ 0.165. Examples -------- >>> turbulent_Kawase_De(Re=1E5, Pr=1.2, fd=0.0185) 296.5019733271324 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] Kawase, Yoshinori, and Addie De. “Turbulent Heat and Mass Transfer in Newtonian and Dilute Polymer Solutions Flowing through Rough Pipes.” International Journal of Heat and Mass Transfer 27, no. 1 (January 1984): 140-42. doi:10.1016/0017-9310(84)90246-1. """ return 0.0471*Re*Pr**0.5*(fd/4.)**0.5*(1.11 + 0.44*Pr**(-1/3.) - 0.7*Pr**(-1/6.)) def turbulent_Bhatti_Shah(Re: float, Pr: float, fd: float, eD: float) -> float: r"""Calculates internal convection Nusselt number for turbulent flows in pipe according to [2]_ as shown in [1]_. The most widely used rough pipe turbulent correlation. .. math:: Nu_D = \frac{(f/8)Re_DPr}{1+\sqrt{f/8}(4.5Re_{\epsilon}^{0.2}Pr^{0.5}-8.48)} Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] fd : float Darcy friction factor [-] eD : float Relative roughness, [-] Returns ------- Nu : float Nusselt number, [-] Notes ----- According to [1]_, the limits are: 0.5 ≤ Pr ≤ 10 0.002 ≤ ε/D ≤ 0.05 10,000 ≤ Re_{D} Another correlation is listed in this equation, with a wider variety of validity. Examples -------- >>> turbulent_Bhatti_Shah(Re=1E5, Pr=1.2, fd=0.0185, eD=1E-3) 302.7037617414273 References ---------- .. [1] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [2] M. S. Bhatti and R. K. Shah. Turbulent and transition flow convective heat transfer in ducts. In S. Kakaç, R. K. Shah, and W. Aung, editors, Handbook of Single-Phase Convective Heat Transfer, chapter 4. Wiley-Interscience, New York, 1987. """ Re_e = Re*eD*(fd/8.)**0.5 return Re*Pr*fd/8./(1 + (fd/8.)**0.5*(4.5*Re_e**0.2*Pr**0.5 - 8.48)) conv_tube_laminar_methods = { "Laminar - constant T": (laminar_T_const, ()), "Laminar - constant Q": (laminar_Q_const, ()), "Baehr-Stephan laminar thermal/velocity entry": (laminar_entry_thermal_Hausen, ("Re", "Pr", "L", "Di")), "Hausen laminar thermal entry": (laminar_entry_Seider_Tate, ("Re", "Pr", "L", "Di")), "Seider-Tate laminar thermal entry": (laminar_entry_Baehr_Stephan, ("Re", "Pr", "L", "Di")), } conv_tube_turbulent_methods = { "Churchill-Zajic": (turbulent_Churchill_Zajic, ("Re", "Pr", "fd")), "Petukhov-Kirillov-Popov": (turbulent_Petukhov_Kirillov_Popov, ("Re", "Pr", "fd")), "Gnielinski": (turbulent_Gnielinski, ("Re", "Pr", "fd")), "Sandall": (turbulent_Sandall, ("Re", "Pr", "fd")), "Webb": (turbulent_Webb, ("Re", "Pr", "fd")), "Friend-Metzner": (turbulent_Friend_Metzner, ("Re", "Pr", "fd")), "Prandtl": (turbulent_Prandtl, ("Re", "Pr", "fd")), "von-Karman": (turbulent_von_Karman, ("Re", "Pr", "fd")), "Martinelli": (turbulent_Martinelli, ("Re", "Pr", "fd")), "Gowen-Smith": (turbulent_Gowen_Smith, ("Re", "Pr", "fd")), "Kawase-Ulbrecht": (turbulent_Kawase_Ulbrecht, ("Re", "Pr", "fd")), "Kawase-De": (turbulent_Kawase_De, ("Re", "Pr", "fd")), "Dittus-Boelter": (turbulent_Dittus_Boelter, ("Re", "Pr")), "Sieder-Tate": (turbulent_Sieder_Tate, ("Re", "Pr")), "Drexel-McAdams": (turbulent_Drexel_McAdams, ("Re", "Pr")), "Colburn": (turbulent_Colburn, ("Re", "Pr")), "ESDU": (turbulent_ESDU, ("Re", "Pr")), "Gnielinski smooth low Pr": (turbulent_Gnielinski_smooth_1, ("Re", "Pr")), "Gnielinski smooth high Pr": (turbulent_Gnielinski_smooth_2, ("Re", "Pr")), "Hausen": (turbulent_entry_Hausen, ("Re", "Pr", "Di", "x")), "Bhatti-Shah": (turbulent_Bhatti_Shah, ("Re", "Pr", "fd", "eD")), "Dipprey-Sabersky": (turbulent_Dipprey_Sabersky, ("Re", "Pr", "fd", "eD")), "Nunner": (turbulent_Nunner, ("Re", "Pr", "fd", "fd_smooth")), } conv_tube_methods = conv_tube_laminar_methods.copy() conv_tube_methods.update(conv_tube_turbulent_methods) conv_tube_methods_list = list(conv_tube_methods.keys()) def Nu_conv_internal_methods(Re: float, Pr: float, eD: float=0, Di: float | None=None, x: float | None=None, fd: None=None, check_ranges: bool=True) -> list[str]: r"""This function returns a list of correlation names for the calculation of heat transfer coefficient for internal convection inside a circular pipe. Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] eD : float, optional Relative roughness, [-] Di : float, optional Inside diameter of pipe, [m] x : float, optional Length inside of pipe for calculation, [m] fd : float, optoinal Darcy friction factor [-] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- methods : list List of methods which can be used to calculate `Nu` with the given inputs Examples -------- Turbulent example >>> Nu_conv_internal_methods(Re=1E5, Pr=.7)[0] 'Churchill-Zajic' Entry length - laminar example >>> Nu_conv_internal_methods(Re=1E2, Pr=.7, x=.01, Di=.1)[0] 'Baehr-Stephan laminar thermal/velocity entry' """ methods = [] if Re < LAMINAR_TRANSITION_PIPE or not check_ranges: # Laminar! if (Re is not None and Pr is not None and x is not None and Di is not None): methods.append("Baehr-Stephan laminar thermal/velocity entry") methods.append("Hausen laminar thermal entry") methods.append("Seider-Tate laminar thermal entry") methods.append("Laminar - constant T") methods.append("Laminar - constant Q") if Re >= LAMINAR_TRANSITION_PIPE or not check_ranges: if (Re is not None and Pr is not None and Pr < 0.03) or not check_ranges: # Liquid metals methods.append("Martinelli") if (Re is not None and Pr is not None and x is not None and Di is not None) or not check_ranges: methods.append("Hausen") if (Re is not None and Pr is not None and (eD is not None or fd is not None)) or not check_ranges: # handle correlations with roughness methods.append("Churchill-Zajic") methods.append("Petukhov-Kirillov-Popov") methods.append("Gnielinski") methods.append("Bhatti-Shah") methods.append("Dipprey-Sabersky") methods.append("Sandall") methods.append("Webb") methods.append("Friend-Metzner") methods.append("Prandtl") methods.append("von-Karman") methods.append("Gowen-Smith") methods.append("Kawase-Ulbrecht") methods.append("Kawase-De") methods.append("Nunner") if (Re is not None and Pr is not None) or not check_ranges: methods.append("Dittus-Boelter") methods.append("Sieder-Tate") methods.append("Drexel-McAdams") methods.append("Colburn") methods.append("ESDU") methods.append("Gnielinski smooth low Pr") # 1 methods.append("Gnielinski smooth high Pr") # 2 return methods def Nu_conv_internal(Re: float, Pr: float, eD: float=0.0, Di: float | None=None, x: float | None=None, fd: float | None=None, Method: str | None=None) -> float: r"""This function calculates the heat transfer coefficient for internal convection inside a circular pipe. Requires at a minimum a flow's Reynolds and Prandtl numbers `Re` and `Pr`. Relative roughness `eD` can be specified to include the enhancement of heat transfer from the added turbulence. For laminar flow, thermally and hydraulically developing flow is supported with the pipe diameter `Di` and distance `x` is provided. If no correlation's name is provided as `Method`, the most accurate applicable correlation is selected. * If laminar, `x` and `Di` provided: 'Baehr-Stephan laminar thermal/velocity entry' * Otherwise if laminar, no entry information provided: 'Laminar - constant T' (Nu = 3.66) * If turbulent and `Pr` < 0.03: 'Martinelli' * If turbulent, `x` and `Di` provided: 'Hausen' * Otherwise if turbulent: 'Churchill-Zajic' Parameters ---------- Re : float Reynolds number, [-] Pr : float Prandtl number, [-] eD : float, optional Relative roughness, [-] Di : float, optional Inside diameter of pipe, [m] x : float, optional Length inside of pipe for calculation, [m] fd : float, optoinal Darcy friction factor [-] Returns ------- Nu : float Nusselt number, [-] Other Parameters ---------------- Method : string, optional A string of the function name to use, as in the dictionary vertical_cylinder_correlations Examples -------- Turbulent example >>> Nu_conv_internal(Re=1E5, Pr=.7) 183.71057902604906 Entry length - laminar example >>> Nu_conv_internal(Re=1E2, Pr=.7, x=.01, Di=.1) 14.91799128769779 """ if Method is None: Method2 = Nu_conv_internal_methods(Re=Re, Pr=Pr, eD=eD, Di=Di, x=x, fd=fd, check_ranges=True)[0] else: Method2 = Method L = x if eD is not None and fd is None: fd = Clamond(Re=Re, eD=eD) if Method2 == "Laminar - constant T": return laminar_T_const() elif Method2 == "Laminar - constant Q": return laminar_Q_const() elif Method2 == "Baehr-Stephan laminar thermal/velocity entry": return laminar_entry_thermal_Hausen(Re=Re, Pr=Pr, L=L, Di=Di) elif Method2 == "Hausen laminar thermal entry": return laminar_entry_Seider_Tate(Re=Re, Pr=Pr, L=L, Di=Di) elif Method2 == "Seider-Tate laminar thermal entry": return laminar_entry_Baehr_Stephan(Re=Re, Pr=Pr, L=L, Di=Di) elif Method2 == "Churchill-Zajic": return turbulent_Churchill_Zajic(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Petukhov-Kirillov-Popov": return turbulent_Petukhov_Kirillov_Popov(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Gnielinski": return turbulent_Gnielinski(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Sandall": return turbulent_Sandall(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Webb": return turbulent_Webb(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Friend-Metzner": return turbulent_Friend_Metzner(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Prandtl": return turbulent_Prandtl(Re=Re, Pr=Pr, fd=fd) elif Method2 == "von-Karman": return turbulent_von_Karman(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Martinelli": return turbulent_Martinelli(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Gowen-Smith": return turbulent_Gowen_Smith(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Kawase-Ulbrecht": return turbulent_Kawase_Ulbrecht(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Kawase-De": return turbulent_Kawase_De(Re=Re, Pr=Pr, fd=fd) elif Method2 == "Dittus-Boelter": return turbulent_Dittus_Boelter(Re=Re, Pr=Pr) elif Method2 == "Sieder-Tate": return turbulent_Sieder_Tate(Re=Re, Pr=Pr) elif Method2 == "Drexel-McAdams": return turbulent_Drexel_McAdams(Re=Re, Pr=Pr) elif Method2 == "Colburn": return turbulent_Colburn(Re=Re, Pr=Pr) elif Method2 == "ESDU": return turbulent_ESDU(Re=Re, Pr=Pr) elif Method2 == "Gnielinski smooth low Pr": return turbulent_Gnielinski_smooth_1(Re=Re, Pr=Pr) elif Method2 == "Gnielinski smooth high Pr": return turbulent_Gnielinski_smooth_2(Re=Re, Pr=Pr) elif Method2 == "Hausen": return turbulent_entry_Hausen(Re=Re, Pr=Pr, Di=Di, x=x) elif Method2 == "Bhatti-Shah": return turbulent_Bhatti_Shah(Re=Re, Pr=Pr, fd=fd, eD=eD) elif Method2 == "Dipprey-Sabersky": return turbulent_Dipprey_Sabersky(Re=Re, Pr=Pr, fd=fd, eD=eD) elif Method2 == "Nunner": fd_smooth = Clamond(Re, eD=0.0) return turbulent_Nunner(Re=Re, Pr=Pr, fd=fd, fd_smooth=fd_smooth) else: raise ValueError("Correlation name not recognized; see the " "documentation for the available options.") ## Comparison #import matplotlib.pyplot as plt #import numpy as np #from fluids.friction import friction_factor #Pr = 0.3 #Di = 0.0254*4 #roughness = .00015 # #methods = Nu_conv_internal_methods(Re=10000, Pr=Pr, fd=1.8E-5, x=2.5, Di=0.5) # #plt.figure() #Res = np.logspace(4, 6, 300) #for way in methods: # Nus = [] # for Re in Res: # fd = friction_factor(Re=Re, eD=roughness/Di) # Nus.append(Nu_conv_internal(Re=Re, Pr=Pr, fd=fd, x=2.5, Di=0.5, Method=way)) # plt.plot(Res, Nus, label=way) #plt.xlabel(r'Res') #plt.ylabel('Nus') #plt.legend() # #plt.show() ### Spiral heat exchangers def Morimoto_Hotta(Re: float, Pr: float, Dh: float, Rm: float) -> float: r"""Calculates Nusselt number for flow inside a spiral heat exchanger of spiral mean diameter `Rm` and hydraulic diameter `Dh` according to [1]_, also as shown in [2]_ and [3]_. .. math:: Nu = 0.0239\left(1 + 5.54\frac{D_h}{R_m}\right)Re^{0.806}Pr^{0.268} .. math:: D_h = \frac{2HS}{H+S} .. math:: R_m = \frac{R_{min} + R_{max}}{2} Parameters ---------- Re : float Reynolds number with bulk properties, [-] Pr : float Prandtl number with bulk properties [-] Dh : float Average hydraulic diameter, [m] Rm : float Average spiral radius, [m] Returns ------- Nu : float Nusselt number with respect to `Dh`, [-] Notes ----- [1]_ is in Japanese. Examples -------- >>> Morimoto_Hotta(1E5, 5.7, .05, .5) 634.4879473869859 References ---------- .. [1] Morimoto, Eiji, and Kazuyuki Hotta. "Study of Geometric Structure and Heat Transfer Characteristics of Spiral Plate Heat Exchanger." Transactions of the Japan Society of Mechanical Engineers Series B 52, no. 474 (1986): 926-33. doi:10.1299/kikaib.52.926. .. [2] Bidabadi, M. and Sadaghiani, A. and Azad, A. "Spiral heat exchanger optimization using genetic algorithm." Transaction on Mechanical Engineering, International Journal of Science and Technology, vol. 20, no. 5 (2013): 1445-1454. http://www.scientiairanica.com/en/ManuscriptDetail?mid=47. .. [3] Turgut, Oğuz Emrah, and Mustafa Turhan Çoban. "Thermal Design of Spiral Heat Exchangers and Heat Pipes through Global Best Algorithm." Heat and Mass Transfer, July 7, 2016, 1-18. doi:10.1007/s00231-016-1861-y. """ return 0.0239*(1. + 5.54*Dh/Rm)*Re**0.806*Pr**0.268 ### Helical/curved coils def helical_turbulent_Nu_Mori_Nakayama(Re: float, Pr: float, Di: float, Dc: float) -> float: r"""Calculates Nusselt number for a fluid flowing inside a curved pipe such as a helical coil under turbulent conditions, using the method of Mori and Nakayama [1]_, also shown in [2]_ and [3]_. For :math:`Pr < 1`: .. math:: Nu = \frac{Pr}{26.2(Pr^{2/3}-0.074)}Re^{0.8}\left(\frac{D_i}{D_c} \right)^{0.1}\left[1 + \frac{0.098}{\left[Re\left(\frac{D_i}{D_c} \right)^2\right]^{0.2}}\right] For :math:`Pr \ge 1`: .. math:: Nu = \frac{Pr^{0.4}}{41}Re^{5/6}\left(\frac{D_i}{D_c}\right)^{1/12} \left[1 + \frac{0.061}{\left[Re\left(\frac{D_i}{D_c}\right)^{2.5} \right]^{1/6}}\right] Parameters ---------- Re : float Reynolds number with `D=Di`, [-] Pr : float Prandtl number with bulk properties [-] Di : float Inner diameter of the coil, [m] Dc : float Diameter of the helix/coil measured from the center of the tube on one side to the center of the tube on the other side, [m] Returns ------- Nu : float Nusselt number with respect to `Di`, [-] Notes ----- At very low curvatures, the predicted heat transfer coefficient grows unbounded. Applicable for :math:`Re\left(\frac{D_i}{D_c}\right)^2 > 0.1` Examples -------- >>> helical_turbulent_Nu_Mori_Nakayama(2E5, 0.7, 0.01, .2) 496.2522480663327 References ---------- .. [1] Mori, Yasuo, and Wataru Nakayama. "Study on Forced Convective Heat Transfer in Curved Pipes." International Journal of Heat and Mass Transfer 10, no. 5 (May 1, 1967): 681-95. doi:10.1016/0017-9310(67)90113-5. .. [2] El-Genk, Mohamed S., and Timothy M. Schriener. "A Review and Correlations for Convection Heat Transfer and Pressure Losses in Toroidal and Helically Coiled Tubes." Heat Transfer Engineering 0, no. 0 (June 7, 2016): 1-28. doi:10.1080/01457632.2016.1194693. .. [3] Hardik, B. K., P. K. Baburajan, and S. V. Prabhu. "Local Heat Transfer Coefficient in Helical Coils with Single Phase Flow." International Journal of Heat and Mass Transfer 89 (October 2015): 522-38. doi:10.1016/j.ijheatmasstransfer.2015.05.069. """ D_ratio = Di/Dc if Pr < 1: term1 = Pr/(26.2*(Pr**(2/3.) - 0.074))*Re**0.8*D_ratio**0.1 term2 = 1. + 0.098*(Re*D_ratio*D_ratio)**-0.2 else: term1 = Pr**0.4/41.*Re**(5/6.)*(Di/Dc)**(1/12.) term2 = 1. + 0.061/(Re*(Di/Dc)**2.5)**(1/6.) return term1*term2 def helical_turbulent_Nu_Schmidt(Re: float, Pr: float, Di: float, Dc: float) -> float: r"""Calculates Nusselt number for a fluid flowing inside a curved pipe such as a helical coil under turbulent conditions, using the method of Schmidt [1]_, also shown in [2]_, [3]_, and [4]_. For :math:`Re_{crit} < Re < 2.2\times 10 ^4`: .. math:: Nu = 0.023\left[1 + 14.8\left(1 + \frac{D_i}{D_c}\right)\left( \frac{D_i}{D_c}\right)^{1/3}\right]Re^{0.8-0.22\left(\frac{D_i}{D_c} \right)^{0.1}}Pr^{1/3} For :math:`2.2\times 10^4 < Re < 1.5\times 10^5`: .. math:: Nu = 0.023\left[1 + 3.6\left(1 - \frac{D_i}{D_c}\right)\left(\frac{D_i} {D_c}\right)^{0.8}\right]Re^{0.8}Pr^{1/3} Parameters ---------- Re : float Reynolds number with `D=Di`, [-] Pr : float Prandtl number with bulk properties [-] Di : float Inner diameter of the coil, [m] Dc : float Diameter of the helix/coil measured from the center of the tube on one side to the center of the tube on the other side, [m] Returns ------- Nu : float Nusselt number with respect to `Di`, [-] Notes ----- For very low curvatures, reasonable results are returned by both cases of Reynolds numbers. Examples -------- >>> helical_turbulent_Nu_Schmidt(2E5, 0.7, 0.01, .2) 466.2569996832083 References ---------- .. [1] Schmidt, Eckehard F. "Wärmeübergang Und Druckverlust in Rohrschlangen." Chemie Ingenieur Technik 39, no. 13 (July 10, 1967): 781-89. doi:10.1002/cite.330391302. .. [2] El-Genk, Mohamed S., and Timothy M. Schriener. "A Review and Correlations for Convection Heat Transfer and Pressure Losses in Toroidal and Helically Coiled Tubes." Heat Transfer Engineering 0, no. 0 (June 7, 2016): 1-28. doi:10.1080/01457632.2016.1194693. .. [3] Hardik, B. K., P. K. Baburajan, and S. V. Prabhu. "Local Heat Transfer Coefficient in Helical Coils with Single Phase Flow." International Journal of Heat and Mass Transfer 89 (October 2015): 522-38. doi:10.1016/j.ijheatmasstransfer.2015.05.069. .. [4] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ D_ratio = Di/Dc if Re <= 2.2E4: term = Re**(0.8 - 0.22*D_ratio**0.1)*Pr**(1/3.) return 0.023*(1. + 14.8*(1. + D_ratio)*D_ratio**(1/3.))*term else: return 0.023*(1. + 3.6*(1. - D_ratio)*D_ratio**0.8)*Re**0.8*Pr**(1/3.) def helical_turbulent_Nu_Xin_Ebadian(Re: float, Pr: float, Di: float, Dc: float) -> float: r"""Calculates Nusselt number for a fluid flowing inside a curved pipe such as a helical coil under turbulent conditions, using the method of Xin and Ebadian [1]_, also shown in [2]_ and [3]_. For :math:`Re_{crit} < Re < 1\times 10^5`: .. math:: Nu = 0.00619Re^{0.92} Pr^{0.4}\left[1 + 3.455\left(\frac{D_i}{D_c} \right)\right] Parameters ---------- Re : float Reynolds number with `D=Di`, [-] Pr : float Prandtl number with bulk properties [-] Di : float Inner diameter of the coil, [m] Dc : float Diameter of the helix/coil measured from the center of the tube on one side to the center of the tube on the other side, [m] Returns ------- Nu : float Nusselt number with respect to `Di`, [-] Notes ----- For very low curvatures, reasonable results are returned. The correlation was developed with data in the range of :math:`0.7 < Pr < 5; 0.0267 < \frac{D_i}{D_c} < 0.0884`. Examples -------- >>> helical_turbulent_Nu_Xin_Ebadian(2E5, 0.7, 0.01, .2) 474.11413424344755 References ---------- .. [1] Xin, R. C., and M. A. Ebadian. "The Effects of Prandtl Numbers on Local and Average Convective Heat Transfer Characteristics in Helical Pipes." Journal of Heat Transfer 119, no. 3 (August 1, 1997): 467-73. doi:10.1115/1.2824120. .. [2] El-Genk, Mohamed S., and Timothy M. Schriener. "A Review and Correlations for Convection Heat Transfer and Pressure Losses in Toroidal and Helically Coiled Tubes." Heat Transfer Engineering 0, no. 0 (June 7, 2016): 1-28. doi:10.1080/01457632.2016.1194693. .. [3] Hardik, B. K., P. K. Baburajan, and S. V. Prabhu. "Local Heat Transfer Coefficient in Helical Coils with Single Phase Flow." International Journal of Heat and Mass Transfer 89 (October 2015): 522-38. doi:10.1016/j.ijheatmasstransfer.2015.05.069. """ return 0.00619*Re**0.92*Pr**0.4*(1. + 3.455*Di/Dc) ### Rectangular Channels def Nu_laminar_rectangular_Shan_London(a_r: float) -> float: r"""Calculates internal convection Nusselt number for laminar flows in a rectangular pipe of varying aspect ratio, as developed in [1]_. This model is derived assuming a constant wall heat flux from all sides. This is entirely theoretically derived and reproduced experimentally. .. math:: Nu_{lam} = 8.235\left(1 - 2.0421\alpha + 3.0853\alpha^2 - 2.4765\alpha^3 + 1.0578\alpha^4 - 0.1861\alpha^5\right) Parameters ---------- a_r : float The aspect ratio of the channel, from 0 to 1 [-] Returns ------- Nu : float Nusselt number of flow in a rectangular channel, [-] Notes ----- At an aspect ratio of 1 (square channel), the Nusselt number converges to 3.610224. The authors of [1]_ also published [2]_, which tabulates in their table 42 some less precise results that are used to check this function. Examples -------- >>> Nu_laminar_rectangular_Shan_London(.7) 3.751762675455 References ---------- .. [1] Shah, R. K, and Alexander Louis London. Supplement 1: Laminar Flow Forced Convection in Ducts: A Source Book for Compact Heat Exchanger Analytical Data. New York: Academic Press, 1978. .. [2] Shah, Ramesh K., and A. L. London. "Laminar Flow Forced Convection Heat Transfer and Flow Friction in Straight and Curved Ducts - A Summary of Analytical Solutions." STANFORD UNIV CA DEPT OF MECHANICAL ENGINEERING, STANFORD UNIV CA DEPT OF MECHANICAL ENGINEERING, November 1971. http://www.dtic.mil/docs/citations/AD0736260. """ return 8.235*(1 - 2.0421*a_r + 3.0853*a_r**2 - 2.4765*a_r**3 + 1.0578*a_r**4 - 0.1861*a_r**5) ================================================ FILE: ht/conv_jacket.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import log, pi from fluids.constants import g from fluids.friction import friction_factor __all__: list[str] = ["Lehrer", "Stein_Schmidt"] def Lehrer(m: float, Dtank: float, Djacket: float, H: float, Dinlet: float, rho: float, Cp: float, k: float, mu: float, muw: float | None=None, isobaric_expansion: float | None=None, dT: float | None=None, inlettype: str="tangential", inletlocation: str="auto") -> float: r"""Calculates average heat transfer coefficient for a jacket around a vessel according to [1]_ as described in [2]_. .. math:: Nu_{S,L} = \left[\frac{0.03Re_S^{0.75}Pr}{1 + \frac{1.74(Pr-1)} {Re_S^{0.125}}}\right]\left(\frac{\mu}{\mu_w}\right)^{0.14} .. math:: d_g = \left(\frac{8}{3}\right)^{0.5}\delta .. math:: v_h = (v_Sv_{inlet})^{0.5} + v_A .. math:: v_{inlet} = \frac{Q}{\frac{\pi}{4}d_{inlet}^2} .. math:: v_s = \frac{Q}{\frac{\pi}{4}(D_{jacket}^2 - D_{tank}^2)} For Radial inlets: .. math:: v_A = 0.5(2g H \beta\delta \Delta T)^{0.5} For Tangential inlets: .. math:: v_A = 0 Parameters ---------- m : float Mass flow rate of fluid, [kg/s] Dtank : float Outer diameter of tank or vessel surrounded by jacket, [m] Djacket : float Inner diameter of jacket surrounding a vessel or tank, [m] H : float Height of the vessel or tank, [m] Dinlet : float Inner diameter of inlet into the jacket, [m] rho : float Density of the fluid at Tm [kg/m^3] Cp : float Heat capacity of fluid at Tm [J/kg/K] k : float Thermal conductivity of fluid at Tm [W/m/K] mu : float Viscosity of fluid at Tm [Pa*s] muw : float, optional Viscosity of fluid at Tw [Pa*s] isobaric_expansion : float, optional Constant pressure expansivity of a fluid, [m^3/mol/K] dT : float, optional Temperature difference of fluid in jacket, [K] inlettype : str, optional Either 'tangential' or 'radial' inletlocation : str, optional Either 'top' or 'bottom' or 'auto' Returns ------- h : float Average heat transfer coefficient inside the jacket [W/m^2/K] Notes ----- If the fluid is heated and enters from the bottom, natural convection assists the heat transfer and the Grashof term is added; if it were to enter from the top, it would be subtracted. The situation is reversed if entry is from the top. Examples -------- Example as in [2]_, matches completely. >>> Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., ... rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6) 2922.128124761829 Examples similar to in [2]_ but covering the other case: >>> Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., ... rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, ... inlettype='radial', isobaric_expansion=0.000303) 3269.4389632666557 References ---------- .. [1] Lehrer, Isaac H. "Jacket-Side Nusselt Number." Industrial & Engineering Chemistry Process Design and Development 9, no. 4 (October 1, 1970): 553-58. doi:10.1021/i260036a010. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ delta = (Djacket-Dtank)/2. Q = m/rho Pr = Cp*mu/k vs = Q/H/delta vo = Q/(pi/4*Dinlet**2) if dT is not None and isobaric_expansion is not None and inlettype == "radial" and inletlocation is not None: if dT > 0: # Heating jacket fluid if inletlocation in ("auto", "bottom"): va = 0.5*(2*g*H*isobaric_expansion*abs(dT))**0.5 else: va = -0.5*(2*g*H*isobaric_expansion*abs(dT))**0.5 else: # cooling fluid if inletlocation in ("auto", "top"): va = 0.5*(2*g*H*isobaric_expansion*abs(dT))**0.5 else: va = -0.5*(2*g*H*isobaric_expansion*abs(dT))**0.5 else: va = 0 vh = (vs*vo)**0.5 + va dg = (8/3.)**0.5*delta Res = vh*dg*rho/mu if muw is not None: NuSL = (0.03*Res**0.75*Pr)/(1 + 1.74*(Pr-1)/Res**0.125)*(mu/muw)**0.14 else: NuSL = (0.03*Res**0.75*Pr)/(1 + 1.74*(Pr-1)/Res**0.125) return NuSL*k/dg def Stein_Schmidt(m: float, Dtank: float, Djacket: float, H: float, Dinlet: float, rho: float, Cp: float, k: float, mu: float, muw: float | None=None, rhow: float | None=None, inlettype: str="tangential", inletlocation: str="auto", roughness: float=0.0) -> float: r"""Calculates average heat transfer coefficient for a jacket around a vessel according to [1]_ as described in [2]_. .. math:: l_{ch} = \left[\left(\frac{\pi}{2}\right)^2 D_{tank}^2+H^2\right]^{0.5} .. math:: d_{ch} = 2\delta .. math:: Re_j = \frac{v_{ch}d_{ch}\rho}{\mu} .. math:: Gr_J = \frac{g\rho(\rho-\rho_w)d_{ch}^3}{\mu^2} .. math:: Re_{J,eq} = \left[Re_J^2\pm \left(\frac{|Gr_J|\frac{H}{d_{ch}}}{50} \right)\right]^{0.5} .. math:: Nu_J = (Nu_A^3 + Nu_B^3 + Nu_C^3 + Nu_D^3)^{1/3}\left(\frac{\mu} {\mu_w}\right)^{0.14} .. math:: Nu_J = \frac{h d_{ch}}{k} .. math:: Nu_A = 3.66 .. math:: Nu_B = 1.62 Pr^{1/3}Re_{J,eq}^{1/3}\left(\frac{d_{ch}}{l_{ch}} \right)^{1/3} .. math:: Nu_C = 0.664Pr^{1/3}(Re_{J,eq}\frac{d_{ch}}{l_{ch}})^{0.5} .. math:: \text{if } Re_{J,eq} < 2300: Nu_D = 0 .. math:: Nu_D = 0.0115Pr^{1/3}Re_{J,eq}^{0.9}\left(1 - \left(\frac{2300} {Re_{J,eq}}\right)^{2.5}\right)\left(1 + \left(\frac{d_{ch}}{l_{ch}} \right)^{2/3}\right) For Radial inlets: .. math:: v_{ch} = v_{Mit}\left(\frac{\ln\frac{b_{Mit}}{b_{Ein}}}{1 - \frac{b_{Ein}}{b_{Mit}}}\right) .. math:: b_{Ein} = \frac{\pi}{8}\frac{D_{inlet}^2}{\delta} .. math:: b_{Mit} = \frac{\pi}{2}D_{tank}\sqrt{1 + \frac{\pi^2}{4}\frac {D_{tank}^2}{H^2}} .. math:: v_{Mit} = \frac{Q}{2\delta b_{Mit}} For Tangential inlets: .. math:: v_{ch} = (v_x^2 + v_z^2)^{0.5} .. math:: v_x = v_{inlet}\left(\frac{\ln[1 + \frac{f_d D_{tank}H}{D_{inlet}^2} \frac{v_x(0)}{v_{inlet}}]}{\frac{f_d D_{tank}H}{D_{inlet}^2}}\right) .. math:: v_x(0) = K_3 + (K_3^2 + K_4)^{0.5} .. math:: K_3 = \frac{v_{inlet}}{4} -\frac{D_{inlet}^2v_{inlet}}{4f_d D_{tank}H} .. math:: K_4 = \frac{D_{inlet}^2v_{inlet}^2}{2f_d D_{tank} H} .. math:: v_z = \frac{Q}{\pi D_{tank}\delta} .. math:: v_{inlet} = \frac{Q}{\frac{\pi}{4}D_{inlet}^2} Parameters ---------- m : float Mass flow rate of fluid, [kg/m^3] Dtank : float Outer diameter of tank or vessel surrounded by jacket, [m] Djacket : float Inner diameter of jacket surrounding a vessel or tank, [m] H : float Height of the vessel or tank, [m] Dinlet : float Inner diameter of inlet into the jacket, [m] rho : float Density of the fluid at Tm [kg/m^3] Cp : float Heat capacity of fluid at Tm [J/kg/K] k : float Thermal conductivity of fluid at Tm [W/m/K] mu : float Viscosity of fluid at Tm [Pa*s] muw : float, optional Viscosity of fluid at Tw [Pa*s] rhow : float, optional Density of the fluid at Tw [kg/m^3] inlettype : str, optional Either 'tangential' or 'radial' inletlocation : str, optional Either 'top' or 'bottom' or 'auto' roughness : float, optional Roughness of the tank walls [m] Returns ------- h : float Average transfer coefficient inside the jacket [W/m^2/K] Notes ----- [1]_ is in German and has not been reviewed. Multiple other formulations are considered in [1]_. If the fluid is heated and enters from the bottom, natural convection assists the heat transfer and the Grashof term is added; if it were to enter from the top, it would be subtracted. The situation is reversed if entry is from the top. Examples -------- Example as in [2]_, matches in all but friction factor: >>> Stein_Schmidt(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, ... rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, rhow=971.8) 5695.2041698088615 References ---------- .. [1] Stein, Prof Dr-Ing Werner Alexander, and Dipl-Ing (FH) Wolfgang Schmidt. "Wärmeübergang auf der Wärmeträgerseite eines Rührbehälters mit einem einfachen Mantel." Forschung im Ingenieurwesen 59, no. 5 (May 1993): 73-90. doi:10.1007/BF02561203. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ delta = (Djacket-Dtank)/2. Q = m/rho Pr = Cp*mu/k lch = (pi**2/4*Dtank**2 + H**2)**0.5 dch = 2*delta if inlettype == "radial": bEin = pi/8*Dinlet**2/delta bMit = pi/2*Dtank*(1 + pi**2/4*Dtank**2/H**2)**0.5 vMit = Q/(2*delta*bMit) vch = vMit*log(bMit/bEin)/(1 - bEin/bMit) ReJ = vch*dch*rho/mu elif inlettype == "tangential": f = friction_factor(1E5, roughness/dch) for run in range(5): vinlet = Q/(pi/4*Dinlet**2) vz = Q/(pi*Dtank*delta) K4 = Dinlet**2*vinlet**2/(2*f*Dtank*H) K3 = vinlet/4. - Dinlet**2*vinlet/(4*f*Dtank*H) vx0 = K3 + (K3**2 + K4)**0.5 vx = vinlet*log(1 + f*Dtank*H/Dinlet**2*vx0/vinlet)/(f*Dtank*H/Dinlet**2) vch = (vx**2 + vz**2)**0.5 ReJ = vch*dch*rho/mu f = friction_factor(ReJ, roughness/dch) if inletlocation and rhow: GrJ = g*rho*(rho-rhow)*dch**3/mu**2 if rhow < rho: # Heating jacket fluid if inletlocation in ("auto", "bottom"): ReJeq = (ReJ**2 + GrJ*H/dch/50.)**0.5 else: ReJeq = (ReJ**2 - GrJ*H/dch/50.)**0.5 else: # Cooling jacket fluid if inletlocation in ("auto", "top"): ReJeq = (ReJ**2 + GrJ*H/dch/50.)**0.5 else: ReJeq = (ReJ**2 - GrJ*H/dch/50.)**0.5 else: ReJeq = (ReJ**2)**0.5 NuA = 3.66 NuB = 1.62*Pr**(1/3.)*ReJeq**(1/3.)*(dch/lch)**(1/3.) NuC = 0.664*Pr**(1/3.)*(ReJeq*dch/lch)**0.5 if ReJeq < 2300: NuD = 0 else: NuD = 0.0115*Pr**(1/3.)*ReJeq**0.9*(1 - (2300./ReJeq)**2.5)*(1 + (dch/lch)**(2/3.)) if muw: NuJ = (NuA**3 + NuB**3 + NuC**3 + NuD**3)**(1/3.)*(mu/muw)**0.14 else: NuJ = (NuA**3 + NuB**3 + NuC**3 + NuD**3)**(1/3.) return NuJ*k/dch ================================================ FILE: ht/conv_packed_bed.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations __all__: list[str] = [ "Nu_Achenbach", "Nu_KTA", "Nu_Wakao_Kagei", "Nu_packed_bed_Gnielinski", ] def Nu_packed_bed_Gnielinski(dp: float, voidage: float, vs: float, rho: float, mu: float, Pr: float, fa: float | None=None) -> float: r"""Calculates Nusselt number of a fluid passing over a bed of particles using a correlation shown in [3]_ and cited as from [1]_ and [2]_. Likely the best available model as the author of [1]_ is the same as [2]_ and [3]_. .. math:: Nu = f_a Nu_{sphere} .. math:: Nu_{sphere} = 2 + \sqrt{Nu_{m,lam}^2 + Nu_{m,turb}^2} .. math:: Nu_{m,lam} = 0.664Re^{0.5} Pr^{1/3} .. math:: Nu_{m,turb} = \frac{0.037Re^{0.8} Pr}{1 + 2.443Re^{-0.1}(Pr^{2/3} -1)} .. math:: Re = \frac{\rho v_s d_p}{\mu \epsilon} Parameters ---------- dp : float Equivalent spherical particle diameter of packing [m] voidage : float Void fraction of bed packing [-] vs : float Superficial velocity of the fluid [m/s] rho : float Density of the fluid [kg/m^3] mu : float Viscosity of the fluid, [Pa*s] Pr : float Prandtl number of the fluid [] fa : float, optional Fator increasing heat transfer [] Returns ------- Nu : float Nusselt number for heat transfer to the packed bed [-] Notes ----- `fa` is a factor relating how much more heat transfer happens than would normally, around one sphere. For spheres of the same size, :math:`f_a = 1 + 1.5(1-\epsilon)`. For cylinders with l/d ratio of 0.24 < l/d < 1.2 use fa = 1.6. For cubes, use fa = 1.6 For Raschig rings, use `fa` = 2.1 For Berl saddles, use `fa` = 2.3. fa is calculated with the relationship for spheres if not provided. Confirmed with experimental data for a range of :math:`1E-1 < Re <1,000` and :math:`0.4 < Pr < 1000` for spheres. Limits are smaller for other shapes. Examples -------- >>> Nu_packed_bed_Gnielinski(dp=8E-4, voidage=0.4, vs=1, rho=1E3, mu=1E-3, Pr=0.7) 61.37823202546954 References ---------- .. [1] Gnielinski, V. (1981) "Equations for the calculation of heat and mass transfer during flow through stationary spherical packings at moderate and high Peclet numbers". International Chemical Engineering 21 (3): 378-383 .. [2] Gnielinski, V. (1982) "Berechnung des Warmeund Stoffaustauschs in durchstomten ruhenden Schuttungen". Verfahrenstechnik 16(1): 36-39 .. [3] Gnielinski, V. in G esellschaft, V. D. I., ed. VDI Heat Atlas. 2nd ed. 2010 edition. Berlin; New York: Springer, 2010. """ Re = rho*vs*dp/mu/voidage Nu_lam = 0.664*Re**0.5*Pr**(1/3.) Nu_turb = 0.037*Re**0.8*Pr/(1 + 2.443*Re**-0.1*(Pr**(2/3.)-1)) Nu_sphere = 2 + (Nu_lam**2 + Nu_turb**2)**0.5 if fa is None: fa = 1.0 + 1.5*(1.0 - voidage) return fa*Nu_sphere def Nu_Wakao_Kagei(Re: float, Pr: float) -> float: r"""Calculates Nusselt number of a fluid passing over a bed of particles using a correlation shown in [1]_ and also cited in the review of [2]_. Relatively rough, as it has no dependence on voidage. .. math:: Nu = 2 + 1.1Pr^{1/3}Re^{0.6} Parameters ---------- Re : float Reynolds number with pebble diameter as characteristic dimension, [-] Pr : float Prandtl number of the fluid [] Returns ------- Nu : float Nusselt number for heat transfer to the packed bed [-] Notes ----- Fit for Re from 3 to 3000; claimed reasonableness of fit to to 1E6. Examples -------- >>> Nu_Wakao_Kagei(2000, 0.7) 95.40641328041248 References ---------- .. [1] Wakao, Noriaki, and Seiichirō Kagei. Heat and Mass Transfer in Packed Beds. Taylor & Francis, 1982. .. [2] Abdulmohsin, Rahman S., and Muthanna H. Al-Dahhan. "Characteristics of Convective Heat Transport in a Packed Pebble-Bed Reactor." Nuclear Engineering and Design 284 (April 1, 2015): 143-52. doi:10.1016/j.nucengdes.2014.11.041. """ return 2 + 1.1*Pr**(1/3.)*Re**0.6 def Nu_Achenbach(Re: float, Pr: float, voidage: float) -> float: r"""Calculates Nusselt number of a fluid passing over a bed of particles using a correlation shown in [1]_ and also cited in the review of [2]_. .. math:: Nu = [(1.18Re^{0.58})^4 + (0.23\left(\frac{Re}{1-\epsilon} \right)^{0.75})^4]^{0.25} Parameters ---------- Re : float Reynolds number with pebble diameter as characteristic dimension, [-] Pr : float Prandtl number of the fluid [] voidage : float Void fraction of bed packing [-] Returns ------- Nu : float Nusselt number for heat transfer to the packed bed [-] Notes ----- Claimed value for Re/ε < 7.7E5 Developed with tests performed in a wind tunnel at conditions up to 30 bar. Examples -------- >>> Nu_Achenbach(2000, 0.7, 0.4) 117.70343608599121 References ---------- .. [1] Achenbach, E. "Heat and Flow Characteristics of Packed Beds." Experimental Thermal and Fluid Science 10, no. 1 (January 1, 1995): 17-27. doi:10.1016/0894-1777(94)00077-L. .. [2] Abdulmohsin, Rahman S., and Muthanna H. Al-Dahhan. "Characteristics of Convective Heat Transport in a Packed Pebble-Bed Reactor." Nuclear Engineering and Design 284 (April 1, 2015): 143-52. doi:10.1016/j.nucengdes.2014.11.041. """ return ((1.18*Re**0.58)**4 + (0.23*(Re/(1-voidage))**0.75)**4)**0.25 def Nu_KTA(Re: float, Pr: float, voidage: float) -> float: r"""Calculates Nusselt number of a fluid passing over a bed of particles using a correlation shown in [1]_ and also cited in the review of [2]_. .. math:: Nu = 1.27\frac{Pr^{1/3}}{\epsilon^{1.18}}Re^{0.36} + 0.033\frac{Pr^{0.5}}{\epsilon^{1.07}}Re^{0.86} Parameters ---------- Re : float Reynolds number with pebble diameter as characteristic dimension, [-] Pr : float Prandtl number of the fluid [-] voidage : float Void fraction of bed packing [-] Returns ------- Nu : float Nusselt number for heat transfer to the packed bed [-] Notes ----- 100 < Re < 1E5; 0.36 < ε < 0.42; D/d > 20 with D as bed diameter, d as particle diameter; H > 4d with H as bed height. Examples -------- >>> Nu_KTA(2000, 0.7, 0.4) 102.08516480718129 References ---------- .. [1] Reactor Core Design of High-Temperature Gas-Cooled Reactors Part 2: Heat Transfer in Spherical Fuel Elements (June 1983). http://www.kta-gs.de/e/standards/3100/3102_2_engl_1983_06.pdf .. [2] Abdulmohsin, Rahman S., and Muthanna H. Al-Dahhan. "Characteristics of Convective Heat Transport in a Packed Pebble-Bed Reactor." Nuclear Engineering and Design 284 (April 1, 2015): 143-52. doi:10.1016/j.nucengdes.2014.11.041. """ return (1.27*Pr**(1/3.)*Re**0.36/voidage**1.18 + 0.033*Pr**0.5/voidage**1.07*Re**0.86) ================================================ FILE: ht/conv_plate.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2018, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import pi, radians, sin from fluids.friction import Kumar_beta_list, friction_plate_Martin_1999, friction_plate_Martin_VDI __all__: list[str] = [ "Nu_plate_Khan_Khan", "Nu_plate_Kumar", "Nu_plate_Martin", "Nu_plate_Muley_Manglik", ] Kumar_ms = [[0.349, 0.663, 0.663], [0.349, 0.598, 0.663], [0.333, 0.591, 0.732], [0.326, 0.529, 0.703], [0.326, 0.503, 0.718]] Kumar_C1s = [[0.718, 0.348, 0.348], [0.718, 0.400, 0.300], [0.630, 0.291, 0.130], [0.562, 0.306, 0.108], [0.562, 0.331, 0.087]] Kumar_Nu_Res = [[10.0, 10.0], [10.0, 100.0], [20.0, 300.0], [20.0, 400.0], [20.0, 500.0]] def Nu_plate_Kumar(Re: float, Pr: float, chevron_angle: float, mu: float | None=None, mu_wall: float | None=None) -> float: r"""Calculates Nusselt number for single-phase flow in a **well-designed** Chevron-style plate heat exchanger according to [1]_. The data is believed to have been developed by APV International Limited, since acquired by SPX Corporation. This uses a curve fit of that data published in [2]_. .. math:: Nu = C_1 Re^m Pr^{0.33}\left(\frac{\mu}{\mu_{wall}}\right)^{0.17} `C1` and `m` are coefficients looked up in a table, with varying ranges of Re validity and chevron angle validity. See the source for their exact values. The wall fluid property correction is included only if the viscosity values are provided. Parameters ---------- Re : float Reynolds number with respect to the hydraulic diameter of the channels, [-] Pr : float Prandtl number calculated with bulk fluid properties, [-] chevron_angle : float Angle of the plate corrugations with respect to the vertical axis (the direction of flow if the plates were straight), between 0 and 90. Many plate exchangers use two alternating patterns; use their average angle for that situation [degrees] mu : float, optional Viscosity of the fluid at the bulk (inlet and outlet average) temperature, [Pa*s] mu_wall : float, optional Viscosity of fluid at wall temperature, [Pa*s] Returns ------- Nu : float Nusselt number with respect to `Dh`, [-] Notes ----- Data on graph from Re=0.1 to Re=10000, with chevron angles 30 to 65 degrees. See `PlateExchanger` for further clarification on the definitions. It is believed the constants used in this correlation were curve-fit to the actual graph in [1]_ by the author of [2]_ as there is no As the coefficients change, there are numerous small discontinuities, although the data on the graphs is continuous with sharp transitions of the slope. The author of [1]_ states clearly this correlation is "applicable only to well designed Chevron PHEs". Examples -------- >>> Nu_plate_Kumar(Re=2000, Pr=0.7, chevron_angle=30) 47.757818892853955 With the wall-correction factor included: >>> Nu_plate_Kumar(Re=2000, Pr=0.7, chevron_angle=30, mu=1E-3, mu_wall=8E-4) 49.604284135097544 References ---------- .. [1] Kumar, H. "The plate heat exchanger: construction and design." In First U.K. National Conference on Heat Transfer: Held at the University of Leeds, 3-5 July 1984, Institute of Chemical Engineering Symposium Series, vol. 86, pp. 1275-1288. 1984. .. [2] Ayub, Zahid H. "Plate Heat Exchanger Literature Survey and New Heat Transfer and Pressure Drop Correlations for Refrigerant Evaporators." Heat Transfer Engineering 24, no. 5 (September 1, 2003): 3-16. doi:10.1080/01457630304056. """ # Uses the standard diameter as characteristic diameter beta_list_len = len(Kumar_beta_list) for i in range(beta_list_len): if chevron_angle <= Kumar_beta_list[i]: C1_options, m_options, Re_ranges = Kumar_C1s[i], Kumar_ms[i], Kumar_Nu_Res[i] break elif i == beta_list_len-1: C1_options, m_options, Re_ranges = Kumar_C1s[-1], Kumar_ms[-1], Kumar_Nu_Res[-1] Re_len = len(Re_ranges) for j in range(Re_len): if Re <= Re_ranges[j]: C1, m = C1_options[j], m_options[j] break elif j == Re_len-1: C1, m = C1_options[-1], m_options[-1] Nu = C1*Re**m*Pr**0.33 if mu_wall is not None and mu is not None: Nu *= (mu/mu_wall)**0.17 return Nu def Nu_plate_Martin(Re: float, Pr: float, chevron_angle: float, variant: str="1999") -> float: r"""Calculates Nusselt number for single-phase flow in a Chevron-style plate heat exchanger according to [1]_, also shown in [2]_ and [3]_. .. math:: Nu = 0.122 Pr^{1/3} \left[f_d Re^2 \sin (2\phi)\right]^{0.374} The Darcy friction factor should be calculated with the Martin (1999) friction factor correlation, as that is what the power of 0.374 was regressed with. It can be altered to a later formulation by Martin in the VDI Heat Atlas 2E, which increases the calculated heat transfer friction slightly. Parameters ---------- Re : float Reynolds number with respect to the hydraulic diameter of the channels, [-] Pr : float Prandtl number calculated with bulk fluid properties, [-] chevron_angle : float Angle of the plate corrugations with respect to the vertical axis (the direction of flow if the plates were straight), between 0 and 90. Many plate exchangers use two alternating patterns; use their average angle for that situation [degrees] variant : str One of '1999' or 'VDI'; chooses between the two Martin friction factor correlations, [-] Returns ------- Nu : float Nusselt number with respect to `Dh`, [-] Notes ----- Based on experimental data from Re from 200 - 10000 and enhancement factors calculated with chevron angles of 0 to 80 degrees. See `PlateExchanger` for further clarification on the definitions. Note there is a discontinuity at Re = 2000 for the transition from laminar to turbulent flow, arising from the friction factor correlation's transition ONLY, although the literature suggests the transition is actually smooth. A viscosity correction power for liquid flows of (1/6) is suggested, and for gases, no correction factor. Examples -------- >>> Nu_plate_Martin(Re=2000.0, Pr=.7, chevron_angle=45.0) 30.427601053757 References ---------- .. [1] Martin, Holger. "A Theoretical Approach to Predict the Performance of Chevron-Type Plate Heat Exchangers." Chemical Engineering and Processing: Process Intensification 35, no. 4 (January 1, 1996): 301-10. https://doi.org/10.1016/0255-2701(95)04129-X. .. [2] Martin, Holger. "Economic optimization of compact heat exchangers." EF-Conference on Compact Heat Exchangers and Enhancement Technology for the Process Industries, Banff, Canada, July 18-23, 1999, 1999. https://publikationen.bibliothek.kit.edu/1000034866. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if variant == "1999": fd = friction_plate_Martin_1999(Re, chevron_angle) elif variant == "VDI": fd = friction_plate_Martin_VDI(Re, chevron_angle) else: raise ValueError("Supported friction factor correlations are Martin's" " '1999' correlation or his 'VDI' correlation only") # VDI, original, and Björn Palm and Joachim Claesson recommend 0.122 leading coeff # The 0.205 in some publications is what happens when the friction factor # is in a fanning basis; = 4^0.374*1.22 = 2.048944 Nu = 0.122*Pr**(1/3.)*(fd*Re*Re*sin(2.0*radians(chevron_angle)))**0.374 return Nu def Nu_plate_Muley_Manglik(Re: float, Pr: float, chevron_angle: float, plate_enlargement_factor: float) -> float: r"""Calculates Nusselt number for single-phase flow in a Chevron-style plate heat exchanger according to [1]_, also shown in [2]_. .. math:: Nu = [0.2668 - 0.006967(\beta) + 7.244\times 10^{-5}(\beta)^2] \times[20.7803 - 50.9372\phi + 41.1585\phi^2 - 10.1507\phi^3] \times Re^{[0.728 + 0.0543\sin[(2\pi\beta/90) + 3.7]]} Pr^{1/3} Parameters ---------- Re : float Reynolds number with respect to the hydraulic diameter of the channels, [-] Pr : float Prandtl number calculated with bulk fluid properties, [-] chevron_angle : float Angle of the plate corrugations with respect to the vertical axis (the direction of flow if the plates were straight), between 0 and 90. Many plate exchangers use two alternating patterns; use their average angle for that situation [degrees] plate_enlargement_factor : float The extra surface area multiplier as compared to a flat plate caused the corrugations, [-] Returns ------- Nu : float Nusselt number with respect to `Dh`, [-] Notes ----- The correlation as presented in [1]_ suffers from a typo, with a coefficient of 10.51 instead of 10.15. Several more decimal places were published along with the corrected typo in [2]_. This has a *very large* difference if not implemented. The viscosity correction power is recommended to be the blanket Sieder and Tate (1936) value of 0.14. The correlation is recommended in the range of Reynolds numbers above 1000, chevron angles between 30 and 60 degrees, and enlargement factors from 1 to 1.5. Due to its cubic nature it is not likely to give good results if the chevron angle or enlargement factors are out of those ranges. Examples -------- >>> Nu_plate_Muley_Manglik(Re=2000, Pr=.7, chevron_angle=45, ... plate_enlargement_factor=1.18) 36.49087100602062 References ---------- .. [1] Muley, A., and R. M. Manglik. "Experimental Study of Turbulent Flow Heat Transfer and Pressure Drop in a Plate Heat Exchanger With Chevron Plates." Journal of Heat Transfer 121, no. 1 (February 1, 1999): 110-17. doi:10.1115/1.2825923. .. [2] Palm, Björn, and Joachim Claesson. "Plate Heat Exchangers: Calculation Methods for Single- and Two-Phase Flow (Keynote)," January 1, 2005, 103-13. https://doi.org/10.1115/ICMM2005-75092. """ beta, phi = chevron_angle, plate_enlargement_factor t1 = (0.2668 - 0.006967*beta + 7.244E-5*beta**2) #t2 = (20.78 - 50.94*phi + 41.16*phi**2 - 10.51*phi**3) # It was the extra decimals which were needed t2 = (20.7803 - 50.9372*phi + 41.1585*phi**2 - 10.1507*phi**3) t3 = (0.728 + 0.0543*sin((2*pi*beta/90) + 3.7)) return t1*t2*Re**t3*Pr**(1/3.) def Nu_plate_Khan_Khan(Re: float, Pr: float, chevron_angle: float) -> float: r"""Calculates Nusselt number for single-phase flow in a Chevron-style plate heat exchanger according to [1]_. .. math:: Nu = \left(0.0161\frac{\beta}{\beta_{max}} + 0.1298\right) Re^{\left(0.198 \frac{\beta}{\beta_{max}} + 0.6398\right)} Pr^{0.35} Parameters ---------- Re : float Reynolds number with respect to the hydraulic diameter of the channels, [-] Pr : float Prandtl number calculated with bulk fluid properties, [-] chevron_angle : float Angle of the plate corrugations with respect to the vertical axis (the direction of flow if the plates were straight), between 0 and 90. Many plate exchangers use two alternating patterns; use their average angle for that situation [degrees] Returns ------- Nu : float Nusselt number with respect to `Dh`, [-] Notes ----- The viscosity correction power is recommended to be the blanket Sieder and Tate (1936) value of 0.14. The correlation is recommended in the range of Reynolds numbers from 500 to 2500, chevron angles between 30 and 60 degrees, and Prandtl numbers between 3.5 and 6. Examples -------- >>> Nu_plate_Khan_Khan(Re=1000, Pr=4.5, chevron_angle=30) 38.40883639103741 References ---------- .. [1] Khan, T. S., M. S. Khan, Ming-C. Chyu, and Z. H. Ayub. "Experimental Investigation of Single Phase Convective Heat Transfer Coefficient in a Corrugated Plate Heat Exchanger for Multiple Plate Configurations." Applied Thermal Engineering 30, no. 8 (June 1, 2010): 1058-65. https://doi.org/10.1016/j.applthermaleng.2010.01.021. """ beta_max = 60. beta_ratio = chevron_angle/beta_max Nu = (0.0161*beta_ratio + 0.1298)*Re**(0.198*beta_ratio + 0.6398)*Pr**0.35 return Nu ================================================ FILE: ht/conv_supercritical.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import log10 __all__: list[str] = [ "Nu_Bishop", "Nu_Bringer_Smith", "Nu_Gorban", "Nu_Griem", "Nu_Gupta", "Nu_Jackson", "Nu_Kitoh", "Nu_Krasnoshchekov", "Nu_Krasnoshchekov_Protopopov", "Nu_McAdams", "Nu_Mokry", "Nu_Ornatsky", "Nu_Petukhov", "Nu_Shitsman", "Nu_Swenson", "Nu_Xu", "Nu_Yamagata", "Nu_Zhu", ] ### Vertical upflow only def Nu_McAdams(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. Found in [2]_ to fit the enhanced heat transfer regime with a MAD of 10.3% which was better than and of the other reviewed correlations. .. math:: Nu_b = 0.0243Re_b^{0.8}Pr_b^{0.4} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties, [-] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- This has also been one of the forms of the Dittus-Boelter correlations. Claimed to fit data for high pressures and low heat fluxes. Examples -------- >>> Nu_McAdams(1E5, 1.2) 261.3838629346147 References ---------- .. [1] Mac Adams, William H. Heat Transmission. New York and London, 1942. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ return 0.0243*Re**0.8*Pr**0.4 def Nu_Shitsman(Re: float, Pr_b: float, Pr_w: float) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_ and [2] as shown in both [3]_ and [4]_. .. math:: Nu_b = 0.023 Re_b^{0.8}(min(Pr_b, Pr_w))^{0.8} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr_b : float Prandtl number with bulk fluid properties, [-] Pr_w : float Prandtl number with wall fluid properties, [-] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- [3]_ states this correlation was developed with D = 7.8 and 8.2 mm and with a `Pr` approximately 1. [3]_ ranked it third in the enhanced heat transfer category, with a MAD as 11.5% [4]_ cites a [1]_ as the source of the correlation. Neither have been reviewed, and both are in Russian. [4]_ lists this as third most accurate of the 14 correlations reviewed from a database of all regimes. Examples -------- >>> Nu_Shitsman(1E5, 1.2, 1.6) 266.1171311047253 References ---------- .. [1] M. E Shitsman, Impairment of the heat transmission at supercritical pressures, High. Temperature, 1963, 1(2): 237-244 .. [2] Miropol`skiy ZL, Shitsman ME (1957). Heat transfer to water and steam at variable specific heat. J Tech Phys XXVII(10): 2359-2372 .. [3] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [4] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. """ return 0.023*Re**0.8*min(Pr_b, Pr_w)**0.8 def Nu_Griem(Re: float, Pr: float, H: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_, also shown in [2]_, [3]_ and [4]_. Has complicated rules regarding where properties should be evaluated. .. math:: Nu_m = 0.0169Re_b^{0.8356} Pr_{sel}^{0.432}\omega Parameters ---------- Re : float Reynolds number as explained below, [-] Pr : float Prandtl number as explained below, [-] H : float, optional Enthalpy of water (if the fluid is water), [J/kg] Returns ------- Nu : float Nusselt number as explained below, [-] Notes ----- w is calculated as follows, for water only, with a reference point from the 1967-IFC formulation. It is set to 1 if H is not provided: if Hb < 1.54E6 J/kg, w = 0.82; if Hb > 1.74E6 J/kg, w = 1; otherwise w = 0.82 + 9E-7*(Hb-1.54E6). To determine heat capacity to be used, Cp should be calculated at 5 points, and the lowest three points should be averaged. The five points are: Tw, (Tw+Tf)/2, Tf, (Tb+Tf)/2, Tb. Viscosity should be the bulk viscosity. Thermal conductivity should be the average of the bulk and wall values. Density should be the bulk density. [2]_ states this correlation was developed with D = 10, 14, and 20 mm, P from 22 to 27 MPa, G from 300 to 2500 kg/m^2/s, and q from 200 to 700 kW/m^2. It was ranked 6th among the 14 correlations reviewed for enhanced heat transfer, with a MAD of 13.8%, and 6th overall for the three heat transfer conditions with a overall MAD of 14.8%. [3]_ ranked it 8th of 14 correlations for the three heat transfer conditions. [2]_ has an almost complete description of the model; both [3]_ and [4]_ simplify it. Examples -------- >>> Nu_Griem(1E5, 1.2) 275.4818576600527 References ---------- .. [1] Griem, H. "A New Procedure for the Prediction of Forced Convection Heat Transfer at near- and Supercritical Pressure." Heat and Mass Transfer 31, no. 5 (1996): 301-5. doi:10.1007/BF02184042. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ if H is not None: if H < 1.54E6: w = 0.82 elif H > 1.74E6: w = 1.0 else: w = 0.82 + 9E-7*(H - 1.54E6) else: w = 1.0 Nu = 0.0169*Re**0.8356*Pr**0.432*w return Nu def Nu_Jackson(Re: float, Pr: float, rho_w: float | None=None, rho_b: float | None=None, Cp_avg: float | None=None, Cp_b: float | None=None, T_b: int | None=None, T_w: int | None=None, T_pc: int | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = 0.0183 Re_b^{0.82} Pr^{0.5} \left(\frac{\rho_w}{\rho_b}\right)^{0.3} \left(\frac{\bar C_p}{C_{p,b}}\right)^n .. math:: n = 0.4 \text{ for } T_b < T_w < T_{pc} \text{ or } 1.2T_{pc} < T_b < T_w .. math:: n = 0.4 + 0.2(T_w/T_{pc} - 1)[1 - 5(T_b/T_{pc}-1)] \text{ for } T_{pc} < T_b < 1.2T_{pc} \text{ and } T_b < T_w .. math:: n = 0.4 + 0.2(T_w/T_{pc} - 1) \text{ for } T_b < T_{pc} < T_w .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties, [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] Cp_avg : float, optional Average heat capacity between the wall and bulk temperatures, [J/kg/K] Cp_b : float, optional Heat capacity at the bulk temperature, [J/kg/K] T_b : float Bulk temperature, [K] T_w : float Wall temperature, [K] T_pc : float Pseudocritical temperature, i.e. temperature at P where Cp is at a maximum, [K] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- The range of examined parameters is as follows: P from 23.4 to 29.3 MPa; G from 700-3600 kg/m^2/s; q from 46 to 2600 kW/m^2; Re from 8E4 to 5E5; D from 1.6 to 20 mm. For enhanced heat transfer database in [2]_, this correlation was the second best with a MAD of 11.5%. In the database in [3]_, the correlation was the second best as well. This is sometimes called the Jackson-Hall correlation. If the extra information is not provided, the correlation will be used without the corrections. Examples -------- >>> Nu_Jackson(1E5, 1.2) 252.37231572974918 References ---------- .. [1] Jackson, J. D. "Consideration of the Heat Transfer Properties of Supercritical Pressure Water in Connection with the Cooling of Advanced Nuclear Reactors", 2002. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ if T_b is not None and T_w is not None and T_pc is not None: if T_b < T_w < T_pc or 1.2*T_pc < T_b < T_w: n = 0.4 elif T_b < T_pc < T_w: n = 0.4 + 0.2*(T_w/T_pc - 1) else: n = 0.4 + 0.2*(T_w/T_pc - 1)*(1 - 5*(T_b/T_pc - 1)) else: n = 0.4 Nu = 0.0183*Re**0.82*Pr**0.5 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.3 if Cp_avg is not None and Cp_b is not None: Nu *= (Cp_avg/Cp_b)**n return Nu def Nu_Gupta(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | None=None, mu_w: float | None=None, mu_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_w = 0.004 Re_w^{0.923} \bar{Pr}_w^{0.773} \left(\frac{\rho_w}{\rho_b}\right)^{0.186} \left(\frac{\mu_w}{\mu_b}\right)^{0.366} .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with wall fluid properties, [-] Pr : float Prandtl number with wall fluid properties and an average heat capacity between the wall and bulk temperatures [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] mu_w : float, optional Viscosity at the wall temperature, [Pa*s] mu_b : float, optional Viscosity at the bulk temperature, [Pa*s] Returns ------- Nu : float Nusselt number with wall fluid properties, [-] Notes ----- For the data used to develop the correlation, P was set at 24 MPa, and D was 10 mm. G varied from 200-1500 kg/m^2/s and q varied from 0 to 1250 kW/m^2. Cp used in the calculation of Prandtl number should be the average value of those at the wall and the bulk temperatures. For deteriorated heat transfer, this was the most accurate correlation in [2]_ with a MAD of 18.1%. If the extra density and viscosity information is not provided, it will not be used. Examples -------- >>> Nu_Gupta(1E5, 1.2, 330, 290., 8e-4, 9e-4) 186.20135477175126 References ---------- .. [1] Gupta, Sahil, Amjad Farah, Krysten King, Sarah Mokry, and Igor Pioro. "Developing New Heat-Transfer Correlation for SuperCritical-Water Flow in Vertical Bare Tubes," January 1, 2010, 809-17. doi:10.1115/ICONE18-30024. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ Nu = 0.004*Re**0.923*Pr**0.773 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.186 if mu_w is not None and mu_b is not None: Nu *= (mu_w/mu_b)**0.366 return Nu def Nu_Swenson(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_w = 0.00459 Re_w^{0.923} Pr_w^{0.613} \left(\frac{\rho_w}{\rho_b}\right)^{0.231} .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with wall fluid properties, [-] Pr : float Prandtl number with wall fluid properties and an average heat capacity between the wall and bulk temperatures [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] Returns ------- Nu : float Nusselt number with wall fluid properties, [-] Notes ----- The range of examined parameters is as follows: P from 22.8 to 27.6 MPa; G from 542-2150 kg/m^2/s; Re from 7.5E4 to 3.16E6; T_b from 75 to 576 degrees Celsius and T_w from 93 to 649 degrees Celsius. Cp used in the calculation of Prandtl number should be the average value of those at the wall and the bulk temperatures. For deteriorated heat transfer, this was the most accurate correlation in [2]_ with a MAD of 18.4%. On the overall database in [3]_, it was the 9th most accurate correlation. If the extra density information is not provided, it will not be used. Examples -------- >>> Nu_Swenson(1E5, 1.2, 330, 290.) 217.92827034803668 References ---------- .. [1] Swenson, H. S., J. R. Carver, and C. R. Kakarala. "Heat Transfer to Supercritical Water in Smooth-Bore Tubes." Journal of Heat Transfer 87, no. 4 (November 1, 1965): 477-83. doi:10.1115/1.3689139. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ Nu = 0.00459*Re**0.923*Pr**0.613 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.231 return Nu def Nu_Xu(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | None=None, mu_w: float | None=None, mu_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = 0.02269 Re_b^{0.8079} \bar{Pr}_b^{0.9213} \left(\frac{\rho_w}{\rho_b}\right)^{0.6638} \left(\frac{\mu_w}{\mu_b}\right)^{0.8687} .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties and an average heat capacity between the wall and bulk temperatures [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] mu_w : float, optional Viscosity at the wall temperature, [Pa*s] mu_b : float, optional Viscosity at the bulk temperature, [Pa*s] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- For the data used to develop the correlation, P varied from 23 to 30 MPa, and D was 12 mm. G varied from 600-1200 kg/m^2/s and q varied from 100 to 600 kW/m^2. Cp used in the calculation of Prandtl number should be the average value of those at the wall and the bulk temperatures. For deteriorated heat transfer, this was the third most accurate correlation in [2]_ with a MAD of 20.5%. If the extra density and viscosity information is not provided, it will not be used. Examples -------- >>> Nu_Xu(1E5, 1.2, 330, 290., 8e-4, 9e-4) 289.133054256742 References ---------- .. [1] Xu, F., Guo, L.J., Mao, Y.F., Jiang, X.E., 2005. "Experimental investigation to the heat transfer characteristics of water in vertical pipes under supercritical pressure". J. Xi'an Jiaotong University 39, 468-471. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ Nu = 0.02269*Re**0.8079*Pr**0.9213 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.6638 if mu_w is not None and mu_b is not None: Nu *= (mu_w/mu_b)**0.8687 return Nu def Nu_Mokry(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_, and reviewed in [2]_. .. math:: Nu_b = 0.0061 Re_b^{0.904} \bar{Pr}_b^{0.684} \left(\frac{\rho_w}{\rho_b}\right)^{0.564} .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties and an average heat capacity between the wall and bulk temperatures [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- For the data used to develop the correlation, P was set at 20 MPa, and D was 10 mm. G varied from 200-1500 kg/m^2/s and q varied from 0 to 1250 kW/m^2. Cp used in the calculation of Prandtl number should be the average value of those at the wall and the bulk temperatures. For deteriorated heat transfer, this was the four most accurate correlation in [2]_ with a MAD of 24.0%. It was also the 7th most accurate against enhanced heat transfer, with a MAD of 14.7%, and the most accurate for the normal heat transfer database as well as the top correlation in all categories combined. If the extra density information is not provided, it will not be used. Examples -------- >>> Nu_Mokry(1E5, 1.2, 330, 290.) 246.1156319156 References ---------- .. [1] Mokry, Sarah, Igor Pioro, Amjad Farah, Krysten King, Sahil Gupta, Wargha Peiman, and Pavel Kirillov. "Development of Supercritical Water Heat-Transfer Correlation for Vertical Bare Tubes." Nuclear Engineering and Design, International Conference on Nuclear Energy for New Europe 2009, 241, no. 4 (April 2011): 1126-36. doi:10.1016/j.nucengdes.2010.06.012. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ Nu = 0.0061*Re**0.904*Pr**0.684 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.564 return Nu def Nu_Bringer_Smith(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under near-supercritical conditions according to [1]_ and as shown in [2]_ and [3]_. .. math:: Nu_x = 0.0266Re_x^{0.77}Pr_w^{0.55} Parameters ---------- Re : float Reynolds number with fluid properties at T_ref, [-] Pr : float Prandtl number with wall fluid properties, [-] Returns ------- Nu : float Nusselt number with fluid properties at T_ref, [-] Notes ----- Fit to data somewhat distant from the critical and pseudo-critical regions. Found to fit the data in [3]_ fourth best; in [2]_ however, it was ranked so low that no ranking was given. Tref and the properties therein should be evaluated as follows: .. math:: T_{ref} = T_b \text{ if } \frac{T_{pc}-T_b}{T_w-T_b} < 0 .. math:: T_{ref} = T_{pc} \text{ if } 0 < \frac{T_{pc}-T_b}{T_w-T_b} < 1 .. math:: T_{ref} = T_w \text{ if } \frac{T_{pc}-T_b}{T_w-T_b} > 1 Examples -------- >>> Nu_Bringer_Smith(1E5, 1.2) 208.1763175327 References ---------- .. [1] Bringer, R. P., and J. M. Smith. "Heat Transfer in the Critical Region." AIChE Journal 3, no. 1 (March 1, 1957): 49-55. doi:10.1002/aic.690030110. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. """ return 0.0266*Re**0.77*Pr**0.55 def Nu_Ornatsky(Re: float, Pr_b: float, Pr_w: float, rho_w: int | None=None, rho_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_ as shown in both [2]_ and [3]_. .. math:: Nu_b = 0.023Re_b^{0.8}(\min(Pr_b, Pr_w))^{0.8} \left(\frac{\rho_w}{\rho_b}\right)^{0.3} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr_b : float Prandtl number with bulk fluid properties, [-] Pr_w : float Prandtl number with wall fluid properties, [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- [2]_ ranked it thirteenth in the enhanced heat transfer category, with a MAD of 19.8% and 11th in the normal heat transfer with a MAD of 17.6%. [3]_ ranked it seventh on a combined database. If the extra density information is not provided, it will not be used. Examples -------- >>> Nu_Ornatsky(1E5, 1.2, 1.5, 330, 290.) 276.6353115083 References ---------- .. [1] Ornatsky A.P., Glushchenko, L.P., Siomin, E.T. (1970). The research of temperature conditions of small diameter parallel tubes cooled by water under supercritical pressures. In: Proceedings of the 4th international heat transfer conference, Paris-Versailles, France. Elsevier, Amsterdam, vol VI, Paper no. B, 8 November 1970 .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. """ Nu = 0.023*Re**0.8*min(Pr_b, Pr_w)**0.8 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.3 return Nu def Nu_Gorban(Re: float, Pr: float) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. Not recommended. .. math:: Nu_b = 0.0059Re_b^{0.90}Pr_b^{-0.12} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties, [-] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- Reviewed in [2]_ and [3]_; [2]_ did not even rank it, and [3]_ ranked it 12th of 14. Examples -------- >>> Nu_Gorban(1E5, 1.2) 182.536728273 References ---------- .. [1] Gorban LM, Pomet`ko RS, Khryaschev OA (1990) Modeling of water heat transfer with Freon of supercritical pressure, 1766, Institute of Physics and Power Engineering, Obninsk .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. """ return 0.0059*Re**0.90*Pr**-0.12 def Nu_Zhu(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | None=None, k_w: float | None=None, k_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = 0.0068 Re_b^{0.9} \bar{Pr}_b^{0.63} \left(\frac{\rho_w}{\rho_b}\right)^{0.17} \left(\frac{k_w}{k_b}\right)^{0.29} .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties and an average heat capacity between the wall and bulk temperatures [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] k_w : float, optional Thermal conductivity at the wall temperature, [W/m/K] k_b : float, optional Thermal conductivity at the bulk temperature, [W/m/K] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- For the data used to develop the correlation, P varied from 22 to 30 MPa, and D was 26 mm. G varied from 600-1200 kg/m^2/s and q varied from 200 to 600 kW/m^2. Cp used in the calculation of Prandtl number should be the average value of those at the wall and the bulk temperatures. On the overall database in [2]_, this was the 8th most accurate correlation,and ninth most accurate against normal heat transfer. If the extra density and thermal conductivity information is not provided, it will not be used. Examples -------- >>> Nu_Zhu(1E5, 1.2, 330, 290., 0.63, 0.69) 240.145985449 References ---------- .. [1] Zhu, Xiaojing, Qincheng Bi, Dong Yang, and Tingkuan Chen. "An Investigation on Heat Transfer Characteristics of Different Pressure Steam-Water in Vertical Upward Tube." Nuclear Engineering and Design 239, no. 2 (February 2009): 381-88. doi:10.1016/j.nucengdes.2008.10.026. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ Nu = 0.0068*Re**0.9*Pr**0.63 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.17 if k_w is not None and k_b is not None: Nu *= (k_w/k_b)**0.29 return Nu def Nu_Bishop(Re: float, Pr: float, rho_w: float | None=None, rho_b: float | None=None, D: float | None=None, x: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. Correlation includes an adjustment for the thermal entry length. One of the most common correlations for supercritical convection. .. math:: Nu_b = 0.0069 Re_b^{0.9} \bar Pr_b^{0.66} \left(\frac{\rho_w}{\rho_b}\right)^{0.43}(1+2.4D/x) .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties and an average heat capacity between the wall and bulk temperatures [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] D : float, optional Diameter of tube, [m] x : float, optional Axial distance along the tube, [m] Returns ------- Nu : float Nusselt number with wall fluid properties, [-] Notes ----- For the data used to develop the correlation, P varied from 22.8 to 27.6 MPa, and D was x/D varied from 30-365. G varied from 651-3662 kg/m^2/s and q varied from 310 to 3460 kW/m^2. T_b varied from 282 to 527 degrees Celsius. Cp used in the calculation of Prandtl number should be the average value of those at the wall and the bulk temperatures. For enhanced heat transfer, this was the 11th most accurate correlation in [2]_ with a MAD of 19.0%. On the overall database in [3]_, it was the most accurate correlation however. If the extra density information is not provided, it will not be used. If both diameter and axial distance are not provided, the entrance correction is not used. Examples -------- >>> Nu_Bishop(1E5, 1.2, 330, 290., .01, 1.2) 265.362005007 References ---------- .. [1] Bishop A.A., Sandberg R.O., Tong L.S. (1965) Forced convection heat transfer to water at near-critical temperature and supercritical pressures. In: AIChE J. Chemical engineering symposium series, no. 2. Institute of Chemical Engineers, London .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ Nu = 0.0069*Re**0.9*Pr**0.66 if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.43 if D is not None and x is not None: Nu *= (1 + 2.4*D/x) return Nu def Nu_Yamagata(Re: float, Pr: float, Pr_pc: float | None=None, Cp_avg: float | None=None, Cp_b: float | None=None, T_b: int | None=None, T_w: int | None=None, T_pc: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = 0.0138 Re_b^{0.85}Pr_b^{0.8}F .. math:: F = \left(\frac{\bar C_p}{C_{p,b}}\right)^{n_2} \text{ if } \frac{T_{pc}-T_b}{T_w-T_b} < 0 .. math:: F = 0.67Pr_{pc}^{-0.05} \left(\frac{\bar C_p}{C_{p,b}}\right)^{n_1} \text{ if } 0 < \frac{T_{pc}-T_b}{T_w-T_b} < 1 .. math:: F = 1\text{ if } \frac{T_{pc}-T_b}{T_w-T_b} > 1 .. math:: n_1 = -0.77(1 + 1/Pr_{pc}) + 1.49 .. math:: n_2 = 1.44(1 + 1/Pr_{pc}) - 0.53 .. math:: \bar{Cp} = \frac{H_w-H_b}{T_w-T_b} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties, [-] Pr_pc : float, optional Prandtl number at the pseudocritical temperature, [-] Cp_avg : float, optional Average heat capacity between the wall and bulk temperatures, [J/kg/K] Cp_b : float, optional Heat capacity at the bulk temperature, [J/kg/K] T_b : float Bulk temperature, [K] T_w : float Wall temperature, [K] T_pc : float Pseudocritical temperature, i.e. temperature at P where Cp is at a maximum, [K] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- For the data used to develop the correlation, P varied from 22.6 to 29.4 MPa, and D was 7.5 and 10 mm. G varied from 310-1830 kg/m^2/s, q varied from 116 to 930 kW/m^2, and bulk temperature varied from 230 to 540 decrees Celsius. In the database in [3]_, the correlation was considered but not tested. In [2]_, the correlation was considered but no results were reported. For enhanced heat transfer database in [2]_, this correlation was the second best with a MAD of 11.5%. In the database in [3]_, the correlation was the second best as well. If the extra information is not provided, the correlation will be used without the corrections. Examples -------- >>> Nu_Yamagata(Re=1E5, Pr=1.2, Pr_pc=1.5, Cp_avg=2080.845, Cp_b=2048.621, T_b=650, T_w=700, T_pc=600.0) 292.347342800 References ---------- .. [1] Yamagata, K, K Nishikawa, S Hasegawa, T Fujii, and S Yoshida. "Forced Convective Heat Transfer to Supercritical Water Flowing in Tubes." International Journal of Heat and Mass Transfer 15, no. 12 (December 1, 1972): 2575-93. doi:10.1016/0017-9310(72)90148-2. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ F = 1.0 if (T_b is not None and T_w is not None and T_pc is not None and Pr_pc is not None and Cp_avg is not None and Cp_b is not None): E = (T_pc - T_b)/(T_w - T_b) if E < 0.0: n2 = 1.44*(1 + 1/Pr_pc) - 0.53 F = (Cp_avg/Cp_b)**n2 elif E < 1.0: n1 = -0.77*(1 + 1/Pr_pc) + 1.49 F = 0.67*Pr_pc**-0.05*(Cp_avg/Cp_b)**n1 return 0.0138*Re**0.85*Pr**0.8*F def Nu_Kitoh(Re: float, Pr: float, H: float | None=None, G: int | None=None, q: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_, also shown in [2]_, [3]_ and [4]_. Depends on fluid enthalpy, mass flux, and heat flux. .. math:: Nu_b = 0.015Re_b^{0.85} Pr_b^m .. math:: m = 0.69 - \frac{81000}{q_{dht}} + f_cq .. math:: q_{dht} = 200 G^{1.2} .. math:: f_c = 2.9\times10^{-8} + \frac{0.11}{q_{dht}} \text{ for } H_b < 1500 \text{ kJ/kg} .. math:: f_c = -8.7\times10^{-8} - \frac{0.65}{q_{dht}} \text{ for } 1500 \text{ kJ/kg} < H_b < 3300 \text{ kJ/kg} .. math:: f_c = -9.7\times10^{-7} + \frac{1.3}{q_{dht}} \text{ for } H_b > 3300 \text{ kJ/kg} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties, [-] H : float, optional Enthalpy of water (if the fluid is water), [J/kg] G : float, optional Mass flux of the fluid, [kg/m^2/s] q : float, optional Heat flux to wall, [W/m^2] Returns ------- Nu : float Nusselt number as explained below, [-] Notes ----- The reference point for the enthalpy values is not stated in [1]_. The upper and lower enthalpy limits for this correlation are 4000 kJ/kg and 0 kJ/kg, but these are not enforced in this function. If not all of H, G, and q are provided, the correlation is used without the correction. This correlation was ranked 6th best in [3]_, and found 4th best for enhanced heat transfer in [2]_ with a MAD of 12.3%. For the data used to develop the correlation, G varied from 100-1750 kg/m^2/s, q varied from 0 to 1800 kW/m^2, and bulk temperature varied from 20 to 550 decrees Celsius. This correlation does not have realistic behavior for values outside those used in the study, and should not be used. Examples -------- >>> Nu_Kitoh(1E5, 1.2, 1.3E6, 1500, 5E6) 331.8023413959 References ---------- .. [1] Kitoh, Kazuaki, Seiichi Koshizuka, and Yoshiaki Oka. "Refinement of Transient Criteria and Safety Analysis for a High-Temperature Reactor Cooled by Supercritical Water." Nuclear Technology 135, no. 3 (September 1, 2001): 252-64. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ if H is not None and G is not None and q is not None: qht = 200.*G**1.2 if H < 1.5E6: fc = 2.9E-8 + 0.11/qht elif 1.5E6 <= H <= 3.3E6: fc = -8.7E-8 - 0.65/qht else: fc = -9.7E-7 + 1.3/qht m = 0.69 - 81000./qht + fc*q else: m = 0.69 return 0.015*Re**0.85*Pr**m def Nu_Krasnoshchekov_Protopopov(Re: float, Pr: float, Cp_avg: int | None=None, Cp_b: float | None=None, k_w: float | None=None, k_b: float | None=None, mu_w: float | None=None, mu_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = Nu_0\left(\frac{\mu_w}{\mu_b}\right)^{0.11}\left(\frac{k_b}{k_w} \right)^{-0.33}\left(\frac{\bar C_p}{C_{p,b}}\right)^{0.35} .. math:: Nu_0 = \frac{(f/8)Re_b \bar Pr_b}{1.07+12.7(f/8)^{1/2} (\bar Pr_b)^{2/3}-1)} .. math:: fd = [1.82\log_{10}(Re_b) - 1.64]^{-2} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties [-] Cp_avg : float, optional Average heat capacity between the wall and bulk temperatures, [J/kg/K] Cp_b : float, optional Heat capacity at the bulk temperature, [J/kg/K] k_w : float, optional Thermal conductivity at the wall temperature, [W/m/K] k_b : float, optional Thermal conductivity at the bulk temperature, [W/m/K] mu_w : float, optional Viscosity at the wall temperature, [Pa*s] mu_b : float, optional Viscosity at the bulk temperature, [Pa*s] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- For the data used to develop the correlation, P varied from 22.3 to 32 MPa, Re varied from 2E4 to 8.6E6, Pr from 0.86-86, viscosity ration from 0.9 to 3.6, thermal conductivity ratio from 1 to 6, and heat capacity ratio from 0.07 to 4.5. For the heat transfer database in [3]_, this correlation was 14th most accurate. If the extra heat capacity, viscosity, and thermal conductivity information is not provided, it will not be used. Examples -------- >>> Nu_Krasnoshchekov_Protopopov(1E5, 1.2, 330, 290., 0.62, 0.52, 8e-4, 9e-4) 228.8529673740 References ---------- .. [1] Krasnoshchekov EA, Protopopov VS (1959) Heat transfer at supercritical region in flow of carbon dioxide and water in tubes. Therm Eng 12:26-30 .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. .. [3] Yu, Jiyang, Baoshan Jia, Dan Wu, and Daling Wang. "Optimization of Heat Transfer Coefficient Correlation at Supercritical Pressure Using Genetic Algorithms." Heat and Mass Transfer 45, no. 6 (January 8, 2009): 757-66. doi:10.1007/s00231-008-0475-4. .. [4] Jäger, Wadim, Victor Hugo Sánchez Espinoza, and Antonio Hurtado. "Review and Proposal for Heat Transfer Predictions at Supercritical Water Conditions Using Existing Correlations and Experiments." Nuclear Engineering and Design, (W3MDM) University of Leeds International Symposium: What Where When? Multi-dimensional Advances for Industrial Process Monitoring, 241, no. 6 (June 2011): 2184-2203. doi:10.1016/j.nucengdes.2011.03.022. """ fd = (1.82*log10(Re) - 1.64)**-2 Nu = (fd/8.)*Re*Pr/(1.07 + 12.7*(fd/8.)**0.5*(Pr**(2/3.)-1)) if mu_w is not None and mu_b is not None: Nu *= (mu_w/mu_b)**0.11 if k_w is not None and k_b is not None: Nu *= (k_w/k_b)**-0.33 if Cp_avg is not None and Cp_b is not None: Nu *= (Cp_avg/Cp_b)**0.35 return Nu def Nu_Petukhov(Re: float, Pr: float, rho_w: float | None=None, rho_b: float | None=None, mu_w: float | None=None, mu_b: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = \frac{(f/8)Re_b \bar Pr_b}{1+900/Re_b+12.7(f/8)^{1/2} (\bar Pr_b)^{2/3}-1)} .. math:: f = f_d\left(\frac{\rho_w}{\rho_b}\right)^{0.4} \left(\frac{\mu_w}{\mu_b}\right)^{0.2} .. math:: f_d = [1.82\log_{10}(Re_b) - 1.64]^{-2} Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] mu_w : float, optional Viscosity at the wall temperature, [Pa*s] mu_b : float, optional Viscosity at the bulk temperature, [Pa*s] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- For the heat transfer database in [2]_, this correlation was 5th most accurate in the enhanced heat transfer category, and second in the normal heat transfer category with MADs of 13.8% and 12.0% respectively. If the extra viscosity and density information is not provided, it will not be used. Examples -------- >>> Nu_Petukhov(1E5, 1.2, 330, 290., 8e-4, 9e-4) 254.825859846 References ---------- .. [1] Petukhov, B.S., V.A. Kurganov, and V.B. Ankudinov. "HEAT TRANSFER AND FLOW RESISTANCE IN THE TURBULENT PIPE FLOW OF A FLUID WITH NEAR-CRITICAL STATE PARAMETERS." High Temperature 21, no. 1 (1983): 81-89. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ fd = (1.82*log10(Re) - 1.64)**-2 if rho_w is not None and rho_b is not None: fd *= (rho_w/rho_b)**0.4 if mu_w is not None and mu_b is not None: fd *= (mu_w/mu_b)**0.2 return (fd/8.)*Re*Pr/(1 + 900./Re + 12.7*(fd/8.)**0.5*(Pr**(2/3.)-1)) def Nu_Krasnoshchekov(Re: float, Pr: float, rho_w: float | None=None, rho_b: float | None=None, Cp_avg: float | None=None, Cp_b: float | None=None, T_b: float | None=None, T_w: float | None=None, T_pc: float | None=None) -> float: r"""Calculates internal convection Nusselt number for turbulent vertical upward flow in a pipe under supercritical conditions according to [1]_. .. math:: Nu_b = Nu_0\left(\frac{\rho_w}{\rho_b}\right)^{0.3}\left( \frac{\bar C_p}{C_{p,b}}\right)^{n} .. math:: Nu_0 = \frac{(f/8)Re_b \bar Pr_b}{1.07+12.7(f/8)^{1/2} (\bar Pr_b^{2/3}-1)} .. math:: f_d = [1.82\log_{10}(Re_b) - 1.64]^{-2} .. math:: n = 0.4 \text{ for } T_b < T_w < T_{pc} \text{ or } 1.2T_{pc} < T_b < T_w .. math:: n = n_1 = 0.22 + 0.18T_w/T_{pc} \text{ for } 1 < T_w/T_{pc} < 2.5 .. math:: n = n_1 + (5n_1 - 2)(1 - T_b/T_{pc}) \text{ for } T_{pc} < T_b < 1.2T_{pc} \text{ and } T_{b} < T_w Parameters ---------- Re : float Reynolds number with bulk fluid properties, [-] Pr : float Prandtl number with bulk fluid properties, [-] rho_w : float, optional Density at the wall temperature, [kg/m^3] rho_b : float, optional Density at the bulk temperature, [kg/m^3] Cp_avg : float, optional Average heat capacity between the wall and bulk temperatures, [J/kg/K] Cp_b : float, optional Heat capacity at the bulk temperature, [J/kg/K] T_b : float Bulk temperature, [K] T_w : float Wall temperature, [K] T_pc : float Pseudocritical temperature, i.e. temperature at P where Cp is at a maximum, [K] Returns ------- Nu : float Nusselt number with bulk fluid properties, [-] Notes ----- The range of examined parameters is as follows: P from 23.4 to 29.3 MPa; G from 700-3600 kg/m^2/s; q from 46 to 2600 kW/m^2; Re from 8E4 to 5E5; D from 1.6 to 20 mm. If the extra information is not provided, the correlation will be used without the corrections. Examples -------- >>> Nu_Krasnoshchekov(1E5, 1.2) 234.8285518561 References ---------- .. [1] Krasnoshchekov, E.A., Protopopov, V.S., Van Fen, Kuraeva, I.V., 1967. Experimental investigation of heat transfer for carbon dioxide in the supercritical region. In Proceedings of the Second All-Soviet Union Conference on Heat and Mass Transfer, Minsk, Belarus, May. .. [2] Chen, Weiwei, Xiande Fang, Yu Xu, and Xianghui Su. "An Assessment of Correlations of Forced Convection Heat Transfer to Water at Supercritical Pressure." Annals of Nuclear Energy 76 (February 2015): 451-60. doi:10.1016/j.anucene.2014.10.027. """ if T_b is not None and T_w is not None and T_pc is not None: n1 = 0.22 + 0.18*T_w/T_pc if T_b < T_w < T_pc or 1.2*T_pc < T_b < T_w: n = 0.4 elif 1.0 < T_w/T_pc < 2.5: n = n1 else: n = n1 + (5.0*n1 - 2.0)*(1.0 - T_b/T_pc) else: n = 0.4 fd = (1.82*log10(Re) - 1.64)**-2 Nu = (fd/8.)*Re*Pr/(1.07 + 12.7*(fd/8.)**0.5*(Pr**(2/3.)-1.0)) if rho_w is not None and rho_b is not None: Nu *= (rho_w/rho_b)**0.3 if Cp_avg is not None and Cp_b is not None: Nu *= (Cp_avg/Cp_b)**n return Nu ================================================ FILE: ht/conv_tube_bank.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import exp, pi, radians, sin from fluids.numerics import bisplev, horner, implementation_optimize_tck, splev from ht.core import WALL_FACTOR_PRANDTL, wall_factor __all__: list[str] = [ "ESDU_tube_angle_correction", "ESDU_tube_row_correction", "Nu_ESDU_73031", "Nu_Grimison_tube_bank", "Nu_HEDH_tube_bank", "Nu_Zukauskas_Bejan", "Zukauskas_tube_row_correction", "baffle_correction_Bell", "baffle_leakage_Bell", "bundle_bypassing_Bell", "dP_Kern", "dP_Zukauskas", "laminar_correction_Bell", "unequal_baffle_spacing_Bell", ] __numba_additional_funcs__ = ["Grimison_C1_aligned_interp", "Grimison_m_aligned_interp", "Grimson_C1_staggered_interp", "Grimson_m_staggered_interp", "Kern_f_Re", "Bell_baffle_configuration_obj", "Bell_baffle_leakage_obj", "Bell_bundle_bypass_low_obj", "Bell_bundle_bypass_high_obj"] IS_NUMBA = "IS_NUMBA" in globals() # Applies for row 1-9. Grimson_Nl_aligned = [0.64, 0.8, 0.87, 0.9, 0.92, 0.94, 0.96, 0.98, 0.99] Grimson_Nl_staggered = [0.68, 0.75, 0.83, 0.89, 0.92, 0.95, 0.97, 0.98, 0.99] Grimison_SL_aligned = [1.25, 1.5, 2, 3] Grimison_ST_aligned = Grimison_SL_aligned Grimison_C1_aligned = [[0.348, 0.275, 0.1, 0.0633], [0.367, 0.25, 0.101, 0.0678], [0.418, 0.299, 0.229, 0.198], [0.29, 0.357, 0.374, 0.286]] Grimison_m_aligned = [[0.592, 0.608, 0.704, 0.752], [0.586, 0.62, 0.702, 0.744], [0.57, 0.602, 0.632, 0.648], [0.601, 0.584, 0.581, 0.608]] Grimison_C1_aligned_tck = implementation_optimize_tck([[1.25, 1.25, 1.25, 1.25, 3.0, 3.0, 3.0, 3.0], [1.25, 1.25, 1.25, 1.25, 3.0, 3.0, 3.0, 3.0], [0.34800000000000003, 0.20683194444444492, -0.18023055555555617, 0.06330000000000001, 0.3755277777777776, -0.28351037808642043, 0.24365763888889008, -0.0007166666666667326, 0.5481111111111114, 0.2925767746913588, 0.8622214506172828, 0.5207777777777779, 0.29, 0.5062500000000002, 0.26944444444444426, 0.286], 3, 3], force_numpy=IS_NUMBA) Grimison_C1_aligned_interp = lambda x, y : float(bisplev(x, y, Grimison_C1_aligned_tck)) Grimison_m_aligned_tck = implementation_optimize_tck([[1.25, 1.25, 1.25, 1.25, 3.0, 3.0, 3.0, 3.0], [1.25, 1.25, 1.25, 1.25, 3.0, 3.0, 3.0, 3.0], [0.5920000000000001, 0.5877777777777775, 0.9133333333333344, 0.752, 0.5828472222222219, 0.7998613040123475, 0.7413584104938251, 0.7841111111111112, 0.5320833333333332, 0.5504147376543196, 0.30315663580247154, 0.4148888888888891, 0.601, 0.5454861111111109, 0.6097500000000002, 0.608], 3, 3], force_numpy=IS_NUMBA) Grimison_m_aligned_interp = lambda x, y : float(bisplev(x, y, Grimison_m_aligned_tck)) Grimson_SL_staggered = [1.25, 1.5, 2, 3, 1, 1.25, 1.5, 2, 3, 0.9, 1.125, 1.25, 1.5, 2, 3, 0.6, 0.9, 1.125, 1.25, 1.5, 2, 3] Grimson_ST_staggered = [1.25, 1.25, 1.25, 1.25, 1.5, 1.5, 1.5, 1.5, 1.5, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3] Grimson_m_staggered = [0.556, 0.568, 0.572, 0.592, 0.558, 0.554, 0.562, 0.568, 0.58, 0.571, 0.565, 0.556, 0.568, 0.556, 0.562, 0.636, 0.581, 0.56, 0.562, 0.568, 0.57, 0.574] Grimson_C1_staggered = [0.518, 0.451, 0.404, 0.31, 0.497, 0.505, 0.46, 0.416, 0.356, 0.446, 0.478, 0.519, 0.452, 0.482, 0.44, 0.213, 0.401, 0.518, 0.522, 0.488, 0.449, 0.428] """`interp2d` creates warnings when used on these. They are avoided by pre-generating the splines, and interfacing with fitpack at a lower level. """ tck_Grimson_m_staggered = implementation_optimize_tck([[1.25, 1.25, 1.8667584356619125, 2.0, 2.8366905775206916, 3.0, 3.0], [0.6, 0.6, 1.0085084989709654, 1.340729148958038, 1.5154196399508033, 3.0, 3.0], [1.731351706314169, 0.3675823638826614, 0.6267891238439347, 0.5623083927989683, 0.5920000000000982, 1.180171700201992, 0.7874995409316767, 0.4622370503994375, 0.562004066622535, 0.5623955950882191, 0.5680620929528815, 0.5720626262793304, 0.5510099520872309, 0.5641771077227365, 0.5597975310692721, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6361653765016168, 0.5601991640778442, 0.5621224100266599, 0.5684014375982079, 0.573932491076899], 1, 1], force_numpy=IS_NUMBA) tck_Grimson_C1_staggered = implementation_optimize_tck([[1.25, 1.25, 1.936293121624252, 2.0, 2.094408820089069, 3.0, 3.0], [0.6, 0.6, 1.1841422334268308, 1.3897531616318943, 1.6483901017748916, 3.0, 3.0], [0.534042720665836, 0.5446897215451869, 0.4613632028066018, 0.4370513304331604, 0.31000000000000005, 0.3060114256888106, 0.4719357486311919, 0.5043332405690643, 0.4371755864391464, 0.4362779343788622, 0.364660449991649, 0.5144234623651529, 0.4513822953351327, 0.4852710459180796, 0.4420724694173403, 0.0, 0.0, 0.0, 0.0, 0.0, 0.21898644381978172, 0.5500312131715677, 0.4969529176876636, 0.46150347905703587, 0.4270770845430577], 1, 1], force_numpy=IS_NUMBA) Grimson_m_staggered_interp = lambda x, y: float(bisplev(x, y, tck_Grimson_m_staggered)) Grimson_C1_staggered_interp = lambda x, y: float(bisplev(x, y, tck_Grimson_C1_staggered)) def Nu_Grimison_tube_bank(Re: float, Pr: float, Do: float, tube_rows: int, pitch_parallel: float, pitch_normal: float) -> float: r"""Calculates Nusselt number for crossflow across a tube bank of tube rows at a specified `Re`, `Pr`, and `D` using the Grimison methodology as described in [1]_. .. math:: \bar{Nu_D} = 1.13C_1Re_{D,max}^m Pr^{1/3}C_2 Parameters ---------- Re : float Reynolds number with respect to average (bulk) fluid properties and tube outside diameter, [-] Pr : float Prandtl number with respect to average (bulk) fluid properties, [-] Do : float Tube outer diameter, [m] tube_rows : int Number of tube rows per bundle, [-] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] Returns ------- Nu : float Nusselt number with respect to tube outside diameter, [-] Notes ----- Tube row correction factors are applied for tube row counts less than 10, also published in [1]_. Examples -------- >>> Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, ... pitch_normal=.05, pitch_parallel=.05, Do=.025) 79.07883866010 >>> Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, ... pitch_normal=.07, pitch_parallel=.05, Do=.025) 79.92721078571 References ---------- .. [1] Grimson, E. D. (1937) Correlation and Utilisation of New Data on Flow Resistance and Heat Transfer for Cross Flow of Gases over Tube Banks. Trans. ASME. 59 583-594 """ staggered = abs(1 - pitch_normal/pitch_parallel) > 0.05 a = pitch_normal/Do # sT b = pitch_parallel/Do if not staggered: C1 = float(bisplev(b, a, Grimison_C1_aligned_tck)) m = float(bisplev(b, a, Grimison_m_aligned_tck)) else: C1 = float(bisplev(b, a, tck_Grimson_C1_staggered)) m = float(bisplev(b, a, tck_Grimson_m_staggered)) tube_rows = int(tube_rows) if tube_rows < 10: if tube_rows < 1: tube_rows = 1 if staggered: C2 = Grimson_Nl_staggered[tube_rows] else: C2 = Grimson_Nl_aligned[tube_rows] else: C2 = 1.0 Nu = 1.13*Re**m*Pr**(1.0/3.0)*C2*C1 return Nu Zukauskas_Czs_low_Re_staggered = [0.8295, 0.8792, 0.9151, 0.9402, 0.957, 0.9677, 0.9745, 0.9785, 0.9808, 0.9823, 0.9838, 0.9855, 0.9873, 0.9891, 0.991, 0.9929, 0.9948, 0.9967, 0.9987] Zukauskas_Czs_high_Re_staggered = [0.6273, 0.7689, 0.8473, 0.8942, 0.9254, 0.945, 0.957, 0.9652, 0.9716, 0.9765, 0.9803, 0.9834, 0.9862, 0.989, 0.9918, 0.9943, 0.9965, 0.998, 0.9986] Zukauskas_Czs_inline = [0.6768, 0.8089, 0.8687, 0.9054, 0.9303, 0.9465, 0.9569, 0.9647, 0.9712, 0.9766, 0.9811, 0.9847, 0.9877, 0.99, 0.992, 0.9937, 0.9953, 0.9969, 0.9986] def Zukauskas_tube_row_correction(tube_rows: int, staggered: bool=True, Re: float=1E4) -> float: r"""Calculates the tube row correction factor according to a graph digitized from [1] for heat transfer across a tube bundle. The correction factors are slightly different for staggered vs. inline configurations; for the staggered configuration, factors are available separately for `Re` larger or smaller than 1000. This method is a tabular lookup, with values of 1 when the tube row count is 20 or more. Parameters ---------- tube_rows : int Number of tube rows per bundle, [-] staggered : bool, optional Whether in the in-line or staggered configuration, [-] Re : float, optional The Reynolds number of flow through the tube bank using the bare tube outer diameter and the minimum flow area through the bundle, [-] Returns ------- F : float Tube row count correction factor, [-] Notes ----- The basis for this method is that an infinitely long tube bank has a factor of 1; in practice the factor is reached at 20 rows. Examples -------- >>> Zukauskas_tube_row_correction(4, staggered=True) 0.8942 >>> Zukauskas_tube_row_correction(6, staggered=False) 0.9465 References ---------- .. [1] Zukauskas, A. Heat transfer from tubes in crossflow. In T.F. Irvine, Jr. and J. P. Hartnett, editors, Advances in Heat Transfer, volume 8, pages 93-160. Academic Press, Inc., New York, 1972. """ tube_rows = int(tube_rows) # sanity for indexing if tube_rows < 1: tube_rows = 1 if staggered: # in-line, with a tolerance of 0.05 proximity if tube_rows <= 19: factors = Zukauskas_Czs_low_Re_staggered if Re < 1000 else Zukauskas_Czs_high_Re_staggered correction = factors[tube_rows-1] else: correction = 1.0 else: if tube_rows <= 19: correction = Zukauskas_Czs_inline[tube_rows-1] else: correction = 1.0 return correction def Nu_Zukauskas_Bejan(Re: float, Pr: float, tube_rows: int, pitch_parallel: float, pitch_normal: float, Pr_wall: float | None=None) -> float: r"""Calculates Nusselt number for crossflow across a tube bank of tube number n at a specified `Re` according to the method of Zukauskas [1]_. A fit to graphs from [1]_ published in [2]_ is used for the correlation. The tube row correction factor is obtained from digitized graphs from [1]_, and a lookup table was created and is used for speed. The formulas are as follows: Aligned tube banks: .. math:: \bar Nu_D = 0.9 C_nRe_D^{0.4}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \text{ for } 1 < Re < 100 .. math:: \bar Nu_D = 0.52 C_nRe_D^{0.5}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \text{ for } 100 < Re < 1000 .. math:: \bar Nu_D = 0.27 C_nRe_D^{0.63}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \text{ for } 1000 < Re < 20000 .. math:: \bar Nu_D = 0.033 C_nRe_D^{0.8}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \text{ for } 20000 < Re < 200000 Staggered tube banks: .. math:: \bar Nu_D = 1.04C_nRe_D^{0.4}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \text{ for } 1 < Re < 500 .. math:: \bar Nu_D = 0.71C_nRe_D^{0.5}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \text{ for } 500 < Re < 1000 .. math:: \bar Nu_D = 0.35 C_nRe_D^{0.6}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \left(\frac{X_t}{X_l}\right)^{0.2} \text{ for } 1000 < Re < 20000 .. math:: \bar Nu_D = 0.031 C_nRe_D^{0.8}Pr^{0.36}\left(\frac{Pr}{Pr_w}\right)^{0.25} \left(\frac{X_t}{X_l}\right)^{0.2} \text{ for } 20000 < Re < 200000 Parameters ---------- Re : float Reynolds number with respect to average (bulk) fluid properties and tube outside diameter, [-] Pr : float Prandtl number with respect to average (bulk) fluid properties, [-] tube_rows : int Number of tube rows per bundle, [-] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] Pr_wall : float, optional Prandtl number at the wall temperature; provide if a correction with the defaults parameters is desired; otherwise apply the correction elsewhere, [-] Returns ------- Nu : float Nusselt number with respect to tube outside diameter, [-] Notes ----- If `Pr_wall` is not provided, the Prandtl number correction is not used and left to an outside function. A Prandtl number exponent of 0.25 is recommended in [1]_ for heating and cooling for both liquids and gases. Examples -------- >>> Nu_Zukauskas_Bejan(Re=1E4, Pr=7., tube_rows=10, pitch_parallel=.05, pitch_normal=.05) 175.9202277145248 References ---------- .. [1] Zukauskas, A. Heat transfer from tubes in crossflow. In T.F. Irvine, Jr. and J. P. Hartnett, editors, Advances in Heat Transfer, volume 8, pages 93-160. Academic Press, Inc., New York, 1972. .. [2] Bejan, Adrian. "Convection Heat Transfer", 4E. Hoboken, New Jersey: Wiley, 2013. """ staggered = abs(1 - pitch_normal/pitch_parallel) > 0.05 f = 1.0 if not staggered: if Re < 100: c, m = 0.9, 0.4 elif Re < 1000: c, m = 0.52, 0.05 elif Re < 2E5: c, m = 0.27, 0.63 else: c, m = 0.033, 0.8 else: if Re < 500: c, m = 1.04, 0.4 elif Re < 1000: c, m = 0.71, 0.5 elif Re < 2E5: c, m = 0.35, 0.6 f = (pitch_normal/pitch_parallel)**0.2 else: c, m = 0.031, 0.8 f = (pitch_normal/pitch_parallel)**0.2 Nu = c*Re**m*Pr**0.36*f if Pr_wall is not None: Nu*= (Pr/Pr_wall)**0.25 Cn = Zukauskas_tube_row_correction(tube_rows, staggered=staggered, Re=Re) Nu *= Cn return Nu # For row counts 3 to 9, inclusive. Lower tube counts shouldn't be considered # tube banks. 10 is 1. ESDU_73031_F2_inline = [0.8479, 0.8957, 0.9306, 0.9551, 0.9724, 0.9839, 0.9902] ESDU_73031_F2_staggered = [0.8593, 0.8984, 0.9268, 0.9482, 0.965, 0.9777, 0.9868] def ESDU_tube_row_correction(tube_rows: int, staggered: bool=True, Re: float=3000.0, method: str="Hewitt") -> float: r"""Calculates the tube row correction factor according to [1]_ as shown in [2]_ for heat transfer across a tube bundle. This is also used for finned bundles. The correction factors are slightly different for staggered vs. inline configurations. This method is a tabular lookup, with values of 1 when the tube row count is 10 or more. Parameters ---------- tube_rows : int Number of tube rows per bundle, [-] staggered : bool, optional Whether in the in-line or staggered configuration, [-] Re : float, optional The Reynolds number of flow through the tube bank using the bare tube outer diameter and the minimum flow area through the bundle, [-] method : str, optional 'Hewitt'; this may have another option in the future, [-] Returns ------- F2 : float ESDU tube row count correction factor, [-] Notes ----- In [1]_, for line data, there are two curves given for different Reynolds number ranges. This is not included in [2]_ and only an average curve is given. This is not implemented here; `Re` is an argument but does not impact the result of this function. For tube counts 1-7, [3]_ claims the factors from [1]_ are on average: [0.65, 0.77, 0.84, 0.9, 0.94, 0.97, 0.99]. Examples -------- >>> ESDU_tube_row_correction(4, staggered=True) 0.8984 >>> ESDU_tube_row_correction(6, staggered=False) 0.9551 References ---------- .. [1] "Convective Heat Transfer During Crossflow of Fluids Over Plain Tube Banks." ESDU 73031 (November 1, 1973). .. [2] Hewitt, G. L. Shires, T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1st edition. Boca Raton: CRC Press, 1994. .. [3] Rabas, T. J., and J. Taborek. "Survey of Turbulent Forced-Convection Heat Transfer and Pressure Drop Characteristics of Low-Finned Tube Banks in Cross Flow." Heat Transfer Engineering 8, no. 2 (January 1987): 49-62. """ if method == "Hewitt": if staggered: # in-line, with a tolerance of 0.05 proximity if tube_rows <= 2: correction = ESDU_73031_F2_staggered[0] elif tube_rows >= 10: correction = 1.0 else: correction = ESDU_73031_F2_staggered[tube_rows-3] else: if tube_rows <= 2: correction = ESDU_73031_F2_inline[0] elif tube_rows >= 10: correction = 1.0 else: correction = ESDU_73031_F2_inline[tube_rows-3] return correction def ESDU_tube_angle_correction(angle: float) -> float: r"""Calculates the tube bank inclination correction factor according to [1]_ for heat transfer across a tube bundle. .. math:: F_3 = \frac{Nu_{\theta}}{Nu_{\theta=90^{\circ}}} = (\sin(\theta))^{0.6} Parameters ---------- angle : float The angle of inclination of the tuba bank with respect to the longitudinal axis (90° for a straight tube bank) Returns ------- F3 : float ESDU tube inclination correction factor, [-] Notes ----- A curve is given in [1]_ but it is so close the function, it is likely the function is all that is used. [1]_ claims this correction is valid for :math:`100 < Re < 10^{6}`. For angles less than 10°, the problem should be considered internal flow, not flow across a tube bank. Examples -------- >>> ESDU_tube_angle_correction(75) 0.9794139080247666 References ---------- .. [1] "Convective Heat Transfer During Crossflow of Fluids Over Plain Tube Banks." ESDU 73031 (November 1, 1973). """ return sin(radians(angle))**0.6 def Nu_ESDU_73031(Re: float, Pr: float, tube_rows: int, pitch_parallel: float, pitch_normal: float, Pr_wall: float | None=None, angle: float=90.0) -> float: r"""Calculates the Nusselt number for crossflow across a tube bank with a specified number of tube rows, at a specified `Re` according to [1]_, also shown in [2]_. .. math:: \text{Nu} = a \text{Re}^m\text{Pr}^{0.34}F_1 F_2 The constants `a` and `m` come from the following tables: In-line tube banks: +---------+-------+-------+ | Re | a | m | +=========+=======+=======+ | 10-300 | 0.742 | 0.431 | +---------+-------+-------+ | 300-2E5 | 0.211 | 0.651 | +---------+-------+-------+ | 2E5-2E6 | 0.116 | 0.700 | +---------+-------+-------+ Staggered tube banks: +---------+-------+-------+ | Re | a | m | +=========+=======+=======+ | 10-300 | 1.309 | 0.360 | +---------+-------+-------+ | 300-2E5 | 0.273 | 0.635 | +---------+-------+-------+ | 2E5-2E6 | 0.124 | 0.700 | +---------+-------+-------+ Parameters ---------- Re : float Reynolds number with respect to average (bulk) fluid properties and tube outside diameter, [-] Pr : float Prandtl number with respect to average (bulk) fluid properties, [-] tube_rows : int Number of tube rows per bundle, [-] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] Pr_wall : float, optional Prandtl number at the wall temperature; provide if a correction with the defaults parameters is desired; otherwise apply the correction elsewhere, [-] angle : float, optional The angle of inclination of the tuba bank with respect to the longitudinal axis (90° for a straight tube bank) Returns ------- Nu : float Nusselt number with respect to tube outside diameter, [-] Notes ----- The tube-row count correction factor `F2` can be disabled by setting `tube_rows` to 10. The property correction factor `F1` can be disabled by not specifying `Pr_wall`. A Prandtl number exponent of 0.26 is recommended in [1]_ for heating and cooling for both liquids and gases. The pitches are used to determine whhether or not to use data for staggered or inline tube banks. The inline coefficients are valid for a normal pitch to tube diameter ratio from 1.2 to 4; and the staggered ones from 1 to 4. The overall accuracy of this method is claimed to be 15%. See Also -------- ESDU_tube_angle_correction ESDU_tube_row_correction Examples -------- >>> Nu_ESDU_73031(Re=1.32E4, Pr=0.71, tube_rows=8, pitch_parallel=.09, ... pitch_normal=.05) 98.2563319140594 References ---------- .. [1] "High-Fin Staggered Tube Banks: Heat Transfer and Pressure Drop for Turbulent Single Phase Gas Flow." ESDU 86022 (October 1, 1986). .. [2] Hewitt, G. L. Shires, T. Reg Bott G. F., George L. Shires, and T. R. Bott. Process Heat Transfer. 1st edition. Boca Raton: CRC Press, 1994. """ staggered = abs(1 - pitch_normal/pitch_parallel) > 0.05 if staggered: if Re <= 300: a, m = 1.309, 0.360 elif Re <= 2E5: a, m = 0.273, 0.635 else: a, m = 0.124, 0.700 else: if Re <= 300: a, m = 0.742, 0.431 elif Re <= 2E5: a, m = 0.211, 0.651 else: a, m = 0.116, 0.700 F2 = ESDU_tube_row_correction(tube_rows=tube_rows, staggered=staggered) F3 = ESDU_tube_angle_correction(angle) if Pr_wall is not None: F1 = wall_factor(Pr=Pr, Pr_wall=Pr_wall, Pr_heating_coeff=0.26, Pr_cooling_coeff=0.26, property_option=WALL_FACTOR_PRANDTL) else: F1 = 1.0 return a*Re**m*Pr**0.34*F1*F2*F3 def Nu_HEDH_tube_bank(Re: float, Pr: float, Do: float, tube_rows: int, pitch_parallel: float, pitch_normal: float) -> float: r"""Calculates Nusselt number for crossflow across a tube bank of tube rows at a specified `Re`, `Pr`, and `D` using the Heat Exchanger Design Handbook (HEDH) methodology, presented in [1]_. .. math:: Nu = Nu_m f_N .. math:: Nu_m = 0.3 + \sqrt{Nu_{m,lam}^2 + Nu_{m,turb}^2} .. math:: Nu_{m,turb} = \frac{0.037Re^{0.8} Pr}{1 + 2.443Re^{-0.1}(Pr^{2/3} -1)} .. math:: Nu_{m,lam} = 0.664Re^{0.5} Pr^{1/3} .. math:: \psi = 1 - \frac{\pi}{4a} \text{ if b >= 1} .. math:: \psi = 1 - \frac{\pi}{4ab} \text{if b < 1} .. math:: f_A = 1 + \frac{0.7}{\psi^{1.5}}\frac{b/a-0.3}{(b/a) + 0.7)^2} \text{if inline} .. math:: f_A = 1 + \frac{2}{3b} \text{elif partly staggered} .. math:: f_N = \frac{1 + (n-1)f_A}{n} Parameters ---------- Re : float Reynolds number with respect to average (bulk) fluid properties and tube outside diameter, [-] Pr : float Prandtl number with respect to average (bulk) fluid properties, [-] Do : float Tube outer diameter, [m] tube_rows : int Number of tube rows per bundle, [-] pitch_parallel : float Distance between tube center along a line parallel to the flow; has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m] pitch_normal : float Distance between tube centers in a line 90° to the line of flow; has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m] Returns ------- Nu : float Nusselt number with respect to tube outside diameter, [-] Notes ----- Prandtl number correction left to an outside function, although a set of coefficients were specified in [1]_ because they depent on whether heating or cooling is happening, and for gases, use a temperature ratio instaed of Prandtl number. The claimed range of validity of these expressions is :math:`10 < Re < 1E5` and :math:`0.6 < Pr < 1000`. Examples -------- >>> Nu_HEDH_tube_bank(Re=1E4, Pr=7., tube_rows=10, pitch_normal=.05, ... pitch_parallel=.05, Do=.03) 382.4636554404698 Example 3.11 in [2]_: >>> Nu_HEDH_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=.05, ... pitch_parallel=.05, Do=.025) 149.18735251017594 References ---------- .. [1] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [2] Baehr, Hans Dieter, and Karl Stephan. Heat and Mass Transfer. Springer, 2013. """ staggered = abs(1 - pitch_normal/pitch_parallel) > 0.05 a = pitch_normal/Do b = pitch_parallel/Do if b >= 1: voidage = 1. - pi/(4.0*a) else: voidage = 1. - pi/(4.0*a*b) Re = Re/voidage Nu_laminar = 0.664*Re**0.5*Pr**(1.0/3.) Nu_turbulent = 0.037*Re**0.8*Pr/(1. + 2.443*Re**-0.1*(Pr**(2/3.) - 1.0)) Nu = 0.3 + (Nu_laminar*Nu_laminar + Nu_turbulent*Nu_turbulent)**0.5 if not staggered: fA = 1.0 + 0.7/voidage**1.5*(b/a - 0.3)/(b/a + 0.7)**2 else: fA = 1.0 + 2./(3.0*b) # a further partly staggered tube bank correlation exists, using another pitch if tube_rows < 10: fn = (1.0 + (tube_rows - 1.0)*fA)/tube_rows else: fn = fA Nu = Nu*fn return Nu """ Graph presented in Peters and Timmerhaus uses fanning friction factor. This uses Darcy's friction factor. These coefficients were generated to speed up loading of this module. They are regenerated and checked in the tests. """ Kern_f_Re_tck = implementation_optimize_tck([[9.9524, 9.9524, 9.9524, 9.9524, 17.9105, 27.7862, 47.2083, 83.9573, 281.996, 1122.76, 42999.9, 1012440.0, 1012440.0, 1012440.0, 1012440.0], [6.040435949178239, 4.64973456285782, 2.95274850806163, 1.9569061885042, 1.1663069946420412, 0.6830549536215098, 0.4588680265447762, 0.22387792331971723, 0.12721190975530583, 0.1395456548881242, 0.12888895743468684, 0.0, 0.0, 0.0, 0.0], 3], force_numpy=IS_NUMBA) Kern_f_Re = lambda x: float(splev(x, Kern_f_Re_tck)) def dP_Kern(m: float, rho: float, mu: float, DShell: float, LSpacing: float, pitch: float, Do: float, NBaffles: int, mu_w: float | None=None) -> float: r"""Calculates pressure drop for crossflow across a tube bank according to the equivalent-diameter method developed by Kern [1]_, presented in [2]_. .. math:: \Delta P = \frac{f (m/S_s)^2 D_s(N_B+1)}{2\rho D_e(\mu/\mu_w)^{0.14}} .. math:: S_S = \frac{D_S (P_T-D_o) L_B}{P_T} .. math:: D_e = \frac{4(P_T^2 - \pi D_o^2/4)}{\pi D_o} Parameters ---------- m : float Mass flow rate, [kg/s] rho : float Fluid density, [kg/m^3] mu : float Fluid viscosity, [Pa*s] DShell : float Diameter of exchanger shell, [m] LSpacing : float Baffle spacing, [m] pitch : float Tube pitch, [m] Do : float Tube outer diameter, [m] NBaffles : float Baffle count, [] mu_w : float Fluid viscosity at wall temperature, [Pa*s] Returns ------- dP : float Pressure drop across bundle, [Pa] Notes ----- Adjustment for viscosity left out of this function. Example is from [2]_. Roughly 10% difference due to reading of graph. Graph scanned from [1]_, and interpolation is used to read it. Examples -------- >>> dP_Kern(m=11., rho=995., mu=0.000803, mu_w=0.000657, DShell=0.584, ... LSpacing=0.1524, pitch=0.0254, Do=.019, NBaffles=22) 18980.58768759033 References ---------- .. [1] Kern, Donald Quentin. Process Heat Transfer. McGraw-Hill, 1950. .. [2] Peters, Max, Klaus Timmerhaus, and Ronald West. Plant Design and Economics for Chemical Engineers. 5E. New York: McGraw-Hill, 2002. """ # Adjustment for viscosity performed if given Ss = DShell*(pitch-Do)*LSpacing/pitch De = 4*(pitch*pitch - pi*Do*Do/4.)/pi/Do Vs = m/Ss/rho Re = rho*De*Vs/mu f = Kern_f_Re(Re) if mu_w: return f*(Vs*rho)**2*DShell*(NBaffles+1)/(2*rho*De*(mu/mu_w)**0.14) else: return f*(Vs*rho)**2*DShell*(NBaffles+1)/(2*rho*De) dP_staggered_f_tck = implementation_optimize_tck([ [1.00000e+01, 1.00000e+01, 1.00000e+01, 1.00000e+01, 1.16733e+01, 1.31024e+01, 1.40153e+01, 1.49918e+01, 1.71536e+01, 1.85267e+01, 1.98182e+01, 2.07261e+01, 2.22430e+01, 2.37936e+01, 2.67057e+01, 3.22732e+01, 3.48580e+01, 3.72879e+01, 4.10554e+01, 4.44722e+01, 4.78949e+01, 5.12337e+01, 5.53369e+01, 6.51821e+01, 7.04025e+01, 7.60437e+01, 8.21368e+01, 8.87182e+01, 9.51284e+01, 1.03386e+02, 1.08398e+02, 1.29188e+02, 1.55444e+02, 1.68914e+02, 1.82793e+02, 1.97771e+02, 2.23559e+02, 2.78915e+02, 3.35015e+02, 4.97559e+02, 7.31917e+02, 1.11914e+03, 1.74856e+03, 2.30827e+03, 3.36357e+03, 4.36703e+03, 4.85424e+03, 6.81715e+03, 9.91409e+03, 1.96837e+04, 3.98384e+04, 8.69147e+04, 1.77710e+05, 3.29652e+05, 4.53370e+05, 6.17548e+05, 1.38874e+06, 2.75675e+06, 2.75675e+06, 2.75675e+06, 2.75675e+06], [1.25, 1.25, 1.25, 1.25, 2.5 , 2.5 , 2.5 , 2.5 ], [ 23.993727564949694 , -10.244567028024317 , 10.175863843810976 , 1.7999490702171597, 23.522204247227894 , -10.206973826192078 , 10.10518140310304 , 1.780787357025903 , 21.056398673217306 , -8.809585811775245 , 9.191305635699116 , 1.7221653902388994, 19.30502693908655 , -8.454343171481037 , 8.87084592501417 , 1.6628307168281609, 17.765109289150598 , -7.7133771799002115, 8.35255872722289 , 1.6185259877007414, 16.183640933328732 , -6.833873600289119 , 7.722187414020649 , 1.5690827476330849, 14.694535763926543 , -6.150405226666587 , 7.22393729697023 , 1.5213836637875222, 13.605573060235757 , -5.4221574812289735, 6.657567596977061 , 1.4767279611552022, 13.00457363939894 , -5.517651045065633 , 6.649512891288891 , 1.4466870011383937, 11.995125279422092 , -4.905206010958158 , 6.23685497204518 , 1.4176362748134403, 11.320923863947378 , -4.3609663889341554, 5.823387965136728 , 1.3889295522585867, 10.55081833434363 , -4.2246761575331195, 5.6424642699146474, 1.3494878611293746, 9.136637777542301 , -3.3738486997113624, 4.988770673854508 , 1.2912945512808613, 8.142082809925073 , -3.0640564076701335, 4.673896497087432 , 1.2394666235276033, 7.307435480320903 , -2.4507512853218927, 4.184651311906238 , 1.197270318810632 , 6.823162531391426 , -2.341520042292131 , 4.031868925359543 , 1.1654503211708163, 6.292609559336966 , -2.172105165179806 , 3.851181307698692 , 1.1342864317994863, 5.7736639984989955, -1.8024125693369684, 3.540068552249239 , 1.103663937920097 , 5.456996186731616 , -1.6944442210715904, 3.394074222435801 , 1.0771356178101834, 5.04760166750843 , -1.5241640716734404, 3.2252084065116606, 1.051345959799993 , 4.547228150208529 , -1.2330993185368226, 2.940049991699233 , 1.0141134328178203, 4.14534270298608 , -1.0164114172228296, 2.7045876551545796, 0.9793370063645501, 3.771066238885074 , -0.8748901176380479, 2.528229788862222 , 0.9470018923394338, 3.578714777343189 , -0.7900362603927293, 2.4000658218893482, 0.9229840539840486, 3.2657684683098536, -0.5425913450233749, 2.185378113310861 , 0.8999825646391477, 3.1380949473394075, -0.5491995911762386, 2.1192169444451854, 0.8771813561150372, 2.8710190479957056, -0.3178235005664693, 1.9163896313226092, 0.8571821847817371, 2.7427220096515885, -0.350653244945934 , 1.8829269684603023, 0.8348323725060139, 2.4836006182031682, -0.1352407446908991, 1.6647896666111899, 0.8142139723548999, 2.2164004811891416, -0.0371909044098638, 1.492301567301747 , 0.7533707758356281, 1.9945634411125996, 0.1123996806357918, 1.2956302127875685, 0.7249191411158085, 1.842686059394845 , 0.1607181649910975, 1.2153169523034464, 0.6897021922030252, 1.7377903418611442, 0.289292795896553 , 1.08344601062934 , 0.6729372197342073, 1.6396914034896823, 0.1989615127535343, 1.0850029234159282, 0.6499001080984353, 1.487502515283381 , 0.3730846731246894, 0.9013971733000221, 0.6182632804995061, 1.3522211422419148, 0.2911476890926835, 0.8964977394892637, 0.5767990957491218, 1.1810712591370587, 0.3394790407828216, 0.7652655011740844, 0.5323341532737186, 1.02811191715534 , 0.2887724828326413, 0.7066399775545615, 0.4778931341762794, 0.8895112183187074, 0.3210418642443666, 0.6143186197557094, 0.4467010633652874, 0.7932715410752142, 0.3211662514007235, 0.5619909984438982, 0.4128889836673581, 0.7375513367809549, 0.3144259758348928, 0.528973351907581 , 0.4094681633270362, 0.6972754846297644, 0.3205333114809629, 0.4918522964719353, 0.379830474083385 , 0.6641316600566393, 0.281121553527687 , 0.509197543189297 , 0.3783415752249853, 0.6244559596650743, 0.2837572552667433, 0.4993191757674828, 0.3669254758741835, 0.6023735792473888, 0.2761933758206954, 0.4987865496444748, 0.364477871468622 , 0.5590674813131451, 0.2621282514144033, 0.4629707633705547, 0.344500324531334 , 0.4782618774782103, 0.2485411042324743, 0.3784315811630596, 0.3033924096858183, 0.3937668979943847, 0.2127684318169733, 0.3154828002796004, 0.2539053524737366, 0.31876438844008 , 0.158795177701543 , 0.2564298818590743, 0.2026052448806063, 0.2666571057762706, 0.125595092472467 , 0.2213179726704931, 0.1679289011957269, 0.2145850940558933, 0.0821608103084532, 0.1746802190336271, 0.1296792214950967, 0.226583087040426 , 0.1193363052691195, 0.1939070597138282, 0.1415527224622063, 0.2236870028120291, 0.187323377693516 , 0.1956693312744808, 0.1768800813854576, 0.2267018978658211, 0.1724455167753975, 0.1962302349704176, 0.1908917559338361, 0.2244440017710325, 0.1881858244890039, 0.2045576574847943, 0.1746985249870369, 0.2264047973889584, 0.1728059947546293, 0.2044769358453351, 0.1891991544177786, 0.2281141554162358, 0.1740746507220272, 0.2115805378214652, 0.1837416784138279], 3, 3 ]) dP_staggered_correction_tck = implementation_optimize_tck([ [0.4387 , 0.4387 , 0.609319, 0.84214 , 1.22243 , 1.45385 , 2.22751 , 3.54351 , 3.54351 ], [ 100., 100., 100., 100., 100000., 100000., 100000., 100000.], [ 0.9974058596752864, 2.2479507236683025, -2.927557461079741 , 1.4506256824457617, 0.9973256008060759, 1.2891120004637224, 2.57756043014528 , 1.308194055102965 , 0.99328828308559 , 0.9777071534489982, 2.690730180307897 , 1.1511575741885283, 1.0225516740093321, 0.654636148703268 , 3.5328468308711605, 0.9841004806410495, 1.1242321684355248, -1.285317900605766 , 18.760679189927053 , 0.9465800254548173, 1.4029517756874132, -6.782908749288587 , 62.67683464108982 , 0.9308263957725467, 1.6896512655274156, -12.264942447053453 , 107.33977758986381 , 0.9417015493955058], 1, 3 ]) dP_inline_f_tck = implementation_optimize_tck([ [2.85094e+01, 2.85094e+01, 2.85094e+01, 2.85094e+01, 3.29727e+01, 3.53563e+01, 4.12101e+01, 5.26143e+01, 5.91070e+01, 6.37533e+01, 8.29896e+01, 1.24713e+02, 1.57106e+02, 2.78938e+02, 5.28457e+02, 7.95679e+02, 1.10738e+03, 1.61619e+03, 4.85232e+03, 6.54585e+03, 8.11339e+03, 1.45214e+04, 5.39717e+04, 9.84306e+04, 1.69621e+05, 6.05857e+05, 1.87104e+06, 1.87104e+06, 1.87104e+06, 1.87104e+06], [1.25, 1.25, 1.25, 1.25, 2.5 , 2.5 , 2.5 , 2.5 ], [ 5.930973938528779 , -1.4817265127271453, 0.455379701866362 , 0.3498836798224479, 5.658593208961423 , -0.864342651441936 , 0.0859302669335574, 0.3461356038232322, 5.313966014275998 , -0.9488816374714495, 0.1958290207282649, 0.3409206025216998, 4.7479017538057215, -0.9103110524004313, 0.2580592449510698, 0.3323409371000172, 3.985380347891111 , -0.8082048062363013, 0.3083826543793066, 0.3209043535273658, 3.386764509595187 , -0.6210063941958163, 0.2773912522639271, 0.3101279173763394, 2.997259057262402 , -0.6436424918427293, 0.3541133458508599, 0.301791078024438 , 2.5313481368123476, -0.4636698687516479, 0.3080129633362942, 0.291588283194631 , 1.9067355729954962, -0.4062699968822227, 0.3701523002949395, 0.2746968125881167, 1.4860009590268453, -0.3047213533591268, 0.3724777524638413, 0.2586442595556128, 0.9575648609085928, -0.204166339279796 , 0.3941734724654094, 0.2357333057087107, 0.617062433119386 , -0.1088086799459149, 0.3940863377254021, 0.2089273471639561, 0.4722964733105959, -0.0558097050630406, 0.3870405603703184, 0.1913524081468901, 0.3995262320386994, -0.025364894783629 , 0.3882892706432594, 0.1722324338381349, 0.4645326824064042, 0.00678125270346 , 0.3521348515608552, 0.1753669353885564, 0.5446976745961594, 0.152419457668676 , 0.2469868959242308, 0.176815573751498 , 0.5104609851427283, 0.1262432245605664, 0.2778791531974582, 0.1778458681575732, 0.4747778372905396, 0.1841815013323758, 0.2390262532478637, 0.1784924161355043, 0.4331707217581522, 0.1966086663135327, 0.2420381252055944, 0.1781297356716778, 0.3463614484492485, 0.2262216378962929, 0.1657411941914758, 0.1704140168055773, 0.3137425743888637, 0.2228368744465938, 0.1287382966547652, 0.1588140647661027, 0.2666982751158755, 0.2063058104326992, 0.1587051313760535, 0.158741122371858 , 0.2469799643017819, 0.2074867426640942, 0.1619911852835217, 0.1580881696361456, 0.2548927358191072, 0.2072520441235713, 0.1588874948041774, 0.1572373415378531, 0.2498062734215278, 0.2019373850118195, 0.1635492298499229, 0.1573584818834395, 0.2539191494035861, 0.2008897736617199, 0.1630751863377278, 0.1570998393113034], 3, 3 ]) dP_inline_correction_tck = implementation_optimize_tck([ [0.02 , 0.02 , 0.02 , 0.02 , 0.066164, 0.08 , 0.16 , 0.32 , 0.64 , 1.28 , 2.56 , 5.7141 , 5.7141 , 5.7141 , 5.7141 ], [ 1000., 1000., 1000., 1000., 1000000., 1000000., 1000000., 1000000.], [ 1.6050000000000011e+01, -9.9185525084848649e+01, 8.0188908903030449e+02, 5.3722000000000012e+00, 9.7642684825332609e+00, -5.9212586722045586e+01, 4.8681375915163028e+02, 3.8371626170338349e+00, 8.2631729047131390e+00, -5.3624502687061614e+01, 4.4328465663719857e+02, 3.3370326010388487e+00, 5.0902027741289038e+00, -2.5039923342821140e+01, 2.0959688954308831e+02, 2.4638592963292831e+00, 3.3252028425473772e+00, -1.2251528629540998e+01, 1.1583792320215544e+02, 2.0227774411216131e+00, 1.7798328575941409e+00, 2.7213811631652502e+00, -1.5319618243273222e+01, 1.4299822635240957e+00, 1.1740829684913225e+00, 9.4029637834424262e-01, 3.9974603020412691e+00, 1.1420933087379526e+00, 6.3974151735102358e-01, 3.5438685909104040e+00, -2.2199543860124965e+01, 8.1942218695180635e-01, 4.0902459885654202e-01, 1.0486758802627454e+00, -2.1456210505006823e-01, 6.4235546429644219e-01, 3.0522194412697784e-01, 2.4239289999078180e+00, -1.3982480984625624e+01, 5.5177093110224618e-01, 2.7113000000000009e-01, 1.8811070427272716e+00, -9.5202273363636269e+00, 5.1587000000000016e-01], 3, 3 ]) def dP_Zukauskas(Re: float, n: int, ST: float, SL: float, D: float, rho: float, Vmax: float) -> float: r"""Calculates pressure drop for crossflow across a tube bank of tube number n at a specified Re. Method presented in [1]_. Also presented in [2]_. .. math:: \Delta P = N_L \chi \left(\frac{\rho V_{max}^2}{2}\right)f Parameters ---------- Re : float Reynolds number, [-] n : float Number of tube rows, [-] ST : float Transverse pitch, used only by some conditions, [m] SL : float Longitudal pitch, used only by some conditions, [m] D : float Tube outer diameter, [m] rho : float Fluid density, [kg/m^3] Vmax : float Maximum velocity, [m/s] Returns ------- dP : float Pressure drop, [Pa] Notes ----- Does not account for effects in a heat exchanger. Example 2 is from [2]_. Matches to 0.3%; figures are very approximate. Interpolation used with 4 graphs to obtain friction factor and a correction factor. Examples -------- >>> dP_Zukauskas(Re=13943., n=7, ST=0.0313, SL=0.0343, D=0.0164, rho=1.217, Vmax=12.6) 235.2291 >>> dP_Zukauskas(Re=13943., n=7, ST=0.0313, SL=0.0313, D=0.0164, rho=1.217, Vmax=12.6) 161.147 References ---------- .. [1] Zukauskas, A. Heat transfer from tubes in crossflow. In T.F. Irvine, Jr. and J. P. Hartnett, editors, Advances in Heat Transfer, volume 8, pages 93-160. Academic Press, Inc., New York, 1972. .. [2] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ a = ST/D b = SL/D if a == b: parameter = (a-1.)/(b-1.) f = float(bisplev(Re, b, dP_inline_f_tck)) x = float(bisplev(parameter, Re, dP_inline_correction_tck)) else: parameter = a/b f = float(bisplev(Re, a, dP_staggered_f_tck)) x = float(bisplev(parameter, Re, dP_staggered_correction_tck)) return n*x*f*rho/2*Vmax**2 """Note: the smoothing factor was tunned to keep only 7 knots/9 coeffs while getting near to requiring more knots. The fitting for a digitized graph is likely to be at the maximum possible accuracy. Any speed increasing fit function should fit the smoothed function, not the raw data. """ Bell_baffle_configuration_tck = implementation_optimize_tck([[0.0, 0.0, 0.0, 0.0, 0.517361, 0.802083, 0.866319, 0.934028, 0.977431, 1.0, 1.0, 1.0, 1.0], [0.5328447885827443, 0.6821475548927218, 0.9074424740361304, 1.0828783604984582, 1.1485665329698214, 1.1612486065399008, 1.1216591944456349, 1.0762015137576528, 1.0314244120288227, 0.0, 0.0, 0.0, 0.0], 3], force_numpy=IS_NUMBA) Bell_baffle_configuration_obj = lambda x : float(splev(x, Bell_baffle_configuration_tck)) """Derived with: fit = Chebfun.from_function(lambda x: Bell_baffle_configuration_obj(0.5*(x+1)), domain=[-1,1], N=8) cheb2poly(fit.coefficients())[::-1].tolist() xs = np.linspace(0, 1, 3000) f = Bell_baffle_configuration_obj print(max([(f(i)-fit(i*2-1))/f(i) for i in xs]), 'MAX ERR') print(np.mean([abs(f(i)-fit(i*2-1))/f(i) for i in xs]), 'MEAN ERR') """ Bell_baffle_configuration_coeffs = [-17.267087530974095, -17.341072676377735, 60.38380262590988, 60.78202803861199, -83.86556326987701, -84.74024411236306, 58.66461844872558, 59.56146082596216, -21.786957547130935, -22.229378707598116, 4.1167302227508, 4.226246012504343, -0.3349723004600481, -0.3685826653263089, -0.0629839069257099, 0.35883309630976157, 0.9345478582873352] def baffle_correction_Bell(crossflow_tube_fraction: float, method: str="spline") -> float: r"""Calculate the baffle correction factor `Jc` which accounts for the fact that all tubes are not in crossflow to the fluid - some have fluid flowing parallel to them because they are situated in the "window", where the baffle is cut, instead of between the tips of adjacent baffles. Equal to 1 for no tubes in the window, increases to 1.15 when the windows are small and velocity there is high; decreases to about 0.52 for very large baffle cuts. Well designed exchangers should typically have a value near 1.0. Cubic spline interpolation is the default method of retrieving a value from the graph, which was digitized with Engauge-Digitizer. The interpolation can be slightly slow, so a Chebyshev polynomial was fit to a maximum error of 0.142%, average error 0.04% - well within the margin of error of the digitization of the graph; this is approximately 10 times faster, accessible via the 'chebyshev' method. The Heat Exchanger Design Handbook [4]_, [5]_ provides the linear curve fit, which covers the "practical" range of baffle cuts 15-45% but not the last dip in the graph. This method is not recommended, but can be used via the method "HEDH". .. math:: J_c = 0.55 + 0.72Fc Parameters ---------- crossflow_tube_fraction : float Fraction of tubes which are between baffle tips and not in the window, [-] method : str, optional One of 'chebyshev', 'spline', or 'HEDH' Returns ------- Jc : float Baffle correction factor in the Bell-Delaware method, [-] Notes ----- max: ~1.1536 at ~0.9066 min: ~0.5328 at 0 value at 1: ~1.0314 For the 'spline' method, this function takes ~13 us per call. The other two methods are approximately 10x faster. Examples -------- For a HX with four groups of tube bundles; the top and bottom being 9 tubes each, in the window, and the two middle bundles having 41 tubes each, for a total of 100 tubes, the fraction between baffle tubes and not in the window is 0.82. The correction factor is then: >>> baffle_correction_Bell(0.82) 1.1258554691854046 References ---------- .. [1] Bell, Kenneth J. Final Report of the Cooperative Research Program on Shell and Tube Heat Exchangers. University of Delaware, Engineering Experimental Station, 1963. .. [2] Bell, Kenneth J. Delaware Method for Shell-Side Design. In Heat Transfer Equipment Design, by Shah, R. K., Eleswarapu Chinna Subbarao, and R. A. Mashelkar. CRC Press, 1988. .. [3] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook, Eighth Edition. McGraw-Hill Professional, 2007. .. [4] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [5] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. """ if method == "spline": Jc = Bell_baffle_configuration_obj(crossflow_tube_fraction) elif method == "chebyshev": return horner(Bell_baffle_configuration_coeffs, 2.0*crossflow_tube_fraction - 1.0) elif method == "HEDH": Jc = 0.55 + 0.72*crossflow_tube_fraction return Jc """Note: The smoothing factor was hand tuned to not overfit from points which were clearly wrong in the digitization. It will predict values above 1 however for some values; this must be checked! """ Bell_baffle_leakage_x_max = 0.743614 Bell_baffle_leakage_tck = implementation_optimize_tck([[0.0, 0.0, 0.0, 0.0, 0.0213694, 0.0552542, 0.144818, 0.347109, 0.743614, 0.743614, 0.743614, 0.743614], [0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0], [1.0001228445490002, 0.9988161050974387, 0.9987070557919563, 0.9979385859402731, 0.9970983069823832, 0.96602540121758, 0.955136014969614, 0.9476842472211648, 0.9351143114374392, 0.9059649602818451, 0.9218915266550902, 0.9086000082864022, 0.8934758292610783, 0.8737960765592091, 0.83185251064324, 0.8664296734965998, 0.8349705397843921, 0.809133298969704, 0.7752206120745123, 0.7344035693011536, 0.817047920445813, 0.7694560150930563, 0.7250979336267909, 0.6766754605968431, 0.629304180420512, 0.7137237030611423, 0.6408238328161417, 0.5772000233279148, 0.504889627280836, 0.440579886434288, 0.6239736474980684, 0.5273646894226224, 0.43995388722059986, 0.34359277007615313, 0.26986439252143746, 0.5640689738382749, 0.4540959882735219, 0.35278120580740957, 0.24364672351604122, 0.1606942128340308], 3, 1], force_numpy=IS_NUMBA) Bell_baffle_leakage_obj = lambda x, z : float(bisplev(x, z, Bell_baffle_leakage_tck)) def baffle_leakage_Bell(Ssb: float, Stb: float, Sm: float, method: str="spline") -> float: r"""Calculate the baffle leakage factor `Jl` which accounts for leakage between each baffle. Cubic spline interpolation is the default method of retrieving a value from the graph, which was digitized with Engauge-Digitizer. The Heat Exchanger Design Handbook [4]_, [5]_ provides a curve fit as well. This method is not recommended, but can be used via the method "HEDH". .. math:: J_L = 0.44(1-r_s) + [1 - 0.44(1-r_s)]\exp(-2.2r_{lm}) .. math:: r_s = \frac{S_{sb}}{S_{sb} + S_{tb}} .. math:: r_{lm} = \frac{S_{sb} + S_{tb}}{S_m} Parameters ---------- Ssb : float Shell to baffle leakage area, [m^2] Stb : float Total baffle leakage area, [m^2] Sm : float Crossflow area, [m^2] method : str, optional One of 'spline', or 'HEDH' Returns ------- Jl : float Baffle leakage factor in the Bell-Delaware method, [-] Notes ----- Takes ~5 us per call. If the `x` parameter is larger than 0.743614, it is clipped to it. The HEDH curve fits are rather poor and only 6x faster to evaluate. The HEDH example in [6]_'s spreadsheet has an error and uses 0.044 instead of 0.44 in the equation. Examples -------- >>> baffle_leakage_Bell(1, 3, 8) 0.5906621282470 >>> baffle_leakage_Bell(1, 3, 8, 'HEDH') 0.5530236260777 References ---------- .. [1] Bell, Kenneth J. Final Report of the Cooperative Research Program on Shell and Tube Heat Exchangers. University of Delaware, Engineering Experimental Station, 1963. .. [2] Bell, Kenneth J. Delaware Method for Shell-Side Design. In Heat Transfer Equipment Design, by Shah, R. K., Eleswarapu Chinna Subbarao, and R. A. Mashelkar. CRC Press, 1988. .. [3] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook, Eighth Edition. McGraw-Hill Professional, 2007. .. [4] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [5] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. .. [6] Hall, Stephen. Rules of Thumb for Chemical Engineers, Fifth Edition. 5th edition. Oxford ; Waltham , MA: Butterworth-Heinemann, 2012. """ x = (Ssb + Stb)/Sm if x > Bell_baffle_leakage_x_max: x = Bell_baffle_leakage_x_max z = Ssb/(Ssb + Stb) if z > 1.0 or z < 0.0: raise ValueError("Ssb/(Ssb + Stb) must be between 0 and 1") if method == "spline": Jl = Bell_baffle_leakage_obj(x, z) Jl = min(float(Jl), 1.0) elif method == "HEDH": # Hemisphere uses 0.44 as coefficient, rules of thumb uses 0.044 in spreadsheet Jl = 0.44*(1.0 - z) + (1.0 - 0.44*(1.0 - z))*exp(-2.2*x) return Jl Bell_bundle_bypass_x_max = 0.69532 Bell_bundle_bypass_high_spl = implementation_optimize_tck([[0.0, 0.0, 0.0, 0.0, 0.434967, 0.69532, 0.69532, 0.69532, 0.69532], [0.0, 0.0, 0.0, 0.0, 0.1, 0.16666666666666666, 0.5, 0.5, 0.5, 0.5], [0.9992518012440722, 0.9989007625058475, 1.0018411070735471, 0.9941457497302127, 1.0054152224744488, 1.0000002120327414, 0.8193710201718651, 0.8906557463728106, 0.9236476444228989, 0.9466472125718047, 1.002564972451326, 1.0000001328221189, 0.6099796629837915, 0.7779198818216049, 0.8128716798013131, 0.935864247770527, 0.932707600057425, 0.9999978349038892, 0.46653330555544065, 0.6543895994806808, 0.7244471950409509, 0.8599376452211228, 0.9622021460141503, 0.9999989177211911, 0.42206076955873406, 0.6230810793228677, 0.6903177740858685, 0.8544752061829647, 0.9373953303873518, 0.9999983130568033], 3, 3], force_numpy=IS_NUMBA) Bell_bundle_bypass_high_obj = lambda x, y: float(bisplev(x, y, Bell_bundle_bypass_high_spl)) Bell_bundle_bypass_low_spl = implementation_optimize_tck([[0.0, 0.0, 0.0, 0.0, 0.434967, 0.69532, 0.69532, 0.69532, 0.69532], [0.0, 0.0, 0.0, 0.0, 0.1, 0.16666666666666666, 0.5, 0.5, 0.5, 0.5], [1.0015970586968514, 0.9976793473578099, 1.0037098839305505, 0.9953304170745584, 1.0031587186511541, 1.00000028406872, 0.8027498596582175, 0.9050562101782131, 0.9133675590990569, 0.9611563766991582, 0.9879481797594364, 0.9999988983171519, 0.5813496854191834, 0.7520908533825839, 0.7927234268976187, 0.9090698658126287, 0.9857133220039945, 0.9999986096716597, 0.43493461007512263, 0.6478801160783917, 0.6961255921403956, 0.861432071791341, 0.9243020549338703, 0.999997894037133, 0.39110224578093694, 0.606829928454368, 0.6600680810505178, 0.8482579667665061, 0.9223728343461776, 0.9999978298360785], 3, 3], force_numpy=IS_NUMBA) Bell_bundle_bypass_low_obj = lambda x, y : float(bisplev(x, y, Bell_bundle_bypass_low_spl)) def bundle_bypassing_Bell(bypass_area_fraction, seal_strips, crossflow_rows, laminar=False, method="spline"): r"""Calculate the bundle bypassing effect `Jb` according to the Bell-Delaware method for heat exchanger design. Cubic spline interpolation is the default method of retrieving a value from the graph, which was digitized with Engauge-Digitizer. The Heat Exchanger Design Handbook [4]_ provides a curve fit as well. This method is not recommended, but can be used via the method "HEDH": .. math:: J_b = \exp\left[-1.25 F_{sbp} (1 - {2r_{ss}}^{1/3} )\right] For laminar flows, replace 1.25 with 1.35. Parameters ---------- bypass_area_fraction : float Fraction of the crossflow area which is not blocked by a baffle or anything else and available for bypassing, [-] seal_strips : int Number of seal strips per side of a baffle added to prevent bypassing, [-] crossflow_rows : int The number of tube rows in the crosslfow of the baffle, [-] laminar : bool Whether to use the turbulent correction values or the laminar ones; the Bell-Delaware method uses a Re criteria of 100 for this, [-] method : str, optional One of 'spline', or 'HEDH' Returns ------- Jb : float Bundle bypassing effect correction factor in the Bell-Delaware method, [-] Notes ----- Takes ~5 us per call. If the `bypass_area_fraction` parameter is larger than 0.695, it is clipped to it. Examples -------- >>> bundle_bypassing_Bell(0.5, 5, 25) 0.84696117608 >>> bundle_bypassing_Bell(0.5, 5, 25, method='HEDH') 0.84832109705 References ---------- .. [1] Bell, Kenneth J. Final Report of the Cooperative Research Program on Shell and Tube Heat Exchangers. University of Delaware, Engineering Experimental Station, 1963. .. [2] Bell, Kenneth J. Delaware Method for Shell-Side Design. In Heat Transfer Equipment Design, by Shah, R. K., Eleswarapu Chinna Subbarao, and R. A. Mashelkar. CRC Press, 1988. .. [3] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook, Eighth Edition. McGraw-Hill Professional, 2007. .. [4] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. """ z = seal_strips/crossflow_rows x = bypass_area_fraction if method == "spline": if x > Bell_bundle_bypass_x_max: x = Bell_bundle_bypass_x_max if laminar: Jb = Bell_bundle_bypass_low_obj(x, z) else: Jb = Bell_bundle_bypass_high_obj(x, z) Jb = min(Jb, 1.0) elif method == "HEDH": c = 1.35 if laminar else 1.25 Jb = exp(-c*x*(1.0 - (2.0*z)**(1/3.))) return Jb def unequal_baffle_spacing_Bell(baffles: int, baffle_spacing: float, baffle_spacing_in: float | None=None, baffle_spacing_out: float | None=None, laminar: bool=False) -> float: r"""Calculate the correction factor for unequal baffle spacing `Js`, which accounts for higher velocity of fluid flow and greater heat transfer coefficients when the in and/or out baffle spacing is less than the standard spacing. .. math:: J_s = \frac{(n_b - 1) + (B_{in}/B)^{(1-n_b)} + (B_{out}/B)^{(1-n_b)}} {(n_b - 1) + (B_{in}/B) + (B_{out}/B)} Parameters ---------- baffles : int Number of baffles, [-] baffle_spacing : float Average spacing between one end of one baffle to the start of the next baffle for non-exit baffles, [m] baffle_spacing_in : float, optional Spacing between entrace to first baffle, [m] baffle_spacing_out : float, optional Spacing between last baffle and exit, [m] laminar : bool, optional Whether to use the turbulent exponent or the laminar one; the Bell-Delaware method uses a Re criteria of 100 for this, [-] Returns ------- Js : float Unequal baffle spacing correction factor, [-] Notes ----- Examples -------- >>> unequal_baffle_spacing_Bell(16, .1, .15, 0.15) 0.9640087802805195 References ---------- .. [1] Bell, Kenneth J. Final Report of the Cooperative Research Program on Shell and Tube Heat Exchangers. University of Delaware, Engineering Experimental Station, 1963. .. [2] Bell, Kenneth J. Delaware Method for Shell-Side Design. In Heat Transfer Equipment Design, by Shah, R. K., Eleswarapu Chinna Subbarao, and R. A. Mashelkar. CRC Press, 1988. .. [3] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [4] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. .. [5] Hall, Stephen. Rules of Thumb for Chemical Engineers, Fifth Edition. 5th edition. Oxford ; Waltham , MA: Butterworth-Heinemann, 2012. """ if baffle_spacing_in is None: baffle_spacing_in = baffle_spacing if baffle_spacing_out is None: baffle_spacing_out = baffle_spacing n = 1.0/3.0 if laminar else 0.6 Js = ((baffles - 1.0) + (baffle_spacing_in/baffle_spacing)**(1.0 - n) + (baffle_spacing_out/baffle_spacing)**(1.0 - n))/((baffles - 1.0) + (baffle_spacing_in/baffle_spacing) + (baffle_spacing_out/baffle_spacing)) return Js def laminar_correction_Bell(Re: float, total_row_passes: int) -> float: r"""Calculate the correction factor for adverse temperature gradient built up in laminar flow `Jr`. This correction begins at Re = 100, and is interpolated between the value of the formula until Re = 20, when it is the value of the formula. It is 1 for Re >= 100. The value of the formula is not allowed to be less than 0.4. .. math:: Jr^* = \left(\frac{10}{N_{row,passes,tot}}\right)^{0.18} Parameters ---------- Re : float Shell Reynolds number in the Bell-Delaware method, [-] total_row_passes : int The total number of rows passed by the fluid, including those in windows and counting repeat passes of tube rows, [-] Returns ------- Jr : float Correction factor for adverse temperature gradient built up in laminar flow, [-] Notes ----- [5]_ incorrectly uses the number of tube rows per crosslfow section, not total. Examples -------- >>> laminar_correction_Bell(30, 80) 0.7267995454361379 References ---------- .. [1] Bell, Kenneth J. Final Report of the Cooperative Research Program on Shell and Tube Heat Exchangers. University of Delaware, Engineering Experimental Station, 1963. .. [2] Bell, Kenneth J. Delaware Method for Shell-Side Design. In Heat Transfer Equipment Design, by Shah, R. K., Eleswarapu Chinna Subbarao, and R. A. Mashelkar. CRC Press, 1988. .. [3] Schlünder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1987. .. [4] Serth, R. W., Process Heat Transfer: Principles, Applications and Rules of Thumb. 2E. Amsterdam: Academic Press, 2014. .. [5] Hall, Stephen. Rules of Thumb for Chemical Engineers, Fifth Edition. 5th edition. Oxford ; Waltham , MA: Butterworth-Heinemann, 2012. """ if Re > 100.0: return 1.0 Jrr = (10.0/total_row_passes)**0.18 if Re < 20.0: Jr = Jrr else: Jr = Jrr + ((20.0-Re)/80.0)*(Jrr - 1.0) if Jr < 0.4: Jr = 0.4 return Jr ================================================ FILE: ht/conv_two_phase.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import pi from fluids.core import Prandtl, Reynolds from ht.conv_internal import laminar_entry_Seider_Tate __all__: list[str] = [ "Aggour", "Davis_David", "Elamvaluthi_Srinivas", "Groothuis_Hendal", "Hughmark", "Knott", "Kudirka_Grosh_McFadden", "Martin_Sims", "Ravipudi_Godbold", "h_two_phase", "h_two_phase_methods", ] def Davis_David(m: float, x: float, D: float, rhol: float, rhog: float, Cpl: float, kl: float, mul: float) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: \frac{h_{TP} D}{k_l} = 0.060\left(\frac{\rho_L}{\rho_G}\right)^{0.28} \left(\frac{DG_{TP} x}{\mu_L}\right)^{0.87} \left(\frac{C_{p,L} \mu_L}{k_L}\right)^{0.4} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mul : float Viscosity of liquid [Pa*s] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Developed for both vertical and horizontal flow, and flow patters of annular or mist annular flow. Steam-water and air-water were the only considered fluid combinations. Quality ranged from 0.1 to 1 in their data. [1]_ claimed an AAE of 17%. Examples -------- >>> Davis_David(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, kl=.6, ... mul=1E-3) 1437.3282869955121 References ---------- .. [1] Davis, E. J., and M. M. David. "Two-Phase Gas-Liquid Convection Heat Transfer. A Correlation." Industrial & Engineering Chemistry Fundamentals 3, no. 2 (May 1, 1964): 111-18. doi:10.1021/i160010a005. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ G = m/(pi/4*D**2) Prl = Prandtl(Cp=Cpl, mu=mul, k=kl) Nu_TP = 0.060*(rhol/rhog)**0.28*(D*G*x/mul)**0.87*Prl**0.4 return Nu_TP*kl/D def Elamvaluthi_Srinivas(m: float, x: float, D: float, rhol: float, rhog: float, Cpl: float, kl: float, mug: float, mu_b: float, mu_w: float | None=None) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: \frac{h_{TP} D}{k_L} = 0.5\left(\frac{\mu_G}{\mu_L}\right)^{0.25} Re_M^{0.7} Pr^{1/3}_L (\mu_b/\mu_w)^{0.14} .. math:: Re_M = \frac{D V_L \rho_L}{\mu_L} + \frac{D V_g \rho_g}{\mu_g} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mug : float Viscosity of gas [Pa*s] mu_b : float Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- If the viscosity at the wall temperature is not given, the liquid viscosity correction is not applied. Developed for vertical flow, and flow patters of bubbly and slug. Gas/liquid superficial velocity ratios from 0.3 to 4.6, liquid mass fluxes from 200 to 1600 kg/m^2/s, and the fluids tested were air-water and air-aqueous glycerine solutions. The tube inner diameter was 1 cm, and the L/D ratio was 86. Examples -------- >>> Elamvaluthi_Srinivas(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, ... kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) 3901.2134471578584 References ---------- .. [1] Elamvaluthi, G., and N. S. Srinivas. "Two-Phase Heat Transfer in Two Component Vertical Flows." International Journal of Multiphase Flow 10, no. 2 (April 1, 1984): 237-42. doi:10.1016/0301-9322(84)90021-1. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vg = m*x/(rhog*pi/4*D**2) Vl = m*(1-x)/(rhol*pi/4*D**2) Prl = Prandtl(Cp=Cpl, mu=mu_b, k=kl) ReM = D*Vl*rhol/mu_b + D*Vg*rhog/mug Nu_TP = 0.5*(mug/mu_b)**0.25*ReM**0.7*Prl**(1/3.) if mu_w: Nu_TP *= (mu_b/mu_w)**0.14 return Nu_TP*kl/D def Groothuis_Hendal(m: float, x: float, D: float, rhol: float, rhog: float, Cpl: float, kl: float, mug: float, mu_b: float, mu_w: float | None=None, water: bool=False) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: Re_M = \frac{D V_{ls} \rho_l}{\mu_l} + \frac{D V_{gs} \rho_g}{\mu_g} For the air-water system: .. math:: \frac{h_{TP} D}{k_L} = 0.029 Re_M^{0.87}Pr^{1/3}_l (\mu_b/\mu_w)^{0.14} For gas/air-oil systems (default): .. math:: \frac{h_{TP} D}{k_L} = 2.6 Re_M^{0.39}Pr^{1/3}_l (\mu_b/\mu_w)^{0.14} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mug : float Viscosity of gas [Pa*s] mu_b : float Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] water : bool, optional Whether to use the water-air correlation or the gas/air-oil correlation Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- If the viscosity at the wall temperature is not given, the liquid viscosity correction is not applied. Developed for vertical pipes, with superficial velocity ratios of 0.6-250. Tested fluids were air-water, and gas/air-oil. Examples -------- >>> Groothuis_Hendal(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, kl=.6, ... mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) 1192.9543445455754 References ---------- .. [1] Groothuis, H., and W. P. Hendal. "Heat Transfer in Two-Phase Flow.: Chemical Engineering Science 11, no. 3 (November 1, 1959): 212-20. doi:10.1016/0009-2509(59)80089-0. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vg = m*x/(rhog*pi/4*D**2) Vl = m*(1-x)/(rhol*pi/4*D**2) Prl = Prandtl(Cp=Cpl, mu=mu_b, k=kl) ReM = D*Vl*rhol/mu_b + D*Vg*rhog/mug if water: Nu_TP = 0.029*(ReM)**0.87*(Prl)**(1/3.) else: Nu_TP = 2.6*ReM**0.39*Prl**(1/3.) if mu_w: Nu_TP *= (mu_b/mu_w)**0.14 return Nu_TP*kl/D def Hughmark(m: float, x: float, alpha: float, D: float, L: float, Cpl: float, kl: float, mu_b: float | None=None, mu_w: float | None=None) -> float: r"""Calculates the two-phase non-boiling laminar heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: \frac{h_{TP} D}{k_l} = 1.75(1-\alpha)^{-0.5}\left(\frac{m_l C_{p,l}} {(1-\alpha)k_l L}\right)^{1/3}\left(\frac{\mu_b}{\mu_w}\right)^{0.14} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] alpha : float Void fraction in the tube, [] D : float Diameter of the tube [m] L : float Length of the tube, [m] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mu_b : float Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- This model is based on a laminar entry length correlation - for a sufficiently long tube, this will predict unrealistically low heat transfer coefficients. If the viscosity at the wall temperature is not given, the liquid viscosity correction is not applied. Developed for horizontal pipes in laminar slug flow. Data consisted of the systems air-water, air-SAE 10 oil, gas-oil, air-diethylene glycol, and air-aqueous glycerine. Examples -------- >>> Hughmark(m=1, x=.9, alpha=.9, D=.3, L=.5, Cpl=2300, kl=0.6, mu_b=1E-3, ... mu_w=1.2E-3) 212.7411636127175 References ---------- .. [1] Hughmark, G. A. "Holdup and Heat Transfer in Horizontal Slug Gas- Liquid Flow." Chemical Engineering Science 20, no. 12 (December 1, 1965): 1007-10. doi:10.1016/0009-2509(65)80101-4. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ ml = m*(1-x) RL = 1-alpha Nu_TP = 1.75*(RL)**-0.5*(ml*Cpl/RL/kl/L)**(1/3.) if mu_b and mu_w: Nu_TP *= (mu_b/mu_w)**0.14 return Nu_TP*kl/D def Knott(m: float, x: float, D: float, rhol: int, rhog: float, Cpl: float | None=None, kl: float | None=None, mu_b: float | None=None, mu_w: float | None=None, L: float | None=None, hl: None=None) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. Either a specified `hl` is required, or `Cpl`, `kl`, `mu_b`, `mu_w` and `L` are required to calculate `hl`. .. math:: \frac{h_{TP}}{h_l} = \left(1 + \frac{V_{gs}}{V_{ls}}\right)^{1/3} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] Cpl : float, optional Constant-pressure heat capacity of liquid [J/kg/K] kl : float, optional Thermal conductivity of liquid [W/m/K] mu_b : float, optional Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] L : float, optional Length of the tube [m] hl : float, optional Liquid-phase heat transfer coefficient as described below, [W/m^2/K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- The liquid-only heat transfer coefficient will be calculated with the `laminar_entry_Seider_Tate` correlation, should it not be provided as an input. Many of the arguments to this function are optional and are only used if `hl` is not provided. `hl` should be calculated with a velocity equal to that determined with a combined volumetric flow of both the liquid and the gas. All other parameters used in calculating the heat transfer coefficient are those of the liquid. If the viscosity at the wall temperature is not given, the liquid viscosity correction in `laminar_entry_Seider_Tate` is not applied. Examples -------- >>> Knott(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, kl=.6, mu_b=1E-3, ... mu_w=1.2E-3, L=4) 4225.536758045839 References ---------- .. [1] Knott, R. F., R. N. Anderson, Andreas. Acrivos, and E. E. Petersen. "An Experimental Study of Heat Transfer to Nitrogen-Oil Mixtures." Industrial & Engineering Chemistry 51, no. 11 (November 1, 1959): 1369-72. doi:10.1021/ie50599a032. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vgs = m*x/(rhog*pi/4*D**2) Vls = m*(1-x)/(rhol*pi/4*D**2) if not hl: V = Vgs + Vls # Net velocity Re = Reynolds(V=V, D=D, rho=rhol, mu=mu_b) Pr = Prandtl(Cp=Cpl, k=kl, mu=mu_b) Nul = laminar_entry_Seider_Tate(Re=Re, Pr=Pr, L=L, Di=D, mu=mu_b, mu_w=mu_w) hl = Nul*kl/D return hl*(1 + Vgs/Vls)**(1/3.) def Kudirka_Grosh_McFadden(m: float, x: float, D: float, rhol: float, rhog: float, Cpl: float, kl: float, mug: float, mu_b: float, mu_w: float | None=None) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: Nu = \frac{h_{TP} D}{k_l} = 125 \left(\frac{V_{gs}}{V_{ls}} \right)^{0.125}\left(\frac{\mu_g}{\mu_l}\right)^{0.6} Re_{ls}^{0.25} Pr_l^{1/3}\left(\frac{\mu_b}{\mu_w}\right)^{0.14} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mug : float Viscosity of gas [Pa*s] mu_b : float Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- If the viscosity at the wall temperature is not given, the liquid viscosity correction is not applied. Developed for air-water and air-ethylene glycol systems with a L/D of 17.6 and at low gas-liquid ratios. The flow regimes studied were bubble, slug, and froth flow. Examples -------- >>> Kudirka_Grosh_McFadden(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, ... kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) 303.9941255903587 References ---------- .. [1] Kudirka, A. A., R. J. Grosh, and P. W. McFadden. "Heat Transfer in Two-Phase Flow of Gas-Liquid Mixtures." Industrial & Engineering Chemistry Fundamentals 4, no. 3 (August 1, 1965): 339-44. doi:10.1021/i160015a018. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vgs = m*x/(rhog*pi/4*D**2) Vls = m*(1-x)/(rhol*pi/4*D**2) Prl = Prandtl(Cp=Cpl, mu=mu_b, k=kl) Rels = D*Vls*rhol/mu_b Nu = 125*(Vgs/Vls)**0.125*(mug/mu_b)**0.6*Rels**0.25*Prl**(1/3.) if mu_w: Nu *= (mu_b/mu_w)**0.14 return Nu*kl/D def Martin_Sims(m: float, x: float, D: float, rhol: float, rhog: float, hl: float | None=None, Cpl: float | None=None, kl: float | None=None, mu_b: float | None=None, mu_w: float | None=None, L: float | None=None) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: \frac{h_{TP}}{h_l} = 1 + 0.64\sqrt{\frac{V_{gs}}{V_{ls}}} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] hl : float, optional Liquid-phase heat transfer coefficient as described below, [W/m^2/K] Cpl : float, optional Constant-pressure heat capacity of liquid [J/kg/K] kl : float, optional Thermal conductivity of liquid [W/m/K] mu_b : float, optional Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] L : float, optional Length of the tube [m] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- No specific suggestion for how to calculate the liquid-phase heat transfer coefficient is given in [1]_; [2]_ suggests to use the same procedure as in `Knott`. Examples -------- >>> Martin_Sims(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, hl=141.2) 5563.280000000001 >>> Martin_Sims(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, kl=.6, ... mu_b=1E-3, mu_w=1.2E-3, L=24) 5977.505465781747 References ---------- .. [1] Martin, B. W, and G. E Sims. "Forced Convection Heat Transfer to Water with Air Injection in a Rectangular Duct." International Journal of Heat and Mass Transfer 14, no. 8 (August 1, 1971): 1115-34. doi:10.1016/0017-9310(71)90208-0. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vgs = m*x/(rhog*pi/4*D**2) Vls = m*(1-x)/(rhol*pi/4*D**2) if hl is None: V = Vgs + Vls # Net velocity Re = Reynolds(V=V, D=D, rho=rhol, mu=mu_b) Pr = Prandtl(Cp=Cpl, k=kl, mu=mu_b) Nul = laminar_entry_Seider_Tate(Re=Re, Pr=Pr, L=L, Di=D, mu=mu_b, mu_w=mu_w) hl = Nul*kl/D return hl*(1.0 + 0.64*(Vgs/Vls)**0.5) def Ravipudi_Godbold(m: float, x: float, D: float, rhol: float, rhog: float, Cpl: float, kl: float, mug: float, mu_b: float, mu_w: float | None=None) -> float: r"""Calculates the two-phase non-boiling heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. .. math:: Nu = \frac{h_{TP} D}{k_l} = 0.56 \left(\frac{V_{gs}}{V_{ls}} \right)^{0.3}\left(\frac{\mu_g}{\mu_l}\right)^{0.2} Re_{ls}^{0.6} Pr_l^{1/3}\left(\frac{\mu_b}{\mu_w}\right)^{0.14} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mug : float Viscosity of gas [Pa*s] mu_b : float Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- If the viscosity at the wall temperature is not given, the liquid viscosity correction is not applied. Developed with a vertical pipe, superficial gas/liquid velocity ratios of 1-90, in the froth regime, and for fluid mixtures of air and water, toluene, benzene, and methanol. Examples -------- >>> Ravipudi_Godbold(m=1, x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300, kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) 299.3796286459285 References ---------- .. [1] Ravipudi, S., and Godbold, T., The Effect of Mass Transfer on Heat Transfer Rates for Two-Phase Flow in a Vertical Pipe, Proceedings 6th International Heat Transfer Conference, Toronto, V. 1, p. 505-510, 1978. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vgs = m*x/(rhog*pi/4*D**2) Vls = m*(1-x)/(rhol*pi/4*D**2) Prl = Prandtl(Cp=Cpl, mu=mu_b, k=kl) Rels = D*Vls*rhol/mu_b Nu = 0.56*(Vgs/Vls)**0.3*(mug/mu_b)**0.2*Rels**0.6*Prl**(1/3.) if mu_w is not None: Nu *= (mu_b/mu_w)**0.14 return Nu*kl/D def Aggour(m: float, x: float, alpha: float, D: float, rhol: float, Cpl: float, kl: float, mu_b: float, mu_w: float | None=None, L: float | None=None, turbulent: None=None) -> float: r"""Calculates the two-phase non-boiling laminar heat transfer coefficient of a liquid and gas flowing inside a tube of any inclination, as in [1]_ and reviewed in [2]_. Laminar for Rel <= 2000: .. math:: h_{TP} = 1.615\frac{k_l}{D}\left(\frac{Re_l Pr_l D}{L}\right)^{1/3} \left(\frac{\mu_b}{\mu_w}\right)^{0.14} Turbulent for Rel > 2000: .. math:: h_{TP} = 0.0155\frac{k_l}{D} Pr_l^{0.5} Re_l^{0.83} .. math:: Re_l = \frac{\rho_l v_l D}{\mu_l} .. math:: V_l = \frac{V_{ls}}{1-\alpha} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] alpha : float Void fraction in the tube, [-] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] mu_b : float Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] L : float, optional Length of the tube, [m] turbulent : bool or None, optional Whether or not to force the correlation to return the turbulent result; will return the laminar regime if False Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- Developed with mixtures of air-water, helium-water, and freon-12-water and vertical tests. Studied flow patterns were bubbly, slug, annular, bubbly-slug, and slug-annular regimes. Superficial velocity ratios ranged from 0.02 to 470. A viscosity correction is only suggested for the laminar regime. If the viscosity at the wall temperature is not given, the liquid viscosity correction is not applied. Examples -------- >>> Aggour(m=1, x=.9, D=.3, alpha=.9, rhol=1000, Cpl=2300, kl=.6, mu_b=1E-3) 420.9347146885667 References ---------- .. [1] Aggour, Mohamed A. Hydrodynamics and Heat Transfer in Two-Phase Two-Component Flows, Ph.D. Thesis, University of Manutoba, Canada (1978). http://mspace.lib.umanitoba.ca/xmlui/handle/1993/14171. .. [2] Dongwoo Kim, Venkata K. Ryali, Afshin J. Ghajar, Ronald L. Dougherty. "Comparison of 20 Two-Phase Heat Transfer Correlations with Seven Sets of Experimental Data, Including Flow Pattern and Tube Inclination Effects." Heat Transfer Engineering 20, no. 1 (February 1, 1999): 15-40. doi:10.1080/014576399271691. """ Vls = m*(1-x)/(rhol*pi/4*D**2) Vl = Vls/(1.-alpha) Prl = Prandtl(Cp=Cpl, k=kl, mu=mu_b) Rel = Reynolds(V=Vl, D=D, rho=rhol, mu=mu_b) if turbulent or (Rel > 2000.0 and turbulent is None): hl = 0.0155*(kl/D)*Rel**0.83*Prl**0.5 return hl*(1-alpha)**-0.83 else: hl = 1.615*(kl/D)*(Rel*Prl*D/L)**(1/3.) if mu_w: hl *= (mu_b/mu_w)**0.14 return hl*(1.0 - alpha)**(-1/3.) conv_two_phase_methods = { "Davis-David": (Davis_David, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mul")), "Elamvaluthi_Srinivas": (Elamvaluthi_Srinivas, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mug", "mu_b", "mu_w")), "Groothuis_Hendal": (Groothuis_Hendal, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mug", "mu_b", "mu_w")), "Hughmark": (Hughmark, ("m", "x", "alpha", "D", "L", "Cpl", "kl", "mu_b", "mu_w")), "Knott": (Knott, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mu_b", "mu_w", "L")), "Kudirka_Grosh_McFadden": (Kudirka_Grosh_McFadden, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mug", "mu_b", "mu_w")), "Martin_Sims": (Martin_Sims, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mu_b", "mu_w", "L")), "Ravipudi_Godbold": (Ravipudi_Godbold, ("m", "x", "D", "rhol", "rhog", "Cpl", "kl", "mug", "mu_b", "mu_w")), "Aggour": (Aggour, ("m", "x", "alpha", "D", "rhol", "Cpl", "kl", "mu_b", "mu_w", "L")), } # Not actually ranked conv_two_phase_methods_ranked = ["Knott", "Martin_Sims", "Kudirka_Grosh_McFadden", "Groothuis_Hendal", "Aggour", "Hughmark", "Elamvaluthi_Srinivas", "Davis-David", "Ravipudi_Godbold"] conv_two_phase_bad_method = f"Correlation name not recognized; the availble methods are {list(conv_two_phase_methods.keys())}." """Generating code: for m in conv_two_phase_methods_ranked: (f_obj, args) = conv_two_phase_methods[m] print('elif method2 == "%s":' %(m)) t = ' return %s(' %f_obj.__name__ for ar in args: t += '%s=%s, ' %(ar, ar) t = t[:-2] + ')' print(t) for m in conv_two_phase_methods_ranked: (f_obj, args) = conv_two_phase_methods[m] t = 'if ' for ar in args: if ar not in ('m', 'x', 'D', 'Cpl', 'kl'): t += '%s is not None and ' %(ar) t = t[:-5] + ':' print(t) print(' methods.append("%s")' %m) """ def h_two_phase_methods(m, x, D, Cpl, kl, rhol=None, rhog=None, mul=None, mu_b=None, mu_w=None, mug=None, L=None, alpha=None, check_ranges=True): r"""Returns a list of correlation names for the case of two-phase non-boiling liquid-gas laminar flow heat transfer inside a tube. Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] rhol : float, optional Density of the liquid [kg/m^3] rhog : float, optional Density of the gas [kg/m^3] mul : float, optional Viscosity of liquid [Pa*s] mu_b : float, optional Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] mug : float, optional Viscosity of gas [Pa*s] L : float, optional Length of the tube, [m] alpha : float, optional Void fraction in the tube, [-] check_ranges : bool, optional Whether or not to return only correlations suitable for the provided data, [-] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- A review of the correlations for which has the best performance has not been performed. Examples -------- >>> h_two_phase_methods(m=1, x=.9, D=.3, alpha=.9, rhol=1000, Cpl=2300, kl=.6, mu_b=1E-3, mu_w=1.2E-3, L=5)[0] 'Aggour' """ methods = [] if rhol is not None and rhog is not None and mu_b is not None and mu_w is not None and L is not None: methods.append("Knott") if rhol is not None and rhog is not None and mu_b is not None and mu_w is not None and L is not None: methods.append("Martin_Sims") if rhol is not None and rhog is not None and mug is not None and mu_b is not None and mu_w is not None: methods.append("Kudirka_Grosh_McFadden") if rhol is not None and rhog is not None and mug is not None and mu_b is not None and mu_w is not None: methods.append("Groothuis_Hendal") if alpha is not None and rhol is not None and mu_b is not None and mu_w is not None and L is not None: methods.append("Aggour") if alpha is not None and L is not None and mu_b is not None and mu_w is not None: methods.append("Hughmark") if rhol is not None and rhog is not None and mug is not None and mu_b is not None and mu_w is not None: methods.append("Elamvaluthi_Srinivas") if rhol is not None and rhog is not None and mul is not None: methods.append("Davis-David") if rhol is not None and rhog is not None and mug is not None and mu_b is not None and mu_w is not None: methods.append("Ravipudi_Godbold") return methods def h_two_phase(m: float, x: float, D: float, Cpl: float, kl: float, rhol: float | None=None, rhog: None=None, mul: None=None, mu_b: float | None=None, mu_w: float | None=None, mug: None=None, L: float | None=None, alpha: float | None=None, method: str | None=None) -> float: r"""Calculates the two-phase non-boiling laminar heat transfer coefficient of a liquid and gas flowing inside a tube according to the specified method. Nine methods are available. Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [-] D : float Diameter of the tube [m] Cpl : float Constant-pressure heat capacity of liquid [J/kg/K] kl : float Thermal conductivity of liquid [W/m/K] rhol : float, optional Density of the liquid [kg/m^3] rhog : float, optional Density of the gas [kg/m^3] mul : float, optional Viscosity of liquid [Pa*s] mu_b : float, optional Viscosity of liquid at bulk conditions (average of inlet/outlet temperature) [Pa*s] mu_w : float, optional Viscosity of liquid at wall temperature [Pa*s] mug : float, optional Viscosity of gas [Pa*s] L : float, optional Length of the tube, [m] alpha : float, optional Void fraction in the tube, [-] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Other Parameters ---------------- method : string, optional A string of the function name to use, as in the dictionary conv_two_phase_methods. Notes ----- It is difficult to compare correlations. Examples -------- >>> h_two_phase(m=1, x=.9, D=.3, alpha=.9, rhol=1000, Cpl=2300, kl=.6, mu_b=1E-3, mu_w=1.2E-3, L=5, method='Aggour') 420.9347146885667 """ if method is None: method2 = h_two_phase_methods(m=m, x=x, D=D, Cpl=Cpl, kl=kl, rhol=rhol, rhog=rhog, mul=mul, mu_b=mu_b, mu_w=mu_w, mug=mug, L=L, alpha=alpha, check_ranges=True)[0] else: method2 = method if method2 == "Knott": return Knott(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mu_b=mu_b, mu_w=mu_w, L=L) elif method2 == "Martin_Sims": return Martin_Sims(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mu_b=mu_b, mu_w=mu_w, L=L) elif method2 == "Kudirka_Grosh_McFadden": return Kudirka_Grosh_McFadden(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mug=mug, mu_b=mu_b, mu_w=mu_w) elif method2 == "Groothuis_Hendal": return Groothuis_Hendal(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mug=mug, mu_b=mu_b, mu_w=mu_w) elif method2 == "Aggour": return Aggour(m=m, x=x, alpha=alpha, D=D, rhol=rhol, Cpl=Cpl, kl=kl, mu_b=mu_b, mu_w=mu_w, L=L) elif method2 == "Hughmark": return Hughmark(m=m, x=x, alpha=alpha, D=D, L=L, Cpl=Cpl, kl=kl, mu_b=mu_b, mu_w=mu_w) elif method2 == "Elamvaluthi_Srinivas": return Elamvaluthi_Srinivas(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mug=mug, mu_b=mu_b, mu_w=mu_w) elif method2 == "Davis-David": return Davis_David(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mul=mul) elif method2 == "Ravipudi_Godbold": return Ravipudi_Godbold(m=m, x=x, D=D, rhol=rhol, rhog=rhog, Cpl=Cpl, kl=kl, mug=mug, mu_b=mu_b, mu_w=mu_w) else: raise ValueError(conv_two_phase_bad_method) ================================================ FILE: ht/core.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations from math import log from fluids.numerics import i0, i1, k0, k1 __all__: list[str] = [ "LMTD", "Kays_Crawford_laminar_gas_Nu", "Kays_Crawford_laminar_gas_fd", "Kays_Crawford_laminar_liquid_Nu", "Kays_Crawford_laminar_liquid_fd", "Kays_Crawford_turbulent_gas_Nu", "Kays_Crawford_turbulent_gas_fd", "Kays_Crawford_turbulent_liquid_Nu", "Kays_Crawford_turbulent_liquid_fd", "countercurrent_hx_temperature_check", "fin_efficiency_Kern_Kraus", "is_heating_property", "is_heating_temperature", "wall_factor", "wall_factor_Nu", "wall_factor_fd", ] def LMTD(Thi: float, Tho: float, Tci: float, Tco: float, counterflow: bool=True) -> float: r"""Returns the log-mean temperature difference of an ideal counterflow or co-current heat exchanger. .. math:: \Delta T_{LMTD}=\frac{\Delta T_1-\Delta T_2}{\ln(\Delta T_1/\Delta T_2)} .. math:: \text{For countercurrent: } \\ \Delta T_1=T_{h,i}-T_{c,o}\\ \Delta T_2=T_{h,o}-T_{c,i} .. math:: \text{Parallel Flow Only:} \\ {\Delta T_1=T_{h,i}-T_{c,i}}\\ {\Delta T_2=T_{h,o}-T_{c,o}} Parameters ---------- Thi : float Inlet temperature of hot fluid, [K] Tho : float Outlet temperature of hot fluid, [K] Tci : float Inlet temperature of cold fluid, [K] Tco : float Outlet temperature of cold fluid, [K] counterflow : bool, optional Whether the exchanger is counterflow or co-current Returns ------- LMTD : float Log-mean temperature difference [K] Notes ----- Any consistent set of units produces a consistent output. For the case where the temperature difference is the same in counterflow, the arithmeric mean difference (either difference in that case) is the correct result following evaluation of the limit. For the same problem with the co-current case, the limit evaluates to a temperature difference of zero. Examples -------- Example 11.1 in [1]_. >>> LMTD(100., 60., 30., 40.2) 43.200409294131525 >>> LMTD(100., 60., 30., 40.2, counterflow=False) 39.75251118049003 >>> LMTD(100., 60., 20., 60) 40.0 >>> LMTD(100., 60., 20., 60, counterflow=False) 0.0 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ if counterflow: dTF1 = Thi-Tco dTF2 = Tho-Tci else: dTF1 = Thi-Tci dTF2 = Tho-Tco ratio = dTF2/dTF1 if not counterflow and (ratio <= 0.0 or ratio == 1.0): return 0.0 elif counterflow and (ratio <= 0.0 or ratio == 1.0): return dTF1 else: return (dTF2 - dTF1)/log(dTF2/dTF1) def countercurrent_hx_temperature_check(T0i, T0o, T1i, T1o): r"""Perform a check on two sets of temperatures that could represent a countercurrent heat exchanger, and return whether they are possible or not. Parameters ---------- T0i : float Inlet temperature of one fluid, [K] T0o : float Outlet temperature of one fluid, [K] T1i : float Inlet temperature of another fluid, [K] T1o : float Outlet temperature of another fluid, [K] Returns ------- plausible : bool Whether the exchange is possilble, [-] Notes ----- Examples -------- """ if T0i > T1i: Thi, Tho = T0i, T0o Tci, Tco = T1i, T1o else: Thi, Tho = T1i, T1o Tci, Tco = T0i, T0o if Thi < Tho: return False return not Tci > Tco def is_heating_temperature(T: int, T_wall: int) -> bool: r"""Checks whether or not a fluid side is being heated or cooled, from the temperature of the wall and the bulk temperature. Returns True for heating the bulk fluid, and False for cooling the bulk fluid. Parameters ---------- T : float Temperature of flowing fluid away from the heat transfer surface, [K] T_wall : float Temperature of the fluid at the wall, [K] Returns ------- is_heating : bool Whether or not the flow is being heated up by the wall, [-] Examples -------- >>> is_heating_temperature(298.15, 350) True """ return T_wall > T def is_heating_property(prop: float, prop_wall: float) -> bool: r"""Checks whether or not a fluid side is being heated or cooled, from a property of the fluid at the wall and the bulk temperature. Returns True for heating the bulk fluid, and False for cooling the bulk fluid. Parameters ---------- prop : float Viscosity (or Prandtl number) of flowing fluid away from the heat transfer surface, [Pa*s] prop_wall : float Viscosity (or Prandtl number) of the fluid at the wall, [Pa*s] Returns ------- is_heating : bool Whether or not the flow is being heated up by the wall, [-] Examples -------- >>> is_heating_property(1E-3, 1.2E-3) False """ return prop_wall < prop WALL_FACTOR_VISCOSITY = "Viscosity" WALL_FACTOR_PRANDTL = "Prandtl" WALL_FACTOR_TEMPERATURE = "Temperature" WALL_FACTOR_DEFAULT = "Default" # All powers were originally for (wall/bulk)^power, but have been negated here. # Results for Deissler # -0.11 is also an option from another presented correlation Kays_Crawford_laminar_liquid_Nu = {"mu_heating_coeff": 0.14, "mu_cooling_coeff": 0.14, "property_option": WALL_FACTOR_VISCOSITY} Kays_Crawford_laminar_liquid_fd = {"mu_heating_coeff": -0.58, "mu_cooling_coeff": -0.5, "property_option": WALL_FACTOR_VISCOSITY} # 1.35 as a result suggested by an experiment byt the analysis is "preferred" Kays_Crawford_laminar_gas_fd = {"mu_heating_coeff": -1, "mu_cooling_coeff": -1, "property_option": WALL_FACTOR_VISCOSITY} # This is uncertain Kays_Crawford_laminar_gas_Nu = {"mu_heating_coeff": 0.0, "mu_cooling_coeff": 0.0, "property_option": WALL_FACTOR_VISCOSITY} # These seem fairly well measured Kays_Crawford_turbulent_liquid_fd = {"mu_heating_coeff": -0.25, "mu_cooling_coeff": -0.25, "property_option": WALL_FACTOR_VISCOSITY} # This is uncertain Kays_Crawford_turbulent_liquid_Nu = {"mu_heating_coeff": 0.11, "mu_cooling_coeff": 0.25, "property_option": WALL_FACTOR_VISCOSITY} # These see pretty accurate Kays_Crawford_turbulent_gas_fd = {"mu_heating_coeff": 0.1, "mu_cooling_coeff": 0.1, "property_option": WALL_FACTOR_VISCOSITY} Kays_Crawford_turbulent_gas_Nu = {"mu_heating_coeff": 0.5, "mu_cooling_coeff": 0.0, "property_option": WALL_FACTOR_VISCOSITY} # is_turbulent, is_liquid wall_factor_fd_defaults = {(True, True): Kays_Crawford_turbulent_liquid_fd, (True, False): Kays_Crawford_turbulent_gas_fd, (False, True): Kays_Crawford_laminar_liquid_fd, (False, False): Kays_Crawford_laminar_gas_fd} wall_factor_Nu_defaults = {(True, True): Kays_Crawford_turbulent_liquid_Nu, (True, False): Kays_Crawford_turbulent_gas_Nu, (False, True): Kays_Crawford_laminar_liquid_Nu, (False, False): Kays_Crawford_laminar_gas_Nu} def wall_factor_fd(mu, mu_wall, turbulent=True, liquid=False): r"""Computes the wall correction factor for pressure drop due to friction between a fluid and a wall. These coefficients were derived for internal flow inside a pipe, but can be used elsewhere where appropriate data is missing. .. math:: \frac{f_d}{f_{d,\text{constant properties}}} = \left(\frac{\mu}{\mu_{wall}}\right)^n Parameters ---------- mu : float Viscosity (or Prandtl number) of flowing fluid away from the wall, [Pa*s] mu_wall : float Viscosity (or Prandtl number) of the fluid at the wall, [Pa*s] turbulent : bool Whether or not to use the turbulent coefficient, [-] liquid : bool Whether or not to use the liquid phase coefficient; otherwise the gas coefficient is used, [-] Returns ------- factor : float Correction factor for pressure loss; to be multiplied by the friction factor, or pressure drop to obtain the actual result, [-] Notes ----- The exponents are determined as follows: +-----------+--------+---------+---------+ | Regime | Phase | Heating | Cooling | +===========+========+=========+=========+ | Turbulent | Liquid | -0.25 | -0.25 | +-----------+--------+---------+---------+ | Turbulent | Gas | 0.1 | 0.1 | +-----------+--------+---------+---------+ | Laminar | Liquid | -0.58 | -0.5 | +-----------+--------+---------+---------+ | Laminar | Gas | -1 | -1 | +-----------+--------+---------+---------+ Examples -------- >>> wall_factor_fd(mu=8E-4, mu_wall=3E-4, turbulent=True, liquid=True) 0.7825422900366437 References ---------- .. [1] Kays, William M., and Michael E. Crawford. Convective Heat and Mass Transfer. 3rd edition. New York: McGraw-Hill Science/Engineering/Math, 1993. """ params = wall_factor_fd_defaults[(turbulent, liquid)] return wall_factor(mu=mu, mu_wall=mu_wall, **params) def wall_factor_Nu(mu, mu_wall, turbulent=True, liquid=False): r"""Computes the wall correction factor for heat transfer between a fluid and a wall. These coefficients were derived for internal flow inside a pipe, but can be used elsewhere where appropriate data is missing. It is also useful to compare these results with the coefficients used in various heat transfer coefficients. .. math:: \frac{Nu}{Nu_{\text{constant properties}}} = \left(\frac{\mu}{\mu_{wall}}\right)^n Parameters ---------- mu : float Viscosity (or Prandtl number) of flowing fluid away from the heat transfer surface, [Pa*s] mu_wall : float Viscosity (or Prandtl number) of the fluid at the wall, [Pa*s] turbulent : bool Whether or not to use the turbulent coefficient, [-] liquid : bool Whether or not to use the liquid phase coefficient; otherwise the gas coefficient is used, [-] Returns ------- factor : float Correction factor for heat transfer; to be multiplied by the Nusselt number, or heat transfer coefficient to obtain the actual result, [-] Notes ----- The exponents are determined as follows: +-----------+--------+---------+---------+ | Regime | Phase | Heating | Cooling | +===========+========+=========+=========+ | Turbulent | Liquid | 0.11 | 0.25 | +-----------+--------+---------+---------+ | Turbulent | Gas | 0.5 | 0 | +-----------+--------+---------+---------+ | Laminar | Liquid | 0.14 | 0.14 | +-----------+--------+---------+---------+ | Laminar | Gas | 0 | 0 | +-----------+--------+---------+---------+ Examples -------- >>> wall_factor_Nu(mu=8E-4, mu_wall=3E-4, turbulent=True, liquid=True) 1.1139265634480144 >>> wall_factor_Nu(mu=8E-4, mu_wall=3E-4, turbulent=False, liquid=True) 1.147190712947014 >>> wall_factor_Nu(mu=1.5E-5, mu_wall=1.3E-5, turbulent=True, liquid=False) 1.0741723110591495 >>> wall_factor_Nu(mu=1.5E-5, mu_wall=1.3E-5, turbulent=False, liquid=False) 1.0 References ---------- .. [1] Kays, William M., and Michael E. Crawford. Convective Heat and Mass Transfer. 3rd edition. New York: McGraw-Hill Science/Engineering/Math, 1993. """ params = wall_factor_Nu_defaults[(turbulent, liquid)] return wall_factor(mu=mu, mu_wall=mu_wall, **params) wall_factor_bad_option_msg = "Supported options are: "+ str( [WALL_FACTOR_VISCOSITY, WALL_FACTOR_PRANDTL, WALL_FACTOR_TEMPERATURE, WALL_FACTOR_DEFAULT]) def wall_factor(mu: int | None=None, mu_wall: int | None=None, Pr: float | None=None, Pr_wall: float | None=None, T: int | None=None, T_wall: int | None=None, mu_heating_coeff: float=0.11, mu_cooling_coeff: float=0.25, Pr_heating_coeff: float=0.11, Pr_cooling_coeff: float=0.25, T_heating_coeff: float=0.11, T_cooling_coeff: float=0.25, property_option: str=WALL_FACTOR_PRANDTL) -> float: r"""Computes the wall correction factor for heat transfer, mass transfer, or momentum transfer between a fluid and a wall. Utility function; the coefficients for the phenomenon must be provided to this method. The default coefficients are for heat transfer of a turbulent liquid. The general formula is as follows; substitute the property desired and the phenomenon desired into the equation for things other than heat transfer. .. math:: \frac{Nu}{Nu_{\text{constant properties}}} = \left(\frac{\mu}{\mu_{wall}}\right)^n Parameters ---------- mu : float, optional Viscosity of flowing fluid away from the surface, [Pa*s] mu_wall : float, optional Viscosity of the fluid at the wall, [Pa*s] Pr : float, optional Prandtl number of flowing fluid away from the surface, [-] Pr_wall : float, optional Prandtl number of the fluid at the wall, [-] T : float, optional Temperature of flowing fluid away from the surface, [K] T_wall : float, optional Temperature of the fluid at the wall, [K] mu_heating_coeff : float, optional Coefficient for viscosity - surface providing heating, [-] mu_cooling_coeff : float, optional Coefficient for viscosity - surface providing cooling, [-] Pr_heating_coeff : float, optional Coefficient for Prandtl number - surface providing heating, [-] Pr_cooling_coeff : float, optional Coefficient for Prandtl number - surface providing cooling, [-] T_heating_coeff : float, optional Coefficient for temperature - surface providing heating, [-] T_cooling_coeff : float, optional Coefficient for temperature - surface providing cooling, [-] property_option : str, optional Which property to use for computing the correction factor; one of 'Viscosity', 'Prandtl', or 'Temperature'. Returns ------- factor : float Correction factor for heat transfer; to be multiplied by the Nusselt number or heat transfer coefficient or friction factor or pressure drop to obtain the actual result, [-] Examples -------- >>> wall_factor(mu=8E-4, mu_wall=3E-4, Pr=1.2, Pr_wall=1.1, T=300, ... T_wall=350, property_option='Prandtl') 1.0096172023817749 """ if property_option == WALL_FACTOR_DEFAULT: property_option = WALL_FACTOR_PRANDTL if property_option == WALL_FACTOR_VISCOSITY: if mu is None or mu_wall is None: raise TypeError("Viscosity wall correction specified but both " "viscosity values are not available.") heating = is_heating_property(mu, mu_wall) if heating: return (mu/mu_wall)**mu_heating_coeff else: return (mu/mu_wall)**mu_cooling_coeff elif property_option == WALL_FACTOR_TEMPERATURE: if T is None or T_wall is None: raise TypeError("Temperature wall correction specified but both " "temperature values are not available.") heating = is_heating_temperature(T, T_wall) if heating: return (T/T_wall)**T_heating_coeff else: return (T/T_wall)**T_cooling_coeff elif property_option == WALL_FACTOR_PRANDTL: if Pr is None or Pr_wall is None: raise TypeError("Prandtl number wall correction specified but both" " Prandtl number values are not available.") heating = is_heating_property(Pr, Pr_wall) if heating: return (Pr/Pr_wall)**Pr_heating_coeff else: return (Pr/Pr_wall)**Pr_cooling_coeff else: raise ValueError(wall_factor_bad_option_msg) def fin_efficiency_Kern_Kraus(Do: float, D_fin: float, t_fin: float, k_fin: float, h: float) -> float: r"""Returns the efficiency `eta_f` of a circular fin of constant thickness attached to a circular tube, based on the tube diameter `Do`, fin diameter `D_fin`, fin thickness `t_fin`, fin thermal conductivity `k_fin`, and heat transfer coefficient `h`. .. math:: \eta_f = \frac{2r_o}{m(r_e^2 - r_o^2)} \left[\frac{I_1(mr_e)K_1(mr_o) - K_1(mr_e) I_1(mr_o)} {I_0(mr_o) K_1(mr_e) + I_1(mr_e) K_0(mr_o)}\right] .. math:: m = \sqrt{\frac{2h}{k_{fin} t_{fin}}} .. math:: r_e = 0.5 D_{fin} .. math:: r_o = 0.5 D_{o} Parameters ---------- Do : float Outer diameter of bare pipe (as if there were no fins), [m] D_fin : float Outer diameter of the fin, from the center of the tube to the edge of the fin, [m] t_fin : float Thickness of the fin (for constant thickness fins only), [m] k_fin : float Thermal conductivity of the fin, [W/m/K] h : float Heat transfer coefficient of the finned pipe, [W/K] Returns ------- eta_fin : float Fin efficiency [-] Examples -------- >>> fin_efficiency_Kern_Kraus(0.0254, 0.05715, 3.8E-4, 200, 58) 0.841258862023 Notes ----- I0, I1, K0 and K1 are modified Bessel functions of order 0 and 1, modified Bessel function of the second kind of order 0 and 1 respectively. A number of assumptions are made in deriving this set of equations [5]_: * 1-D radial conduction * Steady-state operation * No radiative heat transfer * Temperature-independent fin thermal conductivity * Constant heat transfer coefficient across the whole fin * The fin base temperature is a constant value * There is no constant resistance between the tube material and the added fin * The surrounding fluid is at a constant temperature References ---------- .. [1] Kern, Donald Quentin, and Allan D. Kraus. Extended Surface Heat Transfer. McGraw-Hill, 1972. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [4] Kraus, Allan D., Abdul Aziz, and James Welty. Extended Surface Heat Transfer. 1st edition. New York: Wiley-Interscience, 2001. .. [5] Perrotin, Thomas, and Denis Clodic. "Fin Efficiency Calculation in Enhanced Fin-and-Tube Heat Exchangers in Dry Conditions." In Proc. Int. Congress of Refrigeration 2003, 2003. """ re = 0.5*D_fin ro = 0.5*Do m = (2.0*h/(k_fin*t_fin))**0.5 mre = m*re mro = m*ro x0 = i1(mre) x1 = k1(mre) num = x0*k1(mro) - x1*i1(mro) den = i0(mro)*x1 + x0*k0(mro) return float(2.0*ro/(m*(re*re - ro*ro))*num/den) ================================================ FILE: ht/hx.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations import os from math import exp, floor, log, sqrt, tanh # tanh= 1/coth from typing import Callable from fluids.constants import Btu, degree_Fahrenheit, foot, hour, inch from fluids.numerics import bisect, brenth, factorial, gamma, horner, iv, quad, secant from fluids.numerics import numpy as np from fluids.piping import BWG_SI, BWG_gauges __all__: list[str] = [ "DBundle_for_Ntubes_HEDH", "DBundle_for_Ntubes_Phadkeb", "DBundle_min", "D_baffle_holes", "D_for_Ntubes_VDI", "F_LMTD_Fakheri", "L_unsupported_max", "NTU_from_P_E", "NTU_from_P_G", "NTU_from_P_H", "NTU_from_P_J", "NTU_from_P_basic", "NTU_from_P_plate", "NTU_from_UA", "NTU_from_effectiveness", "Ntubes", "Ntubes_HEDH", "Ntubes_Perrys", "Ntubes_Phadkeb", "Ntubes_VDI", "P_NTU_Pc", "P_NTU_Pp", "P_NTU_method", "R_value", "TEMA_heads", "TEMA_rears", "TEMA_services", "TEMA_shells", "UA_from_NTU", "baffle_thickness", "baffle_types", "calc_Cmax", "calc_Cmin", "calc_Cr", "effectiveness_NTU_method", "effectiveness_from_NTU", "shell_clearance", "size_bundle_from_tubecount", "square_C1s", "square_Ns", "temperature_effectiveness_TEMA_E", "temperature_effectiveness_TEMA_G", "temperature_effectiveness_TEMA_H", "temperature_effectiveness_TEMA_J", "temperature_effectiveness_air_cooler", "temperature_effectiveness_basic", "temperature_effectiveness_plate", "triangular_C1s", "triangular_Ns", ] R_value = foot*foot*degree_Fahrenheit*hour/Btu __numba_additional_funcs__ = ["crossflow_effectiveness_to_int", "to_solve_Ntubes_Phadkeb", "_tubecount_objf_Perry", "_NTU_max_for_P_solver", "_NTU_from_P_solver", "_NTU_from_P_objective", "_NTU_from_P_erf"] IS_NUMBA = "IS_NUMBA" in globals() if IS_NUMBA: __numba_additional_funcs__.append("factorial") def factorial(n): return gamma(n + 1.0) def crossflow_effectiveness_to_int(v: float, NTU: float, t0: float) -> float: x0 = v*v*t0 return (1. + NTU - x0)*exp(-x0)*v*float(iv(0.0, v)) def effectiveness_from_NTU(NTU, Cr, subtype="counterflow", n_shell_tube=None): r"""Returns the effectiveness of a heat exchanger at a specified heat capacity rate, number of transfer units, and configuration. The following configurations are supported: * Counterflow (ex. double-pipe) * Parallel (ex. double pipe inefficient configuration) * Shell and tube exchangers with even numbers of tube passes, one or more shells in series * Crossflow, single pass, fluids unmixed * Crossflow, single pass, Cmax mixed, Cmin unmixed * Crossflow, single pass, Cmin mixed, Cmax unmixed * Boiler or condenser These situations are normally not those which occur in real heat exchangers, but are useful for academic purposes. More complicated expressions are available for other methods. These equations are confirmed in [1]_, [2]_, and [3]_. For parallel flow heat exchangers: .. math:: \epsilon = \frac{1 - \exp[-NTU(1+C_r)]}{1+C_r} For counterflow heat exchangers: .. math:: \epsilon = \frac{1 - \exp[-NTU(1-C_r)]}{1-C_r\exp[-NTU(1-C_r)]},\; C_r < 1 .. math:: \epsilon = \frac{NTU}{1+NTU},\; C_r = 1 For TEMA E shell-and-tube heat exchangers with one shell pass, 2n tube passes: .. math:: \epsilon_1 = 2\left\{1 + C_r + \sqrt{1+C_r^2}\times\frac{1+\exp [-(NTU)_1\sqrt{1+C_r^2}]}{1-\exp[-(NTU)_1\sqrt{1+C_r^2}]}\right\}^{-1} For TEMA E shell-and-tube heat exchangers with more than one shell pass, 2n tube passes (this model assumes each exchanger has an equal share of the overall NTU or said more plainly, the same UA): .. math:: \epsilon = \left[\left(\frac{1-\epsilon_1 C_r}{1-\epsilon_1}\right)^2 -1\right]\left[\left(\frac{1-\epsilon_1 C_r}{1-\epsilon_1}\right)^n - C_r\right]^{-1} For cross-flow (single-pass) heat exchangers with both fluids unmixed, there is an approximate and an exact formula. The approximate one is: .. math:: \epsilon = 1 - \exp\left[\left(\frac{1}{C_r}\right) (NTU)^{0.22}\left\{\exp\left[C_r(NTU)^{0.78}\right]-1\right\}\right] The exact solution for crossflow (fluids unmixed) uses SciPy's quad to perform an integral (there is no analytical integral solution available). :math:`I_0(v)` is the modified Bessel function of the first kind. This formula was developed in [4]_. .. math:: \epsilon = \frac{1}{C_r} - \frac{\exp(-C_r \cdot NTU)}{2(C_r NTU)^2} \int_0^{2 NTU\sqrt{C_r}} \left(1 + NTU - \frac{v^2}{4C_r NTU}\right) \exp\left(-\frac{v^2}{4C_r NTU}\right)v I_0(v) dv For cross-flow (single-pass) heat exchangers with Cmax mixed, Cmin unmixed: .. math:: \epsilon = \left(\frac{1}{C_r}\right)(1 - \exp\left\{-C_r[1-\exp(-NTU)]\right\}) For cross-flow (single-pass) heat exchangers with Cmin mixed, Cmax unmixed: .. math:: \epsilon = 1 - \exp(-C_r^{-1}\{1 - \exp[-C_r(NTU)]\}) For cases where `Cr` = 0, as in an exchanger with latent heat exchange, flow arrangement does not matter: .. math:: \epsilon = 1 - \exp(-NTU) Parameters ---------- NTU : float Thermal Number of Transfer Units [-] Cr : float The heat capacity rate ratio, of the smaller fluid to the larger fluid, [-] subtype : str, optional The subtype of exchanger; one of 'counterflow', 'parallel', 'crossflow' 'crossflow approximate', 'crossflow, mixed Cmin', 'crossflow, mixed Cmax', 'boiler', 'condenser', 'S&T' n_shell_tube : None or int, optional The number of shell and tube exchangers in a row, [-] Returns ------- effectiveness : float The thermal effectiveness of the heat exchanger, [-] Notes ----- Once the effectiveness of the exchanger has been calculated, the total heat transferred can be calculated according to the following formulas, depending on which stream temperatures are known: If the inlet temperatures for both sides are known: .. math:: Q=\epsilon C_{min}(T_{h,i}-T_{c,i}) If the outlet temperatures for both sides are known: .. math:: Q = \frac{\epsilon C_{min}C_{hot}C_{cold}(T_{c,o}-T_{h,o})} {\epsilon C_{min}(C_{hot} +C_{cold}) - (C_{hot}C_{cold}) } If the hot inlet and cold outlet are known: .. math:: Q = \frac{\epsilon C_{min}C_c(T_{co}-T_{hi})}{\epsilon C_{min}-C_c} If the hot outlet stream and cold inlet stream are known: .. math:: Q = \frac{\epsilon C_{min}C_h(T_{ci}-T_{ho})}{\epsilon C_{min}-C_h} If the inlet and outlet conditions for a single side are known, the effectiveness wasn't needed for it to be calculated. For completeness, the formulas are as follows: .. math:: Q = C_c(T_{c,o} - T_{c,i}) = C_h(T_{h,i} - T_{h,o}) There is also a term called :math:`Q_{max}`, which is the heat which would have been transferred if the effectiveness was 1. It is calculated as follows: .. math:: Q_{max} = \frac{Q}{\text{effectiveness}} Examples -------- Worst case, parallel flow: >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='parallel') 0.5881156068417585 Crossflow, somewhat higher effectiveness: >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='crossflow') 0.84448217997 Counterflow, better than either crossflow or parallel flow: >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='counterflow') 0.920670368605 One shell and tube heat exchanger gives worse performance than counterflow, but they are designed to be economical and compact which a counterflow exchanger would not be. As the number of shells approaches infinity, the counterflow result is obtained exactly. >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='S&T') 0.683497704431 >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='S&T', n_shell_tube=50) 0.920505870278 Overall case of rating an existing heat exchanger where a known flowrate of steam and oil are contacted in crossflow, with the steam side mixed (example 10-9 in [3]_): >>> U = 275 # W/m^2/K >>> A = 10.82 # m^2 >>> Cp_oil = 1900 # J/kg/K >>> Cp_steam = 1860 # J/kg/K >>> m_steam = 5.2 # kg/s >>> m_oil = 0.725 # kg/s >>> Thi = 130 # °C >>> Tci = 15 # °C >>> Cmin = calc_Cmin(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> Cmax = calc_Cmax(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> Cr = calc_Cr(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> NTU = NTU_from_UA(UA=U*A, Cmin=Cmin) >>> eff = effectiveness_from_NTU(NTU=NTU, Cr=Cr, subtype='crossflow, mixed Cmax') >>> Q = eff*Cmin*(Thi - Tci) >>> Tco = Tci + Q/(m_oil*Cp_oil) >>> Tho = Thi - Q/(m_steam*Cp_steam) >>> Cmin, Cmax, Cr (1377.5, 9672.0, 0.1424214226633) >>> NTU, eff, Q (2.16007259528, 0.831218036142, 131675.3271504) >>> Tco, Tho (110.5900741563, 116.3859256461) Alternatively, if only the outlet temperatures had been known: >>> Tco = 110.59007415639887 >>> Tho = 116.38592564614977 >>> Cc, Ch = Cmin, Cmax # In this case but not always >>> Q = eff*Cmin*Cc*Ch*(Tco - Tho)/(eff*Cmin*(Cc+Ch) - Ch*Cc) >>> Thi = Tho + Q/Ch >>> Tci = Tco - Q/Cc >>> Q, Tci, Thi (131675.3271504, 14.99999999999, 130.0000000000) References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [2] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [3] Holman, Jack. Heat Transfer. 10th edition. Boston: McGraw-Hill Education, 2009. .. [4] Triboix, Alain. "Exact and Approximate Formulas for Cross Flow Heat Exchangers with Unmixed Fluids." International Communications in Heat and Mass Transfer 36, no. 2 (February 1, 2009): 121-24. doi:10.1016/j.icheatmasstransfer.2008.10.012. """ if Cr > 1: raise ValueError("Heat capacity rate must be less than 1 by definition.") if subtype == "counterflow": if Cr < 1: return (1. - exp(-NTU*(1. - Cr)))/(1. - Cr*exp(-NTU*(1. - Cr))) elif Cr == 1: return NTU/(1. + NTU) elif subtype == "parallel": return (1. - exp(-NTU*(1. + Cr)))/(1. + Cr) elif "S&T" == subtype: # str_shells = subtype.split('S&T')[0] shells = n_shell_tube if n_shell_tube is not None else 1 NTU = NTU/shells x0 = sqrt(1. + Cr*Cr) x1 = exp(-NTU*x0) top = 1. + x1 bottom = 1. - x1 effectiveness = 2./(1. + Cr + x0*top/bottom) if shells > 1: # this applies to crossflow also according to Efficiency and Effectiveness of Heat Exchanger Series equation 21 term = ((1. - effectiveness*Cr)/(1. - effectiveness))**shells effectiveness = (term - 1.)/(term - Cr) return effectiveness elif subtype == "crossflow": t0 = 1.0/(4.*Cr*NTU) res, err = quad(crossflow_effectiveness_to_int, 0, 2.*NTU*sqrt(Cr), args=(NTU, t0,)) int_term = res CrNTU = Cr*NTU return 1./Cr - exp(-CrNTU)/(2.*CrNTU*CrNTU)*int_term elif subtype == "crossflow approximate": return 1. - exp(1./Cr*NTU**0.22*(exp(-Cr*NTU**0.78) - 1.)) elif subtype == "crossflow, mixed Cmin": return 1. -exp(-1.0/Cr*(1. - exp(-Cr*NTU))) elif subtype == "crossflow, mixed Cmax": return (1./Cr)*(1. - exp(-Cr*(1. - exp(-NTU)))) elif subtype in ("boiler", "condenser"): return 1. - exp(-NTU) else: raise ValueError("Input heat exchanger type not recognized") def NTU_from_effectiveness(effectiveness, Cr, subtype="counterflow", n_shell_tube=None): r"""Returns the Number of Transfer Units of a heat exchanger at a specified heat capacity rate, effectiveness, and configuration. The following configurations are supported: * Counterflow (ex. double-pipe) * Parallel (ex. double pipe inefficient configuration) * Shell and tube exchangers with even numbers of tube passes, one or more shells in series (TEMA E (one pass shell) only) * Crossflow, single pass, fluids unmixed * Crossflow, single pass, Cmax mixed, Cmin unmixed * Crossflow, single pass, Cmin mixed, Cmax unmixed * Boiler or condenser These situations are normally not those which occur in real heat exchangers, but are useful for academic purposes. More complicated expressions are available for other methods. These equations are confirmed in [1]_, [2]_, and [3]_. For parallel flow heat exchangers: .. math:: NTU = -\frac{\ln[1 - \epsilon(1+C_r)]}{1+C_r} For counterflow heat exchangers: .. math:: NTU = \frac{1}{C_r-1}\ln\left(\frac{\epsilon-1}{\epsilon C_r-1}\right) .. math:: NTU = \frac{\epsilon}{1-\epsilon} \text{ if } C_r = 1 For TEMA E shell-and-tube heat exchangers with one shell pass, 2n tube passes: .. math:: (NTU)_1 = -(1 + C_r^2)^{-0.5}\ln\left(\frac{E-1}{E+1}\right) .. math:: E = \frac{2/\epsilon_1 - (1 + C_r)}{(1 + C_r^2)^{0.5}} For TEMA E shell-and-tube heat exchangers with more than one shell pass, 2n tube passes (this model assumes each exchanger has an equal share of the overall NTU or said more plainly, the same UA): .. math:: \epsilon_1 = \frac{F-1}{F-C_r} .. math:: F = \left(\frac{\epsilon C_r-1}{\epsilon-1}\right)^{1/n} .. math:: NTU = n(NTU)_1 For cross-flow (single-pass) heat exchangers with both fluids unmixed, there is no analytical solution. However, the function is monotonically increasing, and a closed-form solver is implemented as 'crossflow approximate', guaranteed to solve for :math:`10^{-7} < NTU < 10^5`. The exact solution for 'crossflow' uses the approximate solution's initial guess as a starting point for Newton's method. Some issues are noted at effectivenesses higher than 0.9 and very high NTUs, because the numerical integral term approaches 1 too quickly. For cross-flow (single-pass) heat exchangers with Cmax mixed, Cmin unmixed: .. math:: NTU = -\ln\left[1 + \frac{1}{C_r}\ln(1 - \epsilon C_r)\right] For cross-flow (single-pass) heat exchangers with Cmin mixed, Cmax unmixed: .. math:: NTU = -\frac{1}{C_r}\ln[C_r\ln(1-\epsilon)+1] For cases where `Cr` = 0, as in an exchanger with latent heat exchange, flow arrangement does not matter: .. math:: NTU = -\ln(1-\epsilon) Parameters ---------- effectiveness : float The thermal effectiveness of the heat exchanger, [-] Cr : float The heat capacity rate ratio, of the smaller fluid to the larger fluid, [-] subtype : str, optional The subtype of exchanger; one of 'counterflow', 'parallel', 'crossflow' 'crossflow approximate', 'crossflow, mixed Cmin', 'crossflow, mixed Cmax', 'boiler', 'condenser', 'S&T'. n_shell_tube : None or int, optional The number of shell and tube exchangers in a row, [-] Returns ------- NTU : float Thermal Number of Transfer Units [-] Notes ----- Unlike :obj:`ht.hx.effectiveness_from_NTU`, not all inputs can calculate the NTU - many exchanger types have effectiveness limits below 1 which depend on `Cr` and the number of shells in the case of heat exchangers. If an impossible input is given, an error will be raised and the maximum possible effectiveness will be printed. >>> NTU_from_effectiveness(.99, Cr=.7, subtype='5S&T') # doctest: +SKIP Traceback (most recent call last): Exception: The specified effectiveness is not physically possible for this configuration; the maximum effectiveness possible is 0.974122977755. Examples -------- Worst case, parallel flow: >>> NTU_from_effectiveness(effectiveness=0.5881156068417585, Cr=0.7, subtype='parallel') 5.000000000000 Crossflow, somewhat higher effectiveness: >>> NTU_from_effectiveness(effectiveness=0.8444821799748551, Cr=0.7, subtype='crossflow') 5.000000000000 Counterflow, better than either crossflow or parallel flow: >>> NTU_from_effectiveness(effectiveness=0.9206703686051108, Cr=0.7, subtype='counterflow') 5.0 Shell and tube exchangers: >>> NTU_from_effectiveness(effectiveness=0.6834977044311439, Cr=0.7, subtype='S&T') 5.000000000000 >>> NTU_from_effectiveness(effectiveness=0.9205058702789254, Cr=0.7, n_shell_tube=50, subtype='S&T') 4.999999999999 Overall case of rating an existing heat exchanger where a known flowrate of steam and oil are contacted in crossflow, with the steam side mixed, known inlet and outlet temperatures, and unknown UA (based on example 10-8 in [3]_): >>> Cp_oil = 1900 # J/kg/K >>> Cp_steam = 1860 # J/kg/K >>> m_steam = 5.2 # kg/s >>> m_oil = 1.45 # kg/s >>> Thi = 130 # °C >>> Tci = 15 # °C >>> Tco = 85 # °C # Design specification >>> Q = Cp_oil*m_oil*(Tci-Tco) >>> dTh = Q/(m_steam*Cp_steam) >>> Tho = Thi + dTh >>> Cmin = calc_Cmin(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> Cmax = calc_Cmax(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> Cr = calc_Cr(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil) >>> effectiveness = -Q/Cmin/(Thi-Tci) >>> NTU = NTU_from_effectiveness(effectiveness, Cr, subtype='crossflow, mixed Cmax') >>> UA = UA_from_NTU(NTU, Cmin) >>> U = 200 # Assume this was calculated; would actually need to be obtained iteratively as U depends on the exchanger geometry >>> A = UA/U >>> Tho, Cmin, Cmax, Cr (110.06100082712986, 2755.0, 9672.0, 0.2848428453267163) >>> effectiveness, NTU, UA, A (0.608695652173, 1.1040839095, 3041.75117083, 15.2087558541) References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [2] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [3] Holman, Jack. Heat Transfer. 10th edition. Boston: McGraw-Hill Education, 2009. """ if Cr > 1: raise ValueError("Heat capacity rate must be less than 1 by definition.") if subtype == "counterflow": # [2]_ gives the expression 1./(1-Cr)*log((1-Cr*eff)/(1-eff)), but # this is just the same equation rearranged differently. if Cr < 1: return 1./(Cr - 1.)*log((effectiveness - 1.)/(effectiveness*Cr - 1.)) elif Cr == 1: return effectiveness/(1. - effectiveness) elif subtype == "parallel": if effectiveness*(1. + Cr) > 1: raise ValueError("The specified effectiveness is not physically " "possible for this configuration; the maximum effectiveness " "possible is %s." % (1./(Cr + 1.))) # numba: delete # ) # numba: uncomment return -log(1. - effectiveness*(1. + Cr))/(1. + Cr) elif "S&T" == subtype: # [2]_ gives the expression # D = (1+Cr**2)**0.5 # 1/D*log((2-eff*(1+Cr-D))/(2-eff*(1+Cr + D))) # This is confirmed numerically to be the same equation rearranged # differently shells = n_shell_tube if n_shell_tube is not None else 1 F = ((effectiveness*Cr - 1.)/(effectiveness - 1.))**(1./shells) e1 = (F - 1.)/(F - Cr) E = (2./e1 - (1. + Cr))/(1. + Cr**2)**0.5 if (E - 1.)/(E + 1.) <= 0: # Derived with SymPy max_effectiveness = (-((-Cr + sqrt(Cr**2 + 1) + 1)/(Cr + sqrt(Cr**2 + 1) - 1))**shells + 1)/(Cr - ((-Cr + sqrt(Cr**2 + 1) + 1)/(Cr + sqrt(Cr**2 + 1) - 1))**shells) raise ValueError("The specified effectiveness is not physically " # numba: delete f"possible for this configuration; the maximum effectiveness possible is {max_effectiveness}.") # numba: delete # raise ValueError("Fail") # numba: uncomment NTU = -(1. + Cr*Cr)**-0.5*log((E - 1.)/(E + 1.)) return shells*NTU elif subtype == "crossflow": # Can't use a bisect solver here because at high NTU there's a derivative of 0 # due to the integral term not changing when it's very near one guess = NTU_from_effectiveness(effectiveness, Cr, "crossflow approximate") def to_solve(NTU, Cr, effectiveness): return effectiveness_from_NTU(NTU, Cr, subtype="crossflow") - effectiveness return secant(to_solve, guess, args=(Cr, effectiveness)) elif subtype == "crossflow approximate": # This will fail if NTU is more than 10,000 or less than 1E-7, but # this is extremely unlikely to occur in normal usage. # Maple and SymPy and Wolfram Alpha all failed to obtain an exact # analytical expression even with coefficients for 0.22 and 0.78 or # with an explicit value for Cr. The function has been plotted, # and appears to be monotonic - there is only one solution. def to_solve(NTU, Cr, effectiveness): return (1. - exp(1./Cr*NTU**0.22*(exp(-Cr*NTU**0.78) - 1.))) - effectiveness return secant(to_solve, x0=2.5, low=1e-7, high=1e5, bisection=True, require_eval=True, args=(Cr, effectiveness)) elif subtype == "crossflow, mixed Cmin": if Cr*log(1. - effectiveness) < -1: raise ValueError("The specified effectiveness is not physically \ possible for this configuration; the maximum effectiveness possible is %s." % (1. - exp(-1./Cr))) return -1./Cr*log(Cr*log(1. - effectiveness) + 1.) elif subtype == "crossflow, mixed Cmax": if 1./Cr*log(1. - effectiveness*Cr) < -1: raise ValueError("The specified effectiveness is not physically \ possible for this configuration; the maximum effectiveness possible is %s." % ((exp(Cr) - 1.0)*exp(-Cr)/Cr)) return -log(1. + 1./Cr*log(1. - effectiveness*Cr)) elif subtype in ["boiler", "condenser"]: return -log(1. - effectiveness) else: raise ValueError("Input heat exchanger type not recognized") def calc_Cmin(mh: float, mc: float, Cph: float, Cpc: int) -> float: r"""Returns the heat capacity rate for the minimum stream having flows `mh` and `mc`, with averaged heat capacities `Cph` and `Cpc`. .. math:: C_c = m_cC_{p,c} C_h = m_h C_{p,h} C_{min} = \min(C_c, C_h) Parameters ---------- mh : float Mass flow rate of hot stream, [kg/s] mc : float Mass flow rate of cold stream, [kg/s] Cph : float Averaged heat capacity of hot stream, [J/kg/K] Cpc : float Averaged heat capacity of cold stream, [J/kg/K] Returns ------- Cmin : float The heat capacity rate of the smaller fluid, [W/K] Notes ----- Used with the effectiveness method for heat exchanger design. Technically, it doesn't matter if the hot and cold streams are in the right order for the input, but it is easiest to use this function when the order is specified. Examples -------- >>> calc_Cmin(mh=22., mc=5.5, Cph=2200, Cpc=4400.) 24200.0 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Ch = mh*Cph Cc = mc*Cpc return min(Ch, Cc) def calc_Cmax(mh: float, mc: float, Cph: float, Cpc: int) -> float: r"""Returns the heat capacity rate for the maximum stream having flows `mh` and `mc`, with averaged heat capacities `Cph` and `Cpc`. .. math:: C_c = m_cC_{p,c} .. math:: C_h = m_h C_{p,h} .. math:: C_{max} = \max(C_c, C_h) Parameters ---------- mh : float Mass flow rate of hot stream, [kg/s] mc : float Mass flow rate of cold stream, [kg/s] Cph : float Averaged heat capacity of hot stream, [J/kg/K] Cpc : float Averaged heat capacity of cold stream, [J/kg/K] Returns ------- Cmax : float The heat capacity rate of the larger fluid, [W/K] Notes ----- Used with the effectiveness method for heat exchanger design. Technically, it doesn't matter if the hot and cold streams are in the right order for the input, but it is easiest to use this function when the order is specified. Examples -------- >>> calc_Cmax(mh=22., mc=5.5, Cph=2200, Cpc=4400.) 48400.0 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Ch = mh*Cph Cc = mc*Cpc return max(Ch, Cc) def calc_Cr(mh: float, mc: float, Cph: float, Cpc: int) -> float: r"""Returns the heat capacity rate ratio for a heat exchanger having flows `mh` and `mc`, with averaged heat capacities `Cph` and `Cpc`. .. math:: C_r=C^*=\frac{C_{min}}{C_{max}} Parameters ---------- mh : float Mass flow rate of hot stream, [kg/s] mc : float Mass flow rate of cold stream, [kg/s] Cph : float Averaged heat capacity of hot stream, [J/kg/K] Cpc : float Averaged heat capacity of cold stream, [J/kg/K] Returns ------- Cr : float The heat capacity rate ratio, of the smaller fluid to the larger fluid, [-] Notes ----- Used with the effectiveness method for heat exchanger design. Technically, it doesn't matter if the hot and cold streams are in the right order for the input, but it is easiest to use this function when the order is specified. Examples -------- >>> calc_Cr(mh=22., mc=5.5, Cph=2200, Cpc=4400.) 0.5 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ Ch = mh*Cph Cc = mc*Cpc Cmin = min(Ch, Cc) Cmax = max(Ch, Cc) return Cmin/Cmax def NTU_from_UA(UA: float, Cmin: float) -> float: r"""Returns the Number of Transfer Units for a heat exchanger having `UA`, and with `Cmin` heat capacity rate. .. math:: NTU = \frac{UA}{C_{min}} Parameters ---------- UA : float Combined Area-heat transfer coefficient term, [W/K] Cmin : float The heat capacity rate of the smaller fluid, [W/K] Returns ------- NTU : float Thermal Number of Transfer Units [-] Notes ----- Used with the effectiveness method for heat exchanger design. Examples -------- >>> NTU_from_UA(4400., 22.) 200.0 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return UA/Cmin def UA_from_NTU(NTU: float, Cmin: float) -> float: r"""Returns the combined area-heat transfer term for a heat exchanger having a specified `NTU`, and with `Cmin` heat capacity rate. .. math:: UA = NTU C_{min} Parameters ---------- NTU : float Thermal Number of Transfer Units [-] Cmin : float The heat capacity rate of the smaller fluid, [W/K] Returns ------- UA : float Combined area-heat transfer coefficient term, [W/K] Notes ----- Used with the effectiveness method for heat exchanger design. Examples -------- >>> UA_from_NTU(200., 22.) 4400.0 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ return NTU*Cmin def P_NTU_Pp(x, y): r"""Basic helper calculator which accepts a transformed R1 and NTU1 as inputs for a common term used in the calculation of the P-NTU method for plate exchangers. Returns a value which is normally used in other calculations before the actual P1 is calculated. .. math:: P_p(x, y) = \frac{1 - \exp[-x(1 + y)]}{1 + y} Parameters ---------- x : float A modification of NTU1, the Thermal Number of Transfer Units [-] y : float A modification of R1, the thermal effectiveness [-] Returns ------- z : float Just another term in the calculation, [-] Notes ----- Used with the P-NTU plate method for heat exchanger design. At y = -1, this function has a ZeroDivisionError but can be evaluated at the limit to be z = x Examples -------- >>> P_NTU_Pp(5, .4) 0.713634370024 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ if y == -1.0: return x return (1. - exp(-x*(1. + y)))/(1. + y) def P_NTU_Pc(x, y): r"""Basic helper calculator which accepts a transformed R1 and NTU1 as inputs for a common term used in the calculation of the P-NTU method for plate exchangers. Returns a value which is normally used in other calculations before the actual P1 is calculated. Nominally used in counterflow calculations .. math:: P_c(x, y) = \frac{1 - \exp[-x(1 - y)]}{1 - y\exp[-x(1 - y)]} Parameters ---------- x : float A modification of NTU1, the Thermal Number of Transfer Units [-] y : float A modification of R1, the thermal effectiveness [-] Returns ------- z : float Just another term in the calculation, [-] Notes ----- Used with the P-NTU plate method for heat exchanger design. At y =-1, this function has a ZeroDivisionError but can be evaluated at the limit to be :math:`z = \frac{x}{1+x}`. Examples -------- >>> P_NTU_Pc(5, .7) 0.920670368605 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ term = exp(-x*(1. - y)) if (1. - y*term) == 0.0: return x/(1. + x) return (1. - term)/(1. - y*term) def effectiveness_NTU_method(mh, mc, Cph, Cpc, subtype="counterflow", Thi=None, Tho=None, Tci=None, Tco=None, UA=None, n_shell_tube=None): r"""Wrapper for the various effectiveness-NTU method function calls, which can solve a heat exchanger. The heat capacities and mass flows of each stream and the type of the heat exchanger are always required. As additional inputs, one combination of the following inputs is required: * Three of the four inlet and outlet stream temperatures. * Temperatures for the cold outlet and hot inlet and UA * Temperatures for the cold outlet and hot outlet and UA * Temperatures for the cold inlet and hot inlet and UA * Temperatures for the cold inlet and hot outlet and UA Parameters ---------- mh : float Mass flow rate of hot stream, [kg/s] mc : float Mass flow rate of cold stream, [kg/s] Cph : float Averaged heat capacity of hot stream, [J/kg/K] Cpc : float Averaged heat capacity of cold stream, [J/kg/K] subtype : str, optional The subtype of exchanger; one of 'counterflow', 'parallel', 'crossflow' 'crossflow, mixed Cmin', 'crossflow, mixed Cmax', 'boiler', 'condenser', 'S&T', or 'nS&T' where n is the number of shell and tube exchangers in a row Thi : float, optional Inlet temperature of hot fluid, [K] Tho : float, optional Outlet temperature of hot fluid, [K] Tci : float, optional Inlet temperature of cold fluid, [K] Tco : float, optional Outlet temperature of cold fluid, [K] UA : float, optional Combined Area-heat transfer coefficient term, [W/K] n_shell_tube : None or int, optional The number of shell and tube exchangers in a row, [-] Returns ------- results : dict * Q : Heat exchanged in the heat exchanger, [W] * UA : Combined area-heat transfer coefficient term, [W/K] * Cr : The heat capacity rate ratio, of the smaller fluid to the larger fluid, [-] * Cmin : The heat capacity rate of the smaller fluid, [W/K] * Cmax : The heat capacity rate of the larger fluid, [W/K] * effectiveness : The thermal effectiveness of the heat exchanger, [-] * NTU : Thermal Number of Transfer Units [-] * Thi : Inlet temperature of hot fluid, [K] * Tho : Outlet temperature of hot fluid, [K] * Tci : Inlet temperature of cold fluid, [K] * Tco : Outlet temperature of cold fluid, [K] See Also -------- effectiveness_from_NTU NTU_from_effectiveness Examples -------- Solve a heat exchanger to determine UA and effectiveness given the configuration, flows, subtype, the cold inlet/outlet temperatures, and the hot stream inlet temperature. >>> from pprint import pprint >>> pprint(effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, ... subtype='crossflow, mixed Cmax', Tci=15, Tco=85, Thi=130)) {'Cmax': 9672.0, 'Cmin': 2755.0, 'Cr': 0.284842845326, 'NTU': 1.104083909, 'Q': 192850.0, 'Tci': 15, 'Tco': 85, 'Thi': 130, 'Tho': 110.0610008271, 'UA': 3041.75117083, 'effectiveness': 0.608695652173} Solve the same heat exchanger with the UA specified, and known inlet temperatures: >>> pprint(effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, ... subtype='crossflow, mixed Cmax', Tci=15, Thi=130, UA=3041.75)) {'Cmax': 9672.0, 'Cmin': 2755.0, 'Cr': 0.284842845326, 'NTU': 1.104083484573, 'Q': 192849.9631022, 'Tci': 15, 'Tco': 84.9999866069, 'Thi': 130, 'Tho': 110.0610046420, 'UA': 3041.75, 'effectiveness': 0.608695535712} """ Cmin = calc_Cmin(mh=mh, mc=mc, Cph=Cph, Cpc=Cpc) Cmax = calc_Cmax(mh=mh, mc=mc, Cph=Cph, Cpc=Cpc) Cr = calc_Cr(mh=mh, mc=mc, Cph=Cph, Cpc=Cpc) Cc = mc*Cpc Ch = mh*Cph if UA is not None: NTU = NTU_from_UA(UA=UA, Cmin=Cmin) effectiveness = eff = effectiveness_from_NTU(NTU=NTU, Cr=Cr, n_shell_tube=n_shell_tube, subtype=subtype) possible_inputs = [(Tci, Thi), (Tci, Tho), (Tco, Thi), (Tco, Tho)] if not any(i for i in possible_inputs if None not in i): raise ValueError("One set of (Tci, Thi), (Tci, Tho), (Tco, Thi), or (Tco, Tho) are required along with UA.") if Thi is not None and Tci is not None: Q = eff*Cmin*(Thi - Tci) elif Tho is not None and Tco is not None: Q = eff*Cmin*Cc*Ch*(Tco - Tho)/(eff*Cmin*(Cc+Ch) - Ch*Cc) elif Thi is not None and Tco is not None: Q = Cmin*Cc*eff*(Tco-Thi)/(eff*Cmin - Cc) elif Tho is not None and Tci is not None: Q = Cmin*Ch*eff*(Tci-Tho)/(eff*Cmin - Ch) # The following is not used as it was decided to require complete temperature information # elif Tci and Tco: # Q = Cc*(Tco - Tci) # elif Tho and Thi: # Q = Ch*(Thi-Tho) # Compute the remaining temperatures with the fewest lines of code if Tci is not None and Tco is None: Tco = Tci + Q/(Cc) else: Tci = Tco - Q/(Cc) if Thi is not None and Tho is None: Tho = Thi - Q/(Ch) else: Thi = Tho + Q/(Ch) elif UA is None: # Case where we're solving for UA # Three temperatures are required # Ensures all four temperatures are set and Q is calculated if Thi is not None and Tho is not None: Q = mh*Cph*(Thi-Tho) if Tci is not None and Tco is None: Tco = Tci + Q/(mc*Cpc) elif Tco is not None and Tci is None: Tci = Tco - Q/(mc*Cpc) elif Tco is not None and Tci is not None: Q2 = mc*Cpc*(Tco-Tci) if abs((Q-Q2)/Q) > 0.01: raise ValueError("The specified heat capacities, mass flows, and temperatures are inconsistent") else: raise ValueError("At least one temperature is required to be specified on the cold side.") elif Tci is not None and Tco is not None: Q = mc*Cpc*(Tco-Tci) if Thi is not None and Tho is None: Tho = Thi - Q/(mh*Cph) elif Tho is not None and Thi is None: Thi = Tho + Q/(mh*Cph) else: raise ValueError("At least one temperature is required to be specified on the cold side.") else: raise ValueError("Three temperatures are required to be specified " "when solving for UA") effectiveness = Q/Cmin/(Thi-Tci) NTU = NTU_from_effectiveness(effectiveness, Cr, n_shell_tube=n_shell_tube, subtype=subtype) UA = UA_from_NTU(NTU, Cmin) return {"Q": Q, "UA": UA, "Cr":Cr, "Cmin": Cmin, "Cmax":Cmax, "effectiveness": effectiveness, "NTU": NTU, "Thi": Thi, "Tho": Tho, "Tci": Tci, "Tco": Tco} def temperature_effectiveness_air_cooler(R1: float, NTU1: float, rows: int, passes: int, coerce: bool=True) -> float: r"""Returns temperature effectiveness `P1` of an air cooler with a specified heat capacity ratio, number of transfer units `NTU1`, number of rows `rows`, and number of passes `passes`. The supported cases are as follows: * N rows 1 pass * N row N pass (up to N = 5) * 4 rows 2 passes For N rows 1 passes ([2]_, shown in [1]_ and [3]_): .. math:: P = \frac{1}{R} \left\{1 - \left[\frac{N\exp(NKR)} {1 + \sum_{i=1}^{N-1}\sum_{j=0}^i {{i}\choose{j}}K^j \exp(-(i-j)NTU/N) \sum_{k=0}^j \frac{(NKR)^k}{k!}}\right]^{-1}\right\} For 2 rows 2 passes (cited as from [4]_ in [1]_): .. math:: P_1 = \frac{1}{R}\left(1 -\frac{1}{\xi}\right) .. math:: \xi = \frac{K}{2} + \left(1 - \frac{K}{2}\right)\exp(2KR) .. math:: K = 1 - \exp\left(\frac{-NTU}{2}\right) For 3 rows / 3 passes (cited as from [4]_ in [1]_): .. math:: \xi = K\left[1 - \frac{K}{4} - RK\left(1 - \frac{K}{2}\right)\right] \exp(KR) + \exp(3KR)\left(1 - \frac{K}{2}\right)^2 .. math:: K = 1 - \exp\left(\frac{-NTU}{3}\right) For 4 rows / 4 passes (cited as from [4]_ in [1]_): .. math:: \xi = \frac{K}{2}\left(1 - \frac{K}{2} + \frac{K^2}{4}\right) + K\left(1 - \frac{K}{2}\right) \left[1 - \frac{R}{8}K\left(1 - \frac{K}{2}\right)\exp(2KR)\right] + \exp(4KR)\left(1 - \frac{K}{2}\right)^3 .. math:: K = 1 - \exp\left(\frac{-NTU}{4}\right) For 5 rows / 5 passes (cited as from [4]_ in [1]_): .. math:: \xi = \left\{K \left(1 - \frac{3}{4}K + \frac{K^2}{2}- \frac{K^3}{8} \right) - RK^2\left[1 -K + \frac{3}{4}K^2 - \frac{1}{4}K^3 - \frac{R}{2}K^2\left(1 - \frac{K}{2}\right)^2\right]\right\}\exp(KR) + \left[K\left(1 - \frac{3}{4}K + \frac{1}{16}K^3\right) - 3RK^2\left(1 - \frac{K}{2}\right)^3\right]\exp(3KR)+ \left(1 - \frac{K}{2}\right)^4 \exp(5KR) For 4 rows / 2 passes (cited as from [4]_ in [1]_): .. math:: P_1 = \frac{1}{R}\left(1 -\frac{1}{\xi}\right) .. math:: \xi = \left\{\frac{R}{2}K^3[4 - K + 2RK^2] + \exp(4KR) + K\left[1 - \frac{K}{2} + \frac{K^2}{8}\right] \left[1 - \exp(4KR)\right] \right\}\frac{1}{(1+RK^2)^2} .. math:: K = 1 - \exp\left(\frac{-NTU}{4}\right) Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (process fluid side) [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (process fluid side) [-] rows : int Number of rows of tubes in the air cooler [-] passes : int Number of passes the process fluid undergoes [-] coerce : bool If True, the number of passes or rows, if otherwise unsupported, will be replaced with a similar number to allow the calculation to proceed, [-] Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (process fluid side) [-] Notes ----- For the 1-pass case, the exact formula used can take a while to compute for large numbers of tube rows; 100 us for 20 rows, 1 ms for 50 rows. Floating point rounding behavior can also be an issue for large numbers of tube passes, leading to thermal effectivenesses larger than one being returned: >>> temperature_effectiveness_air_cooler(1e-10, 100, rows=150, passes=1.0) 1.000026728092962 Furthermore, as a factorial of the number of tube counts is used, there comes a point where standard floats are not able to hold the intermediate calculations values and an error will occur: >>> temperature_effectiveness_air_cooler(.5, 1.1, rows=200, passes=1.0) Traceback (most recent call last): ... OverflowError: int too large to convert to float Examples -------- >>> temperature_effectiveness_air_cooler(.5, 2, rows=2, passes=2) 0.7523072855817072 References ---------- .. [1] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [2] Schedwill, H., "Thermische Auslegung von Kreuzstromwarmeaustauschern, Fortschr-Ber." VDI Reihe 6 19, VDI, Germany, 1968. .. [3] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1983. .. [4] Nicole, F. J. L.. "Mean temperature difference for heat exchanger design." Council for Scientific and Industrial Research, Special Report Chem. 223, Pretoria, South Africa (1972). """ if passes == 1: N = rows K = 1. - exp(-NTU1/N) NKR1 = N*K*R1 NTU1_N = NTU1/N top = N*exp(N*K*R1) # TODO speed this up # Precalculate integer factorials up to N # Factorial fits in 64 bit int only up to N = 20 # https://stackoverflow.com/questions/62056035/how-to-call-math-factorial-from-numba-with-nopython-mode Np1 = N+1 factorials = [factorial(i) for i in range(N)] K_powers = [K**j for j in range(N+1)] NKR1_powers = [NKR1**k for k in range(N+1)] exp_terms = [exp(i*NTU1_N) for i in range(-N+1, 1)] # Only need to compute one exp, then multiply NKR1_powers_over_factorials = [NKR1_powers[k]/factorials[k] for k in range(N)] # Precompute even more... NKR1_pows_div_factorials = [0] for k in NKR1_powers_over_factorials: NKR1_pows_div_factorials.append(NKR1_pows_div_factorials[-1]+k) NKR1_pows_div_factorials.pop(0) final_speed = [0.0]*N for i in range(N): final_speed[i] = K_powers[i]*NKR1_pows_div_factorials[i] # final_speed = [i*j for i, j in zip(K_powers, NKR1_pows_div_factorials)] tot = 0. for i in range(1, N): for j in range(i+1): # can't optimize the factorial prod = factorials[i]/(factorials[i-j]*factorials[j]) tot1 = prod*exp_terms[j-i-1] tot += tot1*final_speed[j] return 1./R1*(1. - 1./(top/(1.+tot))) elif rows == passes == 2: K = 1. - exp(-0.5*NTU1) xi = 0.5*K + (1. - 0.5*K)*exp(2.*K*R1) return 1./R1*(1. - 1./xi) elif rows == passes == 3: K = 1. - exp(-NTU1/3.) xi = (K*(1. - 0.25*K - R1*K*(1. - 0.5*K))*exp(K*R1) + exp(3.*K*R1)*(1. - 0.5*K)**2) return 1./R1*(1. - 1./xi) elif rows == passes == 4: K = 1. - exp(-0.25*NTU1) xi = (0.5*K*(1. - 0.5*K + 0.25*K**2) + K*(1. - 0.5*K)*(1. - 0.125*R1*K*(1. - 0.5*K)*exp(2.*K*R1)) + exp(4.*K*R1)*(1. - 0.5*K)**3) return 1./R1*(1. - 1./xi) elif rows == passes == 5: K = 1. - exp(-0.2*NTU1) K2 = K*K K3 = K2*K xi = (K*(1. - .75*K + .5*K2 - .125*K3) - R1*K2*(1. - K + .75*K2 - .25*K3 - .5*R1*K2*(1. - .5*K)**2))*exp(K*R1) xi += ((K*(1. - .75*K + 1/16.*K3) - 3*R1*K2*(1. - .5*K)**3) *exp(3*K*R1) + (1. - .5*K)**4*exp(5*K*R1)) return 1./R1*(1. - 1./xi) elif rows == 4 and passes == 2: K = 1. - exp(-0.25*NTU1) xi = (0.5*R1*K**3*(4. - K + 2.*R1*K**2) + exp(4.*K*R1) + K*(1. - 0.5*K + 0.125*K**2)*(1 - exp(4.*K*R1)))*(1. + R1*K**2)**-2 return 1./R1*(1. - 1./xi) else: if coerce: if passes > rows: passes = rows # bad user input - replace with an exception? new_passes, new_rows = passes, rows # Domain reduction if passes > 5: new_passes = passes = 5 if rows > 5: new_rows = rows = 5 if rows -1 == passes: new_rows, new_passes = rows -1, passes elif (passes in (2, 3, 5)) and rows >= 4: new_rows, new_passes = 4, 2 return temperature_effectiveness_air_cooler(R1=R1, NTU1=NTU1, rows=new_rows, passes=new_passes) else: raise ValueError("Number of passes and rows not supported.") def temperature_effectiveness_basic(R1: float, NTU1: float, subtype: str="crossflow") -> float: r"""Returns temperature effectiveness `P1` of a heat exchanger with a specified heat capacity ratio, number of transfer units `NTU1`, and of type `subtype`. This function performs the calculations for the basic cases, not actual shell-and-tube exchangers. The supported cases are as follows: * Counterflow (ex. double-pipe) * Parallel (ex. double pipe inefficient configuration) * Crossflow, single pass, fluids unmixed * Crossflow, single pass, fluid 1 mixed, fluid 2 unmixed * Crossflow, single pass, fluid 2 mixed, fluid 1 unmixed * Crossflow, single pass, both fluids mixed For parallel flow heat exchangers (this configuration is symmetric): .. math:: P_1 = \frac{1 - \exp[-NTU_1(1+R_1)]}{1 + R_1} For counterflow heat exchangers (this configuration is symmetric): .. math:: P_1 = \frac{1 - \exp[-NTU_1(1-R_1)]}{1 - R_1 \exp[-NTU_1(1-R_1)]} For cross-flow (single-pass) heat exchangers with both fluids unmixed (this configuration is symmetric), there are two solutions available; a frequently cited approximation and an exact solution which uses a numerical integration developed in [4]_. The approximate solution is: .. math:: P_1 \approx 1 - \exp\left[\frac{NTU_1^{0.22}}{R_1} (\exp(-R_1 NTU_1^{0.78})-1)\right] The exact solution for crossflow (single pass, fluids unmixed) is: .. math:: \epsilon = \frac{1}{R_1} - \frac{\exp(-R_1 \cdot NTU_1)}{2(R_1 NTU_1)^2} \int_0^{2 NTU_1\sqrt{R_1}} \left(1 + NTU_1 - \frac{v^2}{4R_1 NTU_1} \right)\exp\left(-\frac{v^2}{4R_1 NTU_1}\right)v I_0(v) dv For cross-flow (single-pass) heat exchangers with fluid 1 mixed, fluid 2 unmixed: .. math:: P_1 = 1 - \exp\left(-\frac{K}{R_1}\right) .. math:: K = 1 - \exp(-R_1 NTU_1) For cross-flow (single-pass) heat exchangers with fluid 2 mixed, fluid 1 unmixed: .. math:: P_1 = \frac{1 - \exp(-K R_1)}{R_1} .. math:: K = 1 - \exp(-NTU_1) For cross-flow (single-pass) heat exchangers with both fluids mixed (this configuration is symmetric): .. math:: P_1 = \left(\frac{1}{K_1} + \frac{R_1}{K_2} - \frac{1}{NTU_1}\right)^{-1} .. math:: K_1 = 1 - \exp(-NTU_1) .. math:: K_2 = 1 - \exp(-R_1 NTU_1) Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] subtype : float The type of heat exchanger; one of 'counterflow', 'parallel', 'crossflow', 'crossflow approximate', 'crossflow, mixed 1', 'crossflow, mixed 2', 'crossflow, mixed 1&2'. Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- The crossflow case is an approximation only. There is an actual solution involving an infinite sum. This was implemented, but found to differ substantially so the approximation is used instead. Examples -------- >>> temperature_effectiveness_basic(R1=.1, NTU1=4, subtype='counterflow') 0.9753412729761263 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [4] Triboix, Alain. "Exact and Approximate Formulas for Cross Flow Heat Exchangers with Unmixed Fluids." International Communications in Heat and Mass Transfer 36, no. 2 (February 1, 2009): 121-24. doi:10.1016/j.icheatmasstransfer.2008.10.012. """ if subtype == "counterflow": # Same as TEMA 1 pass if R1 == 1.0: """from sympy import * R1, NTU1 = symbols('R1, NTU1') P1 = (1 - exp(-NTU1*(1 - R1)))/(1 - R1*exp(-NTU1*(1-R1))) limit(P1, R1, 1) """ P1 = -NTU1/(-NTU1 - 1.0) else: P1 = (1.0 - exp(-NTU1*(1 - R1)))/(1.0 - R1*exp(-NTU1*(1-R1))) elif subtype == "parallel": P1 = (1.0 - exp(-NTU1*(1 + R1)))/(1.0 + R1) elif subtype == "crossflow approximate": # This isn't technically accurate, an infinite sum is required # It has been computed from two different sources # but is found not to be within the 1% claimed of this equation P1 = 1.0 - exp(NTU1**0.22/R1*(exp(-R1*NTU1**0.78) - 1.)) elif subtype == "crossflow": # TODO attempt chebyshev approximation of P1 as a function of R1, NTU1 (for stability) R1_NTU1_4_inv = 1.0/(4.*R1*NTU1) int_term = quad(crossflow_effectiveness_to_int, 0.0, 2.*NTU1*R1**0.5, args=(NTU1, R1_NTU1_4_inv))[0] P1 = 1./R1 - exp(-R1*NTU1)/(2.*(R1*NTU1)**2)*int_term elif subtype == "crossflow, mixed 1": # Not symmetric K = 1 - exp(-R1*NTU1) P1 = 1 - exp(-K/R1) elif subtype == "crossflow, mixed 2": # Not symmetric K = 1 - exp(-NTU1) P1 = (1 - exp(-K*R1))/R1 elif subtype == "crossflow, mixed 1&2": K1 = 1. - exp(-NTU1) K2 = 1. - exp(-R1*NTU1) P1 = (1./K1 + R1/K2 - 1./NTU1)**-1 else: raise ValueError("Subtype not recognized.") return P1 def temperature_effectiveness_TEMA_J(R1: float, NTU1: float, Ntp: int) -> float: r"""Returns temperature effectiveness `P1` of a TEMA J type heat exchanger with a specified heat capacity ratio, number of transfer units `NTU1`, and of number of tube passes `Ntp`. The supported cases are as follows: * One tube pass (shell fluid mixed) * Two tube passes (shell fluid mixed, tube pass mixed between passes) * Four tube passes (shell fluid mixed, tube pass mixed between passes) For 1-1 TEMA J shell and tube exchangers, shell and tube fluids mixed: .. math:: P_1 = \frac{1}{R_1}\left[1- \frac{(2-R_1)(2E + R_1 B)}{(2+R_1) (2E - R_1/B)}\right] For 1-2 TEMA J, shell and tube fluids mixed. There are two possible arrangements for the flow and the number of tube passes, but the equation is the same in both: .. math:: P_1 = \left[1 + \frac{R_1}{2} + \lambda B - 2\lambda C D\right]^{-1} .. math:: B = \frac{(A^\lambda +1)}{A^\lambda -1} .. math:: C = \frac{A^{(1 + \lambda)/2}}{\lambda - 1 + (1 + \lambda)A^\lambda} .. math:: D = 1 + \frac{\lambda A^{(\lambda-1)/2}}{A^\lambda -1} .. math:: A = \exp(NTU_1) .. math:: \lambda = (1 + R_1^2/4)^{0.5} For 1-4 TEMA J, shell and tube exchanger with both sides mixed: .. math:: P_1 = \left[1 + \frac{R_1}{4}\left(\frac{1+3E}{1+E}\right) + \lambda B - 2 \lambda C D\right]^{-1} .. math:: B = \frac{A^\lambda +1}{A^\lambda -1} .. math:: C = \frac{A^{(1+\lambda)/2}}{\lambda - 1 + (1 + \lambda)A^\lambda} .. math:: D = 1 + \frac{\lambda A^{(\lambda-1)/2}}{A^\lambda -1} .. math:: A = \exp(NTU_1) .. math:: E = \exp(R_1 NTU_1/2) .. math:: \lambda = (1 + R_1^2/16)^{0.5} Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1, 2, or 4, [-] Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- For numbers of tube passes that are not 1, 2, or 4, an exception is raised. The convention for the formulas in [1]_ and [3]_ are with the shell side as side 1, and the tube side as side 2. [2]_ has formulas with the opposite convention. Examples -------- >>> temperature_effectiveness_TEMA_J(R1=1/3., NTU1=1., Ntp=1) 0.5699085193651295 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ if Ntp == 1: A = exp(NTU1) B = exp(-NTU1*R1/2.) if R1 != 2: P1 = 1./R1*(1. - (2. - R1)*(2.*A + R1*B)/(2. + R1)/(2.*A - R1/B)) else: P1 = 0.5*(1. - (1. + A**-2)/2./(1. + NTU1)) elif Ntp == 2: lambda1 = (1. + R1*R1/4.)**0.5 A = exp(NTU1) D = 1. + lambda1*A**((lambda1 - 1.)/2.)/(A**lambda1 - 1.) C = A**((1+lambda1)/2.)/(lambda1 - 1. + (1. + lambda1)*A**lambda1) B = (A**lambda1 + 1.)/(A**lambda1 - 1.) P1 = 1./(1. + R1/2. + lambda1*B - 2.*lambda1*C*D) elif Ntp == 4: lambda1 = (1. + R1**2/16.)**0.5 E = exp(R1*NTU1/2.) A = exp(NTU1) D = 1. + lambda1*A**((lambda1-1)/2.)/(A**lambda1-1.) C = A**((1+lambda1)/2.)/(lambda1 - 1. + (1. + lambda1)*A**lambda1) B = (A**lambda1 + 1.)/(A**lambda1-1) P1 = 1./(1. + R1/4.*(1. + 3.*E)/(1. + E) + lambda1*B - 2.*lambda1*C*D) else: raise ValueError("Supported numbers of tube passes are 1, 2, and 4.") return P1 def temperature_effectiveness_TEMA_H(R1: float, NTU1: float, Ntp: int, optimal: bool=True) -> float: r"""Returns temperature effectiveness `P1` of a TEMA H type heat exchanger with a specified heat capacity ratio, number of transfer units `NTU1`, and of number of tube passes `Ntp`. For the two tube pass case, there are two possible orientations, one inefficient and one efficient controlled by the `optimal` option. The supported cases are as follows: * One tube pass (tube fluid split into two streams individually mixed, shell fluid mixed) * Two tube passes (shell fluid mixed, tube pass mixed between passes) * Two tube passes (shell fluid mixed, tube pass mixed between passes, inlet tube side next to inlet shell-side) 1-1 TEMA H, tube fluid split into two streams individually mixed, shell fluid mixed: .. math:: P_1 = E[1 + (1 - BR_1/2)(1 - A R_1/2 + ABR_1)] - AB(1 - BR_1/2) .. math:: A = \frac{1}{1 + R_1/2}\{1 - \exp[-NTU_1(1 + R_1/2)/2]\} .. math:: B = \frac{1-D}{1-R_1 D/2} .. math:: D = \exp[-NTU_1(1-R_1/2)/2] .. math:: E = (A + B - ABR_1/2)/2 1-2 TEMA H, shell and tube fluids mixed in each pass at the cross section: .. math:: P_1 = \frac{1}{R_1}\left[1 - \frac{(1-D)^4}{B - 4G/R_1}\right] .. math:: B = (1+H)(1+E)^2 .. math:: G = (1-D)^2(D^2 + E^2) + D^2(1 + E)^2 .. math:: H = [1 - \exp(-2\beta)]/(4/R_1 -1) .. math:: E = [1 - \exp(-\beta)]/(4/R_1 - 1) .. math:: D = [1 - \exp(-\alpha)]/(4/R_1 + 1) .. math:: \alpha = NTU_1(4 + R_1)/8 .. math:: \beta = NTU_1(4-R_1)/8 1-2 TEMA H, shell and tube fluids mixed in each pass at the cross section but with the inlet tube stream coming in next to the shell fluid inlet in an inefficient way (this is only shown in [2]_, and the stream 1/2 convention in it is different but converted here; P1 is still returned): .. math:: P_2 = \left[1 - \frac{B + 4GR_2}{(1-D)^4}\right] .. math:: B = (1 + H)(1 + E)^2 .. math:: G = (1-D)^2(D^2 + E^2) + D^2(1 + E)^2 .. math:: D = \frac{1 - \exp(-\alpha)}{1 - 4R_2} .. math:: E = \frac{\exp(-\beta) - 1}{4R_2 +1} .. math:: H = \frac{\exp(-2\beta) - 1}{4R_2 +1} .. math:: \alpha = \frac{NTU_2}{8}(4R_2 -1) .. math:: \beta = \frac{NTU_2}{8}(4R_2 +1) Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1, or 2, [-] optimal : bool, optional Whether or not the arrangement is configured to give more of a countercurrent and efficient (True) case or an inefficient parallel case, [-] Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- For numbers of tube passes greater than 1 or 2, an exception is raised. The convention for the formulas in [1]_ and [3]_ are with the shell side as side 1, and the tube side as side 2. [2]_ has formulas with the opposite convention. Examples -------- >>> temperature_effectiveness_TEMA_H(R1=1/3., NTU1=1., Ntp=1) 0.5730728284905833 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ if Ntp == 1: A = 1./(1 + R1/2.)*(1. - exp(-NTU1*(1. + R1/2.)/2.)) D = exp(-NTU1*(1. - R1/2.)/2.) if R1 != 2: B = (1. - D)/(1. - R1*D/2.) else: B = NTU1/(2. + NTU1) E = (A + B - A*B*R1/2.)/2. P1 = E*(1. + (1. - B*R1/2.)*(1. - A*R1/2. + A*B*R1)) - A*B*(1. - B*R1/2.) elif Ntp == 2 and optimal: alpha = NTU1*(4. + R1)/8. beta = NTU1*(4. - R1)/8. D = (1. - exp(-alpha))/(4./R1 + 1) if R1 != 4: E = (1. - exp(-beta))/(4./R1 - 1.) H = (1. - exp(-2.*beta))/(4./R1 - 1.) else: E = NTU1/2. H = NTU1 G = (1-D)**2*(D**2 + E**2) + D**2*(1+E)**2 B = (1. + H)*(1. + E)**2 P1 = 1./R1*(1. - (1. - D)**4/(B - 4.*G/R1)) elif Ntp == 2 and not optimal: R1_orig = R1 #NTU2 = NTU1*R1_orig but we want to treat it as NTU1 in this case NTU1 = NTU1*R1_orig # switch 1 # R2 = 1/R1 but we want to treat it as R1 in this case R1 = 1./R1_orig # switch 2 beta = NTU1*(4.*R1 + 1)/8. alpha = NTU1/8.*(4.*R1 - 1.) H = (exp(-2.*beta) - 1.)/(4.*R1 + 1.) E = (exp(-beta) - 1.)/(4.*R1 + 1.) B = (1. + H)*(1. + E)**2 if R1 != 0.25: D = (1. - exp(-alpha))/(1. - 4.*R1) G = (1. - D)**2*(D**2 + E**2) + D**2*(1. + E)**2 P1 = (1. - (B + 4.*G*R1)/(1. - D)**4) else: D = -NTU1/8. G = (1. - D)**2*(D**2 + E**2) + D**2*(1. + E)**2 P1 = (1. - (B + 4.*G*R1)/(1. - D)**4) P1 = P1/R1_orig # switch 3, confirmed else: raise ValueError("Supported numbers of tube passes are 1 and 2.") return P1 def temperature_effectiveness_TEMA_G(R1: float, NTU1: float, Ntp: int, optimal: bool=True) -> float: r"""Returns temperature effectiveness `P1` of a TEMA G type heat exchanger with a specified heat capacity ratio, number of transfer units `NTU1`, and of number of tube passes `Ntp`. For the two tube pass case, there are two possible orientations, one inefficient and one efficient controlled by the `optimal` option. The supported cases are as follows: * One tube pass (tube fluid split into two streams individually mixed, shell fluid mixed) * Two tube passes (shell and tube exchanger with shell and tube fluids mixed in each pass at the cross section), counterflow arrangement * Two tube passes (shell and tube exchanger with shell and tube fluids mixed in each pass at the cross section), parallelflow arrangement 1-1 TEMA G, tube fluid split into two streams individually mixed, shell fluid mixed (this configuration is symmetric): .. math:: P_1 = A + B - AB(1 + R_1) + R_1 AB^2 .. math:: A = \frac{1}{1 + R_1}\{1 - \exp(-NTU_1(1+R_1)/2)\} .. math:: B = \frac{1 - D}{1 - R_1 D} .. math:: D = \exp[-NTU_1(1-R_1)/2] 1-2 TEMA G, shell and tube exchanger with shell and tube fluids mixed in each pass at the cross section: .. math:: P_1 = (B - \alpha^2)/(A + 2 + R_1 B) .. math:: A = -2 R_1(1-\alpha)^2/(2 + R_1) .. math:: B = [4 - \beta(2+R_1)]/(2 - R_1) .. math:: \alpha = \exp[-NTU_1(2+R_1)/4] .. math:: \beta = \exp[-NTU_1(2 - R_1)/2] 1-2 TEMA G, shell and tube exchanger in overall parallelflow arrangement with shell and tube fluids mixed in each pass at the cross section (this is only shown in [2]_, and the stream convention in it is different but converted here; P1 is still returned): .. math:: P_2 = \frac{(B-\alpha^2)}{R_2(A - \alpha^2/R_2 + 2)} .. math:: A = \frac{(1-\alpha)^2}{(R_2-0.5)} .. math:: B = \frac{4R_2 - \beta(2R_2 - 1)}{2R_2 + 1} .. math:: \alpha = \exp\left(\frac{-NTU_2(2R_2-1)}{4}\right) .. math:: \beta = \exp\left(\frac{-NTU_2(2R_2+1)}{2}\right) Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1 or 2, [-] optimal : bool, optional Whether or not the arrangement is configured to give more of a countercurrent and efficient (True) case or an inefficient parallel case (only applies for two passes), [-] Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- For numbers of tube passes greater than 1 or 2, an exception is raised. The convention for the formulas in [1]_ and [3]_ are with the shell side as side 1, and the tube side as side 2. [2]_ has formulas with the opposite convention. Examples -------- >>> temperature_effectiveness_TEMA_G(R1=1/3., NTU1=1., Ntp=1) 0.5730149350867675 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ if Ntp == 1: D = exp(-NTU1*(1. - R1)/2.) if R1 != 1: B = (1. - D)/(1. - R1*D) else: B = NTU1/(2. + NTU1) A = 1./(1. + R1)*(1. - exp(-NTU1*(1. + R1)/2.)) P1 = A + B - A*B*(1. + R1) + R1*A*B**2 elif Ntp == 2 and optimal: if R1 != 2: beta = exp(-NTU1*(2. - R1)/2.) alpha = exp(-NTU1*(2. + R1)/4.) B = (4. - beta*(2. + R1))/(2. - R1) A = -2.*R1*(1-alpha)**2/(2. + R1) P1 = (B - alpha**2)/(A + 2. + R1*B) else: alpha = exp(-NTU1) P1 = (1. + 2.*NTU1 - alpha**2)/(4. + 4.*NTU1 - (1. - alpha)**2) elif Ntp == 2 and not optimal: R1_orig = R1 #NTU2 = NTU1*R1_orig but we want to treat it as NTU1 in this case NTU1 = NTU1*R1_orig # switch 1 # R2 = 1/R1 but we want to treat it as R1 in this case R1 = 1./R1_orig # switch 2 if R1 != 0.5: beta = exp(-NTU1*(2.*R1 + 1.)/2.) alpha = exp(-NTU1*(2.*R1 - 1.)/4.) B = (4.*R1 - beta*(2.*R1 - 1.))/(2.*R1 + 1.) A = (1. - alpha)**2/(R1 - 0.5) P1 = (B - alpha**2)/(R1*(A - alpha**2/R1 + 2.)) else: beta = exp(-2.*R1*NTU1) P1 = (1. + 2.*R1*NTU1 - beta)/R1/(4. + 4.*R1*NTU1 + R1**2*NTU1**2) P1 = P1/R1_orig # switch 3, confirmed else: raise ValueError("Supported numbers of tube passes are 1 and 2.") return P1 def temperature_effectiveness_TEMA_E(R1: float, NTU1: float, Ntp: int=1, optimal: bool=True) -> float: r"""Returns temperature effectiveness `P1` of a TEMA E type heat exchanger with a specified heat capacity ratio, number of transfer units `NTU1`, number of tube passes `Ntp`, and whether or not it is arranged in a more countercurrent (optimal configuration) way or a more parallel (optimal=False) case. The supported cases are as follows: * 1-1 TEMA E, shell fluid mixed * 1-2 TEMA E, shell fluid mixed (this configuration is symmetric) * 1-2 TEMA E, shell fluid split into two steams individually mixed * 1-3 TEMA E, shell and tube fluids mixed, one parallel pass and two counterflow passes (efficient) * 1-3 TEMA E, shell and tube fluids mixed, two parallel passes and one counteflow pass (inefficient) * 1-N TEMA E, shall and tube fluids mixed, efficient counterflow orientation, N an even number 1-1 TEMA E, shell fluid mixed: .. math:: P_1 = \frac{1 - \exp[-NTU_1(1-R_1)]}{1 - R_1 \exp[-NTU_1(1-R_1)]} 1-2 TEMA E, shell fluid mixed (this configuration is symmetric): .. math:: P_1 = \frac{2}{1 + R_1 + E\coth(E\cdot NTU_1/2)} .. math:: E = [1 + R_1^2]^{1/2} 1-2 TEMA E, shell fluid split into two steams individually mixed: .. math:: P_1 = \frac{1}{R_1}\left[1 - \frac{(2-R_1)(2E+R_1B)}{(2+R_1)(2E-R_1/B)} \right] .. math:: E = \exp(NTU_1) .. math:: B = \exp(-NTU_1 R_1/2) 1-3 TEMA E, shell and tube fluids mixed, one parallel pass and two counterflow passes (efficient): .. math:: P_1 = \frac{1}{R_1} \left[1 - \frac{C}{AC + B^2}\right] .. math:: A = X_1(R_1 + \lambda_1)(R_1 - \lambda_2)/(2\lambda_1) - X_3 \delta - X_2(R_1 + \lambda_2)(R_1-\lambda_1)/(2\lambda_2) + 1/(1-R_1) .. math:: B = X_1(R_1-\lambda_2) - X_2(R_1-\lambda_1) + X_3\delta .. math:: C = X_2(3R_1 + \lambda_1) - X_1(3R_1 + \lambda_2) + X_3 \delta .. math:: X_i = \exp(\lambda_i NTU_1/3)/(2\delta),\;\; i = 1,2,3 .. math:: \delta = \lambda_1 - \lambda_2 .. math:: \lambda_1 = -\frac{3}{2} + \left[\frac{9}{4} + R_1(R_1-1)\right]^{1/2} .. math:: \lambda_2 = -\frac{3}{2} - \left[\frac{9}{4} + R_1(R_1-1)\right]^{1/2} .. math:: \lambda_3 = R_1 1-3 TEMA E, shell and tube fluids mixed, two parallel passes and one counteflow pass (inefficient): .. math:: P_2 = \left[1 - \frac{C}{(AC + B^2)}\right] .. math:: A = \chi_1(1 + R_2 \lambda_1)(1 - R_2\lambda_2)/(2R_2^2\lambda_1) - E -\chi_2(1 + R_2\lambda_2)(1 - R_2\lambda_1)/(2R^2\lambda_2) + R/(R-1) .. math:: B = \chi_1(1 - R_2\lambda_2)/R_2 - \chi_2(1 - R_2 \lambda_1)/R_2 + E .. math:: C = -\chi_1(3 + R_2\lambda_2)/R_2 + \chi_2(3 + R_2\lambda_1)/R_2 + E .. math:: E = 0.5\exp(NTU_2/3) .. math:: \lambda_1 = (-3 + \delta)/2 .. math:: \lambda_2 = (-3 - \delta)/2 .. math:: \delta = \frac{[9R_2^2 + 4(1-R_2))]^{0.5}}{R_2} .. math:: \chi_1 = \frac{\exp(\lambda_1 R_2 NTU_2/3)}{2\delta} .. math:: \chi_2 = \frac{\exp(\lambda_2 R_2 NTU_2/3)}{2\delta} 1-N TEMA E, shall and tube fluids mixed, efficient counterflow orientation, N an even number: .. math:: P_2 = \frac{2}{A + B + C} .. math:: A = 1 + R_2 + \coth(NTU_2/2) .. math:: B = \frac{-1}{N_1}\coth\left(\frac{NTU_2}{2N_1}\right) .. math:: C = \frac{1}{N_1}\sqrt{1 + N_1^2 R_2^2} \coth\left(\frac{NTU_2}{2N_1}\sqrt{1 + N_1^2 R_2^2}\right) .. math:: N_1 = \frac{N_{tp}}{2} Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1, 2, 3, 4, or an even number[-] optimal : bool, optional Whether or not the arrangement is configured to give more of a countercurrent and efficient (True) case or an inefficient parallel case, [-] Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- For odd numbers of tube passes greater than 3, an exception is raised. [2]_ actually has a formula for 5 tube passes, but it is an entire page long. The convention for the formulas in [1]_ and [3]_ are with the shell side as side 1, and the tube side as side 2. [2]_ has formulas with the opposite convention. Examples -------- >>> temperature_effectiveness_TEMA_E(R1=1/3., NTU1=1., Ntp=1) 0.5870500654031314 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ if Ntp == 1: # Just the basic counterflow case if R1 != 1: P1 = (1-exp(-NTU1*(1-R1))) / (1 - R1*exp(-NTU1*(1-R1))) else: P1 = NTU1/(1. + NTU1) elif Ntp == 2 and optimal: if R1 != 1: E = (1. + R1**2)**0.5 P1 = 2./(1 + R1 + E/tanh(E*NTU1/2.)) else: P1 = 1/(1 + 1/tanh(NTU1*2**-0.5)*2**-0.5) elif Ntp == 2 and not optimal: # Shah, reverse flow but with divider; without divider would be parallel. # Same as J-1, but E = A and B = B. A = exp(NTU1) B = exp(-NTU1*R1/2.) if R1 != 2: P1 = 1/R1*(1 - (2-R1)*(2*A + R1*B)/(2+R1)/(2*A - R1/B)) else: P1 = 0.5*(1 - (1 + A**-2)/2./(1+NTU1)) elif Ntp == 3 and optimal: # This gives slightly different results than in Thulukkanam! lambda3 = R1 # in Rosehnhow, this is minus. makes a small diff though lambda2 = -1.5 - (2.25 + R1*(R1-1))**0.5 lambda1 = -1.5 + (2.25 + R1*(R1-1))**0.5 delta = lambda1 - lambda2 X1 = exp(lambda1*NTU1/3.)/2/delta X2 = exp(lambda2*NTU1/3.)/2/delta X3 = exp(lambda3*NTU1/3.)/2/delta C = X2*(3*R1 + lambda1) - X1*(3*R1 + lambda2) + X3*delta B = X1*(R1 - lambda2) - X2*(R1 - lambda1) + X3*delta if R1 != 1: A = X1*(R1 + lambda1)*(R1 - lambda2)/2/lambda1 - X3*delta - X2*(R1 + lambda2)*(R1 - lambda1)/2/lambda2 + 1./(1-R1) else: A = -exp(-NTU1)/18 - exp(NTU1/3.)/2 + (NTU1 + 5)/9. P1 = 1./R1*(1. - C/(A*C + B*B)) elif Ntp == 3 and not optimal: # Thulukkanam, Parallel instead of direct. R1_orig = R1 #NTU2 = NTU1*R1_orig but we want to treat it as NTU1 in this case NTU1 = NTU1*R1_orig # switch 1 # R2 = 1/R1 but we want to treat it as R1 in this case R1 = 1./R1_orig # switch 2 delta = (9*R1**2 + 4*(1 - R1))**0.5/R1 l1 = (-3 + delta)/2. l2 = (-3 - delta)/2. chi1 = exp(l1*R1*NTU1/3.)/2/delta chi2 = exp(l2*R1*NTU1/3.)/2/delta E = 0.5*exp(NTU1/3.) C = -chi1*(3 + R1*l2)/R1 + chi2*(3 + R1*l1)/R1 + E B = chi1*(1 - R1*l2)/R1 - chi2*(1 - R1*l1)/R1 + E # if R1 != 1: A = (chi1*(1 + R1*l1)*(1 - R1*l2)/(2*R1**2*l1) - E - chi2*(1 + R1*l2)*(1 - R1*l1)/(2*R1**2*l2) + R1*(R1 -1)) # The below change is NOT CONSISTENT with the main expression and is disabled # else: # A = -exp(-NTU1)/18. - exp(NTU1/3)/2. + (5 + NTU1)/9. P1 = (1 - C/(A*C + B**2)) P1 = P1/R1_orig # switch 3, confirmed elif Ntp == 4 or Ntp %2 == 0: # The 4 pass case is present in all three sources, and is confirmed to # give the same results for all three for Ntp = 4. However, # what is awesome about the Thulukkanam version is that it supports # n tube passes so long as n is even. R1_orig = R1 #NTU2 = NTU1*R1_orig but we want to treat it as NTU1 in this case NTU1 = NTU1*R1_orig # switch 1 # R2 = 1/R1 but we want to treat it as R1 in this case R1 = 1./R1_orig # switch 2 N1 = Ntp/2. C = 1/N1*(1 + N1**2*R1**2)**0.5/tanh(NTU1/(2*N1)*(1 + N1**2*R1**2)**0.5) B = -1/N1/tanh(NTU1/(2*N1)) A = 1 + R1 + 1/tanh(NTU1/2.) P1 = 2/(A + B + C) P1 = P1/R1_orig # switch 3, confirmed else: raise ValueError("For TEMA E shells with an odd number of tube passes more than 3, no solution is implemented.") return P1 def temperature_effectiveness_plate(R1: float, NTU1: float, Np1: int, Np2: int, counterflow: bool=True, passes_counterflow: bool=True, reverse: bool=False) -> float: r"""Returns the temperature effectiveness `P1` of side 1 of a plate heat exchanger with a specified side 1 heat capacity ratio `R1`, side 1 number of transfer units `NTU1`, number of passes on sides 1 and 2 (respectively `Np1` and `Np2`). For all cases, the function also takes as arguments whether the exchanger is setup in an overall counter or parallel orientation `counterflow`, and whether or not individual stream passes are themselves counterflow or parallel. The 20 supported cases are as follows. (the first number of sides listed refers to side 1, and the second number refers to side 2): * 1 pass/1 pass parallelflow * 1 pass/1 pass counterflow * 1 pass/2 pass * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in parallel) * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in counterflow) * 1 pass/4 pass * 2 pass/2 pass, overall parallelflow, individual passes in parallel * 2 pass/2 pass, overall parallelflow, individual passes counterflow * 2 pass/2 pass, overall counterflow, individual passes parallelflow * 2 pass/2 pass, overall counterflow, individual passes counterflow * 2 pass/3 pass or 3 pass/2 pass, overall parallelflow * 2 pass/3 pass or 3 pass/2 pass, overall counterflow * 2 pass/4 pass or 4 pass/2 pass, overall parallel flow * 2 pass/4 pass or 4 pass/2 pass, overall counterflow flow For all plate heat exchangers, there are two common formulas used by most of the expressions. .. math:: P_p(x, y) = \frac{1 - \exp[-x(1 + y)]}{1 + y} P_c(x, y) = \frac{1 - \exp[-x(1 - y)]}{1 - y\exp[-x(1 - y)]} The main formulas used are as follows. Note that for some cases such as 4 pass/2 pass, the formula is not shown because it is that of 2 pass/4 pass, but with R1, NTU1, and P1 conversions. For 1 pass/1 pass paralleflow (streams symmetric): .. math:: P_1 = P_p(NTU_1, R_1) For 1 pass/1 pass counterflow (streams symmetric): .. math:: P_1 = P_c(NTU_1, R_1) For 1 pass/2 pass (any of the four possible configurations): .. math:: P_1 = 0.5(A + B - 0.5ABR_1) .. math:: A = P_p(NTU_1, 0.5R_1) .. math:: B = P_c(NTU_1, 0.5R_1) For 1 pass/3 pass (with the two end passes in parallel): .. math:: P_1 = \frac{1}{3}\left[B + A\left(1 - \frac{R_1 B}{3}\right)\left(2 - \frac{R_1 A}{3}\right)\right] .. math:: A = P_p\left(NTU_1, \frac{R_1}{3}\right) .. math:: B = P_c\left(NTU_1, \frac{R_1}{3}\right) For 1 pass/3 pass (with the two end passes in counterflow): .. math:: P_1 = \frac{1}{3}\left[A + B\left(1 - \frac{R_1 A}{3}\right)\left(2 - \frac{R_1 B}{3}\right)\right] .. math:: A = P_p\left(NTU_1, \frac{R_1}{3}\right) .. math:: B = P_c\left(NTU_1, \frac{R_1}{3}\right) For 1 pass/4 pass (any of the four possible configurations): .. math:: P_1 = \frac{1-Q}{R_1} .. math:: Q = \left(1 - \frac{AR_1}{4}\right)^2\left(1 - \frac{BR_1}{4}\right)^2 .. math:: A = P_p\left(NTU_1, \frac{R_1}{4}\right) .. math:: B = P_c\left(NTU_1, \frac{R_1}{4}\right) For 2 pass/2 pass, overall parallelflow, individual passes in parallel (stream symmetric): .. math:: P_1 = P_p(NTU_1, R_1) For 2 pass/2 pass, overall parallelflow, individual passes counterflow (stream symmetric): .. math:: P_1 = B[2 - B(1 + R_1)] .. math:: B = P_c\left(\frac{NTU_1}{2}, R_1\right) For 2 pass/2 pass, overall counterflow, individual passes parallelflow (stream symmetric): .. math:: P_1 = \frac{2A - A^2(1 + R_1)}{1 - R_1 A^2} .. math:: A = P_p\left(\frac{NTU_1}{2}, R_1\right) For 2 pass/2 pass, overall counterflow and individual passes counterflow (stream symmetric): .. math:: P_1 = P_c(NTU_1, R_1) For 2 pass/3 pass, overall parallelflow: .. math:: P_1 = A + B - \left(\frac{2}{9} + \frac{D}{3}\right) (A^2 + B^2) - \left(\frac{5}{9} + \frac{4D}{3}\right)AB + \frac{D(1+D)AB(A+B)}{3} - \frac{D^2A^2B^2}{9} .. math:: A = P_p\left(\frac{NTU_1}{2}, D\right) .. math:: B = P_c\left(\frac{NTU_1}{2}, D\right) .. math:: D = \frac{2R_1}{3} For 2 pass/3 pass, overall counterflow: .. math:: P_1 = \frac{A + 0.5B + 0.5C + D}{R_1} .. math:: A = \frac{2R_1 EF^2 - 2EF + F - F^2} {2R_1 E^2 F^2 - E^2 - F^2 - 2EF + E + F} .. math:: B = \frac{A(E-1)}{F} .. math:: C = \frac{1 - A}{E} .. math:: D = R_1 E^2 C - R_1 E + R_1 - \frac{C}{2} .. math:: E = \frac{3}{2R_1 G} .. math:: F = \frac{3}{2R_1 H} .. math:: G = P_c\left(\frac{NTU_1}{2}, \frac{2R_1}{3}\right) .. math:: H = P_p\left(\frac{NTU_1}{2}, \frac{2R_1}{3}\right) For 2 pass/4 pass, overall parallel flow: .. math:: P_1 = 2D - (1 + R_1)D^2 .. math:: D = \frac{A + B - 0.5ABR_1}{2} .. math:: A = P_p\left(\frac{NTU_1}{2}, \frac{R_1}{2}\right) .. math:: B = P_c\left(\frac{NTU_1}{2}, \frac{R_1}{2}\right) For 2 pass/4 pass, overall counterflow flow: .. math:: P_1 = \frac{2D - (1+R_1)D^2}{1 - D^2 R_1} .. math:: D = \frac{A + B - 0.5ABR_1}{2} .. math:: A = P_p\left(\frac{NTU_1}{2}, \frac{R_1}{2}\right) .. math:: B = P_c\left(\frac{NTU_1}{2}, \frac{R_1}{2}\right) Parameters ---------- R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Np1 : int Number of passes on side 1 [-] Np2 : int Number of passes on side 2 [-] counterflow : bool Whether or not the overall flow through the heat exchanger is in counterflow or parallel flow, [-] passes_counterflow : bool In addition to the overall flow direction, in some cases individual passes may be in counter or parallel flow; this controls that [-] reverse : bool Used **internally only** to allow cases like the 1-4 formula to work for the 4-1 flow case, without having to duplicate the code [-] Returns ------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- For diagrams of these heat exchangers, see [3]_. In all cases, each pass is assumed to be made up of an infinite number of plates. The fluid velocities must be uniform across the plate channels, and the flow must be uniformly distributed between the channels. The heat transfer coefficient is also assumed constant. The defaults of counterflow=True and passes_counterflow=True will always result in the most efficient heat exchanger option, normally what is desired. If a number of passes which is not supported is provided, an exception is raised. Examples -------- Three passes on side 1; one pass on side 2; two end passes in counterflow orientation. >>> temperature_effectiveness_plate(R1=1/3., NTU1=1., Np1=3, Np2=1) 0.5743514352720835 If the same heat exchanger (in terms of NTU1 and R1) were operating with sides 1 and 2 switched, a slightly less efficient design results. >>> temperature_effectiveness_plate(R1=1/3., NTU1=1., Np1=1, Np2=3) 0.5718726757657066 References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. .. [3] Kandlikar, S. G., and R. K. Shah. "Asymptotic Effectiveness-NTU Formulas for Multipass Plate Heat Exchangers." Journal of Heat Transfer 111, no. 2 (May 1, 1989): 314-21. doi:10.1115/1.3250679. .. [4] Kandlikar, S. G., and R. K. Shah. "Multipass Plate Heat Exchangers Effectiveness-NTU Results and Guidelines for Selecting Pass Arrangements." Journal of Heat Transfer 111, no. 2 (May 1, 1989): 300-313. doi:10.1115/1.3250678. """ if Np1 == 1 and Np2 == 1 and counterflow: return P_NTU_Pc(NTU1, R1) elif Np1 == 1 and Np2 == 1 and not counterflow: return P_NTU_Pp(NTU1, R1) elif Np1 == 1 and Np2 == 2: # There are four configurations but all have the same formula # They do behave different depending on the number of available plates # but this model assues infinity # There are four more arrangements that are equivalent as well A = P_NTU_Pp(NTU1, 0.5*R1) B = P_NTU_Pc(NTU1, 0.5*R1) return 0.5*(A + B - 0.5*A*B*R1) elif Np1 == 1 and Np2 == 3 and counterflow: # There are six configurations, two formulas # Each behaves differently though as a function of number of plates A = P_NTU_Pp(NTU1, R1/3.) B = P_NTU_Pc(NTU1, R1/3.) return 1/3.*(A + B*(1. - R1*A/3.)*(2. - R1*B/3.)) elif Np1 == 1 and Np2 == 3 and not counterflow: A = P_NTU_Pp(NTU1, R1/3.) B = P_NTU_Pc(NTU1, R1/3.) return 1/3.*(B + A*(1. - R1*B/3.)*(2. - R1*A/3.)) elif Np1 == 1 and Np2 == 4: # four configurations # Again a function of number of plates, but because expressions assume # infinity it gets ignored and they're the same A = P_NTU_Pp(NTU1, 0.25*R1) B = P_NTU_Pc(NTU1, 0.25*R1) t1 = (1. - 0.25*A*R1) t2 = (1. - 0.25*B*R1) t3 = t1*t2 # minor optimization return (1. - t3*t3)/R1 elif Np1 == 2 and Np2 == 2: if counterflow and passes_counterflow: return P_NTU_Pc(NTU1, R1) elif counterflow and not passes_counterflow: A = P_NTU_Pp(0.5*NTU1, R1) return (2.*A - A*A*(1. + R1))/(1. - R1*A*A) elif not counterflow and passes_counterflow: B = P_NTU_Pc(0.5*NTU1, R1) return B*(2. - B*(1. + R1)) elif not counterflow and not passes_counterflow: return temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=1, passes_counterflow=True, counterflow=False, reverse=False) elif Np1 == 2 and Np2 == 3: # One place says there are four configurations; no other discussion is # presented if counterflow: H = P_NTU_Pp(0.5*NTU1, 2./3.*R1) G = P_NTU_Pc(0.5*NTU1, 2./3.*R1) E = 1./(2./3.*R1*G) F = 1./(2./3.*R1*H) E2 = E*E F2 = F*F A = (2.*R1*E*F2 - 2.*E*F + F - F2)/(2.*R1*E2*F2 - E2 - F2 - 2.*E*F + E + F) C = (1. - A)/E D = R1*E*E*C - R1*E + R1 - 0.5*C B = A*(E - 1.)/F return (A + 0.5*B + 0.5*C + D)/R1 elif not counterflow: D = 2*R1/3. A = P_NTU_Pp(NTU1/2, D) B = P_NTU_Pc(NTU1/2, D) return (A + B - (2/9. + D/3.)*(A*A + B*B) -(5./9. + 4./3.*D)*A*B + D*(1. + D)*A*B*(A + B)/3. - D*D*A*A*B*B/9.) elif Np1 == 2 and Np2 == 4: # Both cases are correct for passes_counterflow=True or False if counterflow: A = P_NTU_Pp(0.5*NTU1, 0.5*R1) B = P_NTU_Pc(0.5*NTU1, 0.5*R1) D = 0.5*(A + B - 0.5*A*B*R1) return (2.*D - (1. + R1)*D*D)/(1. - D*D*R1) elif not counterflow: A = P_NTU_Pp(0.5*NTU1, 0.5*R1) B = P_NTU_Pc(0.5*NTU1, 0.5*R1) D = 0.5*(A + B - 0.5*A*B*R1) return 2.*D - ((1. + R1)*D*D) if not reverse: # only the asymmetric cases will be able to solve by flipping things # Note that asymmetric performs differently depending on the arguments # The user still needs to input R1, NTU1 for side 1 # so if they want to do a 3-1 instead of a 1-3 as is implemented here # They give R1 and NTU1 for "3 pass" side instead of the "1 pass" side # and will get back P1 for the "3 pass" side. # numba is dying on this recursion when caching is on, disable caching for now R2 = 1./R1 NTU2 = NTU1*R1 P2 = temperature_effectiveness_plate(R1=R2, NTU1=NTU2, Np1=Np2, Np2=Np1, counterflow=counterflow, passes_counterflow=passes_counterflow, reverse=True) P1 = P2*R2 return P1 raise ValueError("Supported number of passes does not have a formula available") NTU_from_plate_2_3_parallel_offset = [7.5e-09, 1.4249999999999999e-08, 2.7074999999999996e-08, 5.144249999999999e-08, 9.774074999999998e-08, 1.8570742499999996e-07, 3.528441074999999e-07, 6.704038042499998e-07, 1.2737672280749996e-06, 2.420157733342499e-06, 4.598299693350748e-06, 8.73676941736642e-06, 1.6599861892996197e-05, 3.153973759669277e-05, 5.9925501433716265e-05, 0.0001138584527240609, 0.0002163310601757157, 0.0004110290143338598, 0.0007809551272343336, 0.0014838147417452338, 0.002819248009315944, 0.005356571217700294, 0.010177485313630557, 0.019337222095898058, 0.036740721982206306, 0.06980737176619198, 0.13263400635576475, 0.25200461207595304, 0.47880876294431074, 0.9097366495941903, 1.7284996342289616, 3.2841493050350268, 6.23988367956655, 11.855778991176445, 22.525980083235243, 42.79936215814696, 81.31878810047922, 154.5056973909105, 293.56082504272996, 557.7655675811869 ] NTU_from_plate_2_3_parallel_p = [ [6.462119185839311e+42, -7.89735987601022e+35, -9.087083856062007e+27, 1.7050113422248866e+19, -1688535720.902906, 39.613950137696285], [2.318252075647079e+40, 4.220218073315233e+33, 1.8285573384676073e+26, 3.084411438713216e+18, 21021750157.222004, 38.330242461631514], [-2.725246602458712e+39, 2.3068644128776514e+32, 4.627829472767517e+25, 1.1310001900283219e+18, 966837452.7610933, 37.046534751334335], [1.2235529142820281e+37, 8.353473028988188e+30, 1.1577363564247304e+24, 5.26111342386283e+16, 1261185446.350375, 35.762826989453515], [-2.268909561806343e+38, -1.3039314202429206e+32, -1.0060952358606218e+25, 2.825039921898014e+17, -4479452951.060747, 34.47911926523136], [3.1317729161848482e+35, 1.2588127743217981e+30, 3.276383218600776e+23, 5869028606256265.0, 1575663397.1343608, 33.19541167935255], [1.269695711103147e+34, 4.290756179192165e+28, 2.9775379948312816e+22, 5786034109781830.0, 164435306.647866, 31.911704317934102], [2.3505913930420825e+33, 1.1812928249795345e+28, 9.295788631785714e+21, 646240998532863.4, 152570202.7077346, 30.627996765657006], [9.372652014253886e+30, 1.3865784239035505e+26, 4.189500257555692e+20, 382474529586341.4, 87229551.47782452, 29.34428979550431], [2.4394239228002745e+29, 8.48439767361896e+24, 5.6565841650672206e+19, 104617438878045.67, 31457806.46330641, 28.060583495837115], [1.1756565638642434e+28, 8.043965680172975e+23, 9.83892031482124e+18, 28459853223759.457, -9973441.564916672, 26.776878200784502], [-7.434125480121518e+25, -9.162097000642035e+21, -1.8461512379952253e+17, -265723153860.95157, 9996257.22456268, 25.493174743647415], [2.0776904219507715e+25, 5.321566516109094e+21, 2.830031172775278e+17, 5145809833029.548, 30832206.351568446, 24.20947444204136], [-2.0513335420466532e+24, -2.289228286826745e+20, -929209616723210.4, 76660920130.18863, -1702371.8751699005, 22.925779468541926], [1.0921158668621774e+22, 1.0481031815969962e+19, 1918841223032205.0, 108793646298.43532, 1871101.8043717288, 21.642093202587958], [4.225007197010862e+20, 1.0174799962646577e+18, 418193922144622.8, 50623892747.72083, 1687944.9487507509, 20.35842139409845], [5.3571938699724366e+19, 1.9585365429957734e+17, 127061183144718.2, 22936953503.569767, 702355.1752422716, 19.074772810895016], [-4.732337226080112e+19, -1.1230910989054965e+17, -30110540624204.457, 6413264110.352206, -490016.8688730465, 17.791160116387005], [7.152554048415291e+17, 4327530795294686.5, 3402335929741.3604, -374943769.5945296, 826.0224931539136, 16.507601903407284], [984051013256121.4, 24396685224717.965, 97948169286.30563, 109018337.85835141, 26368.75705403507, 15.22412348284966], [50680993613887.11, 879940841713.1937, 3547863447.171348, 8460582.67871622, 12799.65592799938, 13.940760190166468], [-875507484434.7006, 25808107236.834812, 1010170510.2032485, 5345388.737994069, 4433.937793425797, 12.65756872526503], [-2545609349.155158, 1744025195.285, 104920108.05542274, 1393742.0881427797, 5165.737303799409, 11.374674386567444], [-44107270179.70516, -9966996021.270891, -227618846.07818735, 221021.67601654973, -3146.660020222547, 10.092433966069105], [-17223688.786470495, -23864400.404286776, -3058744.705574641, -92465.34160573207, 79.26693119855088, 8.811928082820112], [207089.07738276388, 1227730.4574734285, 66877.00675493496, -61.49057944248921, -50.69640655670642, 7.536263692403265], [-119095.47949526514, -896903.6380506152, -426050.23346265673, -50907.75212691221, -869.3604740196895, 6.27351591893888], [938.3304032079799, 15625.662817632307, 14723.43865453997, 3567.9928498984227, 173.109130231987, 5.042051163423371], [10.211511823367426, 282.8051136783963, 451.2780669544769, 189.16900463066924, 20.74121725239963, 3.8767657222797793], [1.1464843558305287, 40.27461672000967, 59.16225274077587, 10.379056077245352, 5.7929254308893565, 2.829369860323157], [-0.01276147756024723, -0.48777843281385097, 0.6032501474532808, 4.559017517350226, 1.8620463339982214, 1.9541776601728913], [-0.00014226514748006712, -0.023808643483844285, -0.289121674152071, -1.114005659180411, -1.1972235528397606, 1.2832212535010843], [-2.581120907700105e-05, -0.00418426633038938, 0.009339781397016987, 0.45600757529303554, -0.4034481381751588, 0.8099512337803161], [9.273821864840757e-07, 0.0007547318108085425, 0.03245749164236026, 0.2965758497751961, -0.27934160268782265, 0.4972383631571961], [-6.233458586767228e-06, -0.015884785719281296, -0.7538527292952916, 0.3269735695585127, -1.7829706967875358, 0.2994738457522529], [-5.781637596658974e-06, -0.0035915273327539812, -0.01157399390997103, 0.029559240961787817, 0.07388163435077193, 0.17786369186050757], [3.961559826678127e-10, 1.4543254012176232e-06, 0.0001957616811595701, 0.0005362176178959326, 0.013938290268178794, 0.10448348895756158], [-3.2803452031636568e-09, -5.9441710409871e-06, -2.9118957190454924e-05, 0.0009942748181445327, -0.004462241345741868, 0.06081951964311699], [-4.279563643962056e-12, -6.561380309179023e-08, -3.081429869708402e-05, 0.0006859774272069931, -0.005747071000328576, 0.035126028098573805], [-2.8686462733352614e-14, -7.790696462481343e-10, -6.653666019778786e-07, 1.23027958890778e-05, -0.0010604984777168322, 0.020147982521643307] ] NTU_from_plate_2_3_parallel_q = [ [1.3683227596264214e+41, -2.165884158620195e+34, -2.2644202669570306e+26, 4.297163948003187e+17, -35893193.88590451, 1.0], [7.421678196787487e+38, 1.2085634160881847e+32, 5.007192734165482e+24, 8.236249667102427e+16, 552099325.8742635, 1.0], [-7.873002863853876e+37, 7.713934423114926e+30, 1.3099803429483467e+24, 3.054836281163621e+16, 28091870.519562516, 1.0], [4.291647340124204e+35, 2.5740797788209477e+29, 3.375618651322059e+22, 1500065233601050.2, 36352377.943491936, 1.0], [-7.882176775266176e+36, -3.9793411938031736e+30, -2.8656985978364168e+23, 8113694498408136.0, -129324377.97821534, 1.0], [1.41563514882479e+34, 4.110117035735913e+28, 9.893491716866435e+21, 191433581840910.7, 47790732.509775005, 1.0], [4.999607502786247e+32, 1.4716031312619367e+27, 9.64517819667706e+20, 182008960314679.44, 5330443.45680183, 1.0], [9.614824718113606e+31, 4.141511196223844e+26, 3.0530586050969154e+20, 21521729107166.945, 5078800.175545379, 1.0], [4.1734091264684445e+29, 5.27412019351319e+24, 1.4929447878932255e+19, 13174953396585.623, 3026132.05876614, 1.0], [1.1769712254415933e+28, 3.433836747260235e+23, 2.1211399654690634e+18, 3756068921305.945, 1150517.6889615546, 1.0], [6.078422345745446e+26, 3.430506170699249e+22, 3.854675824830577e+17, 1055299400508.5115, -356221.4839331609, 1.0], [-4.089621096755519e+24, -4.1038010771907025e+20, -7474524051100801.0, -7335562207.356166, 401094.5632574399, 1.0], [1.2324411281606296e+24, 2.5711536866148785e+20, 1.2592856537866202e+16, 218766477060.82593, 1278536.155959092, 1.0], [-1.1176764700506538e+23, -1.0287618349794472e+19, -27891844673339.754, 3102282562.5889225, -71489.86007645915, 1.0], [7.664428901664619e+20, 5.762787874997372e+17, 95614758117398.92, 5149781499.376999, 87998.69012738229, 1.0], [3.4119512881216352e+19, 6.137984726024919e+16, 22450868109060.75, 2555122134.822306, 83774.17840871602, 1.0], [4.6001726715440077e+18, 1.251467305711515e+16, 7213875441880.186, 1219435827.5993042, 37305.80357283532, 1.0], [-3.7939650022637384e+18, -6879494995144079.0, -1586382848606.9028, 352684674.27383363, -27269.243916658892, 1.0], [6.7216868954665256e+16, 295784765041063.1, 202637936045.26175, -22780898.93638217, 205.15524877583223, 1.0], [123022914844252.03, 2025598600989.9355, 7038321451.723578, 7292211.533895971, 1820.5540872255187, 1.0], [5798659105371.397, 73063980599.12009, 280808963.08350337, 647166.1744273758, 969.0169714952737, 1.0], [-78112612992.76324, 3642113943.605489, 91804780.43020414, 430751.9137746645, 379.78281402152874, 1.0], [386280523.6835199, 257220540.02040184, 11120485.271634161, 129819.12602039713, 471.40597550979186, 1.0], [-10693211009.303871, -1223510009.089181, -22274545.978089698, 18549.30307838344, -301.55242573732323, 1.0], [-9964085.923738554, -4365150.737138249, -411386.6438696054, -10484.174450932624, 15.149670076979616, 1.0], [541842.864264581, 193482.276072197, 9029.947067528488, -46.62458209582185, -2.9624038642089237, 1.0], [-220621.23296789522, -274557.9340203262, -86401.22446411186, -8443.070539356306, -136.23322675049926, 1.0], [3251.5504349303765, 6784.343990577191, 3934.5214064481625, 757.415724007334, 35.811562912463835, 1.0], [53.522734456906555, 170.40437538950374, 161.01012955496134, 53.54537645357537, 6.286762810448105, 1.0], [7.431444192909625, 26.301988720695135, 22.960637706381636, 4.801125750057049, 2.6341536761332267, 1.0], [-0.09119759477325269, -0.1801061475533146, 1.1300524605333688, 2.654994783545935, 1.3104918958676977, 1.0], [-0.003219836192304881, -0.06183937303877033, -0.40176496853684673, -1.0694441157327017, -0.7230743001450916, 1.0], [-0.0006010237525033756, -0.004761184653841278, 0.0793179816483663, 0.5022239858108873, -0.3793171422255701, 1.0], [8.145183997513813e-05, 0.005514321701324045, 0.10462537982334606, 0.5591837393502016, -0.4962661882194018, 1.0], [-0.0015949168754582125, -0.14279546004455532, -2.4776545238210446, 0.8797754471386745, -5.918081385718453, 1.0], [-0.0004175562163856941, -0.021446016127794405, -0.06189815758682979, 0.1741242853443794, 0.4345628604094691, 1.0], [1.3086618344372457e-07, 3.314955362016943e-05, 0.0019252809212552016, 0.00649537976330168, 0.14368642580610227, 1.0], [-5.903426856053935e-07, -0.00010040636378527861, -0.00038871197761919683, 0.015942278794350923, -0.06787066591282914, 1.0], [-5.058158205238362e-09, -4.452304825119217e-06, -0.0008198822672829164, 0.01904864306291245, -0.16068039934665684, 1.0], [-5.653709672617434e-11, -9.033364681018711e-08, -3.206216480769893e-05, 0.0005282786596535016, -0.05107404858407456, 1.0] ] NTU_from_plate_2_4_parallel_offset = [7.5e-08, 1.5e-07, 3e-07, 6e-07, 1.2e-06, 2.4e-06, 4.8e-06, 9.6e-06, 1.92e-05, 3.84e-05, 7.68e-05, 0.0001536, 0.0003072, 0.0006144, 0.0012288, 0.0024576, 0.0049152, 0.0098304, 0.0196608, 0.0393216, 0.0786432, 0.1572864, 0.3145728, 0.6291456, 1.2582912, 2.5165824, 5.0331648, 10.0663296, 20.1326592, 40.2653184, 80.5306368 ] NTU_from_plate_2_4_parallel_p = [ [9.311147683727706e+30, 4.5399729910913565e+24, 3.1701820445412416e+17, -3255667075.4059496, 34.19784973443562], [-8.462341214355869e+28, -1.0512639657566505e+23, -2.397873666023334e+16, -1034750601.3220837, 32.81155554496288], [-3.6927863016874774e+26, -5.302214079709395e+20, 234857662396561.97, 234020488.33100477, 31.425261515365865], [1.4107278670661704e+28, 1.0940739712642787e+22, -9489860868172880.0, -687734269.4135523, 30.03896731810403], [1.1202073850258956e+26, 9.003750344938989e+20, 1018203877208583.4, -168299066.7668561, 28.65267341622791], [1.8786218497132513e+24, 2.175783072978128e+19, -23527291714453.8, -214131092.84929407, 27.266380416202626], [1.594257427685343e+23, 2.5690709590606633e+18, 5268834190667.634, -5146824.418519467, 25.880088256899946], [-1.288122210563083e+24, -2.519981017465274e+19, 71292241683275.88, 50019381.18860227, 24.493798790383742], [4.550486151425468e+20, 6.198563773137455e+16, 1012994755481.759, -6103023.336117795, 23.107514134304758], [1.4422649047868342e+19, 4076064182439184.0, 179797171632.60355, 949172.8606007934, 21.721238910630372], [-1.9069286759409193e+19, -1.2582415698734126e+16, -1417288085609.7737, -31351459.7223076, 20.334983003863815], [1.7928329210392084e+18, 381796280677969.25, -92227167011.14949, -1569077.3721812628, 18.94876593787189], [760697593557199.6, 2716878092435.118, 1858731756.7367892, 384593.9690124189, 17.562626424902703], [-2980345873795.288, 16009202094.020222, 90691363.25017029, 80729.4890879997, 16.17664340548921], [131451648291400.28, 725493611523.8276, 609748841.6264181, 108426.56091552545, 14.790977175191966], [-6486852327.346251, -700388548.9015493, -5924391.104567888, -7146.787711492918, 13.40595533578488], [3015535151.0983796, 286704001.5011526, 4236710.766282784, 17437.87545106799, 12.022255562960611], [-10942972.229197213, -1899779.4140737972, -10761.968304033877, 1039.3968445688706, 10.641289014994197], [8220953.631429097, 2918560.727065227, 148692.00451266085, 2287.789927663398, 9.266006978920416], [-1464338.9963959353, 124017.62034205094, 30117.83144319644, 483.6759667244992, 7.902526411526671], [66462.98718285977, 130896.08947621491, 17250.88254238583, 284.03597528444567, 6.5631079987885395], [10553.524946159525, 11986.93762850903, 1221.0626417135122, -30.985812105731874, 5.270578375104107], [10.082588418517084, 303.14370012586807, 202.6175740387262, 25.89698508509613, 4.062131572817784], [0.5499329874641293, 17.521023550239388, 43.72139301663008, 27.37645885999178, 2.986658250212071], [-0.010913878122056958, 0.45840298571677157, 2.2251798856421043, 2.423889635140142, 2.090206859688884], [0.0006495212907688513, 0.32658889500269045, 4.465059447323286, 11.849683742435108, 1.3958641696720409], [-0.00010585785411094622, -0.036741246609915734, -0.7420406683674805, -3.1387647586257446, 0.8947297519985067], [0.0002762890128259569, 0.07593463651942846, 1.0334886344644887, -1.3947478189369613, 0.5543111790837415], [5.081221447274532e-06, 0.017547763053272165, 0.7422027775716376, -0.4571910134917369, 0.3340446930199166], [-2.616743919341764e-08, 0.00020934843129808607, 0.020386785890459298, -0.02006239152998345, 0.19683902757373653], [-3.101568430040571e-08, 0.00033734949109025253, 0.06821623160637866, -0.15878348849333124, 0.1138774497175727] ] NTU_from_plate_2_4_parallel_q = [ [3.291974284016541e+29, 1.4046022258329841e+23, 9191295776559532.0, -94421155.58205885, 1.0], [-3.193039887202064e+27, -3.4584178939213135e+21, -744806062875625.5, -31129803.142464183, 1.0], [-1.4381734658615522e+25, -1.693967837916621e+19, 8744771440563.748, 7659034.457524104, 1.0], [5.334771832140096e+26, 3.310748737686997e+20, -318539018055488.6, -22783770.25004286, 1.0], [4.926301588203296e+24, 3.3624134280590442e+19, 35173571760931.96, -5815597.159534814, 1.0], [8.649595607153984e+22, 8.156774026772406e+17, -1108318826792.9722, -7822738.38075017, 1.0], [7.404592532060123e+21, 1.0301055990958646e+17, 198966807158.52313, -182772.06662951043, 1.0], [-6.23738327477897e+22, -1.0048009493765537e+18, 2927622159692.4263, 2050629.9961337105, 1.0], [2.6592386481247224e+19, 2908854062634457.0, 42550661625.26152, -259606.3621141768, 1.0], [9.134101063562578e+17, 206794469159654.2, 8356789568.550677, 46095.68024652044, 1.0], [-1.3457090730356344e+18, -697630890940867.9, -71678079579.3958, -1540469.3978697397, 1.0], [1.1648162142676827e+17, 16957170961125.854, -4925846474.02719, -82119.20066004853, 1.0], [69235668361192.89, 184631921507.40326, 113484951.21664792, 22269.071053282812, 1.0], [-214631136673.61963, 1622101574.6971405, 6486940.571456372, 5191.662978533358, 1.0], [13037201476178.576, 53359497555.82677, 41997862.035921745, 7440.558062890958, 1.0], [-1510382156.1036587, -72336552.91298369, -482917.9480942061, -472.4787451295257, 1.0], [601613270.3192981, 32675351.48453366, 399065.14389228437, 1484.22312195332, 1.0], [-2806385.722630836, -221612.42147962135, 234.94182591367698, 116.6915537324147, 1.0], [2708621.889882308, 453979.7463113455, 18565.770503192096, 257.75689787361085, 1.0], [-222744.307636411, 37667.70409365177, 4153.602682046006, 67.49251582259824, 1.0], [57484.8140277244, 29249.909883546512, 2777.238094492285, 46.96976971793605, 1.0], [6211.193365476433, 2807.633263716254, 215.9750919403814, -3.689822852474681, 1.0], [68.48406976517343, 136.01076483156524, 57.439094496787085, 7.674214233620933, 1.0], [3.138051268210192, 15.474492941908835, 21.438163552556862, 9.928142597214924, 1.0], [0.052309953216059274, 0.647521303684222, 1.5240912769957224, 1.5965807136033143, 1.0], [0.03724768734929924, 0.9146363469974856, 5.2570155120528295, 8.733003177321063, 1.0], [-0.004061747315746429, -0.14038236184631592, -1.29781295599634, -3.375368854837777, 1.0], [0.008747917519380184, 0.2707496260037431, 1.685727780371999, -2.445440187207637, 1.0], [0.0015720431608568725, 0.1353192513128876, 2.1708447802007065, -1.3315013189823655, 1.0], [1.6024647013909014e-05, 0.0030671976634641853, 0.10156204670681959, -0.08262254790017286, 1.0], [2.3229283659683647e-05, 0.008935849190909461, 0.5851500679118268, -1.384387683729736, 1.0] ] NTU_from_plate_2_2_parallel_counterflow_offset = [7.5e-08, 1.5e-07, 3e-07, 6e-07, 1.2e-06, 2.4e-06, 4.8e-06, 9.6e-06, 1.92e-05, 3.84e-05, 7.68e-05, 0.0001536, 0.0003072, 0.0006144, 0.0012288, 0.0024576, 0.0049152, 0.0098304, 0.0196608, 0.0393216, 0.0786432, 0.1572864, 0.3145728, 0.6291456, 1.2582912, 2.5165824, 5.0331648, 10.0663296, 20.1326592, 40.2653184, 80.5306368 ] NTU_from_plate_2_2_parallel_counterflow_p = [ [-1.3540947044203507e+30, -8.410868237241077e+23, -9.592458969288491e+16, -2069815723.7830818, 32.81155785582896], [-5.909190345903646e+27, -4.24286158072528e+21, 939084401227737.4, 468010968.1889994, 31.42526592915459], [2.2580839013543478e+29, 8.759887723905137e+22, -3.795586069473787e+16, -1376170474.1965516, 30.03897572979474], [1.792810256083172e+27, 7.204726957462461e+21, 4073730638465264.0, -336704176.572998, 28.652689407724193], [3.0136351918641795e+25, 1.7449506492771002e+20, -94436408765550.34, -429497368.9344826, 27.2664107356295], [2.5503663600480286e+24, 2.0547278237477323e+19, 21065007797896.387, -10291984.296030033, 25.8801455685103], [-2.068959886273596e+25, -2.0275177343647046e+20, 283363617196754.2, 99817061.51082629, 24.493906760153582], [7.316250745095077e+21, 4.981073282863969e+17, 4071178832783.351, -12246412.243451085, 23.10771675633743], [2.303093386026652e+20, 3.2523232692334844e+16, 716985370983.0242, 1890719.1416514283, 21.72161752401211], [-4.4715545423999264e+20, -1.4745520169872864e+17, -8317355851715.972, -92722811.75950827, 20.335686947153395], [3.048309794645512e+19, 3461917411846938.0, -362599062006.45734, -3395666.6901195566, 18.95006719428506], [1.0840830998464214e+16, 18649593079388.15, 6191089972.499226, 639683.9318348671, 17.565015462728365], [-56829165736312.85, 70564590547.99759, 306929905.61277986, 148301.85724583076, 16.18099391544868], [310867835909252.25, 2199488772836.398, 1846929222.0995405, 116534.16034655277, 14.79882120114736], [-334740697066.67944, -11566416197.512352, -46807636.94628418, -36055.36084122248, 13.41992479832999], [26840001873.396393, 1176747347.4470434, 8813678.971174415, 20286.661696956173, 12.046746116490432], [331913333.4983728, 40516050.23123385, 806397.6785176995, 5236.358697987059, 10.683356504174844], [44564097.249639705, 3136095.9802567307, 13197.27737274941, 472.46470130684173, 9.336331182644951], [17994131.336623687, 4944651.796285454, 176426.70513709838, 1635.990096945895, 8.015855071773482], [1175197.7515924468, -5987292.526887681, -1249674.1787671829, -47443.87985192143, 6.736866971917378], [32169.38393625651, 35667.678007831906, 3281.4651865653304, 10.308412025244055, 5.519759829746941], [726.6676328162513, 2656.6374294136144, 938.8331291251928, 91.97929785764381, 4.389835242348104], [19.69824258336419, 111.99161644970393, 66.99538082454526, 16.542529871221728, 3.3746537841440527], [0.14533082879935388, 4.621483129606414, 13.046154764778136, 10.666553990710677, 2.4990539191450107], [0.002263983168833709, 0.012748221620148423, 0.08650686829071288, 1.2031142177285348, 1.779035515465167], [-9.830589986253666e-05, -0.0029990944273936377, 0.07473329578861215, 0.6680604884342561, 1.2170809731605838], [-0.00023383933828264596, -0.055094751033442003, -0.9014330674014724, -3.2873931899225397, 0.8013800839068237], [-4.59305763926361e-06, -0.0023384643471826417, -0.075298488101913, -0.519264025248997, 0.5094004416089638], [8.296798367082449e-06, 0.005019553134541902, 0.14520784160424574, -0.1480560699285633, 0.3138448562017667], [-0.00010404498951688742, -0.042625544741268964, 0.07039958323658324, -0.19160439461435394, 0.18823178739081262], [1.9257874863594892e-08, 5.065044581803412e-05, 0.005970986973437208, 0.007799255403717845, 0.11036344917508734] ] NTU_from_plate_2_2_parallel_counterflow_q = [ [-5.109312737272847e+28, -2.7669837286303267e+22, -2979525666165644.0, -62269188.45954034, 1.0], [-2.3013695133305585e+26, -1.3555563147438998e+20, 34967643100737.21, 15317110.975382129, 1.0], [8.539176311283978e+27, 2.651062211838455e+21, -1274041588071886.2, -45590896.094170064, 1.0], [7.884125680680726e+25, 2.690570806322227e+20, 140725727389471.77, -11634889.668290695, 1.0], [1.3875230679709336e+24, 6.541525966824309e+18, -4448026600910.923, -15690760.761365911, 1.0], [1.1845109172174247e+23, 8.238643367203461e+17, 795468754178.3317, -365479.85193633026, 1.0], [-1.0019142582756757e+24, -8.086708001888148e+18, 11636573241044.895, 4092189.8130896357, 1.0], [4.274893801197289e+20, 2.337481097686945e+16, 171016757014.2874, -520955.8420503665, 1.0], [1.4582333948892862e+19, 1649920265439080.0, 33323373506.59859, 91837.83231499583, 1.0], [-3.154069768612261e+19, -8175627871376203.0, -420703621074.17615, -4557050.2207395835, 1.0], [1.9847893757166674e+18, 157724843212160.53, -19387590413.827114, -177816.88499161773, 1.0], [978720427117839.0, 1262120134355.849, 377566806.9648005, 37158.4755696676, 1.0], [-4513922446287.058, 8413417806.407876, 22154634.809136182, 9566.661311724654, 1.0], [38070523736321.266, 175066430198.3794, 126396868.72314765, 8093.653907458961, 1.0], [-61489371269.06048, -1171842872.8663423, -3846269.020383889, -2566.272998013682, 1.0], [5045193301.256966, 133092026.4251891, 834715.8381330054, 1750.712772778083, 1.0], [87384327.07596397, 5685052.186587454, 91282.50331715525, 527.4122879147639, 1.0], [10190075.423735108, 370265.87179662683, 1818.871879286359, 71.60265020472393, 1.0], [6596827.383166512, 848613.961529598, 24268.6940300083, 216.01923171376424, 1.0], [-1866586.5008091372, -1826397.6735039286, -233559.34184976644, -7035.606933227122, 1.0], [23329.113452411028, 8850.625264022498, 589.593396865346, 5.78276680823048, 1.0], [1025.6980944478596, 1030.4061217224696, 257.83080542412074, 23.20339328125292, 1.0], [36.169804435094974, 56.11202269478698, 25.35072952019013, 6.191692301948293, 1.0], [0.870223837663587, 4.952736276431698, 8.141700513572173, 5.001805420623979, 1.0], [0.0053042419079786796, 0.012398428202822248, 0.27504711840752394, 1.0888403188024758, 1.0], [-0.0006872648984212443, 0.006963112330393481, 0.17402749161779235, 0.7777215103341757, 1.0], [-0.006590838477539741, -0.19611938648031352, -1.640780687507329, -3.97716372393897, 1.0], [-0.00024349857853046174, -0.013728589278679348, -0.21715653326322837, -0.9520846696079065, 1.0], [0.0005124101312746552, 0.03261073652701299, 0.4456416681314436, -0.4360265030003868, 1.0], [-0.004815365117901423, -0.219395737344097, 0.35487804621679386, -0.9991702722289716, 1.0], [4.108812052585529e-06, 0.000985483311381511, 0.05478155149325794, 0.0804130994573366, 1.0] ] NTU_from_H_2_unoptimal_offset = [7.5e-08, 1.5e-07, 3e-07, 6e-07, 1.2e-06, 2.4e-06, 4.8e-06, 9.6e-06, 1.92e-05, 3.84e-05, 7.68e-05, 0.0001536, 0.0003072, 0.0006144, 0.0012288, 0.0024576, 0.0049152, 0.0098304, 0.0196608, 0.0393216, 0.0786432, 0.1572864, 0.3145728, 0.6291456, 1.2582912, 2.5165824, 5.0331648, 10.0663296, 20.1326592, 40.2653184, 80.5306368 ] NTU_from_H_2_unoptimal_p = [ [9.311090177068194e+30, 4.5399475564038554e+24, 3.1701651864920506e+17, -3255644601.8905473, 34.19784939508176], [-8.462125737049189e+28, -1.051236894580789e+23, -2.39780488292632e+16, -1034705971.0814927, 32.81155489224577], [-3.69265415130165e+26, -5.301822567178955e+20, 234882398081691.12, 234024752.99665904, 31.425260261916403], [1.410560599685134e+28, 1.093808467030952e+22, -9490122636110636.0, -687633577.4144348, 30.038964915173185], [1.1201146547787187e+26, 9.003076575091149e+20, 1018131781915969.1, -168283241.34922192, 28.652668818329857], [1.877203115154918e+24, 2.174212412689376e+19, -23504286677434.996, -213953804.07995003, 27.266371636249264], [1.5943258764466105e+23, 2.5692522022742523e+18, 5269608999836.621, -5146997.019028249, 25.88007152864389], [-1.2864224116384471e+24, -2.5151093731537924e+19, 71437838753037.27, 50054408.15140979, 24.49376699655128], [4.543942596226072e+20, 6.190381882242281e+16, 1011578832812.2455, -6097146.227414874, 23.107453874576674], [1.443032893602968e+19, 4079127223298191.0, 179961190587.62994, 950472.5517376459, 21.72112504269345], [-1.728525301221655e+19, -1.1407375350719094e+16, -1284373100357.226, -28343072.8472184, 20.334768568331974], [1.758725436886904e+18, 365882402028983.06, -92828439127.84097, -1530591.291875413, 18.948363656972532], [792036415908859.2, 2862760755487.6484, 1976373644.0058162, 409085.29888017545, 17.561875017574707], [-2786989433439.3467, 18416834507.402943, 95324460.57822925, 82900.5916379863, 16.175246819451317], [128921676834374.16, 719301630875.2167, 615567114.8357931, 110348.07378592492, 14.788396313036571], [-2794040572.6818414, -507746769.4624018, -4437994.568915218, -4358.585576142468, 13.401217944728895], [3853463713.3106866, 379360075.81886023, 5594276.687695783, 22313.0443512979, 12.013630281672944], [66137990.08414679, 19025214.66224554, 664515.59380578, 6201.159018478011, 10.625745240108772], [9822129.89007198, 3897096.80061018, 202285.06707868786, 2999.612381935198, 9.238376729914885], [-948514.706520815, 374713.5998567277, 41481.55233261393, 494.59805780216936, 7.8543751267742525], [-11980.837950093153, -20667.491465511925, -2068.4413590656304, 80.22238868415197, 6.481745527422999], [-838.008545833977, -6831.181943934252, -3093.5280521633795, -306.91642288399237, 5.139846224565047], [19.032578110906325, 391.13780322667174, 466.3339913926907, 133.0562843703077, 3.8685445515879007], [-0.6990369659716387, 25.392942977606992, 27.402588487800582, 0.6420111258460738, 2.733344882284591], [0.09338324529681567, 2.865351487389797, 5.114582382139363, 1.469511752917645, 1.8084168254866542], [-0.0007321936823915426, -0.11675608789113232, -1.0568175987314021, -1.748890445704759, 1.13225374983665], [0.0011779746288648211, 0.32293841158631104, 2.913257638698029, -2.4643506838886613, 0.6823222255523778], [5.3274221084394595e-06, 0.0014584108461436326, 0.00018851507626193995, -0.3401471788904687, 0.4011229006580691], [1.8271761689352308e-07, 0.0007480503587161893, 0.02957290171761351, -0.11994510120785795, 0.23177982864651042], [6.8784748559726126e-09, 1.0482965040695156e-05, 0.0002687892019338303, -0.025003862996259105, 0.13213333004089212], [8.763989208567784e-11, 3.591705984063844e-07, 4.921736181910166e-05, 0.0005333054403615145, 0.07446942413083177] ] NTU_from_H_2_unoptimal_q = [ [3.291954404217546e+29, 1.4045943998953848e+23, 9191247072077158.0, -94420499.23235528, 1.0], [-3.1929593654948206e+27, -3.458328656247019e+21, -744784566006064.1, -31128443.437835522, 1.0], [-1.4381207928261743e+25, -1.693830694462608e+19, 8745588741895.983, 7659170.596221632, 1.0], [5.334127451972166e+26, 3.309852357106662e+20, -318547388902977.1, -22780419.901297953, 1.0], [4.925904942982619e+24, 3.3621635012359696e+19, 35171092574049.785, -5815045.64860498, 1.0], [8.643102749014142e+22, 8.150919305905134e+17, -1107277799713.401, -7816238.671263142, 1.0], [7.404943670579725e+21, 1.0301814770628384e+17, 198996743041.04614, -182778.72900289771, 1.0], [-6.228991027766363e+22, -1.0027628939625004e+18, 2933582652790.756, 2052062.8183752794, 1.0], [2.6555314925123396e+19, 2905025729565769.5, 42490608344.3377, -259352.57588899546, 1.0], [9.139672887482086e+17, 206954301896627.38, 8364534478.701708, 46155.88226561543, 1.0], [-1.219999405456035e+18, -632482998158155.4, -64953233304.81653, -1392542.5072398013, 1.0], [1.1417868279414859e+17, 16091601606423.625, -4956298785.520264, -80089.7158649981, 1.0], [72281648309673.0, 194796621486.79834, 120708758.31747963, 23664.722681493673, 1.0], [-191688538350.4686, 1813128315.751175, 6801704.817635524, 5326.460112015771, 1.0], [12819548881301.445, 52995232684.17356, 42414015.75348217, 7571.915653206027, 1.0], [-902051651.0068148, -53121439.77681727, -359594.87348877365, -264.4646423506963, 1.0], [789302837.8910002, 43370445.43163683, 526337.3354614506, 1891.217157665758, 1.0], [20890921.591943897, 2670229.183391574, 73117.13860794589, 602.7662205426564, 1.0], [3563425.2597333514, 615785.8657853191, 25312.120646154457, 335.70276322683435, 1.0], [-5281.921891333585, 79753.7409802169, 5646.05302573743, 69.41887774964894, 1.0], [-10534.39102576053, -4471.762975806752, -281.81541885802784, 16.231070841093203, 1.0], [-2357.310740142227, -2582.904848108706, -744.555607310463, -57.3657004239234, 1.0], [113.868855200266, 255.96866595663204, 169.54873521825837, 35.836022237199735, 1.0], [6.394734078690184, 18.116469980060796, 10.094374048899589, 1.1083023536795746, 1.0], [0.7587189251723472, 3.0042361512189037, 3.205115156278497, 1.3209192849079732, 1.0], [-0.022869248133995868, -0.34815770989249484, -1.3774996352160318, -1.2636425293046998, 1.0], [0.060036246550538616, 1.1203157641107213, 3.7280232186165474, -3.462501131451635, 1.0], [0.00026946774515168733, 0.004270492842455563, -0.06591637316270788, -0.7704935865841344, 1.0], [0.0001068179798574021, 0.008394972657004668, 0.10682274388388532, -0.4776775454943614, 1.0], [1.506979766231119e-06, 0.00012838571550289634, -0.001854978747745747, -0.16888199753883318, 1.0], [4.521472806270594e-08, 1.1656542235467652e-05, 0.0007261231042891383, 0.01752787098221555, 1.0] ] NTU_from_G_2_unoptimal_offset = [7.5e-08, 1.5e-07, 3e-07, 6e-07, 1.2e-06, 2.4e-06, 4.8e-06, 9.6e-06, 1.92e-05, 3.84e-05, 7.68e-05, 0.0001536, 0.0003072, 0.0006144, 0.0012288, 0.0024576, 0.0049152, 0.0098304, 0.0196608, 0.0393216, 0.0786432, 0.1572864, 0.3145728, 0.6291456, 1.2582912, 2.5165824, 5.0331648, 10.0663296, 20.1326592, 40.2653184 ] NTU_from_G_2_unoptimal_p = [ [9.3109303298284e+30, 4.5398767600969564e+24, 3.170118237710197e+17, -3255582028.524916, 34.197848452021496], [-8.46153002817336e+28, -1.0511619908571051e+23, -2.3976144993098692e+16, -1034582403.5113205, 32.811553084100204], [-3.692290021887378e+26, -5.30074247026417e+20, 234950665825511.66, 234036525.3056526, 31.425256801589224], [1.4101005965316078e+28, 1.0930781123070384e+22, -9490843606104960.0, -687356679.2161583, 30.038958306458262], [1.1198607183619476e+26, 9.001227755812281e+20, 1017933807262523.2, -168239785.08146304, 28.652656224909716], [1.8733467374857413e+24, 2.16994206245589e+19, -23441677453694.316, -213471555.46532023, 27.26634769739391], [1.594512099343422e+23, 2.569743318791424e+18, 5271709548496.723, -5147454.137281263, 25.88002614751128], [-1.2818229253935495e+24, -2.5019202614319935e+19, 71832292062919.38, 50148771.068874955, 24.4936812282376], [4.526495011307341e+20, 6.168512896379921e+16, 1007792749560.6382, -6081385.246719949, 23.107292341412784], [1.4451218664677962e+19, 4087331357766796.5, 180399075525.08047, 953934.9722401868, 21.720822001301844], [-1.3869417526718001e+19, -9157013351748946.0, -1029788858691.2903, -22579365.615234394, 20.33420262961592], [1.6729941515616177e+18, 324820952603289.25, -94618051655.88188, -1432919.5602435556, 18.947312380207574], [881377079365588.4, 3273908659562.771, 2305553502.530553, 477247.7586482068, 17.5599347216217], [-2294712887420.415, 24687543307.214867, 107469238.04231983, 88609.84525340016, 16.171694155750217], [122305930099635.03, 705130609607.45, 635856450.1020223, 115721.47051671412, 14.78195757555553], [4611767764.284328, -109441848.88329092, -1372356.3121382846, 1370.2740105504195, 13.389707907074477], [10777953775.11337, 1102063066.2696054, 16002550.06022829, 59325.3942992436, 11.993442742792807], [177557128.393412, 49828237.759808525, 1637511.0220782922, 13518.269190691442, 10.591302235163992], [-11319417.192115635, -4781582.158127945, -231145.86260741882, -2382.4032164871915, 9.181987939464335], [4453125.420063021, 3506923.02340456, 273936.10624431854, 4847.528594239127, 7.7676966035380675], [13382.567476165745, 21430.76649959107, 2674.227284505346, 103.35356016929184, 6.3607932986774065], [543.272216259408, 4094.6104229588664, 2371.7677593267967, 332.1165814161398, 4.994049687616688], [1019.6252971158917, 7258.186980145784, 2649.616182665721, -234.08989484797726, 3.726702452635227], [-0.16061952890054718, 26.475103623327957, 43.95078747890926, 10.898179144782612, 2.631460758603036], [0.09149784249886796, 4.507115402896758, 15.671800736565093, 13.267529540880941, 1.761750359230347], [-0.00015770925539570476, -0.022822040552860745, -0.16411319759532497, 0.0669124755747916, 1.126534416642668], [0.00017204089843551025, 0.016550563961219227, -0.11929008580672096, -1.7458596555122052, 0.6944621419501356], [-7.685285829457997e-05, 0.0018127599213621482, 0.2174778970361136, -0.33456541736023027, 0.41625508736123845], [9.408372464685374e-05, 0.02102006446772354, 0.00912807079932442, -0.1551911187056057, 0.24418730333772493], [5.544070897390911e-08, 9.56760771830238e-05, 0.006378971832410667, -0.003131142559408388, 0.1408475496320552] ] NTU_from_G_2_unoptimal_q = [ [3.291899133189346e+29, 1.4045726158959873e+23, 9191111431994574.0, -94418671.74467972, 1.0], [-3.192736723751476e+27, -3.4580817365485486e+21, -744725064710364.8, -31124678.831225593, 1.0], [-1.4379756315457668e+25, -1.6934523380520071e+19, 8747844401754.986, 7659546.39574443, 1.0], [5.332355293465041e+26, 3.307386549899383e+20, -318570445606863.2, -22771206.601799294, 1.0], [4.924818318184813e+24, 3.361477658856301e+19, 35164284666572.586, -5813531.206756055, 1.0], [8.625452913515019e+22, 8.135001664691246e+17, -1104444855152.0972, -7798558.608923992, 1.0], [7.405897764320868e+21, 1.0303870840706498e+17, 199077908074.35632, -182796.37614918998, 1.0], [-6.206279982934437e+22, -9.972450845965889e+17, 2949730732036.1255, 2055922.8784550051, 1.0], [2.6456407174731583e+19, 2894792447086410.5, 42330038939.27757, -258671.97913873894, 1.0], [9.154740126119596e+17, 207382210576832.97, 8385210219.820697, 46316.26048683696, 1.0], [-9.792909081377169e+17, -507714298948915.3, -52072419949.23038, -1109132.0457241398, 1.0], [1.0837682471753584e+17, 13849549366085.156, -5047510075.563598, -74938.92605805417, 1.0], [80947665751539.0, 223424288658.0732, 140917314.1477617, 27549.357113325757, 1.0], [-132644052917.59738, 2311595719.622853, 7627291.298373281, 5680.979777532329, 1.0], [12251665633349.52, 52214132651.39238, 43847873.4458912, 7939.025429824954, 1.0], [357856427.292005, -13251164.49142286, -104886.64220397608, 163.45236732230813, 1.0], [2296389972.242021, 126515566.06074195, 1501329.2443050412, 4980.7209489861, 1.0], [57930714.72719192, 6980962.132745493, 178865.45887007687, 1295.833190332679, 1.0], [-4491299.168210062, -759374.3426606245, -28261.14356909038, -248.17491581939632, 1.0], [2506378.5603899686, 662130.496745444, 39393.242284047185, 630.7413500970226, 1.0], [11844.477007158022, 4964.567889890172, 475.3789207352269, 20.27033156384447, 1.0], [1416.4504720022767, 1813.2683066018321, 634.6090169991996, 68.9432285360459, 1.0], [2588.630195894044, 3034.5274746385926, 618.0900263390289, -61.3460322498087, 1.0], [6.112796092986507, 23.856734715976987, 20.0956829765361, 5.00250593541833, 1.0], [1.0163551310040695, 6.580584763171997, 12.526779989536907, 8.018967861683878, 1.0], [-0.00425071771516727, -0.057961324447462347, -0.14113094148720395, 0.3271266362038584, 1.0], [0.003503234523706573, 0.006625141755068311, -0.5342364646029686, -2.37092136161077, 1.0], [-0.00024692055494272906, 0.04414352357324488, 0.4614638247959701, -0.7287208247762657, 1.0], [0.00372150090621385, 0.08764328509210832, 0.012510039071747442, -0.5966700220138256, 1.0], [1.214635258673814e-05, 0.0015853341009969197, 0.0448060202483571, -0.0022540017940275857, 1.0] ] NTU_from_P_J_2_offset = [7.5e-08, 2.25e-07, 6.75e-07, 2.025e-06, 6.075e-06, 1.8225000000000003e-05, 5.467500000000001e-05, 0.00016402500000000002, 0.000492075, 0.001476225, 0.004428675, 0.013286025, 0.039858075, 0.119574225, 0.358722675, 1.0761680249999999, 3.2285040749999996, 9.685512224999998, 29.056536674999997, 87.169610025] NTU_from_P_J_2_p = [ [1.379293737184234e+44, 5.439023353733237e+37, 4.2006081013865867e+30, 1.5600236133130092e+23, 1.0691012154169038e+16, 607948038.8625113, 35.584143946088], [3.977011844664325e+41, 4.3621401764830945e+35, 5.394318809746255e+28, 6.18977231622784e+20, 2371944977808139.5, -13729570.055684585, 33.386919579479546], [5.503399680509165e+39, 1.817486676934107e+34, 2.4835200809625483e+27, -4.94507688364635e+21, 1710942974186981.0, -323425148.685019, 31.189694983023596], [-5.688303341798886e+34, 1.9802128520243527e+30, 3.873601341667205e+25, 1.3266955228207854e+20, 32936950212583.0, -192901487.91097888, 28.99247043733991], [2.6577822797427475e+31, 3.677744229595766e+27, 9.720475078230118e+22, 7.630266992579775e+17, 1079206988084.657, -713421.4769980257, 26.795245857337733], [2.535644640454403e+31, 3.3854769442469036e+27, 7.414350002779228e+22, 2.7270860191982288e+17, 2032720913118.922, 2586699.473660061, 24.598021286804627], [-3.670558764178667e+26, -2.907829370394574e+23, -3.832557120797078e+19, -972481647974356.9, 8754962146.297441, -578040.0935389815, 22.40079670572366], [1.4567732266714603e+24, 2.0002302464098957e+21, 5.807908884342328e+17, 86872945369936.56, 11954307761.34507, 805855.0329613951, 20.203572198563034], [-1.8717456698528226e+20, -1.2176465524651423e+18, -778715222269321.5, 869869235169.809, 701696300.6903126, 123267.94767870879, 18.00634747585284], [-1.2720358195046216e+18, -2.994023342276172e+16, -115432726426873.97, -103528958523.52058, 5714026.505779477, -15843.156326800376, 15.809121639609089], [-693868088372775.0, -14665128532110.385, -40097140813.94941, -12207173.118260076, 874474.9845013419, 4765.386828156788, 13.611888987763974], [608526214919.5482, 213144699162.69724, 10494925952.93119, 161216968.40265524, 912961.118340575, 3645.556442288636, 11.414614080037193], [12400266.233880278, 55616915.46934441, 15009980.829033542, 1319417.6118004804, 48979.82322881266, 885.3320092533332, 9.217129450305661], [-2175425.2487209123, -50112748.10174761, -19806780.207386833, -1499087.9941959996, 37429.79938076639, -732.1927840030977, 7.019288428844824], [180.38230298958038, -6877.271284301687, -7818.717128329305, -1317.358943766587, 230.73216317292162, -43.76812039187138, 4.830718891488284], [-0.17395706235381111, -16.6766544106866, 17.378017727113797, 28.091561354012356, -3.3753097336005804, 9.746402232018756, 2.7734138119975618], [-0.08170110767689108, -1.9622386891531531, 31.973619141306784, -18.994072067345428, 18.01530939582972, -10.00277120433454, 1.2830169212938194], [-1.8673203116193894e-06, -0.0006729694269362634, -0.0038298059267577044, 0.17481160261931097, 0.5595504373144352, 1.1890765321959993, 0.5336606579343032], [-4.126347971671396e-07, -0.00010383779245126719, 0.0004941341620841793, -0.0036143614373294695, 0.03215823233734107, -0.13594349795793487, 0.2153400299994889], [1.1352761642923954e-12, 4.466300862534921e-09, 4.980466105756786e-07, -1.747924038757887e-05, 0.0005662659607976993, -0.0008124803373884537, 0.08545603782210896] ] NTU_from_P_J_2_q = [ [4.591167043547481e+42, 1.606114843399718e+36, 1.2027419595813414e+29, 4.5709455933251277e+21, 308809321150730.1, 17834210.46773281, 1.0], [1.4176611491239528e+40, 1.3607402099050933e+34, 1.5783578828462145e+27, 3.912507110657984e+19, 70413898494599.84, -144987.26733659825, 1.0], [2.1375766831490205e+38, 6.038431370476358e+32, 6.054805550705697e+25, -1.5264408314370513e+20, 53809598724793.99, -10274617.610954134, 1.0], [-2.141040713656621e+33, 8.876323674907398e+28, 1.4680506954801534e+24, 4.665180136962245e+18, 902143492964.3142, -6619437.068866051, 1.0], [1.432164429610735e+30, 1.6247132650490878e+26, 3.9301808580131244e+21, 2.9081928379120252e+16, 39088664097.274124, -14338.490436435039, 1.0], [1.3632572533340117e+30, 1.5022149283687727e+26, 3.0554280135011666e+21, 1.1447971515162446e+16, 83004234706.80063, 109620.15224286402, 1.0], [-2.429838319244439e+25, -1.5116925717630222e+22, -1.7919330403274191e+18, -42320389648484.82, 336427956.90190256, -24171.475216249233, 1.0], [1.029014370536014e+23, 1.1241714145850822e+20, 3.066377639010162e+16, 4603604903923.01, 614289753.7531514, 40490.27848023662, 1.0], [-1.7047361017944777e+19, -7.913845937988589e+16, -38110753754005.18, 56102625195.25173, 40336224.323338695, 7071.527033415434, 1.0], [-1.466460222837932e+17, -2368131962476749.0, -7883636266218.59, -6487219371.973534, 253874.3242902243, -916.4549397393939, 1.0], [-80727057098668.84, -1158659586259.3425, -3079314024.2938294, 658517.4797846868, 73213.81902982714, 383.2675505551966, 1.0], [154696420804.57706, 27182107403.606, 1083690882.8286123, 15089725.838928567, 83871.50963458995, 332.564715920644, 1.0], [14186109.875687288, 11604436.970824337, 2246816.302173693, 168932.58014667718, 5798.38653812484, 101.49810005349056, 1.0], [-10506354.591316702, -12816984.672097836, -3355998.4791482566, -200389.50800387145, 5079.542670150122, -101.92851768910828, 1.0], [-1306.7658976560958, -3118.914532403747, -1953.578672005278, -214.44697466919783, 37.10146877200857, -7.922586619768505, 1.0], [-3.4777812458808555, -2.66496086895917, 12.06040187108384, 9.382513303976543, 0.7618565915752666, 4.082394043941239, 1.0], [-0.5478521985532157, 4.4820443343765985, 21.325292193447055, -11.421627483324068, 12.178810914368531, -7.5579809400927775, 1.0], [-9.742102821144731e-05, -0.002065573589076824, 0.019790600076312437, 0.41450177639499663, 1.235749069760604, 2.312490295019506, 1.0], [-1.5856176703649368e-05, -0.00041496261982164757, 0.0018006879594633617, -0.012449317525219798, 0.13114216762570655, -0.6025944343758391, 1.0], [4.690094899391156e-10, 1.1078843951667822e-07, 3.7862270042457132e-06, -0.0001399289022755603, 0.006526270606084676, 0.00022681636474048618, 1.0] ] NTU_from_P_J_4_offset = [7.5e-08, 1.5e-07, 3e-07, 6e-07, 1.2e-06, 2.4e-06, 4.8e-06, 9.6e-06, 1.92e-05, 3.84e-05, 7.68e-05, 0.0001536, 0.0003072, 0.0006144, 0.0012288, 0.0024576, 0.0049152, 0.0098304, 0.0196608, 0.0393216, 0.0786432, 0.1572864, 0.3145728, 0.6291456, 1.2582912, 2.5165824, 5.0331648, 10.0663296, 20.1326592, 40.2653184 ] NTU_from_P_J_4_p = [ [1.1137179009139922e+22, 7276370402964671.0, 982286593.77106, 35.36100061385685], [3.9520974777651436e+20, 817675826063219.9, 326507731.5694956, 33.97470625485492], [-6.841863118463395e+19, -40856958897824.58, 81315681.91084157, 32.588411778703986], [-1.226994790378735e+20, -467148543840559.25, -248085221.6635263, 31.20211748691618], [6.48597266541994e+17, 11019359745896.43, 35584715.41588573, 29.8158232003719], [2.1079329317454016e+18, 35398661212068.19, 99510270.06019339, 28.429528868794645], [8935402325113268.0, 618377307638.4402, 8039429.253688355, 27.043234454056968], [5560958051752470.0, 452304392321.3483, 6957555.9298604345, 25.656940184631672], [126235735091357.48, 34964623767.870056, 1810157.0390347333, 24.270645740470233], [103033182132830.16, 36058139909.400696, 2072264.0804883514, 22.884351392471412], [-103982270484.21591, 838356119.2129722, 306578.86931115517, 21.498057055394064], [-7251975283452.386, -8287862367.975066, -1294281.061275808, 20.111763168535326], [-158106070969.9289, -328902231.04025, -61097.005884115875, 18.725469990458958], [-17294918972.910526, -76296368.6262589, -29707.458670753877, 17.339179506802296], [256922268.69636458, 5269647.432513786, 18204.805713360474, 15.952898438512468], [58898464.41169616, 1813429.6541062293, 10011.43433572321, 14.566648122016973], [446520.17751930194, 153233.62152383465, 3090.2164859982668, 13.180497004021207], [192422.14842327707, 48315.74348976142, 1547.3573889245085, 11.794657577802244], [22663.130317209336, 11333.59117949032, 700.5385580157777, 10.409766262101133], [1967.4393020460104, 2327.830281611502, 297.97297093031005, 9.027639452147119], [-318.3313733466705, 16.786387998216867, 82.34942017917173, 7.653122592980561], [11.458463050331853, 90.2649720625161, 49.98898169401306, 6.297942670584156], [0.1370275766836422, 13.527475822643838, 18.083157099365827, 4.9866549707448655], [-0.08811753949415488, 0.7580719231984764, 5.1618411905678885, 3.7607700748102135], [-0.026715037890536985, -7.268631456244228, -11.596324791801184, 2.6722136470814455], [0.0007400741868303971, 0.1167759145386527, 0.8860970545163467, 1.7671234621614083], [0.004830310767271772, 1.079863001801655, 6.078126424454076, 1.0804293879908267], [-0.0014226263147185095, -0.33627027530350345, -0.060678051246797524, 0.6204891876781652], [1.2996495928311868e-08, 7.487428756304456e-05, 0.01170061756683815, 0.34410237764731944], [-2.781758163682452e-07, -0.0008169026844673782, -0.06269490294979985, 0.18770099027480536] ] NTU_from_P_J_4_q = [ [3.838083335177554e+20, 222263807511839.7, 28532938.656622943, 1.0], [1.4833618909488933e+19, 26684604852189.977, 10002766.828354824, 1.0], [-2.475411036306694e+18, -1042375335783.4248, 2699804.4727502298, 1.0], [-4.833610251211771e+18, -15898701744151.383, -7844079.587894955, 1.0], [2.8854497720401664e+16, 416128712820.01227, 1249382.9866715993, 1.0], [9.364002670645787e+16, 1342489459568.0027, 3529555.587532283, 1.0], [453285058958963.44, 26079026061.61398, 312688.0379653563, 1.0], [286149498334759.3, 19473946611.856277, 279296.371346023, 1.0], [7422551752650.021, 1667363729.5110798, 78874.03269449141, 1.0], [6265733267256.983, 1757307727.953103, 92829.6964275974, 1.0], [1999652867.803483, 49852548.82714711, 15472.119794909637, 1.0], [-512013153282.79803, -455443511.83280444, -63707.00543582891, 1.0], [-12222218188.63782, -19143850.59303709, -2915.0995349095583, 1.0], [-1506979769.7819808, -4839415.404125513, -1525.5783956902367, 1.0], [29423132.775343254, 415645.0522902444, 1243.1836879724854, 1.0], [7318822.988441785, 154640.7646059886, 743.1491517287512, 1.0], [141201.0534254414, 16674.388169779257, 265.3197588068411, 1.0], [45153.80125997977, 5777.697281822111, 148.43127372342246, 1.0], [6966.966337389611, 1591.6903920455154, 77.05203947040957, 1.0], [917.2988119948286, 402.52765640680553, 38.61336284687747, 1.0], [-71.87668749611038, 26.972070330473336, 14.038555292975968, 1.0], [18.036042518702104, 27.128549126117615, 9.887065213985007, 1.0], [1.9767237853128081, 6.305760321878647, 4.800623897964247, 1.0], [0.04809865838967166, 1.0212459815948616, 2.083454689699847, 1.0], [-1.0298310153064745, -4.619556300508356, -3.90904614301033, 1.0], [0.016605629497247247, 0.1917630272238318, 0.7612198916261416, 1.0], [0.15032334712603257, 1.849779948438833, 5.776977591182038, 1.0], [-0.04700684698139208, -0.5503640789830394, -0.015203083681555351, 1.0], [7.574689191705429e-06, 0.0015757631170495458, 0.07698932879125621, 1.0], [-8.744918585890159e-05, -0.011691953640170439, -0.31212155570753924, 1.0] ] NTU_from_P_basic_crossflow_mixed_12_offset = [7.5e-08, 1.5e-07, 3e-07, 6e-07, 1.2e-06, 2.4e-06, 4.8e-06, 9.6e-06, 1.92e-05, 3.84e-05, 7.68e-05, 0.0001536, 0.0003072, 0.0006144, 0.0012288, 0.0024576, 0.0049152, 0.0098304, 0.0196608, 0.0393216, 0.0786432, 0.1572864, 0.3145728, 0.6291456, 1.2582912, 2.5165824, 5.0331648, 10.0663296, 20.1326592, 40.2653184, 80.5306368, 161.0612736, 322.1225472, 644.2450944, 1288.4901888 ] NTU_from_P_basic_crossflow_mixed_12_p = [ [1.0648657843926371e+28, 1.953857955950789e+22, 7266974622321844.0, 901310037.367627, 35.29646216590824], [-2.725970106134222e+28, -5.668910625458952e+22, -2.214546614947417e+16, -2075136770.0413792, 33.91016772641483], [3.317334215900722e+36, 1.83532794193258e+30, 1.3685590706146844e+21, 363848581451.6158, 32.52387319458047], [2.287548017339333e+25, 2.235626801942006e+20, 421302243477568.8, 237921355.15637708, 31.1375789140508], [3.7397053636559112e+25, 3.711246345042558e+20, 677728033156592.5, 293850388.0168432, 29.75128463980416], [1.0868350552139688e+30, 1.8455968608452927e+25, 4.7684805369296495e+19, 73543771834.092, 28.36499027336039], [-5.68877408709714e+22, -1.58579560884536e+18, -4299134851761.376, 23968028.669715144, 26.978695929717148], [1.6747242923170734e+22, 1.255438889589369e+18, 15434191675148.246, 27075146.549738526, 25.592401393795864], [1.0569269667427512e+22, 1.2502500074264484e+18, 18964792281222.582, -130738981.69876468, 24.2061071320861], [-2.5803066379744737e+18, 372261157232874.94, 28264835013.28186, -17050.367595574695, 22.819812804805323], [-9.755620022787496e+17, -725650029723333.5, -41926633472.52738, 3113611.357517518, 21.433518761319352], [-4.5273524706659283e+17, -909679128993800.9, -318106354724.0829, -28491925.038955502, 20.047224673431018], [9262291183292458.0, 19527324488391.098, 4613123691.341467, -663030.266969785, 18.660931382962406], [-1268775247799946.5, -14687587336923.14, -27389900433.704067, -12603645.373545203, 17.274641094810104], [3158577553978.4697, 71185736277.97443, 261619313.33159643, 249174.90223218722, 15.888360350134677], [152798849901.24323, 5254166915.823552, 29780970.83267744, 47672.85047654532, 14.502111276022058], [292692355448.6804, 11747722863.857492, 50327505.150009006, -93575.49190852011, 13.115964119634741], [1106317355.2031553, 127940907.99872206, 2104897.7308680704, 8042.20078053657, 11.7301387421864], [2197193.2553803152, 1285706.599464342, 94469.9714858117, 1982.9696716666438, 10.345295129780379], [628759.7709569605, 367127.78202066384, 28069.761808265386, 683.706593111148, 8.963325126986435], [528.078676943018, 2632.4181877354436, 1230.1904049453256, 174.67508689366082, 7.589305501960269], [-1494.2855542418577, -6088.743359266542, -2320.2207257673876, -183.19106697358848, 6.2356286720949745], [0.8507108729411813, 22.302501634010234, 45.4763530114471, 27.27372898572285, 4.928616927537855], [-1.2077329580227858, -36.62873095860811, -74.72114158071923, -30.567927983101946, 3.713928731013624], [0.8647680033690556, 31.52269638492401, 55.565702550395976, -4.780723933354412, 2.6509731108629784], [0.2985244757802897, 47.382849336432166, 448.40056689075203, 934.1164294026304, 1.7905411822239048], [-0.00021659913188603507, -0.031019118273585895, -0.06734409089895292, 1.4954198465032587, 1.1499737101680396], [2.1622908509544346e-05, 0.008770641444302658, 0.16604173854981807, 0.382168453132166, 0.7082821192347464], [4.196344927837933e-06, 0.0037795723343284817, 0.13015088360211113, 0.025875640008383147, 0.42214432523510087], [6.341115075750826e-09, 0.00019953280608039998, 0.01857524533658307, 0.00019535277989255078, 0.24534755148670773], [4.642762949093271e-06, 0.005095731601409439, -0.04639395438641826, 0.03610487934676299, 0.1398621181683815], [1.2306148922906546e-08, 4.004868303268181e-05, 0.003670103859870603, -0.030187701704396393, 0.07853403174401082], [4.3797422412183115e-12, 8.829865413669967e-08, 5.3467304702596584e-05, 0.0016073164432044349, 0.0435699623017177], [-7.062736110507592e-09, -7.668007088536754e-05, 0.0023318927789922703, -0.041990582834008715, 0.023936688503263276], [1.2508061870706862e-14, 2.573853599556939e-09, 7.24838458529996e-06, -0.00012330891215276327, 0.013044234435602044] ] NTU_from_P_basic_crossflow_mixed_12_q = [ [3.973671938704683e+26, 6.326552638003598e+20, 220710170167753.03, 26290926.9768005, 1.0], [-1.026756192847948e+27, -1.8529253231359474e+21, -678280420727251.2, -60801923.8992356, 1.0], [1.135562772430311e+35, 5.643879638731395e+28, 4.208089303288672e+19, 11187328400.684576, 1.0], [9.744383540727682e+23, 8.115450878596962e+18, 14270577510742.908, 7748023.36603521, 1.0], [1.5931786175761789e+24, 1.3561342279548963e+19, 23312891578604.902, 9932917.423178872, 1.0], [4.859688211478509e+28, 7.000358779926185e+23, 1.6811909822046449e+18, 2592795007.454948, 1.0], [-2.666944038287914e+21, -6.228033882597861e+16, -147002301520.06125, 903850.04214734, 1.0], [8.607232894250505e+20, 5.360902575722586e+16, 611331531040.4706, 1066077.3271792284, 1.0], [5.6923263643194255e+20, 5.5529930521810664e+16, 760135235703.9015, -5396770.908948376, 1.0], [-1.1447343385186822e+17, 19557170326968.332, 1212359780.557646, 1535.249044757823, 1.0], [-6.7087362640990504e+16, -37115893206839.71, -1786055637.3556702, 146483.33188585352, 1.0], [-3.4756109279027156e+16, -53271233725098.945, -16792652327.870298, -1420590.8612348323, 1.0], [717903881236155.8, 1149403566251.7737, 234365669.11825627, -35181.515793654195, 1.0], [-131068069929832.05, -1062923626979.946, -1723158048.0735118, -729415.4542761801, 1.0], [373138123021.75226, 5693132516.134963, 18041434.36138755, 15785.296488339987, 1.0], [19866365968.41335, 452347302.11955184, 2229754.0098727057, 3343.417286909013, 1.0], [39770464992.804535, 1030615797.581643, 3613628.7482191008, -7103.45523067367, 1.0], [200128825.45281503, 13652967.796242092, 190746.07900072972, 702.9359016386885, 1.0], [734053.0142461995, 188917.2993281958, 10859.229501012394, 201.49437641508658, 1.0], [209302.76040083222, 56139.34393204773, 3521.801145946451, 81.92398920910554, 1.0], [598.8866174601792, 715.9466859667624, 227.62193299094852, 26.319829515259006, 1.0], [-1479.3910297195062, -1619.331929198887, -432.5244387771081, -27.41252766738836, 1.0], [3.2574469229510354, 12.8571035438057, 15.091263524092597, 6.715596193986065, 1.0], [-5.223964240539322, -22.61021951185674, -26.126504739428494, -7.521201583422795, 1.0], [4.384367871411823, 20.758534092876616, 20.16342961309194, -1.3847296429245695, 1.0], [4.891865775589256, 81.08707101822866, 375.67471782459376, 521.9351243262005, 1.0], [-0.003354856095171886, -0.037975637276086216, 0.11222885303757815, 1.4337579152114632, 1.0], [0.0007839864026962514, 0.02894459533320549, 0.272639521725721, 0.611588697885286, 1.0], [0.0003025722525591445, 0.020683093661872085, 0.3104855870447247, 0.09936172614571803, 1.0], [1.3308588756670941e-05, 0.002314200802093514, 0.0756883464283556, 0.020616151735835784, 1.0], [0.0004083029953833101, 0.03304346443946263, -0.32908410954386225, 0.2683607066488552, 1.0], [2.7228204913745714e-06, 0.000755047941743242, 0.0447212276770666, -0.37916271374424443, 1.0], [4.914765639846191e-09, 5.27587799649255e-06, 0.001324873372051413, 0.03955253112545274, 1.0], [-4.6335857071244e-06, -0.0030716430602319663, 0.0950492776042504, -1.7528843558197416, 1.0], [1.2061450998792682e-10, 5.775284765597841e-07, 0.0005491863749069216, -0.008769384960859137, 1.0] ] def _NTU_from_P_objective(NTU1, R1, P1, function, *args): """Private function to hold the common objective function used by all backwards solvers for the P-NTU method. These methods are really hard on on floating points (overflows and divide by zeroes due to numbers really close to 1), so if the function fails, mpmath is imported and tried. """ P1_calc = function(R1, NTU1, *args) # Handled a larger range, not worth it # try: # P1_calc = function(R1, NTU1, **kwargs) # except : # try: # import mpmath # except ImportError: # pragma: no cover # raise ValueError('For some reverse P-NTU numerical solutions, the \ #intermediary results are ill-conditioned and do not fit in a float; mpmath must \ #be installed for this calculation to proceed.') # globals()['exp'] = mpmath.exp # P1_calc = float(function(R1, NTU1, **kwargs)) # globals()['exp'] = math.exp return P1_calc - P1 def _NTU_from_P_erf(NTU1: float, *args) -> float: """Private function to hold the common objective function used by all backwards solvers for the P-NTU method. These methods are really hard on on floating points (overflows and divide by zeroes due to numbers really close to 1), so if the function fails, mpmath is imported and tried. """ R1, P1, function = args[0], args[1], args[2] return function(R1, NTU1, *args[3:]) - P1 def _NTU_from_P_solver(P1: float, R1: float, NTU_min: float | None, NTU_max: float | None, function: Callable, guess: float | None, *args) -> int | float: """Private function to solve the P-NTU method backwards, given the function to use, the upper and lower NTU bounds for consideration, and the desired P1 and R1 values. """ args2 = (R1, P1, function) + args try: if guess is not None: guess2 = guess elif NTU_min < 2.0 < NTU_max: guess2 = 2.0 else: guess2 = NTU_min + 0.001*NTU_max if (NTU_min is not None and NTU_max is not None) and (guess2 < NTU_min or guess2 > NTU_max): guess2 = 0.5*(NTU_min + NTU_max) return secant(_NTU_from_P_erf, guess2, low=NTU_min, high=NTU_max, bisection=False, xtol=1e-13, args=args2) except: # secant failed. For some reason, the bisection in secant is going to wrong wrong value # floating point really sucks pass # Better for numerical stability if we don't need to evaluate these P1_max = _NTU_from_P_erf(NTU_max, *(R1, 0.0, function) + args) P1_min = _NTU_from_P_erf(NTU_min, *(R1, 0.0, function) + args) if P1 > P1_max: raise ValueError(f"No solution possible gives such a high P1; maximum P1={P1_max:f} at NTU1={NTU_max:f}") # numba: delete # raise ValueError("No solution") # numba: uncomment if P1 < P1_min: # raise ValueError("No solution") # numba: uncomment raise ValueError(f"No solution possible gives such a low P1; minimum P1={P1_min:f} at NTU1={NTU_min:f}") # numba: delete # Construct the function as a lambda expression as solvers don't support kwargs return brenth(_NTU_from_P_erf, NTU_min, NTU_max, args=args2) def _NTU_max_for_P_solver(ps: list[list[float]], qs: list[list[float]], offsets: list[float], R1: float) -> float: """Private function to calculate the upper bound on the NTU1 value in the P-NTU method. This value is calculated via a pade approximation obtained on the result of a global minimizer which calculated the maximum P1 at a given R1 from ~1E-7 to approximately 100. This should suffice for engineering applications. This value is needed to bound the solver. """ offset_max = offsets[-1] for offset, p, q in zip(offsets, ps, qs): if R1 < offset or offset == offset_max: x = R1 - offset return horner(p, x)/horner(q, x) def NTU_from_P_basic(P1: float, R1: float, subtype: str="crossflow") -> float: r"""Returns the number of transfer units of a basic heat exchanger type with a specified (for side 1) thermal effectiveness `P1`, and heat capacity ratio `R1`. The supported cases are as follows: * Counterflow (ex. double-pipe) [analytical] * Parallel (ex. double pipe inefficient configuration) [analytical] * Crossflow, single pass, fluids unmixed [numerical] * Crossflow, single pass, fluid 1 mixed, fluid 2 unmixed [analytical] * Crossflow, single pass, fluid 2 mixed, fluid 1 unmixed [analytical] * Crossflow, single pass, both fluids mixed [numerical] The analytical solutions, for those cases they are available, are as follows: Counterflow: .. math:: NTU_1 = - \frac{1}{R_{1} - 1} \ln{\left (\frac{P_{1} R_{1} - 1}{P_{1} - 1} \right )} Parallel: .. math:: NTU_1 = \frac{1}{R_{1} + 1} \ln{\left (- \frac{1}{P_{1} \left(R_{1} + 1\right) - 1} \right )} Crossflow, single pass, fluid 1 mixed, fluid 2 unmixed: .. math:: NTU_1 = - \frac{1}{R_{1}} \ln{\left (R_{1} \ln{\left (- \left(P_{1} - 1\right) e^{\frac{1}{R_{1}}} \right )} \right )} Crossflow, single pass, fluid 2 mixed, fluid 1 unmixed .. math:: NTU_1 = - \ln{\left (\frac{1}{R_{1}} \ln{\left (- \left(P_{1} R_{1} - 1\right) e^{R_{1}} \right )} \right )} Parameters ---------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] subtype : float The type of heat exchanger; one of 'counterflow', 'parallel', 'crossflow', 'crossflow approximate', 'crossflow, mixed 1', 'crossflow, mixed 2', 'crossflow, mixed 1&2'. Returns ------- NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- Although this function allows the thermal effectiveness desired to be specified, it does not mean such a high value can be obtained. An exception is raised when this occurs, although not always a helpful one. >>> NTU_from_P_basic(P1=.99, R1=.1, subtype='parallel') Traceback (most recent call last): ValueError: math domain error For the 'crossflow approximate' solution the function is monotonic, and a bounded solver is used within the range of NTU1 from 1E-11 to 1E5. For the full correct 'crossflow' solution, the initial guess for newton's method is obtained by the 'crossflow approximate' solution; the function may not converge because of inaccuracy performing the numerical integral involved. For the 'crossflow, mixed 1&2' solution, a bounded solver is first use, but the upper bound on P1 and the upper NTU1 limit is calculated from a pade approximation performed with mpmath. Examples -------- >>> NTU_from_P_basic(P1=.975, R1=.1, subtype='counterflow') 3.984769850376482 """ NTU_min = 1E-11 guess = None if subtype == "counterflow": return -log((P1*R1 - 1.)/(P1 - 1.))/(R1 - 1.) elif subtype == "parallel": return log(-1./(P1*(R1 + 1.) - 1.))/(R1 + 1.) elif subtype == "crossflow, mixed 1": return -log(R1*log(-(P1 - 1.)*exp(1./R1)))/R1 elif subtype == "crossflow, mixed 2": return -log(log(-(P1*R1 - 1.)*exp(R1))/R1) elif subtype == "crossflow, mixed 1&2": NTU_max = _NTU_max_for_P_solver(NTU_from_P_basic_crossflow_mixed_12_p, NTU_from_P_basic_crossflow_mixed_12_q, NTU_from_P_basic_crossflow_mixed_12_offset, R1) elif subtype == "crossflow approximate": # These are tricky but also easy because P1 can always be 1 NTU_max = 1E5 elif subtype == "crossflow": guess = NTU_from_P_basic(P1, R1, subtype="crossflow approximate") return secant(_NTU_from_P_objective, guess, args=(R1, P1, temperature_effectiveness_basic, "crossflow")) else: raise ValueError("Subtype not recognized.") return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, temperature_effectiveness_basic, guess, subtype) def NTU_from_P_G(P1: float, R1: float, Ntp: int, optimal: bool=True) -> float: r"""Returns the number of transfer units of a TEMA G type heat exchanger with a specified (for side 1) thermal effectiveness `P1`, heat capacity ratio `R1`, the number of tube passes `Ntp`, and for the two-pass case whether or not the inlets are arranged optimally. The supported cases are as follows: * One tube pass (tube fluid split into two streams individually mixed, shell fluid mixed) * Two tube passes (shell and tube exchanger with shell and tube fluids mixed in each pass at the cross section), counterflow arrangement * Two tube passes (shell and tube exchanger with shell and tube fluids mixed in each pass at the cross section), parallelflow arrangement Parameters ---------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1 or 2 [-] optimal : bool, optional Whether or not the arrangement is configured to give more of a countercurrent and efficient (True) case or an inefficient parallel case (only applies for two passes), [-] Returns ------- NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Notes ----- For numbers of tube passes greater than 1 or 2, an exception is raised. Although this function allows the thermal effectiveness desired to be specified, it does not mean such a high value can be obtained. An exception is raised which shows the maximum possible effectiveness obtainable at the specified `R1` and configuration. >>> NTU_from_P_G(P1=1, R1=1/3., Ntp=2) Traceback (most recent call last): ValueError: No solution possible gives such a high P1; maximum P1=0.954545 at NTU1=10000.000000 Of the three configurations, 1 pass and the optimal 2 pass have monotonic functions which allow for a bounded solver to work smoothly. In both cases a solution is searched for between NTU1 values of 1E-11 and 1E-4. For the 2 pass unoptimal solution, a bounded solver is first use, but the upper bound on P1 and the upper NTU1 limit is calculated from a pade approximation performed with mpmath. Examples -------- >>> NTU_from_P_G(P1=.573, R1=1/3., Ntp=1) 0.9999513707759524 """ NTU_min = 1E-11 function = temperature_effectiveness_TEMA_G if Ntp == 1 or (Ntp == 2 and optimal): NTU_max = 1E4 # We could fit a curve to determine the NTU where the floating point # does not allow NTU to increase though, but that would be another # binary bisection process, different from the current pipeline elif Ntp == 2 and not optimal: NTU_max = _NTU_max_for_P_solver(NTU_from_G_2_unoptimal_p, NTU_from_G_2_unoptimal_q, NTU_from_G_2_unoptimal_offset, R1) else: raise ValueError("Supported numbers of tube passes are 1 or 2.") return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, function, None, Ntp, optimal) def NTU_from_P_J(P1: float, R1: float, Ntp: int) -> float: r"""Returns the number of transfer units of a TEMA J type heat exchanger with a specified (for side 1) thermal effectiveness `P1`, heat capacity ratio `R1`, and the number of tube passes `Ntp`. The supported cases are as follows: * One tube pass (shell fluid mixed) * Two tube passes (shell fluid mixed, tube pass mixed between passes) * Four tube passes (shell fluid mixed, tube pass mixed between passes) Parameters ---------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1, 2, or 4, [-] Returns ------- NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Notes ----- For numbers of tube passes that are not 1, 2, or 4, an exception is raised. For the 1 tube pass case, a bounded solver is used to solve the equation numerically, with NTU1 ranging from 1E-11 to 1E3. NTU1 grows extremely quickly near its upper limit (NTU1 diverges to infinity at this maximum, but because the solver is bounded it will only increase up to 1000 before an exception is raised). >>> NTU_from_P_J(P1=.995024, R1=.01, Ntp=1) 13.940758768266656 >>> NTU_from_P_J(P1=.99502487562189, R1=.01, Ntp=1) # doctest: +SKIP Traceback (most recent call last): ValueError: No solution possible gives such a high P1; maximum P1=0.995025 at NTU1=1000.000000 For the 2 pass and 4 pass solution, a bounded solver is first use, but the upper bound on P1 and the upper NTU1 limit is calculated from a pade approximation performed with mpmath. These normally do not allow NTU1 to rise above 100. Examples -------- >>> NTU_from_P_J(P1=.57, R1=1/3., Ntp=1) 1.0003070138879664 """ NTU_min = 1E-11 function = temperature_effectiveness_TEMA_J if Ntp == 1: # Very often failes because at NTU=1000, there is no variation in P1 # for instance at NTU=40, P1 already peaked and does not decline with # higher NTU NTU_max = 1E3 # We could fit a curve to determine the NTU where the floating point # does not allow NTU to increase though, but that would be another # binary bisection process, different from the current pipeline elif Ntp == 2: NTU_max = _NTU_max_for_P_solver(NTU_from_P_J_2_p, NTU_from_P_J_2_q, NTU_from_P_J_2_offset, R1) elif Ntp == 4: NTU_max = _NTU_max_for_P_solver(NTU_from_P_J_4_p, NTU_from_P_J_4_q, NTU_from_P_J_4_offset, R1) else: raise ValueError("Supported numbers of tube passes are 1, 2, and 4.") return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, function, None, Ntp) def NTU_from_P_E(P1: float, R1: float, Ntp: int, optimal: bool=True) -> float: r"""Returns the number of transfer units of a TEMA E type heat exchanger with a specified (for side 1) thermal effectiveness `P1`, heat capacity ratio `R1`, the number of tube passes `Ntp`, and for the two-pass case whether or not the inlets are arranged optimally. The supported cases are as follows: * 1-1 TEMA E, shell fluid mixed * 1-2 TEMA E, shell fluid mixed (this configuration is symmetric) * 1-2 TEMA E, shell fluid split into two steams individually mixed * 1-3 TEMA E, shell and tube fluids mixed, one parallel pass and two counterflow passes (efficient) * 1-3 TEMA E, shell and tube fluids mixed, two parallel passes and one counteflow pass (inefficient) * 1-N TEMA E, shall and tube fluids mixed, efficient counterflow orientation, N an even number Two of these cases have analytical solutions; the rest use numerical solvers of varying quality. The analytical solution to 1-1 TEMA E, shell fluid mixed (the same as pure counterflow): .. math:: NTU_1 = - \frac{1}{R_{1} - 1} \ln{\left (\frac{P_{1} R_{1} - 1}{P_{1} - 1} \right )} 1-2 TEMA E, shell fluid mixed: .. math:: NTU_1 = \frac{2}{\sqrt{R_{1}^{2} + 1}} \ln{\left (\sqrt{\frac{P_{1} R_{1} - P_{1} \sqrt{R_{1}^{2} + 1} + P_{1} - 2}{P_{1} R_{1} + P_{1} \sqrt{R_{1}^{2} + 1} + P_{1} - 2}} \right )} Parameters ---------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1, 2, 3, 4, or an even number [-] optimal : bool, optional Whether or not the arrangement is configured to give more of a countercurrent and efficient (True) case or an inefficient parallel case, [-] Returns ------- NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Notes ----- For odd numbers of tube passes greater than 3, an exception is raised. For the 2 pass, unoptimal case, a bounded solver is used with NTU1 between 1E-11 and 100; the solution to any feasible P1 was found to lie in there. For the 4 or a higher even number of pass case, the upper limit on NTU1 is 1000; this solver works pretty well, but as NTU1 reaches its limit the change in P1 is so small a smaller but also correct solution is often returned. For both the optimal and unoptimal 3 tube pass case, a solution is only returned if NTU1 is between 1E-11 and 10. These functions are extremely mathematically frustrating, and as NTU1 rises above 10 catastrophic cancellation quickly results in this expression finding a ZeroDivisionError. The use of arbitrary prevision helps little - quickly 1000 digits are needed, and then 1000000 digits, and so one. Using SymPy's rational number support works better but is extremely slow for these complicated solutions. Nevertheless, so long as a solution is between 1E-11 and 10, the solver is quite robust. Examples -------- >>> NTU_from_P_E(P1=.58, R1=1/3., Ntp=2) 1.0381979240816719 """ NTU_min = 1E-11 function = temperature_effectiveness_TEMA_E if Ntp == 1: return NTU_from_P_basic(P1, R1, subtype="counterflow") elif Ntp == 2 and optimal: # Nice analytical solution is available # There are actually two roots but one of them is complex x1 = R1*R1 + 1. return 2.*log(((P1*R1 - P1*x1**0.5 + P1 - 2.)/(P1*R1 + P1*x1**0.5 + P1 - 2.))**0.5)*(x1)**-.5 elif Ntp == 2 and not optimal: NTU_max = 1E2 # Can't find anywhere it needs to go above 70 to reach the maximum elif Ntp == 3 and optimal: # no pade could be found, just about the worst-conditioned problem # I've ever found # Higher starting values result in errors NTU_max = 10 elif Ntp == 3 and not optimal: # no pade could be found, just about the worst-conditioned problem # I've ever found NTU_max = 10 elif Ntp == 4 or Ntp %2 == 0: NTU_max = 1E3 else: raise ValueError("For TEMA E shells with an odd number of tube passes more than 3, no solution is implemented.") return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, function, None, Ntp, optimal) def NTU_from_P_H(P1: float, R1: float, Ntp: int, optimal: bool=True) -> float: r"""Returns the number of transfer units of a TEMA H type heat exchanger with a specified (for side 1) thermal effectiveness `P1`, heat capacity ratio `R1`, the number of tube passes `Ntp`, and for the two-pass case whether or not the inlets are arranged optimally. The supported cases are as follows: * One tube pass (tube fluid split into two streams individually mixed, shell fluid mixed) * Two tube passes (shell fluid mixed, tube pass mixed between passes) * Two tube passes (shell fluid mixed, tube pass mixed between passes, inlet tube side next to inlet shell-side) Parameters ---------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Ntp : int Number of tube passes, 1, or 2, [-] optimal : bool, optional Whether or not the arrangement is configured to give more of a countercurrent and efficient (True) case or an inefficient parallel case, [-] Returns ------- NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 (shell side = 1, tube side = 2) [-] Notes ----- For numbers of tube passes greater than 1 or 2, an exception is raised. Only numerical solutions are available for this function. For the case of 1 tube pass or the optimal 2 tube pass, the function is monotonic and a bounded solver is used with NTU1 between 1E-11 and 100; it will find the solution anywhere in that range. For the non-optimal 2 pass case, the function is not monotonic and a pade approximation was used to obtain a curve of NTU1s which give the maximum P1s which is used as the upper bound in the bounded solver. The lower bound is still 1E-11. These solvers are all robust. Examples -------- >>> NTU_from_P_H(P1=0.573, R1=1/3., Ntp=1) 0.9997628696891168 """ NTU_min = 1E-11 if Ntp == 1: NTU_max = 100 elif Ntp == 2 and optimal: NTU_max = 100 elif Ntp == 2 and not optimal: NTU_max = _NTU_max_for_P_solver(NTU_from_H_2_unoptimal_p, NTU_from_H_2_unoptimal_q, NTU_from_H_2_unoptimal_offset, R1) else: raise ValueError("Supported numbers of tube passes are 1 and 2.") return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, temperature_effectiveness_TEMA_H, None, Ntp, optimal) def NTU_from_P_plate(P1: float, R1: float, Np1: int, Np2: int, counterflow: bool=True, passes_counterflow: bool=True, reverse: bool=False) -> float: r"""Returns the number of transfer units of a plate heat exchanger with a specified side 1 heat capacity ratio `R1`, side 1 number of transfer units `NTU1`, number of passes on sides 1 and 2 (respectively `Np1` and `Np2`). For all cases, the function also takes as arguments whether the exchanger is setup in an overall counter or parallel orientation `counterflow`, and whether or not individual stream passes are themselves counterflow or parallel. The 20 supported cases are as follows. (the first number of sides listed refers to side 1, and the second number refers to side 2): * 1 pass/1 pass parallelflow * 1 pass/1 pass counterflow * 1 pass/2 pass * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in parallel) * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in counterflow) * 1 pass/4 pass * 2 pass/2 pass, overall parallelflow, individual passes in parallel * 2 pass/2 pass, overall parallelflow, individual passes counterflow * 2 pass/2 pass, overall counterflow, individual passes parallelflow * 2 pass/2 pass, overall counterflow, individual passes counterflow * 2 pass/3 pass or 3 pass/2 pass, overall parallelflow * 2 pass/3 pass or 3 pass/2 pass, overall counterflow * 2 pass/4 pass or 4 pass/2 pass, overall parallel flow * 2 pass/4 pass or 4 pass/2 pass, overall counterflow flow For all except the simplest cases numerical solutions are used. 1 pass/1 pass counterflow (also 2/2 fully counterflow): .. math:: NTU_1 = - \frac{1}{R_{1} - 1} \ln{\left (\frac{P_{1} R_{1} - 1}{P_{1} - 1} \right )} 1 pass/1 pass parallel flow (also 2/2 fully parallelflow): .. math:: NTU_1 = \frac{1}{R_{1} + 1} \ln{\left (- \frac{1}{P_{1} \left(R_{1} + 1\right) - 1} \right )} Parameters ---------- P1 : float Thermal effectiveness of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] R1 : float Heat capacity ratio of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Np1 : int Number of passes on side 1 [-] Np2 : int Number of passes on side 2 [-] counterflow : bool Whether or not the overall flow through the heat exchanger is in counterflow or parallel flow, [-] passes_counterflow : bool In addition to the overall flow direction, in some cases individual passes may be in counter or parallel flow; this controls that [-] reverse : bool Used **internally only** to allow cases like the 1-4 formula to work for the 4-1 flow case, without having to duplicate the code [-] Returns ------- NTU1 : float Thermal number of transfer units of the heat exchanger in the P-NTU method, calculated with respect to stream 1 [-] Notes ----- The defaults of counterflow=True and passes_counterflow=True will always result in the most efficient heat exchanger option, normally what is desired. If a number of passes which is not supported is provided, an exception is raised. For more details, see :obj:`temperature_effectiveness_plate`. Examples -------- Three passes on side 1; one pass on side 2; two end passes in counterflow orientation. >>> NTU_from_P_plate(P1=0.5743, R1=1/3., Np1=3, Np2=1) 0.9998336056090733 """ NTU_min = 1E-11 if Np1 == 1 and Np2 == 1 and counterflow: try: return -log((P1*R1 - 1.)/(P1 - 1.))/(R1 - 1.) except: # raise ValueError("impossible") # numba: uncomment raise ValueError("The maximum P1 obtainable at the specified R1 is %f at the limit of NTU1=inf." %(1./R1)) # numba: delete elif Np1 == 1 and Np2 == 1 and not counterflow: try: return log(-1./(P1*(R1 + 1.) - 1.))/(R1 + 1.) except: # raise ValueError("impossible") # numba: uncomment raise ValueError(f"The maximum P1 obtainable at the specified R1 is {P_NTU_Pp(1E10, R1):f} at the limit of NTU1=inf.") # numba: delete elif Np1 == 1 and Np2 == 2: NTU_max = 100. elif Np1 == 1 and Np2 == 3 and counterflow: NTU_max = 100. elif Np1 == 1 and Np2 == 3 and not counterflow: NTU_max = 100. elif Np1 == 1 and Np2 == 4: NTU_max = 100. elif Np1 == 2 and Np2 == 2: if counterflow and passes_counterflow: return NTU_from_P_plate(P1, R1, Np1=1, Np2=1, counterflow=True, passes_counterflow=True) elif counterflow and not passes_counterflow: NTU_max = 100.0 elif not counterflow and passes_counterflow: NTU_max = _NTU_max_for_P_solver(NTU_from_plate_2_2_parallel_counterflow_p, NTU_from_plate_2_2_parallel_counterflow_q, NTU_from_plate_2_2_parallel_counterflow_offset, R1) elif not counterflow and not passes_counterflow: return NTU_from_P_plate(P1, R1, Np1=1, Np2=1, counterflow=False, passes_counterflow=False) elif Np1 == 2 and Np2 == 3: if counterflow: NTU_max = 100.0 elif not counterflow: NTU_max = _NTU_max_for_P_solver(NTU_from_plate_2_3_parallel_p, NTU_from_plate_2_3_parallel_q, NTU_from_plate_2_3_parallel_offset, R1) elif Np1 == 2 and Np2 == 4: if counterflow: NTU_max = 100.0 elif not counterflow: NTU_max = _NTU_max_for_P_solver(NTU_from_plate_2_4_parallel_p, NTU_from_plate_2_4_parallel_q, NTU_from_plate_2_4_parallel_offset, R1) elif not reverse: # Proved to work by example P2 = P1*R1 R2 = 1./R1 NTU2 = NTU_from_P_plate(R1=R2, P1=P2, Np1=Np2, Np2=Np1, counterflow=counterflow, passes_counterflow=passes_counterflow, reverse=True) NTU1 = NTU2/R1 return NTU1 else: raise ValueError("Supported number of passes does not have a formula available") return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, temperature_effectiveness_plate, None, Np1, Np2, counterflow, passes_counterflow) def P_NTU_method(m1: float, m2: float, Cp1: float, Cp2: float, UA: float | None=None, T1i: int | None=None, T1o: float | None=None, T2i: int | None=None, T2o: int | None=None, subtype: str="crossflow", Ntp: int=1, optimal: bool=True) -> dict[str, float]: r"""Wrapper for the various P-NTU method function calls, which can solve a heat exchanger. The heat capacities and mass flows of each stream and the type of the heat exchanger are always required. As additional inputs, one combination of the following inputs is required: * Three of the four inlet and outlet stream temperatures. * Temperatures for the side 1 outlet and side 2 inlet and UA * Temperatures for the side 1 outlet side 2 outlet and UA * Temperatures for the side 1 inlet and side 2 inlet and UA * Temperatures for the side 1 inlet and side 2 outlet and UA Computes the total heat exchanged as well as both temperatures of both streams. Parameters ---------- m1 : float Mass flow rate of stream 1 (shell side = 1, tube side = 2), [kg/s] m2 : float Mass flow rate of stream 2 (shell side = 1, tube side = 2), [kg/s] Cp1 : float Averaged heat capacity of stream 1 (shell side), [J/kg/K] Cp2 : float Averaged heat capacity of stream 2 (tube side), [J/kg/K] UA : float, optional Combined Area-heat transfer coefficient term, [W/K] T1i : float, optional Inlet temperature of stream 1 (shell side), [K] T1o : float, optional Outlet temperature of stream 1 (shell side), [K] T2i : float, optional Inlet temperature of stream 2 (tube side), [K] T2o : float, optional Outlet temperature of stream 2 (tube-side), [K] subtype : str, optional The subtype of exchanger; one of 'E', 'G', 'H', 'J', 'counterflow', 'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', or 'crossflow, mixed 1&2'. For plate exchangers 'Np1/Np2' where `Np1` is the number of side 1 passes and `Np2` is the number of side 2 passes Ntp : int, optional For real heat exchangers (types 'E', 'G', 'H', and 'J'), the number of tube passes needss to be specified as well. Not all types support any number of tube passes. optimal : bool, optional For real heat exchangers (types 'E', 'G', 'H', and 'J'), there is often a more countercurrent (optimal) way to arrange the tube passes and a more parallel (optimal=False) way to arrange them. This controls that. Returns ------- results : dict * Q : Heat exchanged in the heat exchanger, [W] * UA : Combined area-heat transfer coefficient term, [W/K] * T1i : Inlet temperature of stream 1, [K] * T1o : Outlet temperature of stream 1, [K] * T2i : Inlet temperature of stream 2, [K] * T2o : Outlet temperature of stream 2, [K] * P1 : Thermal effectiveness with respect to stream 1, [-] * P2 : Thermal effectiveness with respect to stream 2, [-] * R1 : Heat capacity ratio with respect to stream 1, [-] * R2 : Heat capacity ratio with respect to stream 2, [-] * C1 : The heat capacity rate of fluid 1, [W/K] * C2 : The heat capacity rate of fluid 2, [W/K] * NTU1 : Thermal Number of Transfer Units with respect to stream 1 [-] * NTU2 : Thermal Number of Transfer Units with respect to stream 2 [-] Notes ----- The main equations used in this method are as follows. For the individual expressions used to calculate `P1`, see the `See Also` section. .. math:: Q = P_1 C_1 \Delta T_{max} = P_2 C_2 \Delta T_{max} .. math:: \Delta T_{max} = T_{h,i} - T_{c,i} = |T_{2,i} - T_{1,i}| .. math:: R_1 = \frac{C_1}{C_2} = \frac{T_{2,i} - T_{2,o}}{T_{1,o} - T_{1, i}} .. math:: R_2 = \frac{C_2}{C_1} = \frac{T_{1,o} - T_{1, i}}{T_{2,i} - T_{2,o}} .. math:: R_1 = \frac{1}{R_2} .. math:: NTU_1 = \frac{UA}{C_1} .. math:: NTU_2 = \frac{UA}{C_2} .. math:: NTU_1 = NTU_2 R_2 .. math:: NTU_2 = NTU_1 R_1 .. math:: P_1 = \frac{T_{1,o} - T_{1,i}}{T_{2,i} - T_{1,i}} .. math:: P_2 = \frac{T_{2,i} - T_{2,o}}{T_{2,i} - T_{1,i}} .. math:: P_1 = P_2 R_2 .. math:: P_2 = P_1 R_1 .. math:: C_1 = m_1 Cp_1 .. math:: C_2 = m_2 Cp_2 Once `P1` has been calculated, there are six different cases for calculating the other stream temperatures depending on the two temperatures provided. They were derived with SymPy. Two known inlet temperatures: .. math:: T_{1,o} = - P_{1} T_{1,i} + P_{1} T_{2,i} + T_{1,i} .. math:: T_{2,o} = P_{1} R_{1} T_{1,i} - P_{1} R_{1} T_{2,i} + T_{2,i} Two known outlet temperatures: .. math:: T_{1,i} = \frac{P_{1} R_{1} T_{1,o} + P_{1} T_{2,o} - T_{1,o}}{P_{1} R_{1} + P_{1} - 1} .. math:: T_{2,i} = \frac{P_{1} R_{1} T_{1,o} + P_{1} T_{2,o} - T_{2,o}}{P_{1} R_{1} + P_{1} - 1} Inlet 1 known, outlet 2 known: .. math:: T_{1,o} = \frac{1}{P_{1} R_{1} - 1} \left(P_{1} R_{1} T_{1,i} + P_{1} T_{1,i} - P_{1} T_{2,o} - T_{1,i}\right) .. math:: T_{2,i} = \frac{P_{1} R_{1} T_{1,i} - T_{2,o}}{P_{1} R_{1} - 1} Outlet 1 known, inlet 2 known: .. math:: T_{1,i} = \frac{P_{1} T_{2,i} - T_{1,o}}{P_{1} - 1} .. math:: T_{2,o} = \frac{1}{P_{1} - 1} \left(R_{1} \left(P_{1} T_{2,i} - T_{1,o}\right) - \left(P_{1} - 1\right) \left(R_{1} T_{1,o} - T_{2,i}\right)\right) Input and output of 2 known: .. math:: T_{1,i} = \frac{1}{P_{1} R_{1}} \left(P_{1} R_{1} T_{2,i} - T_{2,i} + T_{2,o}\right) .. math:: T_{1,o} = \frac{1}{P_{1} R_{1}} \left(P_{1} R_{1} T_{2,i} + \left(P_{1} - 1\right) \left(T_{2,i} - T_{2,o}\right)\right) Input and output of 1 known: .. math:: T_{2,i} = \frac{1}{P_{1}} \left(P_{1} T_{1,i} - T_{1,i} + T_{1,o}\right) .. math:: T_{2,o} = \frac{1}{P_{1}} \left(P_{1} R_{1} \left(T_{1,i} - T_{1,o}\right) + P_{1} T_{1,i} - T_{1,i} + T_{1,o}\right) See Also -------- temperature_effectiveness_basic temperature_effectiveness_plate temperature_effectiveness_TEMA_E temperature_effectiveness_TEMA_G temperature_effectiveness_TEMA_H temperature_effectiveness_TEMA_J NTU_from_P_basic NTU_from_P_plate NTU_from_P_E NTU_from_P_G NTU_from_P_H NTU_from_P_J Examples -------- Solve a heat exchanger with the UA specified, and known inlet temperatures: >>> from pprint import pprint >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900, ... subtype='E', Ntp=4, T2i=15, T1i=130, UA=3041.75)) {'C1': 9672.0, 'C2': 2755.0, 'NTU1': 0.314490281224, 'NTU2': 1.104083484573, 'P1': 0.173081161436, 'P2': 0.60763738417, 'Q': 192514.714242, 'R1': 3.5107078039, 'R2': 0.28484284532, 'T1i': 130, 'T1o': 110.095666434, 'T2i': 15, 'T2o': 84.878299180, 'UA': 3041.75} Solve the same heat exchanger as if T1i, T2i, and T2o were known but UA was not: >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900, subtype='E', ... Ntp=4, T1i=130, T2i=15, T2o=84.87829918042112)) {'C1': 9672.0, 'C2': 2755.0, 'NTU1': 0.31449028122, 'NTU2': 1.10408348457, 'P1': 0.173081161436, 'P2': 0.60763738417, 'Q': 192514.714242, 'R1': 3.5107078039, 'R2': 0.2848428453, 'T1i': 130, 'T1o': 110.095666434, 'T2i': 15, 'T2o': 84.878299180, 'UA': 3041.7499999} Solve a 2 pass/2 pass plate heat exchanger with overall parallel flow and its individual passes operating in parallel and known outlet temperatures. Note the overall parallel part is trigered with `optimal=False`, and the individual pass parallel is triggered by appending 'p' to the subtype. The subpass counterflow can be specified by appending 'c' instead to the subtype, but this is never necessary as it is the default. >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, ... T1o=126.7, T2o=26.7, subtype='2/2p', optimal=False)) {'C1': 9672.0, 'C2': 2755.0, 'NTU1': 0.0310173697270, 'NTU2': 0.10889292196, 'P1': 0.0289452959747, 'P2': 0.101618476467, 'Q': 32200.0503078, 'R1': 3.5107078039, 'R2': 0.28484284532, 'T1i': 130.029202885, 'T1o': 126.7, 'T2i': 15.0121414490, 'T2o': 26.7, 'UA': 300} References ---------- .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002. .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. CRC Press, 2013. .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat Transfer, 3E. New York: McGraw-Hill, 1998. """ # Shellside: 1 # Tubeside: 2 C1 = m1*Cp1 C2 = m2*Cp2 R1 = C1/C2 R2 = C2/C1 if UA is not None: NTU1 = UA/C1 NTU2 = UA/C2 if subtype in ("counterflow", "parallel", "crossflow", "crossflow, mixed 1", "crossflow, mixed 2", "crossflow, mixed 1&2"): P1 = temperature_effectiveness_basic(R1, NTU1, subtype=subtype) elif subtype == "E": P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal) elif subtype == "G": P1 = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal) elif subtype == "H": P1 = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal) elif subtype == "J": P1 = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1, Ntp=Ntp) elif "/" in subtype: passes_counterflow = True Np1, end = subtype.split("/") if end[-1] in ["c","p"]: passes_counterflow = end[-1] == "c" end = end[0:-1] Np1, Np2 = int(Np1), int(end) P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=Np1, Np2=Np2, counterflow=optimal, passes_counterflow=passes_counterflow) else: raise ValueError("Supported types are 'E', 'G', 'H', 'J', 'counterflow',\ 'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', \ 'crossflow, mixed 1&2', or 'Np1/Np2' for plate exchangers") possible_inputs = [(T1i, T2i), (T1o, T2o), (T1i, T2o), (T1o, T2i), (T1i, T1o), (T2i, T2o)] if not any(i for i in possible_inputs if None not in i): raise ValueError("One set of (T1i, T2i), (T1o, T2o), (T1i, T2o), (T1o, T2i), (T1i, T1o), or (T2i, T2o) is required along with UA.") # Deal with different temperature inputs, generated with SymPy if T1i and T2i: T2o = P1*R1*T1i - P1*R1*T2i + T2i T1o = -P1*T1i + P1*T2i + T1i elif T1o and T2o: T2i = (P1*R1*T1o + P1*T2o - T2o)/(P1*R1 + P1 - 1.) T1i = (P1*R1*T1o + P1*T2o - T1o)/(P1*R1 + P1 - 1.) elif T1o and T2i: T2o = (R1*(P1*T2i - T1o) - (P1 - 1.)*(R1*T1o - T2i))/(P1 - 1.) T1i = (P1*T2i - T1o)/(P1 - 1.) elif T1i and T2o: T1o = (P1*R1*T1i + P1*T1i - P1*T2o - T1i)/(P1*R1 - 1.) T2i = (P1*R1*T1i - T2o)/(P1*R1 - 1.) elif T2i and T2o: T1o = (P1*R1*T2i + (P1 - 1.)*(T2i - T2o))/(P1*R1) T1i = (P1*R1*T2i - T2i + T2o)/(P1*R1) elif T1i and T1o: T2o = (P1*R1*(T1i - T1o) + P1*T1i - T1i + T1o)/P1 T2i = (P1*T1i - T1i + T1o)/P1 else: # Case where we're solving for UA # Three temperatures are required # Ensures all four temperatures are set and Q is calculated if T1i is not None and T1o is not None: Q = m1*Cp1*(T1i-T1o) if T2i is not None and T2o is None: T2o = T2i + Q/(m2*Cp2) elif T2o is not None and T2i is None: T2i = T2o - Q/(m2*Cp2) elif T2o is not None and T2i is not None: Q2 = m2*Cp2*(T2o-T2i) if abs((Q-Q2)/Q) > 0.01: raise ValueError("The specified heat capacities, mass flows," " and temperatures are inconsistent") else: raise ValueError("At least one temperature is required to be " "specified on side 2.") elif T2i is not None and T2o is not None: Q = m2*Cp2*(T2o-T2i) if T1i is not None and T1o is None: T1o = T1i - Q/(m1*Cp1) elif T1o is not None and T1i is None: T1i = T1o + Q/(m1*Cp1) else: raise ValueError("At least one temperature is required to be " "specified on side 2.") else: raise ValueError("Three temperatures are required to be specified " "when solving for UA") P1 = Q/(C1*abs(T2i-T1i)) if subtype in ("counterflow", "parallel", "crossflow", "crossflow, mixed 1", "crossflow, mixed 2", "crossflow, mixed 1&2"): NTU1 = NTU_from_P_basic(P1=P1, R1=R1, subtype=subtype) elif subtype == "E": NTU1 = NTU_from_P_E(P1=P1, R1=R1, Ntp=Ntp, optimal=optimal) elif subtype == "G": NTU1 = NTU_from_P_G(P1=P1, R1=R1, Ntp=Ntp, optimal=optimal) elif subtype == "H": NTU1 = NTU_from_P_H(P1=P1, R1=R1, Ntp=Ntp, optimal=optimal) elif subtype == "J": NTU1 = NTU_from_P_J(P1=P1, R1=R1, Ntp=Ntp) elif "/" in subtype: passes_counterflow = True Np1, end = subtype.split("/") if end[-1] in ["c","p"]: passes_counterflow = end[-1] == "c" end = end[0:-1] Np1, Np2 = int(Np1), int(end) NTU1 = NTU_from_P_plate(P1=P1, R1=R1, Np1=Np1, Np2=Np2, counterflow=optimal, passes_counterflow=passes_counterflow) else: raise ValueError("Supported types are 'E', 'G', 'H', 'J', 'counterflow',\ 'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', \ 'crossflow, mixed 1&2', or 'Np1/Np2' for plate exchangers") UA = NTU1*C1 NTU2 = UA/C2 Q = abs(T1i-T2i)*P1*C1 # extra: P2 = P1*R1 # effectiveness = max(C1, C2)/min(C1, C2) results = {"Q": Q, "T1i": T1i, "T1o": T1o, "T2i": T2i, "T2o": T2o, "C1": C1, "C2": C2, "R1": R1, "R2": R2, "P1": P1, "P2": P2, "NTU1": NTU1, "NTU2": NTU2, "UA": UA} return results def F_LMTD_Fakheri(Thi: int, Tho: float, Tci: int, Tco: float, shells: int=1) -> float: r"""Calculates the log-mean temperature difference correction factor `Ft` for a shell-and-tube heat exchanger with one or an even number of tube passes, and a given number of shell passes, with the expression given in [1]_ and also shown in [2]_. .. math:: F_t=\frac{S\ln W}{\ln \frac{1+W-S+SW}{1+W+S-SW}} .. math:: S = \frac{\sqrt{R^2+1}}{R-1} .. math:: W = \left(\frac{1-PR}{1-P}\right)^{1/N} .. math:: R = \frac{T_{in}-T_{out}}{t_{out}-t_{in}} .. math:: P = \frac{t_{out}-t_{in}}{T_{in}-t_{in}} If R = 1 and logarithms cannot be evaluated: .. math:: W' = \frac{N-NP}{N-NP+P} .. math:: F_t = \frac{\sqrt{2}\frac{1-W'}{W'}}{\ln\frac{\frac{W'}{1-W'}+\frac{1} {\sqrt{2}}}{\frac{W'}{1-W'}-\frac{1}{\sqrt{2}}}} Parameters ---------- Thi : float Inlet temperature of hot fluid, [K] Tho : float Outlet temperature of hot fluid, [K] Tci : float Inlet temperature of cold fluid, [K] Tco : float Outlet temperature of cold fluid, [K] shells : int, optional Number of shell-side passes, [-] Returns ------- Ft : float Log-mean temperature difference correction factor, [-] Notes ----- This expression is symmetric - the same result is calculated if the cold side values are swapped with the hot side values. It also does not depend on the units of the temperature given. Examples -------- >>> F_LMTD_Fakheri(Tci=15, Tco=85, Thi=130, Tho=110, shells=1) 0.9438358829645933 References ---------- .. [1] Fakheri, Ahmad. "A General Expression for the Determination of the Log Mean Temperature Correction Factor for Shell and Tube Heat Exchangers." Journal of Heat Transfer 125, no. 3 (May 20, 2003): 527-30. doi:10.1115/1.1571078. .. [2] Hall, Stephen. Rules of Thumb for Chemical Engineers, Fifth Edition. Oxford; Waltham, MA: Butterworth-Heinemann, 2012. """ R = (Thi - Tho)/(Tco - Tci) P = (Tco - Tci)/(Thi - Tci) if R == 1.0: W2 = (shells - shells*P)/(shells - shells*P + P) return (2**0.5*(1. - W2)/W2)/log((W2/(1. - W2) + 2**-0.5)/(W2/(1. - W2) - 2**-0.5)) else: W = ((1. - P*R)/(1. - P))**(1./shells) S = (R*R + 1.)**0.5/(R - 1.) return S*log(W)/log((1. + W - S + S*W)/(1. + W + S - S*W)) ### Tubes # TEMA tubes from http://www.engineeringpage.com/technology/thermal/tubesize.html # NPSs in inches, which convert to outer diameter exactly. _NPSs = [0.25, 0.25, 0.375, 0.375, 0.375, 0.5, 0.5, 0.625, 0.625, 0.625, 0.75, 0.75, 0.75, 0.75, 0.75, 0.875, 0.875, 0.875, 0.875, 1, 1, 1, 1, 1.25, 1.25, 1.25, 1.25, 2, 2] _Dos = [0.00635, 0.00635, 0.009525, 0.009525, 0.009525, 0.0127, 0.0127, 0.015875, 0.015875, 0.015875, 0.01905, 0.01905, 0.01905, 0.01905, 0.01905, 0.022225, 0.022225, 0.022225, 0.022225, 0.0254, 0.0254, 0.0254, 0.0254, 0.03175, 0.03175, 0.03175, 0.03175, 0.0508, 0.0508] _BWGs = [22, 24, 18, 20, 22, 18, 20, 16, 18, 20, 12, 14, 16, 18, 20, 14, 16, 18, 20, 12, 14, 16, 18, 10, 12, 14, 16, 12, 14] _ts = [0.000711, 0.000559, 0.001245, 0.000889, 0.000711, 0.001245, 0.000889, 0.001651, 0.001245, 0.000889, 0.002769, 0.002108, 0.001651, 0.001245, 0.000889, 0.002108, 0.001651, 0.001245, 0.000889, 0.002769, 0.002108, 0.001651, 0.001245, 0.003404, 0.002769, 0.002108, 0.001651, 0.002769, 0.002108] _Dis = [0.004928, 0.005232, 0.007035, 0.007747, 0.008103, 0.01021, 0.010922, 0.012573, 0.013385, 0.014097, 0.013512, 0.014834, 0.015748, 0.01656, 0.017272, 0.018009, 0.018923, 0.019735, 0.020447, 0.019862, 0.021184, 0.022098, 0.02291, 0.024942, 0.026212, 0.027534, 0.028448, 0.045262, 0.046584] # Structure: Look up NPS, get BWGs. BWGs listed in increasing order --> decreasing thickness TEMA_tubing = {0.25: (22, 24), 0.375: (18, 20, 22), 0.5: (18, 20), 0.625: (16, 18, 20), 0.75: (12, 14, 16, 18, 20), 0.875: (14, 16, 18, 20), 1.: (12, 14, 16, 18), 1.25: (10, 12, 14, 16), 2.: (12, 14)} # #for tup in TEMA_Full_Tubing: # Do, BWG = tup[0]/1000., tup[1] # t = BWG_SI[BWG_gauges.index(BWG)] # Di = Do-2*t # print t*1000, Di*1000 # def check_tubing_TEMA(NPS=None, BWG=None): """Check if tubing NPS and BWG combination is valid per TEMA standards. >>> check_tubing_TEMA(2, 22) False >>> check_tubing_TEMA(0.375, 22) True """ if NPS in TEMA_tubing: if BWG in TEMA_tubing[NPS]: return True return False def get_tube_TEMA(NPS=None, BWG=None, Do=None, Di=None, tmin=None): # Tube defined by a thickness and an outer diameter only, no pipe. # If Di or Do are specified, they must be exactly correct. if NPS and BWG: # Fully defined, guaranteed if not check_tubing_TEMA(NPS, BWG): raise ValueError("NPS and BWG Specified are not listed in TEMA") Do = 0.0254*NPS t = BWG_SI[BWG_gauges.index(BWG)] Di = Do-2*t elif Do and BWG: NPS = Do/.0254 if not check_tubing_TEMA(NPS, BWG): raise ValueError("NPS and BWG Specified are not listed in TEMA") t = BWG_SI[BWG_gauges.index(BWG)] Di = Do-2*t elif BWG and Di: t = BWG_SI[BWG_gauges.index(BWG)] # Will fail if BWG not int Do = t*2 + Di NPS = Do/.0254 if not check_tubing_TEMA(NPS, BWG): raise ValueError("NPS and BWG Specified are not listed in TEMA") elif NPS and Di: Do = 0.0254*NPS t = (Do - Di)/2 BWG = [BWG_gauges[BWG_SI.index(t)]] if not check_tubing_TEMA(NPS, BWG): raise ValueError("NPS and BWG Specified are not listed in TEMA") elif Di and Do: NPS = Do/.0254 t = (Do - Di)/2 BWG = [BWG_gauges[BWG_SI.index(t)]] if not check_tubing_TEMA(NPS, BWG): raise ValueError("NPS and BWG Specified are not listed in TEMA") # Begin Fuzzy matching elif NPS and tmin: Do = 0.0254*NPS ts = [BWG_SI[BWG_gauges.index(BWG)] for BWG in TEMA_tubing[NPS]] ts.reverse() # Small to large if tmin > ts[-1]: raise ValueError("Specified minimum thickness is larger than available in TEMA") for t in ts: # Runs if at least 1 of the thicknesses are the right size. if tmin <= t: break BWG = [BWG_gauges[BWG_SI.index(t)]] Di = Do-2*t elif Do and tmin: NPS = Do/.0254 NPS, BWG, Do, Di, t = get_tube_TEMA(NPS=NPS, tmin=tmin) elif Di and tmin: raise ValueError("Not funny defined input for TEMA Schedule; multiple solutions") elif NPS: BWG = TEMA_tubing[NPS][0] # Pick the first listed size Do = 0.0254*NPS t = BWG_SI[BWG_gauges.index(BWG)] Di = Do-2*t else: raise ValueError("Insufficient information provided") return NPS, BWG, Do, Di, t TEMA_Ls_imperial = [96., 120., 144., 192., 240.] # inches TEMA_Ls = [2.438, 3.048, 3.658, 4.877, 6.096] HTRI_Ls_imperial = [6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60] # ft HTRI_Ls = [1.829, 2.438, 3.048, 3.658, 4.267, 4.877, 5.486, 6.096, 6.706, 7.315, 8.534, 9.754, 10.973, 12.192, 13.411, 14.63, 15.85, 17.069, 18.288] # Shells up to 120 inch in diameter. # This is for plate shells, not pipe (up to 12 inches, pipe is used) HEDH_shells_imperial = [12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 24., 26., 28., 30., 32., 34., 36., 38., 40., 42., 44., 46., 48., 50., 52., 54., 56., 58., 60., 63., 66., 69., 72., 75., 78., 81., 84., 87., 90., 93., 96., 99., 102., 105., 108., 111., 114., 117., 120.] HEDH_shells = [0.3048, 0.3302, 0.3556, 0.381, 0.4064, 0.4318, 0.4572, 0.4826, 0.508, 0.5334, 0.5588, 0.6096, 0.6604, 0.7112, 0.762, 0.8128, 0.8636, 0.9144, 0.9652, 1.016, 1.0668, 1.1176, 1.1684, 1.2192, 1.27, 1.3208, 1.3716, 1.4224, 1.4732, 1.524, 1.6002, 1.6764, 1.7526, 1.8288, 1.905, 1.9812, 2.0574, 2.1336, 2.2098, 2.286, 2.3622, 2.4384, 2.5146, 2.5908, 2.667, 2.7432, 2.8194, 2.8956, 2.9718, 3.048] HEDH_pitches = {0.25: (1.25, 1.5), 0.375: (1.330, 1.420), 0.5: (1.250, 1.310, 1.380), 0.625: (1.250, 1.300, 1.400), 0.75: (1.250, 1.330, 1.420, 1.500), 1.: (1.250, 1.312, 1.375), 1.25: (1.250,), 1.5: (1.250,), 2.: (1.250,)} def DBundle_min(Do: float) -> float: r"""Very roughly, determines a good choice of shell diameter for a given tube outer diameter, according to figure 1, section 3.3.5 in [1]_. Parameters ---------- Do : float Tube outer diameter, [m] Returns ------- DShell : float Shell inner diameter, optional, [m] Notes ----- This function should be used if a tube diameter is specified but not a shell size. DShell will have to be adjusted later, once the area requirement is known. This function is essentially a lookup table. Examples -------- >>> DBundle_min(0.0254) 1.0 References ---------- .. [1] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1983. """ data = ((0.006, 0.1), (0.01, 0.1), (.014, 0.3), (0.02, 0.5), (0.03, 1.0)) for Do_tabulated, DBundle in data: if Do <= Do_tabulated: return DBundle return 1.5 def shell_clearance(DBundle: float | None=None, DShell: float | None=None) -> float: r"""Looks up the recommended clearance between a shell and tube bundle in a TEMA HX [1]. Either the bundle diameter or the shell diameter are needed provided. Parameters ---------- DBundle : float, optional Outer diameter of tube bundle, [m] DShell : float, optional Shell inner diameter, [m] Returns ------- c : float Shell-tube bundle clearance, [m] Notes ----- Lower limits are extended up to the next limit where intermediate limits are not provided. Examples -------- >>> shell_clearance(DBundle=1.245) 0.0064 References ---------- .. [1] Standards of the Tubular Exchanger Manufacturers Association, Ninth edition, 2007, TEMA, New York. """ DShell_data = [(0.457, 0.0032), (1.016, 0.0048), (1.397, 0.0064), (1.778, 0.0079), (2.159, 0.0095)] DBundle_data = [(0.457 - 0.0048, 0.0032), (1.016 - 0.0064, 0.0048), (1.397 - 0.0079, 0.0064), (1.778 - 0.0095, 0.0079), (2.159 - 0.011, 0.0095)] if DShell: for DShell_tabulated, c in DShell_data: if DShell < DShell_tabulated: return c return 0.011 elif DBundle: for DBundle_tabulated, c in DBundle_data: if DBundle < DBundle_tabulated: return c return 0.011 else: raise ValueError("Either DShell or DBundle must be specified") _TEMA_baffles_refinery = [[0.0032, 0.0048, 0.0064, 0.0095, 0.0095], [0.0048, 0.0064, 0.0095, 0.0095, 0.0127], [0.0064, 0.0075, 0.0095, 0.0127, 0.0159], [0.0064, 0.0095, 0.0127, 0.0159, 0.0159], [0.0095, 0.0127, 0.0159, 0.0191, 0.0191]] _TEMA_baffles_other = [[0.0016, 0.0032, 0.0048, 0.0064, 0.0095, 0.0095], [0.0032, 0.0048, 0.0064, 0.0095, 0.0095, 0.0127], [0.0048, 0.0064, 0.0075, 0.0095, 0.0127, 0.0159], [0.0064, 0.0064, 0.0095, 0.0127, 0.0159, 0.0159], [0.0064, 0.0095, 0.0127, 0.0127, 0.0191, 0.0191]] def baffle_thickness(Dshell, L_unsupported, service="C"): r"""Determines the thickness of baffles and support plates in TEMA HX [1]_. Applies to latitudinal baffles along the diameter of the HX, but not longitudinal baffles parallel to the tubes. Parameters ---------- Dshell : float Shell inner diameter, [m] L_unsupported : float Distance between tube supports, [m] service : str Service type, C, R or B, [-] Returns ------- t : float Baffle or support plate thickness, [m] Notes ----- No checks are implemented to ensure the given shell size is TEMA compatible. The baffles do not need to be strongas the pressure is almost the same on both of their sides. `L_unsupported` is a design choice; the more baffles in a given length, the higher the pressure drop. Examples -------- >>> baffle_thickness(Dshell=.3, L_unsupported=50, service='R') 0.0095 References ---------- .. [1] Standards of the Tubular Exchanger Manufacturers Association, Ninth edition, 2007, TEMA, New York. """ if Dshell < 0.381: j = 0 elif 0.381 <= Dshell < 0.737: j = 1 elif 0.737 <= Dshell < 0.991: j = 2 elif 0.991 <= Dshell < 1.524: j = 3 else: j = 4 if service == "R": if L_unsupported <= 0.61: i = 0 elif 0.61 < L_unsupported <= 0.914: i = 1 elif 0.914 < L_unsupported <= 1.219: i = 2 elif 1.219 < L_unsupported <= 1.524: i = 3 else: i = 4 t = _TEMA_baffles_refinery[j][i] elif service in ("C", "B"): if L_unsupported <= 0.305: i = 0 elif 0.305 < L_unsupported <= 0.610: i = 1 elif 0.61 < L_unsupported <= 0.914: i = 2 elif 0.914 < L_unsupported <= 1.219: i = 3 elif 1.219 < L_unsupported <= 1.524: i = 4 else: i = 5 t = _TEMA_baffles_other[j][i] return t def D_baffle_holes(Do, L_unsupported): r"""Determines the diameter of holes in baffles for tubes according to TEMA [1]_. Applies for all geometries. Parameters ---------- Do : float Tube outer diameter, [m] L_unsupported : float Distance between tube supports, [m] Returns ------- dB : float Baffle hole diameter, [m] Notes ----- Examples -------- >>> D_baffle_holes(Do=.0508, L_unsupported=0.75) 0.0516 >>> D_baffle_holes(Do=0.01905, L_unsupported=0.3) 0.01985 >>> D_baffle_holes(Do=0.01905, L_unsupported=1.5) 0.019450000000000002 References ---------- .. [1] Standards of the Tubular Exchanger Manufacturers Association, Ninth edition, 2007, TEMA, New York. """ if Do > 0.0318 or L_unsupported <= 0.914: # 1-1/4 inches and 36 inches extra = 0.0008 else: extra = 0.0004 d = Do + extra return d _L_unsupported_Do = [0.25, 0.375, 0.5, 0.628, 0.75, 0.875, 1., 1.25, 1.5, 2., 2.5, 3.] _L_unsupported_steel = [0.66, 0.889, 1.118, 1.321, 1.524, 1.753, 1.88, 2.235, 2.54, 3.175, 3.175, 3.175] _L_unsupported_aluminium = [0.559, 0.762, 0.965, 1.143, 1.321, 1.524, 1.626, 1.93, 2.21, 2.794, 2.794, 2.794] def L_unsupported_max(Do: float, material: str="CS") -> float: r"""Determines the maximum length of a heat exchanger tube can go without a support, according to TEMA [1]_. The limits provided apply for the worst-case temperature allowed for the material to be used at. Parameters ---------- Do : float Outer tube diameter, [m] material : str Material type, either 'CS' or 'aluminium', [-] Returns ------- L_unsupported : float Maximum length of unsupported tube, [m] Notes ----- The 'CS' results is also listed as representing high alloy steel, low alloy steel, nickel-copper, nickel, and nickel-chromium-iron alloys. The 'aluminium' results are those of copper and copper alloys and also titanium alloys. The maximum and minimum tube outer diameter tabulated are 3 inch and 1/4 inch respectively. The result is returned for the nearest tube diameter equal or smaller than the provided diameter, which helps ensures the returned tube length will not be optimistic. However, if the diameter is under 0.25 inches, the result will be optimistic! Examples -------- >>> L_unsupported_max(Do=.0254, material='CS') 1.88 References ---------- .. [1] Standards of the Tubular Exchanger Manufacturers Association, Ninth edition, 2007, TEMA, New York, p 5.4-5. """ Do = Do/inch # convert diameter to inches for i in range(12): if _L_unsupported_Do[i] == Do: break # perfect elif _L_unsupported_Do[i] > Do: i -= 1 # too big, go down a length break i = min(11, i) i = 0 if i == -1 else i if material == "CS": return _L_unsupported_steel[i] elif material == "aluminium": return _L_unsupported_aluminium[i] else: raise ValueError('Material argument should be one of "CS" or "aluminium"') ### Tube bundle count functions square_C1s = square_Ns = triangular_C1s = triangular_Ns = None def _load_coeffs_Phadkeb() -> None: global square_C1s, square_Ns, triangular_C1s, triangular_Ns hx_data_folder = os.path.join(os.path.dirname(__file__), "data") triangular_Ns = np.load(os.path.join(hx_data_folder, "triangular_Ns_Phadkeb.npy")) triangular_C1s = np.load(os.path.join(hx_data_folder, "triangular_C1s_Phadkeb.npy")) square_Ns = np.load(os.path.join(hx_data_folder, "square_Ns_Phadkeb.npy")) square_C1s = np.load(os.path.join(hx_data_folder, "square_C1s_Phadkeb.npy")) def Ntubes_Phadkeb(DBundle: float, Do: float, pitch: float, Ntp: int, angle: float=30) -> int: r"""Using tabulated values and correction factors for number of passes, the highly accurate method of [1]_ is used to obtain the tube count of a given tube bundle outer diameter for a given tube size and pitch. Parameters ---------- DBundle : float Outer diameter of tube bundle, [m] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] Ntp : int Number of tube passes, [-] angle : float, optional The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- Nt : int Total number of tubes that fit in the heat exchanger, [-] Notes ----- For single-pass cases, the result is exact, and no tubes need to be removed for any reason. For 4, 6, 8 pass arrangements, a number of tubes must be removed to accommodate pass partition plates. The following assumptions are involved with that: * The pass partition plate is where a row of tubes would have been. Only one or two rows are assumed affected. * The thickness of partition plate is < 70% of the tube outer diameter. * The distance between the centerline of the partition plate and the centerline of the nearest row of tubes is equal to the pitch. This function will fail when there are more than 100,000 tubes. [1]_ tabulated values up to approximately 3,000 tubes derived with number theory. The sequesnces of integers were identified in the On-Line Encyclopedia of Integer Sequences (OEIS), and formulas listed in it were used to generate more coefficient to allow up to 100,000 tubes. The integer sequences are A003136, A038590, A001481, and A057961. The generation of coefficients for A038590 is very slow, but the rest are reasonably fast. The number of tubes that fit generally does not increase one-by-one, but by several. >>> Ntubes_Phadkeb(DBundle=1.007, Do=.028, pitch=.036, Ntp=2, angle=45.) 558 >>> Ntubes_Phadkeb(DBundle=1.008, Do=.028, pitch=.036, Ntp=2, angle=45.) 574 Because a pass partition needs to be installed in multiple tube pass shells, more tubes fit in an exchanger the fewer passes are used. >>> Ntubes_Phadkeb(DBundle=1.008, Do=.028, pitch=.036, Ntp=1, angle=45.) 593 Examples -------- >>> Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=2, angle=45.) 782 References ---------- .. [1] Phadke, P. S., Determining tube counts for shell and tube exchangers, Chem. Eng., September, 91, 65-68 (1984). """ if square_C1s is None: # numba: delete _load_coeffs_Phadkeb() # numba: delete if DBundle <= Do*Ntp: return 0 if Ntp == 6: e = 0.265 elif Ntp == 8: e = 0.404 else: e = 0. r = 0.5*(DBundle - Do)/pitch s = r*r Ns, Nr = floor(s), floor(r) # If Ns is between two numbers, take the smaller one # C1 is the number of tubes for a single pass arrangement. if angle in (30, 60): i = np.searchsorted(triangular_Ns, Ns, side="right") C1 = int(triangular_C1s[i-1]) elif angle in (45, 90): i = np.searchsorted(square_Ns, Ns, side="right") C1 = int(square_C1s[i-1]) Cx = 2*Nr + 1. # triangular and rotated triangular if (angle in (30, 60)): w = 2*r/3**0.5 Nw = floor(w) if Nw % 2 == 0: Cy = 3*Nw else: Cy = 3*Nw + 1 if Ntp == 2: if angle == 30 : C2 = C1 - Cx else: C2 = C1 - Cy - 1 else: # 4 passes, or 8; this value is needed C4 = C1 - Cx - Cy if (angle in (30, 60)) and (Ntp in (6, 8)): if angle == 30: # triangular v = 2*e*r/3**0.5 + 0.5 Nv = floor(v) u = 3**0.5*Nv/2. if Nv % 2 == 0: z = (s-u*u)**0.5 else: z = (s-u*u)**0.5 - 0.5 Nz = floor(z) if Ntp == 6: C6 = C1 - Cy - 4*Nz - 1 else: C8 = C4 - 4*Nz else: # rotated triangular v = 2.*e*r Nv = floor(v) u1 = 0.5*Nv z = (s - u1*u1)**0.5 w1 = 2*z/2**0.5 # w1 = 2**2**0.5 # WRONG u2 = 0.5*(Nv + 1) zs = (s-u2*u2)**0.5 w2 = 2.*zs/3**0.5 if Nv%2 == 0: z1 = 0.5*w1 z2 = 0.5*(w2+1) else: z1 = 0.5*(w1+1) z2 = 0.5*w2 Nz1 = floor(z1) Nz2 = floor(z2) if Ntp == 6: C6 = C1 - Cx - 4.*(Nz1 + Nz2) else: # 8 C8 = C4 - 4.*(Nz1 + Nz2) if (angle in (45, 90)): if angle == 90: Cy = Cx - 1. # eq 6 or 8 for c2 or c4 if Ntp == 2: C2 = C1 - Cx else: # 4 passes, or 8; this value is needed C4 = C1 - Cx - Cy else: # rotated square w = r/2**0.5 Nw = floor(w) Cx = 2.*Nw + 1 Cy = Cx - 1 if Ntp == 2: C2 = C1 - Cx else: # 4 passes, or 8; this value is needed C4 = C1 - Cx - Cy if (angle in (45, 90)) and (Ntp in (6, 8)): if angle == 90: v = e*r + 0.5 Nv = floor(v) z = (s - Nv*Nv)**0.5 Nz = floor(z) if Ntp == 6: C6 = C1 - Cy - 4*Nz - 1 else: C8 = C4 - 4*Nz else: w = r/2**0.5 Nw = floor(w) Cx = 2*Nw + 1 v = 2**0.5*e*r Nv = floor(v) u1 = Nv/2**0.5 z = (s-u1*u1)**0.5 w1 = 2**0.5*z u2 = (Nv + 1)/2**0.5 zs = (s-u2*u2)**0.5 w2 = 2**0.5*zs # if Nv is odd, 21a and 22a. If even, 21b and 22b. Nz1, Nz2 if Nv %2 == 0: z1 = 0.5*w1 z2 = 0.5*(w2 + 1) else: z1 = 0.5*(w1 + 1) z2 = 0.5*w2 Nz1 = floor(z1) Nz2 = floor(z2) if Ntp == 6: C6 = C1 - Cx - 4*(Nz1 + Nz2) else: # 8 C8 = C4 - 4*(Nz1 + Nz2) if Ntp == 1: ans = C1 elif Ntp == 2: ans = C2 elif Ntp == 4: ans = C4 elif Ntp == 6: ans = C6 elif Ntp == 8: ans = C8 else: raise ValueError("Only 1, 2, 4, 6, or 8 tube passes are supported") ans = int(ans) # In some cases, a negative number would be returned by these formulas if ans < 0: ans = 0 # pragma: no cover return ans def to_solve_Ntubes_Phadkeb(DBundle: int, Do: float, pitch: float, Ntp: int, angle: int, Ntubes: int) -> int: ans = Ntubes_Phadkeb(DBundle=DBundle, Do=Do, pitch=pitch, Ntp=Ntp, angle=angle) - Ntubes return ans def DBundle_for_Ntubes_Phadkeb(Ntubes: int, Do: float, pitch: float, Ntp: int, angle: int=30) -> float: r"""Determine the bundle diameter required to fit a specified number of tubes in a heat exchanger. Uses the highly accurate method of [1]_, which takes into account pitch, number of tube passes, angle, and tube diameter. The method is analytically correct when used in the other direction (calculating number of tubes from bundle diameter); in reverse, it is solved by bisection. Parameters ---------- Ntubes : int Total number of tubes that fit in the heat exchanger, [-] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] Ntp : int Number of tube passes, [-] angle : float, optional The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- DBundle : float Outer diameter of tube bundle, [m] Notes ----- This function will fail when there are more than 100,000 tubes. There are a range of correct diameters for which there can be the given number of tubes; a number within that range is returned as found by bisection. Examples -------- >>> DBundle_for_Ntubes_Phadkeb(Ntubes=782, Do=.028, pitch=.036, Ntp=2, angle=45.) 1.1879392959379533 References ---------- .. [1] Phadke, P. S., Determining tube counts for shell and tube exchangers, Chem. Eng., September, 91, 65-68 (1984). """ if square_C1s is None: # numba: delete _load_coeffs_Phadkeb() # numba: delete if angle in (30, 60): Ns = triangular_Ns[-1] elif angle in (45, 90): Ns = square_Ns[-1] s = Ns + 1 r = s**0.5 DBundle_max = (Do + 2.*pitch*r)*(1. - 1E-8) # Cannot be exact or floor(s) will give an int too high return float(bisect(to_solve_Ntubes_Phadkeb, 0, DBundle_max, args=(Do, pitch, Ntp, angle, Ntubes))) def Ntubes_Perrys(DBundle: float, Do: float, Ntp: int, angle: int=30) -> int: r"""A rough equation presented in Perry's Handbook [1]_ for estimating the number of tubes in a tube bundle of differing geometries and tube sizes. Claimed accuracy of 24 tubes. Parameters ---------- DBundle : float Outer diameter of tube bundle, [m] Do : int Tube outer diameter, [m] Ntp : float Number of tube passes, [-] angle : float The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- Nt : int Number of tubes, [-] Notes ----- Perry's equation 11-74. Pitch equal to 1.25 times the tube outside diameter No other source for this equation is given. Experience suggests this is accurate to 40 tubes, but is often around 20 tubes off. Examples -------- >>> Ntubes_Perrys(DBundle=1.184, Do=.028, Ntp=2, angle=45) 803 References ---------- .. [1] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook, Eighth Edition. New York: McGraw-Hill Education, 2007. """ if angle in (30, 60): C = 0.75 * DBundle / Do - 36. if Ntp == 1: Nt = (((-0.0006 * C - 0.0078) * C + 1.283) * C + 74.86) * C + 1298. elif Ntp == 2: Nt = (((-0.0005 * C - 0.0071) * C + 1.234) * C + 73.58) * C + 1266. elif Ntp == 4: Nt = (((-0.0004 * C - 0.0059) * C + 1.180) * C + 70.79) * C + 1196. elif Ntp == 6: Nt = (((-0.0006 * C - 0.0074) * C + 1.269) * C + 70.72) * C + 1166. else: raise ValueError("N passes not 1, 2, 4 or 6") elif angle in (45, 90): C = DBundle / Do - 36. if Ntp == 1: Nt = (((0.0001 * C - 0.0012) * C + 0.3782) * C + 33.52) * C + 593.6 elif Ntp == 2: Nt = (((0.0001 * C - 0.0013) * C + 0.3847) * C + 33.36) * C + 578.8 elif Ntp == 4: Nt = (((0.0002 * C - 0.0016) * C + 0.3661) * C + 33.04) * C + 562.0 elif Ntp == 6: Nt = (((0.0001 * C - 0.0013) * C + 0.3873) * C + 32.49) * C + 550.4 else: raise ValueError("N passes not 1, 2, 4 or 6") return int(Nt) def Ntubes_VDI(DBundle: float | None=None, Ntp: int | None=None, Do: float | None=None, pitch: float | None=None, angle: int=30.) -> int: r"""A rough equation presented in the VDI Heat Atlas for estimating the number of tubes in a tube bundle of differing geometries and tube sizes. No accuracy estimation given. Parameters ---------- DBundle : float Outer diameter of tube bundle, [m] Ntp : float Number of tube passes, [-] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] angle : float The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- N : float Number of tubes, [-] Notes ----- No coefficients for this method with Ntp=6 are available in [1]_. For consistency, estimated values were added to support 6 tube passes, f2 = 90.. This equation is a rearranged form of that presented in [1]_. The calculated tube count is rounded down to an integer. Examples -------- >>> Ntubes_VDI(DBundle=1.184, Ntp=2, Do=.028, pitch=.036, angle=30) 966 References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if Ntp == 1: f2 = 0. elif Ntp == 2: f2 = 22. elif Ntp == 4: f2 = 70. elif Ntp == 8: f2 = 105. elif Ntp == 6: f2 = 90. # Estimated! else: raise ValueError("Only 1, 2, 4 and 8 passes are supported") if angle in (30, 60): f1 = 1.1 elif angle in (45, 90): f1 = 1.3 else: raise ValueError("Only 30, 60, 45 and 90 degree layouts are supported") DBundle, Do, pitch = DBundle*1000, Do*1000, pitch*1000 # convert to mm, equation is dimensional. t = pitch Ntubes = (-(-4*f1*t**4*f2**2*Do + 4*f1*t**4*f2**2*DBundle**2 + t**4*f2**4)**0.5 - 2*f1*t**2*Do + 2*f1*t**2*DBundle**2 + t**2*f2**2) / (2*f1**2*t**4) return int(Ntubes) def D_for_Ntubes_VDI(N: int, Ntp: float, Do: float, pitch: float, angle: float=30) -> float: r"""A rough equation presented in the VDI Heat Atlas for estimating the size of a tube bundle from a given number of tubes, number of tube passes, outer tube diameter, pitch, and arrangement. No accuracy estimation given. .. math:: OTL = \sqrt{f_1 z t^2 + f_2 t \sqrt{z} - d_o} Parameters ---------- N : float Number of tubes, [-] Ntp : float Number of tube passes, [-] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] angle : float The angle the tubes are positioned; 30, 45, 60 or 90 Returns ------- DBundle : float Outer diameter of tube bundle, [m] Notes ----- f1 = 1.1 for triangular, 1.3 for square patterns f2 is as follows: 1 pass, 0; 2 passes, 22; 4 passes, 70; 8 passes, 105. 6 tube passes is not officially supported, only 1, 2, 4 and 8. However, an estimated constant has been added to support it. f2 = 90. Examples -------- >>> D_for_Ntubes_VDI(N=970, Ntp=2., Do=0.00735, pitch=0.015, angle=30.) 0.5003600119829544 References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if Ntp == 1: f2 = 0. elif Ntp == 2: f2 = 22. elif Ntp == 4: f2 = 70. elif Ntp == 6: f2 = 90. elif Ntp == 8: f2 = 105. else: raise ValueError("Only 1, 2, 4 and 8 passes are supported") if angle in (30, 60): f1 = 1.1 elif angle in (45, 90): f1 = 1.3 else: raise ValueError("Only 30, 60, 45 and 90 degree layouts are supported") Do, pitch = Do*1000, pitch*1000 # convert to mm, equation is dimensional. Dshell = (f1*N*pitch**2 + f2*N**0.5*pitch +Do)**0.5 return Dshell/1000. def Ntubes_HEDH(DBundle: float | None=None, Do: float | None=None, pitch: float | None=None, angle: int=30) -> int: r"""A rough equation presented in the HEDH for estimating the number of tubes in a tube bundle of differing geometries and tube sizes. No accuracy estimation given. Only 1 pass is supported. .. math:: N = \frac{0.78(D_{bundle} - D_o)^2}{C_1(\text{pitch})^2} C1 = 0.866 for 30° and 60° layouts, and 1 for 45 and 90° layouts. Parameters ---------- DBundle : float Outer diameter of tube bundle, [m] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] angle : float The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- N : float Number of tubes, [-] Notes ----- Seems reasonably accurate. Examples -------- >>> Ntubes_HEDH(DBundle=1.184, Do=.028, pitch=.036, angle=30) 928 References ---------- .. [1] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1983. """ if angle in (30, 60): C1 = 13/15. elif angle in (45, 90): C1 = 1. else: raise ValueError("Only 30, 60, 45 and 90 degree layouts are supported") Dctl = DBundle - Do N = 0.78*Dctl*Dctl/(C1*pitch*pitch) return int(N) def DBundle_for_Ntubes_HEDH(N: int, Do: float, pitch: float, angle: int=30) -> float: r"""A rough equation presented in the HEDH for estimating the tube bundle diameter necessary to fit a given number of tubes. No accuracy estimation given. Only 1 pass is supported. .. math:: D_{bundle} = (D_o + (\text{pitch})\sqrt{\frac{1}{0.78}}\cdot \sqrt{C_1\cdot N}) C1 = 0.866 for 30° and 60° layouts, and 1 for 45 and 90° layouts. Parameters ---------- N : float Number of tubes, [-] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] angle : float The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- DBundle : float Outer diameter of tube bundle, [m] Notes ----- Easily reversed from the main formulation. Examples -------- >>> DBundle_for_Ntubes_HEDH(N=928, Do=.028, pitch=.036, angle=30) 1.183993079564 References ---------- .. [1] Schlunder, Ernst U, and International Center for Heat and Mass Transfer. Heat Exchanger Design Handbook. Washington: Hemisphere Pub. Corp., 1983. """ if angle in (30, 60): C1 = 13/15. elif angle in (45, 90): C1 = 1. else: raise ValueError("Only 30, 60, 45 and 90 degree layouts are supported") return (Do + (1./.78)**0.5*pitch*(C1*N)**0.5) def Ntubes(DBundle: float, Do: float, pitch: float, Ntp: int=1, angle: int=30, Method: str | None=None) -> int: r"""Calculates the number of tubes which can fit in a heat exchanger. The tube count is effected by the pitch, number of tube passes, and angle. The result is an exact number of tubes and is calculated by a very accurate method using number theory by default. This method is available only up to 100,000 tubes. Parameters ---------- DBundle : float Outer diameter of tube bundle, [m] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] Ntp : int, optional Number of tube passes, [-] angle : float, optional The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- N : int Total number of tubes that fit in the heat exchanger, [-] Other Parameters ---------------- Method : string, optional One of 'Phadkeb', 'HEDH', 'VDI' or 'Perry' See Also -------- Ntubes_Phadkeb Ntubes_Perrys Ntubes_VDI Ntubes_HEDH size_bundle_from_tubecount Examples -------- >>> Ntubes(DBundle=1.2, Do=0.025, pitch=0.03125) 1285 The approximations are pretty good too: >>> Ntubes(DBundle=1.2, Do=0.025, pitch=0.03125, Method='Perry') 1297 >>> Ntubes(DBundle=1.2, Do=0.025, pitch=0.03125, Method='VDI') 1340 >>> Ntubes(DBundle=1.2, Do=0.025, pitch=0.03125, Method='HEDH') 1272 """ if Method is None: Method = "Phadkeb" if Method == "Phadkeb": return Ntubes_Phadkeb(DBundle=DBundle, Ntp=Ntp, Do=Do, pitch=pitch, angle=angle) elif Method == "HEDH": return Ntubes_HEDH(DBundle=DBundle, Do=Do, pitch=pitch, angle=angle) elif Method == "VDI": return Ntubes_VDI(DBundle=DBundle, Ntp=Ntp, Do=Do, pitch=pitch, angle=angle) elif Method == "Perry": return Ntubes_Perrys(DBundle=DBundle, Do=Do, Ntp=Ntp, angle=angle) else: raise ValueError('Method not recognized; allowable methods are ' '"Phadkeb", "HEDH", "VDI", and "Perry"') def _tubecount_objf_Perry(D: float, Do: float, Ntp: int, angle: int, N: int) -> int: return Ntubes_Perrys(DBundle=D, Do=Do, Ntp=Ntp, angle=angle) - N def size_bundle_from_tubecount(N: int, Do: float, pitch: float, Ntp: int=1, angle: int=30, Method: str | None=None) -> float: r"""Calculates the outer diameter of a tube bundle containing a specified number of tubes. The tube count is effected by the pitch, number of tube passes, and angle. The result is an exact number of tubes and is calculated by a very accurate method using number theory by default. This method is available only up to 100,000 tubes. Parameters ---------- N : int Total number of tubes that fit in the heat exchanger, [-] Do : float Tube outer diameter, [m] pitch : float Pitch; distance between two orthogonal tube centers, [m] Ntp : int, optional Number of tube passes, [-] angle : float, optional The angle the tubes are positioned; 30, 45, 60 or 90, [degrees] Returns ------- DBundle : float Outer diameter of tube bundle, [m] Other Parameters ---------------- Method : string, optional One of 'Phadkeb', 'HEDH', 'VDI' or 'Perry' See Also -------- Ntubes DBundle_for_Ntubes_Phadkeb D_for_Ntubes_VDI DBundle_for_Ntubes_HEDH Notes ----- The 'Perry' method is solved with a numerical solver and is very unreliable. Examples -------- >>> size_bundle_from_tubecount(N=1285, Do=0.025, pitch=0.03125) 1.1985676402390355 """ if Method is None: Method2 = "Phadkeb" else: Method2 = Method if Method2 == "Phadkeb": return DBundle_for_Ntubes_Phadkeb(Ntubes=N, Ntp=Ntp, Do=Do, pitch=pitch, angle=angle) elif Method2 == "VDI": return D_for_Ntubes_VDI(N=N, Ntp=Ntp, Do=Do, pitch=pitch, angle=angle) elif Method2 == "HEDH": return DBundle_for_Ntubes_HEDH(N=N, Do=Do, pitch=pitch, angle=angle) elif Method2 == "Perry": return brenth(_tubecount_objf_Perry, Do*5, 1000*Do, args=(Do, Ntp, angle, N)) else: raise ValueError('Method not recognized; allowable methods are ' '"Phadkeb", "HEDH", "VDI", and "Perry"') TEMA_heads = {"A": "Removable Channel and Cover", "B": "Bonnet (Integral Cover)", "C": "Integral With Tubesheet Removable Cover", "N": "Channel Integral With Tubesheet and Removable Cover", "D": "Special High-Pressure Closures"} TEMA_shells = {"E": "One-Pass Shell", "F": "Two-Pass Shell with Longitudinal Baffle", "G": "Split Flow", "H": "Double Split Flow", "J": "Divided Flow", "K": "Kettle-Type Reboiler", "X": "Cross Flow"} TEMA_rears = {"L": 'Fixed Tube Sheet; Like "A" Stationary Head', "M": 'Fixed Tube Sheet; Like "B" Stationary Head', "N": 'Fixed Tube Sheet; Like "C" Stationary Head', "P": "Outside Packed Floating Head", "S": "Floating Head with Backing Device", "T": "Pull-Through Floating Head", "U": "U-Tube Bundle", "W": "Externally Sealed Floating Tubesheet"} TEMA_services = {"B": "Chemical", "R": "Refinery", "C": "General"} baffle_types = ["segmental", "double segmental", "triple segmental", "disk and doughnut", "no tubes in window", "orifice", "rod"] ================================================ FILE: ht/insulation.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import interp from ht.conduction import R_to_k __all__ = [ "ASHRAE", "ASHRAE_k", "Cp_material", "building_materials", "k_material", "materials_dict", "nearest_material", "refractories", "refractory_VDI_Cp", "refractory_VDI_k", "rho_material", ] # building_materials in VDI Heat Atlas; full table in DIN EN 12524-2000 which # is used here # Format: density, thermal conductivity, heat capacity # Units: kg/m^3, W/m/K, and J/kg/K # A roughly room-teperature value is attached to all values building_materials = {"Asphalt": (2100.0, 0.7, 1000.0), "Bitumen, pure": (1050.0, 0.17, 1000.0), "Bitumen, felt or sheet": (1100.0, 0.23, 1000.0), "Concrete, medium density 1800 kg/m^3": (1800.0, 1.15, 1000.0), "Concrete, medium density 2000 kg/m^3": (2000.0, 1.35, 1000.0), "Concrete, medium density 2200 kg/m^3": (2200.0, 1.65, 1000.0), "Concrete, high density": (2400.0, 2.0, 1000.0), "Concrete, reinforced with 1% steel": (2300.0, 2.3, 1000.0), "Concrete, reinforced with 2% steel": (2400.0, 2.5, 1000.0), "Floor covering, rubber": (1200.0, 0.17, 1400.0), "Floor covering, plastic": (1700.0, 0.25, 1400.0), "Floor covering, underlay, cellular or plastic": (270.0, 0.1, 1400.0), "Floor covering, underlay, felt": (120.0, 0.05, 1300.0), "Floor covering, underlay, wool": (200.0, 0.06, 1300.0), "Floor covering, underlay, cork": (200.0, 0.05, 1500.0), "Floor covering, tiles, cork": (400.0, 65.0, 1500.0), "Floor covering, carpet / textile flooring": (200.0, 0.06, 1300.0), "Floor covering, Linoleum": (1200.0, 0.17, 1400.0), "Gases, air": (1.23, 0.025, 1008.0), "Gases, carbon dioxide": (1.95, .014, 820.0), "Gases, argon": (1.7, .017, 519.0), "Gases, sulphur hexafluoride": (6.36, .013, 614.0), "Gases, krypton": (3.56, 0.009, 245.0), "Gases, xenon": (5.68, 0.0054, 160.0), "Glass, soda lime": (2500.0, 1.0, 750.0), "Glass, quartz": (2200.0, 1.4, 750.0), "Glass, glass mosaic": (2000.0, 1.2, 750.0), "Water, ice at -10 °C": (920.0, 2.3, 2000.0), "Water, ice at 0 °C": (900.0, 2.2, 2000.0), "Water, snow, freshly fallen (<30 mm)": (100.0, 0.05, 2000.0), "Water, snow, soft (30 mm to 70 mm)": (200.0, 0.12, 2000.0), "Water, snow, slightly compacted (70 mm to 100 mm)": (300.0, 0.23, 2000.0), "Water, snow, compacted (<200 mm)": (500.0, 0.6, 2000.0), "Water at 10°C": (1000.0, 0.6, 4190.0), "Water at 40°C": (990.0, 0.63, 4190.0), "Water at 80°C": (970.0, 0.67, 4190.0), "Metals, aluminium alloys": (2800.0, 160.0, 880.0), "Metals, bronze": (8700.0, 65.0, 380.0), "Metals, brass": (8400.0, 120.0, 380.0), "Metals, copper": (8900.0, 380.0, 380.0), "Metals, iron, cast": (7500.0, 50.0, 450.0), "Metals, lead": (11300.0, 35.0, 130.0), "Metals, steel": (7800.0, 50.0, 450.0), "Metals, stainless steel": (7900.0, 17.0, 460.0), "Metals, zinc": (7200.0, 110.0, 380.0), "Plastics, acrylic": (1050.0, 0.2, 1500.0), "Plastics, polycarbonates": (1200.0, 0.2, 1200.0), "Plastics, polytetrafluoroethylene (PTFE)": (2200.0, 0.25, 1000.0), "Plastics, Polyvinylchloride (PVC)": (1390.0, 0.17, 900.0), "Plastics, polymethylmethacrylate (PMMA)": (1180.0, 0.18, 1500.0), "Plastics, polyacetate": (1410.0, 0.3, 1400.0), "Plastics, polyamide (nylon)": (1150.0, 0.25, 1600.0), "Plastics, polyamide 6.6 with 25% glass fibre": (1450.0, 0.3, 1600.0), "Plastics, polyethylene / polythene, high density": (980.0, 0.5, 1800.0), "Plastics, polyethylene / polythene, low density": (920.0, 0.33, 2200.0), "Plastics, polystyrene": (1050.0, 0.16, 1300.0), "Plastics, polypropylene": (910.0, 0.22, 1800.0), "Plastics, polypropylene with 25% glass fibre": (1200.0, 0.25, 1800.0), "Plastics, polyurethane (PU)": (1200.0, 0.25, 1800.0), "Plastics, epoxy resin": (1200.0, 0.2, 1400.0), "Plastics, phenolic resin": (1300.0, 0.3, 1700.0), "Plastics, polyester resin": (1400.0, 0.19, 1200.0), "Rubber, natural": (910.0, 0.13, 1100.0), "Rubber, neoprene (polychloroprene)": (1240.0, 0.23, 2140.0), "Rubber, butyl (isobutene), solid melt": (1200.0, 0.24, 1400.0), "Rubber, foam rubber": (70.0, 0.06, 1500.0), "Rubber, hard rubber (ebonite), solid": (1200.0, 0.17, 1400.0), "Rubber, ethylene propylene diene monomer (EPDM)": (1150.0, 0.25, 1000.0), "Rubber, polyisobutylene": (930.0, 0.2, 1100.0), "Rubber, polysulfide": (1700.0, 0.4, 1000.0), "Rubber, butadiene": (980.0, 0.25, 1000.0), "Sealant, silica gel (dessicant)": (720.0, 0.13, 1000.0), "Sealant, silicone, pure": (1200.0, 0.35, 1000.0), "Sealant, silicone, filled": (1450.0, 0.5, 1000.0), "Sealant, silicone foam": (750.0, 0.12, 1000.0), "Sealant, urethane / polyurethane (thermal break)": (1300.0, 0.21, 1800.0), "Sealant, polyvinylchloride (PVC) flexible, with 40% softner": (1200.0, 0.14, 1000.0), "Sealant, elastomeric foam, flexible": (70.0, 0.05, 1500.0), "Sealant, polyurethane (PU) foam": (70.0, 0.05, 1500.0), "Sealant, polyethylene foam": (70.0, 0.05, 2300.0), "Gypsum, 600 kg/m^3": (600.0, 0.18, 1000.0), "Gypsum, 900 kg/m^3": (900.0, 0.3, 1000.0), "Gypsum, 1200 kg/m^3": (1200.0, 0.43, 1000.0), "Gypsum, 1500 kg/m^3": (1500.0, 0.56, 1000.0), "Gypsum, plasterboard": (900.0, 0.25, 1000.0), "Plasters and renders, gypsum insulating plaster": (600.0, 0.18, 1000.0), "Plasters and renders, gypsum plastering, 1000 kg/m^3": (1000.0, 0.4, 1000.0), "Plasters and renders, gypsum plastering, 1300 kg/m^3": (1300.0, 0.57, 1000.0), "Plasters and renders, lime sand": (1600.0, 0.8, 1000.0), "Plasters and renders, cement sand": (1600.0, 0.8, 1000.0), "Plasters and renders, gypsum sand": (1800.0, 1.0, 1000.0), "Solids, clay or silt": (1500.0, 1.5, 2085.0), "Solids, sand and gravel": (1950.0, 2.0, 1045.0), "Stone, natural, crystalline rock": (2800.0, 3.5, 1000.0), "Stone, natural, sedimentary rock": (2600.0, 2.3, 1000.0), "Stone, natural, sedimentary rock, light": (1500.0, 0.85, 1000.0), "Stone, natural, porous": (1600.0, 0.55, 1000.0), "Stone, basalt": (2850.0, 3.5, 1000.0), "Stone, gneiss": (2550.0, 3.5, 1000.0), "Stone, granite": (2600.0, 2.8, 1000.0), "Stone, marble": (2800.0, 3.5, 1000.0), "Stone, slate": (2400.0, 2.2, 1000.0), "Stone, limestone, extra soft": (1600.0, 0.85, 1000.0), "Stone, limestone, soft": (1800.0, 1.1, 1000.0), "Stone, limestone, semi-hard": (2000.0, 1.4, 1000.0), "Stone, limestone, hard": (2200.0, 1.7, 1000.0), "Stone, limestone, extra hard": (2600.0, 2.3, 1000.0), "Stone, sandstone (silica)": (2600.0, 2.3, 1000.0), "Stone, natural pumice": (400.0, 0.12, 1000.0), "Stone, artificial stone": (1750.0, 1.3, 1000.0), "Tiles, clay": (2000.0, 1.0, 800.0), "Tiles, concrete": (2100.0, 1.5, 1000.0), "Tiles, ceramic or porcelain": (2300.0, 1.3, 840.0), "Tiles, plastic": (1000.0, 0.2, 1000.0), "Timber, 500 kg/m^3": (500.0, 0.13, 1600.0), "Timber, 700 kg/m^3": (700.0, 0.18, 1600.0), "Wood, plywood 300 kg/m^3": (300.0, 0.09, 1600.0), "Wood, plywood 500 kg/m^3": (500.0, 0.13, 1600.0), "Wood, plywood 700 kg/m^3": (700.0, 0.17, 1600.0), "Wood, plywood 1000 kg/m^3": (1000.0, 0.24, 1600.0), "Wood, cement-bonded particleboard": (1200.0, 0.23, 1500.0), "Wood, particleboard, 300 kg/m^3": (300.0, 0.1, 1700.0), "Wood, particleboard, 600 kg/m^3": (600.0, 0.14, 1700.0), "Wood, particleboard, 900 kg/m^3": (900.0, 0.18, 1700.0), "Wood, oriented strand board": (650.0, 0.13, 1700.0), "Wood, fibreboard, 250 kg/m^3": (250.0, 0.07, 1700.0), "Wood, fibreboard, 400 kg/m^3": (400.0, 0.1, 1700.0), "Wood, fibreboard, 600 kg/m^3": (600.0, 0.14, 1700.0), "Wood, fibreboard, 800 kg/m^3": (800.0, 0.18, 1700.0)} # Format for ASHRAE strings: [rho, Cp, k, R, t] # Units [kg/m^3, J/kg/K, W/m/K, m^2*K/W, mm]; only t is in non-SI units ASHRAE_board_siding = {"Board, Asbestos/cement": [1900.0, 1000.0, 0.57, None, None], "Board, Cement": [1150.0, 840.0, 0.25, None, None], "Board, Fiber/cement, 1400 kg/m^3": [1400.0, 840.0, 0.25, None, None], "Board, Fiber/cement, 1000 kg/m^3": [1000.0, 840.0, 0.19, None, None], "Board, Fiber/cement, 400 kg/m^3": [400.0, 1880.0, 0.07, None, None], "Board, Fiber/cement, 300 kg/m^3": [300.0, 1150.0, 0.06, None, None], "Gypsum or plaster board": [640.0, 1880.0, 0.16, None, None], "Oriented strand board (OSB)": [650.0, 1880.0, None, 0.12, 12.7], "Plywood (douglas fir)": [460.0, 1880.0, None, 0.14, 12.7], "Plywood/wood panels": [450.0, 1880.0, None, 0.19, 19], "Vegetable fiber board, Sheathing, regular density": [290.0, 1300.0, None, 0.23, 12.7], "Vegetable fiber board, Sheathing, intermediate density": [350.0, 1300.0, None, 0.19, 12.7], "Vegetable fiber board, Nail-base sheathing": [400.0, 1300.0, None, 0.19, 12.7], "Vegetable fiber board, Shingle backer": [290.0, 1300.0, None, 0.17, 9.5], "Vegetable fiber board, Sound deadening board": [240.0, 1260.0, None, 0.24, 12.7], "Vegetable fiber board, Tile and lay-in panels, plain or acoustic": [290.0, 590.0, 0.058, None, None], "Vegetable fiber board, Laminated paperboard": [480.0, 1380.0, 0.072, None, None], "Vegetable fiber board, Homogeneous board from repulped paper": [480.0, 1170.0, 0.072, None, None], "Hardboard, medium density": [800.0, 1300.0, 0.105, None, None], "Hardboard, high density, service-tempered grade and service grade": [880.0, 1340.0, 0.12, None, None], "Hardboard, high density, standard-tempered grade": [1010.0, 1340.0, 0.144, None, None], "Particleboard, low density": [590.0, 1300.0, 0.102, None, None], "Particleboard, medium density": [800.0, 1300.0, 0.135, None, None], "Particleboard, high density": [1000.0, 1300.0, 1.18, None, None], "Particleboard, underlayment": [640.0, 1210.0, None, 1.22, 15.9], "Waferboard": [700.0, 1880.0, 0.072, None, None], "Shingles, Asbestos/cement": [1900.0, 1000.0, None, 0.037, 6.4], "Shingles, Wood, 400 mm, 190 mm exposure": [None, 1300.0, None, 0.15, 6], "Shingles, Wood, double, 400 mm, 300 mm exposure": [None, 1170.0, None, 0.21, 12], "Shingles, Wood, plus ins. backer board": [None, 1300.0, None, 0.25, 8], "Shingles, Siding, Asbestos/cement, lapped": [None, 1010.0, None, 0.037, 6.4], "Shingles, Siding, Asphalt roll siding": [None, 1470.0, None, 0.026, 2], "Siding, Asphalt insulating siding": [None, 1470.0, None, 0.26, 12.7], "Siding, Hardboard siding": [None, 1170.0, None, 0.12, 11], "Siding, Wood, drop, 200 mm": [None, 1170.0, None, 0.14, 25], "Siding, Wood, bevel, 200 mm, lapped": [None, 1170.0, None, 0.14, 13], "Siding, Wood, bevel, 250 mm, lapped": [None, 1170.0, None, 0.18, 19], "Siding, Wood, plywood, lapped": [None, 1220.0, None, 0.1, 9.5], "Siding, Aluminum, steel, or vinyl, over sheathing, hollow-backed": [None, 1220.0, None, 0.11, 0.6], "Siding, Aluminum, steel, or vinyl, over sheathing, insulating-board-backed": [None, 1340.0, None, 0.32, 9.5], "Siding, Aluminum, steel, or vinyl, over sheathing foil-backed": [None, None, None, 0.52, 9.5], "Siding, Architectural (soda-lime float) glass": [2500.0, 840.0, 1.0, None, None]} ASHRAE_flooring = {"Carpet and rebounded urethane pad": [110.0, None, None, 0.42, 19], "Carpet and rubber pad, one-piece": [320.0, None, None, 0.12, 9.5], "Pile carpet with rubber pad": [290.0, None, None, 0.28, 11], "Linoleum/cork tile": [465.0, None, None, 0.09, 6.4], "PVC/Rubber floor covering, Rubber tile": [1900.0, None, 0.4, 0.06, 25], "PVC/Rubber floor covering, Terrazzo": [None, 800.0, 0.4, 0.014, 25]} ASHRAE_insulation = {"Glass-fiber batts, 90 mm": [12.0, 840.0, 0.043, None, 90], "Glass-fiber batts, 50 mm": [10.5, 840.0, 0.0465, None, 50], "Mineral fiber": [30.0, 840.0, 0.036, None, 140], "Mineral wool, felted, 32 kg/m^3": [32.0, 840.0, 0.04, None, None], "Mineral wool, felted, 100 kg/m^3": [97.5, 840.0, 0.035, None, None], "Slag wool, 120 kg/m^3": [120.0, 950.0, 0.038, None, None], "Slag wool, 255 kg/m^3": [255.0, 950.0, 0.04, None, None], "Slag wool, 305 kg/m^3": [305.0, 950.0, 0.043, None, None], "Slag wool, 350 kg/m^3": [350.0, 950.0, 0.048, None, None], "Slag wool, 400 kg/m^3": [400.0, 950.0, 0.05, None, None], "Cellular glass": [130.0, 750.0, 0.048, None, None], "Cement fiber slabs, shredded wood, with Portland cement binder": [415.0, 1300.0, 0.074, None, None], "Cement fiber slabs, shredded wood, with magnesia oxysulfide binder": [350.0, 1300.0, 0.082, None, None], "Glass fiber board": [160.0, 840.0, 0.036, None, None], "Expanded rubber": [70.0, 1670.0, 0.032, None, None], "Expanded polystyrene, extruded": [32.5, 1470.0, 0.026, None, None], "Expanded polystyrene, molded beads": [20.0, 1470.0, 0.0355, None, None], "Mineral fiberboard, wet felted": [160.0, 840.0, 0.038, None, None], "Mineral fiberboard, wet felted, core or roof insulation": [262.5, 840.0, 0.049, None, None], "Mineral fiberboard, wet felted, acoustical tile, 290 kg/m^3": [290.0, 800.0, 0.05, None, None], "Mineral fiberboard, wet felted, acoustical tile, 335 kg/m^3": [335.0, None, 0.053, None, None], "Mineral fiberboard, wet-molded, acoustical tile": [370.0, 590.0, 0.061, None, None], "Perlite board": [160.0, None, 0.052, None, None], "Polyisocyanurate, aged, unfaced": [30.0, None, 0.0235, None, None], "Polyisocyanurate, aged, with facers": [65.0, 1470.0, 0.019, None, None], "Phenolic foam board with facers, aged": [65.0, None, 0.019, None, None], "Loose fill, Cellulosic": [42.5, 1380.0, 0.042, None, None], "Loose fill, Perlite, expanded, 50 kg/m^3": [50.0, 1090.0, 0.042, None, None], "Loose fill, Perlite, expanded, 100 kg/m^3": [100.0, None, 0.0485, None, None], "Loose fill, Perlite, expanded, 150 kg/m^3": [150.0, None, 0.0565, None, None], "Loose fill, Mineral fiber, 95 to 130 mm": [20.0, 710.0, None, 1.92, 112.5], "Loose fill, Mineral fiber, 170 to 220 mm": [20.0, None, None, 3.33, 195], "Loose fill, Mineral fiber, 190 to 250 mm": [20.0, None, None, 3.85, 220], "Loose fill, Mineral fiber, 260 to 350 mm": [20.0, None, None, 5.26, 305], "Loose fill, Mineral fiber, 90 mm": [42.5, None, None, 2.3, 90], "Loose fill, Vermiculite, exfoliated, 120 kg/m^3": [120.0, 1340.0, 0.068, None, None], "Loose fill, Vermiculite, exfoliated, 80 kg/m^3": [80.0, None, 0.063, None, None], "Spray-applied Cellulosic fiber": [75.0, None, 0.0455, None, None], "Spray-applied Glass fiber": [62.5, None, 0.0385, None, None], "Spray-applied Polyurethane foam, 7 kg/m^3": [7.0, 1470.0, 0.042, None, None], "Spray-applied Polyurethane foam, 40 kg/m^3": [40.0, 1470.0, 0.026, None, None], "Spray-applied Polyurethane foam, aged and dry, 40 mm": [30.0, 1470.0, None, 1.6, 40], "Spray-applied Polyurethane foam, aged and dry, 50 mm": [55.0, 1470.0, None, 1.92, 50], "Spray-applied Polyurethane foam, aged and dry, 120 mm": [30.0, None, None, 3.69, 120], "Spray-applied Ureaformaldehyde foam, dry": [14.0, None, 0.031, None, None]} ASHRAE_roofing = {"Asbestos/cement shingles": [1120.0, 1000.0, None, 0.037, 6], "Asphalt (bitumen with inert fill), 1600 kg/m^3": [1600.0, None, 0.43, None, None], "Asphalt (bitumen with inert fill), 1900 kg/m^3": [1900.0, None, 0.58, None, None], "Asphalt (bitumen with inert fill), 2300 kg/m^3": [2300.0, None, 1.15, None, None], "Asphalt roll roofing": [920.0, 1510.0, None, 0.027, 2], "Asphalt shingles": [920.0, 1260.0, None, 0.078, 12], "Built-up roofing": [920.0, 1470.0, None, 0.059, 10], "Mastic asphalt (heavy, 20% grit)": [950.0, None, 0.19, None, None], "Reed thatch": [270.0, None, 0.09, None, None], "Roofing felt": [2250.0, None, 1.2, None, None], "Slate": [None, 1260.0, None, 0.009, 13], "Straw thatch": [240.0, None, 0.07, None, None], "Wood shingles, plain and plastic-film-faced": [None, 1300.0, None, 0.166, 10]} ASHRAE_plastering = {"Cement plaster, sand aggregate": [1860.0, 840.0, 0.72, None, None], "Gypsum plaster, 1120 kg/m^3": [1120.0, None, 0.38, None, None], "Gypsum plaster, 1280 kg/m^3": [1280.0, None, 0.46, None, None], "Lightweight aggregate": [720.0, None, None, 0.056, 13], "Perlite aggregate": [720.0, 1340.0, 0.22, None, None], "Sand aggregate": [1680.0, 840.0, 0.81, 0.013, 10], "Vermiculite aggregate, 480 kg/m^3": [480.0, None, 0.14, None, None], "Vermiculite aggregate, 600 kg/m^3": [600.0, None, 0.2, None, None], "Vermiculite aggregate, 720 kg/m^3": [720.0, None, 0.25, None, None], "Vermiculite aggregate, 840 kg/m^3": [840.0, None, 0.26, None, None], "Vermiculite aggregate, 960 kg/m^3": [960.0, None, 0.3, None, None], "Perlite plaster, 400 kg/m^3": [400.0, None, 0.08, None, None], "Perlite plaster, 600 kg/m^3": [600.0, None, 0.19, None, None], "Pulpboard or paper plaster": [600.0, None, 0.07, None, None], "Sand/cement plaster, conditioned": [1560.0, None, 0.63, None, None], "Sand/cement/lime plaster, conditioned": [1440.0, None, 0.48, None, None], "Sand/gypsum (3:1) plaster, conditioned": [1550.0, None, 0.65, None, None]} ASHRAE_masonry = {"Brick, fired clay, 2400 kg/m^3": [2400.0, 800.0, 1.34, None, None], "Brick, fired clay, 2240 kg/m^3": [2240.0, 800.0, 1.185, None, None], "Brick, fired clay, 2080 kg/m^3": [2080.0, 800.0, 1.02, None, None], "Brick, fired clay, 1920 kg/m^3": [1920.0, 800.0, 0.895, None, None], "Brick, fired clay, 1760 kg/m^3": [1760.0, 800.0, 0.78, None, None], "Brick, fired clay, 1600 kg/m^3": [1600.0, 800.0, 0.675, None, None], "Brick, fired clay, 1440 kg/m^3": [1440.0, 800.0, 0.57, None, None], "Brick, fired clay, 1280 kg/m^3": [1280.0, 800.0, 0.48, None, None], "Brick, fired clay, 1120 kg/m^3": [1120.0, 800.0, 0.405, None, None], "Clay tile, hollow, 1 cell deep": [None, 880.0, None, 0.14, 75], "Clay tile, hollow, 2 cells deep": [None, 880.0, None, 0.27, 150], "Clay tile, hollow, 3 cells deep": [None, 880.0, None, 0.44, 300], "Lightweight brick, 800 kg/m^3": [800.0, None, 0.2, None, None], "Lightweight brick, 770 kg/m^3": [770.0, None, 0.22, None, None], "Concrete blocks, Limestone aggregate, 200 mm, 16 kg, 2200 kg/m^3, 2 cores with perlite-filled cores": [None, None, None, 0.37, 200], "Concrete blocks, Limestone aggregate, 300 mm, 25 kg, 2200 kg/m^3, 2 cores with perlite-filled cores.": [None, None, None, 0.65, 300], "Concrete blocks, normal-weight aggregate, 300 mm, 23 kg, 2100 kg/m^3, 2 or 3 cores": [None, 920.0, None, 0.185, 200], "Concrete blocks, normal-weight aggregate, 300 mm, 23 kg, 2100 kg/m^3, with perlite-filled cores": [None, None, None, 0.35, 200], "Concrete blocks, normal-weight aggregate, 300 mm, 23 kg, 2100 kg/m^3, with vermiculite-filled cores": [None, None, None, 0.29, 200], "Concrete blocks, normal-weight aggregate, 300 mm, 23 kg, 2000 kg/m^3, 2 cores": [None, 920.0, None, 0.217, 300], "Concrete blocks, medium-weight aggregate, 200 mm, 13 kg, 1650 kg/m^3, 2 or 3 cores": [1650.0, None, None, 0.26, 200], "Concrete blocks, medium-weight aggregate, 200 mm, 13 kg, 1650 kg/m^3, with perlite-filled cores": [1650.0, None, None, 0.53, 200], "Concrete blocks, medium-weight aggregate, 200 mm, 13 kg, 1650 kg/m^3, with vermiculite-filled cores": [None, None, None, 0.58, 200], "Concrete blocks, medium-weight aggregate, 200 mm, 13 kg, 1650 kg/m^3, with molded-EPS-filled cores": [1650.0, None, None, 0.56, 200], "Concrete blocks, medium-weight aggregate, 200 mm, 13 kg, 1650 kg/m^3, with molded EPS inserts in cores": [1650.0, None, None, 0.47, 200], "Concrete blocks, low-mass aggregate, 150 mm, 7.5 kg, 1400 kg/m^3, 2 or 3 cores": [None, None, None, 0.315, 150], "Concrete blocks, low-mass aggregate, 150 mm, 7.5 kg, 1400 kg/m^3, with perlite-filled cores": [None, None, None, 0.74, 150], "Concrete blocks, low-mass aggregate, 150 mm, 7.5 kg, 1400 kg/m^3, with vermiculite-filled cores": [None, None, None, 0.53, 150], "Concrete blocks, low-mass aggregate, 200 mm, 9 kg, 1250 kg/m^3": [None, 880.0, None, 0.445, 200], "Concrete blocks, low-mass aggregate, 200 mm, 9 kg, 1250 kg/m^3, with perlite-filled cores": [None, 880.0, None, 0.985, 200], "Concrete blocks, low-mass aggregate, 200 mm, 9 kg, 1250 kg/m^3, with vermiculite-filled cores": [None, 880.0, None, 0.81, 200], "Concrete blocks, low-mass aggregate, 200 mm, 9 kg, 1250 kg/m^3, with molded-EPS-filled cores": [None, 880.0, None, 0.85, 200], "Concrete blocks, low-mass aggregate, 200 mm, 9 kg, 1250 kg/m^3, with UF foam-filled cores": [None, 880.0, None, 0.79, 200], "Concrete blocks, low-mass aggregate, 200 mm, 9 kg, 1250 kg/m^3, with molded EPS inserts in cores": [None, 880.0, None, 0.62, 200], "Concrete blocks, low-mass aggregate, 300 mm, 16 kg, 1400 kg/m^3, 2 or 3 cores": [None, None, None, 0.43, 300], "Concrete blocks, low-mass aggregate, 300 mm, 16 kg, 1400 kg/m^3, with perlite-filled cores": [None, None, None, 1.35, 300], "Concrete blocks, low-mass aggregate, 300 mm, 16 kg, 1400 kg/m^3, with vermiculite-filled cores": [None, None, None, 1.0, 300], "Stone, lime, or sand": [2880.0, None, 10.4, None, None], "Quartzitic and sandstone, 2560 kg/m^3": [2560.0, None, 6.2, None, None], "Quartzitic and sandstone, 2240 kg/m^3": [2240.0, None, 3.46, None, None], "Quartzitic and sandstone, 1920 kg/m^3": [1920.0, 880.0, 1.88, None, None], "Calcitic, dolomitic, limestone, marble, and granite, 2880 kg/m^3": [2880.0, None, 4.33, None, None], "Calcitic, dolomitic, limestone, marble, and granite, 2560 kg/m^3": [2560.0, None, 3.17, None, None], "Calcitic, dolomitic, limestone, marble, and granite, 2240 kg/m^3": [2240.0, None, 2.31, None, None], "Calcitic, dolomitic, limestone, marble, and granite, 1920 kg/m^3": [1920.0, 880.0, 1.59, None, None], "Calcitic, dolomitic, limestone, marble, and granite, 1600 kg/m^3": [1600.0, None, 1.15, None, None], "Gypsum partition tile, 75 by 300 by 760 mm, solid, 3 cells": [None, 790.0, None, 0.222, 75], "Gypsum partition tile, 75 by 300 by 760 mm, with 4 cells": [None, None, None, 0.238, 75], "Gypsum partition tile, 100 by 300 by 760 mm, 3 cells": [None, None, None, 0.294, 100], "Limestone, 2400 kg/m^3": [2400.0, 840.0, 0.57, None, None], "Limestone, 2600 kg/m^3": [2600.0, 840.0, 0.93, None, None], "Concrete, Sand and gravel or stone aggregate concretes, 2400 kg/m^3": [2400.0, None, 2.15, None, None], "Concrete, Sand and gravel or stone aggregate concretes, 2240 kg/m^3": [2240.0, 900.0, 1.95, None, None], "Concrete, Sand and gravel or stone aggregate concretes, 2080 kg/m^3": [2080.0, None, 1.45, None, None], "Concrete, Low-mass aggregate or limestone": [1920.0, None, 1.1, None, None], "Concrete, Expanded shale, clay, or slate; expanded slags; cinders; pumice; scoria; 1600 kg/m^3": [1600.0, 840.0, 0.785, None, None], "Concrete, Expanded shale, clay, or slate; expanded slags; cinders; pumice; scoria; 1280 kg/m^3": [1280.0, 840.0, 0.535, None, None], "Concrete, Expanded shale, clay, or slate; expanded slags; cinders; pumice; scoria; 960 kg/m^3": [960.0, None, 0.33, None, None], "Concrete, Expanded shale, clay, or slate; expanded slags; cinders; pumice; scoria; 640 kg/m^3": [640.0, None, 0.18, None, None], "Concrete, Gypsum/fiber concrete (87.5% gypsum, 12.5% wood chips)": [800.0, 840.0, 0.24, None, None], "Concrete, Cement/lime, mortar, and stucco, 1920 kg/m^3": [1920.0, None, 1.4, None, None], "Concrete, Cement/lime, mortar, and stucco, 1600 kg/m^3": [1600.0, None, 0.97, None, None], "Concrete, Cement/lime, mortar, and stucco, 1280 kg/m^3": [1280.0, None, 0.65, None, None], "Concrete, Perlite, vermiculite, and polystyrene beads, 800 kg/m^3": [800.0, None, 0.265, None, None], "Concrete, Perlite, vermiculite, and polystyrene beads, 640 kg/m^3": [640.0, 795.0, 0.21, None, None], "Concrete, Perlite, vermiculite, and polystyrene beads, 480 kg/m^3": [480.0, None, 0.16, None, None], "Concrete, Perlite, vermiculite, and polystyrene beads, 320 kg/m^3": [320.0, None, 0.12, None, None], "Concrete, Foam concretes, 1920 kg/m^3": [1920.0, None, 0.75, None, None], "Concrete, Foam concretes, 1600 kg/m^3": [1600.0, None, 0.6, None, None], "Concrete, Foam concretes, 1280 kg/m^3": [1280.0, None, 0.44, None, None], "Concrete, Foam concretes, 1120 kg/m^3": [1120.0, None, 0.36, None, None], "Concrete, Foam concretes and cellular concretes, 960 kg/m^3": [960.0, None, 0.3, None, None], "Concrete, Foam concretes and cellular concretes, 640 kg/m^3": [640.0, None, 0.2, None, None], "Concrete, Foam concretes and cellular concretes, 320 kg/m^3": [320.0, None, 0.12, None, None], "Concrete, Aerated concrete (oven-dried)": [615.0, 840.0, 0.2, None, None], "Concrete, Polystyrene concrete (oven-dried)": [527.5, 840.0, 0.37, None, None], "Concrete, Polymer concrete, 1950 kg/m^3": [1950.0, None, 1.64, None, None], "Concrete, Polymer concrete, 2200 kg/m^3": [2200.0, None, 1.03, None, None], "Concrete, Polymer cement": [1870.0, None, 0.78, None, None], "Concrete, Slag concrete, 960 kg/m^3": [960.0, None, 0.22, None, None], "Concrete, Slag concrete, 1280 kg/m^3": [1280.0, None, 0.32, None, None], "Concrete, Slag concrete, 1600 kg/m^3": [1600.0, None, 0.43, None, None], "Concrete, Slag concrete, 2000 kg/m^3": [2000.0, None, 1.23, None, None]} ASHRAE_woods = {"Oak": [705.0, 1630.0, 0.17, None, None], "Birch": [702.5, 1630.0, 0.175, None, None], "Maple": [667.5, 1630.0, 0.165, None, None], "Ash": [642.5, 1630.0, 0.155, None, None], "Southern pine": [615.0, 1630.0, 0.15, None, None], "Southern yellow pine": [500.0, 1630.0, 0.13, None, None], "Eastern white pine": [400.0, 1630.0, 0.1, None, None], "Douglas fir/larch": [557.5, 1630.0, 0.145, None, None], "Southern cypress": [507.5, 1630.0, 0.13, None, None], "Hem/fir, spruce/pine/fir": [445.0, 1630.0, 0.12, None, None], "Spruce": [400.0, 1630.0, 0.09, None, None], "Western red cedar": [350.0, 1630.0, 0.09, None, None], "West coast woods, cedars": [425.0, 1630.0, 0.115, None, None], "Eastern white cedar": [360.0, 1630.0, 0.1, None, None], "California redwood": [420.0, 1630.0, 0.115, None, None], "Pine (oven-dried)": [370.0, 1880.0, 0.092, None, None], "Spruce (oven-dried)": [395.0, 1880.0, 0.1, None, None]} ASHRAE = {} for i in [ASHRAE_board_siding, ASHRAE_flooring, ASHRAE_insulation, ASHRAE_roofing, ASHRAE_plastering, ASHRAE_masonry, ASHRAE_woods]: ASHRAE.update(i) def ASHRAE_k(ID): r"""Returns thermal conductivity of a building or insulating material from a table in [1]_. Thermal conductivity is independent of temperature here. Many entries in the table are listed for varying densities, but the appropriate ID from the table must be selected to account for that. Parameters ---------- ID : str ID corresponding to a material in the dictionary `ASHRAE` Returns ------- k : float Thermal conductivity of the material, [W/m/K] Examples -------- >>> ASHRAE_k(ID='Mineral fiber') 0.036 References ---------- .. [1] ASHRAE Handbook: Fundamentals. American Society of Heating, Refrigerating and Air-Conditioning Engineers, Incorporated, 2013. """ values = ASHRAE[ID] if values[2]: return values[2] else: R = values[3] t = values[4]/1000. # mm to m return R_to_k(R, t) _refractory_Ts = [673.15, 873.15, 1073.15, 1273.15, 1473.15] refractories = {"Silica": [1820.0, (1.2, 1.36, 1.51, 1.64, 1.76), (915.0, 944.0, 961.0, 969.0, 979.0)], "Silica special": [1910.0, (1.55, 1.76, 1.95, 2.12, 2.28), (915.0, 944.0, 961.0, 970.0, 980.0)], "Fused silica": [1940.0, (1.44, 1.53, 1.61, 1.67, 1.73), (917.0, 946.0, 963.0, 972.0, 982.0)], "Fireclay": [2150.0, (1.05, 1.1, 1.15, 1.18, 1.22), (956.0, 997.0, 1021.0, 1037.0, 1054.0)], "High-duty fireclay": [2320.0, (1.2, 1.27, 1.33, 1.38, 1.42), (958.0, 999.0, 1024.0, 1040.0, 1058.0)], "Sillimanite": [2530.0, (1.66, 1.76, 1.84, 1.92, 1.98), (978.0, 1024.0, 1052.0, 1072.0, 1093.0)], "Mullite": [2540.0, (1.45, 1.52, 1.58, 1.63, 1.67), (987.0, 1035.0, 1065.0, 1087.0, 1109.0)], "Corundum 90%": [2830.0, (2.0, 2.1, 2.19, 2.27, 2.33), (993.0, 1043.0, 1072.0, 1095.0, 1118.0)], "Bauxite": [2760.0, (2.06, 2.03, 2.02, 2.0, 1.99), (994.0, 1045.0, 1077.0, 1100.0, 1124.0)], "Corundum 99%": [2830.0, (4.97, 4.36, 3.93, 3.6, 3.35), (1011.0, 1066.0, 1099.0, 1124.0, 1150.0)], "Corundum Spinel": [3100.0, (3.01, 3.02, 3.03, 3.04, 3.05), (1013.0, 1067.0, 1100.0, 1126.0, 1152.0)], "ACr 90": [3180.0, (4.2, 3.81, 3.52, 3.3, 3.12), (782.0, 794.0, 806.0, 816.0, 825.0)], "ACrZ 20": [3780.0, (2.4, 2.33, 2.27, 2.22, 2.18), (772.0, 789.0, 804.0, 814.0, 825.0)], "ACrZ 60": [3200.0, (3.8, 3.4, 3.11, 2.89, 2.71), (905.0, 945.0, 970.0, 990.0, 1010.0)], "Magnesite Chrome": [3060.0, (3.5, 3.27, 3.1, 2.96, 2.85), (1004.0, 1043.0, 1079.0, 1110.0, 1138.0)], "Magnesia": [3000.0, (7.5, 6.23, 5.37, 4.75, 4.28), (1047.0, 1088.0, 1125.0, 1158.0, 1188.0)], "Magnesite Spinel": [2850.0, (3.8, 3.44, 3.18, 2.98, 2.82), (1050.0, 1093.0, 1131.0, 1164.0, 1194.0)], "Magnesite Graphite H15": [2980.0, (9.96, 8.46, 7.44, 6.68, 6.1), (1061.0, 1117.0, 1168.0, 1215.0, 1258.0)], "Dolomite P10": [2970.0, (4.17, 3.99, 3.92, 3.75, 3.66), (950.0, 988.0, 1022.0, 1051.0, 1078.0)], "Sillimanite P5": [2740.0, (1.5, 1.5, 1.5, 1.5, 1.5), (986.0, 1037.0, 1070.0, 1095.0, 1120.0)], "Bauxite P5": [2830.0, (2.9, 2.67, 2.49, 2.36, 2.25), (1000.0, 1056.0, 1092.0, 1121.0, 1149.0)], "Corundum P10": [3020.0, (5.49, 5.19, 4.96, 4.78, 4.62), (1020.0, 1083.0, 1126.0, 1160.0, 1195.0)], "Magnesite P5": [2920.0, (5.05, 4.53, 4.15, 3.86, 3.63), (1050.0, 1097.0, 1139.0, 1177.0, 1211.0)], "Zirconia": [4950.0, (1.63, 1.54, 1.48, 1.43, 1.38), (624.0, 668.0, 698.0, 718.0, 737.0)], "Zircon": [3940.0, (2.67, 2.49, 2.35, 2.24, 2.15), (708.0, 747.0, 773.0, 788.0, 804.0)], "AZS 41": [4000.0, (4.55, 4.17, 4.25, 4.85, 5.4), (831.0, 878.0, 908.0, 929.0, 950.0)], "AZS 33": [3720.0, (5.17, 4.42, 4.0, 4.45, 5.4), (861.0, 908.0, 938.0, 958.0, 980.0)], "a/b-Alumina": [3200.0, (4.78, 4.45, 4.3, 5.0, 6.05), (989.0, 1044.0, 1080.0, 1107.0, 1133.0)], "SIC 40%": [2400.0, (4.2, 4.41, 4.58, 4.73, 4.86), (993.0, 1043.0, 1072.0, 1095.0, 1118.0)], "SIC 70%": [2600.0, (7.0, 6.81, 6.67, 6.55, 6.45), (998.0, 1049.0, 1079.0, 1103.0, 1126.0)], "SIC 90%": [2680.0, (18.6, 17.55, 16.76, 16.14, 15.62), (1005.0, 1058.0, 1090.0, 1115.0, 1140.0)], "L1260": [490.0, (0.14, 0.16, 0.18, 0.2, 0.22), (942.0, 979.0, 1002.0, 1017.0, 1033.0)], "L1400": [790.0, (0.27, 0.3, 0.32, 0.34, 0.36), (954.0, 994.0, 1018.0, 1034.0, 1050.0)], "L1540": [890.0, (0.32, 0.35, 0.38, 0.41, 0.43), (979.0, 1026.0, 1054.0, 1075.0, 1096.0)], "L1760": [1270.0, (0.45, 0.47, 0.49, 0.51, 0.53), (991.0, 1040.0, 1070.0, 1092.0, 1114.0)], "L1870": [1440.0, (1.5, 1.34, 1.23, 1.14, 1.07), (1011.0, 1066.0, 1099.0, 1124.0, 1150.0)], "Carbon, anthracite": [1540.0, (7.0, 8.51, 9.95, 11.33, 12.65), (1106.0, 1240.0, 1362.0, 1474.0, 1581.0)], "Carbon, graphite": [1550.0, (67.0, 60.67, 56.06, 52.01, 49.46), (1108.0, 1244.0, 1366.0, 1479.0, 1588.0)]} materials_dict = {} for mat_dict, reference in [(refractories, 1), (ASHRAE, 2), (building_materials, 3)]: for key in mat_dict.keys(): materials_dict[key] = reference def refractory_VDI_k(ID, T=None): r"""Returns thermal conductivity of a refractory material from a table in [1]_. Here, thermal conductivity is a function of temperature between 673.15 K and 1473.15 K according to linear interpolation among 5 equally-spaced points. Here, thermal conductivity is not a function of porosity, which can affect it. If T is outside the acceptable range, it is rounded to the nearest limit. If T is not provided, the lowest temperature's value is provided. Parameters ---------- ID : str ID corresponding to a material in the dictionary `refractories` T : float, optional Temperature of the refractory material, [K] Returns ------- k : float Thermal conductivity of the refractory material, [W/m/K] Examples -------- >>> [refractory_VDI_k('Fused silica', i) for i in [None, 200.0, 1000.0, 1500]] [1.44, 1.44, 1.58074, 1.73] References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if T is None: return float(refractories[ID][1][0]) else: ks = refractories[ID][1] if T < _refractory_Ts[0]: T = _refractory_Ts[0] elif T > _refractory_Ts[-1]: T = _refractory_Ts[-1] return float(interp(T, _refractory_Ts, ks)) def refractory_VDI_Cp(ID, T=None): r"""Returns heat capacity of a refractory material from a table in [1]_. Here, heat capacity is a function of temperature between 673.15 K and 1473.15 K according to linear interpolation among 5 equally-spaced points. Here, heat capacity is not a function of porosity, affects it. If T is outside the acceptable range, it is rounded to the nearest limit. If T is not provided, the lowest temperature's value is provided. Parameters ---------- ID : str ID corresponding to a material in the dictionary `refractories` T : float, optional Temperature of the refractory material, [K] Returns ------- Cp : float Heat capacity of the refractory material, [W/m/K] Examples -------- >>> [refractory_VDI_Cp('Fused silica', i) for i in [None, 200.0, 1000.0, 1500]] [917.0, 917.0, 956.78225, 982.0] References ---------- .. [1] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if T is None: return float(refractories[ID][2][0]) else: Cps = refractories[ID][2] if T < _refractory_Ts[0]: T = _refractory_Ts[0] elif T > _refractory_Ts[-1]: T = _refractory_Ts[-1] return float(interp(T, _refractory_Ts, Cps)) def nearest_material(name, complete=False): r"""Returns the nearest hit to a given name from from dictionaries of building, insulating, or refractory material from tables in [1]_, [2]_, and [3]_. Function will pick the closest match based on a fuzzy search. if `complete` is True, will only return hits with all three of density, heat capacity, and thermal conductivity available. Parameters ---------- name : str Search keywords to be used by difflib function complete : bool, optional If True, returns only hits with all parameters available Returns ------- ID : str A key to one of the dictionaries mentioned above Examples -------- >>> nearest_material('stainless steel') 'Metals, stainless steel' References ---------- .. [1] ASHRAE Handbook: Fundamentals. American Society of Heating, Refrigerating and Air-Conditioning Engineers, Incorporated, 2013. .. [2] DIN EN 12524 (2000-07) Building Materials and Products Hygrothermal Properties - Tabulated Design Values; English Version of DIN EN 12524. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ import difflib if complete: hits = difflib.get_close_matches(name, materials_dict.keys(), n=1000, cutoff=0) for hit in hits: if materials_dict[hit] == 1 or materials_dict[hit]==3 or (ASHRAE[hit][0] and ASHRAE[hit][1]): return hit else: ID = difflib.get_close_matches(name, materials_dict.keys(), n=1, cutoff=0.6) if not ID: ID = difflib.get_close_matches(name, materials_dict.keys(), n=1, cutoff=0.3) if not ID: ID = difflib.get_close_matches(name, materials_dict.keys(), n=1, cutoff=0) return ID[0] def k_material(ID, T=298.15): r"""Returns thermal conductivity of a building, insulating, or refractory material from tables in [1]_, [2]_, and [3]_. Thermal conductivity may or may not be dependent on temperature depending on the source used. Function must be provided with either a key to one of the dictionaries `refractories`, `ASHRAE`, or `building_materials` - or a search term which will pick the closest match based on a fuzzy search. To determine which source the fuzzy search will pick, use the function `nearest_material`. Fuzzy searches are slow; it is preferable to call this function with a material key directly. Parameters ---------- ID : str String as described above T : float, optional Temperature of the material, [K] Returns ------- k : float Thermal conductivity of the material, [W/m/K] Examples -------- >>> k_material('Mineral fiber') 0.036 References ---------- .. [1] ASHRAE Handbook: Fundamentals. American Society of Heating, Refrigerating and Air-Conditioning Engineers, Incorporated, 2013. .. [2] DIN EN 12524 (2000-07) Building Materials and Products Hygrothermal Properties - Tabulated Design Values; English Version of DIN EN 12524. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if ID not in materials_dict: ID = nearest_material(ID) if ID in refractories: return refractory_VDI_k(ID, T) elif ID in ASHRAE: return ASHRAE_k(ID) else: return float(building_materials[ID][1]) def rho_material(ID): r"""Returns the density of a building, insulating, or refractory material from tables in [1]_, [2]_, and [3]_. No temperature dependence is available. Function must be provided with either a key to one of the dictionaries `refractories`, `ASHRAE`, or `building_materials` - or a search term which will pick the closest match based on a fuzzy search. To determine which source the fuzzy search will pick, use the function `nearest_material`. Fuzzy searches are slow; it is preferable to call this function with a material key directly. Parameters ---------- ID : str String as described above Returns ------- rho : float Density of the material, [kg/m^3] Examples -------- >>> rho_material('Board, Asbestos/cement') 1900.0 References ---------- .. [1] ASHRAE Handbook: Fundamentals. American Society of Heating, Refrigerating and Air-Conditioning Engineers, Incorporated, 2013. .. [2] DIN EN 12524 (2000-07) Building Materials and Products Hygrothermal Properties - Tabulated Design Values; English Version of DIN EN 12524. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if ID not in materials_dict: ID = nearest_material(ID) if ID in refractories: rho = float(refractories[ID][0]) # Density available for all hits elif ID in building_materials: rho = float(building_materials[ID][0]) # Density available for all hits else: rho = ASHRAE[ID][0] if rho is None: raise ValueError("Density is not available for this material") else: rho = float(rho) return rho def Cp_material(ID, T=298.15): r"""Returns heat capacity of a building, insulating, or refractory material from tables in [1]_, [2]_, and [3]_. Heat capacity may or may not be dependent on temperature depending on the source used. Function must be provided with either a key to one of the dictionaries `refractories`, `ASHRAE`, or `building_materials` - or a search term which will pick the closest match based on a fuzzy search. To determine which source the fuzzy search will pick, use the function `nearest_material`. Fuzzy searches are slow; it is preferable to call this function with a material key directly. Parameters ---------- ID : str String as described above T : float, optional Temperature of the material, [K] Returns ------- Cp : float Heat capacity of the material, [W/m/K] Examples -------- >>> Cp_material('Mineral fiber') 840.0 References ---------- .. [1] ASHRAE Handbook: Fundamentals. American Society of Heating, Refrigerating and Air-Conditioning Engineers, Incorporated, 2013. .. [2] DIN EN 12524 (2000-07) Building Materials and Products Hygrothermal Properties - Tabulated Design Values; English Version of DIN EN 12524. .. [3] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. """ if ID not in materials_dict: ID = nearest_material(ID) if ID in refractories: Cp = refractory_VDI_Cp(ID, T) elif ID in building_materials: Cp = float(building_materials[ID][2]) # Density available for all hits else: Cp = ASHRAE[ID][1] if Cp is None: raise ValueError("Heat capacity is not available for this material") else: Cp = float(Cp) return Cp ================================================ FILE: ht/numba.py ================================================ # type: ignore """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2020, 2021, 2022, 2023 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import inspect import fluids import fluids.numba import numba import ht normal_fluids = fluids normal = ht orig_file = __file__ caching = False """ """ __all__ = [] __funcs = {} numerics = fluids.numba.numerics replaced = fluids.numba.numerics_dict.copy() def transform_complete_ht(replaced, __funcs, __all__, normal, vec=False): cache_blacklist = {"h_Ganguli_VDI", "fin_efficiency_Kern_Kraus", "h_Briggs_Young", "h_ESDU_high_fin", "h_ESDU_low_fin", "Nu_Nusselt_Rayleigh_Holling_Herwig", "DBundle_for_Ntubes_Phadkeb", "Thome", "to_solve_q_Thome", "temperature_effectiveness_air_cooler", "factorial", "size_bundle_from_tubecount", "crossflow_effectiveness_to_int", "temperature_effectiveness_basic", "_NTU_from_P_solver", "NTU_from_P_basic", "_NTU_from_P_erf", "NTU_from_P_G", "NTU_from_P_J", "NTU_from_P_E", "NTU_from_P_H", "NTU_from_P_plate", "_NTU_from_P_objective", "temperature_effectiveness_plate", # dies on recursion } __funcs.update(normal_fluids.numba.numbafied_fluids_functions.copy()) new_mods = normal_fluids.numba.transform_module(normal, __funcs, replaced, vec=vec, cache_blacklist=cache_blacklist) if vec: conv_fun = numba.vectorize else: conv_fun = numba.njit to_change_full_output = [] to_change = {} to_change.update(dict.fromkeys(to_change_full_output, "full_output")) # to_change['hx.Ntubes_Phadkeb'] = 'square_C1s is None' to_change["boiling_nucleic.Gorenflo"] = "h0 is None: # NUMBA: DELETE" for s, bad_branch in to_change.items(): mod, func = s.split(".") source = inspect.getsource(getattr(getattr(normal, mod), func)) fake_mod = __funcs[mod] source = normal_fluids.numba.remove_branch(source, bad_branch) normal_fluids.numba.numba_exec_cacheable(source, fake_mod.__dict__, fake_mod.__dict__) new_func = fake_mod.__dict__[func] obj = conv_fun(cache=caching)(new_func) __funcs[func] = obj globals()[func] = obj obj.__doc__ = "" to_change = ["air_cooler.Ft_aircooler", "hx.Ntubes_Phadkeb", "hx.DBundle_for_Ntubes_Phadkeb", "boiling_nucleic.h_nucleic_methods", "hx._NTU_from_P_solver", "hx.NTU_from_P_plate"] normal_fluids.numba.transform_lists_to_arrays(normal, to_change, __funcs, cache_blacklist=cache_blacklist) for mod in new_mods: mod.__dict__.update(__funcs) try: __all__.extend(mod.__all__) except AttributeError: pass __funcs["hx"]._load_coeffs_Phadkeb() # Run after everything is done transform_complete_ht(replaced, __funcs, __all__, normal, vec=False) globals().update(__funcs) globals().update(replaced) __name__ = "ht.numba" __file__ = orig_file ================================================ FILE: ht/numba_vectorized.py ================================================ # type: ignore """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2020, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import fluids import fluids.numba import ht as normal_ht import ht.numba orig_file = __file__ normal = normal_ht __all__ = [] __funcs = {} numerics = fluids.numba.numerics replaced = fluids.numba.numerics_dict.copy() ht.numba.transform_complete_ht(replaced, __funcs, __all__, normal, vec=True) globals().update(__funcs) globals().update(replaced) __name__ = "ht.numba_vectorized" __file__ = orig_file ================================================ FILE: ht/py.typed ================================================ ================================================ FILE: ht/radiation.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations import os from math import e, exp from fluids.constants import c, h, k, sigma from fluids.numerics import numpy as np __all__: list[str] = [ "blackbody_spectral_radiance", "grey_transmittance", "q_rad", "solar_spectrum", ] def blackbody_spectral_radiance(T: float, wavelength: float) -> float: r"""Returns the spectral radiance, in units of W/m^2/sr/µm. .. math:: I_{\lambda,blackbody,e}(\lambda,T)=\frac{2hc_o^2} {\lambda^5[\exp(hc_o/\lambda k T)-1]} Parameters ---------- T : float Temperature of the surface, [K] wavelength : float Length of the wave to be considered, [m] Returns ------- I : float Spectral radiance [W/(m^2*sr*m)] Notes ----- Can be used to derive the Stefan-Boltzman law, or determine the maximum radiant frequency for a given temperature. Examples -------- Checked with Spectral-calc.com, at [2]_. >>> blackbody_spectral_radiance(800., 4E-6) 1311694129.7430933 Calculation of power from the sun (earth occupies 6.8E-5 steradian of the sun): >>> from scipy.integrate import quad >>> rad = lambda l: blackbody_spectral_radiance(5778., l)*6.8E-5 >>> quad(rad, 1E-10, 1E-4)[0] 1367.9827067638964 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. .. [2] Spectral-calc.com. Blackbody Calculator, 2015. http://www.spectralcalc.com/blackbody_calculator/blackbody.php """ to_exp = h*c/(wavelength*T*k) if to_exp > 709.7: return 0.0 else: exp_term = exp(to_exp) return 2.*h*c*c*wavelength**-5/(exp_term - 1.0) def q_rad(emissivity: float, T: int, T2: float=0) -> float: r"""Returns the radiant heat flux of a surface, optionally including assuming radiant heat transfer back to the surface. .. math:: q = \epsilon \sigma (T_1^4 - T_2^4) Parameters ---------- emissivity : float Fraction of black-body radiation which is emitted, [-] T : float Temperature of the surface, [K] T2 : float, optional Temperature of the surrounding material of the surface [K] Returns ------- q : float Heat exchange [W/m^2] Notes ----- Emissivity must be less than 1. T2 may be larger than T. Examples -------- >>> q_rad(emissivity=1, T=400) 1451.613952 >>> q_rad(.85, T=400, T2=305.) 816.7821722650002 References ---------- .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ: Wiley, 2011. """ T_T = T*T T2_T2 = T2*T2 return sigma*emissivity*(T_T*T_T - T2_T2*T2_T2) def grey_transmittance(extinction_coefficient: float, molar_density: int, length: float, base: float=e) -> float: r"""Calculates the transmittance of a grey body, given the extinction coefficient of the material, its molar density, and the path length of the radiation. .. math:: \tau = base^{(-\epsilon \cdot l\cdot \rho_m )} Parameters ---------- extinction_coefficient : float The extinction coefficient of the material the radiation is passing at the modeled frequency, [m^2/mol] molar_density : float The molar density of the material the radiation is passing through, [mol/m^3] length : float The length of the body the radiation is transmitted through, [m] base : float, optional The exponent used in calculations; `e` is more theoretically sound but 10 is often used as a base by chemists, [-] Returns ------- transmittance : float The fraction of spectral radiance which is transmitted through a grey body (can be liquid, gas, or even solid ex. in the case of glasses) [-] Notes ----- For extinction coefficients, see the HITRAN database. They are temperature and pressure dependent for each chemical and phase. Examples -------- Overall transmission loss through 1 cm of precipitable water equivalent atmospheric water vapor at a frequency of 1.3 um [2]_: >>> grey_transmittance(3.8e-4, molar_density=55300, length=1e-2) 0.8104707721191062 References ---------- .. [1] Modest, Michael F. Radiative Heat Transfer, Third Edition. 3rd edition. New York: Academic Press, 2013. .. [2] Eldridge, Ralph G. "Water Vapor Absorption of Visible and Near Infrared Radiation." Applied Optics 6, no. 4 (April 1, 1967): 709-13. https://doi.org/10.1364/AO.6.000709. """ transmittance = molar_density*extinction_coefficient*length return base**(-transmittance) def solar_spectrum(model="SOLAR-ISS"): r"""Returns the solar spectrum of the sun according to the specified model. Only the 'SOLAR-ISS' model is supported. Parameters ---------- model : str, optional The model to use; 'SOLAR-ISS' is the only model available, [-] Returns ------- wavelengths : ndarray The wavelengths of the solar spectra, [m] SSI : ndarray The solar spectral irradiance of the sun, [W/(m^2*m)] uncertainties : ndarray The estimated absolute uncertainty of the measured spectral irradiance of the sun, [W/(m^2*m)] Notes ----- The power of the sun changes as the earth gets closer or further away. In [1]_, the UV and VIS data come from observations in 2008; the IR comes from measurements made from 2010-2016. There is a further 28 W/m^2 for the 3 micrometer to 160 micrometer range, not included in this model. All data was corrected to a standard distance of one astronomical unit from the Sun, as is the resultant spectrum. The variation of the spectrum as a function of distance from the sun should alter only the absolute magnitudes. [2]_ contains another dataset. 99.9% of the time this function takes is to read in the solar data from disk. This could be reduced by using pandas. Examples -------- >>> wavelengths, SSI, uncertainties = solar_spectrum() Calculate the minimum and maximum values of the wavelengths (0.5 nm/3000nm) and SSI: >>> float(min(wavelengths)), float(max(wavelengths)), float(min(SSI)), float(max(SSI)) (5e-10, 2.9999e-06, 1330.0, 2256817820.0) Integration - calculate the solar constant, in units of W/m^2 hitting earth's atmosphere. >>> from scipy.integrate import trapezoid >>> float(trapezoid(SSI, wavelengths)) 1344.802978 References ---------- .. [1] Meftah, M., L. Damé, D. Bolsée, A. Hauchecorne, N. Pereira, D. Sluse, G. Cessateur, et al. "SOLAR-ISS: A New Reference Spectrum Based on SOLAR/SOLSPEC Observations." Astronomy & Astrophysics 611 (March 1, 2018): A1. https://doi.org/10.1051/0004-6361/201731316. .. [2] Woods Thomas N., Chamberlin Phillip C., Harder Jerald W., Hock Rachel A., Snow Martin, Eparvier Francis G., Fontenla Juan, McClintock William E., and Richard Erik C. "Solar Irradiance Reference Spectra (SIRS) for the 2008 Whole Heliosphere Interval (WHI)." Geophysical Research Letters 36, no. 1 (January 1, 2009). https://doi.org/10.1029/2008GL036373. """ if model == "SOLAR-ISS": folder = os.path.join(os.path.dirname(__file__), "data") pth = os.path.join(folder, "solar_iss_2018_spectrum.dat") data = np.genfromtxt(pth, dtype=np.float64, delimiter=" ") wavelengths, SSI, uncertainties = data[:, 0], data[:, 1], data[:, 2] wavelengths *= 1E-9 SSI *= 1E9 # Convert -1 uncertainties to nans uncertainties[uncertainties == -1] = np.nan uncertainties *= 1E9 return wavelengths, SSI, uncertainties ================================================ FILE: ht/units.py ================================================ # type: ignore """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2017, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import types import ht from ht import R_to_k, k_to_R, k_to_thermal_resistivity, thermal_resistivity_to_k __all__ = ["u", "wraps_numpydoc"] try: from pint import _DEFAULT_REGISTRY as u except ImportError: # pragma: no cover raise ImportError("The unit handling in fluids requires the installation " "of the package pint, available on pypi or from " "https://github.com/hgrecco/pint") from fluids.units import wraps_numpydoc __funcs = {} for name in dir(ht): if name in ("__getattr__", "__test__"): continue obj = getattr(ht, name) if isinstance(obj, types.FunctionType): obj = wraps_numpydoc(u)(obj) elif isinstance(obj, str): continue if name == "__all__": continue __all__.append(name) __funcs.update({name: obj}) globals().update(__funcs) wrapped_R_to_k = R_to_k wrapped_k_to_R = k_to_R def R_to_k(R, t, A=1*u.m**2): if R.dimensionality == (u.K/u.W).dimensionality: if A.to_base_units().magnitude != 1: raise ValueError("The conversion with R in units of K/W is only permissible if A = 1 length**2") R = R*u.m**2 elif R.dimensionality != (u.K*u.m**2/u.W).dimensionality: raise ValueError("Units of R must be either K/W if A = 1 length**2 or m^2*K/W otherwise") return wrapped_R_to_k(R, t, A) # k_to_R(k=0.5*u.W/u.m/u.K, t=0.025*u.m) # TODO define behavior def R_value_to_k(R_value, SI=True): r = R_value.to("m*K/W") return thermal_resistivity_to_k(r) def k_to_R_value(k, SI=True): r = k_to_thermal_resistivity(k) if SI: return r.to("m^2*K/(W*inch)") else: return r.to("ft^2*delta_degF*hour/(BTU*inch)") ================================================ FILE: ht/vectorized.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2017, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import FakePackage from fluids.numerics import numpy as np import ht """Basic module which wraps all ht functions with numpy's vectorize. All other object - dicts, classes, etc - are not wrapped. Supports star imports; so the same objects exported when importing from the main library will be imported from here. >>> from ht.vectorized import * Inputs do not need to be numpy arrays; they can be any iterable: >>> import ht.vectorized >>> ht.vectorized.LMTD([100, 101], 60., 30., 40.2) array([ 43.20040929, 43.60182765]) Note that because this needs to import ht itself, ht.vectorized needs to be imported separately; the following will cause an error: >>> import ht >>> ht.vectorized # Won't work, has not been imported yet The correct syntax is as follows: >>> import ht.vectorized # Necessary >>> from ht.vectorized import * # May be used without first importing ht """ __all__ = [] __funcs = {} if isinstance(np, FakePackage): pass else: import types for name in dir(ht): obj = getattr(ht, name) if isinstance(obj, types.FunctionType): obj = np.vectorize(obj) elif isinstance(obj, str): continue __all__.append(name) __funcs.update({name: obj}) globals().update(__funcs) ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["setuptools>=70.1", "wheel", "python-minifier"] build-backend = "backend" backend-path = ["_custom_build"] [project] name = "ht" version = "1.2.0" description = "Heat transfer component of Chemical Engineering Design Library (ChEDL)" readme = "README.rst" requires-python = ">=3.9" license = {text = "MIT"} authors = [ {name = "Caleb Bell", email = "Caleb.Andrew.Bell@gmail.com"} ] keywords = [ "heat-transfer", "heat-exchanger", "air-cooler", "tube-bank", "condensation", "boiling", "chemical-engineering", "mechanical-engineering", "pressure-drop", "radiation", "process-simulation", "engineering", "insulation", "flow-boiling", "nucleate-boiling", "reboiler", "cross-flow" ] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Education", "Intended Audience :: Manufacturing", "Intended Audience :: Science/Research", "Natural Language :: English", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: POSIX :: BSD", "Operating System :: POSIX :: Linux", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Education", "Topic :: Scientific/Engineering :: Atmospheric Science", "Topic :: Scientific/Engineering :: Chemistry", "Topic :: Scientific/Engineering :: Physics", ] dependencies = [ "fluids>=1.2.0", "numpy>=1.5.0", "scipy>=1.6.0" ] [project.optional-dependencies] # Core testing (what all CI uses) test = [ "pytest>=6.0", "pytest-cov", "pytest-xdist", "sympy", "fuzzywuzzy", "pint", "pytz", "pandas", "IPython", "matplotlib", "coveralls", "mpmath", "wheel", ] # For multiarch CI (lighter, no pandas/matplotlib issues, no coverage for speed) test-multiarch = [ "pytest>=6.0", "thefuzz", "pint", "pytz", "wheel", ] # Optional performance boost (installed conditionally in CI) numba = [ "numba", "coverage>=7.6.1", # Required for numba 0.62+ compatibility ] # Documentation docs = [ "sphinx", "numpydoc", "nbsphinx", "ipython", "numba", "sphinxcontrib-katex", "sphinx-sitemap", "sphinxcontrib-applehelp", "sphinxcontrib-devhelp", "sphinxcontrib-htmlhelp", "sphinxcontrib-qthelp", "sphinxcontrib-serializinghtml", "sphinxcontrib.googleanalytics", "matplotlib", "pint", ] # Linting and type checking lint = ["ruff", "mypy"] # Pre-release validation prerelease = [ "ht[test]", "ht[docs]", "nbval", "jacobi", "numdifftools", "mpmath", ] # Security scanning (barebones for CI) security = ["pip-audit", "bandit"] # Local development (everything) dev = [ "ht[test,numba,docs,lint,security]", "prek", "wheel", "build", "twine", ] [project.urls] Homepage = "https://github.com/CalebBell/ht" Download = "https://github.com/CalebBell/ht/tarball/1.2.0" [tool.setuptools] packages = ["ht"] [tool.setuptools.package-data] ht = ["data/*"] [tool.coverage.run] branch = true relative_files = true omit = [] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", ] [tool.mypy] exclude = [ "^_build/", "^_custom_build/", "^build/", "^dist/", ".*\\.egg-info/", "^venv/", "^\\.venv/", "^benchmarks/", "^dev/", "^notebooks/", ] [[tool.mypy.overrides]] module = [ "scipy.*", "numba.*", "mpmath.*", "sympy.*", "matplotlib.*", "geopy.*", "appdirs.*", "pandas.*", "pvlib.*", "pint.*", ] ignore_missing_imports = true [[tool.mypy.overrides]] module = "fluids.numerics" follow_imports = "skip" ignore_errors = true [[tool.mypy.overrides]] module = "fluids.numerics.*" ignore_errors = true [tool.ruff] line-length = 160 target-version = "py37" exclude = ["*.pyi"] [tool.ruff.lint] select = ["ALL"] extend-ignore = [ "FBT001", # boolean positional argument "PD008", # .at with pandas is fine with me "PD011", # Use .values instead of .to_numpy() - I prefer not to copy arrays "RUF059", # Allow unused variables "FIX002", # TODO are OK "FIX004", # HACK is OK "SIM109", # multiple equality comparisons are more performant than a set in small cases "PLR1714", # multiple equality comparisons are more performant than a set in small cases "D415", # First docstring line should end with a period, question mark, or exclamation point "DTZ004", # utcfromtimestamp makes sense for atmosphere model "PGH003", # type ignoring makes sense for numba-related things "S102", # Yes, exec is dangerous but it can be quite useful as well "PYI056", # changing __all__ "RUF012", # not using typing today "PERF403", # obvious, use an autofix if one becomes available "PERF203", # `try`-`except` within a loop incurs performance overhead "PERF401", # PERF401 Use a list comprehension to create a transformed list "PLR1730", # use of min/max are less performant than `if "PLC0415", # Allow lazy imports # ht specific "E701", # lots of this here "S302", # marshal is OK "SIM116", # 3 if statements does not require a dict # maybe these "PLE0605", "PLE0604", "TD002", "TD003", "TD004", "TD005", # Originally from astropy's pyproject.toml, see about removing many of them later "A00", "ANN", "ARG001", "ARG002", "ARG005", "B004", "B006", "B007", "B008", "B904", "BLE001", "C408", "C416", "C901", "COM812", "COM819", "D101", "D102", "D103", "D105", "D107", "D200", "D203", "D205", "D212", "D213", "D403", "D404", "D400", "D401", "D413", "D414", "D417", "DTZ001", "DTZ007", "E501", # "E711", "E721", "E731", "E741", "EM", "ERA001", "F841", "E722", "FBT002", "FBT003", "N8", "PLR2004", "PLR0911", "PLR0912", "PLR5501", "PLR0913", "PLR0915", "PLW2901", "PLW0603", "PTH", "RET", "RSE102", "RUF001", "RUF003", # greek characters are used in this project e.g. for allotropes "RUF005", # this one is not micropython compatible # "S101", # Use assert - removed from global ignore, now controlled by per-file-ignores below "S110", "S112", "S311", "S310", "SIM102", "SIM105", "SIM108", "SIM114", "SIM115", "SIM118", "SIM300", "SLF001", "T201", "TRY003", "TRY201", "TRY300", "TRY301", "PYI024", # PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple ] [tool.ruff.lint.mccabe] max-complexity = 10 [tool.ruff.lint.per-file-ignores] # Allow asserts and star imports in test files "tests/**/*.py" = ["S101", "F403", "F405", "PT011", "B017", "TRY002"] # asserts, star imports, broad exceptions OK in tests "**/test_*.py" = ["S101", "F403", "F405", "PT011", "B017", "TRY002"] # Allow asserts and star imports in documentation examples "docs/**/*.py" = ["S101", "F403", "F405", "PT011", "B017", "TRY002"] "docs/**/*.ipynb" = ["S101", "F403", "F405", "PT011", "B017", "TRY002"] [tool.pytest.ini_options] addopts = "--doctest-glob='*.rst' --ignore='conf.py'" norecursedirs = [".cache", ".git", "htmlcov", "notebooks", "dist", "build", "*.egg-info", ".tox", "surfaces", "prof", "benchmarks", "dev", "_build", "_custom_build", "__pycache__", "plots"] doctest_optionflags = ["NORMALIZE_WHITESPACE"] markers = [ "slow: slow tests", "thermo: relies on the thermo library, for integration testing", "online: needs internet", "mpmath: needs mpmath to check results against a higher-precision result", "fuzz: test not relevant to normal development, but can reveal bugs or provide certainty the results are correct", "numba: numba", "scipy: Needs scipy to work", "numpy: Needs numpy to work", "pytz: Needs pytz to work", ] ================================================ FILE: requirements_security.txt ================================================ numpy>=1.5.0 scipy>=1.6.0 fluids>=1.2.0 ================================================ FILE: tests/test_air_cooler.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.constants import foot, hp, inch, minute from fluids.geometry import AirCooledExchanger from fluids.numerics import assert_close, assert_close2d from ht import ( Ft_aircooler, air_cooler_noise_GPSA, air_cooler_noise_Mukherjee, dP_ESDU_high_fin, dP_ESDU_low_fin, h_Briggs_Young, h_ESDU_high_fin, h_ESDU_low_fin, h_Ganguli_VDI, ) ### Air Cooler def test_air_cooler_Ft(): Ft_1 = Ft_aircooler(Thi=93, Tho=52, Tci=35, Tco=54.59, Ntp=2, rows=4) assert_close(Ft_1, 0.9570456123827129) # Example 2 as in [1]_; author rounds to obtain a slightly different result. Ft_2 = Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4) assert_close(Ft_2, 0.5505093604092708) Ft_many = [[Ft_aircooler(Thi=125., Tho=80., Tci=25., Tco=95., Ntp=i, rows=j) for i in range(1,6)] for j in range(1, 6)] Ft_values = [[0.6349871996666123, 0.9392743008890244, 0.9392743008890244, 0.9392743008890244, 0.9392743008890244], [0.7993839562360742, 0.9184594715750571, 0.9392743008890244, 0.9392743008890244, 0.9392743008890244], [0.8201055328279105, 0.9392743008890244, 0.9784008071402877, 0.9392743008890244, 0.9392743008890244], [0.8276966706732202, 0.9392743008890244, 0.9392743008890244, 0.9828365967034366, 0.9392743008890244], [0.8276966706732202, 0.9392743008890244, 0.9392743008890244, 0.9392743008890244, 0.9828365967034366]] assert_close2d(Ft_many, Ft_values) def test_air_cooler_noise_GPSA(): noise = air_cooler_noise_GPSA(tip_speed=3177/minute, power=25.1*hp) assert_close(noise, 100.53680477959792) def test_air_cooler_noise_Mukherjee(): """# Confirmed to be log10's because of example tip speed reduction # of 60 m/s to 40 m/s saves 5.3 dB. # hp in shaft horse power # d in meters # sound pressure level, ref level 2E-5 pa """ noise = air_cooler_noise_Mukherjee(tip_speed=3177/minute, power=25.1*hp, fan_diameter=4.267) assert_close(noise, 99.11026329092925) noise = air_cooler_noise_Mukherjee(tip_speed=3177/minute, power=25.1*hp, fan_diameter=4.267, induced=True) assert_close(noise, 96.11026329092925) def test_h_ESDU_high_fin(): AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=20, tube_length=3, tube_diameter=1*inch, fin_thickness=0.000406, fin_density=1/0.002309, pitch_normal=.06033, pitch_parallel=.05207, fin_height=0.0159, tube_thickness=(.0254-.0186)/2, bundles_per_bay=1, parallel_bays=1, corbels=True) h_bare_tube_basis = h_ESDU_high_fin(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205.0) assert_close(h_bare_tube_basis, 1390.888918049757) def test_h_ESDU_low_fin(): AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=8, tube_length=0.5, tube_diameter=0.0164, fin_thickness=0.001, fin_density=1/0.003, pitch_normal=0.0313, pitch_parallel=0.0271, fin_height=0.0041, bundles_per_bay=1, parallel_bays=1, corbels=True) # All factors are matching again, except for the A min being different! # 5% diff, minor. h = h_ESDU_low_fin(m=0.914, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.217, Cp=1007., mu=1.8E-5, k=0.0253, k_fin=15.0) assert_close(h, 553.853836470948) h = h_ESDU_low_fin(m=0.914, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.217, Cp=1007., mu=1.8E-5, k=0.0253, k_fin=15.0, Pr_wall=0.68) assert_close(h, 560.74807767957759) def test_h_Briggs_Young(): AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=20, tube_length=3, tube_diameter=1*inch, fin_thickness=0.000406, fin_density=1/0.002309, pitch_normal=.06033, pitch_parallel=.05207, fin_height=0.0159, tube_thickness=(.0254-.0186)/2, bundles_per_bay=1, parallel_bays=1, corbels=True) h_bare_tube_basis = h_Briggs_Young(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205.) assert_close(h_bare_tube_basis, 1422.8722403237716) # Serth Process Heat Transfer Principles, Applications and Rules of Thumb # example with a different correlation entirely AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=56, tube_length=36*foot, tube_diameter=1*inch, fin_thickness=0.013*inch, fin_density=10/inch, angle=30, pitch_normal=2.5*inch, fin_height=0.625*inch, corbels=True) h = h_Briggs_Young(m=130.70315, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, rho=1.2013848, Cp=1009.0188, mu=1.9304793e-05, k=0.027864828, k_fin=238) # Goal. 51.785762 # Back converting to their choice of basis - finned heat transfer coefficient # Very close answer assert_close(h/AC.A_increase/.853, 51.785762, atol=.3) def test_h_Ganguli_VDI(): AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=56, tube_length=36*foot, tube_diameter=1*inch, fin_thickness=0.013*inch, fin_density=10/inch, angle=30, pitch_normal=2.5*inch, fin_height=0.625*inch, corbels=True) h = h_Ganguli_VDI(m=130.70315, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, rho=1.2013848, Cp=1009.0188, mu=1.9304793e-05, k=0.027864828, k_fin=238.0) assert_close(h, 969.2850818578595) # Example in VDI # assumed in-line = angle 45, rest specified # VDI misses some parameters like fin tip area AC = AirCooledExchanger(tube_rows=4, tube_passes=1, tubes_per_row=17, tube_length=0.98, tube_diameter=1*inch, fin_thickness=0.4E-3, fin_density=9/inch, angle=45, pitch_normal=0.06, fin_diameter=0.056) # Pr forced to match h = h_Ganguli_VDI(m=1.92, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, rho=0.909, Cp=1009.0188, mu=2.237e-05, k=0.03197131806799132, k_fin=209.0) # 22.49 goal, but there was a correction for velocity due to temperature increase # in the vdi answer assert_close(h/AC.A_increase, 22.49, rtol=2e-2) def test_dP_ESDU_high_fin(): AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=8, tube_length=0.5, tube_diameter=0.0164, fin_thickness=0.001, fin_density=1/0.003, pitch_normal=0.0313, pitch_parallel=0.0271, fin_height=0.0041, corbels=True) dP = dP_ESDU_high_fin(m=0.914, A_min=AC.A_min, A_increase=AC.A_increase, flow_area_contraction_ratio=AC.flow_area_contraction_ratio, tube_diameter=AC.tube_diameter, pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, tube_rows=AC.tube_rows, rho=1.217, mu=0.000018) assert_close(dP, 485.6307687791502) def test_dP_ESDU_low_fin(): AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=8, tube_length=0.5, tube_diameter=0.0164, fin_thickness=0.001, fin_density=1/0.003, pitch_normal=0.0313, pitch_parallel=0.0271, fin_height=0.0041, corbels=True) dP = dP_ESDU_low_fin(m=0.914, A_min=AC.A_min, A_increase=AC.A_increase, flow_area_contraction_ratio=AC.flow_area_contraction_ratio, tube_diameter=AC.tube_diameter, fin_height=AC.fin_height, bare_length=AC.bare_length, pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, tube_rows=AC.tube_rows, rho=1.217, mu=0.000018) assert_close(dP, 464.54331418655914) # vs 414 Pa; Kf, ka almost perfect - A_min is the source of difference @pytest.mark.slow @pytest.mark.fuzz def test_AirCooledExchangerPermutations(): """Demonstration of permutating all sorts of different options. need to try both nonlinear optimization (will work easier, need initial guesses) and some form of discrete problem solution space. 2 billion - that's just as many as there are floats! :) (len(tube_rows_options)*len(tube_passes_options)*len(tubes_per_row_options) *len(HTRI_Ls)*len(pitches)*len(fin_densities) *len(fin_heights)*len(ODs)*len(angles) *len(fin_thicknesses))/2.**31#15E-6/3600 """ from random import choice tube_rows_options = range(1, 20) tube_passes_options = range(1, 20) tubes_per_row_options = range(1, 40) from ht.air_cooler import ODs, fin_densities, fin_heights from ht.hx import HEDH_pitches, HTRI_Ls angles = [30, 45, 60, 90] pitches = sorted({j for k in HEDH_pitches.values() for j in k}) pitches = [1.25, 1.312, 1.33, 1.375, 1.4, 1.5] fin_thicknesses = [3E-4, 4E-4, 5E-4, 6E-4, 7E-4] # made up for i in range(100): angle = choice(angles) pitch = choice(pitches) tube_length = choice(HTRI_Ls) fin_density = choice(fin_densities) fin_height = choice(fin_heights) fin_thickness = choice(fin_thicknesses) tube_diameter = choice(ODs) tube_rows = choice(tube_rows_options) tube_passes = choice(tube_passes_options) tubes_per_row = choice(tubes_per_row_options) AC = AirCooledExchanger(tube_rows=tube_rows, tube_passes=tube_passes, tubes_per_row=tubes_per_row, tube_length=tube_length, tube_diameter=tube_diameter, fin_thickness=fin_thickness, fin_density=fin_density, angle=angle, pitch=pitch, fin_height=fin_height) ================================================ FILE: tests/test_boiling_flow.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close from ht.boiling_flow import Chen_Bennett, Chen_Edelstein, Lazarek_Black, Li_Wu, Liu_Winterton, Sun_Mishima, Thome, Yun_Heo_Kim def test_Lazarek_Black(): q = 1E7 h1 = Lazarek_Black(m=10.0, D=0.3, mul=1E-3, kl=0.6, Hvap=2E6, q=q) Te = q/h1 assert_close(h1, 51009.87001967105) h2 = Lazarek_Black(m=10.0, D=0.3, mul=1E-3, kl=0.6, Hvap=2E6, Te=Te) assert_close(h1, h2) with pytest.raises(Exception): Lazarek_Black(m=10, D=0.3, mul=1E-3, kl=0.6, Hvap=2E6) """ The code to derive the form with `Te` specified is as follows: >>> from sympy import * >>> Relo, Bgish, kl, D, h, Te = symbols('Relo, Bgish, kl, D, h, Te', ... positive=True, real=True) >>> solve(Eq(h, 30*Relo**Rational(857,1000)*(Bgish*h*Te)**Rational(714, ... 1000)*kl/D), h) [27000*30**(71/143)*Bgish**(357/143)*Relo**(857/286)*Te**(357/143)*kl**(500/143)/D**(500/143)] """ def test_Li_Wu(): q = 1E5 h = Li_Wu(m=1.0, x=0.2, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, q=q) Te = 1E5/h assert_close(h, 5345.409399239493) h2 = Li_Wu(m=1, x=0.2, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, Te=Te) assert_close(h2, h) with pytest.raises(Exception): Li_Wu(m=1, x=0.2, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5) """ The code to derive the form with `Te` specified is as follows: >>> from sympy import * >>> h, A, Te, G, Hvap = symbols('h, A, Te, G, Hvap', positive=True, real=True) >>> solve(Eq(h, A*(h*Te/G/Hvap)**0.3), h) [A**(10/7)*Te**(3/7)/(G**(3/7)*Hvap**(3/7))] """ def test_Sun_Mishima(): h = Sun_Mishima(m=1.0, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, Te=10) assert_close(h, 507.6709168372167) q = 1E5 h = Sun_Mishima(m=1, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, q=q) Te = q/h h2 = Sun_Mishima(m=1, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, Te=Te) assert_close(h, h2) assert_close(h2, 2538.4455424345983) with pytest.raises(Exception): Sun_Mishima(m=1, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5) """ The code to derive the form with `Te` specified is as follows: >>> from sympy import * >>> h, A, Te, G, Hvap = symbols('h, A, Te, G, Hvap', positive=True, real=True) >>> solve(Eq(h, A*(h*Te/G/Hvap)**0.54), h) [A**(50/23)*Te**(27/23)/(G**(27/23)*Hvap**(27/23))] """ def test_Thome(): h = Thome(m=1.0, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300.0, Cpg=1400, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, q=1E5) assert_close(h, 1633.008836502032) h = Thome(m=10, x=0.5, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300.0, Cpg=1400.0, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, q=1E5) assert_close(h, 3120.1787715124824) Te = 32.04944566414243 h2 = Thome(m=10.0, x=0.5, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300.0, Cpg=1400.0, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, Te=Te) assert_close(h, h2) with pytest.raises(Exception): Thome(m=1, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300., Cpg=1400., sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6) def test_Yun_Heo_Kim(): q = 1E4 h1 = Yun_Heo_Kim(m=1.0, x=0.4, D=0.3, rhol=567., mul=156E-6, sigma=0.02, Hvap=9E5, q=q) Te = q/h1 h2 = Yun_Heo_Kim(m=1, x=0.4, D=0.3, rhol=567., mul=156E-6, sigma=0.02, Hvap=9E5, Te=Te) assert_close(h1, h2) with pytest.raises(Exception): Yun_Heo_Kim(m=1, x=0.4, D=0.3, rhol=567., mul=156E-6, sigma=0.02, Hvap=9E5) """ The code to derive the form with `Te` specified is as follows: >>> from sympy import * >>> h, A = symbols('h, A', positive=True, real=True) >>> solve(Eq(h, A*(h)**0.1993), h) [A**(10000/8707)] """ def test_Liu_Winterton(): h = Liu_Winterton(m=1.0, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Cpl=2300.0, P=1E6, Pc=22E6, MW=44.02, Te=7.0) assert_close(h, 4747.749477190532) def test_Chen_Edelstein(): # Odd numbers for the test case from Serth, but not actually compared to # anything. h = Chen_Edelstein(m=0.106, x=0.2, D=0.0212, rhol=567.0, rhog=18.09, mul=156E-6, mug=7.11E-6, kl=0.086, Cpl=2730.0, Hvap=2E5, sigma=0.02, dPsat=1E5, Te=3.0) assert_close(h, 3289.058731974052) def test_Chen_Bennett(): h = Chen_Bennett(m=0.106, x=0.2, D=0.0212, rhol=567.0, rhog=18.09, mul=156E-6, mug=7.11E-6, kl=0.086, Cpl=2730.0, Hvap=2E5, sigma=0.02, dPsat=1E5, Te=3.0) assert_close(h, 4938.275351219369) ================================================ FILE: tests/test_boiling_nucleic.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close, assert_close1d from ht import ( Bier, Cooper, Forster_Zuber, Gorenflo, HEDH_Montinsky, HEDH_Taborek, McNelly, Montinsky, Rohsenow, Serth_HEDH, Stephan_Abdelsalam, Zuber, h_nucleic, h_nucleic_methods, qmax_boiling, qmax_boiling_methods, ) ### Nucleic boiling def test_boiling_nucleic_Rohsenow(): # Checked with 10.30 Problem set 8. h_calc = [Rohsenow(Te=i, Cpl=4180, kl=0.688, mul=2.75E-4, sigma=0.0588, Hvap=2.25E6, rhol=958, rhog=0.597, Csf=0.013, n=1) for i in [4.3, 9.1, 13]] h_values = [2860.6242230238613, 12811.697777642301, 26146.321995188344] assert_close1d(h_calc, h_values) q_test = Rohsenow(Te=4.9, Cpl=4217., kl=0.680, mul=2.79E-4, sigma=0.0589, Hvap=2.257E6, rhol=957.854, rhog=0.595593, Csf=0.011, n=1.26)*4.9 assert_close(18245.91080863059, q_test) h_Te = 1316.2269561541964 h_q = Rohsenow(q=5*h_Te, Cpl=4180, kl=0.688, mul=2.75E-4, sigma=0.0588, Hvap=2.25E6, rhol=958, rhog=0.597) assert_close(h_Te, h_q) with pytest.raises(Exception): Rohsenow(Cpl=4180, kl=0.688, mul=2.75E-4, sigma=0.0588, Hvap=2.25E6, rhol=958, rhog=0.597) def test_boiling_nucleic_McNelly(): # Water matches expectations, ammonia is somewhat distant. Likely just # error in the text's calculation. h_McNelly1 = McNelly(Te=4.3, P=101325, Cpl=4180., kl=0.688, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) h_McNelly2 = McNelly(Te=9.1, P=101325, Cpl=4472., kl=0.502, sigma=0.0325, Hvap=1.37E6, rhol=689., rhog=0.843) assert_close1d([h_McNelly1, h_McNelly2], [533.8056972951352, 6387.3951029225855]) # Check the the solution with q gives the same h h_Te = 533.8056972951352 h_q= McNelly(q=4.3*h_Te, P=101325, Cpl=4180., kl=0.688, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) assert_close(h_Te, h_q) with pytest.raises(Exception): McNelly(P=101325, Cpl=4472., kl=0.502, sigma=0.0325, Hvap=1.37E6, rhol=689., rhog=0.843) def test_boiling_nucleic_Forster_Zuber(): # All examples are for water from [1]_ and match. # 4th example is from [3]_ and matches completely. FZ1 = Forster_Zuber(Te=4.3, dPsat=3906*4.3, Cpl=4180., kl=0.688, mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) FZ2 = Forster_Zuber(Te=9.1, dPsat=3906*9.1, Cpl=4180., kl=0.688, mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) FZ3 = Forster_Zuber(Te=13, dPsat=3906*13, Cpl=4180., kl=0.688, mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) FZ4 = Forster_Zuber(Te=16.2, dPsat=106300., Cpl=2730., kl=0.086, mul=156E-6, sigma=.0082, Hvap=272E3, rhol=567., rhog=18.09) FZ_values = [3519.9239897462644, 7393.507072909551, 10524.54751261952, 5512.279068294656] assert_close1d([FZ1, FZ2, FZ3, FZ4], FZ_values) h_Te = 3519.9239897462644 h_q = Forster_Zuber(q=4.3*h_Te, dPsat=3906*4.3, Cpl=4180., kl=0.688, mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) assert_close(h_Te, h_q) with pytest.raises(Exception): Forster_Zuber(dPsat=3906*4.3, Cpl=4180., kl=0.688, mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597) def test_boiling_nucleic_Montinsky(): # Fourth example is from [4]_ and matches to within the error of the algebraic # manipulation rounding. # First three examples are for water, ammonia, and benzene, from [1]_, and # match to within 20%. W_Te = [Montinsky(Te=i, P=101325., Pc=22048321.0) for i in [4.3, 9.1, 13]] W_Te_values = [1185.0509770292663, 6814.079848742471, 15661.924462897328] assert_close1d(W_Te, W_Te_values) A_Te = [Montinsky(Te=i, P=101325., Pc=112E5) for i in [4.3, 9.1, 13]] A_Te_values = [377.04493949460635, 2168.0200886557072, 4983.118427770712] assert_close1d(A_Te, A_Te_values) B_Te = [Montinsky(Te=i, P=101325., Pc=48.9E5) for i in [4.3, 9.1, 13]] B_Te_values = [96.75040954887533, 556.3178536987874, 1278.6771501657056] assert_close1d(B_Te, B_Te_values) assert_close(Montinsky(310.3E3, 2550E3, 16.2), 2423.2656339862583) h_Te = 1185.0509770292663 h_q = Montinsky(q=4.3*h_Te, P=101325., Pc=22048321.0) assert_close(h_Te, h_q) with pytest.raises(Exception): Montinsky(P=101325., Pc=22048321.0) def test_boiling_nucleic_Stephan_Abdelsalam(): # Stephan Abdelsalam function; allow bad function method Stephan_Abdelsalam(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567.0, rhog=18.09, angle=35.0, correlation="fail") cs = ["general", "water", "hydrocarbon", "cryogenic", "refrigerant"] h_SA = [Stephan_Abdelsalam(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, correlation=i) for i in cs] h_values = [26722.441071108373, 30571.788078886435, 21009.03422203015, 3548.8050360907037, 84657.98595551957] assert_close1d(h_SA, h_values) h_qs = [] for h, c in zip(h_values, cs): h_qs.append(Stephan_Abdelsalam(q=16.2*h, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567.0, rhog=18.09, correlation=c)) assert_close1d(h_qs, h_values) with pytest.raises(Exception): Stephan_Abdelsalam(Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567.0, rhog=18.09) def test_boiling_nucleic_HEDH_Taborek(): h = HEDH_Taborek(Te=16.2, P=310.3E3, Pc=2550E3) assert_close(h, 1397.272486525486) h_q = HEDH_Taborek(P=310.3E3, Pc=2550E3, q=16.2*1397.272486525486) assert_close(h, h_q) with pytest.raises(Exception): HEDH_Taborek(P=310.3E3, Pc=2550E3) def test_boiling_nucleic_Bier(): h_W = [Bier(Te=i, P=101325., Pc=22048321.0) for i in [4.3, 9.1, 13]] h_W_values = [1290.5349471503353, 7420.6159464293305, 17056.026492351128] assert_close1d(h_W, h_W_values) h_B = [Bier(101325., 48.9E5, i) for i in [4.3, 9.1, 13]] h_B_values = [77.81190344679615, 447.42085661013226, 1028.3812069865799] assert_close1d(h_B, h_B_values) h_Te = 1290.5349471503353 h_q = Bier(101325., 22048321.0, q=4.3*h_Te) assert_close(h_Te, h_q) with pytest.raises(Exception): Bier(P=310.3E3, Pc=2550E3) def test_boiling_nucleic_Cooper(): h_W = [Cooper(Te=i, P=101325., Pc=22048321.0, MW=18.02) for i in [4.3, 9.1, 13]] h_W_values = [1558.1435442153575, 7138.700876530947, 14727.09551225091] assert_close1d(h_W, h_W_values) h_B = [Cooper(101325., 48.9E5, 78.11184, i) for i in [4.3, 9.1, 13]] h_B_values = [504.57942247904055, 2311.7520711767947, 4769.130145905329] assert_close1d(h_B, h_B_values) h_Te = 1558.1435442153575 h_q = Cooper(P=101325., Pc=22048321.0, MW=18.02, q=h_Te*4.3) assert_close(h_Te, h_q) with pytest.raises(Exception): Cooper(P=101325., Pc=22048321.0, MW=18.02) def test_Gorenflo(): # water case, boiling at 3 bar q = 2E4 h1 = Gorenflo(P=3E5, Pc=22048320., q=q, CASRN="7732-18-5") assert_close(h1, 3043.344595525422) Te = q/h1 h2 = Gorenflo(P=3E5, Pc=22048320., Te=Te, CASRN="7732-18-5") assert_close(h1, h2) # Ethanol case, boiling at 3 bar q = 2E4 h1 = Gorenflo(P=3E5, Pc=6137000., q=q, CASRN="64-17-5") Te = q/h1 assert_close(h1, 3101.133553596696) h2 = Gorenflo(P=3E5, Pc=6137000., Te=Te, CASRN="64-17-5") assert_close(h1, h2) # Custom h0 case h = Gorenflo(3E5, 6137000., q=2E4, h0=3700.0) assert_close(h, 2607.771397342676) with pytest.raises(Exception): # Case with a CAS number not in the database Gorenflo(3E5, 6137000., q=2E4, CASRN="6400-17-5") with pytest.raises(Exception): # Case with neither Te or q provided: Gorenflo(3E5, 6137000., CASRN="64-17-5") def test_h_nucleic(): h = h_nucleic(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9, Method="Rohsenow") assert_close(h, 1094.0242011089285) h = h_nucleic(Te=4.3, P=101325.0, Cpl=4180., kl=0.688, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597, Method="McNelly") assert_close(h, 533.8056972951352) h = h_nucleic(Te=4.3, dPsat=3906*4.3, Cpl=4180., kl=0.688, mul=0.275E-3, sigma=0.0588, Hvap=2.25E6, rhol=958., rhog=0.597, Method="Forster-Zuber") assert_close(h, 3519.9239897462644) h = h_nucleic(P=101325, Pc=22048321, Te=4.3, Method="Montinsky") assert_close(h, 1185.0509770292663) h = h_nucleic(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, Method="Stephan-Abdelsalam") assert_close(h, 26722.441071108373) h = h_nucleic(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, Method="Stephan-Abdelsalam water", CAS="7732-18-5") assert_close(h, 30571.788078886435) h = h_nucleic(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, Method="Stephan-Abdelsalam cryogenic", CAS="1333-74-0") assert_close(h, 3548.8050360907037) h = h_nucleic(Te=16.2, P=310.3E3, Pc=2550E3, Method="HEDH-Taborek") assert_close(h, 1397.272486525486) h = h_nucleic(P=101325., Pc=22048321.0, Te=4.3, Method="Bier") assert_close(h, 1290.5349471503353) h = h_nucleic(P=101325., Pc=22048321.0, MW=18.02, Te=4.3, Method="Cooper") assert_close(h, 1558.1435442153575) h = h_nucleic(P=3E5, Pc=22048320., q=2E4, CAS="7732-18-5", Method="Gorenflo (1993)") assert_close(h, 3043.344595525422) # Test the kwargs h = h_nucleic(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9, Method="Rohsenow", Csf=0.011, n=1.26) assert_close(h, 3723.655267067467) # methods methods = h_nucleic_methods(P=101325., Pc=22048321.0, MW=18.02, dPsat=3906*4.3, Tsat=437.5, CAS="7732-18-5", rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9) assert len(methods) == 10 methods = h_nucleic_methods(Te=16.2, Tsat=437.5, Cpl=2730., kl=0.086, mul=156E-6, sigma=0.0082, Hvap=272E3, rhol=567, rhog=18.09, CAS="1333-74-0") assert len(methods) == 3 with pytest.raises(Exception): h_nucleic(P=101325., Pc=22048321.0, Te=4.3, Method="BADMETHOD") with pytest.raises(Exception): h_nucleic() # test the default method h = h_nucleic(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9) assert_close(h, 1094.0242011089285) def test_qmax_Zuber(): q_calc_ex = Zuber(sigma=8.2E-3, Hvap=272E3, rhol=567.0, rhog=18.09, K=0.149) assert_close(q_calc_ex, 444307.22304342285) q_max = Zuber(8.2E-3, 272E3, 567, 18.09, 0.18) assert_close(q_max, 536746.9808578263) def test_qmax_Serth_HEDH(): qmax = Serth_HEDH(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567.0, rhog=18.09) assert_close(qmax, 351867.46522901946) # Test K calculated as a function of R qmax = Serth_HEDH(0.00127, 8.2E-3, 272E3, 567, 18.09) assert_close(qmax, 440111.4740326096) def test_HEDH_Montinsky(): assert_close(HEDH_Montinsky(310.3E3, 2550E3), 398405.66545181436) def test_qmax_nucleic(): q = qmax_boiling(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09) assert_close(q, 351867.46522901946) q = qmax_boiling(sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09, Method="Zuber") assert_close(q, 536746.9808578263) q = qmax_boiling(P=310.3E3, Pc=2550E3) assert_close(q, 398405.66545181436) with pytest.raises(Exception): qmax_boiling(D=0.0127) with pytest.raises(Exception): qmax_boiling(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09, Method="BADMETHOD") methods = qmax_boiling_methods(P=310.3E3, Pc=2550E3, D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09) assert len(methods) == 3 ================================================ FILE: tests/test_boiling_plate.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close from ht import h_boiling_Amalfi, h_boiling_Han_Lee_Kim, h_boiling_Huang_Sheer, h_boiling_Lee_Kang_Kim, h_boiling_Yan_Lin def test_h_boiling_Amalfi(): h = h_boiling_Amalfi(m=3E-5, x=.4, Dh=0.00172, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=7.11E-6, sigma=0.02, Hvap=9E5, q=1E5, A_channel_flow=0.0003) assert_close(h, 776.0781179096225) h = h_boiling_Amalfi(m=3E-5, x=.4, Dh=0.0172, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=7.11E-6, sigma=0.02, Hvap=9E5, q=1E5, A_channel_flow=0.0003) assert_close(h, 527.4075513650002) def test_h_boiling_Lee_Kang_Kim(): h = h_boiling_Lee_Kang_Kim(m=3E-5, x=.4, D_eq=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=9E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003) assert_close(h, 1229.6271295086806) h = h_boiling_Lee_Kang_Kim(m=3E-5, x=.1, D_eq=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=9E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003) assert_close(h, 4211.51881493242) def test_h_boiling_Han_Lee_Kim(): h = h_boiling_Han_Lee_Kim(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Hvap=9E5, Cpl=2200.0, q=1E5, A_channel_flow=0.0003, wavelength=3.7E-3, chevron_angle=45) assert_close(h, 675.7322255419421) # Eldeeb said in the Ge1, Ge2 terms it should be b/Dh but the original and # four others agree it's wavelength # Solotych has pi*beta/180 which is the same just written differently # Garcia-Cascales documents the original correctly def test_h_boiling_Huang_Sheer(): h = h_boiling_Huang_Sheer(rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Hvap=9E5, sigma=0.02, Cpl=2200.0, q=1E4, Tsat=279.15) assert_close(h, 4401.055635078054) def test_h_boiling_Yan_Lin(): h = h_boiling_Yan_Lin(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, kl=0.086, Cpl=2200.0, mul=156E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003) assert_close(h, 318.7228565961241) ================================================ FILE: tests/test_condensation.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from math import pi from fluids import Prandtl from fluids.numerics import assert_close, assert_close1d, linspace from ht import Akers_Deans_Crosser, Boyko_Kruzhilin, Cavallini_Smith_Zecchin, Nusselt_laminar, Shah, h_kinetic ### Condensation def test_h_Nusselt_laminar(): h = Nusselt_laminar(370., 350., 7.0, 585., 0.091, 158.9E-6, 776900., 0.1) assert_close(h, 1482.206403453679) h_angle = [Nusselt_laminar(Tsat=370., Tw=350., rhog=7.0, rhol=585., kl=0.091, mul=158.9E-6, Hvap=776900., L=0.1, angle=float(i)) for i in linspace(0, 90, 8)] h_angle_values = [0.0, 1018.0084987903685, 1202.9623322809389, 1317.0917328126477, 1393.7567182107628, 1444.0629692910647, 1472.8272516024929, 1482.206403453679] assert_close1d(h_angle, h_angle_values) def test_h_Boyko_Kruzhilin(): h_xs = [Boyko_Kruzhilin(m=0.35, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=i) for i in linspace(0,1,11)] h_xs_values = [1190.3309510899785, 3776.3883678904836, 5206.2779830848758, 6320.5657791981021, 7265.9323628276288, 8101.7278671405438, 8859.0188940546595, 9556.4866502932564, 10206.402815353165, 10817.34162173243, 11395.573750069829] assert_close1d(h_xs, h_xs_values) def test_Akers_Deans_Crosser(): h = Akers_Deans_Crosser(m=0.35, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=0.85) assert_close(h, 7117.24177265201) h = Akers_Deans_Crosser(m=0.01, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=0.85) assert_close(h, 737.5654803081094) def test_h_kinetic(): h = h_kinetic(300.0, 1E5, 18.02, 2441674.0) assert_close(h, 30788845.562480535, rtol=1e-5) def test_Cavallini_Smith_Zecchin(): assert_close(Cavallini_Smith_Zecchin(m=1.0, x=0.4, D=.3, rhol=800.0, rhog=2.5, mul=1E-5, mug=1E-3, kl=0.6, Cpl=2300.0), 5578.218369177804) def test_Shah(): assert_close(Shah(m=1.0, x=0.4, D=.3, rhol=800.0, mul=1E-5, kl=0.6, Cpl=2300.0, P=1E6, Pc=2E7), 2561.2593415479214) # In Shaw's second paper, they used the following definition. However, it # is just rearanged differently. It was coded to verify this, and is left # in case further sources list it in different forms. def Shah3(m, x, D, rhol, mul, kl, Cpl, P, Pc): Pr = P/Pc G = m/(pi/4*D**2) Prl = Prandtl(Cp=Cpl, k=kl, mu=mul) Rel = G*D/mul hL = kl/D*(0.023*Rel**0.8*Prl**0.4) hl = hL*(1-x)**0.8 Z = (1/x -1)**0.8*Pr**0.4 h_TP = hl*(1 + 3.8/Z**0.95) return h_TP assert_close(Shah(m=1, x=0.4, D=.3, rhol=800, mul=1E-5, kl=0.6, Cpl=2300, P=1E6, Pc=2E7), 2561.2593415479214) # The following is in the review of Balcular (2011), and incorrect. # Pr = P/Pc # G = m/(pi/4*D**2) # Prl = Prandtl(Cp=Cpl, k=kl, mu=mul) # Rel = G*D*(1-x)/mul # hl = kl/D*(0.023*(Rel/(1-x))**0.8*Prl**0.4) # hsf = hl*(1-x)**0.8 # Co = (1/x-1)**0.8*(rhog/rhol)**0.5 # return hsf*1.8/Co**0.8 ================================================ FILE: tests/test_conduction.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close, assert_close1d from ht import ( ASHRAE, ASHRAE_k, Cp_material, R_cylinder, R_to_k, R_value_to_k, S_isothermal_pipe_eccentric_to_isothermal_pipe, S_isothermal_pipe_normal_to_plane, S_isothermal_pipe_to_isothermal_pipe, S_isothermal_pipe_to_plane, S_isothermal_pipe_to_two_planes, S_isothermal_sphere_to_plane, building_materials, cylindrical_heat_transfer, k_material, k_to_R, k_to_R_value, k_to_thermal_resistivity, materials_dict, nearest_material, refractory_VDI_Cp, refractory_VDI_k, rho_material, thermal_resistivity_to_k, ) ### Conduction # Nothing is necessary, it's all in doctests def test_conduction(): assert_close(R_to_k(R=0.05, t=0.025), 0.5) assert_close(k_to_R(k=0.5, t=0.025), 0.05) assert_close(k_to_thermal_resistivity(0.25), 4.0) assert_close(thermal_resistivity_to_k(4.), 0.25) Rs = [R_value_to_k(0.12), R_value_to_k(0.71, SI=False)] assert_close1d(Rs, [0.2116666666666667, 0.20313787163983468]) assert_close(R_value_to_k(1., SI=False)/R_value_to_k(1.), 5.678263341113488) values = k_to_R_value(R_value_to_k(0.12)), k_to_R_value(R_value_to_k(0.71, SI=False), SI=False) assert_close1d(values, [0.11999999999999998, 0.7099999999999999]) assert_close(R_cylinder(0.9, 1., 20., 10.), 8.38432343682705e-05) ### Shape Factors assert_close(S_isothermal_sphere_to_plane(1., 100.), 6.298932638776527) assert_close(S_isothermal_pipe_to_plane(1., 100., 3.), 3.146071454894645) assert_close(S_isothermal_pipe_normal_to_plane(1., 100.), 104.86893910124888) assert_close(S_isothermal_pipe_to_isothermal_pipe(.1, .2, 1., 1.), 1.188711034982268) assert_close(S_isothermal_pipe_to_two_planes(.1, 5., 1.), 1.2963749299921428) assert_close(S_isothermal_pipe_eccentric_to_isothermal_pipe(.1, .4, .05, 10.), 47.709841915608976) def test_cylindrical_heat_transfer(): data = cylindrical_heat_transfer(Ti=453.15, To=301.15, hi=1e12, ho=22.697193, Di=0.0779272, ts=[0.0054864, .05], ks=[56.045, 0.0598535265]) expect = {"Q": 73.12000884069367, "Rs": [0.00022201030738405449, 1.189361782070256], "Ts": [453.15, 453.1226455779877, 306.578530147744], "UA": 0.48105268974140575, "U_inner": 1.9649599487726137, "U_outer": 0.8106078714663484, "q": 123.21239646288495} for k, v in expect.items(): if type(v) is float: assert_close(v, data[k]) else: assert_close1d(v, data[k]) def test_insulation(): rho_tot = sum([i[0] for i in building_materials.values()]) k_tot = sum([i[1] for i in building_materials.values()]) Cp_tot = sum([i[2] for i in building_materials.values()]) ans = [213240.48, 1132.7733999999994, 164486] assert_close1d([rho_tot, k_tot, Cp_tot], ans) assert_close(0.036, ASHRAE_k(ID="Mineral fiber")) k_VDIs = [refractory_VDI_k("Fused silica", i) for i in [None, 200, 1000, 1500]] assert_close1d(k_VDIs, [1.44, 1.44, 1.58074, 1.73]) Cp_VDIs = [refractory_VDI_Cp("Fused silica", i) for i in [None, 200, 1000, 1500]] assert_close1d(Cp_VDIs, [917.0, 917.0, 956.78225, 982.0]) k = k_material("Mineral fiber") assert_close(k, 0.036) k = k_material("stainless steel") assert_close(k, 17.0) rho = rho_material("Mineral fiber") assert_close(rho, 30.0) rho = rho_material("stainless steel") assert_close(rho, 7900.0) rho = rho_material("Board, Asbestos/cement") assert_close(rho, 1900.0) Cp = Cp_material("Mineral fiber") assert_close(Cp, 840.0) Cp = Cp_material("stainless steel") assert_close(Cp, 460.0) with pytest.raises(Exception): rho_material("Clay tile, hollow, 1 cell deep") with pytest.raises(Exception): Cp_material("Siding, Aluminum, steel, or vinyl, over sheathing foil-backed") @pytest.mark.slow def test_insulation_fuzz(): assert_close(sum([ASHRAE_k(ID) for ID in ASHRAE]), 102.33813464784427) k_tot = sum([k_material(ID) for ID in materials_dict]) assert_close(k_tot, 1436.251534647845) rho = sum([rho_material(mat) for mat in materials_dict if (materials_dict[mat] == 1 or materials_dict[mat]==3 or ASHRAE[mat][0])]) assert_close(rho, 473135.98) Cp = sum([Cp_material(mat) for mat in materials_dict if ( materials_dict[mat] == 1 or materials_dict[mat]==3 or ASHRAE[mat][1])]) assert_close(Cp, 353115.0) # fuzzy matching is slow assert nearest_material("stainless steel") == "Metals, stainless steel" assert nearest_material("stainless wood") == "Metals, stainless steel" assert nearest_material("asdfasdfasdfasdfasdfasdfads ") == "Expanded polystyrene, molded beads" assert nearest_material("stainless steel", complete=True) == "Metals, stainless steel" ================================================ FILE: tests/test_conv_external.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close, assert_close1d, logspace from ht import ( Nu_cylinder_Churchill_Bernstein, Nu_cylinder_Fand, Nu_cylinder_McAdams, Nu_cylinder_Perkins_Leppert_1962, Nu_cylinder_Perkins_Leppert_1964, Nu_cylinder_Sanitjai_Goldstein, Nu_cylinder_Whitaker, Nu_cylinder_Zukauskas, Nu_external_cylinder, Nu_external_cylinder_methods, Nu_external_horizontal_plate, Nu_external_horizontal_plate_methods, Nu_horizontal_plate_laminar_Baehr, Nu_horizontal_plate_laminar_Churchill_Ozoe, Nu_horizontal_plate_turbulent_Kreith, Nu_horizontal_plate_turbulent_Schlichting, ) from ht.conv_external import conv_horizontal_plate_laminar_methods, conv_horizontal_plate_turbulent_methods ### Conv external def test_Nu_cylinder_Zukauskas(): Nu = Nu_cylinder_Zukauskas(7992, 0.707, 0.69) assert_close(Nu, 50.523612661934386) Nus_allRe = [Nu_cylinder_Zukauskas(Re, 0.707, 0.69) for Re in logspace(0, 6, 8)] Nus_allRe_values = [0.66372630070423799, 1.4616593536687801, 3.2481853039940831, 8.7138930573143227, 26.244842388228189, 85.768869004450067, 280.29503021904566, 1065.9610995854582] assert_close1d(Nus_allRe, Nus_allRe_values) Nu_highPr = Nu_cylinder_Zukauskas(7992, 42.) assert_close(Nu_highPr, 219.24837219760443) def test_Nu_cylinder_Churchill_Bernstein(): Nu = Nu_cylinder_Churchill_Bernstein(6071.0, 0.7) assert_close(Nu, 40.63708594124974) def test_Nu_cylinder_Sanitjai_Goldstein(): Nu = Nu_cylinder_Sanitjai_Goldstein(6071.0, 0.7) assert_close(Nu, 40.38327083519522) def test_Nu_cylinder_Fand(): Nu = Nu_cylinder_Fand(6071.0, 0.7) assert_close(Nu, 45.19984325481126) def test_Nu_cylinder_McAdams(): Nu = Nu_cylinder_McAdams(6071.0, 0.7) assert_close(Nu, 46.98179235867934) def test_Nu_cylinder_Whitaker(): Nu = Nu_cylinder_Whitaker(6071.0, 0.7) assert_close(Nu, 45.94527461589126) Nu = Nu_cylinder_Whitaker(6071.0, 0.7, 1E-3, 1.2E-3) assert_close(Nu, 43.89808146760356) def test_Nu_cylinder_Perkins_Leppert_1962(): Nu = Nu_cylinder_Perkins_Leppert_1962(6071.0, 0.7) assert_close(Nu, 49.97164291175499) Nu = Nu_cylinder_Perkins_Leppert_1962(6071.0, 0.7, 1E-3, 1.2E-3) assert_close(Nu, 47.74504603464674) def test_Nu_cylinder_Perkins_Leppert_1964(): Nu = Nu_cylinder_Perkins_Leppert_1964(6071.0, 0.7) assert_close(Nu, 53.61767038619986) Nu = Nu_cylinder_Perkins_Leppert_1964(6071.0, 0.7, 1E-3, 1.2E-3) assert_close(Nu, 51.22861670528418) def test_Nu_external_cylinder(): Nu = Nu_external_cylinder(6071.0, 0.7) assert_close(Nu, 40.38327083519522) Nu = Nu_external_cylinder(6071.0, 0.7, Method="Zukauskas") assert_close(Nu, 42.4244052368103) methods = Nu_external_cylinder_methods(6071.0, 0.7, Prw=.8, mu=1e-4, muw=2e-4) with pytest.raises(Exception): Nu_external_cylinder(6071.0, 0.7, Method="BADMETHOD") Nu = Nu_external_cylinder(6071.0, 0.7, Prw=.8, Method="Zukauskas") assert_close(Nu, 41.0315360788783) Nu = Nu_external_cylinder(6071.0, 0.7, mu=1e-4, muw=2e-4, Method="Whitaker") assert_close(Nu, 38.63521672235044) def test_Nu_horizontal_plate_laminar_Baehr(): Prs = [1e-4, 1e-1, 1, 100] Nu_expects = [3.5670492006699317, 97.46187137010543, 209.9752366351804, 995.1679034477633] for Pr, Nu_expect in zip(Prs, Nu_expects): assert_close(Nu_horizontal_plate_laminar_Baehr(1e5, Pr), Nu_expect) def test_Nu_horizontal_plate_laminar_Churchill_Ozoe(): assert_close(Nu_horizontal_plate_laminar_Churchill_Ozoe(1e5, .7), 183.08600782591418) def test_Nu_horizontal_plate_turbulent_Schlichting(): assert_close(Nu_horizontal_plate_turbulent_Schlichting(1e5, 0.7), 309.620048541267) def test_Nu_horizontal_plate_turbulent_Kreith(): Nu = Nu_horizontal_plate_turbulent_Kreith(1.03e6, 0.71) assert_close(Nu, 2074.8740070411122) def test_Nu_external_horizontal_plate(): # default function - turbulent assert_close(Nu_external_horizontal_plate(5e6, .7), Nu_external_horizontal_plate(5e6, .7, turbulent_method="Schlichting")) # specific function - turbulent - vs specify turbulent method assert_close(Nu_horizontal_plate_turbulent_Kreith(5e6, .7), Nu_external_horizontal_plate(5e6, .7, turbulent_method="Kreith")) # specific function - turbulent - vs specify method assert_close(Nu_horizontal_plate_turbulent_Kreith(5e6, .7), Nu_external_horizontal_plate(5e6, .7, Method="Kreith")) # default function - laminar assert_close(Nu_external_horizontal_plate(5e3, .7), Nu_external_horizontal_plate(5e3, .7, laminar_method="Baehr")) # specific function - laminar - vs specify laminar method assert_close(Nu_horizontal_plate_laminar_Baehr(5e3, .7), Nu_external_horizontal_plate(5e3, .7, laminar_method="Baehr")) # specific function - laminar - vs specify method assert_close(Nu_horizontal_plate_laminar_Churchill_Ozoe(5e6, .7), Nu_external_horizontal_plate(5e6, .7, Method="Churchill Ozoe")) # Swith the transition region to be higher assert_close(Nu_horizontal_plate_laminar_Baehr(5e6, .7), Nu_external_horizontal_plate(5e6, .7, Re_transition=1e7)) # Check the AvailableMethods assert (set(Nu_external_horizontal_plate_methods(1e5, .7, L=1.0, x=0.5, check_ranges=True)) == set(conv_horizontal_plate_laminar_methods.keys()) ) assert (set(Nu_external_horizontal_plate_methods(1e7, .7)) == set(conv_horizontal_plate_turbulent_methods.keys()) ) ================================================ FILE: tests/test_conv_free_enclosed.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close, assert_close1d from fluids.numerics import numpy as np from ht import ( Nu_Nusselt_Rayleigh_Hollands, Nu_Nusselt_Rayleigh_Probert, Nu_Nusselt_vertical_Thess, Nu_vertical_helical_coil_Ali, Rac_Nusselt_Rayleigh, Rac_Nusselt_Rayleigh_disk, ) from ht.conv_free_enclosed import Nu_Nusselt_Rayleigh_Holling_Herwig try: from scipy.interpolate import UnivariateSpline, bisplrep except: pass def test_Nu_Nusselt_Rayleigh_Holling_Herwig(): Ras = [10.0**n for n in range(5, 16)] Nus_expect = [4.566, 8.123, 15.689, 31.526, 64.668, 134.135, 279.957, 586.404, 1230.938, 2587.421, 5443.761] Nus_calc = [round(Nu_Nusselt_Rayleigh_Holling_Herwig(1., Gr), 3) for Gr in Ras] assert_close1d(Nus_expect, Nus_calc) assert 1 == Nu_Nusselt_Rayleigh_Holling_Herwig(1., 100., buoyancy=True) assert 1 == Nu_Nusselt_Rayleigh_Holling_Herwig(1., 100., buoyancy=False) def test_Nu_Nusselt_Rayleigh_Probert(): Nu = Nu_Nusselt_Rayleigh_Probert(5.54, 3.21e8, buoyancy=True) assert_close(Nu, 111.46181048289132) # Test the boundary Nu = Nu_Nusselt_Rayleigh_Probert(1., 2.19999999999999e4, buoyancy=True) assert_close(Nu, 2.5331972341122833) Nu = Nu_Nusselt_Rayleigh_Probert(1., 2.2e4, buoyancy=True) assert_close(Nu, 2.577876184202956) assert 1 == Nu_Nusselt_Rayleigh_Probert(1., 100., buoyancy=True) assert 1 == Nu_Nusselt_Rayleigh_Probert(1., 100., buoyancy=False) def test_Rac_Nusselt_Rayleigh(): for Rac_expect, insulation in zip([3011480.513694726, 9802960.0], [True, False]): for L in (8.0, 9.0, 100.0): W_L = .125 Rac = Rac_Nusselt_Rayleigh(1., L, W_L*L, insulation) assert_close(Rac, Rac_expect) def test_Rac_Nusselt_Rayleigh_disk(): assert_close(Rac_Nusselt_Rayleigh_disk(4., 1., True), 51800) assert_close(Rac_Nusselt_Rayleigh_disk(H=1, D=.4, insulated=True), 51800) assert_close(Rac_Nusselt_Rayleigh_disk(H=1, D=.4, insulated=False), 151200) for r in (4,10, 100): assert_close(Rac_Nusselt_Rayleigh_disk(r, 1., False), 151200) for D in (5.9999999999, 6, 7, 50): assert_close(Rac_Nusselt_Rayleigh_disk(H=1., D=D, insulated=False), 1708.) assert_close(Rac_Nusselt_Rayleigh_disk(H=1., D=D, insulated=True), 1708.) def test_Nu_Nusselt_vertical_Thess(): Nu = Nu_Nusselt_vertical_Thess(.7, 3.21e6) assert_close(Nu, 6.112587569602785) Nu = Nu_Nusselt_vertical_Thess(.7, 3.21e6, L=10, H=1) assert_close(Nu, 28.79328626041646) Nu = Nu_Nusselt_vertical_Thess(.7, 2e7) assert_close(Nu, 11.179395785432854) def test_Nu_Nusselt_Rayleigh_Hollands(): assert_close(Nu_Nusselt_Rayleigh_Hollands(5.54, 3.21e8, buoyancy=True), 69.02668649510164) assert_close(Nu_Nusselt_Rayleigh_Hollands(.7, 3.21e6, buoyancy=True, Rac=Rac_Nusselt_Rayleigh(H=1, L=2, W=.2, insulated=False)), 4.666249131876477) assert_close(Nu_Nusselt_Rayleigh_Hollands(.7, 3.21e6, buoyancy=True, Rac=Rac_Nusselt_Rayleigh(H=1, L=1, W=1, insulated=False)), 8.786362614129537) def test_Rac_Nusselt_Rayleigh_fit_uninsulated(): from ht.conv_free_enclosed import Racs_uninstulated_Catton, ratios_uninsulated_Catton, tck_uninstulated_Catton all_zs = [] all_xs = [] all_ys = [] for ratio1, Rac_row in zip(ratios_uninsulated_Catton, Racs_uninstulated_Catton): for Rac, ratio2 in zip(Rac_row, ratios_uninsulated_Catton): all_zs.append(Rac) all_xs.append(ratio1) all_ys.append(ratio2) tck = bisplrep(all_xs, all_ys, np.log(all_zs), kx=3, ky=3, s=0) for i in range(len(tck)-2): assert_close1d(tck[i], tck_uninstulated_Catton[i], rtol=1e-5) # for i, Racs in enumerate(Racs_uninstulated_Catton): # plt.semilogy(ratios_uninsulated_Catton, Racs, label=str(ratios_uninsulated_Catton[i])) # fit = np.exp(bisplev(ratios_uninsulated_Catton[i], ratios_uninsulated_Catton, tck)) # plt.semilogy(ratios_uninsulated_Catton, fit, 'o') # # plt.legend() # plt.show() def test_Rac_Nusselt_Rayleigh_fit_insulated(): from ht.conv_free_enclosed import Racs_instulated_Catton, ratios_insulated_Catton all_zs = [] all_xs = [] all_ys = [] for ratio1, Rac_row in zip(ratios_insulated_Catton, Racs_instulated_Catton): for Rac, ratio2 in zip(Rac_row, ratios_insulated_Catton): if Rac is not None: all_zs.append(Rac) all_xs.append(ratio1) all_ys.append(ratio2) # This fit is not great, might be worth refitting at some point # Do not compare anything. tck = bisplrep(all_xs, all_ys, np.log(all_zs), kx=1, ky=2) # for i in range(len(tck)): # assert_close1d(tck[i], tck_insulated_Catton[i], rtol=1e-5) # for i, Racs in enumerate(Racs_instulated_Catton): # plt.semilogy(ratios_insulated_Catton, Racs, '-', label=str(ratios_insulated_Catton[i])) # fit = np.exp(bisplev(ratios_insulated_Catton[i], ratios_insulated_Catton, tck)) # plt.semilogy(ratios_insulated_Catton, fit, 'o') # # plt.legend() # plt.show() def test_Rac_Nusselt_Rayleigh_disk_fits(): from fluids.optional import pychebfun from ht.conv_free_enclosed import insulated_disk_coeffs, uninsulated_disk_coeffs ratios = [0.4, 0.5, 0.7, 1.0, 1.4, 2.0, 3.0, 4.0, 6] Ras_uninsulated = [151200, 66600, 21300, 8010, 4350, 2540, 2010, 1880, 1708] Ras_insulated = [51800, 23800, 8420, 3770, 2650, 2260, 1900, 1830, 1708] uninsulated = UnivariateSpline(ratios, 1/np.log(Ras_uninsulated), k=1, s=0) insulated = UnivariateSpline(ratios, 1/np.log(Ras_insulated), k=1, s=0) N = 8 insulated_fun = pychebfun.chebfun(insulated, domain=[ratios[0], ratios[-1]], N=N) uninsulated_fun = pychebfun.chebfun(uninsulated, domain=[ratios[0], ratios[-1]], N=N) insulated_coeffs = pychebfun.chebfun_to_poly(insulated_fun) uninsulated_coeffs = pychebfun.chebfun_to_poly(uninsulated_fun) assert_close1d(insulated_coeffs, insulated_disk_coeffs) assert_close1d(uninsulated_coeffs, uninsulated_disk_coeffs) # more_ratios = np.logspace(np.log10(ratios[0]), np.log10(ratios[-1]), 1000) # plt.semilogy(ratios, Ras_insulated) # plt.semilogy(ratios, Ras_uninsulated) # # plt.semilogy(more_ratios, np.exp(1/insulated_fun(np.array(more_ratios))), 'x') # plt.semilogy(more_ratios, np.exp(1/uninsulated_fun(np.array(more_ratios))), 'o') # plt.show() def test_Nu_vertical_helical_coil_Ali(): Nu = Nu_vertical_helical_coil_Ali(4.4, 1E11) assert_close(Nu, 1808.5774997297106) ================================================ FILE: tests/test_conv_free_immersed.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close, assert_close1d, assert_close2d, logspace from ht import ( Nu_coil_Xin_Ebadian, Nu_free_horizontal_plate, Nu_free_horizontal_plate_methods, Nu_free_vertical_plate, Nu_free_vertical_plate_methods, Nu_horizontal_cylinder, Nu_horizontal_cylinder_Churchill_Chu, Nu_horizontal_cylinder_Kuehn_Goldstein, Nu_horizontal_cylinder_methods, Nu_horizontal_cylinder_Morgan, Nu_horizontal_plate_McAdams, Nu_horizontal_plate_Rohsenow, Nu_horizontal_plate_VDI, Nu_sphere_Churchill, Nu_vertical_cylinder, Nu_vertical_cylinder_Al_Arabi_Khamis, Nu_vertical_cylinder_Carne_Morgan, Nu_vertical_cylinder_Eigenson_Morgan, Nu_vertical_cylinder_Griffiths_Davis_Morgan, Nu_vertical_cylinder_Hanesian_Kalish_Morgan, Nu_vertical_cylinder_Jakob_Linke_Morgan, Nu_vertical_cylinder_Kreith_Eckert, Nu_vertical_cylinder_McAdams_Weiss_Saunders, Nu_vertical_cylinder_methods, Nu_vertical_cylinder_Popiel_Churchill, Nu_vertical_cylinder_Touloukian_Morgan, Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan, Nu_vertical_plate_Churchill, ) ### Free convection immersed def test_Nu_free_vertical_plate(): Nu = Nu_free_vertical_plate(0.69, 2.63E9) assert_close(147.16185223770603, Nu) Nu = Nu_free_vertical_plate(0.69, 2.63E9, H=1.0, W=1.0) assert_close(147.16185223770603, Nu) methods = Nu_free_vertical_plate_methods(0.69, 2.63E9, H=1.0, W=1.0, check_ranges=True) assert methods[0] == "Churchill" assert len(methods) == 1 with pytest.raises(Exception): Nu_free_vertical_plate(0.69, 2.63E9, Method="BADMETHOD") def test_Nu_horizontal_plate_VDI(): Nu = Nu_horizontal_plate_VDI(5.54, 3.21e8, buoyancy=True) assert_close(Nu, 203.89681224927565) Nu = Nu_horizontal_plate_VDI(5.54, 3.21e8, buoyancy=False) assert_close(Nu, 39.16864971535617) Nu = Nu_horizontal_plate_VDI(5.54, 3.21e3, buoyancy=True) assert_close(Nu, 5.810590581487902) def test_Nu_horizontal_plate_Rohsenow(): Nu = Nu_horizontal_plate_Rohsenow(5.54, 3.21e8, buoyancy=True) assert_close(Nu, 175.91054716322836) Nu = Nu_horizontal_plate_Rohsenow(5.54, 3.21e8, buoyancy=False) assert_close(Nu, 35.95799244863986) def test_Nu_free_horizontal_plate(): Nu = Nu_free_horizontal_plate(5.54, 3.21e8, L=1.0, W=1.0, buoyancy=True) assert_close(Nu, 203.89681224927565) Nu = Nu_free_horizontal_plate(5.54, 3.21e8, buoyancy=True, Method="McAdams") assert_close(Nu, 181.73121274384457) assert Nu_free_horizontal_plate_methods(5.54, 3.21e8, buoyancy=True, L=1.0, W=1.0, check_ranges=True) == ["VDI", "McAdams", "Rohsenow"] def test_Nu_horizontal_plate_McAdams(): Nu = Nu_horizontal_plate_McAdams(5.54, 3.21e8, buoyancy=True) assert_close(Nu, 181.73121274384457) Nu = Nu_horizontal_plate_McAdams(5.54, 3.21e8, buoyancy=False) assert_close(Nu, 55.44564799362829) Nu = Nu_horizontal_plate_McAdams(.01, 3.21e8, buoyancy=True) assert_close(Nu, 22.857041558492334) Nu = Nu_horizontal_plate_McAdams(.01, 3.21e8, buoyancy=False) assert_close(Nu, 11.428520779246167) def test_Nu_vertical_plate_Churchill(): Nu = Nu_vertical_plate_Churchill(0.69, 2.63E9) assert_close(Nu, 147.16185223770603) def test_Nu_sphere_Churchill(): Nu_Res = [Nu_sphere_Churchill(.7, i) for i in logspace(0, 10, 11)] Nu_Res_values = [2.415066377224484, 2.7381040025746382, 3.3125553308635283, 4.3340933312726548, 6.1507272232235417, 9.3821675084055443, 15.145453144794978, 25.670869440317578, 47.271761310748289, 96.479305628419823, 204.74310854292045] assert_close1d(Nu_Res, Nu_Res_values) def test_Nu_vertical_cylinder_Griffiths_Davis_Morgan(): Nu_all = [[Nu_vertical_cylinder_Griffiths_Davis_Morgan(i, 1E9, j) for i in (0.999999, 1.000001)] for j in (True, False, None)] Nu_all_values = [[127.7046167347578, 127.7047079158867], [119.14469068641654, 119.14475025877677], [119.14469068641654, 127.7047079158867]] assert_close2d(Nu_all, Nu_all_values) def test_Nu_vertical_cylinder_Jakob_Linke_Morgan(): Nu_all = [[Nu_vertical_cylinder_Jakob_Linke_Morgan(i, 1E8, j) for i in (0.999999, 1.000001)] for j in (True, False, None)] Nu_all_values = [[59.87647599476619, 59.87651591243016], [55.499986124994805, 55.5000138749948], [55.499986124994805, 59.87651591243016]] assert_close2d(Nu_all, Nu_all_values) def test_Nu_vertical_cylinder_Carne_Morgan(): Nu_all = [[Nu_vertical_cylinder_Carne_Morgan(i, 2E8, j) for i in (0.999999, 1.000001)] for j in (True, False, None)] Nu_all_values = [[216.88764905616722, 216.88781389084312], [225.77302655456344, 225.77315298749372], [225.77302655456344, 216.88781389084312]] assert_close2d(Nu_all, Nu_all_values) def test_Nu_vertical_cylinder_Eigenson_Morgan(): Grs = [1.42E9, 1.43E9, 2.4E10, 2.5E10] Nus_expect = [85.22908647061865, 85.47896057139417, 252.35445465640387, 256.64456353698154] Nus = [Nu_vertical_cylinder_Eigenson_Morgan(0.7, Gr) for Gr in Grs] assert_close1d(Nus, Nus_expect) def test_Nu_vertical_cylinder_Touloukian_Morgan(): Nus = [Nu_vertical_cylinder_Touloukian_Morgan(.7, i) for i in (5.7E10, 5.8E10)] assert_close1d(Nus, [324.47395664562873, 223.80067132541936]) assert_close(249.72879961097854, Nu_vertical_cylinder_Touloukian_Morgan(.7, 2E10, False)) def test_Nu_vertical_cylinder_McAdams_Weiss_Saunders(): Nus = [Nu_vertical_cylinder_McAdams_Weiss_Saunders(.7, i) for i in [1.42E9, 1.43E9]] assert_close1d(Nus, [104.76075212013542, 130.04331889690818]) Nu = Nu_vertical_cylinder_McAdams_Weiss_Saunders(.7, 2E10, False) assert_close(Nu, 202.9476470667732) def test_Nu_vertical_cylinder_Kreith_Eckert(): Nus = [Nu_vertical_cylinder_Kreith_Eckert(.7, i) for i in [1.42E9, 1.43E9]] assert_close1d(Nus, [98.54613123165282, 83.63593679160734]) Nu = Nu_vertical_cylinder_Kreith_Eckert(.7, 2E10, False) assert_close(Nu, 190.90837986789683) def test_Nu_vertical_cylinder_Hanesian_Kalish_Morgan(): Nu = Nu_vertical_cylinder_Hanesian_Kalish_Morgan(.7, 1E7) assert_close(Nu, 18.014150492696604) def test_Nu_vertical_cylinder_Al_Arabi_Khamis(): Nus = [Nu_vertical_cylinder_Al_Arabi_Khamis(.71, i, 10.0, 1.0) for i in [3.6E9, 3.7E9]] assert_close1d(Nus, [185.32314790756703, 183.89407579255627]) def test_Nu_vertical_cylinder_Popiel_Churchill(): Nu = Nu_vertical_cylinder_Popiel_Churchill(0.7, 1E10, 2.5, 1.0) assert_close(Nu, 228.8979005514989) def test_Nu_vertical_cylinder(): Nu = Nu_vertical_cylinder(0.72, 1E7) assert_close(Nu, 30.562236756513943) Nu = Nu_vertical_cylinder(0.72, 1E7, L=1., D=.1) assert_close(Nu, 36.82833881084525) with pytest.raises(Exception): Nu_vertical_cylinder(0.72, 1E7, Method="BADMETHOD") l = Nu_vertical_cylinder_methods(0.72, 1E7, L=1.0, D=.1) assert len(l) == 11 def test_Nu_horizontal_cylinder_Churchill_Chu(): Nu = Nu_horizontal_cylinder_Churchill_Chu(0.69, 2.63E9) assert_close(Nu, 139.13493970073597) def test_Nu_horizontal_cylinder_Kuehn_Goldstein(): Nu = Nu_horizontal_cylinder_Kuehn_Goldstein(0.69, 2.63E9) assert_close(Nu, 122.99323525628186) def test_Nu_horizontal_cylinder_Morgan(): Nus = [Nu_horizontal_cylinder_Morgan(.9, i) for i in (1E-2, 1E2, 1E4, 1E7, 1E10)] Nus_expect = [0.5136293570857408, 1.9853087795801612, 4.707783879945983, 26.290682760247975, 258.0315247153301] assert_close1d(Nus, Nus_expect) def test_Nu_horizontal_cylinder(): Nu = Nu_horizontal_cylinder(Pr=0.72, Gr=1E7) assert_close(Nu, 24.864192615468973) l = Nu_horizontal_cylinder_methods(0.72, 1E7) assert len(l) == 3 with pytest.raises(Exception): Nu_horizontal_cylinder(Pr=0.72, Gr=1E7, Method="BADMETHOD") def test_Nu_coil_Xin_Ebadian(): Nu = Nu_coil_Xin_Ebadian(0.7, 2E4, horizontal=False) assert_close(Nu, 4.755689726250451) Nu = Nu_coil_Xin_Ebadian(0.7, 2E4, horizontal=True) assert_close(Nu, 5.2148597687849785) def test_Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan(): Nu = Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan(4.4, 1E11) assert_close(Nu, 720.6211067718227) ================================================ FILE: tests/test_conv_internal.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close, assert_close1d, linspace from ht.conv_internal import ( Morimoto_Hotta, Nu_conv_internal, Nu_conv_internal_methods, Nu_laminar_rectangular_Shan_London, conv_tube_methods, helical_turbulent_Nu_Mori_Nakayama, helical_turbulent_Nu_Schmidt, helical_turbulent_Nu_Xin_Ebadian, laminar_entry_Baehr_Stephan, laminar_entry_Seider_Tate, laminar_entry_thermal_Hausen, laminar_Q_const, laminar_T_const, turbulent_Bhatti_Shah, turbulent_Churchill_Zajic, turbulent_Colburn, turbulent_Dipprey_Sabersky, turbulent_Dittus_Boelter, turbulent_Drexel_McAdams, turbulent_entry_Hausen, turbulent_ESDU, turbulent_Friend_Metzner, turbulent_Gnielinski, turbulent_Gnielinski_smooth_1, turbulent_Gnielinski_smooth_2, turbulent_Gowen_Smith, turbulent_Kawase_De, turbulent_Kawase_Ulbrecht, turbulent_Martinelli, turbulent_Nunner, turbulent_Petukhov_Kirillov_Popov, turbulent_Prandtl, turbulent_Sandall, turbulent_Sieder_Tate, turbulent_von_Karman, turbulent_Webb, ) ### conv_internal def test_Nu_const(): assert_close(laminar_T_const(), 3.66) assert_close(laminar_Q_const(), 48/11.) def test_laminar_entry_region(): Nu = laminar_entry_thermal_Hausen(100000.0, 1.1, 5.0, .5) assert_close(Nu, 39.01352358988535) Nu = laminar_entry_Seider_Tate(Re=100000, Pr=1.1, L=5, Di=.5) assert_close(Nu, 41.366029684589265) Nu_wall = laminar_entry_Seider_Tate(100000, 1.1, 5, .5, 1E-3, 1.2E-3) assert_close(Nu_wall, 40.32352264095969) Nu = laminar_entry_Baehr_Stephan(100000, 1.1, 5, .5) assert_close(Nu, 72.65402046550976) def test_turbulent_complicated(): Nu1 = turbulent_Dittus_Boelter(1E5, 1.2, True, False) Nu2 = turbulent_Dittus_Boelter(Re=1E5, Pr=1.2, heating=False, revised=False) Nu3 = turbulent_Dittus_Boelter(Re=1E5, Pr=1.2, heating=False) Nu4 = turbulent_Dittus_Boelter(Re=1E5, Pr=1.2) Nu_values = [261.3838629346147, 279.89829163640354, 242.9305927410295, 247.40036409449127] assert_close1d([Nu1, Nu2, Nu3, Nu4], Nu_values) Nu1 = turbulent_Sieder_Tate(Re=1E5, Pr=1.2) Nu2 = turbulent_Sieder_Tate(1E5, 1.2, 0.01, 0.067) assert_close1d([Nu1, Nu2], [286.9178136793052, 219.84016455766044]) Nus = [turbulent_entry_Hausen(1E5, 1.2, 0.154, i) for i in linspace(1e-3,1,11)] Nus_values = [6464.503822124652, 505.67127136455525, 399.6147653094695, 356.6182206114823, 332.39191624636305, 316.53483318707475, 305.21220965431286, 296.6521831991236, 289.91358493027764, 284.4463173972796, 279.90553997822707] assert_close1d(Nus, Nus_values) def test_turbulent_simple(): Nu = turbulent_Colburn(1E5, 1.2) assert_close(Nu, 244.41147091200068) Nu = turbulent_Drexel_McAdams(1E5, 0.6) assert_close(Nu, 171.19055301724387) Nu = turbulent_von_Karman(1E5, 1.2, 0.0185) assert_close(Nu, 255.7243541243272) Nu = turbulent_Prandtl(1E5, 1.2, 0.0185) assert_close(Nu, 256.073339689557) Nu = turbulent_Friend_Metzner(1E5, 100., 0.0185) assert_close(Nu, 1738.3356262055322) Nu = turbulent_Petukhov_Kirillov_Popov(1E5, 1.2, 0.0185) assert_close(Nu, 250.11935088905105) Nu = turbulent_Webb(1E5, 1.2, 0.0185) assert_close(Nu, 239.10130376815872) Nu = turbulent_Sandall(1E5, 1.2, 0.0185) assert_close(Nu, 229.0514352970239) Nu = turbulent_Gnielinski(1E5, 1.2, 0.0185) assert_close(Nu, 254.62682749359632) Nu = turbulent_Gnielinski_smooth_1(1E5, 1.2) assert_close(Nu, 227.88800494373442) Nu = turbulent_Gnielinski_smooth_2(1E5, 7.) assert_close(Nu, 577.7692524513449) Nu = turbulent_Churchill_Zajic(1E5, 1.2, 0.0185) assert_close(Nu, 260.5564907817961) Nu = turbulent_ESDU(1E5, 1.2) assert_close(Nu, 232.3017143430645) def test_turbulent_rough(): Nu = turbulent_Martinelli(1E5, 100., 0.0185) assert_close(Nu, 887.1710686396347) Nu = turbulent_Nunner(1E5, 0.7, 0.0185, 0.005) assert_close(Nu, 101.15841010919947) Nu = turbulent_Dipprey_Sabersky(1E5, 1.2, 0.0185, 1E-3) assert_close(Nu, 288.33365198566656) Nu = turbulent_Gowen_Smith(1E5, 1.2, 0.0185) assert_close(Nu, 131.72530453824106) Nu = turbulent_Kawase_Ulbrecht(1E5, 1.2, 0.0185) assert_close(Nu, 389.6262247333975) Nu = turbulent_Kawase_De(1E5, 1.2, 0.0185) assert_close(Nu, 296.5019733271324) Nu = turbulent_Bhatti_Shah(1E5, 1.2, 0.0185, 1E-3) assert_close(Nu, 302.7037617414273) # TODO meta function Nu internal def test_Morimoto_Hotta(): Nu = Morimoto_Hotta(1E5, 5.7, .05, .5) assert_close(Nu, 634.4879473869859) def test_helical_turbulent_Nu_Mori_Nakayama(): Nu = helical_turbulent_Nu_Mori_Nakayama(2E5, 0.7, 0.01, .2) assert_close(Nu, 496.2522480663327) # High Pr Nu = helical_turbulent_Nu_Mori_Nakayama(2E5, 4.0, 0.01, .2) assert_close(Nu, 889.3060078437253) # Bad behavior! # 1 sun power output per m^2 per K assert 4E24 < helical_turbulent_Nu_Mori_Nakayama(2E6, 0.7, 1.0, 1E80) # .[3]_ specified that the high-Pr formula is calculated using Dean number, # but the actual article says it is not. We use the 2.5 power specified # in the original. def test_helical_turbulent_Nu_Schmidt(): Nu = helical_turbulent_Nu_Schmidt(2E5, 0.7, 0.01, .2) assert_close(Nu, 466.2569996832083) Nus = [helical_turbulent_Nu_Schmidt(i, 0.7, 0.01, .2) for i in [2.2E4, 2.2E4+1E-9]] assert_close1d(Nus, [80.1111786843, 79.75161984693375]) def test_helical_turbulent_Nu_Xin_Ebadian(): Nu = helical_turbulent_Nu_Xin_Ebadian(2E5, 0.7, 0.01, .2) assert_close(Nu, 474.11413424344755) # No bad behavior # Checked with the original def test_Nu_laminar_rectangular_Shan_London(): Nu = Nu_laminar_rectangular_Shan_London(.7) assert_close(Nu, 3.751762675455) def test_Nu_conv_internal_methods(): from fluids.units import func_args for (func, args) in conv_tube_methods.values(): assert tuple(list(func_args(func))[0:len(args)]) == args def test_Nu_conv_internal(): Nu = Nu_conv_internal(1E2, .7) assert_close(Nu, laminar_T_const()) Nu = Nu_conv_internal(1E2, .7, Method="Laminar - constant Q") assert_close(Nu, laminar_Q_const()) Nu = Nu_conv_internal(1E2, .7, x=.01, Di=.1) assert_close(Nu, 14.91799128769779) # test the other laminar entrylength methods Nu = Nu_conv_internal(1E2, .7, x=.01, Di=.1, Method="Hausen laminar thermal entry") assert_close(Nu, 16.51501443241237) Nu = Nu_conv_internal(1E2, .7, x=.01, Di=.1, Method="Seider-Tate laminar thermal entry") assert_close(Nu, 21.054212255270848) # martinili Nu = Nu_conv_internal(1E5, .02, eD=0.0) assert_close(Nu, 8.246171632616187) Nu = Nu_conv_internal(1E5, .7, x=.01, Di=.1) assert_close(Nu, 978.1729258857774) Nu = Nu_conv_internal(1E5, .7) assert_close(Nu, 183.71057902604906) other_methods = ["Churchill-Zajic", "Petukhov-Kirillov-Popov", "Gnielinski", "Bhatti-Shah", "Dipprey-Sabersky", "Sandall", "Webb", "Friend-Metzner", "Prandtl", "von-Karman", "Gowen-Smith", "Kawase-Ulbrecht", "Kawase-De", "Nunner", "Dittus-Boelter", "Sieder-Tate", "Drexel-McAdams", "Colburn", "ESDU", "Gnielinski smooth low Pr","Gnielinski smooth high Pr"] expected = [103.65851760127596, 96.66083769419261, 95.7206648591076, 124.96666518189072, 124.96666518189072, 126.8559349821517, 89.04183860378171, 82.62190521404274, 96.39509181385534, 97.64409839390211, 63.69345925482798, 218.78659693866075, 169.9758751276217, 113.72592148878971, 199.41923780765848, 239.73408047050233, 182.078434520036, 204.21792040079825, 177.52639370276017, 183.6911292257849, 230.01408232621412] for method, expect in zip(other_methods, expected): Nu = Nu_conv_internal(1E5, .7, fd=.01, Method=method) assert_close(Nu, expect) with pytest.raises(Exception): Nu_conv_internal(1E5, .7, Method="NOTAMETHOD") l = Nu_conv_internal_methods(1E5, .7) assert len(l) == 21 test_Nu_conv_internal() ================================================ FILE: tests/test_conv_jacket.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close from ht import Lehrer, Stein_Schmidt def test_conv_jacket(): # actual example h = Lehrer(2.5, 0.6, 0.65, 0.6, 0.025, 995.7, 4178.1, 0.615, 798E-6, 355E-6, dT=20.) assert_close(h, 2922.128124761829) # no wall correction h = Lehrer(2.5, 0.6, 0.65, 0.6, 0.025, 995.7, 4178.1, 0.615, 798E-6, dT=20.) assert_close(h, 2608.8602693706853) # with isobaric expansion, all cases h = Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype="radial", isobaric_expansion=0.000303) assert_close(h, 3269.4389632666557) h = Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype="radial", inletlocation="top", isobaric_expansion=0.000303) assert_close(h, 2566.1198726589996) h = Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=-20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype="radial", isobaric_expansion=0.000303) assert_close(h, 3269.4389632666557) h = Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=-20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype="radial", inletlocation="bottom", isobaric_expansion=0.000303) assert_close(h, 2566.1198726589996) ### Stein Schmidt h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 995.7, 4178.1, 0.615, 798E-6, 355E-6, 971.8) assert_close(h, 5695.204169808863) h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 995.7, 4178.1, 0.615, 798E-6, 355E-6, 971.8, inlettype="radial") assert_close(h, 1217.1449686341773) h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 995.7, 4178.1, 0.615, 798E-6, 355E-6, 971.8, inletlocation="top") assert_close(h, 5675.841635061595) h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 995.7, 4178.1, 0.615, 798E-6, 355E-6, 971.8, inletlocation="bottom") assert_close(h, 5695.2041698088633) h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 971.8, 4178.1, 0.615, 798E-6, 355E-6, 995.7, inletlocation="bottom") assert_close(h, 5694.9722658952096) h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 971.8, 4178.1, 0.615, 798E-6, 355E-6, 995.7, inletlocation="top") assert_close(h, 5676.0744960391157) h = Stein_Schmidt(2.5, 0.6, 0.65, 0.6, 0.025, 971.8, 4178.1, 0.615, 798E-6, 355E-6) assert_close(h, 5685.532991556428) h = Stein_Schmidt(.1, 0.6, 0.65, 0.6, 0.025, 971.8, 4178.1, 0.615, 798E-6) assert_close(h, 151.78819106776797) ================================================ FILE: tests/test_conv_packed_bed.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close from ht import Nu_Achenbach, Nu_KTA, Nu_packed_bed_Gnielinski, Nu_Wakao_Kagei def test_Nu_packed_bed_Gnielinski(): Nu = Nu_packed_bed_Gnielinski(8E-4, 0.4, 1.0, 1E3, 1E-3, 0.7) assert_close(Nu, 61.37823202546954) # fa=2 test Nu = Nu_packed_bed_Gnielinski(8E-4, 0.4, 1.0, 1E3, 1E-3, 0.7, 2.0) assert_close(Nu, 64.60866528996795) def test_Nu_Wakao_Kagei(): Nu = Nu_Wakao_Kagei(2000., 0.7) assert_close(Nu, 95.40641328041248) def test_Nu_Achenbach(): Nu = Nu_Achenbach(2000., 0.7, 0.4) assert_close(Nu, 117.70343608599121) def test_Nu_KTA(): Nu = Nu_KTA(2000., 0.7, 0.4) assert_close(Nu, 102.08516480718129) ================================================ FILE: tests/test_conv_plate.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2018 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close from ht.conv_plate import Nu_plate_Khan_Khan, Nu_plate_Kumar, Nu_plate_Martin, Nu_plate_Muley_Manglik @pytest.mark.numpy def test_Nu_plate_Kumar(): from fluids.friction import Kumar_beta_list from numpy.testing import assert_allclose Kumar_Nu_Res = [[10.0], [10.0, 100.0], [20.0, 300.0], [20.0, 400.0], [20.0, 500.0]] Nu = Nu_plate_Kumar(2000.0, 0.7, 30.0) assert_close(Nu, 47.757818892853955) Nu = Nu_plate_Kumar(Re=2000.0, Pr=0.7, chevron_angle=30.0, mu=1E-3, mu_wall=8E-4) assert_close(Nu, 49.604284135097544) all_ans_expected = [[[1.3741604132237337, 1.5167183720237427], [1.3741604132237337, 1.4917469901578877]], [[1.3741604132237337, 1.4917469901578877, 5.550501072445418, 5.686809480248301], [1.1640875871334992, 1.2445337163511674, 3.9101709259523125, 3.9566649343960067]], [[1.4929588988864342, 1.563892674590831, 7.514446806331191, 7.535921750318442], [1.3046449654318206, 1.3616258463940976, 5.549244219363172, 5.568849176342506]], [[1.3046449654318206, 1.3616258463940976, 6.464254426666383, 6.491074633865849], [1.3046449654318206, 1.360776035122095, 5.9841120030888915, 5.999181017513207]], [[1.3046449654318206, 1.360776035122095, 6.696608679807539, 6.712512276614001], [1.3046449654318206, 1.360776035122095, 6.696608679807539, 6.712512276614001]]] all_ans = [] for i, beta_main in enumerate(Kumar_beta_list): beta_ans = [] for beta in (beta_main-1, beta_main+1): Re_ans = [] for Re_main in Kumar_Nu_Res[i]: for Re in [Re_main-1, Re_main+1]: ans = Nu_plate_Kumar(Re, 0.7, beta) Re_ans.append(ans) beta_ans.append(Re_ans) all_ans.append(beta_ans) for row1, row2 in zip(all_ans_expected, all_ans): assert_allclose(row1, row2) # assert_close(row1, row2) def test_Nu_plate_Martin(): Nu = Nu_plate_Martin(2000.0, .7, 45.0) assert_close(Nu, 30.427601053757712) Nu = Nu_plate_Martin(2000.0, .7, 45.0, variant="VDI") assert_close(Nu, 30.418672) def test_Nu_plate_Muley_Manglik(): Nu = Nu_plate_Muley_Manglik(Re=2000.0, Pr=.7, chevron_angle=45.0, plate_enlargement_factor=1.18) assert_close(Nu, 36.49087100602062) def test_Nu_plate_Khan_Khan(): # The author presented three correlations; all of them are well matched by # the fourth correlation. beta max is not the largest angle in *your* # PHE, but of the ones they tested. Nu = Nu_plate_Khan_Khan(Re=1000.0, Pr=4.5, chevron_angle=30.0) assert_close(Nu, 38.40883639103741 ) ================================================ FILE: tests/test_conv_supercritical.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017, 2018, 2019, Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close, assert_close1d from ht import ( Nu_Bishop, Nu_Bringer_Smith, Nu_Gorban, Nu_Griem, Nu_Gupta, Nu_Jackson, Nu_Kitoh, Nu_Krasnoshchekov, Nu_Krasnoshchekov_Protopopov, Nu_McAdams, Nu_Mokry, Nu_Ornatsky, Nu_Petukhov, Nu_Shitsman, Nu_Swenson, Nu_Xu, Nu_Yamagata, Nu_Zhu, ) def test_Nu_McAdams(): Nu = Nu_McAdams(1E5, 1.2) assert_close(Nu, 261.3838629346147) def test_Nu_Shitsman(): Nus = [Nu_Shitsman(1E5, 1.2, 1.6), Nu_Shitsman(1E5, 1.6, 1.2)] assert_close1d(Nus, [266.1171311047253]*2) def test_Nu_Griem(): Nu = Nu_Griem(1E5, 1.2) assert_close(Nu, 275.4818576600527) hs = [225.8951232812432, 240.77114359488607, 275.4818576600527] hs_calc = [Nu_Griem(1E5, 1.2, H) for H in [1.52E6, 1.6E6, 1.8E6]] assert_close1d(hs, hs_calc) def test_Nu_Jackson(): Nu = Nu_Jackson(1E5, 1.2) assert_close(Nu, 252.37231572974918) Nu_calc = [Nu_Jackson(1E5, 1.2, rho_w=125.8, rho_b=249.0233, Cp_avg=2080.845, Cp_b=2048.621, T_b=650, T_w=700, T_pc=T) for T in [750, 675, 600]] Nu_exp = [206.91175020307264, 206.93567238866916, 206.97455183928113] assert_close1d(Nu_calc, Nu_exp) def test_Nu_Gupta(): Nu = Nu_Gupta(1E5, 1.2) assert_close(Nu, 189.78727690467736) Nu = Nu_Gupta(1E5, 1.2, 330, 290., 8e-4, 9e-4) assert_close(Nu, 186.20135477175126) def test_Nu_Swenson(): Nu = Nu_Swenson(1E5, 1.2) assert_close(Nu, 211.51968418167206) Nu = Nu_Swenson(1E5, 1.2, 330, 290.) assert_close(Nu, 217.92827034803668) def test_Nu_Xu(): Nu = Nu_Xu(1E5, 1.2) assert_close(Nu, 293.9572513612297) Nu = Nu_Xu(1E5, 1.2, 330, 290., 8e-4, 9e-4) assert_close(Nu, 289.133054256742) def test_Nu_Mokry(): Nu = Nu_Mokry(1E5, 1.2) assert_close(Nu, 228.8178008454556) Nu = Nu_Mokry(1E5, 1.2, 330, 290.) assert_close(Nu, 246.1156319156992) def test_Nu_Bringer_Smith(): Nu = Nu_Bringer_Smith(1E5, 1.2) assert_close(Nu, 208.17631753279107) def test_Nu_Ornatsky(): Nu = Nu_Ornatsky(1E5, 1.2, 1.5, 330, 290.) assert_close(Nu, 276.63531150832307) Nu = Nu_Ornatsky(1E5, 1.2, 1.5) assert_close(Nu, 266.1171311047253) def test_Nu_Gorban(): Nu = Nu_Gorban(1E5, 1.2) assert_close(Nu, 182.5367282733999) def test_Nu_Zhu(): Nu = Nu_Zhu(1E5, 1.2, 330, 290., 0.63, 0.69) assert_close(Nu, 240.1459854494706) Nu = Nu_Zhu(1E5, 1.2) assert_close(Nu, 241.2087720246979) def test_Nu_Bishop(): Nu = Nu_Bishop(1E5, 1.2, 330.0, 290., .01, 1.2) assert_close(Nu, 265.3620050072533) Nu = Nu_Bishop(1E5, 1.2) assert_close(Nu, 246.09835634820243) def test_Nu_Yamagata(): Nu = Nu_Yamagata(1E5, 1.2) assert_close(Nu, 283.9383689412967) Nu_calc = [Nu_Yamagata(1E5, 1.2, 1.5, Cp_avg=2080.845, Cp_b=2048.621, T_b=650, T_w=700, T_pc=T) for T in [750.0, 675.0, 600.0]] Nu_exp = [283.9383689412967, 187.02304885276828, 292.3473428004679] assert_close1d(Nu_calc, Nu_exp) def test_Nu_Kitoh(): Nu = Nu_Kitoh(1E5, 1.2) assert_close(Nu, 302.5006546293724) Nu_calc = [Nu_Kitoh(1E5, 1.2, H, 1500, 5E6) for H in [1.4E6, 2E6, 3.5E6]] Nu_exp = [331.80234139591306, 174.8417387874232, 308.40146536866945] assert_close1d(Nu_calc, Nu_exp) def test_Nu_Krasnoshchekov_Protopopov(): Nu = Nu_Krasnoshchekov_Protopopov(1E5, 1.2, 330, 290., 0.62, 0.52, 8e-4, 9e-4) assert_close(Nu, 228.85296737400222) def test_Nu_Petukhov(): Nu = Nu_Petukhov(1E5, 1.2, 330.0, 290., 8e-4, 9e-4) assert_close(Nu, 254.8258598466738) def test_Nu_Krasnoshchekov(): Nu_calc = [Nu_Krasnoshchekov(1E5, 1.2, rho_w=125.8, rho_b=249.0233, Cp_avg=2080.845, Cp_b=2048.621, T_b=650, T_w=700, T_pc=T) for T in [750.0, 675.0]] Nu_exp = [192.52819597784372, 192.54822916468785] assert_close1d(Nu_calc, Nu_exp) Nu_3 = Nu_Krasnoshchekov(1E5, 1.2, rho_w=125.8, rho_b=249.0233, Cp_avg=2080.845, Cp_b=2048.621, T_b=400.0, T_w=200.0, T_pc=400.0) assert_close(Nu_3, 192.2579518680533) Nu = Nu_Krasnoshchekov(1E5, 1.2) assert_close(Nu, 234.82855185610364) ================================================ FILE: tests/test_conv_tube_bank.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import numpy as np from fluids.numerics import assert_close, assert_close1d, assert_close2d, bisplev, linspace from scipy.interpolate import RectBivariateSpline, UnivariateSpline, interp1d, splrep from ht import ( ESDU_tube_angle_correction, ESDU_tube_row_correction, Nu_ESDU_73031, Nu_Grimison_tube_bank, Nu_HEDH_tube_bank, Nu_Zukauskas_Bejan, Zukauskas_tube_row_correction, baffle_correction_Bell, baffle_leakage_Bell, bundle_bypassing_Bell, dP_Kern, dP_Zukauskas, laminar_correction_Bell, unequal_baffle_spacing_Bell, ) from ht.conv_tube_bank import dP_inline_correction_tck, dP_inline_f_tck, dP_staggered_correction_tck, dP_staggered_f_tck def test_Nu_Grimison_tube_bank_tcks(): from ht.conv_tube_bank import Grimison_C1_aligned, Grimison_C1_aligned_tck, Grimison_SL_aligned, Grimison_ST_aligned Grimison_C1_aligned_interp = RectBivariateSpline(Grimison_ST_aligned, Grimison_SL_aligned, np.array(Grimison_C1_aligned)) tck_recalc = Grimison_C1_aligned_interp.tck [assert_close1d(i, j) for i, j in zip(Grimison_C1_aligned_tck, tck_recalc)] from ht.conv_tube_bank import Grimison_m_aligned, Grimison_m_aligned_tck Grimison_m_aligned_interp = RectBivariateSpline(Grimison_ST_aligned, Grimison_SL_aligned, np.array(Grimison_m_aligned)) tck_recalc = Grimison_m_aligned_interp.tck [assert_close1d(i, j) for i, j in zip(Grimison_m_aligned_tck, tck_recalc)] def test_Nu_Grimison_tube_bank(): Nu = Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=.05, pitch_parallel=.05, Do=.025) assert_close(Nu, 79.07883866010096) Nu = Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=.07, pitch_parallel=.05, Do=.025) assert_close(Nu, 79.92721078571385) Nu = Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=7, pitch_normal=.05, pitch_parallel=.05, Do=.025) assert_close(Nu, 77.49726188689894) Nu = Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=7, pitch_normal=.07, pitch_parallel=.05, Do=.025) assert_close(Nu, 78.32866656999958) # Test the negative input args = dict(Re=10263.37, Pr=.708, tube_rows=-1, pitch_normal=.07, pitch_parallel=.05, Do=.025) Nu_neg = Nu_Grimison_tube_bank(**args) args["tube_rows"] = 1 Nu_pos = Nu_Grimison_tube_bank(**args) assert_close(Nu_neg, Nu_pos) # Check all data - for changing interpolations Nu_bulk = [[Nu_Grimison_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=j, pitch_parallel=i, Do=.025) for i in [.025, .04, .05, .75, .1, .15, .2]] for j in [.025, .04, .05, .75, .1, .15, .2]] Nu_bulk_expect = [[83.05244932418451, 152.02626127499462, 92.67853984384722, 80.45909971688272, 80.45909971688272, 80.45909971688272, 80.45909971688272], [81.37409021240403, 75.87989409125535, 88.19403137832364, 90.10492890754932, 90.10492890754932, 90.10492890754932, 90.10492890754932], [80.154658166616, 79.27931854213506, 79.07883866010096, 88.31182349500988, 88.31182349500988, 88.31182349500988, 88.31182349500988], [73.98350370839236, 76.51020564051443, 78.3597838488104, 79.12612063682283, 86.25920529135, 86.25920529135, 86.25920529135], [73.98350370839236, 76.51020564051443, 78.3597838488104, 86.25920529135, 79.12612063682283, 86.25920529135, 86.25920529135], [73.98350370839236, 76.51020564051443, 78.3597838488104, 86.25920529135, 86.25920529135, 79.12612063682283, 86.25920529135], [73.98350370839236, 76.51020564051443, 78.3597838488104, 86.25920529135, 86.25920529135, 86.25920529135, 79.12612063682283]] assert_close2d(Nu_bulk, Nu_bulk_expect) def test_Gimison_coeffs_regeneration(): # These fits are bad, don't check them # SciPy has warnings for both of them pass # tck = bisplrep(Grimson_ST_staggered, Grimson_SL_staggered, Grimson_C1_staggered, kx=1, ky=1, task=0, s=0) # [assert_close1d(i, j) for i, j in zip(tck, tck_Grimson_C1_staggered)] # # tck = bisplrep(Grimson_ST_staggered, Grimson_SL_staggered, Grimson_m_staggered, kx=1, ky=1, task=0, s=0) # [assert_close1d(i, j) for i, j in zip(tck, tck_Grimson_m_staggered)] # def test_ESDU_tube_row_correction(): F2 = ESDU_tube_row_correction(4, staggered=True) assert_close(F2, 0.8984, rtol=1e-4) F2 = ESDU_tube_row_correction(6, staggered=False) assert_close(F2, 0.9551, rtol=1E-4) # Test all of the inputs work all_values = [ESDU_tube_row_correction(i, staggered=j) for i in range(12) for j in (True, False)] def test_ESDU_tube_row_correction_refit(): # Re-fit the data from ht.conv_tube_bank import ESDU_73031_F2_inline, ESDU_73031_F2_staggered ## Commands used to obtain the fitted data: ESDU_nrs = [3., 3.0189, 3.04129, 3.04891, 3.06251, 3.06481, 3.08274, 3.09166, 3.10623, 3.11518, 3.12645, 3.13538, 3.14668, 3.16219, 3.1669, 3.18571, 3.1871, 3.20732, 3.20924, 3.23084, 3.23273, 3.25107, 3.25625, 3.27126, 3.27977, 3.29808, 3.30329, 3.32816, 3.33011, 3.35363, 3.37712, 3.40394, 3.42746, 3.45428, 3.47777, 3.48683, 3.50461, 3.51691, 3.5281, 3.54369, 3.55492, 3.56721, 3.57841, 3.59077, 3.6019, 3.61426, 3.62872, 3.63778, 3.6949, 3.80655, 3.80918, 3.93332, 3.99431, 4.10792, 4.20204, 4.36618, 4.47351, 4.56706, 4.60082, 4.7014, 4.78854, 4.808, 4.90913, 4.97601, 5.05637, 5.0589, 5.1132, 5.11574, 5.14332, 5.14582, 5.1701, 5.17593, 5.19692, 5.20601, 5.22704, 5.2361, 5.25709, 5.26621, 5.28711, 5.293, 5.32308, 5.35319, 5.38324, 5.39435, 5.41336, 5.42088, 5.42116, 5.44347, 5.47355, 5.50364, 5.53372, 5.56159, 5.56383, 5.58834, 5.59392, 5.61846, 5.624, 5.64857, 5.65408, 5.67865, 5.68416, 5.70877, 5.71424, 5.73885, 5.74436, 5.76896, 5.77777, 5.79905, 5.80782, 5.82913, 5.8379, 5.85592, 5.86798, 5.88936, 5.89807, 5.92815, 5.95823, 5.98828, 6.01836, 6.02978, 6.04845, 6.06313, 6.08186, 6.09321, 6.11191, 6.1233, 6.14202, 6.15338, 6.17207, 6.18346, 6.20215, 6.21354, 6.23556, 6.24363, 6.26565, 6.27704, 6.2957, 6.30709, 6.32908, 6.33717, 6.35916, 6.36725, 6.38924, 6.41929, 6.42076, 6.45267, 6.48275, 6.5128, 6.52099, 6.54289, 6.55431, 6.57626, 6.58439, 6.60635, 6.6364, 6.66978, 6.69983, 6.71797, 6.72991, 6.74805, 6.76329, 6.78143, 6.79334, 6.81148, 6.82672, 6.84156, 6.8568, 6.87491, 6.89014, 6.90499, 6.9202, 6.93837, 6.95028, 6.96842, 6.98362, 6.99847, 7.01371, 7.03185, 7.04376, 7.0619, 7.07714, 7.09195, 7.11048, 7.14056, 7.17062, 7.17544, 7.19874, 7.20399, 7.23212, 7.23404, 7.26409, 7.2655, 7.29555, 7.29744, 7.3289, 7.33082, 7.35895, 7.36087, 7.389, 7.39425, 7.42234, 7.42427, 7.45239, 7.45432, 7.48574, 7.4877, 7.51579, 7.51772, 7.54914, 7.55109, 7.57919, 7.58114, 7.61253, 7.61449, 7.64454, 7.67792, 7.70794, 7.74132, 7.74599, 7.77134, 7.80471, 7.83473, 7.86811, 7.87284, 7.89813, 7.90273, 7.93148, 7.93275, 7.96153, 7.9661, 7.99487, 7.99612, 8.02493, 8.02947, 8.05827, 8.06281, 8.08829, 8.09283, 8.12164, 8.12618, 8.15502, 8.1562, 8.18503, 8.18951, 8.21838, 8.22286, 8.2484, 8.25288, 8.28178, 8.28619, 8.31509, 8.34514, 8.37849, 8.40851, 8.40956, 8.43958, 8.44186, 8.46957, 8.47187, 8.50291, 8.50522, 8.53524, 8.53623, 8.56625, 8.56859, 8.5986, 8.59956, 8.63192, 8.63288, 8.66286, 8.66527, 8.69529, 8.69621, 8.72863, 8.72953, 8.75865, 8.75951, 8.792, 8.79283, 8.82202, 8.82614, 8.84944, 8.85533, 8.88868, 8.91866, 8.94938, 8.95201, 8.98273, 8.98536, 9.01269, 9.01538, 9.04869, 9.08201, 9.18589, 9.22535, 9.25866, 9.28865, 9.32196, 9.35531, 9.38862, 9.41861, 9.45193, 9.48524, 9.51852, 9.55184, 9.58183, 9.61511, 9.64842, 9.68174, 9.71502, 9.74834, 9.77832, 9.81161, 9.84492, 9.8782, 9.91152, 9.94483, 9.97482 ] ESDU_F_in = [0.847863, 0.847938, 0.848157, 0.848231, 0.849657, 0.8498, 0.850916, 0.851272, 0.851854, 0.852412, 0.853114, 0.853669, 0.854374, 0.85534, 0.855633, 0.856658, 0.856734, 0.857993, 0.858083, 0.859091, 0.859208, 0.86035, 0.860633, 0.861451, 0.861747, 0.862385, 0.862491, 0.862997, 0.863133, 0.864769, 0.866404, 0.86827, 0.869906, 0.871772, 0.873407, 0.874037, 0.874399, 0.874649, 0.874973, 0.875424, 0.875948, 0.876521, 0.877119, 0.877778, 0.878223, 0.878716, 0.87939, 0.879813, 0.882477, 0.88784, 0.887966, 0.892807, 0.895431, 0.900319, 0.903854, 0.910018, 0.914049, 0.917017, 0.918089, 0.921673, 0.92463, 0.925231, 0.928356, 0.929894, 0.932218, 0.932273, 0.933445, 0.93351, 0.934217, 0.934289, 0.934992, 0.935195, 0.935926, 0.936159, 0.936698, 0.936834, 0.93715, 0.937239, 0.937443, 0.937639, 0.938643, 0.939648, 0.940651, 0.941021, 0.940661, 0.940518, 0.941956, 0.942443, 0.943101, 0.943758, 0.944416, 0.945025, 0.945076, 0.94564, 0.945783, 0.946412, 0.946554, 0.947183, 0.947295, 0.947795, 0.947937, 0.948567, 0.948679, 0.949179, 0.94932, 0.949951, 0.95013, 0.950563, 0.950742, 0.951175, 0.951429, 0.95195, 0.952227, 0.952719, 0.952909, 0.953567, 0.954224, 0.954881, 0.955538, 0.955788, 0.95595, 0.956078, 0.956459, 0.95669, 0.95707, 0.957302, 0.957683, 0.957914, 0.958294, 0.958526, 0.958906, 0.959138, 0.959586, 0.95975, 0.960152, 0.960359, 0.96064, 0.960812, 0.961259, 0.961424, 0.961871, 0.962036, 0.962541, 0.963232, 0.963266, 0.963848, 0.964396, 0.964944, 0.965093, 0.965178, 0.965223, 0.96567, 0.965835, 0.966183, 0.966659, 0.967188, 0.967664, 0.967952, 0.968195, 0.968564, 0.968769, 0.969013, 0.969193, 0.969466, 0.969776, 0.970078, 0.97021, 0.970367, 0.970678, 0.97098, 0.971184, 0.971429, 0.971608, 0.971881, 0.97211, 0.972334, 0.972539, 0.972783, 0.972962, 0.973235, 0.973465, 0.973688, 0.97399, 0.974481, 0.974972, 0.975051, 0.97503, 0.975101, 0.975479, 0.975505, 0.97591, 0.975929, 0.976381, 0.976397, 0.976671, 0.9767, 0.977123, 0.977152, 0.977575, 0.977621, 0.977865, 0.977894, 0.978317, 0.978334, 0.978607, 0.978636, 0.979059, 0.979076, 0.979349, 0.979378, 0.979801, 0.979818, 0.980091, 0.980113, 0.980446, 0.980815, 0.981148, 0.981517, 0.981569, 0.981929, 0.982404, 0.982831, 0.983305, 0.983373, 0.98308, 0.983026, 0.983307, 0.983319, 0.983569, 0.983609, 0.983889, 0.983901, 0.984151, 0.984191, 0.984441, 0.984481, 0.984729, 0.984773, 0.985023, 0.985063, 0.985344, 0.985355, 0.985468, 0.985485, 0.985736, 0.985775, 0.986024, 0.986068, 0.98618, 0.986198, 0.986434, 0.986679, 0.986952, 0.987197, 0.987206, 0.987498, 0.987508, 0.987631, 0.987651, 0.987921, 0.98793, 0.988047, 0.988051, 0.988343, 0.988352, 0.98847, 0.988473, 0.988599, 0.988603, 0.988736, 0.988757, 0.989018, 0.989026, 0.989152, 0.989156, 0.989285, 0.989289, 0.989415, 0.989419, 0.989532, 0.989548, 0.989528, 0.989551, 0.989681, 0.989798, 0.989917, 0.98994, 0.990207, 0.990205, 0.99018, 0.990188, 0.990281, 0.990374, 0.990664, 0.990775, 0.990868, 0.990952, 0.991045, 0.991138, 0.991231, 0.991315, 0.991408, 0.991501, 0.991594, 0.991687, 0.991771, 0.991864, 0.991957, 0.99205, 0.992143, 0.992236, 0.99232, 0.992413, 0.992506, 0.992599, 0.992692, 0.992785, 0.992869 ] ESDU_F_st = [0.859287, 0.859485, 0.860059, 0.860415, 0.861049, 0.861156, 0.861887, 0.86225, 0.86293, 0.863347, 0.863962, 0.864448, 0.864841, 0.865382, 0.865602, 0.866479, 0.866544, 0.867487, 0.867576, 0.868439, 0.868514, 0.869369, 0.869611, 0.870311, 0.870708, 0.871562, 0.871805, 0.872672, 0.87274, 0.873837, 0.874774, 0.875709, 0.876806, 0.877741, 0.878678, 0.879047, 0.879772, 0.880263, 0.88071, 0.881253, 0.881644, 0.882135, 0.882582, 0.883075, 0.883519, 0.88395, 0.884454, 0.884812, 0.88707, 0.891483, 0.891577, 0.896007, 0.898184, 0.901839, 0.904867, 0.909992, 0.913054, 0.915723, 0.91661, 0.919254, 0.921545, 0.922057, 0.924542, 0.926185, 0.92816, 0.928222, 0.929395, 0.929449, 0.93001, 0.930061, 0.930684, 0.930833, 0.93126, 0.931445, 0.931873, 0.932057, 0.932595, 0.932829, 0.933433, 0.933604, 0.934216, 0.934988, 0.93544, 0.935724, 0.936212, 0.936404, 0.936412, 0.936983, 0.937596, 0.938208, 0.93882, 0.939534, 0.939591, 0.94009, 0.940204, 0.940703, 0.940816, 0.941316, 0.941428, 0.941928, 0.94204, 0.94254, 0.942652, 0.943282, 0.943424, 0.943872, 0.944033, 0.944353, 0.944485, 0.944919, 0.945097, 0.945464, 0.945709, 0.946144, 0.946321, 0.946933, 0.947545, 0.947998, 0.94861, 0.948842, 0.949222, 0.94949, 0.949831, 0.950002, 0.950283, 0.950575, 0.951055, 0.951226, 0.951507, 0.951739, 0.952119, 0.952327, 0.952729, 0.952893, 0.953341, 0.953512, 0.953793, 0.953946, 0.954242, 0.954407, 0.954854, 0.955019, 0.955466, 0.955919, 0.955939, 0.956368, 0.95698, 0.957433, 0.957599, 0.958045, 0.958198, 0.958494, 0.958659, 0.959106, 0.959558, 0.960008, 0.96046, 0.960829, 0.961072, 0.961317, 0.961522, 0.961795, 0.961974, 0.962218, 0.962423, 0.962725, 0.963035, 0.963193, 0.963325, 0.963549, 0.963777, 0.964147, 0.96439, 0.964547, 0.964679, 0.964981, 0.965291, 0.965564, 0.965744, 0.965988, 0.966193, 0.966322, 0.966483, 0.967095, 0.967547, 0.967612, 0.967926, 0.967996, 0.96842, 0.968449, 0.968901, 0.968913, 0.969174, 0.969191, 0.969614, 0.96964, 0.970064, 0.970093, 0.970471, 0.970542, 0.970816, 0.970835, 0.971258, 0.971287, 0.97171, 0.971736, 0.97201, 0.972029, 0.972452, 0.972478, 0.972901, 0.972931, 0.973203, 0.97322, 0.973673, 0.974122, 0.974415, 0.974864, 0.97491, 0.975157, 0.975606, 0.975899, 0.976348, 0.976394, 0.976641, 0.976681, 0.97693, 0.97695, 0.977383, 0.977422, 0.977672, 0.977691, 0.978125, 0.978164, 0.978414, 0.978459, 0.978707, 0.978746, 0.978997, 0.979058, 0.979446, 0.979457, 0.979739, 0.979778, 0.980028, 0.980072, 0.980321, 0.980381, 0.98077, 0.980788, 0.9809, 0.981353, 0.981642, 0.981935, 0.981944, 0.982205, 0.982225, 0.982495, 0.982517, 0.982787, 0.982807, 0.983099, 0.983108, 0.983369, 0.983389, 0.983682, 0.983685, 0.983812, 0.98382, 0.98408, 0.984101, 0.984394, 0.984402, 0.984684, 0.984692, 0.984976, 0.984984, 0.985266, 0.985274, 0.985559, 0.985575, 0.985666, 0.985689, 0.985978, 0.986111, 0.986378, 0.986401, 0.986668, 0.98669, 0.986957, 0.986983, 0.987113, 0.987243, 0.98796, 0.988233, 0.988363, 0.988496, 0.988626, 0.988915, 0.989045, 0.989178, 0.989308, 0.989438, 0.989408, 0.989538, 0.989671, 0.989641, 0.989771, 0.989901, 0.989872, 0.990001, 0.990134, 0.990105, 0.990235, 0.990205, 0.990335, 0.990465, 0.990598 ] ESDU_in = interp1d(ESDU_nrs, ESDU_F_in) ESDU_st = interp1d(ESDU_nrs, ESDU_F_st) inline_factors = [round(float(ESDU_in(i)),4) for i in range(3, 10)] staggered_factors = [round(float(ESDU_st(i)),4) for i in range(3, 10)] assert_close1d(inline_factors, ESDU_73031_F2_inline) assert_close1d(staggered_factors, ESDU_73031_F2_staggered) #import matplotlib.pyplot as plt #plt.plot(ESDU_nrs, ESDU_F_in) #plt.plot(ESDU_nrs, ESDU_F_st) # #plt.plot(range(3, 10), inline_factors, 'o') #plt.plot(range(3, 10), staggered_factors, 'o') #plt.show() def test_ESDU_tube_angle_correction(): F3 = ESDU_tube_angle_correction(75) assert_close(F3, 0.9794139080247666) # Digitized data from graph # angles = [19.7349, 20.1856, 20.4268, 20.8778, 21.3597, 21.8404, 22.3523, 22.8326, 23.3148, 23.8252, 24.1867, 24.6375, 25.0891, 25.5697, 26.021, 26.5312, 26.9528, 27.4633, 27.9745, 28.455, 28.8762, 29.3867, 30.0483, 30.5291, 30.9798, 31.4905, 31.9712, 32.4519, 32.9026, 33.3833, 33.8938, 34.3743, 34.855, 35.3655, 35.846, 36.3268, 36.8372, 37.3178, 37.8282, 38.3385, 38.8491, 39.3893, 39.8995, 40.38, 40.9203, 41.4305, 41.9706, 42.511, 43.081, 43.5912, 44.1612, 44.7312, 45.2416, 45.8415, 46.3814, 46.9513, 47.5213, 48.0911, 48.6611, 49.231, 49.8008, 50.4004, 50.9701, 51.5698, 52.1694, 52.7393, 53.3386, 53.9382, 54.5378, 55.1372, 55.7069, 56.3062, 56.9356, 57.5648, 58.1642, 58.7935, 59.4227, 60.0818, 60.711, 61.3103, 61.9694, 62.5986, 63.2573, 63.9163, 64.5752, 65.234, 65.8929, 66.5514, 67.2102, 67.869, 68.5575, 69.2162, 69.9047, 70.5632, 71.2813, 71.94, 72.6284, 73.3464, 74.0346, 74.7526, 75.4706, 76.1587, 76.8765, 77.5944, 78.3122, 79.0299, 79.7476, 80.4952, 81.2129, 81.9305, 82.6479, 83.3952, 84.083, 84.8003, 85.5179, 86.2652, 86.9825, 87.7295, 88.4468, 89.1642, 89.9112, 90] # F3s = [0.528819, 0.534137, 0.538566, 0.544474, 0.552447, 0.557766, 0.566034, 0.570763, 0.579326, 0.584351, 0.590551, 0.59587, 0.602957, 0.608276, 0.614774, 0.619504, 0.626296, 0.631616, 0.63841, 0.643434, 0.649342, 0.654662, 0.663523, 0.669137, 0.674456, 0.680071, 0.685685, 0.691004, 0.696322, 0.701641, 0.706961, 0.711986, 0.7176, 0.72292, 0.727944, 0.733558, 0.738583, 0.743902, 0.748928, 0.753953, 0.759273, 0.764299, 0.769029, 0.774053, 0.779079, 0.78381, 0.788541, 0.793862, 0.798594, 0.803324, 0.808056, 0.812788, 0.817813, 0.822546, 0.826982, 0.831419, 0.836151, 0.840588, 0.84532, 0.849757, 0.854194, 0.858337, 0.86248, 0.866917, 0.871061, 0.875498, 0.879051, 0.883194, 0.887337, 0.891185, 0.895328, 0.898881, 0.90273, 0.906284, 0.910133, 0.913982, 0.917536, 0.921091, 0.924645, 0.928199, 0.931754, 0.935308, 0.938273, 0.941533, 0.944794, 0.947759, 0.951019, 0.953395, 0.95636, 0.959326, 0.961997, 0.964668, 0.967339, 0.969715, 0.971797, 0.974468, 0.976844, 0.978632, 0.980714, 0.982501, 0.984289, 0.986076, 0.987569, 0.989062, 0.990555, 0.991753, 0.992951, 0.99415, 0.995348, 0.996251, 0.996859, 0.997468, 0.998666, 0.998979, 0.999883, 1, 1, 1, 1, 1, 1, 1] # # import matplotlib.pyplot as plt # import numpy as np # # plt.plot(angles, F3s) # plt.plot(angles, np.sin(np.radians(angles))**0.6) # plt.show() def test_Zukauskas_tube_row_correction(): F = Zukauskas_tube_row_correction(4, staggered=True) assert_close(F, 0.8942) F = Zukauskas_tube_row_correction(6, staggered=False) assert_close(F, 0.9465) def test_Zukauskas_tube_row_correction_refit(): from scipy.interpolate import UnivariateSpline from ht.conv_tube_bank import Zukauskas_Czs_high_Re_staggered, Zukauskas_Czs_inline, Zukauskas_Czs_low_Re_staggered # Commands used to obtain the fitted data: Zukauskas_Cz_Zs = [0.968219, 1.01968, 1.04164, 1.04441, 1.07539, 1.09332, 1.13914, 1.16636, 1.23636, 1.2394, 1.24505, 1.3125, 1.33358, 1.38554, 1.43141, 1.48282, 1.4876, 1.55352, 1.58004, 1.60466, 1.65726, 1.67493, 1.70188, 1.79682, 1.91823, 1.99323, 1.99665, 2.04002, 2.16306, 2.18556, 2.19045, 2.30691, 2.3086, 2.36006, 2.45272, 2.45413, 2.57543, 2.59826, 2.72341, 2.7451, 2.8896, 2.91482, 2.98759, 3.1572, 3.23203, 3.25334, 3.3511, 3.42295, 3.4499, 3.52072, 3.6168, 3.83565, 3.9076, 3.9826, 4.02939, 4.17411, 4.20042, 4.44242, 4.48937, 4.61023, 4.82811, 4.95071, 5.07038, 5.28825, 5.31232, 5.3621, 5.50606, 5.53014, 5.60405, 5.74801, 5.74807, 5.82181, 5.99012, 5.99017, 6.13636, 6.23207, 6.23212, 6.37826, 6.44983, 6.44988, 6.62015, 6.69183, 6.69188, 6.95807, 6.95812, 6.98312, 7.1767, 7.20001, 7.41772, 7.41848, 7.65967, 7.87743, 7.90156, 7.95003, 7.97416, 7.97476, 8.21606, 8.2166, 8.45795, 8.60365, 8.67571, 8.79712, 8.91809, 8.96597, 9.18368, 9.20824, 9.42551, 9.45013, 9.66741, 9.69197, 10.0786, 10.3208, 10.5623, 10.5626, 10.7803, 10.9737, 10.9978, 11.2398, 11.2399, 11.4574, 11.4575, 11.6993, 11.7478, 11.9653, 11.9896, 12.2072, 12.2315, 12.4491, 12.691, 12.7152, 12.9812, 13.2231, 13.2715, 13.465, 13.7068, 13.9246, 13.9487, 14.1905, 14.4324, 14.6743, 14.9161, 14.9887, 15.2305, 15.4724, 15.7142, 15.787, 15.811, 15.8835, 16.0046, 16.0287, 16.2465, 16.3673, 16.4883, 16.5124, 16.706, 16.7301, 16.9477, 16.9479, 17.1897, 17.2138, 17.4315, 17.6734, 17.9152, 17.9636, 18.2054, 18.2055, 18.4473, 18.6891, 18.9068, 18.931, 18.9793, 19.2212, 19.4631, 19.5599, 19.7049, 19.9467, 19.9952] low_Re_staggered_Cz = [0.828685, 0.831068, 0.832085, 0.832213, 0.833647, 0.834478, 0.836599, 0.83786, 0.8411, 0.841241, 0.841503, 0.845561, 0.84683, 0.849956, 0.852715, 0.855808, 0.856096, 0.859148, 0.860376, 0.861516, 0.863952, 0.864828, 0.866165, 0.870874, 0.876897, 0.880617, 0.880787, 0.882293, 0.886566, 0.887348, 0.887517, 0.89214, 0.892207, 0.894249, 0.897396, 0.897444, 0.901563, 0.902338, 0.906589, 0.907258, 0.911719, 0.912497, 0.914744, 0.91998, 0.92229, 0.922729, 0.92474, 0.926218, 0.926772, 0.928561, 0.930987, 0.936514, 0.938332, 0.940226, 0.940947, 0.943179, 0.943584, 0.946941, 0.947769, 0.9499, 0.95374, 0.955902, 0.957529, 0.960492, 0.96082, 0.961497, 0.962826, 0.963048, 0.96373, 0.965208, 0.965208, 0.965965, 0.967759, 0.96776, 0.969318, 0.969757, 0.969758, 0.970428, 0.970757, 0.970757, 0.971538, 0.972422, 0.972422, 0.975703, 0.975704, 0.976012, 0.978249, 0.978139, 0.977115, 0.977111, 0.977585, 0.978013, 0.97806, 0.978155, 0.978202, 0.978204, 0.97819, 0.97819, 0.979578, 0.980416, 0.980411, 0.980405, 0.981521, 0.981333, 0.980478, 0.980382, 0.981379, 0.981492, 0.981479, 0.981478, 0.982147, 0.982566, 0.982553, 0.982553, 0.98254, 0.981406, 0.98171, 0.98476, 0.984762, 0.98475, 0.98475, 0.984736, 0.984733, 0.985732, 0.985843, 0.986842, 0.986953, 0.985817, 0.986825, 0.986926, 0.986911, 0.987834, 0.988018, 0.988008, 0.987994, 0.991353, 0.991148, 0.98909, 0.9902, 0.990187, 0.991297, 0.991293, 0.991279, 0.991266, 0.992054, 0.992292, 0.99237, 0.992366, 0.992359, 0.992358, 0.993068, 0.993463, 0.993456, 0.993454, 0.994443, 0.994566, 0.994553, 0.994553, 0.99454, 0.994539, 0.996774, 0.99676, 0.996746, 0.996744, 0.99673, 0.99673, 0.997466, 0.998201, 0.998863, 0.998936, 0.99902, 0.999439, 0.999857, 1.00002, 1.00002, 1, 1] high_Re_staggered_Cz = [0.617923, 0.630522, 0.635897, 0.636344, 0.64134, 0.644232, 0.651621, 0.654452, 0.661728, 0.662045, 0.662632, 0.669643, 0.671835, 0.683767, 0.694302, 0.704706, 0.705673, 0.719014, 0.721221, 0.72327, 0.727649, 0.729119, 0.73359, 0.749337, 0.759443, 0.770509, 0.771014, 0.777413, 0.785006, 0.786394, 0.786756, 0.795376, 0.795545, 0.800697, 0.809975, 0.810062, 0.817547, 0.818955, 0.829084, 0.830839, 0.842534, 0.843935, 0.847977, 0.857398, 0.861555, 0.862739, 0.866619, 0.869471, 0.870563, 0.873432, 0.877325, 0.886614, 0.889668, 0.89251, 0.894282, 0.899765, 0.900781, 0.910119, 0.911931, 0.916595, 0.921077, 0.925619, 0.930052, 0.932064, 0.932286, 0.933053, 0.935273, 0.935644, 0.937165, 0.940127, 0.940128, 0.941835, 0.945731, 0.945731, 0.947081, 0.947964, 0.947965, 0.949465, 0.950199, 0.9502, 0.952562, 0.953557, 0.953558, 0.958036, 0.958037, 0.958267, 0.960054, 0.96027, 0.961381, 0.961388, 0.963615, 0.964614, 0.964725, 0.965472, 0.965844, 0.965847, 0.966954, 0.966957, 0.968064, 0.96956, 0.970299, 0.970762, 0.971224, 0.971406, 0.972518, 0.972516, 0.972504, 0.972617, 0.973614, 0.97368, 0.974715, 0.975264, 0.975811, 0.975814, 0.978048, 0.980033, 0.980281, 0.982515, 0.982515, 0.982502, 0.982503, 0.983612, 0.98361, 0.983597, 0.983709, 0.984707, 0.984819, 0.985817, 0.985804, 0.985896, 0.986911, 0.986898, 0.98712, 0.988008, 0.987994, 0.988994, 0.989104, 0.98909, 0.9902, 0.990187, 0.991297, 0.991293, 0.991279, 0.991266, 0.991252, 0.995742, 0.994903, 0.992366, 0.99573, 0.995729, 0.995716, 0.99571, 0.995703, 0.995826, 0.996814, 0.996813, 0.996801, 0.996801, 0.996787, 0.996786, 0.996774, 0.99676, 0.997682, 0.997867, 0.997854, 0.997854, 0.99784, 0.997826, 0.997814, 0.997813, 0.99781, 0.99892, 0.998907, 0.998901, 0.998893, 0.998879, 0.998877] inline_Cz = [0.658582, 0.681965, 0.69194, 0.6932, 0.700314, 0.704433, 0.710773, 0.714541, 0.724228, 0.724649, 0.725518, 0.735881, 0.738799, 0.74599, 0.751285, 0.75722, 0.757717, 0.76457, 0.767327, 0.776314, 0.781783, 0.783619, 0.786421, 0.794105, 0.803931, 0.81, 0.810227, 0.813093, 0.821227, 0.822615, 0.822917, 0.830103, 0.830207, 0.833383, 0.839101, 0.839188, 0.847046, 0.848103, 0.853898, 0.854902, 0.862547, 0.863881, 0.868371, 0.875104, 0.878568, 0.879555, 0.884081, 0.886933, 0.888003, 0.890813, 0.894236, 0.902032, 0.904114, 0.906285, 0.907639, 0.910812, 0.911389, 0.916696, 0.917725, 0.92029, 0.924912, 0.927513, 0.930052, 0.934534, 0.934906, 0.935673, 0.937893, 0.938227, 0.939252, 0.941249, 0.94125, 0.942957, 0.946853, 0.946854, 0.948204, 0.949088, 0.949088, 0.950588, 0.951322, 0.951323, 0.953685, 0.954679, 0.95468, 0.959159, 0.95916, 0.959274, 0.960163, 0.96027, 0.961381, 0.961388, 0.963615, 0.96585, 0.966222, 0.966969, 0.966968, 0.966968, 0.966954, 0.966957, 0.968064, 0.96956, 0.970299, 0.970762, 0.971224, 0.971406, 0.972518, 0.972516, 0.972504, 0.972617, 0.973614, 0.973737, 0.975675, 0.976888, 0.978099, 0.9781, 0.979191, 0.98016, 0.980281, 0.982515, 0.982515, 0.982502, 0.982503, 0.983612, 0.98361, 0.983597, 0.983709, 0.984707, 0.984819, 0.985817, 0.985804, 0.985896, 0.986911, 0.986898, 0.98712, 0.988008, 0.987994, 0.988994, 0.989104, 0.98909, 0.9902, 0.990187, 0.991297, 0.991293, 0.991279, 0.991266, 0.991252, 0.995742, 0.994903, 0.992366, 0.99573, 0.995729, 0.995716, 0.99571, 0.995703, 0.995826, 0.996814, 0.996813, 0.996801, 0.996801, 0.996787, 0.996786, 0.996774, 0.99676, 0.997682, 0.997867, 0.997854, 0.997854, 0.99784, 0.997826, 0.997814, 0.997813, 0.99781, 0.99892, 0.998907, 0.998901, 0.998893, 0.998879, 0.998877] # hand tuned smoothing Zukauskas_Cz_low_Re_staggered_obj = UnivariateSpline(Zukauskas_Cz_Zs, low_Re_staggered_Cz, s=0.0001) Zukauskas_Cz_high_Re_staggered_obj = UnivariateSpline(Zukauskas_Cz_Zs, high_Re_staggered_Cz, s=0.0005) Zukauskas_Cz_inline_obj = UnivariateSpline(Zukauskas_Cz_Zs, inline_Cz, s=0.0005) Zukauskas_Czs_inline2 = np.round(Zukauskas_Cz_inline_obj(range(1, 20)), 4).tolist() assert_close1d(Zukauskas_Czs_inline, Zukauskas_Czs_inline2) Zukauskas_Czs_low_Re_staggered2 = np.round(Zukauskas_Cz_low_Re_staggered_obj(range(1, 20)), 4).tolist() assert_close1d(Zukauskas_Czs_low_Re_staggered, Zukauskas_Czs_low_Re_staggered2) Zukauskas_Czs_high_Re_staggered2 = np.round(Zukauskas_Cz_high_Re_staggered_obj(range(1, 20)), 4).tolist() assert_close1d(Zukauskas_Czs_high_Re_staggered, Zukauskas_Czs_high_Re_staggered2) def test_Nu_Zukauskas_Bejan(): Nu = Nu_Zukauskas_Bejan(Re=1E4, Pr=7., tube_rows=10, pitch_parallel=.05, pitch_normal=.05) assert_close(Nu, 175.9202277145248) Nu = Nu_Zukauskas_Bejan(Re=1E4, Pr=7., tube_rows=10, pitch_parallel=.05, pitch_normal=.05, Pr_wall=9.0) assert_close(Nu, 165.2074626671159) Nus = [Nu_Zukauskas_Bejan(Re=Re, Pr=7., tube_rows=30, pitch_parallel=.05, pitch_normal=.05) for Re in (10, 2000, 1E5, 1E7)] Nus_expect = [4.554889061992833, 65.35035570869223, 768.4207053648229, 26469.71311148279] assert_close1d(Nus, Nus_expect) Nus = [Nu_Zukauskas_Bejan(Re=Re, Pr=7., tube_rows=30, pitch_parallel=.05, pitch_normal=.09) for Re in (10, 2000, 1E5, 1E7)] Nus_expect = [5.263427360525052, 75.85353712516013, 793.1545862201796, 27967.361063088636] assert_close1d(Nus, Nus_expect) def test_Nu_ESDU_73031(): Nu = Nu_ESDU_73031(Re=1.32E4, Pr=0.71, tube_rows=8, pitch_parallel=.09, pitch_normal=.05) assert_close(98.2563319140594, Nu) Nu = Nu_ESDU_73031(Re=1.32E4, Pr=0.71, tube_rows=8, pitch_parallel=.09, pitch_normal=.05, Pr_wall=0.71) assert_close(98.2563319140594, Nu) Nu = Nu_ESDU_73031(Re=1.32E4, Pr=0.71, tube_rows=8, pitch_parallel=.05, pitch_normal=.05, Pr_wall=0.75) assert_close(87.69324193674449, Nu) Nu = Nu_ESDU_73031(Re=1.32E4, Pr=0.71, tube_rows=3, pitch_parallel=.05, pitch_normal=.05, Pr_wall=0.75) assert_close(Nu, 75.57180591337092) Nus = [Nu_ESDU_73031(Re=Re, Pr=0.71, tube_rows=3, pitch_parallel=pp, pitch_normal=.05, Pr_wall=0.75) for pp in [0.09, 0.05] for Re in [100, 1E5, 1E6]] Nus_expect = [5.179925804379317, 307.9970377601136, 1481.8545490578865, 4.0177935875859365, 282.40096167747, 1367.860174719831] assert_close1d(Nus, Nus_expect) def test_Nu_HEDH_tube_bank(): Nu = Nu_HEDH_tube_bank(Re=1E4, Pr=7., tube_rows=10, pitch_normal=.05, pitch_parallel=.05, Do=.03) assert_close(Nu, 382.4636554404698) Nu = Nu_HEDH_tube_bank(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=.05, pitch_parallel=.05, Do=.025) assert_close(Nu, 149.18735251017594) Nu = Nu_HEDH_tube_bank(Re=1E4, Pr=7., tube_rows=5, pitch_normal=.05, pitch_parallel=.05, Do=.03) assert_close(Nu, 359.0551204831393) def test_dP_Kern(): from ht.conv_tube_bank import Kern_f_Re f = [Kern_f_Re(v) for v in linspace(10, 1E6, 10)] f_values = [6.0155491322862771, 0.19881943524161752, 0.1765198121811164, 0.16032260681398205, 0.14912064432650635, 0.14180674990498099, 0.13727374873569789, 0.13441446600494875, 0.13212172689902535, 0.12928835660421958] assert_close1d(f, f_values) dP = dP_Kern(11., 995., 0.000803, 0.584, 0.1524, 0.0254, .019, 22, 0.000657) assert_close(dP, 18980.58768759033) dP = dP_Kern(m=11., rho=995., mu=0.000803, DShell=0.584, LSpacing=0.1524, pitch=0.0254, Do=.019, NBaffles=22) assert_close(dP, 19521.38738647667) def test_dP_Kern_data(): from ht.conv_tube_bank import Kern_f_Re_tck _Kern_dP_Res = np.array([9.9524, 11.0349, 12.0786, 13.0504, 14.0121, 15.0431, 16.1511, 17.1176, 17.9105, 18.9822, 19.9879, 21.0484, 22.0217, 23.1893, 24.8973, 26.0495, 27.7862, 29.835, 31.8252, 33.9506, 35.9822, 38.3852, 41.481, 43.9664, 47.2083, 50.6891, 54.0782, 58.0635, 63.5667, 68.2537, 74.247, 78.6957, 83.9573, 90.1511, 95.5596, 102.613, 110.191, 116.806, 128.724, 137.345, 150.384, 161.484, 171.185, 185.031, 196.139, 210.639, 230.653, 250.933, 281.996, 300.884, 329.472, 353.842, 384.968, 408.108, 444.008, 505.513, 560.821, 638.506, 690.227, 741.254, 827.682, 918.205, 1018.63, 1122.76, 1213.62, 1320.38, 1417.94, 1522.93, 1667.69, 1838.11, 2012.76, 2247.44, 2592.21, 2932.18, 3381.87, 3875.42, 4440.83, 5056.16, 5608.95, 6344.58, 7038.48, 8224.34, 9123.83, 10121.7, 11598, 12701.4, 14090, 15938.5, 17452.9, 19112.6, 20929.3, 24614, 29324.6, 34044.8, 37282.2, 42999.9, 50570.2, 55737.9, 59860.6, 65553, 70399.2, 78101.5, 84965.7, 96735.3, 110139, 122977, 136431, 152339, 165740, 180319, 194904, 207981, 223357, 241440, 257621, 283946, 317042, 353996, 408315, 452956, 519041, 590939, 668466, 751216, 827981, 894985, 1012440 ]) _Kern_dP_fs = 144.0 * np.array([0.0429177, 0.0382731, 0.0347901, 0.0316208, 0.0298653, 0.0276702, 0.0259671, 0.024523, 0.0237582, 0.0224369, 0.0211881, 0.0202668, 0.0193847, 0.0184234, 0.0172894, 0.0166432, 0.0155182, 0.0147509, 0.0138423, 0.0131572, 0.0124255, 0.0118105, 0.0110842, 0.0106028, 0.0100785, 0.00958019, 0.0092235, 0.00871144, 0.00817649, 0.0077722, 0.00743616, 0.0071132, 0.00684836, 0.00655159, 0.00634789, 0.00611185, 0.00592242, 0.00577517, 0.00552603, 0.00542355, 0.00522267, 0.00502847, 0.00493497, 0.00481301, 0.00469334, 0.00460654, 0.00449314, 0.00438231, 0.00424799, 0.00416922, 0.00406658, 0.00401703, 0.00394314, 0.0038947, 0.00382305, 0.00373007, 0.00368555, 0.00359592, 0.00357512, 0.003509, 0.00344515, 0.00338229, 0.00332057, 0.00328077, 0.00322026, 0.00316102, 0.00308274, 0.00308446, 0.00302787, 0.00297247, 0.0028993, 0.00284654, 0.00277759, 0.0027099, 0.00262738, 0.00256361, 0.00248541, 0.00244055, 0.00238072, 0.0023227, 0.00228032, 0.00222531, 0.00218471, 0.00214484, 0.00206613, 0.00205439, 0.00200402, 0.00196775, 0.00191932, 0.00189622, 0.00186143, 0.00180501, 0.0017393, 0.00170817, 0.00168761, 0.00163622, 0.00158663, 0.0015576, 0.00153862, 0.0015201, 0.00149199, 0.00147418, 0.00142864, 0.00139389, 0.00136874, 0.00133524, 0.00131931, 0.0012953, 0.00127147, 0.00124808, 0.00121724, 0.00121785, 0.00119533, 0.00118082, 0.00116638, 0.00114504, 0.00111702, 0.00108969, 0.00107013, 0.00104389, 0.00101205, 0.000987437, 0.000969567, 0.000939849, 0.000922653, 0.000905634, 0.000894962 ]) # # Used in preference over interp1d as saves 30% of execution time, and # # performs some marginally small amount of smoothing # # s=0.1 is chosen to have 9 knots, a reasonable amount. # Kern_f_Re = UnivariateSpline(_Kern_dP_Res, _Kern_dP_fs, s=0.1) tck = splrep(_Kern_dP_Res, _Kern_dP_fs, s=0.1) [assert_close1d(i, j) for i, j in zip(Kern_f_Re_tck[:-1], tck[:-1])] def test_dP_Zukauskas(): dP1 = dP_Zukauskas(Re=13943., n=7, ST=0.0313, SL=0.0343, D=0.0164, rho=1.217, Vmax=12.6) dP2 = dP_Zukauskas(Re=13943., n=7, ST=0.0313, SL=0.0313, D=0.0164, rho=1.217, Vmax=12.6) assert_close1d([dP1, dP2], [235.22916169118335, 161.14703721855741]) def test_dP_Zukauskas_dP_dP_staggered_f_spline(): _dP_staggered_Res = np.array([10, 10.9129, 11.6733, 13.1024, 14.0153, 14.9918, 17.1536, 18.5267, 19.8182, 20.7261, 22.243, 23.7936, 26.7057, 28.5663, 32.2732, 34.858, 37.2879, 41.0554, 44.4722, 47.8949, 51.2337, 55.3369, 65.1821, 70.4025, 76.0437, 82.1368, 88.7182, 95.1284, 100.553, 103.386, 108.398, 116.441, 118.455, 127.808, 129.188, 139.389, 140.899, 153.665, 155.444, 167.595, 168.914, 182.793, 197.771, 201.613, 217.768, 223.559, 241.759, 246.457, 268.516, 278.915, 292.866, 304.208, 322.535, 335.015, 351.772, 366.482, 402.412, 415.414, 451.79, 465.314, 497.559, 512.453, 542.68, 570.321, 609.312, 610.163, 671.039, 671.953, 731.917, 732.915, 813.886, 839.919, 896.808, 977.69, 1016.19, 1119.14, 1221.31, 1244.48, 1346.07, 1455.66, 1482.44, 1603.12, 1616.93, 1748.56, 1780.79, 1925.77, 1961.27, 2056.71, 2060.37, 2266.81, 2308.27, 2474.96, 2542.2, 2723.03, 2799.84, 2996.9, 3053.95, 3274.27, 3363.57, 3606.09, 4001.84, 4005.75, 4367.03, 4411.71, 4809.6, 4854.24, 5297.21, 5346.19, 5777.99, 5836.5, 6184.44, 6739.62, 6817.15, 7422.65, 7435.62, 8188.61, 8256.81, 9005.89, 9089.79, 9914.09, 9931.42, 10832, 11357.6, 11913.2, 12508.2, 13011.2, 13642.4, 14309.8, 15024.5, 15759.5, 16387, 17188.6, 18046.5, 18772.3, 19683.7, 20458.2, 22313.4, 22950.8, 24573.9, 26311.7, 27049.2, 28976.2, 29516.6, 31605, 32505.6, 34805.6, 35453.4, 37961.9, 39045, 39838.4, 40171.7, 43802.4, 43836, 47853, 48253.3, 52629.1, 57429.8, 57958.7, 60823.7, 63808, 66429.9, 72454.1, 76644.8, 79791.3, 86914.7, 87727.5, 94796.5, 95846.9, 102543, 103393, 112734, 123172, 124193, 134342, 136770, 147946, 149173, 161368, 162701, 177710, 179183, 193825, 197329, 203406, 205093, 224028, 225878, 246499, 248787, 268891, 271756, 296172, 299307, 323098, 329652, 355768, 363073, 388139, 399883, 411321, 411637, 453053, 453370, 494224, 499159, 539099, 549766, 593776, 617117, 617548, 679896, 741914, 748826, 816818, 899347, 899975, 991217, 1029890, 1039630, 1134310, 1145030, 1249310, 1261120, 1375630, 1388740, 1515150, 1529530, 1668760, 1684660, 1837940, 1855450, 2063320, 2064190, 2251140, 2273460, 2479450, 2502990, 2730830, 2756750 ]) _dP_staggered_Re_125 = np.array([23.9929, 22.6513, 21.1808, 19.0604, 17.8231, 16.6661, 14.5725, 13.6264, 12.8644, 12.1931, 11.3569, 10.7219, 9.55649, 8.93611, 7.91304, 7.32822, 6.89654, 6.28568, 5.80434, 5.44301, 5.08949, 4.72306, 4.06698, 3.79555, 3.5683, 3.30447, 3.1177, 2.91006, 2.77913, 2.71412, 2.60635, 2.4487, 2.41753, 2.2802, 2.25939, 2.12672, 2.11005, 1.98054, 1.96397, 1.85661, 1.84576, 1.74274, 1.66846, 1.63677, 1.56011, 1.53763, 1.47248, 1.45689, 1.38943, 1.36053, 1.32959, 1.30743, 1.27402, 1.2528, 1.22604, 1.20401, 1.15477, 1.13664, 1.10541, 1.09271, 1.06394, 1.05209, 1.02957, 1.01043, 0.985509, 0.984989, 0.950966, 0.950537, 0.92446, 0.924083, 0.894818, 0.885516, 0.868347, 0.848317, 0.840024, 0.819658, 0.801646, 0.797824, 0.782058, 0.766644, 0.763863, 0.752037, 0.75061, 0.737713, 0.736366, 0.730623, 0.728723, 0.723802, 0.723618, 0.709974, 0.707146, 0.696311, 0.694446, 0.689689, 0.685538, 0.675409, 0.672874, 0.663594, 0.66181, 0.657217, 0.636046, 0.63585, 0.619904, 0.619273, 0.613337, 0.612083, 0.601667, 0.601114, 0.595116, 0.592882, 0.580202, 0.570252, 0.568954, 0.558333, 0.558117, 0.542262, 0.541366, 0.532074, 0.530674, 0.517089, 0.516819, 0.502141, 0.497421, 0.492707, 0.484889, 0.478584, 0.471858, 0.465173, 0.458449, 0.451954, 0.448019, 0.443305, 0.436261, 0.430589, 0.424819, 0.420179, 0.409927, 0.406655, 0.398825, 0.391145, 0.387928, 0.380033, 0.378482, 0.372795, 0.369679, 0.362205, 0.359995, 0.351918, 0.34995, 0.348549, 0.347907, 0.341093, 0.341015, 0.332198, 0.331281, 0.322228, 0.316669, 0.315569, 0.310077, 0.30713, 0.304674, 0.296022, 0.29109, 0.287612, 0.282751, 0.282227, 0.277435, 0.276759, 0.271491, 0.270748, 0.263364, 0.258755, 0.258047, 0.251406, 0.250064, 0.244264, 0.243818, 0.239612, 0.239024, 0.232805, 0.232168, 0.226194, 0.225387, 0.224028, 0.224027, 0.224011, 0.22401, 0.223994, 0.223993, 0.223979, 0.223977, 0.223962, 0.22396, 0.223947, 0.223943, 0.22393, 0.223926, 0.223915, 0.223909, 0.223904, 0.223904, 0.223887, 0.223887, 0.226011, 0.225818, 0.224086, 0.223853, 0.22384, 0.225949, 0.225988, 0.225971, 0.225955, 0.225954, 0.225938, 0.225921, 0.225921, 0.225904, 0.223951, 0.224158, 0.22588, 0.225878, 0.225863, 0.225861, 0.225846, 0.225844, 0.225829, 0.225827, 0.225812, 0.22581, 0.225794, 0.225793, 0.225774, 0.225774, 0.225759, 0.225757, 0.227901, 0.227913, 0.227897, 0.227896 ]) _dP_staggered_Re_15 = np.array([9.34201, 8.81965, 8.28809, 7.42806, 6.97391, 6.57517, 5.84093, 5.50985, 5.16014, 4.93488, 4.68126, 4.42254, 3.99955, 3.773, 3.39505, 3.20519, 3.02598, 2.77577, 2.61488, 2.474, 2.33566, 2.20505, 1.96531, 1.8554, 1.76851, 1.68568, 1.60674, 1.54592, 1.47385, 1.45584, 1.42566, 1.36641, 1.35191, 1.29626, 1.28859, 1.24598, 1.24005, 1.18197, 1.17596, 1.13745, 1.13449, 1.10514, 1.04611, 1.03299, 1.01551, 1.00639, 0.975508, 0.956979, 0.921361, 0.906001, 0.886645, 0.876509, 0.861323, 0.848885, 0.833067, 0.820018, 0.790987, 0.781353, 0.757982, 0.750599, 0.73523, 0.72878, 0.715526, 0.703825, 0.690704, 0.69043, 0.671089, 0.670816, 0.658238, 0.65804, 0.642607, 0.638042, 0.628642, 0.616468, 0.611099, 0.59789, 0.592593, 0.59088, 0.5807, 0.571709, 0.569635, 0.559848, 0.558786, 0.554428, 0.553416, 0.5491, 0.548097, 0.54293, 0.542793, 0.537633, 0.53548, 0.52734, 0.525928, 0.522325, 0.519436, 0.51244, 0.510312, 0.502563, 0.501212, 0.497646, 0.483767, 0.483639, 0.479479, 0.478991, 0.469919, 0.469457, 0.465373, 0.464541, 0.457403, 0.456485, 0.452112, 0.44443, 0.443408, 0.435131, 0.434907, 0.419981, 0.418722, 0.415054, 0.414669, 0.405475, 0.405291, 0.396251, 0.391403, 0.387694, 0.383945, 0.378953, 0.373041, 0.369506, 0.365933, 0.360178, 0.355541, 0.350503, 0.34544, 0.342437, 0.338861, 0.33562, 0.326088, 0.324262, 0.319875, 0.312702, 0.30994, 0.303633, 0.301961, 0.295857, 0.293384, 0.286801, 0.285051, 0.281193, 0.27962, 0.27688, 0.276192, 0.269144, 0.269082, 0.261395, 0.260961, 0.256484, 0.249175, 0.248418, 0.244472, 0.240616, 0.237428, 0.23135, 0.228333, 0.226286, 0.219953, 0.21934, 0.21432, 0.213615, 0.209306, 0.208785, 0.203406, 0.198042, 0.197549, 0.193322, 0.192633, 0.189133, 0.188615, 0.183883, 0.183431, 0.178539, 0.17805, 0.173635, 0.172752, 0.171298, 0.171157, 0.169685, 0.169823, 0.171289, 0.171462, 0.172926, 0.173289, 0.176258, 0.176871, 0.181386, 0.182469, 0.186641, 0.188307, 0.193913, 0.196789, 0.199552, 0.199594, 0.201486, 0.2015, 0.203218, 0.203417, 0.203404, 0.203401, 0.204688, 0.205335, 0.205334, 0.203367, 0.203354, 0.203352, 0.203338, 0.205273, 0.20528, 0.205265, 0.205258, 0.205257, 0.205243, 0.205241, 0.205227, 0.205226, 0.205212, 0.20521, 0.205196, 0.205195, 0.205181, 0.205179, 0.205165, 0.205164, 0.205146, 0.205146, 0.205132, 0.205131, 0.205117, 0.205115, 0.205101, 0.2051 ]) _dP_staggered_Re_2 = np.array([3.3699, 3.25874, 3.1513, 2.97524, 2.87715, 2.78229, 2.60185, 2.504, 2.4214, 2.36801, 2.2862, 2.21078, 2.08731, 2.01849, 1.89955, 1.82808, 1.76778, 1.68508, 1.61934, 1.56066, 1.50918, 1.4524, 1.33872, 1.28835, 1.23986, 1.19319, 1.14827, 1.10908, 1.07889, 1.06407, 1.03929, 1.00291, 0.994386, 0.957472, 0.951802, 0.912623, 0.908283, 0.874086, 0.869647, 0.841073, 0.838095, 0.808676, 0.780364, 0.773598, 0.747536, 0.738905, 0.721828, 0.717582, 0.690875, 0.682216, 0.671254, 0.666188, 0.658464, 0.647496, 0.633663, 0.625691, 0.607864, 0.60192, 0.586506, 0.581184, 0.573473, 0.57011, 0.559368, 0.551449, 0.543432, 0.543274, 0.533071, 0.532917, 0.522924, 0.522784, 0.512946, 0.509741, 0.503124, 0.493595, 0.490661, 0.483683, 0.479474, 0.477483, 0.469851, 0.466187, 0.465338, 0.461708, 0.461273, 0.453348, 0.452088, 0.448562, 0.447435, 0.443915, 0.443836, 0.439616, 0.43882, 0.43577, 0.434512, 0.431212, 0.430014, 0.427098, 0.426293, 0.423332, 0.422987, 0.422964, 0.422929, 0.422883, 0.414874, 0.414451, 0.410887, 0.410884, 0.410855, 0.410436, 0.40691, 0.406003, 0.40083, 0.393272, 0.392277, 0.385608, 0.385474, 0.374217, 0.373188, 0.36608, 0.365067, 0.355721, 0.355584, 0.349483, 0.344411, 0.338995, 0.335717, 0.333088, 0.328254, 0.323091, 0.319967, 0.316935, 0.312854, 0.307934, 0.303486, 0.299932, 0.296289, 0.293462, 0.288427, 0.286092, 0.279681, 0.274029, 0.271776, 0.265638, 0.264031, 0.260457, 0.258987, 0.253176, 0.251647, 0.24824, 0.2468, 0.243526, 0.242183, 0.237587, 0.237538, 0.231397, 0.230821, 0.224266, 0.218493, 0.217895, 0.215809, 0.213044, 0.210747, 0.20588, 0.202787, 0.200602, 0.196037, 0.195433, 0.19047, 0.189774, 0.185568, 0.18506, 0.181897, 0.176863, 0.176379, 0.172288, 0.171368, 0.166958, 0.166501, 0.162215, 0.161773, 0.158952, 0.15869, 0.15501, 0.154182, 0.153022, 0.152707, 0.151364, 0.15124, 0.152546, 0.152684, 0.155311, 0.155668, 0.158336, 0.158692, 0.162743, 0.164018, 0.169607, 0.171511, 0.177917, 0.179674, 0.181351, 0.181379, 0.184846, 0.184874, 0.188409, 0.188408, 0.188396, 0.18876, 0.190196, 0.190315, 0.190318, 0.190617, 0.190888, 0.190917, 0.191188, 0.191489, 0.191491, 0.191793, 0.191913, 0.191942, 0.191929, 0.191928, 0.191915, 0.191913, 0.1919, 0.192079, 0.193733, 0.193731, 0.193718, 0.193717, 0.193703, 0.193702, 0.193686, 0.193686, 0.193673, 0.193861, 0.195522, 0.195521, 0.195508, 0.195506 ]) _dP_staggered_Re_25 = np.array([1.79994, 1.76013, 1.72122, 1.65648, 1.61986, 1.58405, 1.51479, 1.47657, 1.44391, 1.4226, 1.38964, 1.3589, 1.30781, 1.2789, 1.22814, 1.19714, 1.17066, 1.13385, 1.10416, 1.07732, 1.05349, 1.02689, 0.972573, 0.948019, 0.924073, 0.900732, 0.877981, 0.857886, 0.842238, 0.834508, 0.821498, 0.802211, 0.797489, 0.771119, 0.767464, 0.742087, 0.738758, 0.71986, 0.717012, 0.693528, 0.691126, 0.673248, 0.655161, 0.650796, 0.633605, 0.627855, 0.611017, 0.606947, 0.589142, 0.581418, 0.572075, 0.564906, 0.555118, 0.548858, 0.542958, 0.537932, 0.523109, 0.517617, 0.503395, 0.500444, 0.493804, 0.488993, 0.479779, 0.472711, 0.470631, 0.4705, 0.461663, 0.461524, 0.45287, 0.452758, 0.444238, 0.442841, 0.439921, 0.431589, 0.431576, 0.423352, 0.4167, 0.415283, 0.415257, 0.412759, 0.412007, 0.408794, 0.408443, 0.405032, 0.404211, 0.400713, 0.399901, 0.39662, 0.396488, 0.389473, 0.388156, 0.385458, 0.384426, 0.381792, 0.380731, 0.377866, 0.377075, 0.377054, 0.377046, 0.374429, 0.36984, 0.369804, 0.366623, 0.366285, 0.36626, 0.366258, 0.363072, 0.362738, 0.359622, 0.35915, 0.352384, 0.349036, 0.348637, 0.345681, 0.345518, 0.336581, 0.335833, 0.330069, 0.329459, 0.320103, 0.320041, 0.316923, 0.313944, 0.310956, 0.305966, 0.302055, 0.299216, 0.296371, 0.292087, 0.287957, 0.285478, 0.282464, 0.277935, 0.274359, 0.271138, 0.268545, 0.263228, 0.261591, 0.256305, 0.250791, 0.248501, 0.244499, 0.2436, 0.239026, 0.236807, 0.231877, 0.230603, 0.225941, 0.22405, 0.222707, 0.222159, 0.217943, 0.217893, 0.21226, 0.211731, 0.206127, 0.20064, 0.200073, 0.197111, 0.194215, 0.192662, 0.188591, 0.185104, 0.182162, 0.178493, 0.178125, 0.174048, 0.173476, 0.170156, 0.169754, 0.16495, 0.160643, 0.160246, 0.156122, 0.155402, 0.152988, 0.15273, 0.148798, 0.148394, 0.144533, 0.144169, 0.139245, 0.138483, 0.137648, 0.137427, 0.136218, 0.136117, 0.137425, 0.137564, 0.138759, 0.139196, 0.142785, 0.143115, 0.145559, 0.14672, 0.151214, 0.152571, 0.157129, 0.160246, 0.163232, 0.163273, 0.168458, 0.168493, 0.172862, 0.173428, 0.177879, 0.178569, 0.181323, 0.18658, 0.18658, 0.186566, 0.186553, 0.186552, 0.186539, 0.186525, 0.186508, 0.184125, 0.183189, 0.182964, 0.182952, 0.18295, 0.182938, 0.182936, 0.182924, 0.182922, 0.18291, 0.182909, 0.184483, 0.184655, 0.184643, 0.184641, 0.182866, 0.182873, 0.18444, 0.184612, 0.184599, 0.184598, 0.184585, 0.184584 ]) _dP_staggered_Re_parameters = np.array([_dP_staggered_Re_125, _dP_staggered_Re_15, _dP_staggered_Re_2, _dP_staggered_Re_25]).T others = np.array([1.25, 1.5, 2, 2.5]) # do not delete dP_staggered_f = RectBivariateSpline(_dP_staggered_Res, others, _dP_staggered_Re_parameters, kx=3, ky=3, s=0.002) # Excellent plot, though it does linear extrapolation on some lines #import matplotlib.pyplot as plt #dP_staggered_f_zs = np.array([1.25, 1.5, 2, 2.5]) #low, high = min(_dP_staggered_Res), max(_dP_staggered_Res) #xs = np.linspace(low, high, 50000) #for i in range(4): # plt.loglog(_dP_staggered_Res, _dP_staggered_Re_parameters.T[i, :], '.') # plt.loglog(xs, dP_staggered_f(xs, dP_staggered_f_zs[i]), '--') #plt.show() errors = [] for i, param in enumerate(_dP_staggered_Res): for j, z in enumerate(others): original_value = _dP_staggered_Re_parameters[i][j] interpolated = bisplev(param, z, dP_staggered_f_tck) errors.append(abs(abs(original_value - interpolated) / original_value)) assert np.mean(errors) < 0.01 def test_dP_Zukauskas_dP_staggered_correction_spline(): _dP_staggered_correction_parameters = np.array([0.4387, 0.470647, 0.494366, 0.52085, 0.542787, 0.583019, 0.609319, 0.659047, 0.685413, 0.729582, 0.800982, 0.84214, 0.892449, 0.947309, 1.00903, 1.07052, 1.16389, 1.22243, 1.26584, 1.32314, 1.37597, 1.40437, 1.45385, 1.51093, 1.55814, 1.61775, 1.68647, 1.74589, 1.79853, 1.86586, 1.92335, 1.97322, 2.12053, 2.22751, 2.34521, 2.45793, 2.58193, 2.71226, 2.84909, 2.99282, 3.14389, 3.22668, 3.32915, 3.54351 ]) _dP_staggered_correction_Re_100 = np.array([0.996741, 0.996986, 0.997157, 0.997339, 0.997482, 0.997731, 0.997885, 0.998158, 0.998294, 0.998512, 0.998836, 0.999011, 0.999213, 0.99942, 0.99964, 0.999846, 1.00241, 1.02216, 1.0392, 1.06545, 1.08705, 1.0995, 1.1206, 1.14708, 1.16583, 1.18871, 1.21407, 1.23518, 1.25628, 1.27868, 1.29996, 1.31593, 1.36025, 1.39055, 1.42224, 1.45114, 1.48144, 1.51175, 1.54205, 1.57235, 1.60267, 1.62032, 1.64208, 1.68552 ]) _dP_staggered_correction_Re_1000 = np.array([1.03576, 1.02714, 1.02111, 1.01712, 1.01206, 1.00798, 1.00547, 1.001, 0.999839, 0.999378, 0.998689, 0.998319, 0.997891, 0.997451, 0.996985, 0.999249, 1.00245, 1.0135, 1.02415, 1.03618, 1.04682, 1.0534, 1.06478, 1.07524, 1.0836, 1.09539, 1.10811, 1.11825, 1.12833, 1.13858, 1.1481, 1.15678, 1.17941, 1.19487, 1.21106, 1.22398, 1.24068, 1.25657, 1.27109, 1.28706, 1.30317, 1.31111, 1.3196, 1.33956 ]) _dP_staggered_correction_Re_10000 = np.array([1.20211, 1.18293, 1.16951, 1.15527, 1.14308, 1.12148, 1.10821, 1.09069, 1.08213, 1.06633, 1.04824, 1.04041, 1.03015, 1.02269, 1.01509, 1.00905, 1.00302, 1.00302, 1.00304, 1.00623, 1.00905, 1.0103, 1.01246, 1.01508, 1.01696, 1.01926, 1.0225, 1.02674, 1.03074, 1.03432, 1.03618, 1.03931, 1.04813, 1.05451, 1.05855, 1.0674, 1.07355, 1.08006, 1.08719, 1.09572, 1.10324, 1.10854, 1.11428, 1.12663 ]) _dP_staggered_correction_Re_100000 = np.array([1.45829, 1.42587, 1.40486, 1.38291, 1.36389, 1.32864, 1.30754, 1.27136, 1.25327, 1.22447, 1.18203, 1.15678, 1.12845, 1.10251, 1.07182, 1.04763, 1.00824, 0.984925, 0.975402, 0.965711, 0.960152, 0.957646, 0.9534, 0.948334, 0.945015, 0.942714, 0.940164, 0.937857, 0.936683, 0.936683, 0.934823, 0.933668, 0.933668, 0.933668, 0.933668, 0.933668, 0.933668, 0.936683, 0.936683, 0.936683, 0.939698, 0.939698, 0.939698, 0.939698 ]) _dP_staggered_correction_Re_parameters = np.array([_dP_staggered_correction_Re_100, _dP_staggered_correction_Re_1000, _dP_staggered_correction_Re_10000, _dP_staggered_correction_Re_100000]).T others = np.array([1E2, 1E3, 1E4, 1E5]) # do not delete dP_staggered_correction = RectBivariateSpline(_dP_staggered_correction_parameters, others, _dP_staggered_correction_Re_parameters, kx=1, ky=3, s=0.002) # Maybe good plot - bad around the middle #dP_staggered_correction_zs = np.array([1E2, 1E3, 1E4, 1E5]) #low, high = min(_dP_staggered_correction_parameters), max(_dP_staggered_correction_parameters) #xs = np.linspace(low, high, 50000) #for i in range(4): # plt.loglog(_dP_staggered_correction_parameters, _dP_staggered_correction_Re_parameters.T[i, :], '.') # plt.loglog(xs, dP_staggered_correction(xs, dP_staggered_correction_zs[i]), '--') #plt.show() errors = [] for i, param in enumerate(_dP_staggered_correction_parameters): for j, z in enumerate(others): original_value = _dP_staggered_correction_Re_parameters[i][j] interpolated = bisplev(param, z, dP_staggered_correction_tck) errors.append(abs(abs(original_value - interpolated) / original_value)) assert np.mean(errors) < 0.01 def test_dP_Zukauskas_dP_inline_correction_spline(): """Test that the pre-computed spline correctly reproduces the original data points""" _dP_inline_correction_parameters = np.array([0.02, 0.04, 0.066164, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 5.7141, ]) _dP_inline_correction_Re_1000 = np.array([16.05, 10.359, 7.538, 6.6218, 3.9933, 2.3397, 1.3794, 0.8336, 0.50239, 0.29469, 0.27113, ]) _dP_inline_correction_Re_10000 = np.array([13.181, 8.507, 6.1906, 5.4908, 3.4734, 2.2042, 1.3876, 0.8735, 0.54642, 0.33857, 0.3115 ]) _dP_inline_correction_Re_100000 = np.array([9.092, 6.0549, 4.5073, 4.0322, 2.7957, 1.9162, 1.308, 0.9011, 0.62078, 0.42537, 0.39996, ]) _dP_inline_correction_Re_1000000 = np.array([5.3722, 3.9373, 3.1421, 2.8857, 2.1799, 1.6421, 1.2364, 0.9349, 0.7131, 0.53922, 0.51587, ]) _dP_inline_correction_zs = np.array([1E3, 1E4, 1E5, 1E6]) _dP_inline_correction_Re_parameters = np.array([_dP_inline_correction_Re_1000, _dP_inline_correction_Re_10000, _dP_inline_correction_Re_100000, _dP_inline_correction_Re_1000000]).T dP_inline_correction = RectBivariateSpline(_dP_inline_correction_parameters, _dP_inline_correction_zs, _dP_inline_correction_Re_parameters, kx=3, ky=3, s=0.0) # s=0.002 # 2024 cleaned up the fit #import matplotlib.pyplot as plt #low, high = min(_dP_inline_correction_parameters), max(_dP_inline_correction_parameters) #xs = np.logspace(np.log10(low), np.log10(high), 300000) #for i in range(4): # plt.loglog(_dP_inline_correction_parameters, _dP_inline_correction_Re_parameters.T[i, :], '.') # plt.loglog(xs, dP_inline_correction(xs, _dP_inline_correction_zs[i]), '--') #plt.show() errors = [] for i, param in enumerate(_dP_inline_correction_parameters): for j, z in enumerate(_dP_inline_correction_zs): original_value = _dP_inline_correction_Re_parameters[i][j] interpolated = bisplev(param, z, dP_inline_correction_tck) errors.append(abs(abs(original_value - interpolated) / original_value)) assert np.mean(errors) < 0.01 def test_dP_Zukauskas_dP_inline_f_tck_spline(): _dP_inline_Res = np.array([28.5094, 30.8092, 32.9727, 35.3563, 41.2101, 45.9365, 49.1622, 52.6143, 56.3102, 59.107, 63.7533, 68.3605, 73.1607, 82.9896, 91.2679, 107.829, 116.528, 124.713, 134.774, 144.237, 157.106, 169.784, 183.484, 202.173, 218.488, 241.163, 278.938, 301.447, 325.772, 352.069, 402.667, 439.431, 479.551, 528.457, 576.706, 600.39, 654.321, 666.665, 722.026, 795.679, 802.401, 883.594, 965.211, 973.774, 1022.26, 1107.38, 1126.59, 1220.48, 1343.51, 1368.32, 1468.16, 1616.19, 1646.72, 1764.04, 1814.79, 1944.21, 1998.93, 2038.12, 2041.06, 2246.18, 2249.48, 2455.2, 2476.81, 2705.84, 2729.59, 2982.07, 3008.17, 3257.9, 3313.34, 3590.4, 3618.29, 3946.71, 4030.55, 4063.47, 4434.98, 4446.05, 4852.32, 4895.14, 5347.3, 5394.74, 5830.48, 5994.16, 6003.24, 6545.85, 6615.94, 7143.99, 7226.2, 7873.1, 8101.49, 8113.39, 8928.33, 8941.23, 9765.31, 9845.06, 10343.9, 10430.3, 11407.3, 11956.6, 12562.5, 13176.9, 13719.7, 14521.4, 15236.6, 16651, 17465.4, 18505, 20393.2, 20419.3, 22474.5, 22503.3, 24559, 25546.2, 27064.9, 29789.7, 30724.6, 32829.2, 34810.9, 36179.8, 38362.8, 39871.4, 40721.2, 41061.4, 44854.2, 45239.5, 48975.7, 49855.5, 53971.7, 54426.4, 59979.7, 60058.1, 66101.3, 66184.5, 72230.6, 72907, 81043.8, 81128.8, 89317.2, 89406.8, 97574.2, 98430.6, 103433, 104341, 112924, 114990, 123239, 126726, 135811, 139659, 149668, 153913, 163348, 169621, 180015, 186933, 206011, 206189, 227042, 227233, 247788, 250418, 273078, 275976, 300948, 304142, 331663, 335183, 365513, 369392, 406751, 407092, 448264, 448640, 494013, 494428, 544433, 544890, 605857, 606365, 667691, 668251, 735835, 736453, 803766, 810935, 877478, 893699, 967033, 984910, 1044050, 1044920, 1150600, 1151570, 1268030, 1269100, 1397450, 1398620, 1540070, 1541370, 1697250, 1698680, 1854500, 1871040 ]) _dP_inline_Re_125 = np.array([5.93109, 5.54354, 5.22463, 4.91025, 4.2207, 3.80075, 3.54753, 3.31117, 3.12106, 2.97108, 2.75394, 2.58829, 2.41584, 2.14646, 1.9677, 1.6944, 1.58146, 1.49066, 1.3913, 1.29861, 1.20507, 1.12508, 1.05285, 0.971815, 0.909568, 0.833438, 0.747763, 0.704808, 0.66432, 0.632336, 0.581074, 0.549915, 0.52122, 0.493379, 0.470594, 0.461052, 0.443442, 0.440821, 0.430173, 0.421676, 0.421664, 0.422183, 0.429662, 0.431075, 0.438954, 0.446788, 0.449097, 0.46, 0.468865, 0.471657, 0.482859, 0.492167, 0.493224, 0.496938, 0.499697, 0.506586, 0.50325, 0.502485, 0.502646, 0.512335, 0.512408, 0.516812, 0.517255, 0.52129, 0.521276, 0.521127, 0.521113, 0.520979, 0.520951, 0.520816, 0.520351, 0.51557, 0.515536, 0.515148, 0.51047, 0.510337, 0.505209, 0.504298, 0.49523, 0.493744, 0.480865, 0.480679, 0.480676, 0.476399, 0.47587, 0.467664, 0.466446, 0.453034, 0.456863, 0.457087, 0.448375, 0.448242, 0.439335, 0.438121, 0.430813, 0.430369, 0.426369, 0.421825, 0.417567, 0.415493, 0.413748, 0.40895, 0.404931, 0.397616, 0.393735, 0.389087, 0.3814, 0.3813, 0.373863, 0.373765, 0.366718, 0.36344, 0.361258, 0.355173, 0.352681, 0.347915, 0.343752, 0.341039, 0.33675, 0.333804, 0.332203, 0.331636, 0.325673, 0.325338, 0.322369, 0.321193, 0.316002, 0.315506, 0.309826, 0.30975, 0.303711, 0.303632, 0.297644, 0.297168, 0.291819, 0.291767, 0.288848, 0.288818, 0.283136, 0.281514, 0.272212, 0.272406, 0.274764, 0.273631, 0.269345, 0.267807, 0.264025, 0.263257, 0.261364, 0.26134, 0.26129, 0.260266, 0.258656, 0.257969, 0.256205, 0.25619, 0.255938, 0.255937, 0.253893, 0.253614, 0.253287, 0.253278, 0.253208, 0.253199, 0.251176, 0.2509, 0.250577, 0.250568, 0.250491, 0.25049, 0.250412, 0.250412, 0.250334, 0.250333, 0.250256, 0.250255, 0.25017, 0.250169, 0.250092, 0.250091, 0.250013, 0.250013, 0.249942, 0.250177, 0.252337, 0.252322, 0.252258, 0.252244, 0.252196, 0.252196, 0.252117, 0.252117, 0.252039, 0.252038, 0.25196, 0.251959, 0.251881, 0.25188, 0.251802, 0.251802, 0.254214, 0.25446 ]) _dP_inline_Re_15 = np.array([2.51237, 2.49499, 2.32876, 2.1908, 1.87501, 1.68786, 1.5828, 1.484, 1.38705, 1.31326, 1.23965, 1.16623, 1.08879, 0.973353, 0.88678, 0.773105, 0.72388, 0.681815, 0.636838, 0.600558, 0.55801, 0.525955, 0.495741, 0.458149, 0.43183, 0.403164, 0.367208, 0.350209, 0.334532, 0.32017, 0.299699, 0.288076, 0.276903, 0.268782, 0.258357, 0.25326, 0.250756, 0.249796, 0.245772, 0.243513, 0.243318, 0.245622, 0.252914, 0.25366, 0.257801, 0.264766, 0.266548, 0.276173, 0.284237, 0.286562, 0.295607, 0.301306, 0.303191, 0.310225, 0.312873, 0.319399, 0.321166, 0.322408, 0.3225, 0.328697, 0.328793, 0.335219, 0.335506, 0.338421, 0.33871, 0.341653, 0.341978, 0.344926, 0.344907, 0.344818, 0.344809, 0.344713, 0.34469, 0.344681, 0.344584, 0.344549, 0.341421, 0.341109, 0.341012, 0.341002, 0.331021, 0.337212, 0.337554, 0.33746, 0.337449, 0.334469, 0.334034, 0.33154, 0.330712, 0.33067, 0.327386, 0.327336, 0.324341, 0.324066, 0.320821, 0.320812, 0.320676, 0.317593, 0.314385, 0.312656, 0.311204, 0.309367, 0.30782, 0.304983, 0.303469, 0.301435, 0.296008, 0.295937, 0.295845, 0.295844, 0.29001, 0.28882, 0.287086, 0.284229, 0.28322, 0.280847, 0.277486, 0.275535, 0.273856, 0.272992, 0.272948, 0.272482, 0.267582, 0.267318, 0.264881, 0.264389, 0.262202, 0.261791, 0.257077, 0.257024, 0.254467, 0.254434, 0.252125, 0.25188, 0.246946, 0.246897, 0.244434, 0.244409, 0.239588, 0.239582, 0.239543, 0.239114, 0.235263, 0.234574, 0.232704, 0.232439, 0.232387, 0.231931, 0.230263, 0.230024, 0.22998, 0.229521, 0.228102, 0.227634, 0.227563, 0.227562, 0.227491, 0.227491, 0.225446, 0.225198, 0.225135, 0.225127, 0.225065, 0.225057, 0.224994, 0.224987, 0.224924, 0.224916, 0.224846, 0.224846, 0.224776, 0.224776, 0.224706, 0.224705, 0.224636, 0.224635, 0.224558, 0.224558, 0.224488, 0.224488, 0.224418, 0.224417, 0.224354, 0.224348, 0.224291, 0.224278, 0.224221, 0.224208, 0.224166, 0.224165, 0.224095, 0.224095, 0.224025, 0.224025, 0.223955, 0.223955, 0.223885, 0.223885, 0.223815, 0.223815, 0.223752, 0.223745 ]) _dP_inline_Re_2 = np.array([0.225144, 0.225088, 0.225039, 0.224988, 0.224877, 0.224799, 0.22475, 0.224701, 0.224652, 0.224617, 0.224562, 0.224511, 0.224462, 0.224371, 0.224303, 0.224182, 0.224127, 0.224078, 0.224022, 0.223973, 0.223911, 0.223855, 0.223799, 0.22373, 0.223674, 0.223603, 0.223498, 0.223442, 0.223386, 0.223331, 0.223234, 0.223171, 0.223109, 0.223039, 0.222976, 0.222947, 0.222886, 0.222872, 0.222815, 0.222745, 0.222739, 0.22267, 0.222607, 0.222601, 0.222566, 0.222509, 0.222496, 0.222439, 0.22237, 0.222357, 0.222307, 0.222238, 0.222225, 0.222176, 0.222155, 0.222106, 0.222086, 0.222072, 0.222091, 0.224181, 0.224192, 0.224129, 0.224123, 0.224059, 0.224053, 0.223989, 0.223983, 0.225938, 0.226122, 0.226064, 0.226058, 0.225995, 0.22598, 0.225974, 0.22591, 0.225909, 0.225845, 0.225839, 0.225774, 0.225768, 0.225712, 0.225692, 0.225715, 0.227854, 0.227574, 0.225564, 0.225556, 0.225494, 0.225473, 0.225472, 0.225402, 0.225401, 0.225337, 0.225331, 0.224173, 0.223979, 0.221897, 0.220812, 0.220777, 0.220743, 0.219816, 0.218518, 0.217425, 0.215326, 0.214141, 0.212854, 0.209889, 0.209854, 0.207766, 0.207721, 0.204025, 0.20238, 0.199994, 0.196092, 0.194852, 0.192218, 0.189918, 0.189156, 0.188004, 0.185898, 0.184756, 0.184308, 0.182455, 0.18245, 0.182403, 0.182219, 0.180718, 0.18056, 0.17874, 0.178739, 0.178684, 0.178683, 0.178633, 0.178614, 0.176826, 0.176836, 0.178506, 0.17851, 0.17846, 0.178455, 0.178427, 0.178422, 0.178376, 0.178366, 0.178326, 0.17831, 0.17827, 0.178254, 0.178215, 0.178199, 0.179233, 0.179895, 0.179866, 0.179844, 0.179788, 0.179788, 0.179732, 0.179731, 0.179681, 0.179675, 0.179625, 0.179619, 0.179569, 0.179563, 0.179513, 0.179507, 0.179457, 0.179451, 0.179395, 0.179395, 0.179339, 0.179339, 0.179283, 0.179282, 0.179227, 0.179226, 0.179165, 0.179165, 0.179109, 0.179109, 0.179053, 0.179053, 0.179002, 0.178997, 0.178952, 0.178941, 0.178896, 0.178885, 0.178852, 0.178851, 0.178796, 0.178795, 0.17874, 0.178739, 0.178684, 0.178684, 0.178628, 0.178628, 0.178572, 0.178572, 0.178521, 0.178516 ]) _dP_inline_Re_25 = np.array([0.349884, 0.344353, 0.339587, 0.334753, 0.324384, 0.31723, 0.31284, 0.308509, 0.304238, 0.301224, 0.296579, 0.292359, 0.288312, 0.280944, 0.275511, 0.266235, 0.262027, 0.258398, 0.254314, 0.250794, 0.24643, 0.242534, 0.238699, 0.233991, 0.230291, 0.225667, 0.219023, 0.21556, 0.212151, 0.208795, 0.203116, 0.199504, 0.195956, 0.192086, 0.18867, 0.187117, 0.18384, 0.183136, 0.179837, 0.176255, 0.175964, 0.174204, 0.174155, 0.17415, 0.174122, 0.174078, 0.174068, 0.175436, 0.175686, 0.175676, 0.175636, 0.175582, 0.175571, 0.175532, 0.175516, 0.176657, 0.177193, 0.175451, 0.175475, 0.177126, 0.177125, 0.177076, 0.177071, 0.17702, 0.177015, 0.176965, 0.17696, 0.176915, 0.176905, 0.176859, 0.176855, 0.176805, 0.176793, 0.176789, 0.178483, 0.178481, 0.178431, 0.178426, 0.178375, 0.17837, 0.178326, 0.17831, 0.178309, 0.178259, 0.178253, 0.178209, 0.178203, 0.178154, 0.178137, 0.178136, 0.178082, 0.178081, 0.17803, 0.178026, 0.177997, 0.177992, 0.177941, 0.177208, 0.176296, 0.175343, 0.174528, 0.175213, 0.176039, 0.175988, 0.175263, 0.17421, 0.172454, 0.172453, 0.1724, 0.172399, 0.17235, 0.171731, 0.170592, 0.168894, 0.168351, 0.167192, 0.16716, 0.167139, 0.166121, 0.165455, 0.165443, 0.165435, 0.163918, 0.163771, 0.162422, 0.162121, 0.160642, 0.1605, 0.16361, 0.163528, 0.158824, 0.158823, 0.158779, 0.158774, 0.15872, 0.158736, 0.160236, 0.160219, 0.158765, 0.15862, 0.158595, 0.158591, 0.15855, 0.158541, 0.158506, 0.158492, 0.158456, 0.158442, 0.158407, 0.158392, 0.158362, 0.158343, 0.158313, 0.158293, 0.158244, 0.158257, 0.159755, 0.15974, 0.15815, 0.158145, 0.158101, 0.158095, 0.158051, 0.158046, 0.158002, 0.157996, 0.157952, 0.157947, 0.157898, 0.157898, 0.157849, 0.157848, 0.157799, 0.157799, 0.15775, 0.15775, 0.157696, 0.157695, 0.157646, 0.157646, 0.157597, 0.157597, 0.157552, 0.157548, 0.157508, 0.157499, 0.157459, 0.157449, 0.15742, 0.157419, 0.157371, 0.15737, 0.157321, 0.157321, 0.157272, 0.157272, 0.157223, 0.157223, 0.157174, 0.157173, 0.157129, 0.157125 ]) _dP_inline_Re_parameters = np.array([_dP_inline_Re_125, _dP_inline_Re_15, _dP_inline_Re_2, _dP_inline_Re_25]).T other_things = np.array([1.25, 1.5, 2, 2.5]) # do not delete dP_inline_f = RectBivariateSpline(_dP_inline_Res, other_things, _dP_inline_Re_parameters, kx = 3, ky = 3, s = 0.002) errors = [] for i, param in enumerate(_dP_inline_Res): for j, z in enumerate(other_things): original_value = _dP_inline_Re_parameters[i][j] interpolated = bisplev(param, z, dP_inline_f_tck) errors.append(abs(abs(original_value - interpolated) / original_value)) assert np.mean(errors) < 0.01 Bell_baffle_configuration_Fcs = np.array([0, 0.0138889, 0.0277778, 0.0416667, 0.0538194, 0.0659722, 0.100694, 0.114583, 0.126736, 0.140625, 0.152778, 0.166667, 0.178819, 0.192708, 0.215278, 0.227431, 0.241319, 0.255208, 0.267361, 0.28125, 0.295139, 0.340278, 0.354167, 0.366319, 0.380208, 0.394097, 0.402778, 0.416667, 0.430556, 0.444444, 0.475694, 0.489583, 0.503472, 0.517361, 0.53125, 0.545139, 0.560764, 0.574653, 0.588542, 0.625, 0.638889, 0.652778, 0.668403, 0.682292, 0.697917, 0.701389, 0.713542, 0.729167, 0.743056, 0.758681, 0.802083, 0.817708, 0.833333, 0.848958, 0.866319, 0.881944, 0.901042, 0.918403, 0.934028, 0.947917, 0.960069, 0.970486, 0.977431, 0.984375, 0.991319, 0.994792, 1 ]) Bell_baffle_configuration_Jcs = np.array([0.534317, 0.544632, 0.556665, 0.566983, 0.579014, 0.591045, 0.620271, 0.630589, 0.640904, 0.652937, 0.663252, 0.675286, 0.685601, 0.697635, 0.71483, 0.725145, 0.737179, 0.747497, 0.757812, 0.76813, 0.780163, 0.81627, 0.826588, 0.836903, 0.847221, 0.857539, 0.867848, 0.874734, 0.885052, 0.89537, 0.916012, 0.92633, 0.936648, 0.946966, 0.955568, 0.965886, 0.974492, 0.984809, 0.993412, 1.01578, 1.0261, 1.0347, 1.0433, 1.05362, 1.06223, 1.06052, 1.07083, 1.07944, 1.08804, 1.09664, 1.11731, 1.1242, 1.13109, 1.13798, 1.14487, 1.15004, 1.15522, 1.15354, 1.1467, 1.13815, 1.12787, 1.11588, 1.10388, 1.09017, 1.07474, 1.05759, 1.03015 ]) def test_baffle_correction_Bell(): Jc = baffle_correction_Bell(0.82) assert_close(Jc, 1.1258554691854046, 5e-4) # Check the match is reasonably good errs = np.array([(baffle_correction_Bell(float(Fc))-Jc)/Jc for Fc, Jc in zip(Bell_baffle_configuration_Fcs, Bell_baffle_configuration_Jcs)]) assert np.abs(errs).sum()/len(errs) < 1e-3 Jc = baffle_correction_Bell(0.1, "chebyshev") assert_close(Jc, 0.61868011359447) Jc = baffle_correction_Bell(0.82, "HEDH") assert_close(Jc, 1.1404) # Example in spreadsheet 02 - Heat Exchangers, tab Shell htc imperial, # Rules of Thumb for Chemical Engineers 5E Jc = baffle_correction_Bell(0.67292816689362900, method="HEDH") assert_close(1.034508280163413, Jc) def test_baffle_correction_Bell_fit(): from ht.conv_tube_bank import Bell_baffle_configuration_tck # 125 us to create. spl = splrep(Bell_baffle_configuration_Fcs, Bell_baffle_configuration_Jcs, s=8e-5) [assert_close1d(i, j) for (i, j) in zip(spl[:-1], Bell_baffle_configuration_tck[:-1])] Bell_baffle_configuration_obj = UnivariateSpline(Bell_baffle_configuration_Fcs, Bell_baffle_configuration_Jcs, s=8e-5) # import matplotlib.pyplot as plt # plt.plot(Bell_baffle_configuration_Fcs, Bell_baffle_configuration_Jcs) # pts = np.linspace(0, 1, 5000) # plt.plot(pts, [Bell_baffle_configuration_obj(i) for i in pts]) # plt.plot(pts, [0.55 + 0.72*i for i in pts]) # Serth and HEDH 3.3.6g misses the tip # plt.show() # Bell_baffle_leakage_x = np.array([0.0, 1e-5, 1e-4, 1e-3, 0.0037779, 0.00885994, 0.012644, 0.0189629, 0.0213694, 0.0241428, 0.0289313, 0.0339093, 0.0376628, 0.0425124, 0.0487152, 0.0523402, 0.0552542, 0.0614631, 0.0676658, 0.0719956, 0.0770838, 0.081302, 0.0885214, 0.0956308, 0.101638, 0.102145, 0.111508, 0.119266, 0.12261, 0.129155, 0.136778, 0.144818, 0.148914, 0.15592, 0.164774, 0.16868, 0.177552, 0.181501, 0.189224, 0.196087, 0.200557, 0.209209, 0.220317, 0.230683, 0.236096, 0.242525, 0.247198, 0.255653, 0.2591, 0.266228, 0.274193, 0.281732, 0.285993, 0.295601, 0.302042, 0.311269, 0.312575, 0.322107, 0.33016, 0.332909, 0.341261, 0.347109, 0.353899, 0.360408, 0.369312, 0.374301, 0.380413, 0.388831, 0.392836, 0.401746, 0.403961, 0.413723, 0.422502, 0.424825, 0.432931, 0.442274, 0.450602, 0.454815, 0.463804, 0.46923, 0.475645, 0.483563, 0.491432, 0.501277, 0.501713, 0.510247, 0.513193, 0.523506, 0.530019, 0.534607, 0.544912, 0.550679, 0.557212, 0.563826, 0.569142, 0.576997, 0.583585, 0.588979, 0.595518, 0.601215, 0.601702, 0.611585, 0.613221, 0.623417, 0.629753, 0.634211, 0.640009, 0.646851, 0.653971, 0.665084, 0.672758, 0.683136, 0.689056, 0.698932, 0.702129, 0.711523, 0.712532, 0.722415, 0.724566, 0.732996, 0.738886, 0.743614 ]) Bell_baffle_leakage_z_0 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.982615, 0.962505, 0.952607, 0.939987, 0.935206, 0.930216, 0.922288, 0.91564, 0.910813, 0.904659, 0.896788, 0.892188, 0.889224, 0.883885, 0.879147, 0.875888, 0.872059, 0.868884, 0.86345, 0.858585, 0.854816, 0.854561, 0.849863, 0.846402, 0.844911, 0.841261, 0.837352, 0.833765, 0.831938, 0.828814, 0.824864, 0.823122, 0.819164, 0.817403, 0.813958, 0.810877, 0.80887, 0.804985, 0.799998, 0.795344, 0.792913, 0.790046, 0.787961, 0.78419, 0.782652, 0.779473, 0.776134, 0.773108, 0.771208, 0.766569, 0.762947, 0.758832, 0.758249, 0.753997, 0.750695, 0.749592, 0.746005, 0.743396, 0.740368, 0.737464, 0.733493, 0.731267, 0.728541, 0.723847, 0.721761, 0.717786, 0.716798, 0.712444, 0.708528, 0.707492, 0.703876, 0.699709, 0.695994, 0.694115, 0.690105, 0.687685, 0.684824, 0.681292, 0.677782, 0.672841, 0.672691, 0.669838, 0.668675, 0.662925, 0.66002, 0.657973, 0.653377, 0.651026, 0.648404, 0.645491, 0.64312, 0.639616, 0.636677, 0.634271, 0.633926, 0.628263, 0.628045, 0.623637, 0.622907, 0.618359, 0.615533, 0.613545, 0.611204, 0.608458, 0.605055, 0.599743, 0.596076, 0.591447, 0.588806, 0.584179, 0.582574, 0.578371, 0.577921, 0.573513, 0.572554, 0.568793, 0.566166, 0.564057 ]) Bell_baffle_leakage_z_0_25 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.969362, 0.950324, 0.942087, 0.92833, 0.923091, 0.917463, 0.908263, 0.8987, 0.89149, 0.884407, 0.874926, 0.868404, 0.865482, 0.859179, 0.851926, 0.847458, 0.842356, 0.838126, 0.831234, 0.824993, 0.820171, 0.819781, 0.812734, 0.807844, 0.805761, 0.801747, 0.797071, 0.792124, 0.789555, 0.785317, 0.78038, 0.778202, 0.773138, 0.77066, 0.766058, 0.76223, 0.761802, 0.754362, 0.748168, 0.742388, 0.73989, 0.737023, 0.734092, 0.729014, 0.727092, 0.723117, 0.718675, 0.713975, 0.711407, 0.706049, 0.702306, 0.696944, 0.696185, 0.690717, 0.686227, 0.685001, 0.681275, 0.677607, 0.67354, 0.66991, 0.664945, 0.662097, 0.658262, 0.653372, 0.651139, 0.645344, 0.644356, 0.639733, 0.633126, 0.63202, 0.628405, 0.623295, 0.618256, 0.615613, 0.610601, 0.607588, 0.604035, 0.59965, 0.595292, 0.58984, 0.589599, 0.58484, 0.583239, 0.578639, 0.573864, 0.570568, 0.564821, 0.562249, 0.559118, 0.554969, 0.55186, 0.54748, 0.543806, 0.540727, 0.536551, 0.532912, 0.532604, 0.528196, 0.527417, 0.521732, 0.51779, 0.515024, 0.511791, 0.507996, 0.504098, 0.498013, 0.493805, 0.488017, 0.484304, 0.479275, 0.477805, 0.471912, 0.47135, 0.465839, 0.464639, 0.459938, 0.456295, 0.453329 ]) Bell_baffle_leakage_z_0_5 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.963548, 0.945291, 0.931697, 0.915513, 0.90935, 0.903126, 0.892379, 0.882357, 0.875546, 0.864662, 0.854734, 0.849292, 0.844917, 0.836477, 0.828194, 0.822412, 0.815618, 0.810932, 0.803692, 0.796563, 0.790539, 0.79003, 0.780769, 0.774098, 0.771163, 0.765418, 0.759681, 0.754353, 0.751784, 0.746512, 0.739848, 0.736908, 0.73023, 0.727831, 0.723525, 0.722361, 0.716403, 0.709498, 0.702106, 0.695343, 0.69183, 0.687797, 0.684784, 0.679126, 0.676934, 0.672463, 0.666468, 0.660794, 0.657587, 0.652229, 0.647674, 0.641886, 0.641039, 0.634661, 0.629571, 0.627846, 0.62156, 0.618, 0.614214, 0.609549, 0.603166, 0.599589, 0.595259, 0.589978, 0.587232, 0.580972, 0.579691, 0.574139, 0.567532, 0.566006, 0.560921, 0.553889, 0.548597, 0.545865, 0.539851, 0.536447, 0.532176, 0.526217, 0.52128, 0.514403, 0.514091, 0.509272, 0.507629, 0.500514, 0.49602, 0.49275, 0.485255, 0.481637, 0.477351, 0.472925, 0.46959, 0.46425, 0.459291, 0.456283, 0.452506, 0.448422, 0.448072, 0.440693, 0.439462, 0.433776, 0.429185, 0.42583, 0.422193, 0.417554, 0.412196, 0.404759, 0.399625, 0.392681, 0.38872, 0.382111, 0.379972, 0.374079, 0.373424, 0.366812, 0.365433, 0.360144, 0.355712, 0.352153 ]) Bell_baffle_leakage_z_0_75 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.952164, 0.932054, 0.918775, 0.898166, 0.89158, 0.884084, 0.873337, 0.862164, 0.849748, 0.841023, 0.827707, 0.821818, 0.817167, 0.80764, 0.798123, 0.79148, 0.784355, 0.778722, 0.769082, 0.759588, 0.752322, 0.751711, 0.742222, 0.733336, 0.730403, 0.724627, 0.716983, 0.709925, 0.706578, 0.701202, 0.694408, 0.691425, 0.684748, 0.681776, 0.675771, 0.66987, 0.665387, 0.658756, 0.650396, 0.642594, 0.63852, 0.633681, 0.630164, 0.623243, 0.620278, 0.614914, 0.608773, 0.60289, 0.599564, 0.592066, 0.587039, 0.580086, 0.579103, 0.571929, 0.565004, 0.562871, 0.556585, 0.552183, 0.547073, 0.542175, 0.535473, 0.531288, 0.526005, 0.518616, 0.51564, 0.509016, 0.507369, 0.500113, 0.493584, 0.491836, 0.485642, 0.477775, 0.471507, 0.468336, 0.461571, 0.457487, 0.452093, 0.445202, 0.43798, 0.428108, 0.42792, 0.424141, 0.421924, 0.414162, 0.409255, 0.405735, 0.397827, 0.393402, 0.387784, 0.382579, 0.378578, 0.372665, 0.367707, 0.363647, 0.359545, 0.35391, 0.353453, 0.346015, 0.344783, 0.336861, 0.331413, 0.328057, 0.323694, 0.318544, 0.312629, 0.303584, 0.297808, 0.289997, 0.285542, 0.279346, 0.275077, 0.267704, 0.266945, 0.259507, 0.257888, 0.251468, 0.246404, 0.242337 ]) Bell_baffle_leakage_z_1 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.934094, 0.899408, 0.88689, 0.864752, 0.855175, 0.846, 0.832233, 0.820088, 0.811664, 0.80142, 0.78835, 0.781726, 0.776483, 0.765313, 0.755385, 0.749603, 0.742282, 0.73553, 0.725889, 0.716396, 0.709586, 0.709021, 0.698593, 0.690044, 0.686691, 0.680127, 0.67261, 0.665553, 0.662016, 0.655963, 0.648118, 0.644201, 0.635976, 0.63258, 0.626768, 0.621075, 0.617208, 0.609726, 0.60012, 0.591542, 0.587468, 0.582048, 0.578005, 0.570583, 0.567619, 0.561455, 0.554463, 0.548789, 0.545376, 0.537063, 0.531409, 0.523465, 0.522319, 0.51407, 0.508009, 0.50576, 0.49867, 0.493705, 0.487867, 0.482187, 0.474436, 0.470146, 0.465546, 0.458712, 0.455268, 0.43738, 0.445348, 0.436779, 0.429227, 0.42748, 0.42131, 0.413109, 0.406841, 0.403448, 0.395933, 0.391023, 0.38459, 0.37863, 0.372074, 0.361413, 0.360967, 0.356072, 0.353486, 0.344855, 0.339953, 0.336008, 0.327035, 0.322011, 0.316393, 0.310588, 0.306074, 0.299769, 0.294481, 0.290318, 0.285397, 0.279684, 0.279195, 0.271757, 0.27035, 0.261526, 0.255964, 0.252562, 0.248199, 0.241338, 0.234956, 0.226232, 0.219861, 0.210806, 0.205706, 0.197321, 0.194638, 0.187568, 0.186719, 0.178183, 0.176295, 0.168945, 0.16388, 0.160321 ]) Bell_baffle_leakage_zs = np.array([Bell_baffle_leakage_z_0, Bell_baffle_leakage_z_0_25, Bell_baffle_leakage_z_0_5, Bell_baffle_leakage_z_0_75, Bell_baffle_leakage_z_1]).T Bell_baffle_leakage_z_values = np.array([0, .25, .5, .75, 1]) Bell_baffle_leakage_obj = RectBivariateSpline(Bell_baffle_leakage_x, Bell_baffle_leakage_z_values, Bell_baffle_leakage_zs, kx=3, ky=1, s=0.002) def test_baffle_leakage_Bell(): Jl = baffle_leakage_Bell(1, 1, 4) assert_close(Jl, 0.5159239501898142, rtol=1e-3) Jl = baffle_leakage_Bell(1, 1, 8) assert_close(Jl, 0.6820523047494141, rtol=1e-3) Jl = baffle_leakage_Bell(1, 3, 8) assert_close(Jl, 0.5906621282470395, rtol=1e-3) # Silent clipping Jl = baffle_leakage_Bell(1, .0001, .00001) assert_close(Jl, 0.16072739052053492) Jl = baffle_leakage_Bell(1, 3, 8, method="HEDH") assert_close(Jl, 0.5530236260777133) # Example in spreadsheet 02 - Heat Exchangers, tab Shell htc imperial, # Rules of Thumb for Chemical Engineers 5E # Has an error Jl = baffle_leakage_Bell(Ssb=5.5632369907320000000, Stb=4.7424109055909500, Sm=42.7842616174504, method="HEDH") assert_close(Jl, 0.6719386427830639) def test_baffle_leakage_Bell_refit(): from ht.conv_tube_bank import Bell_baffle_leakage_tck # Test refitting the data obj = RectBivariateSpline(Bell_baffle_leakage_x, Bell_baffle_leakage_z_values, Bell_baffle_leakage_zs, kx=3, ky=1, s=0.002) new_tck = obj.tck + obj.degrees [assert_close1d(i, j) for (i, j) in zip(Bell_baffle_leakage_tck[:-2], new_tck[:-2])] #import matplotlib.pyplot as plt #for ys in Bell_baffle_leakage_zs.T: # plt.plot(Bell_baffle_leakage_x, ys) #for z in Bell_baffle_leakage_z_values: # xs = np.linspace(min(Bell_baffle_leakage_x), max(Bell_baffle_leakage_x), 1000) # ys = np.clip(Bell_baffle_leakage_obj(xs, z), 0, 1) # plt.plot(xs, ys, '--') # #for z in Bell_baffle_leakage_z_values: # xs = np.linspace(min(Bell_baffle_leakage_x), max(Bell_baffle_leakage_x), 1000) # rs = z # rl = xs # ys = 0.44*(1.0 - rs) + (1.0 - 0.44*(1.0 - rs))*np.exp(-2.2*rl) # plt.plot(xs, ys, '--') def test_bundle_bypassing_Bell(): Jb = bundle_bypassing_Bell(0.5, 5, 25) assert_close(Jb, 0.8469611760884599, rtol=1e-3) Jb = bundle_bypassing_Bell(0.5, 5, 25, laminar=True) assert_close(Jb, 0.8327442867825271, rtol=1e-3) Jb = bundle_bypassing_Bell(0.99, 5, 25, laminar=True) assert_close(Jb, 0.7786963825447165, rtol=1e-3) Jb = bundle_bypassing_Bell(0.5, 5, 25, method="HEDH") assert_close(Jb, 0.8483210970579099) Jb = bundle_bypassing_Bell(0.5, 5, 25, method="HEDH", laminar=True) assert_close(0.8372305924553625, Jb) # Example in spreadsheet 02 - Heat Exchangers, tab Shell htc imperial, # Rules of Thumb for Chemical Engineers 5E Jb = bundle_bypassing_Bell(bypass_area_fraction=0.331946755407654, seal_strips=2, crossflow_rows=10.6516290726817, method="HEDH") assert_close(Jb, 0.8908547260332952) Bell_bundle_bypass_x = np.array([0.0, 1e-5, 1e-4, 1e-3, 0.0388568, 0.0474941, 0.0572083, 0.0807999, 0.0915735, 0.0959337, 0.118724, 0.128469, 0.134716, 0.142211, 0.146821, 0.156504, 0.162821, 0.169488, 0.178126, 0.185301, 0.194997, 0.200798, 0.210512, 0.212373, 0.221063, 0.222122, 0.228864, 0.232856, 0.238578, 0.242605, 0.250104, 0.257958, 0.262866, 0.268403, 0.273639, 0.280289, 0.284999, 0.291067, 0.295186, 0.30005, 0.309764, 0.312548, 0.31468, 0.320144, 0.323405, 0.328111, 0.33213, 0.333111, 0.33857, 0.341836, 0.343889, 0.349352, 0.351401, 0.35359, 0.359058, 0.361102, 0.366408, 0.370597, 0.375601, 0.379541, 0.382811, 0.386913, 0.392363, 0.39766, 0.401106, 0.401841, 0.410811, 0.412615, 0.419939, 0.421633, 0.42633, 0.431067, 0.434967, 0.440908, 0.444682, 0.450614, 0.45373, 0.457036, 0.462565, 0.464508, 0.47016, 0.47227, 0.477519, 0.480474, 0.482794, 0.486874, 0.490639, 0.492758, 0.499075, 0.501281, 0.506824, 0.5116, 0.51494, 0.52159, 0.52187, 0.530498, 0.532368, 0.537013, 0.541276, 0.542244, 0.546385, 0.551805, 0.553801, 0.5575, 0.562325, 0.56668, 0.568283, 0.572153, 0.576377, 0.580676, 0.582252, 0.5886, 0.591953, 0.599019, 0.601715, 0.602385, 0.610103, 0.612441, 0.613194, 0.62061, 0.622146, 0.622934, 0.630324, 0.631852, 0.633669, 0.637109, 0.64136, 0.644447, 0.647887, 0.649879, 0.652335, 0.656363, 0.657593, 0.661839, 0.665333, 0.667924, 0.672258, 0.674841, 0.678694, 0.681955, 0.685396, 0.688789, 0.69198, 0.69532 ]) Bell_bundle_bypass_x_max = float(Bell_bundle_bypass_x[-1]) Bell_bundle_bypass_z_values = np.array([0.0, 0.05, 0.1, 1.0 / 6.0, 0.3, 0.5]) Bell_bundle_bypass_z_high_0_5 = np.ones(144) Bell_bundle_bypass_z_high_0_3 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.990537, 0.988984, 0.98724, 0.983016, 0.980614, 0.979535, 0.974346, 0.972054, 0.970522, 0.968688, 0.967675, 0.965549, 0.964164, 0.963959, 0.963171, 0.961603, 0.959253, 0.959162, 0.957048, 0.956644, 0.954757, 0.954523, 0.9529, 0.95197, 0.950734, 0.949953, 0.951574, 0.949936, 0.947587, 0.946396, 0.945271, 0.943845, 0.942835, 0.941537, 0.940656, 0.940788, 0.942546, 0.940563, 0.939047, 0.935797, 0.935104, 0.934105, 0.933252, 0.933045, 0.931888, 0.931164, 0.930682, 0.9294, 0.929485, 0.929948, 0.931104, 0.931397, 0.928907, 0.926946, 0.925893, 0.925065, 0.924344, 0.923388, 0.922149, 0.92104, 0.92032, 0.920166, 0.918293, 0.917917, 0.917341, 0.917207, 0.916838, 0.916466, 0.916159, 0.915693, 0.915397, 0.914931, 0.914687, 0.914428, 0.913994, 0.913842, 0.91334, 0.912902, 0.911815, 0.911203, 0.91078, 0.910038, 0.909353, 0.908968, 0.907821, 0.907421, 0.906416, 0.905551, 0.904947, 0.903745, 0.903694, 0.902137, 0.9018, 0.900963, 0.900195, 0.900021, 0.899276, 0.898303, 0.897944, 0.897281, 0.896416, 0.895636, 0.895349, 0.894656, 0.893901, 0.893133, 0.892852, 0.89172, 0.891122, 0.889865, 0.889385, 0.889266, 0.887895, 0.88748, 0.887347, 0.887002, 0.887002, 0.887002, 0.886113, 0.885805, 0.88544, 0.884748, 0.883894, 0.883275, 0.882575, 0.882132, 0.881585, 0.880689, 0.880426, 0.879577, 0.878879, 0.878362, 0.878362, 0.878362, 0.878362, 0.877712, 0.877026, 0.87635, 0.875715, 0.875051 ]) Bell_bundle_bypass_z_high_0_167 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.98326, 0.97947, 0.974498, 0.962528, 0.957986, 0.956693, 0.949964, 0.947102, 0.945271, 0.94206, 0.94009, 0.935965, 0.93353, 0.932117, 0.928823, 0.925995, 0.923086, 0.921351, 0.918452, 0.917897, 0.915313, 0.914999, 0.913, 0.911818, 0.910127, 0.90895, 0.907403, 0.905106, 0.903391, 0.90146, 0.899637, 0.897328, 0.895696, 0.893598, 0.892176, 0.8905, 0.886812, 0.885691, 0.884834, 0.882399, 0.880948, 0.879769, 0.878966, 0.87877, 0.87685, 0.875407, 0.874501, 0.873182, 0.872775, 0.872342, 0.870581, 0.869774, 0.86768, 0.865848, 0.863665, 0.862771, 0.862131, 0.861322, 0.859193, 0.857129, 0.859086, 0.858609, 0.852897, 0.852509, 0.850934, 0.85034, 0.848528, 0.846705, 0.845041, 0.842545, 0.841823, 0.840689, 0.839677, 0.838418, 0.836305, 0.835485, 0.833106, 0.832278, 0.831286, 0.830728, 0.830291, 0.828583, 0.827011, 0.826114, 0.823157, 0.822169, 0.82102, 0.820047, 0.819426, 0.818189, 0.818085, 0.814886, 0.814194, 0.812289, 0.810543, 0.810058, 0.806263, 0.806263, 0.806263, 0.806137, 0.804373, 0.802783, 0.802256, 0.801473, 0.800619, 0.799812, 0.799526, 0.798328, 0.796926, 0.793982, 0.792861, 0.792583, 0.789808, 0.78897, 0.788701, 0.787226, 0.786921, 0.786757, 0.784122, 0.783578, 0.782932, 0.781709, 0.780202, 0.779109, 0.778433, 0.778042, 0.77756, 0.776422, 0.775988, 0.774494, 0.77333, 0.772824, 0.77198, 0.771442, 0.770094, 0.768954, 0.767753, 0.766571, 0.765461, 0.764301 ]) Bell_bundle_bypass_z_high_0_1 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.978035, 0.974378, 0.970282, 0.960405, 0.955928, 0.953958, 0.941171, 0.935756, 0.932301, 0.928172, 0.925642, 0.92035, 0.916913, 0.9133, 0.908641, 0.904789, 0.899741, 0.89745, 0.893627, 0.892897, 0.889494, 0.88908, 0.886716, 0.885913, 0.884594, 0.881903, 0.877493, 0.874369, 0.87224, 0.869806, 0.867741, 0.865076, 0.863023, 0.86048, 0.858872, 0.856977, 0.853205, 0.851584, 0.850211, 0.846705, 0.845452, 0.843647, 0.842058, 0.841641, 0.839327, 0.837996, 0.837215, 0.835141, 0.834364, 0.833443, 0.831147, 0.830291, 0.828293, 0.826718, 0.824687, 0.82305, 0.821515, 0.819223, 0.816189, 0.814075, 0.812703, 0.81241, 0.808849, 0.808135, 0.805242, 0.804574, 0.802726, 0.800866, 0.799338, 0.797016, 0.795545, 0.793199, 0.791952, 0.790633, 0.78865, 0.787955, 0.785378, 0.784125, 0.781018, 0.779971, 0.779149, 0.777707, 0.776379, 0.775632, 0.77341, 0.77338, 0.770144, 0.767521, 0.766358, 0.764048, 0.763944, 0.760626, 0.759946, 0.758344, 0.756878, 0.756543, 0.754964, 0.752903, 0.752217, 0.750955, 0.749311, 0.74768, 0.747075, 0.745618, 0.743505, 0.741332, 0.740537, 0.738255, 0.737132, 0.731632, 0.729296, 0.729296, 0.729296, 0.728522, 0.728273, 0.725825, 0.725318, 0.725059, 0.72263, 0.722122, 0.72146, 0.720209, 0.718666, 0.71766, 0.716539, 0.715891, 0.715086, 0.713635, 0.713192, 0.711666, 0.708853, 0.706773, 0.705828, 0.705414, 0.704797, 0.703715, 0.702494, 0.701293, 0.700165, 0.698986 ]) Bell_bundle_bypass_z_high_0_05 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.972281, 0.967922, 0.961369, 0.943692, 0.935729, 0.932525, 0.915956, 0.908961, 0.906104, 0.904563, 0.901473, 0.895196, 0.891354, 0.885977, 0.87906, 0.874187, 0.86913, 0.86655, 0.862245, 0.861423, 0.857594, 0.857129, 0.852769, 0.850462, 0.848255, 0.846705, 0.842424, 0.837963, 0.835187, 0.832066, 0.829126, 0.825407, 0.822783, 0.819415, 0.817095, 0.814308, 0.808771, 0.80719, 0.805982, 0.802895, 0.801058, 0.798414, 0.796163, 0.795615, 0.79257, 0.79081, 0.789705, 0.786773, 0.785555, 0.784255, 0.781018, 0.780293, 0.778416, 0.776757, 0.773823, 0.77152, 0.769804, 0.767657, 0.764814, 0.76206, 0.760275, 0.759852, 0.754714, 0.753788, 0.750038, 0.749171, 0.746514, 0.743844, 0.742476, 0.740476, 0.738142, 0.733741, 0.732227, 0.731129, 0.729296, 0.728224, 0.725118, 0.723961, 0.721379, 0.719929, 0.718793, 0.716592, 0.714554, 0.71341, 0.709585, 0.708255, 0.706445, 0.704915, 0.703256, 0.699727, 0.699579, 0.694462, 0.693873, 0.692411, 0.691072, 0.690566, 0.688406, 0.685632, 0.684701, 0.682979, 0.68071, 0.678471, 0.677649, 0.675704, 0.673763, 0.671794, 0.671073, 0.668927, 0.667797, 0.664237, 0.662887, 0.662584, 0.659112, 0.658063, 0.657689, 0.65401, 0.65325, 0.652861, 0.649222, 0.648472, 0.647937, 0.646926, 0.645678, 0.64442, 0.642745, 0.641777, 0.640586, 0.638832, 0.638297, 0.636454, 0.634836, 0.633593, 0.631519, 0.630382, 0.628731, 0.627336, 0.626066, 0.624995, 0.62399, 0.622939 ]) Bell_bundle_bypass_z_high_0 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.952236, 0.940656, 0.929217, 0.902172, 0.890997, 0.886514, 0.863444, 0.851755, 0.845079, 0.837139, 0.832293, 0.822203, 0.816984, 0.810801, 0.80192, 0.794615, 0.78485, 0.779066, 0.769592, 0.767791, 0.759517, 0.758605, 0.752824, 0.749047, 0.743669, 0.739906, 0.73295, 0.725735, 0.722154, 0.717987, 0.713174, 0.707108, 0.702842, 0.697384, 0.693703, 0.689382, 0.680999, 0.678318, 0.676273, 0.671537, 0.669333, 0.666165, 0.662801, 0.661983, 0.657447, 0.654748, 0.653057, 0.648578, 0.646907, 0.645126, 0.640517, 0.638664, 0.634016, 0.631344, 0.628167, 0.625058, 0.622488, 0.619125, 0.614363, 0.610288, 0.607796, 0.607265, 0.60083, 0.599544, 0.59421, 0.592943, 0.589445, 0.585503, 0.582277, 0.577936, 0.575196, 0.571767, 0.569973, 0.567464, 0.563036, 0.561619, 0.557635, 0.556155, 0.55249, 0.550438, 0.548878, 0.546625, 0.544554, 0.543231, 0.538071, 0.536281, 0.532469, 0.529276, 0.527497, 0.523935, 0.52375, 0.518089, 0.516762, 0.513373, 0.51047, 0.509884, 0.507382, 0.504126, 0.502932, 0.500727, 0.497867, 0.495143, 0.494144, 0.491733, 0.488799, 0.485831, 0.484868, 0.481006, 0.479285, 0.476413, 0.473514, 0.472869, 0.469205, 0.468011, 0.467512, 0.462626, 0.461732, 0.461273, 0.457, 0.456012, 0.45484, 0.452628, 0.450352, 0.448953, 0.447398, 0.446281, 0.444731, 0.442201, 0.44145, 0.439096, 0.437168, 0.435842, 0.433942, 0.432813, 0.430923, 0.429157, 0.427301, 0.425479, 0.423772, 0.421993 ]) Bell_bundle_bypass_z_high = np.array([Bell_bundle_bypass_z_high_0, Bell_bundle_bypass_z_high_0_05, Bell_bundle_bypass_z_high_0_1, Bell_bundle_bypass_z_high_0_167, Bell_bundle_bypass_z_high_0_3, Bell_bundle_bypass_z_high_0_5]).T Bell_bundle_bypass_high_obj = RectBivariateSpline(Bell_bundle_bypass_x, Bell_bundle_bypass_z_values, Bell_bundle_bypass_z_high, kx = 3, ky = 3, s = 0.0007) #import matplotlib.pyplot as plt #for ys in Bell_bundle_bypass_z_high.T: # plt.plot(Bell_bundle_bypass_x, ys) #for z in Bell_bundle_bypass_z_values: # xs = np.linspace(min(Bell_bundle_bypass_x), max(Bell_bundle_bypass_x), 1000) # ys = np.clip(Bell_bundle_bypass_high_obj(xs, z), 0, 1) # plt.plot(xs, ys, '--') #for z in Bell_bundle_bypass_z_values: # xs = np.linspace(min(Bell_bundle_bypass_x), max(Bell_bundle_bypass_x), 1000) # ys = np.exp(-1.25*xs*(1.0 - (2.0*z)**(1/3.) )) # This one is a good fit! # plt.plot(xs, ys, '.') #plt.show() Bell_bundle_bypass_z_low_0_5 = Bell_bundle_bypass_z_high_0_5 Bell_bundle_bypass_z_low_0_3 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.991796, 0.989982, 0.987945, 0.983016, 0.980614, 0.979535, 0.974346, 0.972054, 0.970522, 0.968688, 0.967675, 0.965549, 0.964164, 0.963959, 0.963171, 0.961603, 0.959253, 0.959162, 0.957048, 0.956644, 0.954757, 0.954523, 0.9529, 0.95197, 0.950734, 0.949953, 0.951574, 0.949936, 0.947587, 0.946396, 0.945271, 0.943845, 0.942835, 0.941537, 0.940656, 0.940788, 0.942546, 0.940563, 0.939047, 0.935797, 0.935104, 0.934105, 0.933252, 0.933045, 0.931888, 0.931164, 0.930682, 0.9294, 0.929485, 0.929948, 0.931104, 0.931397, 0.928907, 0.926946, 0.925893, 0.925065, 0.924344, 0.923388, 0.922112, 0.920852, 0.920034, 0.919859, 0.917732, 0.917305, 0.915572, 0.915172, 0.914063, 0.912946, 0.912028, 0.910631, 0.909744, 0.908352, 0.907622, 0.906848, 0.905555, 0.905101, 0.903781, 0.903289, 0.902066, 0.901379, 0.900839, 0.899919, 0.899149, 0.898717, 0.897483, 0.897083, 0.89608, 0.895216, 0.894613, 0.893412, 0.893362, 0.891807, 0.89147, 0.890635, 0.889868, 0.889694, 0.888923, 0.887829, 0.887427, 0.886681, 0.88571, 0.884834, 0.884477, 0.883613, 0.882672, 0.881954, 0.881691, 0.880632, 0.880073, 0.878897, 0.878448, 0.878332, 0.876793, 0.876328, 0.876178, 0.874703, 0.874398, 0.874242, 0.872631, 0.872295, 0.871914, 0.871488, 0.870962, 0.87058, 0.870155, 0.869909, 0.869486, 0.868691, 0.868448, 0.867611, 0.866922, 0.866412, 0.86556, 0.864996, 0.864155, 0.863444, 0.86277, 0.862105, 0.86148, 0.860827 ]) Bell_bundle_bypass_z_low_0_167 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.980974, 0.977905, 0.97446, 0.966143, 0.962368, 0.960686, 0.951112, 0.947048, 0.944452, 0.941346, 0.939441, 0.935452, 0.93269, 0.92946, 0.925293, 0.921845, 0.917207, 0.914443, 0.909833, 0.908959, 0.905975, 0.905612, 0.903304, 0.90194, 0.899989, 0.898618, 0.896071, 0.893412, 0.891754, 0.889887, 0.887916, 0.885239, 0.883183, 0.880493, 0.879259, 0.877803, 0.874903, 0.874074, 0.873134, 0.870731, 0.869578, 0.868649, 0.867856, 0.867642, 0.865256, 0.863831, 0.862988, 0.860849, 0.860049, 0.859186, 0.856524, 0.855531, 0.852959, 0.852139, 0.851171, 0.84986, 0.848459, 0.846705, 0.844612, 0.842583, 0.841212, 0.840919, 0.837359, 0.836645, 0.833751, 0.833084, 0.831743, 0.830749, 0.829968, 0.828849, 0.827989, 0.825515, 0.824217, 0.822981, 0.820918, 0.820193, 0.817941, 0.817102, 0.815018, 0.813871, 0.813014, 0.811509, 0.810137, 0.809474, 0.8075, 0.806811, 0.805085, 0.8036, 0.802563, 0.800503, 0.800417, 0.797752, 0.797175, 0.795746, 0.794422, 0.794073, 0.792581, 0.790633, 0.789837, 0.788364, 0.786627, 0.785849, 0.785563, 0.784873, 0.783229, 0.781532, 0.780917, 0.778551, 0.777304, 0.774683, 0.773686, 0.773438, 0.772069, 0.771659, 0.771527, 0.768654, 0.768059, 0.767753, 0.765181, 0.764651, 0.76402, 0.76335, 0.762532, 0.76154, 0.75956, 0.758417, 0.757994, 0.757301, 0.757089, 0.75611, 0.754779, 0.753793, 0.752544, 0.752102, 0.751445, 0.750747, 0.749575, 0.748421, 0.747337, 0.746205 ]) Bell_bundle_bypass_z_low_0_1 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.978947, 0.974857, 0.970278, 0.959247, 0.954251, 0.952236, 0.938267, 0.932356, 0.928587, 0.924085, 0.921326, 0.915559, 0.911816, 0.907882, 0.902811, 0.89862, 0.892988, 0.889635, 0.885582, 0.884834, 0.879037, 0.878345, 0.87566, 0.874074, 0.87124, 0.869251, 0.86556, 0.862478, 0.860473, 0.858072, 0.85515, 0.850859, 0.849041, 0.846705, 0.844334, 0.841542, 0.835995, 0.834411, 0.833976, 0.832942, 0.832325, 0.829367, 0.82685, 0.826237, 0.824191, 0.82297, 0.822203, 0.81994, 0.819093, 0.818189, 0.815149, 0.814015, 0.81124, 0.809258, 0.806898, 0.805045, 0.80351, 0.801588, 0.799042, 0.796575, 0.794975, 0.794634, 0.791377, 0.790729, 0.787823, 0.78715, 0.784863, 0.782599, 0.781214, 0.779109, 0.776888, 0.77341, 0.772317, 0.771158, 0.769179, 0.768425, 0.766237, 0.765263, 0.762533, 0.761, 0.759834, 0.757882, 0.756085, 0.755076, 0.752075, 0.751029, 0.749142, 0.747519, 0.746277, 0.743778, 0.743677, 0.740769, 0.74014, 0.737582, 0.735207, 0.73467, 0.733289, 0.731487, 0.730713, 0.728963, 0.726686, 0.724636, 0.723901, 0.722489, 0.720951, 0.719026, 0.718255, 0.715157, 0.713949, 0.711376, 0.710288, 0.710018, 0.706915, 0.705978, 0.705676, 0.702713, 0.7021, 0.701786, 0.698849, 0.698244, 0.697524, 0.696164, 0.694821, 0.693848, 0.692765, 0.691722, 0.690438, 0.688338, 0.687698, 0.686042, 0.684684, 0.683677, 0.681998, 0.680999, 0.680403, 0.679899, 0.679368, 0.677706, 0.676072, 0.674366 ]) Bell_bundle_bypass_z_low_0_05 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.97132, 0.966107, 0.959971, 0.942755, 0.934996, 0.931875, 0.915726, 0.908906, 0.906104, 0.904563, 0.901473, 0.895196, 0.891354, 0.885977, 0.87906, 0.874187, 0.867386, 0.86321, 0.856262, 0.854938, 0.84878, 0.848124, 0.843964, 0.841509, 0.838004, 0.835546, 0.830988, 0.826241, 0.823289, 0.81997, 0.816844, 0.812892, 0.810104, 0.806526, 0.804106, 0.801259, 0.795601, 0.793967, 0.792688, 0.789419, 0.787596, 0.785077, 0.782932, 0.782351, 0.779127, 0.777205, 0.776119, 0.773238, 0.77216, 0.770953, 0.767771, 0.766585, 0.763514, 0.761099, 0.758428, 0.756396, 0.754714, 0.752376, 0.749281, 0.745922, 0.743739, 0.743322, 0.738296, 0.737388, 0.73372, 0.732874, 0.730275, 0.727663, 0.725519, 0.722266, 0.720207, 0.716983, 0.715295, 0.713509, 0.710531, 0.709487, 0.70646, 0.705709, 0.703842, 0.702816, 0.702076, 0.700776, 0.699579, 0.698012, 0.693361, 0.691743, 0.687698, 0.686208, 0.685168, 0.682279, 0.682134, 0.677697, 0.676739, 0.674366, 0.671761, 0.671172, 0.668654, 0.666449, 0.665778, 0.664536, 0.662357, 0.660396, 0.659676, 0.657624, 0.655391, 0.653126, 0.652298, 0.648972, 0.647223, 0.643551, 0.642155, 0.64196, 0.639714, 0.639035, 0.638682, 0.635109, 0.634371, 0.633993, 0.63046, 0.629731, 0.628867, 0.627232, 0.625218, 0.62376, 0.622139, 0.621202, 0.62005, 0.618248, 0.617731, 0.615947, 0.614484, 0.61328, 0.611273, 0.61008, 0.608305, 0.606806, 0.605229, 0.603678, 0.602222, 0.600702 ]) Bell_bundle_bypass_z_low_0 = np.array([1.0, 0.99999, 0.9999, 0.999, 0.952236, 0.940656, 0.929217, 0.90002, 0.886521, 0.880701, 0.850893, 0.838458, 0.831886, 0.823549, 0.818189, 0.807989, 0.801404, 0.794512, 0.78485, 0.776988, 0.766488, 0.760275, 0.751029, 0.749052, 0.740111, 0.739124, 0.732874, 0.729198, 0.723961, 0.720158, 0.713129, 0.705842, 0.701326, 0.696132, 0.690988, 0.684186, 0.679334, 0.67352, 0.66971, 0.665448, 0.657018, 0.654621, 0.652811, 0.648334, 0.645676, 0.641432, 0.637791, 0.636967, 0.632602, 0.630005, 0.628212, 0.623369, 0.621616, 0.619905, 0.61565, 0.61403, 0.609576, 0.606083, 0.601936, 0.598691, 0.596011, 0.592666, 0.588251, 0.583992, 0.581238, 0.580668, 0.574145, 0.572724, 0.566812, 0.565183, 0.56069, 0.556978, 0.55452, 0.550223, 0.547289, 0.543116, 0.540988, 0.538616, 0.534414, 0.532944, 0.528694, 0.527116, 0.523265, 0.521112, 0.519429, 0.516481, 0.514038, 0.512668, 0.508511, 0.507017, 0.503284, 0.500089, 0.497867, 0.493714, 0.49354, 0.487757, 0.486467, 0.482972, 0.479717, 0.478981, 0.476477, 0.473881, 0.472928, 0.470456, 0.467252, 0.464687, 0.46375, 0.461495, 0.458195, 0.45486, 0.453935, 0.45032, 0.448303, 0.443758, 0.442035, 0.441609, 0.437337, 0.436027, 0.435562, 0.431011, 0.430169, 0.429742, 0.425761, 0.424942, 0.423971, 0.422137, 0.42001, 0.418705, 0.417255, 0.416416, 0.414108, 0.41035, 0.409842, 0.408091, 0.406656, 0.405331, 0.402949, 0.401536, 0.399438, 0.39767, 0.395938, 0.394249, 0.392668, 0.391019 ]) Bell_bundle_bypass_z_low = np.array([Bell_bundle_bypass_z_low_0, Bell_bundle_bypass_z_low_0_05, Bell_bundle_bypass_z_low_0_1, Bell_bundle_bypass_z_low_0_167, Bell_bundle_bypass_z_low_0_3, Bell_bundle_bypass_z_low_0_5]).T Bell_bundle_bypass_low_obj = RectBivariateSpline(Bell_bundle_bypass_x, Bell_bundle_bypass_z_values, Bell_bundle_bypass_z_low, kx = 3, ky = 3, s = 0.0007) #for ys in Bell_bundle_bypass_z_low.T: # plt.plot(Bell_bundle_bypass_x, ys) # #for z in Bell_bundle_bypass_z_values: # xs = np.linspace(min(Bell_bundle_bypass_x), max(Bell_bundle_bypass_x), 1000) # ys = np.clip(Bell_bundle_bypass_low_obj(xs, z), 0, 1) # plt.plot(xs, ys, '--') #plt.show() def test_bundle_bypassing_Bell(): from ht.conv_tube_bank import Bell_bundle_bypass_high_spl, Bell_bundle_bypass_low_spl low_spl = Bell_bundle_bypass_low_obj.tck + Bell_bundle_bypass_low_obj.degrees high_spl = Bell_bundle_bypass_high_obj.tck + Bell_bundle_bypass_high_obj.degrees [assert_close1d(i, j) for i, j in zip(Bell_bundle_bypass_high_spl[:-2], high_spl[:-2])] [assert_close1d(i, j) for i, j in zip(Bell_bundle_bypass_low_spl[:-2], low_spl[:-2])] def test_unequal_baffle_spacing_Bell(): Js = unequal_baffle_spacing_Bell(16, .1, .15, 0.15) assert_close(Js, 0.9640087802805195) def test_laminar_correction_Bell(): Jr = laminar_correction_Bell(30.0, 80) assert_close(Jr, 0.7267995454361379) assert_close(0.4, laminar_correction_Bell(30, 80000)) ================================================ FILE: tests/test_conv_two_phase.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close from ht import Aggour, Davis_David, Elamvaluthi_Srinivas, Groothuis_Hendal, Hughmark, Knott, Kudirka_Grosh_McFadden, Martin_Sims, Ravipudi_Godbold, h_two_phase def test_Davis_David(): h = Davis_David(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mul=1E-3) assert_close(h, 1437.3282869955121) def test_Elamvaluthi_Srinivas(): h = Elamvaluthi_Srinivas(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) assert_close(h, 3901.2134471578584) def test_Groothuis_Hendal(): h = Groothuis_Hendal(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) assert_close(h, 1192.9543445455754) h = Groothuis_Hendal(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3, water=True) assert_close(h, 6362.8989677634545) def test_Hughmark(): h = Hughmark(m=1., x=.9, D=.3, L=.5, alpha=.9, Cpl=2300., kl=0.6, mu_b=1E-3, mu_w=1.2E-3) assert_close(h, 212.7411636127175) def test_Knott(): h = Knott(m=1., x=.9, D=.3, rhol=1000, rhog=2.5, Cpl=2300., kl=.6, mu_b=1E-3, mu_w=1.2E-3, L=4.) assert_close(h, 4225.536758045839) def test_Kudirka_Grosh_McFadden(): h = Kudirka_Grosh_McFadden(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) assert_close(h, 303.9941255903587) def test_Martin_Sims(): h = Martin_Sims(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, hl=141.2) assert_close(h, 5563.280000000001) h = Martin_Sims(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mu_b=1E-3, mu_w=1.2E-3, L=24.) assert_close(h, 5977.505465781747) def test_Ravipudi_Godbold(): h = Ravipudi_Godbold(m=1., x=.9, D=.3, rhol=1000., rhog=2.5, Cpl=2300., kl=.6, mug=1E-5, mu_b=1E-3, mu_w=1.2E-3) assert_close(h, 299.3796286459285) def test_Aggour(): h = Aggour(m=1., x=.9, D=.3, alpha=.9, rhol=1000., Cpl=2300., kl=.6, mu_b=1E-3) assert_close(h, 420.9347146885667) h = Aggour(m=.1, x=.9, D=.3, alpha=.9, rhol=1000., Cpl=2300., kl=.6, mu_b=1E-3, mu_w=1.2E-3, L=4.) assert_close(h, 33.64542760558181) def test_h_two_phase(): h = h_two_phase(m=1., x=.9, D=.3, alpha=.9, rhol=1000., Cpl=2300., kl=.6, mu_b=1E-3, mu_w=1.2E-3, L=5., method="Aggour") assert_close(h, 420.9347146885667) ================================================ FILE: tests/test_core.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close from ht import LMTD, countercurrent_hx_temperature_check, fin_efficiency_Kern_Kraus, wall_factor from ht.core import WALL_FACTOR_PRANDTL, WALL_FACTOR_TEMPERATURE, WALL_FACTOR_VISCOSITY, is_heating_property, is_heating_temperature def test_core(): dT = LMTD(100., 60., 30., 40.2) assert_close(dT, 43.200409294131525) dT = LMTD(100., 60., 30., 40.2, counterflow=False) assert_close(dT, 39.75251118049003) assert LMTD(100., 60., 20., 60) == 40 assert LMTD(100., 60., 20., 60, counterflow=False) == 0 """Test code for limits from sympy import * Thi, Tho, Tci, Tco = symbols('Thi, Tho, Tci, Tco') Thi = 100 Tho=60 Tci=20 dTF1 = Thi-Tco dTF2 = Tho-Tci expression = (dTF2 - dTF1)/log(dTF2/dTF1) limit(expression, Tco, 60) # evaluates to 40 # Numerical check - goes to zero Thi = 100 Tho=60 Tci=20 dTF1 = Thi-Tci dTF2 = Tho-Tco expression = (dTF2 - dTF1)/log(dTF2/dTF1) N(limit(expression, Tco, Rational('60')-Rational('1e-50000'))) limit(expression, Tco, 60) # evaluates to zero """ def test_is_heating_temperature(): assert is_heating_temperature(T=200, T_wall=500) assert not is_heating_temperature(T=400, T_wall=200) # not heating when 400 K assert not is_heating_temperature(T=400, T_wall=400) def test_is_heating_property(): T1, T2 = 280, 330 # C1, C2 = Chemical('hexane', T=T1), Chemical('hexane', T=T2) # mu1, mu2 = C1.mu, C2.mu # Pr1, Pr2 = C1.Pr, C2.Pr mu1, mu2 = 0.0003595695325135477, 0.0002210964201834834 Pr1, Pr2 = 6.2859707150337805, 4.810661011475006 assert is_heating_property(prop=mu1, prop_wall=mu2) assert is_heating_property(prop=Pr1, prop_wall=Pr2) # Equal temperatures - not heating in that case T1, T2 = 280, 280 mu1, mu2 = 0.0003595695325135477, 0.0003595695325135477 Pr1, Pr2 = 6.2859707150337805, 6.2859707150337805 assert not is_heating_property(prop=mu1, prop_wall=mu2) assert not is_heating_property(prop=Pr1, prop_wall=Pr2) # Lower wall temperatures - not heating in that case T1, T2 = 280, 260 mu1, mu2 = 0.0003595695325135477, 0.0004531397378208441 Pr1, Pr2 = 6.2859707150337805, 7.27333944072039 assert not is_heating_property(prop=mu1, prop_wall=mu2) assert not is_heating_property(prop=Pr1, prop_wall=Pr2) def test_wall_factor(): # Only one value provided with pytest.raises(Exception): wall_factor(mu=1, property_option=WALL_FACTOR_VISCOSITY) with pytest.raises(Exception): wall_factor(mu_wall=1, property_option=WALL_FACTOR_VISCOSITY) with pytest.raises(Exception): wall_factor(Pr=1, property_option=WALL_FACTOR_PRANDTL) with pytest.raises(Exception): wall_factor(Pr_wall=1, property_option=WALL_FACTOR_PRANDTL) with pytest.raises(Exception): wall_factor(T=1, property_option=WALL_FACTOR_TEMPERATURE) with pytest.raises(Exception): wall_factor(T_wall=1, property_option=WALL_FACTOR_TEMPERATURE) def test_fin_efficiency_Kern_Kraus(): assert type(fin_efficiency_Kern_Kraus(0.0254, 0.05715, 3.8E-4, 200, 58)) is float eta = fin_efficiency_Kern_Kraus(0.0254, 0.05715, 3.8E-4, 200, 58) assert_close(eta, 0.8412588620231153) """Code for comparing against several formulas: def fin_efficiency_Kern_Kraus(Do, Dfin, fin_thickness, kfin, h): # Should now be about 50/50 function vs special function kf = kfin tf = fin_thickness re = Dfin/2. ro = Do/2. m = (2.0*h/(kf*tf))**0.5 mre = m*re mro = m*ro x0 = i1(mre) x1 = k1(mre) num = x0*k1(mro) - x1*i1(mro) den = i0(mro)*x1 + x0*k0(mro) # num = i1(m*re)*k1(m*ro) - k1(m*re)*i1(m*ro) # den = i0(m*ro)*k1(m*re) + i1(m*re)*k0(m*ro) # num = iv(1, m*re)*kn(1, m*ro) - kn(1, m*re)*iv(1, m*ro) # den = iv(0, m*ro)*kn(1, m*re) + iv(1, m*re)*kn(0, m*ro) # print(num/den) # r2c = re # r1 = ro # num = kn(1, m*r1)*iv(1, m*r2c) - iv(1, m* r1)*kn(1, m*r2c) # den = iv(0, m*r1)*kn(1, m*r2c) + kn(0, m*r1)*iv(1, m*r2c) # print(num/den) eta = 2.0*ro/(m*(re*re - ro*ro))*num/den # r2c = r2, r1 = ro return eta # Confirmed with Introduction to Heat Transfer # To create a pade approximation of this, it would require f(m, re, ro). Not worth it. """ def test_countercurrent_hx_temperature_check(): assert not countercurrent_hx_temperature_check(T0i=500, T0o=466, T1i=348, T1o=329) assert not countercurrent_hx_temperature_check(T0i=453, T0o=466, T1i=310, T1o=329) assert not countercurrent_hx_temperature_check(T0i=453, T0o=466, T1i=348, T1o=329) assert countercurrent_hx_temperature_check(T0i=500, T0o=466, T1i=310, T1o=329) ================================================ FILE: tests/test_hx.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from math import exp, factorial, isnan, sqrt, tanh from random import choice, randint, seed, uniform import numpy as np import pytest from fluids.numerics import assert_close, assert_close1d, assert_close2d import ht from ht import ( LMTD, D_for_Ntubes_VDI, DBundle_for_Ntubes_HEDH, DBundle_for_Ntubes_Phadkeb, DBundle_min, F_LMTD_Fakheri, L_unsupported_max, NTU_from_effectiveness, NTU_from_P_basic, NTU_from_P_E, NTU_from_P_G, NTU_from_P_H, NTU_from_P_J, NTU_from_P_plate, Ntubes, Ntubes_HEDH, Ntubes_Perrys, Ntubes_Phadkeb, Ntubes_VDI, P_NTU_method, effectiveness_from_NTU, effectiveness_NTU_method, shell_clearance, size_bundle_from_tubecount, temperature_effectiveness_air_cooler, temperature_effectiveness_basic, temperature_effectiveness_plate, temperature_effectiveness_TEMA_E, temperature_effectiveness_TEMA_G, temperature_effectiveness_TEMA_H, temperature_effectiveness_TEMA_J, ) seed(0) def test_Ntubes_Perrys(): Nt_perry = [[Ntubes_Perrys(DBundle=1.184, Ntp=i, Do=.028, angle=j) for i in [1,2,4,6]] for j in [30, 45, 60, 90]] Nt_values = [[1001, 973, 914, 886], [819, 803, 784, 769], [1001, 973, 914, 886], [819, 803, 784, 769]] assert_close2d(Nt_perry, Nt_values) # angle = 30 or 60 and ntubes = 1.5 raise exception with pytest.raises(Exception): Ntubes_Perrys(DBundle=1.184, Ntp=5, Do=.028, angle=30) with pytest.raises(Exception): Ntubes_Perrys(DBundle=1.184, Ntp=5, Do=.028, angle=45) def test_Ntubes_Phadkeb(): # For the 45 degree case, ten exanples are given and known to be correct. # Unfortunately no examples were given for any other case. Ntubes_calc = [Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=i, angle=45.) for i in [1,2,4,6,8]] assert_close1d(Ntubes_calc, [805, 782, 760, 698, 680]) Ntubes_calc = [Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.035, Ntp=i, angle=45.) for i in [1,2,4,6,8]] assert_close1d(Ntubes_calc, [861, 838, 816, 750, 732]) # Extra tests N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=2, angle=30.) assert N == 898 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=2, angle=60.) assert N == 876 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=6, angle=60.) assert N == 822 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=8, angle=60.) assert N == 772 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.092, Ntp=8, angle=60.) assert N == 88 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=8, angle=30.) assert N == 788 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.04, Ntp=6, angle=30.) assert N == 652 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=8, angle=90.) assert N == 684 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=2, angle=90.) assert N == 772 N = Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=6, angle=90.) assert N == 712 # Big case N = Ntubes_Phadkeb(DBundle=5, Do=.028, pitch=.036, Ntp=2, angle=90.) assert N == 14842 # negative case N = Ntubes_Phadkeb(DBundle=0.004750018463796297, Do=.001, pitch=.0015, Ntp=8, angle=60) assert N == 0 # reverse case # DBundle_for_Ntubes_Phadkeb(Ntubes=17546, Do=.001, pitch=.00125, Ntp=6, angle=45) 0.19052937784048926 with pytest.raises(Exception): Ntubes_Phadkeb(DBundle=1.008, Do=.028, pitch=.036, Ntp=11, angle=45.) # Test the case of too small for anything assert 0 == Ntubes_Phadkeb(DBundle=.01, Do=.028, pitch=.036, Ntp=2, angle=45.) def test_Ntubes_Phadkeb_fuzz(): seed(100) D_main = 1E-3 for angle in [30, 45, 60, 90]: for Ntp in [1, 2, 4, 6, 8]: for pitch_ratio in [1.25, 1.31, 1.33, 1.375, 1.4, 1.42, 1.5]: pitch = D_main*pitch_ratio for _ in range(10): DBundle = uniform(pitch*2, pitch*300) N = Ntubes_Phadkeb(DBundle=DBundle, Do=D_main, pitch=pitch, Ntp=Ntp, angle=angle) # Test the reverse correlation D_main = 1E-2 for angle in [30, 45, 60, 90]: for Ntp in [1, 2, 4, 6, 8]: for pitch_ratio in [1.25, 1.31, 1.33, 1.375, 1.4, 1.42, 1.5]: pitch = D_main*pitch_ratio DBundle = uniform(pitch*5, pitch*300) N = Ntubes_Phadkeb(DBundle=DBundle, Do=D_main, pitch=pitch, Ntp=Ntp, angle=angle) if N > 2: DBundle2 = DBundle_for_Ntubes_Phadkeb(Ntubes=N, Do=D_main, pitch=pitch, Ntp=Ntp, angle=angle) assert type(DBundle2) is float N2 = Ntubes_Phadkeb(DBundle=DBundle2, Do=D_main, pitch=pitch, Ntp=Ntp, angle=angle) assert N2 == N @pytest.mark.slow def test_Phadkeb_numbers(): # One pain point of this code is that it takes 880 kb to store the results # in memory as a list ht.hx._load_coeffs_Phadkeb() from math import ceil, floor, sqrt from ht.hx import square_C1s, square_Ns, triangular_C1s, triangular_Ns # Triangular Ns # https://oeis.org/A003136 # Translated expression originally in Wolfram Mathematica # nn = 14; Select[Union[Flatten[Table[x^2 + x*y + y^2, {x, 0, nn}, {y, 0, x}]]], # <= nn^2 &] (* T. D. Noe, Apr 18 2011 *) nums = [] nn = 400 # Increase this to generate more numbers for x in range(nn+1): for y in range(x+1): nums.append(x*x + x*y + y*y) nums = sorted(set(nums)) nn_square = nn*nn nums = [i for i in nums if i < nn_square] nums = nums[0:len(triangular_Ns)] assert_close1d(nums, triangular_Ns) # triangular C1s # https://oeis.org/A038590 is the sequence, and it is the unique numbers in: # https://oeis.org/A038589 # Translated from pari expression a(n)=1+6*sum(k=0, n\3, (n\(3*k+1))-(n\(3*k+2))) # Tested with the online interpreter http://pari.math.u-bordeaux.fr/gp.html # This one is very slow, 300 seconds+ # Used to be 300 + seconds, now 50+ seconds def a(n): tot = 0 for k in range(ceil(n/3.)): # numpy could do this k3 = k*3. tot += floor(n/(k3 + 1.)) - floor(n/(k3 + 2.)) return 1 + int(6*tot) s = set() len_triangular_C1s = len(triangular_C1s) i = 0 while len(s) < len_triangular_C1s: val = a(i) s.update([val]) i += 1 ans2 = sorted(s) assert np.all(ans2[0:len(triangular_C1s)] == triangular_C1s) # square Ns # https://oeis.org/A001481 # Quick and efficient # Translated from Mathematica # up to = 160; With[{max = Ceiling[Sqrt[upTo]]}, Select[Union[Total /@ (Tuples[Range[0, max], {2}]^2)], # <= upTo &]] (* Harvey P. Dale, Apr 22 2011 *) # 10 loops, best of 3: 17.3 ms per loop # Confirmed with SymPy up_to = 100000 max_range = ceil(up_to**0.5) squares = [i*i for i in range(max_range+1)] seq = [i+j for i in squares for j in squares] seq = [i for i in set(seq) if i < up_to] # optional seq.sort() # on PyPy, the [i+j for i in squares for j in squares] look yields results in a different order # so we need to sort the result nums = seq[0:len(square_Ns)] assert_close1d(nums, square_Ns) # square C1s # https://oeis.org/A057961 is the sequence, there is one mathematica expression # but it needs SymPy or some hard work to be done # It is also the uniqiue elements in https://oeis.org/A057655 # That has a convenient expression for pari, tested online and translated # a(n)=1+4*sum(k=0, sqrtint(n), sqrtint(n-k^2) ); /* Benoit Cloitre, Oct 08 2012 */ # Currently 1.8 seconds # No numerical issues up to 35000 (confirmed with SymPy to do the root, int) sqrtint = lambda i: int(sqrt(i)) def a2(n): # numpy would be good at this rtf = sqrtint tot = 0.0 for k in range(sqrtint(n) + 1): tot += rtf(n - k*k) return 1 + 4*tot ans = {a2(i) for i in range(35000)} ans = sorted(ans) nums = ans[0:len(square_C1s)] assert_close1d(nums, square_C1s) def test_Ntubes_HEDH(): Ntubes_HEDH_c = [Ntubes_HEDH(DBundle=1.200-.008*2, Do=.028, pitch=.036, angle=i) for i in [30, 45, 60, 90]] assert_close1d(Ntubes_HEDH_c, [928, 804, 928, 804]) with pytest.raises(Exception): # unsuported angle Ntubes_HEDH(DBundle=1.200-.008*2, Do=.028, pitch=.036, angle=20) with pytest.raises(Exception): # unsuported angle DBundle_for_Ntubes_HEDH(N=100, Do=.028, pitch=.036, angle=20) # Fuzzing test Do = 0.028 for angle in [30, 45, 60, 90]: for pitch_ratio in [1.01, 1.1, 1.175, 1.25, 1.5, 1.75, 2]: pitch = Do*pitch_ratio for i in range(100): N = int(uniform(10, 10000)) DBundle = DBundle_for_Ntubes_HEDH(N=N, Do=Do, pitch=pitch, angle=angle) # If we don't increase the bundle by a hair, int() will round the wrong way N_recalculated = Ntubes_HEDH(DBundle=DBundle*(1+1E-12), Do=Do, pitch=pitch, angle=angle) assert N == N_recalculated def test_Ntubes_VDI(): VDI_t = [[Ntubes_VDI(DBundle=1.184, Ntp=i, Do=.028, pitch=.036, angle=j) for i in [1,2,4,6,8]] for j in [30, 45, 60, 90]] VDI_values = [[983, 966, 929, 914, 903], [832, 818, 790, 778, 769], [983, 966, 929, 914, 903], [832, 818, 790, 778, 769]] assert_close2d(VDI_t, VDI_values) with pytest.raises(Exception): Ntubes_VDI(DBundle=1.184, Ntp=5, Do=.028, pitch=.036, angle=30) with pytest.raises(Exception): Ntubes_VDI(DBundle=1.184, Ntp=2, Do=.028, pitch=.036, angle=40) D_VDI = [[D_for_Ntubes_VDI(N=970, Ntp=i, Do=0.00735, pitch=0.015, angle=j) for i in [1, 2, 4, 6, 8]] for j in [30, 60, 45, 90]] D_VDI_values = [[0.489981989464919, 0.5003600119829544, 0.522287673753684, 0.5311570964003711, 0.5377131635291736], [0.489981989464919, 0.5003600119829544, 0.522287673753684, 0.5311570964003711, 0.5377131635291736], [0.5326653264480428, 0.5422270203444146, 0.5625250342473964, 0.5707695340997739, 0.5768755899087357], [0.5326653264480428, 0.5422270203444146, 0.5625250342473964, 0.5707695340997739, 0.5768755899087357]] assert_close2d(D_VDI, D_VDI_values) with pytest.raises(Exception): D_for_Ntubes_VDI(N=970, Ntp=5., Do=0.00735, pitch=0.015, angle=30.) with pytest.raises(Exception): D_for_Ntubes_VDI(N=970, Ntp=2., Do=0.00735, pitch=0.015, angle=40.) def test_Ntubes(): methods = ["Phadkeb", "HEDH", "VDI", "Perry"] Ntubes_calc = [Ntubes(DBundle=1.2, Do=0.025, pitch=.025*1.25, Method=i) for i in methods] assert Ntubes_calc == [1285, 1272, 1340, 1297] assert_close(Ntubes(DBundle=1.2, Do=0.025, pitch=.025*1.25), 1285) with pytest.raises(Exception): Ntubes(DBundle=1.2, Do=0.025, pitch=.025*1.25, Method="failure") D = size_bundle_from_tubecount(N=1285, Do=0.025, pitch=0.03125) assert type(D) is float assert_close(D, 1.1985676402390355) D = size_bundle_from_tubecount(N=1285, Do=0.025, pitch=0.03125, Method="HEDH") assert type(D) is float assert_close(D, 1.205810838411941) D = size_bundle_from_tubecount(N=1285, Do=0.025, pitch=0.03125, Method="VDI") assert type(D) is float assert_close(D, 1.1749025890472795) D = size_bundle_from_tubecount(N=13252, Do=.028, Ntp=2, angle=45, pitch=.028*1.25, Method="Perry") assert type(D) is float assert_close(D, 3.598336054740235, rtol=5e-5) with pytest.raises(Exception): size_bundle_from_tubecount(N=1285, Do=0.025, pitch=0.03125, Method="BADMETHOD") def test_effectiveness_NTU(): # Counterflow for i in range(20): eff = uniform(0, 1) Cr = uniform(0, 1) units = NTU_from_effectiveness(effectiveness=eff, Cr=Cr, subtype="counterflow") eff_calc = effectiveness_from_NTU(NTU=units, Cr=Cr, subtype="counterflow") assert_close(eff, eff_calc) # Case with Cr = 1 NTU = NTU_from_effectiveness(effectiveness=.9, Cr=1, subtype="counterflow") assert_close(NTU, 9) e = effectiveness_from_NTU(NTU=9, Cr=1, subtype="counterflow") assert_close(e, 0.9) # Parallel for i in range(20): Cr = uniform(0, 1) eff = uniform(0, 1./(Cr + 1.)*(1-1E-7)) units = NTU_from_effectiveness(effectiveness=eff, Cr=Cr, subtype="parallel") eff_calc = effectiveness_from_NTU(NTU=units, Cr=Cr, subtype="parallel") assert_close(eff, eff_calc) with pytest.raises(Exception): Cr = 0.6 NTU_from_effectiveness(effectiveness=0.62500001, Cr=Cr, subtype="parallel") # Crossflow, Cmin mixed, Cmax unmixed for i in range(20): Cr = uniform(0, 1) eff = uniform(0, (1 - exp(-1/Cr))*(1-1E-7)) N = NTU_from_effectiveness(eff, Cr=Cr, subtype="crossflow, mixed Cmin") eff_calc = effectiveness_from_NTU(N, Cr=Cr, subtype="crossflow, mixed Cmin") assert_close(eff, eff_calc) with pytest.raises(Exception): Cr = 0.7 NTU_from_effectiveness(0.760348963559, Cr=Cr, subtype="crossflow, mixed Cmin") # Crossflow, Cmax mixed, Cmin unmixed for i in range(20): Cr = uniform(0, 1) eff = uniform(0, (exp(Cr) - 1)*exp(-Cr)/Cr-1E-5) N = NTU_from_effectiveness(eff, Cr=Cr, subtype="crossflow, mixed Cmax") eff_calc = effectiveness_from_NTU(N, Cr=Cr, subtype="crossflow, mixed Cmax") assert_close(eff, eff_calc) with pytest.raises(Exception): Cr = 0.7 eff = 0.7201638517265581 NTU_from_effectiveness(eff, Cr=Cr, subtype="crossflow, mixed Cmax") # Crossflow, this one needed a closed-form solver for i in range(100): Cr = uniform(0, 1) eff = uniform(0, 1) N = NTU_from_effectiveness(eff, Cr=Cr, subtype="crossflow approximate") eff_calc = effectiveness_from_NTU(N, Cr=Cr, subtype="crossflow approximate") assert_close(eff, eff_calc, rtol=1E-6) # brenth differs in old Python versions, rtol is needed # Shell and tube - this one doesn't have a nice effectiveness limit, # and it depends on the number of shells for i in range(20): Cr = uniform(0, 1) shells = randint(1, 10) eff_max = (-((-Cr + sqrt(Cr**2 + 1) + 1)/(Cr + sqrt(Cr**2 + 1) - 1))**shells + 1)/(Cr - ((-Cr + sqrt(Cr**2 + 1) + 1)/(Cr + sqrt(Cr**2 + 1) - 1))**shells) eff = uniform(0, eff_max-1E-5) N = NTU_from_effectiveness(eff, Cr=Cr, n_shell_tube=shells, subtype="S&T") eff_calc = effectiveness_from_NTU(N, Cr=Cr, n_shell_tube=shells, subtype="S&T") assert_close(eff, eff_calc) with pytest.raises(Exception): NTU_from_effectiveness(.99, Cr=.7, n_shell_tube=5, subtype="S&T") # Easy tests effectiveness = effectiveness_from_NTU(NTU=5, Cr=0.7, subtype="crossflow, mixed Cmin") assert_close(effectiveness, 0.7497843941508544) NTU = NTU_from_effectiveness(effectiveness=effectiveness, Cr=0.7, subtype="crossflow, mixed Cmin") assert_close(NTU, 5) eff = effectiveness_from_NTU(NTU=5, Cr=0.7, subtype="crossflow, mixed Cmax") assert_close(eff, 0.7158099831204696) NTU = NTU_from_effectiveness(eff, Cr=0.7, subtype="crossflow, mixed Cmax") assert_close(5, NTU) eff = effectiveness_from_NTU(NTU=5, Cr=0, subtype="boiler") assert_close(eff, 0.9932620530009145) NTU = NTU_from_effectiveness(eff, Cr=0, subtype="boiler") assert_close(NTU, 5) with pytest.raises(Exception): effectiveness_from_NTU(NTU=5, Cr=1.01, subtype="crossflow, mixed Cmin") with pytest.raises(Exception): NTU_from_effectiveness(effectiveness=.2, Cr=1.01, subtype="crossflow, mixed Cmin") # bad names with pytest.raises(Exception): NTU_from_effectiveness(.99, Cr=.7, subtype="FAIL") with pytest.raises(Exception): effectiveness_from_NTU(NTU=5, Cr=.5, subtype="FAIL") # Crossflow analytical solution eff = effectiveness_from_NTU(NTU=5, Cr=.7, subtype="crossflow") assert_close(eff, 0.8444821799748551) def crossflow_unmixed_sum_infinite(NTU, Cr): def Pn(NTU, n): tot = sum([(n+1.-j)/factorial(j)*NTU**(n+j) for j in range(1, n+1)]) return tot/factorial(n + 1) tot = sum([Cr**n*Pn(NTU, n) for n in range(1, 150)]) return 1 - exp(-NTU) - exp(-(1+Cr)*NTU)*tot eff_old = crossflow_unmixed_sum_infinite(5, .7) assert_close(eff, eff_old) # Crossflow analytical, this one needed a closed-form solver for i in range(20): Cr = uniform(0, 1) eff = uniform(0, .9) # A good anser is not always obtainable at eff> 0.9 at very high NTU, # because the integral term gets too close to 1 for floating point numbers # to capture any more accuracy # This is not likely to be a problem to users N = NTU_from_effectiveness(eff, Cr=Cr, subtype="crossflow") eff_calc = effectiveness_from_NTU(N, Cr=Cr, subtype="crossflow") assert_close(eff, eff_calc, rtol=1E-6) # brenth differs in old Python versions, rtol is needed def test_effectiveness_NTU_method(): ans_known = {"Q": 192850.0, "Thi": 130, "Cmax": 9672.0, "Tho": 110.06100082712986, "Cmin": 2755.0, "NTU": 1.1040839095588, "Tco": 85, "Tci": 15, "Cr": 0.2848428453267163, "effectiveness": 0.6086956521739131, "UA": 3041.751170834494} ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tco=85, Tho=110.06100082712986) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tco=85, Thi=130) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Thi=130, Tho=110.06100082712986, Tci=15) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Thi=130, Tho=110.06100082712986, Tco=85) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tco=85, Tho=110.06100082712986, UA=3041.751170834494) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Thi=130, UA=3041.751170834494) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tho=110.06100082712986, UA=3041.751170834494) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tco=85, Thi=130, UA=3041.751170834494) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tco=85, Tho=110.06100082712986, UA=3041.751170834494) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tco=85, Thi=130, Tho=110.06100082712986, UA=3041.751170834494) [assert_close(ans_known[i], ans[i]) for i in ans_known.keys()] with pytest.raises(Exception): # Test raising an error with only on set of stream information effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Thi=130, Tho=110.06100082712986, UA=3041.751170834494) with pytest.raises(Exception): # Inconsistent hot and cold temperatures and heat capacity ratios effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Thi=130, Tho=110.06100082712986, Tco=85, Tci=5) with pytest.raises(Exception): # Calculate UA, but no code side temperature information given effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Thi=130, Tho=110.06100082712986) with pytest.raises(Exception): # Calculate UA, but no hot side temperature information given effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tco=85) with pytest.raises(Exception): # Calculate UA, but only two temperatures given effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Thi=130) def test_F_LMTD_Fakheri(): """Number of tube passes must be a multiple of 2N for correlation to work. N can be 1. Example from http://excelcalculations.blogspot.ca/2011/06/lmtd-correction-factor.html spreadsheet file which Bowman et al (1940). This matches for 3, 6, and 11 shell passes perfectly. This also matches that from the sheet: http://www.mhprofessional.com/getpage.php?c=0071624082_download.php&cat=113 """ F_calc = F_LMTD_Fakheri(Tci=15, Tco=85, Thi=130, Tho=110, shells=1) assert_close(F_calc, 0.9438358829645933) # R = 1 check F_calc = F_LMTD_Fakheri(Tci=15, Tco=35, Thi=130, Tho=110, shells=1) assert_close(F_calc, 0.9925689447100824) for i in range(1, 10): ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, n_shell_tube=i, subtype="S&T", Tci=15, Tco=85, Thi=130) dTlm = LMTD(Thi=130, Tho=110.06100082712986, Tci=15, Tco=85) F_expect = ans["Q"]/ans["UA"]/dTlm F_calc = F_LMTD_Fakheri(Tci=15, Tco=85, Thi=130, Tho=110.06100082712986, shells=i) assert_close(F_expect, F_calc) F_calc = F_LMTD_Fakheri(Thi=15, Tho=85, Tci=130, Tco=110.06100082712986, shells=i) assert_close(F_expect, F_calc) def test_temperature_effectiveness_basic(): # Except for the crossflow mixed 1&2 cases, taken from an example and checked that # it matches the e-NTU method. The approximate formula for crossflow is somewhat # different - it is believed the approximations are different. P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="counterflow") assert_close(P1, 0.173382601503) P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="parallel") assert_close(P1, 0.163852912049) P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="crossflow approximate") assert_close(P1, 0.149974594007) P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="crossflow") assert_close(P1, 0.1698702121873175) P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="crossflow, mixed 1") assert_close(P1, 0.168678230894) P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="crossflow, mixed 2") assert_close(P1, 0.16953790774) P1 = temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="crossflow, mixed 1&2") assert_close(P1, 0.168411216829) with pytest.raises(Exception): temperature_effectiveness_basic(R1=3.5107078039927404, NTU1=0.29786672449248663, subtype="FAIL") # Formulas are in [1]_, [3]_, and [2]_. def test_temperature_effectiveness_TEMA_J(): # All three models are checked with Rosenhow and then Shaw # Formulas presented in Thulukkanam are with respect to the other side of the # exchanger P1 = temperature_effectiveness_TEMA_J(R1=1/3., NTU1=1., Ntp=1) assert_close(P1, 0.5699085193651295) P1 = temperature_effectiveness_TEMA_J(R1=2., NTU1=1., Ntp=1) # R = 2 case assert_close(P1, 0.3580830895954234) P1 = temperature_effectiveness_TEMA_J(R1=1/3., NTU1=1., Ntp=2) assert_close(P1, 0.5688878232315694) P1 = temperature_effectiveness_TEMA_J(R1=1/3., NTU1=1., Ntp=4) assert_close(P1, 0.5688711846568247) with pytest.raises(Exception): temperature_effectiveness_TEMA_J(R1=1/3., NTU1=1., Ntp=3) def test_temperature_effectiveness_TEMA_H(): P1 = temperature_effectiveness_TEMA_H(R1=1/3., NTU1=1., Ntp=1) assert_close(P1, 0.5730728284905833) P1 = temperature_effectiveness_TEMA_H(R1=2., NTU1=1., Ntp=1) # R = 2 case assert_close(P1, 0.3640257049950876) P1 = temperature_effectiveness_TEMA_H(R1=1/3., NTU1=1., Ntp=2) assert_close(P1, 0.5824437803128222) P1 = temperature_effectiveness_TEMA_H(R1=4., NTU1=1., Ntp=2) # R = 4 case assert_close(P1, 0.2366953352462191) P1 = temperature_effectiveness_TEMA_H(R1=1/3., NTU1=1., Ntp=2, optimal=False) assert_close(P1, 0.5560057072310012) P1 = temperature_effectiveness_TEMA_H(R1=4, NTU1=1., Ntp=2, optimal=False) assert_close(P1, 0.19223481412807347) # R2 = 0.25 # The 1 and 2 case by default are checked with Rosenhow and Shah # for the two pass unoptimal case, the result is from Thulukkanam only. # The 2-pass optimal arrangement from Rosenhow and Shaw is the same # as that of Thulukkanam however, and shown below. m1 = .5 m2 = 1.2 Cp1 = 1800. Cp2 = 2200. UA = 500. C1 = m1*Cp1 C2 = m2*Cp2 R1_orig = R1 = C1/C2 NTU1 = UA/C1 R2 = C2/C1 NTU2 = UA/C2 R1 = R2 NTU1 = NTU2 alpha = NTU1*(4*R1 + 1)/8. beta = NTU1*(4*R1 - 1)/8. D = (1 - exp(-alpha))/(4.*R1 + 1) E = (1 - exp(-beta))/(4*R1 - 1) H = (1 - exp(-2*beta))/(4.*R1 - 1) G = (1-D)**2*(D**2 + E**2) + D**2*(1+E)**2 B = (1 + H)*(1 + E)**2 P1 = (1 - (1-D)**4/(B - 4.*R1*G)) P1 = P1/R1_orig assert_close(P1, 0.40026600037802335) with pytest.raises(Exception): temperature_effectiveness_TEMA_H(R1=1/3., NTU1=1., Ntp=5) def test_temperature_effectiveness_TEMA_G(): # Checked with Shah and Rosenhow, formula typed and then the other case is working P1 = temperature_effectiveness_TEMA_G(R1=1/3., NTU1=1., Ntp=1) assert_close(P1, 0.5730149350867675) P1 = temperature_effectiveness_TEMA_G(R1=1/3., NTU1=1., Ntp=2) # TEST CASSE assert_close(P1, 0.5824238778134628) # Ntp = 1, R=1 case P1_Ntp_R1 = 0.8024466201983814 P1 = temperature_effectiveness_TEMA_G(R1=1., NTU1=7., Ntp=1) # R = 1 case assert_close(P1, P1_Ntp_R1) P1_near = temperature_effectiveness_TEMA_G(R1=1-1E-9, NTU1=7, Ntp=1) assert_close(P1_near, P1_Ntp_R1) # Ntp = 2, optimal, R=2 case P1_Ntp_R1 = 0.4838424889135673 P1 = temperature_effectiveness_TEMA_G(R1=2., NTU1=7., Ntp=2) # R = 2 case assert_close(P1, P1_Ntp_R1) P1_near = temperature_effectiveness_TEMA_G(R1=2-1E-9, NTU1=7., Ntp=2) assert_close(P1_near, P1_Ntp_R1) # Ntp = 2, not optimal case, R1=0.5 case P1 = temperature_effectiveness_TEMA_G(R1=1/3., NTU1=1., Ntp=2, optimal=False) assert_close(P1, 0.5559883028569507) P1_Ntp_R1 = 0.3182960796403764 P1 = temperature_effectiveness_TEMA_G(R1=2, NTU1=1., Ntp=2, optimal=False) assert_close(P1, P1_Ntp_R1) P1_near = temperature_effectiveness_TEMA_G(R1=2-1E-9, NTU1=1., Ntp=2, optimal=False) assert_close(P1_near, P1_Ntp_R1) with pytest.raises(Exception): temperature_effectiveness_TEMA_G(R1=2., NTU1=7., Ntp=5) # The optimal 2 pass case from Thulukkanam is checked with the following case # to be the same as those in Rosenhow and Shah # Believed working great. m1 = .5 m2 = 1.2 Cp1 = 1800. Cp2 = 2200. UA = 500. C1 = m1*Cp1 C2 = m2*Cp2 R1_orig = R1 = C1/C2 NTU1 = UA/C1 R2 = C2/C1 NTU2 = UA/C2 P1_good = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=2) # Good G 2 pass case, working R1 = R2 NTU1 = NTU2 beta = exp(-NTU1*(2*R1 - 1)/2.) alpha = exp(-NTU1*(2*R1 + 1)/4.) B = (4*R1 - beta*(2*R1 + 1))/(2*R1 - 1.) A = -1*(1-alpha)**2/(R1 + 0.5) P1 = (B - alpha**2)/(R1*(A + 2 + B/R1)) P1 = P1/R1_orig assert_close(P1, P1_good) def test_temperature_effectiveness_TEMA_E(): # 1, 2 both cases are perfect eff = temperature_effectiveness_TEMA_E(R1=1/3., NTU1=1., Ntp=1) assert_close(eff, 0.5870500654031314) eff = temperature_effectiveness_TEMA_E(R1=1., NTU1=7., Ntp=1) assert_close(eff, 0.875) # Remaining E-shells, checked eff = temperature_effectiveness_TEMA_E(R1=1/3., NTU1=1., Ntp=2) assert_close(eff, 0.5689613217664634) eff = temperature_effectiveness_TEMA_E(R1=1., NTU1=7., Ntp=2) # R = 1 case assert_close(eff, 0.5857620762776082) eff = temperature_effectiveness_TEMA_E(R1=1/3., NTU1=1., Ntp=2, optimal=False) assert_close(eff, 0.5699085193651295) # unoptimal case eff = temperature_effectiveness_TEMA_E(R1=2, NTU1=1., Ntp=2, optimal=False) assert_close(eff, 0.3580830895954234) eff = temperature_effectiveness_TEMA_E(R1=1/3., NTU1=1., Ntp=3) assert_close(eff, 0.5708624888990603) eff = temperature_effectiveness_TEMA_E(R1=1., NTU1=7., Ntp=3) # R = 1 case assert_close(eff, 0.6366132064792461) eff = temperature_effectiveness_TEMA_E(R1=3., NTU1=1., Ntp=3, optimal=False) assert_close(eff, 0.276815590660033) eff = temperature_effectiveness_TEMA_E(R1=1/3., NTU1=1., Ntp=4) assert_close(eff, 0.56888933865756) eff = temperature_effectiveness_TEMA_E(R1=1., NTU1=7., Ntp=4) # R = 1 case, even though it's no longer used assert_close(eff, 0.5571628802075902) with pytest.raises(Exception): temperature_effectiveness_TEMA_E(R1=1., NTU1=7., Ntp=7) # Compare the expression for 4 tube passes in two of the sources with that # in the third. R1 = 1/3. NTU1 = 1 D = (4 + R1**2)**0.5 B = tanh(R1*NTU1/4.) A = 1/tanh(D*NTU1/4.) P1 = 4*(2*(1 + R1) + D*A + R1*B)**-1 assert_close(P1, 0.56888933865756) def test_temperature_effectiveness_air_cooler(): # 1 pass-N rows case R1 = 0.9090909090909091 NTU1 = 14.958251192851375 expected_P1s = [0.6568205178185993, 0.7589599992302802, 0.8064227529035781, 0.8330202134563712, 0.8491213831157698, 0.8594126317585193, 0.8662974164766494, 0.871087594489211, 0.8745345926002213, 0.8770877118478316, 0.8790262425246239, 0.8805299599498708, 0.8817182454510963, 0.8826726050451953, 0.8834500769975893, 0.8840914654885264, 0.8846265414931143, 0.88507741320138, 0.8854607616314836, 0.8857893552314147, 0.886073095973165, 0.8863197546874396, 0.8865354963468465, 0.8867252608860744, 0.8868930430686396] P1s_calc = [temperature_effectiveness_air_cooler(R1=R1, NTU1=NTU1, rows=N, passes=1) for N in range(1, 26)] assert_close1d(expected_P1s, P1s_calc) # Compare the results of 1-N against the function without the annoying optimizations; # may be helpful for debugging def calc_N_1_orig(NTU1, R1, N): NTU, R = NTU1, R1 K = 1 - exp(-NTU/N) top = N*exp(N*K*R) tot = 0 for i in range(1, N): for j in range(i+1): prod = factorial(i)/factorial(i-j)/factorial(j) tot1 = prod*K**j*exp(-(i-j)*NTU/N) tot2 = 0 for k in range(j+1): tot2 += (N*K*R)**k/factorial(k) tot += tot1*tot2 P = 1/R*(1 - (top/(1+tot))**-1) return P P1s_calc = [calc_N_1_orig(R1=R1, NTU1=NTU1, N=N) for N in range(1, 26)] assert_close1d(expected_P1s, P1s_calc) # N rows / N passes (N from 2 to 5) cases R1, NTU1 = 1.1, .5 expected_P1s = [0.3254086785640332, 0.3267486216405819, 0.3272282999575143, 0.3274325680785421] P1s_calc = [temperature_effectiveness_air_cooler(R1, NTU1, rows=N, passes=N) for N in range(2, 6)] assert_close1d(expected_P1s, P1s_calc) # 4 row / 2 pass special case P1_calc = temperature_effectiveness_air_cooler(R1, NTU1, rows=4, passes=2) assert_close(P1_calc, 0.32552127419957044) # Tentative checking of the above has been done with hete.c for isolated cases def test_temperature_effectiveness_air_cooler_coerce(): # Simple test a call that the number of row and passes can be domain reduced # without causing a recursion depth error # Do not test the results for any specific answer, as they will ideally one day # be replaced with the exactly correct one [temperature_effectiveness_air_cooler(.5, 2, rows=j, passes=i) for i in range(1, 10) for j in range(1, 10)] @pytest.mark.mpmath def test_P_NTU_method(): # Counterflow case ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="counterflow", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1i=130, T2i=15, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) # Parallel flow case ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="parallel", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1i=130, T2i=15, subtype="parallel") assert_close(ans2["Q"], ans["Q"]) # Mixed Cmax/ 1 ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1i=130, T2i=15, subtype="crossflow, mixed 1") assert_close(ans2["Q"], ans["Q"]) # Mixed Cmin/2 ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmin", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1i=130, T2i=15, subtype="crossflow, mixed 2") assert_close(ans2["Q"], ans["Q"]) # Counterflow case but with all five different temperature input cases (both inlets known already done) ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="counterflow", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1o=110.06100082712986, T2o=85, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1i=130, T2o=85, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1o=110.06100082712986, T2i=15, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T2o=85, T2i=15, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=ans["UA"], T1o=110.06100082712986, T1i=130, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) # Only 1 temperature input with pytest.raises(Exception): P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, subtype="counterflow") # Bad HX type input with pytest.raises(Exception): P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="BADTYPE") ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="E", Ntp=10) assert_close(ans["Q"], 32212.185563086336,) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="G", Ntp=2) assert_close(ans["Q"], 32224.88788570008) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="H", Ntp=2) assert_close(ans["Q"], 32224.888572366734) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="J", Ntp=2) assert_close(ans["Q"], 32212.185699719837) # Plate tests ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="3/1") assert_close(ans["Q"], 32214.179745602625) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="3/1", optimal=False) assert_close(ans["Q"], 32210.4190840378) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="2/2") assert_close(ans["Q"], 32229.120739501937) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="2/2", optimal=False) assert_close(ans["Q"], 32203.721238671216) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="2/2c", optimal=False) assert_close(ans["Q"], 32203.721238671216) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, T1i=130, T2i=15, subtype="2/2p", optimal=False) assert_close(ans["Q"], 32195.273806845064) def test_P_NTU_method_backwards(): ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="counterflow", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T2i=15, T2o=85, T1o=110.06100082712986, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) # # Parallel flow case ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="parallel", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1i=130, T2i=15, T1o=110.06100082712986, subtype="parallel") assert_close(ans2["Q"], ans["Q"]) # # Mixed Cmax/ 1 ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmax", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T1i=130, T2i=15, subtype="crossflow, mixed 1") assert_close(ans2["Q"], ans["Q"]) # # Mixed Cmin/2 ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="crossflow, mixed Cmin", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T1i=130, T2i=15, subtype="crossflow, mixed 2") assert_close(ans2["Q"], ans["Q"]) # # Counterflow case but with all five different temperature input cases (both inlets known already done) ans = effectiveness_NTU_method(mh=5.2, mc=1.45, Cph=1860., Cpc=1900, subtype="counterflow", Tci=15, Tco=85, Tho=110.06100082712986) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1i=130, T1o=110.06100082712986, T2o=85, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T1i=130, T2o=85, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1i=130, T1o=110.06100082712986, T2i=15, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T2o=85, T2i=15, T1o=110.06100082712986, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T1i=130, T2i=15, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T2o=85, T1i=130, T2i=15, subtype="counterflow") assert_close(ans2["Q"], ans["Q"]) # TEMA types ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1i=130, T1o=126.66954243557834, T2i=15, subtype="E", Ntp=10) assert_close(ans["Q"], 32212.185563086336,) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.66822912678866, T1i=130, T2i=15, subtype="G", Ntp=2) assert_close(ans["Q"], 32224.88788570008) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.66822905579335, T1i=130, T2i=15, subtype="H", Ntp=2) assert_close(ans["Q"], 32224.888572366734) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.66954242145162, T1i=130, T2i=15, subtype="J", Ntp=2) assert_close(ans["Q"], 32212.185699719837) # Plate tests ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.6693362545903, T1i=130, T2i=15, subtype="3/1") assert_close(ans["Q"], 32214.179745602625) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.66972507402421, T1i=130, T2i=15, subtype="3/1", optimal=False) assert_close(ans["Q"], 32210.4190840378) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.66779148681742, T1i=130, T2i=15, subtype="2/2") assert_close(ans["Q"], 32229.120739501937) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.67041757251124, T1i=130, T2i=15, subtype="2/2", optimal=False) assert_close(ans["Q"], 32203.721238671216) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.67041757251124, T1i=130, T2i=15, subtype="2/2c", optimal=False) assert_close(ans["Q"], 32203.721238671216) ans = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=126.67129096289857, T1i=130, T2i=15, subtype="2/2p", optimal=False) assert_close(ans["Q"], 32195.273806845064) # Q for both streams don't match case with pytest.raises(Exception): P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T2o=85, T1i=170, T2i=15, subtype="counterflow") # No T speced on side 2 with pytest.raises(Exception): P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T1i=130, subtype="counterflow") # No T specified on side 1 with pytest.raises(Exception): P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T2o=85, T2i=15, subtype="counterflow") # No T information at all with pytest.raises(Exception): ans2 = P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., subtype="counterflow") # subtype not recognized with pytest.raises(Exception): P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., T1o=110.06100082712986, T1i=130, T2i=15, subtype="NOTAREALTYPEOFHEATEXCHANGER") def test_Pp(): from ht.hx import P_NTU_Pc, P_NTU_Pp # randomly chosen test value ans = P_NTU_Pp(5, .4) assert_close(ans, 0.713634370024604) # Test the limit works with a small difference assert_close(P_NTU_Pp(2, -1), P_NTU_Pp(2, -1+1E-9)) # randomly chosen test value assert_close(P_NTU_Pc(5, .7), 0.9206703686051108) # Test the limit works with a small difference assert_close(P_NTU_Pc(5, 1), P_NTU_Pc(5, 1-1E-8)) def test_temperature_effectiveness_plate(): R1 = 0.5 NTU1 = 1.5 P1 = temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=1, counterflow=True) assert_close(P1, 0.6907854082479168) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=1, counterflow=False) assert_close(P1, 0.5964005169587571) # 1 pass/2 pass for b1 in [True, False]: for b2 in [True, False]: P1 = temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=2, counterflow=b1, passes_counterflow=b2) assert_close(P1, 0.6439306988115887) # We can check we did the conversion right as follows: NTU2 = NTU1*R1 R2 = 1./R1 # switch 2 P2 = P1*R1 P2_reversed = temperature_effectiveness_plate(R2, NTU2, Np1=2, Np2=1) assert_close(P2, P2_reversed) # in reverse P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=1, counterflow=b1, passes_counterflow=b2) assert_close(P1, 0.6505342399575915) # 1 pass/3 pass, counterflow for b1 in [True, False]: P1 = temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=3, counterflow=True, passes_counterflow=b1) assert_close(P1, 0.6491132138517642) # In reverse P1 = temperature_effectiveness_plate(R1, NTU1, Np1=3, Np2=1, counterflow=True, passes_counterflow=b1) assert_close(P1, 0.6565261377239298) # 1 pass/3 pass, parallel for b1 in [True, False]: P1 = temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=3, counterflow=False, passes_counterflow=b1) assert_close(P1, 0.6385443460862099) # in reverse P1 = temperature_effectiveness_plate(R1, NTU1, Np1=3, Np2=1, counterflow=False, passes_counterflow=b1) assert_close(P1, 0.6459675147406085) # 1 pass/4 pass for b1 in [True, False]: for b2 in [True, False]: P1 = temperature_effectiveness_plate(R1, NTU1, Np1=1, Np2=4, counterflow=b1, passes_counterflow=b2) assert_close(P1, 0.6438068496552443) # In reverse P1 = temperature_effectiveness_plate(R1, NTU1, Np1=4, Np2=1, counterflow=b1, passes_counterflow=b2) assert_close(P1, 0.6515539888566283) # Four different results for 4 passes P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=2, counterflow=False, passes_counterflow=False) assert_close(P1, 0.5964005169587571) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=2, counterflow=False, passes_counterflow=True) assert_close(P1, 0.6123845839665905) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=2, counterflow=True, passes_counterflow=False) assert_close(P1, 0.6636659009073801) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=2, counterflow=True, passes_counterflow=True) assert_close(P1, 0.6907854082479168) # 2-3 counterflow for b1 in [True, False]: P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=3, counterflow=True, passes_counterflow=b1) assert_close(P1, 0.67478876724034) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=3, counterflow=False, passes_counterflow=b1) assert_close(P1, 0.6102922060616937) # In reverse P1 = temperature_effectiveness_plate(R1, NTU1, Np1=3, Np2=2, counterflow=True, passes_counterflow=b1) assert_close(P1, 0.675522913050678) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=3, Np2=2, counterflow=False, passes_counterflow=b1) assert_close(P1, 0.6105764872072659) # 2-4 counterflow for b1 in [True, False]: P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=4, counterflow=True, passes_counterflow=b1) assert_close(P1, 0.6777107269336475) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=2, Np2=4, counterflow=False, passes_counterflow=b1) assert_close(P1, 0.6048585344522575) # In reverse P1 = temperature_effectiveness_plate(R1, NTU1, Np1=4, Np2=2, counterflow=True, passes_counterflow=b1) assert_close(P1, 0.6786601861219819) P1 = temperature_effectiveness_plate(R1, NTU1, Np1=4, Np2=2, counterflow=False, passes_counterflow=b1) assert_close(P1, 0.6054166111196166) with pytest.raises(Exception): temperature_effectiveness_plate(R1=1/3., NTU1=1., Np1=3, Np2=3) @pytest.mark.mpmath def test_NTU_from_P_basic(): # Analytical result for counterflow R1s = np.logspace(np.log10(2E-5), np.log10(1E2), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(1E2), 10000).tolist() for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: # Not all of the guesses work forward; some overflow, some divide by 0 P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="counterflow") # Backwards, it's the same divide by zero or log(negative number) NTU1_calc = NTU_from_P_basic(P1, R1, subtype="counterflow") except (ValueError, OverflowError, ZeroDivisionError): continue # Again, multiple values of NTU1 can produce the same P1 P1_calc = temperature_effectiveness_basic(R1=R1, NTU1=NTU1_calc, subtype="counterflow") assert_close(P1, P1_calc) # Analytical result for parallel flow for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="parallel") # Backwards, it's the same divide by zero or log(negative number) NTU1_calc = NTU_from_P_basic(P1, R1, subtype="parallel") except (ValueError, OverflowError, ZeroDivisionError): continue P1_calc = temperature_effectiveness_basic(R1=R1, NTU1=NTU1_calc, subtype="parallel") assert_close(P1, P1_calc) # Analytical result for 'crossflow, mixed 1' for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: # Not all of the guesses work forward; some overflow, some divide by 0 P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="crossflow, mixed 1") # Backwards, it's the same divide by zero or log(negative number) NTU1_calc = NTU_from_P_basic(P1, R1, subtype="crossflow, mixed 1") except (ValueError, OverflowError, ZeroDivisionError): continue # Again, multiple values of NTU1 can produce the same P1 P1_calc = temperature_effectiveness_basic(R1=R1, NTU1=NTU1_calc, subtype="crossflow, mixed 1") assert_close(P1, P1_calc) # Analytical result for 'crossflow, mixed 2' for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: # Not all of the guesses work forward; some overflow, some divide by 0 P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="crossflow, mixed 2") # Backwards, it's the same divide by zero or log(negative number) NTU1_calc = NTU_from_P_basic(P1, R1, subtype="crossflow, mixed 2") except (ValueError, OverflowError, ZeroDivisionError): continue # Again, multiple values of NTU1 can produce the same P1 P1_calc = temperature_effectiveness_basic(R1=R1, NTU1=NTU1_calc, subtype="crossflow, mixed 2") assert_close(P1, P1_calc) # Test 'crossflow, mixed 1&2': R1s = np.logspace(np.log10(2E-5), np.log10(1E2), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(1E2), 10000).tolist() seed(0) tot = 0 for i in range(100): R1 = choice(R1s) NTU1 = choice(NTU1s) P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="crossflow, mixed 1&2") try: # Very rarely, the pade approximation will get a result too close to the infeasibility region and # the solver cannot start as it is already outside the region NTU1_calc = NTU_from_P_basic(P1, R1, subtype="crossflow, mixed 1&2") except: continue # May not get the original NTU1, but the found NTU1 needs to produce the same P1. P1_calc = temperature_effectiveness_basic(R1=R1, NTU1=NTU1_calc, subtype="crossflow, mixed 1&2") assert_close(P1, P1_calc) tot +=1 assert tot == 100 # crossflow approximate - easy as 1 is always a possibility for any R for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="crossflow approximate") NTU1_calc = NTU_from_P_basic(P1, R1, subtype="crossflow approximate") # We have to compare the re calculated P1 values, because for many values of NTU1, # at the initial far guess of 10000 P1 = 1 and at the random NTU1 P1 is also 1 P1_calc = temperature_effectiveness_basic(R1=R1, NTU1=NTU1_calc, subtype="crossflow approximate") # In python 2.6 and 3.3 the solver doesn't converge as well, so we need # to add a little tolerance assert_close(P1, P1_calc, rtol=5E-6) # Crossflow approximate test case R1 = .1 NTU1 = 2 P1_calc_orig = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="crossflow approximate") P1_expect = 0.8408180737140558 assert_close(P1_calc_orig, P1_expect) NTU1_backwards = NTU_from_P_basic(P1=P1_expect, R1=R1, subtype="crossflow approximate") assert_close(NTU1, NTU1_backwards) # Test cross flow - failes VERY OFTEN, should rely on crossflow approximate NTU1 = 10 R1 = 0.5 P1 = temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype="crossflow") NTU1_calc = NTU_from_P_basic(P1, R1=R1, subtype="crossflow") assert_close(NTU1, NTU1_calc) # bad type of exchanger with pytest.raises(Exception): NTU_from_P_basic(P1=.975, R1=.1, subtype="BADTYPE") @pytest.mark.mpmath def test_NTU_from_P_E(): # not yet documented # 1 tube pass AKA counterflow R1s = np.logspace(np.log10(2E-5), np.log10(1E2), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(1E2), 10000).tolist() # Exact same asa as the counterflow basic case tot = 0 seed(0) for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: # Not all of the guesses work forward; some overflow, some divide by 0 P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=1) # Backwards, it's the same divide by zero or log(negative number) NTU1_calc = NTU_from_P_E(P1, R1, Ntp=1) except (ValueError, OverflowError, ZeroDivisionError): continue # Again, multiple values of NTU1 can produce the same P1 P1_calc = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1_calc, Ntp=1) assert_close(P1, P1_calc) tot +=1 assert tot >= 85 # 2 tube passes (optimal arrangement) (analytical) R1 = 1.1 NTU1 = 10 P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=2, optimal=True) P1_expect = 0.5576299522073297 assert_close(P1, P1_expect) NTU1_calc = NTU_from_P_E(P1, R1, Ntp=2, optimal=True) assert_close(NTU1_calc, NTU1) # 2 tube pass (unoptimal) tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: # Not all of the guesses work forward; some overflow, some divide by 0 P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=2, optimal=False) # Backwards, it's the same divide by zero or log(negative number) NTU1_calc = NTU_from_P_E(P1, R1, Ntp=2, optimal=False) except (ValueError, OverflowError, ZeroDivisionError): continue # Again, multiple values of NTU1 can produce the same P1 try: P1_calc = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1_calc, Ntp=2, optimal=False) except (ZeroDivisionError): continue assert_close(P1, P1_calc) tot +=1 assert tot >= 90 # At the default mpmath precision, the following will predict a value larger # than one bad_P1 = temperature_effectiveness_TEMA_E(R1=1E-8 , NTU1=19.60414246043446, Ntp=2, optimal=False) assert_close(bad_P1, 1.0000000050247593) # 4 pass for Ntp in [4, 6, 8, 10, 12]: tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=Ntp) NTU1_calc = NTU_from_P_E(P1, R1, Ntp=Ntp) except ValueError: # The case where with mpmath being used, the result is too high for # the bounded solver to be able to solve it continue P1_calc = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1_calc, Ntp=Ntp) assert_close(P1, P1_calc) tot +=1 assert tot >= 70 # 3 pass optimal and not optimal R1s = np.logspace(np.log10(2E-5), np.log10(1E1), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(1E1), 10000).tolist() seed(0) for optimal in [True, False]: tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=3, optimal=optimal) NTU1_calc = NTU_from_P_E(P1, R1, Ntp=3, optimal=optimal) except (ValueError): # The case where with mpmath being used, the result is too high for # the bounded solver to be able to solve it continue # Again, multiple values of NTU1 can produce the same P1 P1_calc = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1_calc, Ntp=3, optimal=optimal) assert_close(P1, P1_calc) tot +=1 assert tot >= 97 with pytest.raises(Exception): NTU_from_P_E(P1=1, R1=1, Ntp=17) @pytest.mark.mpmath def test_NTU_from_P_H(): # Within these limits everything is fund R1s = np.logspace(np.log10(2E-5), np.log10(1E1), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(10), 10000).tolist() seed(0) for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) P1 = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1, Ntp=1) NTU1_calc = NTU_from_P_H(P1, R1, Ntp=1) P1_calc = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1_calc, Ntp=1) assert_close(P1, P1_calc) for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) P1 = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1, Ntp=2) NTU1_calc = NTU_from_P_H(P1, R1, Ntp=2) P1_calc = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1_calc, Ntp=2) assert_close(P1, P1_calc) for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) P1 = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1, Ntp=2, optimal=False) NTU1_calc = NTU_from_P_H(P1, R1, Ntp=2, optimal=False) P1_calc = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1_calc, Ntp=2, optimal=False) assert_close(P1, P1_calc, rtol=1E-6) with pytest.raises(Exception): NTU_from_P_H(P1=0.573, R1=1/3., Ntp=101) @pytest.mark.mpmath def test_NTU_from_P_G(): # 1 tube pass, random point R1 = 1.1 NTU1 = 2 P1_calc_orig = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=1) P1_expect = 0.5868787117241955 assert_close(P1_calc_orig, P1_expect) NTU1_backwards = NTU_from_P_G(P1=P1_expect, R1=R1, Ntp=1) assert_close(NTU1, NTU1_backwards) # 2 tube pass, randompoint R1 = 1.1 NTU1 = 2 P1_calc_orig = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=2) P1_calc_orig P1_expect = 0.6110347802764724 assert_close(P1_calc_orig, P1_expect) NTU1_backwards = NTU_from_P_G(P1=P1_expect, R1=R1, Ntp=2) assert_close(NTU1, NTU1_backwards) # 2 tube pass, not optimal R1 = .1 NTU1 = 2 P1_calc_orig = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=2, optimal=False) P1_calc_orig P1_expect = 0.8121969945075509 assert_close(P1_calc_orig, P1_expect) NTU1_backwards = NTU_from_P_G(P1=P1_expect, R1=R1, Ntp=2, optimal=False) assert_close(NTU1, NTU1_backwards) # Run the gamut testing all the solvers R1s = np.logspace(np.log10(2E-5), np.log10(1E2), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(1E2), 10000).tolist() seed(0) tot = 0 for Ntp, optimal in zip([1, 2, 2], [True, True, False]): for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal) NTU1_calc = NTU_from_P_G(P1, R1, Ntp=Ntp, optimal=optimal) P1_calc = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1_calc, Ntp=Ntp, optimal=optimal) except (ValueError, OverflowError, ZeroDivisionError, RuntimeError) as e: continue if isnan(P1) or isnan(P1_calc): continue assert_close(P1, P1_calc) tot +=1 assert tot > 270 with pytest.raises(Exception): NTU_from_P_G(P1=.573, R1=1/3., Ntp=10) @pytest.mark.mpmath def test_NTU_from_P_J(): # Run the gamut testing all the solvers R1s = np.logspace(np.log10(2E-5), np.log10(1E2), 10000).tolist() NTU1s = np.logspace(np.log10(1E-4), np.log10(1E2), 10000).tolist() seed(0) tot = 0 for Ntp in [1, 2, 4]: for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1, Ntp=Ntp) NTU1_calc = NTU_from_P_J(P1, R1, Ntp=Ntp) P1_calc = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1_calc, Ntp=Ntp) except (ValueError, OverflowError, ZeroDivisionError, RuntimeError) as e: continue assert_close(P1, P1_calc) tot +=1 assert tot > 270 # Actual individual understandable working test cases # 1 tube pass R1 = 1.1 NTU1 = 3 P1_calc_orig = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1, Ntp=1) P1_expect = 0.5996529947927913 assert_close(P1_calc_orig, P1_expect) NTU1_backwards = NTU_from_P_J(P1=P1_expect, R1=R1, Ntp=1) assert_close(NTU1, NTU1_backwards) # 2 tube passes R1 = 1.1 NTU1 = 2.7363888898379249 P1_calc_orig = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1, Ntp=2) P1_expect = 0.53635261090479802 assert_close(P1_calc_orig, P1_expect) # The exact P1 is slightly higher than that calculated as the upper limit # of the pade approximation, so we multiply it by a small fraction NTU1_backwards = NTU_from_P_J(P1=P1_expect*(1-2E-9), R1=R1, Ntp=2) assert_close(NTU1, NTU1_backwards, rtol=1E-3) # Unfortunately the derivative is so large we can't compare it exactly # 4 tube passes R1 = 1.1 NTU1 = 2.8702676768833268 P1_calc_orig = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1, Ntp=4) P1_expect = 0.53812561986477236 assert_close(P1_calc_orig, P1_expect) # The exact P1 is slightly higher than that calculated as the upper limit # of the pade approximation, so we multiply it by a small fraction NTU1_backwards = NTU_from_P_J(P1=P1_expect*(1-1E-15), R1=R1, Ntp=4) assert_close(NTU1, NTU1_backwards) # The derivative is very large but the pade approximation is really good, ant it works with pytest.raises(Exception): # unsupported number of tube passes case NTU_from_P_J(P1=.57, R1=1/3., Ntp=10) def test_NTU_from_P_plate(): # 1 pass-1 pass counterflow NTU1 = 3.5 R1 = 0.25 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=1, Np2=1) assert_close(P1_calc, 0.944668125335067) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=1, Np2=1) assert_close(NTU1, NTU1_calc) with pytest.raises(Exception): NTU_from_P_plate(P1=.10001, R1=10, Np1=1, Np2=1, counterflow=True) # 1 pass-1 pass parallelflow NTU1 = 3.5 R1 = 0.25 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=1, Np2=1, counterflow=False) assert_close(P1_calc, 0.7899294862060529) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=1, Np2=1, counterflow=False) assert_close(NTU1, NTU1_calc) with pytest.raises(Exception): NTU_from_P_plate(P1=.091, R1=10, Np1=1, Np2=1, counterflow=False) # 1-2 True True R1s = np.logspace(np.log10(2E-5), np.log10(10), 10000).tolist() # too high R1 causes overflows NTU1s = np.logspace(np.log10(1E-4), np.log10(99), 10000).tolist() tot = 0 seed(0) for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=1, Np2=2) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=1, Np2=2) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=1, Np2=2) except (OverflowError, ValueError): continue assert_close(P1, P1_calc) tot +=1 assert tot > 97 tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=1, Np2=3) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=1, Np2=3) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=1, Np2=3) except (OverflowError, ValueError): continue assert_close(P1, P1_calc) tot +=1 assert tot >= 99 tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=1, Np2=3, counterflow=False) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=1, Np2=3, counterflow=False) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=1, Np2=3, counterflow=False) except (OverflowError, ValueError): continue assert_close(P1, P1_calc) tot +=1 assert tot >= 99 tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=1, Np2=4) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=1, Np2=4) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=1, Np2=4) except (OverflowError, ValueError): continue assert_close(P1, P1_calc) tot +=1 assert tot >= 99 # 2-2 pass cases # counterflow and not passes_counterflow tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=2, counterflow=True, passes_counterflow=False) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=2, counterflow=True, passes_counterflow=False) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=2, counterflow=True, passes_counterflow=False) except (OverflowError, ValueError): continue assert_close(P1, P1_calc) tot +=1 assert tot >= 99 # not counterflow and not passes_counterflow # random example NTU1 = 1.1 R1 = .6 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=2, counterflow=False, passes_counterflow=False) assert_close(P1_calc, 0.5174719601105934) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=2, Np2=2, counterflow=False, passes_counterflow=False) assert_close(NTU1, NTU1_calc) # methodical test tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=2, counterflow=False, passes_counterflow=False) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=2, counterflow=False, passes_counterflow=False) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=2, counterflow=False, passes_counterflow=False) except (ZeroDivisionError, ValueError): continue assert_close(P1, P1_calc) tot +=1 assert tot > 85 # not counterflow and passes_counterflow # random example NTU1 = 1.1 R1 = .6 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=2, counterflow=False, passes_counterflow=True) assert_close(P1_calc, 0.529647502598342) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=2, Np2=2, counterflow=False, passes_counterflow=True) assert_close(NTU1, NTU1_calc) # methodical for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=2, counterflow=False, passes_counterflow=True) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=2, counterflow=False, passes_counterflow=True) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=2, counterflow=False, passes_counterflow=True) assert_close(P1, P1_calc) # 2-2 counterflow and passes_counterflow tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=2, counterflow=True, passes_counterflow=True) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=2, counterflow=True, passes_counterflow=True) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=2, counterflow=True, passes_counterflow=True) except (ValueError, ZeroDivisionError): continue tot +=1 assert_close(P1, P1_calc) assert tot > 90 # 2-3 counterflow - random example NTU1 = 1.1 R1 = .6 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=3, counterflow=True) assert_close(P1_calc, 0.5696402802155714) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=2, Np2=3, counterflow=True) assert_close(NTU1, NTU1_calc) # 2-3 counterflow - methodical tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=3, counterflow=True) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=3, counterflow=True) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=3, counterflow=True) except (ValueError, ZeroDivisionError): continue tot +=1 assert_close(P1, P1_calc, rtol=5E-4) assert tot > 85 # 2-3 parallelflow - random example NTU1 = 1.1 R1 = .6 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=3, counterflow=False) assert_close(P1_calc, 0.5272339114328507) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=2, Np2=3, counterflow=False) assert_close(NTU1, NTU1_calc) # 2-3 parallelflow - methodical (all work for given range) for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=3, counterflow=False) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=3, counterflow=False) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=3, counterflow=False) assert_close(P1, P1_calc) # 2-4 counterflow - random example NTU1 = 1.1 R1 = .6 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=4, counterflow=True) assert_close(P1_calc, 0.5717083161054717) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=2, Np2=4, counterflow=True) assert_close(NTU1, NTU1_calc) # 2-4 counterflow - methodical tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=4, counterflow=True) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=4, counterflow=True) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=4, counterflow=True) except (ValueError, ZeroDivisionError): continue tot +=1 assert_close(P1, P1_calc) assert tot > 95 # 2-4 parallelflow - random example NTU1 = 1.1 R1 = .6 P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=4, counterflow=False) assert_close(P1_calc, 0.5238412695944656) NTU1_calc = NTU_from_P_plate(P1=P1_calc, R1=R1, Np1=2, Np2=4, counterflow=False) assert_close(NTU1, NTU1_calc) # 2-4 counterflow - methodical tot = 0 for i in range(100): R1 = float(choice(R1s)) NTU1 = float(choice(NTU1s)) try: P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=2, Np2=4, counterflow=False) NTU1_calc = NTU_from_P_plate(P1, R1, Np1=2, Np2=4, counterflow=False) P1_calc = temperature_effectiveness_plate(R1=R1, NTU1=NTU1_calc, Np1=2, Np2=4, counterflow=False) except (ValueError, ZeroDivisionError): continue tot +=1 assert_close(P1, P1_calc) assert tot > 95 # Backwards, only one example in the tests # No real point in being exhaustive NTU1 = NTU_from_P_plate(P1=0.5743514352720835, R1=1/3., Np1=3, Np2=1) assert_close(NTU1, 1) # Bad number of plates with pytest.raises(Exception): NTU_from_P_plate(P1=0.5743, R1=1/3., Np1=3, Np2=13415151213) def test_DBundle_min(): assert_close(DBundle_min(0.0254), 1) assert_close(DBundle_min(0.005), .1) assert_close(DBundle_min(0.014), .3) assert_close(DBundle_min(0.015), .5) assert_close(DBundle_min(.1), 1.5) def test_shell_clearance(): assert_close(shell_clearance(DBundle=1.245), 0.0064) assert_close(shell_clearance(DBundle=4), 0.011) assert_close(shell_clearance(DBundle=.2), .0032) assert_close(shell_clearance(DBundle=1.778), 0.0095) assert_close(shell_clearance(DShell=1.245), 0.0064) assert_close(shell_clearance(DShell=4), 0.011) assert_close(shell_clearance(DShell=.2), .0032) assert_close(shell_clearance(DShell=1.778), 0.0095) with pytest.raises(Exception): shell_clearance() def test_L_unsupported_max(): assert_close(L_unsupported_max(Do=.0254, material="CS"), 1.88) assert_close(L_unsupported_max(Do=.0253, material="CS"), 1.753) assert_close(L_unsupported_max(Do=1E-5, material="CS"), 0.66) assert_close(L_unsupported_max(Do=.00635, material="CS"), 0.66) assert_close(L_unsupported_max(Do=.00635, material="aluminium"), 0.559) with pytest.raises(Exception): L_unsupported_max(Do=.0254, material="BADMATERIAL") # Terribly pessimistic assert_close(L_unsupported_max(Do=10, material="CS"), 3.175) def test_issue_6(): at_error = P_NTU_method(m1=3, m2=3, Cp1=1860., Cp2=1860, subtype="counterflow", Ntp=4, T2i=15, T1i=130, UA=3041.75) before_error = P_NTU_method(m1=3, m2=3*(1+1e-8), Cp1=1860., Cp2=1860, subtype="counterflow", Ntp=4, T2i=15, T1i=130, UA=3041.75) for k, v in at_error.items(): assert_close(v, before_error[k], rtol=1e-8) Flowh = 5 Flowc = 5 Cph = 4000 Cpc = 4000 subtype = "counterflow" UA = 2500 Thi = 90 Tci = 0 results = effectiveness_NTU_method(Flowh,Flowc,Cph,Cpc,subtype=subtype,UA=UA, Thi = Thi, Tci=Tci) ================================================ FILE: tests/test_numba.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2020, 2021, 2022 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids import AirCooledExchanger from fluids.constants import foot, inch from fluids.numerics import assert_close import ht.vectorized from ht import Nu_external_horizontal_plate_methods, h_Ganguli_VDI try: import numba import ht.numba import ht.numba_vectorized except: numba = None import numpy as np def mark_as_numba(func): func = pytest.mark.numba(func) # func = pytest.mark.slow(func) func = pytest.mark.skipif(numba is None, reason="Numba is missing")(func) return func @mark_as_numba def test_tube_bank(): # not implemented - dP_Zukauskas kwargs = dict(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=.05, pitch_parallel=.05, Do=.025) assert_close(ht.numba.Nu_Grimison_tube_bank(**kwargs), ht.Nu_Grimison_tube_bank(**kwargs)) assert_close(ht.numba.Zukauskas_tube_row_correction(4, staggered=True), ht.Zukauskas_tube_row_correction(4, staggered=True)) kwargs = dict(Re=1E4, Pr=7., tube_rows=10, pitch_parallel=.05, pitch_normal=.05) assert_close(ht.numba.Nu_Zukauskas_Bejan(**kwargs), ht.Nu_Zukauskas_Bejan(**kwargs)) kwargs = dict(Re=1.32E4, Pr=0.71, tube_rows=8, pitch_parallel=.09, pitch_normal=.05) assert_close(ht.numba.Nu_ESDU_73031(**kwargs), ht.Nu_ESDU_73031(**kwargs)) kwargs = dict(Re=10263.37, Pr=.708, tube_rows=11, pitch_normal=.05, pitch_parallel=.05, Do=.025) assert_close(ht.numba.Nu_HEDH_tube_bank(**kwargs), ht.Nu_HEDH_tube_bank(**kwargs)) kwargs = dict(Re=10263.37, Pr=.708, tube_rows=5, pitch_normal=.05, pitch_parallel=.05, Do=.025) assert_close(ht.numba.Nu_HEDH_tube_bank(**kwargs), ht.Nu_HEDH_tube_bank(**kwargs)) kwargs = dict(m=11., rho=995., mu=0.000803, mu_w=0.000657, DShell=0.584, LSpacing=0.1524, pitch=0.0254, Do=.019, NBaffles=22) assert_close(ht.numba.dP_Kern(**kwargs), ht.dP_Kern(**kwargs)) assert_close(ht.numba.baffle_correction_Bell(0.82, "chebyshev"), ht.numba.baffle_correction_Bell(0.82, "chebyshev")) assert_close(ht.numba.baffle_correction_Bell(0.82), ht.numba.baffle_correction_Bell(0.82)) assert_close(ht.numba.baffle_leakage_Bell(1, 3, 8), ht.baffle_leakage_Bell(1, 3, 8)) assert_close(ht.numba.baffle_leakage_Bell(1, 3, 8, "HEDH"), ht.baffle_leakage_Bell(1, 3, 8, "HEDH")) assert_close(ht.numba.bundle_bypassing_Bell(0.5, 5, 25), ht.bundle_bypassing_Bell(0.5, 5, 25)) assert_close(ht.numba.unequal_baffle_spacing_Bell(16, .1, .15, 0.15), ht.unequal_baffle_spacing_Bell(16, .1, .15, 0.15)) @mark_as_numba def test_conv_internal(): assert_close(ht.numba.Nu_conv_internal(Re=1E2, Pr=.7, x=.01, Di=.1), ht.Nu_conv_internal(Re=1E2, Pr=.7, x=.01, Di=.1)) @mark_as_numba def test_conv_free_immersed(): assert_close(ht.numba.Nu_vertical_cylinder(0.72, 1E7, L=1.0, D=3.), ht.Nu_vertical_cylinder(0.72, 1E7, L=1.0, D=3.0)) @mark_as_numba def test_conv_free_enclosed(): assert_close(ht.numba.Nu_Nusselt_Rayleigh_Holling_Herwig(5.54, 3.21e8, buoyancy=True), ht.Nu_Nusselt_Rayleigh_Holling_Herwig(5.54, 3.21e8, buoyancy=True)) assert_close(ht.numba.Rac_Nusselt_Rayleigh(1, .5, 2, False), ht.Rac_Nusselt_Rayleigh(1, .5, 2, False)) assert_close(ht.numba.Rac_Nusselt_Rayleigh(1, .5, 2, True), ht.Rac_Nusselt_Rayleigh(1, .5, 2, True)) assert_close(ht.numba.Rac_Nusselt_Rayleigh_disk(H=1, D=4, insulated=False), ht.Rac_Nusselt_Rayleigh_disk(H=1, D=4, insulated=False)) assert_close(ht.numba.Rac_Nusselt_Rayleigh_disk(H=1, D=4, insulated=True), ht.Rac_Nusselt_Rayleigh_disk(H=1, D=4, insulated=True)) assert_close(ht.numba.Nu_free_vertical_plate(0.69, 2.63E9, False), ht.Nu_free_vertical_plate(0.69, 2.63E9, False)) assert_close(ht.numba.Nu_free_horizontal_plate(5.54, 3.21e8, buoyancy=True), ht.Nu_free_horizontal_plate(5.54, 3.21e8, buoyancy=True)) @mark_as_numba def test_conv_external(): assert_close(ht.numba.Nu_cylinder_Whitaker(6071.0, 0.7), ht.Nu_cylinder_Whitaker(6071.0, 0.7)) assert_close(ht.numba.Nu_cylinder_Perkins_Leppert_1962(6071.0, 0.7), ht.Nu_cylinder_Perkins_Leppert_1962(6071.0, 0.7)) assert_close(ht.numba.Nu_cylinder_Perkins_Leppert_1964(6071.0, 0.7), ht.Nu_cylinder_Perkins_Leppert_1964(6071.0, 0.7)) assert ht.numba.Nu_external_cylinder_methods(0.72, 1E7) == ht.Nu_external_cylinder_methods(0.72, 1E7) assert_close(ht.numba.Nu_external_cylinder(6071, 0.7), ht.Nu_external_cylinder(6071, 0.7)) assert Nu_external_horizontal_plate_methods(Re=1e7, Pr=.7) == ht.numba.Nu_external_horizontal_plate_methods(Re=1e7, Pr=.7) assert_close(ht.numba.Nu_external_horizontal_plate(Re=1E7, Pr=.7), ht.Nu_external_horizontal_plate(Re=1E7, Pr=.7)) @mark_as_numba def test_core_misc(): assert_close(ht.numba.LMTD(100., 60., 20., 60, counterflow=False), ht.LMTD(100., 60., 20., 60, counterflow=False)) assert_close(ht.numba.fin_efficiency_Kern_Kraus(0.0254, 0.05715, 3.8E-4, 200, 58), ht.fin_efficiency_Kern_Kraus(0.0254, 0.05715, 3.8E-4, 200, 58),) assert_close(ht.numba.wall_factor(mu=8E-4, mu_wall=3E-4, Pr=1.2, Pr_wall=1.1, T=300,T_wall=350, property_option="Prandtl"), ht.wall_factor(mu=8E-4, mu_wall=3E-4, Pr=1.2, Pr_wall=1.1, T=300,T_wall=350, property_option="Prandtl")) @mark_as_numba def test_air_cooler(): assert_close(ht.numba.Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4), ht.air_cooler.Ft_aircooler(Thi=125., Tho=45., Tci=25., Tco=95., Ntp=1, rows=4)) assert_close(ht.numba.air_cooler_noise_GPSA(tip_speed=3177/60, power=25.1*750), ht.air_cooler_noise_GPSA(tip_speed=3177/60, power=25.1*750)) AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=20, tube_length=3, tube_diameter=1*inch, fin_thickness=0.000406, fin_density=1/0.002309, pitch_normal=.06033, pitch_parallel=.05207, fin_height=0.0159, tube_thickness=(.0254-.0186)/2, bundles_per_bay=1, parallel_bays=1, corbels=True) # 2.5 us numba, 30 us CPython, 100 us PyPy assert_close(ht.numba.h_Briggs_Young(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205), ht.h_Briggs_Young(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205),) assert_close(ht.numba.h_ESDU_high_fin(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205, Pr_wall=7.0), ht.h_ESDU_high_fin(m=21.56, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.161, Cp=1007., mu=1.85E-5, k=0.0263, k_fin=205, Pr_wall=7.0)) assert_close(ht.numba.h_ESDU_low_fin(m=0.914, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.217, Cp=1007., mu=1.8E-5, k=0.0253, k_fin=15), ht.h_ESDU_low_fin(m=0.914, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_normal=AC.pitch_normal, pitch_parallel=AC.pitch_parallel, rho=1.217, Cp=1007., mu=1.8E-5, k=0.0253, k_fin=15)) AC = AirCooledExchanger(tube_rows=4, tube_passes=4, tubes_per_row=56, tube_length=36*foot, tube_diameter=1*inch, fin_thickness=0.013*inch, fin_density=10/inch, angle=30, pitch_normal=2.5*inch, fin_height=0.625*inch, corbels=True) kwargs = dict(m=130.70315, A=AC.A, A_min=AC.A_min, A_increase=AC.A_increase, A_fin=AC.A_fin, A_tube_showing=AC.A_tube_showing, tube_diameter=AC.tube_diameter, fin_diameter=AC.fin_diameter, bare_length=AC.bare_length, fin_thickness=AC.fin_thickness, tube_rows=AC.tube_rows, pitch_parallel=AC.pitch_parallel, pitch_normal=AC.pitch_normal, rho=1.2013848, Cp=1009.0188, mu=1.9304793e-05, k=0.027864828, k_fin=238) h_numba = ht.numba.h_Ganguli_VDI(**kwargs) h_normal = h_Ganguli_VDI(**kwargs) assert_close(h_numba, h_normal, rtol=1e-11) @mark_as_numba def test_boiling_flow(): assert_close(ht.numba.Thome(m=1, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300, Cpg=1400, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, q=1E5), ht.Thome(m=1, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300, Cpg=1400, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, q=1E5)) Te = 32.04944566414243 assert_close(ht.numba.Thome(m=10, x=0.5, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300, Cpg=1400, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, Te=Te), ht.Thome(m=10, x=0.5, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, mul=156E-6, mug=1E-5, Cpl=2300, Cpg=1400, sigma=0.02, Hvap=9E5, Psat=1E5, Pc=22E6, Te=Te)) assert_close(ht.numba.Lazarek_Black(m=10, D=0.3, mul=1E-3, kl=0.6, Hvap=2E6, Te=100), ht.Lazarek_Black(m=10, D=0.3, mul=1E-3, kl=0.6, Hvap=2E6, Te=100)) assert_close(ht.numba.Li_Wu(m=1, x=0.2, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, q=1E5), ht.Li_Wu(m=1, x=0.2, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, q=1E5)) kwargs = dict(m=1.0, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, sigma=0.02, Hvap=9E5, Te=10.0) assert_close(ht.numba.Sun_Mishima(**kwargs), ht.Sun_Mishima(**kwargs)) kwargs = dict(m=1.0, x=0.4, D=0.3, rhol=567., mul=156E-6, sigma=0.02, Hvap=9E5, q=1E4) assert_close(ht.numba.Yun_Heo_Kim(**kwargs), ht.Yun_Heo_Kim(**kwargs)) kwargs = dict(m=0.106, x=0.2, D=0.0212, rhol=567, rhog=18.09, mul=156E-6, mug=7.11E-6, kl=0.086, Cpl=2730, Hvap=2E5, sigma=0.02, dPsat=1E5, Te=3) assert_close(ht.numba.Chen_Edelstein(**kwargs), ht.Chen_Edelstein(**kwargs)) kwargs = dict(m=0.106, x=0.2, D=0.0212, rhol=567.0, rhog=18.09, mul=156E-6, mug=7.11E-6, kl=0.086, Cpl=2730.0, Hvap=2E5, sigma=0.02, dPsat=1E5, Te=3.0) assert_close(ht.numba.Chen_Bennett(**kwargs), ht.Chen_Bennett(**kwargs)) kwargs = dict(m=1.0, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Cpl=2300.0, P=1E6, Pc=22E6, MW=44.02, Te=7.0) assert_close(ht.numba.Liu_Winterton(**kwargs), ht.Liu_Winterton(**kwargs)) @mark_as_numba def test_radiation(): assert_close(ht.numba.radiation. blackbody_spectral_radiance(800., 4E-6), ht.radiation. blackbody_spectral_radiance(800., 4E-6)) @mark_as_numba def test_conv_jacket(): assert_close(ht.Stein_Schmidt(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, rhow=971.8), ht.numba.Stein_Schmidt(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, rhow=971.8)) assert_close(ht.numba.Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6), ht.Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6)) assert_close(ht.numba.Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype="radial", isobaric_expansion=0.000303), ht.Lehrer(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, dT=20., rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, inlettype="radial", isobaric_expansion=0.000303)) @mark_as_numba def test_condensation(): assert_close(ht.numba.Nusselt_laminar(Tsat=370, Tw=350, rhog=7.0, rhol=585., kl=0.091, mul=158.9E-6, Hvap=776900, L=0.1), ht.Nusselt_laminar(Tsat=370, Tw=350, rhog=7.0, rhol=585., kl=0.091, mul=158.9E-6, Hvap=776900, L=0.1)) assert_close(ht.numba.Akers_Deans_Crosser(m=0.35, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=0.85), ht.Akers_Deans_Crosser(m=0.35, rhog=6.36, rhol=582.9, kl=0.098, mul=159E-6, Cpl=2520., D=0.03, x=0.85)) assert_close(ht.numba.Cavallini_Smith_Zecchin(m=1, x=0.4, D=.3, rhol=800, rhog=2.5, mul=1E-5, mug=1E-3, kl=0.6, Cpl=2300), ht.Cavallini_Smith_Zecchin(m=1, x=0.4, D=.3, rhol=800, rhog=2.5, mul=1E-5, mug=1E-3, kl=0.6, Cpl=2300)) assert_close(ht.numba.Shah(m=1, x=0.4, D=.3, rhol=800, mul=1E-5, kl=0.6, Cpl=2300, P=1E6, Pc=2E7), ht.Shah(m=1, x=0.4, D=.3, rhol=800, mul=1E-5, kl=0.6, Cpl=2300, P=1E6, Pc=2E7)) @mark_as_numba def test_boiling_plate(): assert_close(ht.numba.h_boiling_Amalfi(m=3E-5, x=.4, Dh=0.00172, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=7.11E-6, sigma=0.02, Hvap=9E5, q=1E5, A_channel_flow=0.0003), ht.h_boiling_Amalfi(m=3E-5, x=.4, Dh=0.00172, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=7.11E-6, sigma=0.02, Hvap=9E5, q=1E5, A_channel_flow=0.0003)) assert_close(ht.numba.h_boiling_Lee_Kang_Kim(m=3E-5, x=.4, D_eq=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=9E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003), ht.h_boiling_Lee_Kang_Kim(m=3E-5, x=.4, D_eq=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, mug=9E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003)) assert_close(ht.numba.h_boiling_Han_Lee_Kim(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Hvap=9E5, Cpl=2200, q=1E5, A_channel_flow=0.0003, wavelength=3.7E-3, chevron_angle=45), ht.h_boiling_Han_Lee_Kim(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Hvap=9E5, Cpl=2200, q=1E5, A_channel_flow=0.0003, wavelength=3.7E-3, chevron_angle=45)) assert_close(ht.numba.h_boiling_Huang_Sheer(rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Hvap=9E5, sigma=0.02, Cpl=2200, q=1E4, Tsat=279.15), ht.h_boiling_Huang_Sheer(rhol=567., rhog=18.09, kl=0.086, mul=156E-6, Hvap=9E5, sigma=0.02, Cpl=2200, q=1E4, Tsat=279.15)) assert_close(ht.numba.h_boiling_Yan_Lin(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, kl=0.086, Cpl=2200, mul=156E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003), ht.h_boiling_Yan_Lin(m=3E-5, x=.4, Dh=0.002, rhol=567., rhog=18.09, kl=0.086, Cpl=2200, mul=156E-6, Hvap=9E5, q=1E5, A_channel_flow=0.0003)) @mark_as_numba def test_Ntubes_Phadkeb(): # Extremely impressive performance Bundles = np.linspace(1, 2, 5) Dos = np.linspace(.028, .029, 5) pitches = np.linspace(.036, .037, 5) Ntps = np.linspace(2, 2, 5, dtype=np.int64) angles = np.linspace(45, 45, 5, dtype=np.int64) assert 782 == ht.numba.Ntubes_Phadkeb(DBundle=1.200-.008*2, Do=.028, pitch=.036, Ntp=2, angle=45.) # assert_close(ht.numba_vectorized.Ntubes_Phadkeb(Bundles, Dos, pitches, Ntps, angles), # [ 558, 862, 1252, 1700, 2196]) @mark_as_numba def test_boiling_nucleic(): assert_close(ht.numba.Rohsenow(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9, Csf=0.011, n=1.26), ht.Rohsenow(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9, Csf=0.011, n=1.26)) numba_methods = ht.numba.h_nucleic_methods(P=3E5, Pc=22048320., Te=4.0, CAS="7732-18-5") regular_methods = ht.h_nucleic_methods(P=3E5, Pc=22048320., Te=4.0, CAS="7732-18-5") assert numba_methods == regular_methods # Has a TON of arguments, and numba wants them all to not be Nones. assert_close(ht.numba.h_nucleic(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9, Csf=0.011, n=1.26, P=1e4, Pc=1e6, Tsat=10, MW=33.0, Method="Rohsenow"), ht.h_nucleic(rhol=957.854, rhog=0.595593, mul=2.79E-4, kl=0.680, Cpl=4217, Hvap=2.257E6, sigma=0.0589, Te=4.9, Csf=0.011, n=1.26, P=1e4, Pc=1e6, Tsat=10, MW=33.0, Method="Rohsenow")) kwargs = dict(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567.0, rhog=18.09, P=1e6, Pc=1e7) assert_close(ht.numba.qmax_boiling(**kwargs), ht.qmax_boiling(**kwargs)) kwargs = dict(D=0.0127, sigma=8.2E-3, Hvap=272E3, rhol=567, rhog=18.09) assert ht.qmax_boiling_methods(**kwargs) == ht.numba.qmax_boiling_methods(**kwargs) @mark_as_numba def test_packed_bed(): # All good assert_close(ht.numba.Nu_packed_bed_Gnielinski(dp=8E-4, voidage=0.4, vs=1, rho=1E3, mu=1E-3, Pr=0.7), ht.Nu_packed_bed_Gnielinski(dp=8E-4, voidage=0.4, vs=1, rho=1E3, mu=1E-3, Pr=0.7)) @mark_as_numba def test_two_phase(): kwargs = dict(m=1.0, x=.9, D=.3, alpha=.9, rhol=1000.0, rhog=2.2, mug=1e-5, Cpl=2300.0, kl=.6, mul=1e-3, mu_b=1E-3, mu_w=1.2E-3, L=5) assert_close(ht.numba.h_two_phase(**kwargs), ht.h_two_phase(**kwargs)) @mark_as_numba def test_supercritical(): assert_close(ht.numba.Nu_Griem(1E5, 1.2), ht.Nu_Griem(1E5, 1.2)) kwargs = dict(Re=1E5, Pr=1.2, rho_w=125.8, rho_b=249.0233, Cp_avg=2080.845, Cp_b=2048.621, T_b=650, T_w=700, T_pc=300.0) assert_close(ht.numba.Nu_Jackson(**kwargs), ht.Nu_Jackson(**kwargs)) assert_close(ht.numba.Nu_Jackson(1E5, 1.2), ht.Nu_Jackson(1E5, 1.2)) assert_close(ht.numba.Nu_Gupta(1E5, 1.2), ht.Nu_Gupta(1E5, 1.2)) assert_close(ht.numba.Nu_Swenson(1E5, 1.2), ht.Nu_Swenson(1E5, 1.2)) assert_close(ht.numba.Nu_Xu(1E5, 1.2), ht.Nu_Xu(1E5, 1.2)) assert_close(ht.numba.Nu_Mokry(1E5, 1.2), ht.Nu_Mokry(1E5, 1.2)) assert_close(ht.numba.Nu_Ornatsky(1E5, 1.2, 1.5), ht.Nu_Ornatsky(1E5, 1.2, 1.5)) assert_close(ht.numba.Nu_Zhu(1E5, 1.2), ht.Nu_Zhu(1E5, 1.2)) kwargs = dict(Re=1E5, Pr=1.2, Pr_pc=1.5, Cp_avg=2080.845, Cp_b=2048.621, T_b=650, T_w=700, T_pc=600.0) assert_close(ht.numba.Nu_Yamagata(**kwargs), ht.Nu_Yamagata(**kwargs)) assert_close(ht.numba.Nu_Kitoh(1E5, 1.2), ht.Nu_Kitoh(1E5, 1.2)) assert_close(ht.numba.Nu_Krasnoshchekov_Protopopov(1E5, 1.2), ht.Nu_Krasnoshchekov_Protopopov(1E5, 1.2)) assert_close(ht.numba.Nu_Petukhov(1E5, 1.2), ht.Nu_Petukhov(1E5, 1.2)) assert_close(ht.numba.Nu_Krasnoshchekov(1E5, 1.2), ht.Nu_Krasnoshchekov(1E5, 1.2)) @mark_as_numba def test_conduction(): assert_close(ht.numba.R_value_to_k(1., SI=False), ht.numba.R_value_to_k(1., SI=False)) assert_close(ht.numba.S_isothermal_pipe_to_isothermal_pipe(.1, .2, 1, 1), ht.S_isothermal_pipe_to_isothermal_pipe(.1, .2, 1, 1)) # cylindrical_heat_transfer returns a dictionary, not supported by numba @mark_as_numba def test_hx_tube_bundles(): kwargs = dict(Ntubes=782, Do=.028, pitch=.036, Ntp=2, angle=45.) assert_close(ht.numba.DBundle_for_Ntubes_Phadkeb(**kwargs), ht.DBundle_for_Ntubes_Phadkeb(**kwargs)) kwargs = dict(DBundle=1.184, Do=.028, Ntp=2, angle=45) assert_close(ht.numba.Ntubes_Perrys(**kwargs), ht.Ntubes_Perrys(**kwargs)) assert_close(ht.numba.Ntubes_VDI(DBundle=1.184, Ntp=2, Do=.028, pitch=.036, angle=30), ht.Ntubes_VDI(DBundle=1.184, Ntp=2, Do=.028, pitch=.036, angle=30) ) kwargs = dict(DBundle=1.2, Do=0.025, pitch=0.03125, Method="Phadkeb") assert ht.numba.Ntubes(**kwargs) == ht.Ntubes(**kwargs) kwargs = dict(N=1285, Do=0.025, pitch=0.03125) assert_close(ht.numba.size_bundle_from_tubecount(**kwargs), ht.size_bundle_from_tubecount(**kwargs)) @mark_as_numba def test_hx_data(): assert_close(ht.L_unsupported_max(Do=.0254, material="CS"), ht.numba.L_unsupported_max(Do=.0254, material="CS")) assert_close(ht.numba.baffle_thickness(Dshell=.3, L_unsupported=50, service="R"), ht.baffle_thickness(Dshell=.3, L_unsupported=50, service="R")) @mark_as_numba def test_hx_effectiveness_still_working(): assert_close(ht.numba.temperature_effectiveness_air_cooler(.5, 2, rows=10, passes=10), ht.temperature_effectiveness_air_cooler(.5, 2, rows=10, passes=10)) assert_close(ht.numba.hx.P_NTU_Pp(5, .4), ht.hx.P_NTU_Pp(5, .4)) assert_close(ht.numba.hx.P_NTU_Pc(5, .4), ht.hx.P_NTU_Pc(5, .4)) # broken by numba in the 0.57 release # @mark_as_numba # def test_hx_effectiveness_basic(): # R1 = 3.811315897216142e-05 # NTU1 = 0.31156549511556475 # assert_close(ht.numba.temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype='crossflow'), # ht.temperature_effectiveness_basic(R1=R1, NTU1=NTU1, subtype='crossflow')) # P1 = ht.numba.temperature_effectiveness_basic(R1=R1, NTU1=NTU1, # subtype='crossflow') # NTU1_calc = ht.numba.NTU_from_P_basic(P1, R1, subtype='crossflow') # assert_close(NTU1, NTU1_calc) # # broken by numba in the 0.57 release # @mark_as_numba # def test_hx_effectiveness_broken(): # assert_close(ht.numba.NTU_from_P_J(P1=.99, R1=.01, Ntp=2), # ht.NTU_from_P_J(P1=.99, R1=.01, Ntp=2)) # assert_close(ht.numba.NTU_from_P_G(P1=.573, R1=1/3., Ntp=1), # ht.NTU_from_P_G(P1=.573, R1=1/3., Ntp=1)) # assert_close(ht.numba.NTU_from_P_E(P1=.58, R1=1/3., Ntp=2), # ht.NTU_from_P_E(P1=.58, R1=1/3., Ntp=2)) # assert_close(ht.numba.NTU_from_P_H(P1=0.573, R1=1/3., Ntp=1), # ht.NTU_from_P_H(P1=0.573, R1=1/3., Ntp=1)) # # Quite literally 20x faster than CPython; Pypy is unfortunately slow as there is a scipy function # assert_close(ht.numba.NTU_from_P_plate(P1=0.5743, R1=1/3., Np1=3, Np2=1), # ht.NTU_from_P_plate(P1=0.5743, R1=1/3., Np1=3, Np2=1)) @mark_as_numba def test_conv_plate(): kwargs = dict(Re=2000, Pr=0.7, chevron_angle=30.0, mu=1E-3, mu_wall=8E-4) assert_close(ht.numba.Nu_plate_Kumar(**kwargs), ht.Nu_plate_Kumar(**kwargs)) kwargs = dict(Re=2000, Pr=.7, chevron_angle=30.0) assert_close(ht.numba.Nu_plate_Martin(**kwargs), ht.Nu_plate_Martin(**kwargs)) ================================================ FILE: tests/test_radiation.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2016, 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import pytest from fluids.numerics import assert_close, assert_close1d from ht import blackbody_spectral_radiance, grey_transmittance, q_rad, solar_spectrum def test_radiation(): assert_close(q_rad(1., 400), 1451.613952, rtol=1e-05) assert_close(q_rad(.85, 400, 305.), 816.7821722650002, rtol=1e-05) assert 0.0 == blackbody_spectral_radiance(5500., 5E-10) assert_close(blackbody_spectral_radiance(800., 4E-6), 1311692056.2430143, rtol=1e-05) @pytest.mark.slow def test_solar_spectrum(): from scipy.integrate import trapezoid wavelengths, SSI, uncertainties = solar_spectrum() min_maxes = [min(wavelengths), max(wavelengths), min(SSI), max(SSI)] min_maxes_expect = [5.0000000000000003e-10, 2.9999000000000003e-06, 1330.0, 2256817820.0] assert_close1d(min_maxes, min_maxes_expect) assert_close(trapezoid(SSI, wavelengths), 1344.8029782379999) def test_grey_transmittance(): tau = grey_transmittance(3.8e-4, molar_density=55300, length=1e-2) assert_close(tau, 0.8104707721191062) ================================================ FILE: tests/test_units.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import types import pytest from fluids.numerics import assert_close from fluids.units import check_module_docstring_parameters import ht from ht.units import P_NTU_method, R_to_k, R_value_to_k, effectiveness_NTU_method, k_to_R_value, u def assert_pint_allclose(value, magnitude, units, rtol=1e-7): assert_close(value.to_base_units().magnitude, magnitude, rtol=rtol) assert dict(value.dimensionality) == units def test_sample_cases(): ans = effectiveness_NTU_method(mh=5.2*u.kg/u.s, mc=1.45*u.kg/u.s, Cph=1860.*u.J/u.K/u.kg, Cpc=1900*u.J/u.K/u.kg, subtype="crossflow, mixed Cmax", Tci=15*u.K, Tco=85*u.K, Thi=130*u.K) assert_pint_allclose(ans["Cmax"], 9672.0, {"[length]": 2.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) assert_pint_allclose(ans["Cmin"], 2755.0, {"[length]": 2.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) assert_pint_allclose(ans["Cr"], 0.2848428453267163, {}) assert_pint_allclose(ans["UA"], 3041.751170834494, {"[length]": 2.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) assert_pint_allclose(ans["Q"], 192850, {"[length]": 2.0, "[mass]": 1.0, "[time]": -3.0}) assert_pint_allclose(ans["NTU"], 1.1040839095588, {}) assert_pint_allclose(ans["effectiveness"], 0.6086956521739131, {}) assert_pint_allclose(ans["Tci"], 15, {"[temperature]": 1.0}) assert_pint_allclose(ans["Tco"], 85, {"[temperature]": 1.0}) assert_pint_allclose(ans["Thi"], 130, {"[temperature]": 1.0}) assert_pint_allclose(ans["Tho"], 110.06100082712986, {"[temperature]": 1.0}) ans = P_NTU_method(m1=5.2*u.kg/u.s, m2=1.45*u.kg/u.s, Cp1=1860.*u.J/u.kg/u.K, Cp2=1900*u.J/u.kg/u.K, subtype="E", Ntp=4, T2i=15*u.K, T1i=130*u.K, UA=3041.75*u.W/u.K) assert_pint_allclose(ans["C1"], 9672.0, {"[length]": 2.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) assert_pint_allclose(ans["C2"], 2755.0, {"[length]": 2.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) assert_pint_allclose(ans["UA"], 3041.75, {"[length]": 2.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) assert_pint_allclose(ans["Q"], 192514.71424206023, {"[length]": 2.0, "[mass]": 1.0, "[time]": -3.0}) assert_pint_allclose(ans["NTU1"], 0.3144902812241522, {}) assert_pint_allclose(ans["NTU2"], 1.1040834845735028, {}) assert_pint_allclose(ans["T2i"], 15, {"[temperature]": 1.0}) assert_pint_allclose(ans["T2o"], 84.87829918042112, {"[temperature]": 1.0}) assert_pint_allclose(ans["T1i"], 130, {"[temperature]": 1.0}) assert_pint_allclose(ans["T1o"], 110.09566643485729, {"[temperature]": 1.0}) assert_pint_allclose(ans["P1"], 0.1730811614360235, {}) assert_pint_allclose(ans["P2"], 0.6076373841775751, {}) assert_pint_allclose(ans["R1"], 3.5107078039927404, {}) assert_pint_allclose(ans["R2"], 0.2848428453267163, {}) def test_custom_wraps(): k = R_to_k(R=1*u.K/u.W, t=.01*u.m) assert_pint_allclose(k, 1E-2, {"[length]": 1.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) k = R_to_k(R=1*u.K/u.W*u.m**2, t=.01*u.m, A=5*u.m**2) assert_pint_allclose(k, .002, {"[length]": 1.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) with pytest.raises(Exception): R_to_k(R=1*u.K/u.W, t=.01*u.m, A=2*u.m**2) # R_value_to_k k = R_value_to_k(0.12*u.parse_expression("m^2*K/(W*inch)")) assert_pint_allclose(k, 0.2116666666666667, {"[length]": 1.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}) k = R_value_to_k(0.71*u.parse_expression("ft^2*delta_degF*hour/(BTU*inch)")) assert_pint_allclose(k, 0.20313790001601909, {"[length]": 1.0, "[mass]": 1.0, "[temperature]": -1.0, "[time]": -3.0}, rtol=1e-4) # k_to_R_value R_value = k_to_R_value(k=0.2116666666666667*u.W/u.m/u.K, SI=True) assert_pint_allclose(R_value.to_base_units(), 4.724409448818897, {"[length]": -1.0, "[mass]": -1.0, "[temperature]": 1.0, "[time]": 3.0}, rtol=1e-4) R_value = k_to_R_value(k=0.71*u.W/u.m/u.K, SI=False) assert_pint_allclose(R_value.to_base_units(), 1.4084507042253525, {"[length]": -1.0, "[mass]": -1.0, "[temperature]": 1.0, "[time]": 3.0}, rtol=1e-4) def test_check_signatures(): from fluids.units import check_args_order bad_names = {"__getattr__"} for name in dir(ht): if name in bad_names: continue obj = getattr(ht, name) if isinstance(obj, types.FunctionType): check_args_order(obj) def test_parse_numpydoc_variables_units(): import ht check_module_docstring_parameters(ht) ================================================ FILE: tests/test_vectorized.py ================================================ """Chemical Engineering Design Library (ChEDL). Utilities for process modeling. Copyright (C) 2017 Caleb Bell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from fluids.numerics import assert_close1d import ht import ht.vectorized def test_LMTD_vect(): dTlms = [ht.LMTD(T, 60., 30., 40.2) for T in [100, 101]] dTlms_vect = ht.vectorized.LMTD([100, 101], 60., 30., 40.2) assert_close1d(dTlms, dTlms_vect)