Full Code of CalebBell/ht for AI

master 85e0ee686ec9 cached
100 files
1.2 MB
450.7k tokens
518 symbols
1 requests
Download .txt
Showing preview only (1,316K chars total). Download the full file or copy to clipboard to get everything.
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 <Caleb.Andrew.Bell@gmail.com>



================================================
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 <arch> <distro>).
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 <arch> <distro>).
## 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 <Caleb.Andrew.Bell@gmail.com>

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 <https://conda.io/en/latest/>`_ as your package management, you can simply
install ht in your environment from `conda-forge <https://conda-forge.org/>`_ 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
# "<project> v<release> 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 <link> 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 <https://github.com/numba/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 <Caleb.Andrew.Bell@gmail.com>

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 <https://github.com/hgrecco/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 <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 <https://github.com/numba/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 <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 <Caleb.Andrew.Bell@gmail.com>

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 <Caleb.Andrew.Bell@gmail.com>

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 <Caleb.Andrew.Bell@gmail.com>

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} +
Download .txt
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
Download .txt
SYMBOL INDEX (518 symbols across 49 files)

FILE: conftest.py
  function pytest_ignore_collect (line 12) | def pytest_ignore_collect( collection_path, config):
  function pytest_configure (line 57) | def pytest_configure(config):

FILE: dev/basic_standalone_ht_check.py
  function check_close (line 10) | def check_close(a, b, rtol=1e-7, atol=0):
  function run_checks (line 14) | def run_checks():

FILE: dev/prerelease.py
  function set_file_modification_time (line 18) | def set_file_modification_time(filename, mtime):

FILE: docs/conf.py
  function setup (line 334) | def setup(app):

FILE: docs/test_documentation.py
  function test_documentation_plots (line 37) | def test_documentation_plots(file):

FILE: ht/air_cooler.py
  function Ft_aircooler (line 110) | def Ft_aircooler(Thi: float, Tho: float, Tci: float, Tco: float, Ntp: in...
  function air_cooler_noise_GPSA (line 224) | def air_cooler_noise_GPSA(tip_speed: float, power: float) -> float:
  function air_cooler_noise_Mukherjee (line 265) | def air_cooler_noise_Mukherjee(tip_speed: float, power: float, fan_diame...
  function h_Briggs_Young (line 312) | def h_Briggs_Young(m: float, A: float, A_min: float, A_increase: float, ...
  function h_ESDU_high_fin (line 422) | def h_ESDU_high_fin(m: float, A: float, A_min: float, A_increase: float,...
  function h_ESDU_low_fin (line 567) | def h_ESDU_low_fin(m: float, A: float, A_min: float, A_increase: float, ...
  function h_Ganguli_VDI (line 712) | def h_Ganguli_VDI(m: float, A: float, A_min: float, A_increase: float, A...
  function dP_ESDU_high_fin (line 856) | def dP_ESDU_high_fin(m: float, A_min: float, A_increase: float, flow_are...
  function dP_ESDU_low_fin (line 947) | def dP_ESDU_low_fin(m: float, A_min: float, A_increase: float, flow_area...

FILE: ht/boiling_flow.py
  function Lazarek_Black (line 47) | def Lazarek_Black(m: float, D: float, mul: float, kl: float, Hvap: float...
  function Li_Wu (line 126) | def Li_Wu(m: float, x: float, D: float, rhol: float, rhog: float, mul: f...
  function Sun_Mishima (line 212) | def Sun_Mishima(m: float, D: float, rhol: float, rhog: float, mul: float...
  function Thome (line 295) | def Thome(m: float, x: float, D: float, rhol: float, rhog: float, mul: f...
  function to_solve_q_Thome (line 527) | def to_solve_q_Thome(q: float, m: float, x: float, D: float, rhol: float...
  function Yun_Heo_Kim (line 531) | def Yun_Heo_Kim(m: float, x: float, D: float, rhol: float, mul: float, H...
  function Chen_Edelstein (line 614) | def Chen_Edelstein(m: float, x: float, D: float, rhol: float, rhog: floa...
  function Chen_Bennett (line 729) | def Chen_Bennett(m: float, x: float, D: float, rhol: float, rhog: float,...
  function Liu_Winterton (line 848) | def Liu_Winterton(m: float, x: float, D: float, rhol: float, rhog: float...

FILE: ht/boiling_nucleic.py
  function Rohsenow (line 52) | def Rohsenow(rhol: float, rhog: float, mul: float, kl: float, Cpl: float...
  function McNelly (line 133) | def McNelly(rhol: float, rhog: float, kl: float, Cpl: float, Hvap: float...
  function Forster_Zuber (line 206) | def Forster_Zuber(rhol: float, rhog: float, mul: float, kl: float, Cpl: ...
  function Montinsky (line 284) | def Montinsky(P: float, Pc: float, Te: float | None=None, q: float | Non...
  function Stephan_Abdelsalam (line 359) | def Stephan_Abdelsalam(rhol: float, rhog: float, mul: float, kl: float, ...
  function HEDH_Taborek (line 534) | def HEDH_Taborek(P: float, Pc: float, Te: float | None=None, q: float | ...
  function Bier (line 601) | def Bier(P: float, Pc: float, Te: float | None=None, q: float | None=Non...
  function Cooper (line 661) | def Cooper(P: float, Pc: float, MW: float, Te: float | None=None, q: flo...
  function Gorenflo (line 752) | def Gorenflo(P: float, Pc: float, q: float | None=None, Te: float | None...
  function h_nucleic_methods (line 896) | def h_nucleic_methods(Te: float | None=None, Tsat: float | None=None, P:...
  function h_nucleic (line 980) | def h_nucleic(Te: float | None=None, q: float | None=None, Tsat: float |...
  function Zuber (line 1132) | def Zuber(sigma: float, Hvap: float, rhol: float, rhog: float, K: float=...
  function Serth_HEDH (line 1191) | def Serth_HEDH(D: float, sigma: float, Hvap: float, rhol: float, rhog: f...
  function HEDH_Montinsky (line 1252) | def HEDH_Montinsky(P: float, Pc: float) -> float:
  function qmax_boiling_methods (line 1302) | def qmax_boiling_methods(rhol: int | None=None, rhog: float | None=None,...
  function qmax_boiling (line 1350) | def qmax_boiling(rhol: int | None=None, rhog: float | None=None, sigma: ...

FILE: ht/boiling_plate.py
  function h_boiling_Amalfi (line 38) | def h_boiling_Amalfi(m: float, x: float, Dh: float, rhol: float, rhog: f...
  function h_boiling_Lee_Kang_Kim (line 145) | def h_boiling_Lee_Kang_Kim(m: float, x: float, D_eq: float, rhol: float,...
  function h_boiling_Han_Lee_Kim (line 240) | def h_boiling_Han_Lee_Kim(m: float, x: float, Dh: float, rhol: float, rh...
  function h_boiling_Huang_Sheer (line 369) | def h_boiling_Huang_Sheer(rhol: float, rhog: float, mul: float, kl: floa...
  function h_boiling_Yan_Lin (line 476) | def h_boiling_Yan_Lin(m: float, x: float, Dh: float, rhol: float, rhog: ...

FILE: ht/condensation.py
  function Nusselt_laminar (line 41) | def Nusselt_laminar(Tsat: float, Tw: float, rhog: float, rhol: float, kl...
  function Boyko_Kruzhilin (line 101) | def Boyko_Kruzhilin(m: float, rhog: float, rhol: float, kl: float, mul: ...
  function Akers_Deans_Crosser (line 165) | def Akers_Deans_Crosser(m: float, rhog: float, rhol: float, kl: float, m...
  function h_kinetic (line 240) | def h_kinetic(T: float, P: float, MW: float, Hvap: float, f: float=1.0) ...
  function Cavallini_Smith_Zecchin (line 296) | def Cavallini_Smith_Zecchin(m: float, x: float, D: float, rhol: float, r...
  function Shah (line 378) | def Shah(m: float, x: float, D: float, rhol: float, mul: float, kl: floa...

FILE: ht/conduction.py
  function R_to_k (line 46) | def R_to_k(R: float, t: float, A: float=1.) -> float:
  function k_to_R (line 88) | def k_to_R(k: float, t: float, A: float=1.) -> float:
  function k_to_thermal_resistivity (line 129) | def k_to_thermal_resistivity(k: float) -> float:
  function thermal_resistivity_to_k (line 166) | def thermal_resistivity_to_k(r: float) -> float:
  function R_value_to_k (line 202) | def R_value_to_k(R_value: float, SI: bool=True) -> float:
  function k_to_R_value (line 244) | def k_to_R_value(k: float, SI: bool=True) -> float:
  function R_cylinder (line 282) | def R_cylinder(Di: float, Do: float, k: float, L: float) -> float:
  function S_isothermal_sphere_to_plane (line 324) | def S_isothermal_sphere_to_plane(D: float, Z: float) -> float:
  function S_isothermal_pipe_to_plane (line 366) | def S_isothermal_pipe_to_plane(D: float, Z: float, L: float=1) -> float:
  function S_isothermal_pipe_normal_to_plane (line 412) | def S_isothermal_pipe_normal_to_plane(D: float, L: float) -> float:
  function S_isothermal_pipe_to_isothermal_pipe (line 455) | def S_isothermal_pipe_to_isothermal_pipe(D1: float, D2: float, W: float,...
  function S_isothermal_pipe_to_two_planes (line 503) | def S_isothermal_pipe_to_two_planes(D: float, Z: float, L: float=1.) -> ...
  function S_isothermal_pipe_eccentric_to_isothermal_pipe (line 551) | def S_isothermal_pipe_eccentric_to_isothermal_pipe(D1: float, D2: float,...
  function cylindrical_heat_transfer (line 603) | def cylindrical_heat_transfer(Ti: float, To: float, hi: float, ho: float...

FILE: ht/conv_external.py
  function Nu_cylinder_Zukauskas (line 50) | def Nu_cylinder_Zukauskas(Re: float, Pr: float, Prw: float | None=None) ...
  function Nu_cylinder_Churchill_Bernstein (line 128) | def Nu_cylinder_Churchill_Bernstein(Re: float, Pr: float) -> float:
  function Nu_cylinder_Sanitjai_Goldstein (line 179) | def Nu_cylinder_Sanitjai_Goldstein(Re: float, Pr: float) -> float:
  function Nu_cylinder_Fand (line 230) | def Nu_cylinder_Fand(Re: float, Pr: float) -> float:
  function Nu_cylinder_McAdams (line 277) | def Nu_cylinder_McAdams(Re: float, Pr: float) -> float:
  function Nu_cylinder_Whitaker (line 320) | def Nu_cylinder_Whitaker(Re: float, Pr: float, mu: float | None=None, mu...
  function Nu_cylinder_Perkins_Leppert_1962 (line 376) | def Nu_cylinder_Perkins_Leppert_1962(Re: float, Pr: float, mu: float | N...
  function Nu_cylinder_Perkins_Leppert_1964 (line 430) | def Nu_cylinder_Perkins_Leppert_1964(Re: float, Pr: float, mu: float | N...
  function Nu_external_cylinder_methods (line 509) | def Nu_external_cylinder_methods(Re: float, Pr: float, Prw: float | None...
  function Nu_external_cylinder (line 551) | def Nu_external_cylinder(Re: float, Pr: float, Prw: float | None=None, m...
  function Nu_horizontal_plate_laminar_Baehr (line 625) | def Nu_horizontal_plate_laminar_Baehr(Re: float, Pr: float) -> float:
  function Nu_horizontal_plate_laminar_Churchill_Ozoe (line 698) | def Nu_horizontal_plate_laminar_Churchill_Ozoe(Re: float, Pr: float) -> ...
  function Nu_horizontal_plate_turbulent_Schlichting (line 746) | def Nu_horizontal_plate_turbulent_Schlichting(Re: float, Pr: float) -> f...
  function Nu_horizontal_plate_turbulent_Kreith (line 792) | def Nu_horizontal_plate_turbulent_Kreith(Re: float, Pr: float) -> float:
  function Nu_external_horizontal_plate_methods (line 846) | def Nu_external_horizontal_plate_methods(Re: float, Pr: float, L: float ...
  function Nu_external_horizontal_plate (line 886) | def Nu_external_horizontal_plate(Re: float, Pr: float, L: None=None, x: ...

FILE: ht/conv_free_enclosed.py
  function Nu_Nusselt_Rayleigh_Holling_Herwig_err (line 42) | def Nu_Nusselt_Rayleigh_Holling_Herwig_err(Nu: float, Ra: float, Ra_thir...
  function Nu_Nusselt_Rayleigh_Holling_Herwig (line 47) | def Nu_Nusselt_Rayleigh_Holling_Herwig(Pr: float, Gr: float, buoyancy: b...
  function Nu_Nusselt_Rayleigh_Probert (line 123) | def Nu_Nusselt_Rayleigh_Probert(Pr: float, Gr: float, buoyancy: bool=Tru...
  function Nu_Nusselt_Rayleigh_Hollands (line 191) | def Nu_Nusselt_Rayleigh_Hollands(Pr: float, Gr: float, buoyancy: bool=Tr...
  function Nu_Nusselt_vertical_Thess (line 280) | def Nu_Nusselt_vertical_Thess(Pr: float, Gr: float, H: int | None=None, ...
  function Rac_Nusselt_Rayleigh (line 416) | def Rac_Nusselt_Rayleigh(H: float, L: float, W: float, insulated: bool=T...
  function Rac_Nusselt_Rayleigh_disk (line 490) | def Rac_Nusselt_Rayleigh_disk(H: float, D: float, insulated: bool=True) ...
  function Nu_vertical_helical_coil_Ali (line 552) | def Nu_vertical_helical_coil_Ali(Pr: float, Gr: float) -> float:
  function Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan (line 602) | def Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan(Pr: float, Gr: f...

FILE: ht/conv_free_immersed.py
  function Nu_vertical_plate_Churchill (line 57) | def Nu_vertical_plate_Churchill(Pr: float, Gr: float) -> float:
  function Nu_free_vertical_plate_methods (line 113) | def Nu_free_vertical_plate_methods(Pr: float, Gr: float, H: float | None...
  function Nu_free_vertical_plate (line 151) | def Nu_free_vertical_plate(Pr: float, Gr: float, buoyancy: None=None, H:...
  function Nu_horizontal_plate_McAdams (line 208) | def Nu_horizontal_plate_McAdams(Pr: float, Gr: float, buoyancy: bool=Tru...
  function Nu_horizontal_plate_VDI (line 267) | def Nu_horizontal_plate_VDI(Pr: float, Gr: float, buoyancy: bool=True) -...
  function Nu_horizontal_plate_Rohsenow (line 337) | def Nu_horizontal_plate_Rohsenow(Pr: float, Gr: float, buoyancy: bool=Tr...
  function Nu_free_horizontal_plate_methods (line 421) | def Nu_free_horizontal_plate_methods(Pr: float, Gr: float, buoyancy: boo...
  function Nu_free_horizontal_plate (line 463) | def Nu_free_horizontal_plate(Pr: float, Gr: float, buoyancy: bool, L: fl...
  function Nu_sphere_Churchill (line 529) | def Nu_sphere_Churchill(Pr: float, Gr: float) -> float:
  function Nu_vertical_cylinder_Griffiths_Davis_Morgan (line 577) | def Nu_vertical_cylinder_Griffiths_Davis_Morgan(Pr: float, Gr: float, tu...
  function Nu_vertical_cylinder_Jakob_Linke_Morgan (line 637) | def Nu_vertical_cylinder_Jakob_Linke_Morgan(Pr: float, Gr: float, turbul...
  function Nu_vertical_cylinder_Carne_Morgan (line 699) | def Nu_vertical_cylinder_Carne_Morgan(Pr: float, Gr: float, turbulent: b...
  function Nu_vertical_cylinder_Eigenson_Morgan (line 762) | def Nu_vertical_cylinder_Eigenson_Morgan(Pr: float, Gr: float, turbulent...
  function Nu_vertical_cylinder_Touloukian_Morgan (line 831) | def Nu_vertical_cylinder_Touloukian_Morgan(Pr: float, Gr: float, turbule...
  function Nu_vertical_cylinder_McAdams_Weiss_Saunders (line 893) | def Nu_vertical_cylinder_McAdams_Weiss_Saunders(Pr: float, Gr: float, tu...
  function Nu_vertical_cylinder_Kreith_Eckert (line 958) | def Nu_vertical_cylinder_Kreith_Eckert(Pr: float, Gr: float, turbulent: ...
  function Nu_vertical_cylinder_Hanesian_Kalish_Morgan (line 1020) | def Nu_vertical_cylinder_Hanesian_Kalish_Morgan(Pr: float, Gr: float) ->...
  function Nu_vertical_cylinder_Al_Arabi_Khamis (line 1071) | def Nu_vertical_cylinder_Al_Arabi_Khamis(Pr: float, Gr: float, L: float,...
  function Nu_vertical_cylinder_Popiel_Churchill (line 1136) | def Nu_vertical_cylinder_Popiel_Churchill(Pr: float, Gr: float, L: float...
  function Nu_vertical_cylinder_methods (line 1211) | def Nu_vertical_cylinder_methods(Pr: float, Gr: float, L: float | None=N...
  function Nu_vertical_cylinder (line 1255) | def Nu_vertical_cylinder(Pr: float, Gr: float, L: float | None=None, D: ...
  function Nu_horizontal_cylinder_Churchill_Chu (line 1342) | def Nu_horizontal_cylinder_Churchill_Chu(Pr: float, Gr: float) -> float:
  function Nu_horizontal_cylinder_Kuehn_Goldstein (line 1395) | def Nu_horizontal_cylinder_Kuehn_Goldstein(Pr: float, Gr: float) -> float:
  function Nu_horizontal_cylinder_Morgan (line 1442) | def Nu_horizontal_cylinder_Morgan(Pr: float, Gr: float) -> float:
  function Nu_horizontal_cylinder_methods (line 1518) | def Nu_horizontal_cylinder_methods(Pr: float, Gr: float, check_ranges: b...
  function Nu_horizontal_cylinder (line 1548) | def Nu_horizontal_cylinder(Pr: float, Gr: float, Method: str | None=None...
  function Nu_coil_Xin_Ebadian (line 1618) | def Nu_coil_Xin_Ebadian(Pr: float, Gr: float, horizontal: bool=False) ->...

FILE: ht/conv_internal.py
  function laminar_T_const (line 71) | def laminar_T_const() -> float:
  function laminar_Q_const (line 101) | def laminar_Q_const() -> float:
  function laminar_entry_thermal_Hausen (line 132) | def laminar_entry_thermal_Hausen(Re: float, Pr: float, L: float, Di: flo...
  function laminar_entry_Seider_Tate (line 184) | def laminar_entry_Seider_Tate(Re: float, Pr: float, L: float, Di: float,...
  function laminar_entry_Baehr_Stephan (line 241) | def laminar_entry_Baehr_Stephan(Re: float, Pr: float, L: float, Di: floa...
  function turbulent_Dittus_Boelter (line 293) | def turbulent_Dittus_Boelter(Re: float, Pr: float, heating: bool=True, r...
  function turbulent_Sieder_Tate (line 362) | def turbulent_Sieder_Tate(Re: float, Pr: float, mu: float | None=None, m...
  function turbulent_entry_Hausen (line 412) | def turbulent_entry_Hausen(Re: float, Pr: float, Di: float, x: float) ->...
  function turbulent_Colburn (line 457) | def turbulent_Colburn(Re: float, Pr: float) -> float:
  function turbulent_Drexel_McAdams (line 497) | def turbulent_Drexel_McAdams(Re: float, Pr: float) -> float:
  function turbulent_von_Karman (line 537) | def turbulent_von_Karman(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Prandtl (line 579) | def turbulent_Prandtl(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Friend_Metzner (line 619) | def turbulent_Friend_Metzner(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Petukhov_Kirillov_Popov (line 661) | def turbulent_Petukhov_Kirillov_Popov(Re: float, Pr: float, fd: float) -...
  function turbulent_Webb (line 707) | def turbulent_Webb(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Sandall (line 749) | def turbulent_Sandall(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Gnielinski (line 794) | def turbulent_Gnielinski(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Gnielinski_smooth_1 (line 836) | def turbulent_Gnielinski_smooth_1(Re: float, Pr: float) -> float:
  function turbulent_Gnielinski_smooth_2 (line 876) | def turbulent_Gnielinski_smooth_2(Re: float, Pr: float) -> float:
  function turbulent_Churchill_Zajic (line 916) | def turbulent_Churchill_Zajic(Re: float, Pr: float, fd: float) -> float:
  function turbulent_ESDU (line 973) | def turbulent_ESDU(Re: float, Pr: float) -> float:
  function turbulent_Martinelli (line 1013) | def turbulent_Martinelli(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Nunner (line 1054) | def turbulent_Nunner(Re: float, Pr: float, fd: float, fd_smooth: float) ...
  function turbulent_Dipprey_Sabersky (line 1096) | def turbulent_Dipprey_Sabersky(Re: float, Pr: float, fd: float, eD: floa...
  function turbulent_Gowen_Smith (line 1142) | def turbulent_Gowen_Smith(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Kawase_Ulbrecht (line 1184) | def turbulent_Kawase_Ulbrecht(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Kawase_De (line 1225) | def turbulent_Kawase_De(Re: float, Pr: float, fd: float) -> float:
  function turbulent_Bhatti_Shah (line 1267) | def turbulent_Bhatti_Shah(Re: float, Pr: float, fd: float, eD: float) ->...
  function Nu_conv_internal_methods (line 1358) | def Nu_conv_internal_methods(Re: float, Pr: float, eD: float=0, Di: floa...
  function Nu_conv_internal (line 1440) | def Nu_conv_internal(Re: float, Pr: float, eD: float=0.0, Di: float | No...
  function Morimoto_Hotta (line 1596) | def Morimoto_Hotta(Re: float, Pr: float, Dh: float, Rm: float) -> float:
  function helical_turbulent_Nu_Mori_Nakayama (line 1659) | def helical_turbulent_Nu_Mori_Nakayama(Re: float, Pr: float, Di: float, ...
  function helical_turbulent_Nu_Schmidt (line 1732) | def helical_turbulent_Nu_Schmidt(Re: float, Pr: float, Di: float, Dc: fl...
  function helical_turbulent_Nu_Xin_Ebadian (line 1801) | def helical_turbulent_Nu_Xin_Ebadian(Re: float, Pr: float, Di: float, Dc...
  function Nu_laminar_rectangular_Shan_London (line 1861) | def Nu_laminar_rectangular_Shan_London(a_r: float) -> float:

FILE: ht/conv_jacket.py
  function Lehrer (line 31) | def Lehrer(m: float, Dtank: float, Djacket: float, H: float, Dinlet: flo...
  function Stein_Schmidt (line 157) | def Stein_Schmidt(m: float, Dtank: float, Djacket: float, H: float, Dinl...

FILE: ht/conv_packed_bed.py
  function Nu_packed_bed_Gnielinski (line 31) | def Nu_packed_bed_Gnielinski(dp: float, voidage: float, vs: float, rho: ...
  function Nu_Wakao_Kagei (line 112) | def Nu_Wakao_Kagei(Re: float, Pr: float) -> float:
  function Nu_Achenbach (line 153) | def Nu_Achenbach(Re: float, Pr: float, voidage: float) -> float:
  function Nu_KTA (line 198) | def Nu_KTA(Re: float, Pr: float, voidage: float) -> float:

FILE: ht/conv_plate.py
  function Nu_plate_Kumar (line 55) | def Nu_plate_Kumar(Re: float, Pr: float, chevron_angle: float, mu: float...
  function Nu_plate_Martin (line 154) | def Nu_plate_Martin(Re: float, Pr: float, chevron_angle: float, variant:...
  function Nu_plate_Muley_Manglik (line 236) | def Nu_plate_Muley_Manglik(Re: float, Pr: float, chevron_angle: float, p...
  function Nu_plate_Khan_Khan (line 307) | def Nu_plate_Khan_Khan(Re: float, Pr: float, chevron_angle: float) -> fl...

FILE: ht/conv_supercritical.py
  function Nu_McAdams (line 49) | def Nu_McAdams(Re: float, Pr: float) -> float:
  function Nu_Shitsman (line 93) | def Nu_Shitsman(Re: float, Pr_b: float, Pr_w: float) -> float:
  function Nu_Griem (line 148) | def Nu_Griem(Re: float, Pr: float, H: float | None=None) -> float:
  function Nu_Jackson (line 235) | def Nu_Jackson(Re: float, Pr: float, rho_w: float | None=None, rho_b: fl...
  function Nu_Gupta (line 344) | def Nu_Gupta(Re: float, Pr: float, rho_w: int | None=None, rho_b: float ...
  function Nu_Swenson (line 417) | def Nu_Swenson(Re: float, Pr: float, rho_w: int | None=None, rho_b: floa...
  function Nu_Xu (line 493) | def Nu_Xu(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | N...
  function Nu_Mokry (line 565) | def Nu_Mokry(Re: float, Pr: float, rho_w: int | None=None, rho_b: float ...
  function Nu_Bringer_Smith (line 635) | def Nu_Bringer_Smith(Re: float, Pr: float) -> float:
  function Nu_Ornatsky (line 694) | def Nu_Ornatsky(Re: float, Pr_b: float, Pr_w: float, rho_w: int | None=N...
  function Nu_Gorban (line 756) | def Nu_Gorban(Re: float, Pr: float) -> float:
  function Nu_Zhu (line 803) | def Nu_Zhu(Re: float, Pr: float, rho_w: int | None=None, rho_b: float | ...
  function Nu_Bishop (line 875) | def Nu_Bishop(Re: float, Pr: float, rho_w: float | None=None, rho_b: flo...
  function Nu_Yamagata (line 962) | def Nu_Yamagata(Re: float, Pr: float, Pr_pc: float | None=None, Cp_avg: ...
  function Nu_Kitoh (line 1072) | def Nu_Kitoh(Re: float, Pr: float, H: float | None=None, G: int | None=N...
  function Nu_Krasnoshchekov_Protopopov (line 1177) | def Nu_Krasnoshchekov_Protopopov(Re: float, Pr: float, Cp_avg: int | Non...
  function Nu_Petukhov (line 1267) | def Nu_Petukhov(Re: float, Pr: float, rho_w: float | None=None, rho_b: f...
  function Nu_Krasnoshchekov (line 1335) | def Nu_Krasnoshchekov(Re: float, Pr: float, rho_w: float | None=None, rh...

FILE: ht/conv_tube_bank.py
  function Nu_Grimison_tube_bank (line 133) | def Nu_Grimison_tube_bank(Re: float, Pr: float, Do: float, tube_rows: in...
  function Zukauskas_tube_row_correction (line 219) | def Zukauskas_tube_row_correction(tube_rows: int, staggered: bool=True, ...
  function Nu_Zukauskas_Bejan (line 279) | def Nu_Zukauskas_Bejan(Re: float, Pr: float, tube_rows: int, pitch_paral...
  function ESDU_tube_row_correction (line 409) | def ESDU_tube_row_correction(tube_rows: int, staggered: bool=True, Re: f...
  function ESDU_tube_angle_correction (line 482) | def ESDU_tube_angle_correction(angle: float) -> float:
  function Nu_ESDU_73031 (line 522) | def Nu_ESDU_73031(Re: float, Pr: float, tube_rows: int, pitch_parallel: ...
  function Nu_HEDH_tube_bank (line 645) | def Nu_HEDH_tube_bank(Re: float, Pr: float, Do: float, tube_rows: int, p...
  function dP_Kern (line 770) | def dP_Kern(m: float, rho: float, mu: float, DShell: float, LSpacing: fl...
  function dP_Zukauskas (line 1021) | def dP_Zukauskas(Re: float, n: int, ST: float, SL: float, D: float, rho:...
  function baffle_correction_Bell (line 1119) | def baffle_correction_Bell(crossflow_tube_fraction: float, method: str="...
  function baffle_leakage_Bell (line 1227) | def baffle_leakage_Bell(Ssb: float, Stb: float, Sm: float, method: str="...
  function bundle_bypassing_Bell (line 1340) | def bundle_bypassing_Bell(bypass_area_fraction, seal_strips, crossflow_r...
  function unequal_baffle_spacing_Bell (line 1423) | def unequal_baffle_spacing_Bell(baffles: int, baffle_spacing: float,
  function laminar_correction_Bell (line 1492) | def laminar_correction_Bell(Re: float, total_row_passes: int) -> float:

FILE: ht/conv_two_phase.py
  function Davis_David (line 45) | def Davis_David(m: float, x: float, D: float, rhol: float, rhog: float, ...
  function Elamvaluthi_Srinivas (line 109) | def Elamvaluthi_Srinivas(m: float, x: float, D: float, rhol: float, rhog...
  function Groothuis_Hendal (line 189) | def Groothuis_Hendal(m: float, x: float, D: float, rhol: float, rhog: fl...
  function Hughmark (line 279) | def Hughmark(m: float, x: float, alpha: float, D: float, L: float, Cpl: ...
  function Knott (line 353) | def Knott(m: float, x: float, D: float, rhol: int, rhog: float, Cpl: flo...
  function Kudirka_Grosh_McFadden (line 438) | def Kudirka_Grosh_McFadden(m: float, x: float, D: float, rhol: float, rh...
  function Martin_Sims (line 514) | def Martin_Sims(m: float, x: float, D: float, rhol: float, rhog: float, ...
  function Ravipudi_Godbold (line 591) | def Ravipudi_Godbold(m: float, x: float, D: float, rhol: float, rhog: fl...
  function Aggour (line 665) | def Aggour(m: float, x: float, alpha: float, D: float, rhol: float, Cpl:...
  function h_two_phase_methods (line 802) | def h_two_phase_methods(m, x, D, Cpl, kl, rhol=None, rhog=None, mul=None,
  function h_two_phase (line 878) | def h_two_phase(m: float, x: float, D: float, Cpl: float, kl: float, rho...

FILE: ht/core.py
  function LMTD (line 47) | def LMTD(Thi: float, Tho: float, Tci: float, Tco: float, counterflow: bo...
  function countercurrent_hx_temperature_check (line 127) | def countercurrent_hx_temperature_check(T0i, T0o, T1i, T1o):
  function is_heating_temperature (line 166) | def is_heating_temperature(T: int, T_wall: int) -> bool:
  function is_heating_property (line 190) | def is_heating_property(prop: float, prop_wall: float) -> bool:
  function wall_factor_fd (line 273) | def wall_factor_fd(mu, mu_wall, turbulent=True, liquid=False):
  function wall_factor_Nu (line 333) | def wall_factor_Nu(mu, mu_wall, turbulent=True, liquid=False):
  function wall_factor (line 407) | def wall_factor(mu: int | None=None, mu_wall: int | None=None, Pr: float...
  function fin_efficiency_Kern_Kraus (line 501) | def fin_efficiency_Kern_Kraus(Do: float, D_fin: float, t_fin: float, k_f...

FILE: ht/hx.py
  function factorial (line 93) | def factorial(n):
  function crossflow_effectiveness_to_int (line 96) | def crossflow_effectiveness_to_int(v: float, NTU: float, t0: float) -> f...
  function effectiveness_from_NTU (line 100) | def effectiveness_from_NTU(NTU, Cr, subtype="counterflow", n_shell_tube=...
  function NTU_from_effectiveness (line 365) | def NTU_from_effectiveness(effectiveness, Cr, subtype="counterflow", n_s...
  function calc_Cmin (line 613) | def calc_Cmin(mh: float, mc: float, Cph: float, Cpc: int) -> float:
  function calc_Cmax (line 663) | def calc_Cmax(mh: float, mc: float, Cph: float, Cpc: int) -> float:
  function calc_Cr (line 715) | def calc_Cr(mh: float, mc: float, Cph: float, Cpc: int) -> float:
  function NTU_from_UA (line 764) | def NTU_from_UA(UA: float, Cmin: float) -> float:
  function UA_from_NTU (line 801) | def UA_from_NTU(NTU: float, Cmin: float) -> float:
  function P_NTU_Pp (line 838) | def P_NTU_Pp(x, y):
  function P_NTU_Pc (line 884) | def P_NTU_Pc(x, y):
  function effectiveness_NTU_method (line 931) | def effectiveness_NTU_method(mh, mc, Cph, Cpc, subtype="counterflow", Th...
  function temperature_effectiveness_air_cooler (line 1105) | def temperature_effectiveness_air_cooler(R1: float, NTU1: float, rows: i...
  function temperature_effectiveness_basic (line 1328) | def temperature_effectiveness_basic(R1: float, NTU1: float, subtype: str...
  function temperature_effectiveness_TEMA_J (line 1481) | def temperature_effectiveness_TEMA_J(R1: float, NTU1: float, Ntp: int) -...
  function temperature_effectiveness_TEMA_H (line 1608) | def temperature_effectiveness_TEMA_H(R1: float, NTU1: float, Ntp: int, o...
  function temperature_effectiveness_TEMA_G (line 1785) | def temperature_effectiveness_TEMA_G(R1: float, NTU1: float, Ntp: int, o...
  function temperature_effectiveness_TEMA_E (line 1934) | def temperature_effectiveness_TEMA_E(R1: float, NTU1: float, Ntp: int=1,...
  function temperature_effectiveness_plate (line 2191) | def temperature_effectiveness_plate(R1: float, NTU1: float, Np1: int, Np...
  function _NTU_from_P_objective (line 3145) | def _NTU_from_P_objective(NTU1, R1, P1, function, *args):
  function _NTU_from_P_erf (line 3169) | def _NTU_from_P_erf(NTU1: float, *args) -> float:
  function _NTU_from_P_solver (line 3179) | def _NTU_from_P_solver(P1: float, R1: float, NTU_min: float | None, NTU_...
  function _NTU_max_for_P_solver (line 3213) | def _NTU_max_for_P_solver(ps: list[list[float]], qs: list[list[float]], ...
  function NTU_from_P_basic (line 3227) | def NTU_from_P_basic(P1: float, R1: float, subtype: str="crossflow") -> ...
  function NTU_from_P_G (line 3337) | def NTU_from_P_G(P1: float, R1: float, Ntp: int, optimal: bool=True) -> ...
  function NTU_from_P_J (line 3414) | def NTU_from_P_J(P1: float, R1: float, Ntp: int) -> float:
  function NTU_from_P_E (line 3487) | def NTU_from_P_E(P1: float, R1: float, Ntp: int, optimal: bool=True) -> ...
  function NTU_from_P_H (line 3598) | def NTU_from_P_H(P1: float, R1: float, Ntp: int, optimal: bool=True) -> ...
  function NTU_from_P_plate (line 3666) | def NTU_from_P_plate(P1: float, R1: float, Np1: int, Np2: int, counterfl...
  function P_NTU_method (line 3822) | def P_NTU_method(m1: float, m2: float, Cp1: float, Cp2: float, UA: float...
  function F_LMTD_Fakheri (line 4215) | def F_LMTD_Fakheri(Thi: int, Tho: float, Tci: int, Tco: float, shells: i...
  function check_tubing_TEMA (line 4316) | def check_tubing_TEMA(NPS=None, BWG=None):
  function get_tube_TEMA (line 4329) | def get_tube_TEMA(NPS=None, BWG=None, Do=None, Di=None, tmin=None):
  function DBundle_min (line 4406) | def DBundle_min(Do: float) -> float:
  function shell_clearance (line 4445) | def shell_clearance(DBundle: float | None=None, DShell: float | None=Non...
  function baffle_thickness (line 4508) | def baffle_thickness(Dshell, L_unsupported, service="C"):
  function D_baffle_holes (line 4586) | def D_baffle_holes(Do, L_unsupported):
  function L_unsupported_max (line 4634) | def L_unsupported_max(Do: float, material: str="CS") -> float:
  function _load_coeffs_Phadkeb (line 4696) | def _load_coeffs_Phadkeb() -> None:
  function Ntubes_Phadkeb (line 4704) | def Ntubes_Phadkeb(DBundle: float, Do: float, pitch: float, Ntp: int, an...
  function to_solve_Ntubes_Phadkeb (line 4925) | def to_solve_Ntubes_Phadkeb(DBundle: int, Do: float, pitch: float, Ntp: ...
  function DBundle_for_Ntubes_Phadkeb (line 4929) | def DBundle_for_Ntubes_Phadkeb(Ntubes: int, Do: float, pitch: float, Ntp...
  function Ntubes_Perrys (line 4983) | def Ntubes_Perrys(DBundle: float, Do: float, Ntp: int, angle: int=30) ->...
  function Ntubes_VDI (line 5048) | def Ntubes_VDI(DBundle: float | None=None, Ntp: int | None=None, Do: flo...
  function D_for_Ntubes_VDI (line 5114) | def D_for_Ntubes_VDI(N: int, Ntp: float, Do: float, pitch: float, angle:...
  function Ntubes_HEDH (line 5182) | def Ntubes_HEDH(DBundle: float | None=None, Do: float | None=None, pitch...
  function DBundle_for_Ntubes_HEDH (line 5234) | def DBundle_for_Ntubes_HEDH(N: int, Do: float, pitch: float, angle: int=...
  function Ntubes (line 5286) | def Ntubes(DBundle: float, Do: float, pitch: float, Ntp: int=1, angle: i...
  function _tubecount_objf_Perry (line 5354) | def _tubecount_objf_Perry(D: float, Do: float, Ntp: int, angle: int, N: ...
  function size_bundle_from_tubecount (line 5357) | def size_bundle_from_tubecount(N: int, Do: float, pitch: float, Ntp: int...

FILE: ht/insulation.py
  function ASHRAE_k (line 421) | def ASHRAE_k(ID):
  function refractory_VDI_k (line 504) | def refractory_VDI_k(ID, T=None):
  function refractory_VDI_Cp (line 546) | def refractory_VDI_Cp(ID, T=None):
  function nearest_material (line 588) | def nearest_material(name, complete=False):
  function k_material (line 637) | def k_material(ID, T=298.15):
  function rho_material (line 685) | def rho_material(ID):
  function Cp_material (line 735) | def Cp_material(ID, T=298.15):

FILE: ht/numba.py
  function transform_complete_ht (line 48) | def transform_complete_ht(replaced, __funcs, __all__, normal, vec=False):

FILE: ht/radiation.py
  function blackbody_spectral_radiance (line 38) | def blackbody_spectral_radiance(T: float, wavelength: float) -> float:
  function q_rad (line 93) | def q_rad(emissivity: float, T: int, T2: float=0) -> float:
  function grey_transmittance (line 137) | def grey_transmittance(extinction_coefficient: float, molar_density: int...
  function solar_spectrum (line 190) | def solar_spectrum(model="SOLAR-ISS"):

FILE: ht/units.py
  function R_to_k (line 62) | def R_to_k(R, t, A=1*u.m**2):
  function R_value_to_k (line 74) | def R_value_to_k(R_value, SI=True):
  function k_to_R_value (line 79) | def k_to_R_value(k, SI=True):

FILE: tests/test_air_cooler.py
  function test_air_cooler_Ft (line 42) | def test_air_cooler_Ft():
  function test_air_cooler_noise_GPSA (line 54) | def test_air_cooler_noise_GPSA():
  function test_air_cooler_noise_Mukherjee (line 59) | def test_air_cooler_noise_Mukherjee():
  function test_h_ESDU_high_fin (line 74) | def test_h_ESDU_high_fin():
  function test_h_ESDU_low_fin (line 90) | def test_h_ESDU_low_fin():
  function test_h_Briggs_Young (line 118) | def test_h_Briggs_Young():
  function test_h_Ganguli_VDI (line 151) | def test_h_Ganguli_VDI():
  function test_dP_ESDU_high_fin (line 184) | def test_dP_ESDU_high_fin():
  function test_dP_ESDU_low_fin (line 193) | def test_dP_ESDU_low_fin():
  function test_AirCooledExchangerPermutations (line 206) | def test_AirCooledExchangerPermutations():

FILE: tests/test_boiling_flow.py
  function test_Lazarek_Black (line 29) | def test_Lazarek_Black():
  function test_Li_Wu (line 52) | def test_Li_Wu():
  function test_Sun_Mishima (line 73) | def test_Sun_Mishima():
  function test_Thome (line 97) | def test_Thome():
  function test_Yun_Heo_Kim (line 112) | def test_Yun_Heo_Kim():
  function test_Liu_Winterton (line 132) | def test_Liu_Winterton():
  function test_Chen_Edelstein (line 136) | def test_Chen_Edelstein():
  function test_Chen_Bennett (line 143) | def test_Chen_Bennett():

FILE: tests/test_boiling_nucleic.py
  function test_boiling_nucleic_Rohsenow (line 48) | def test_boiling_nucleic_Rohsenow():
  function test_boiling_nucleic_McNelly (line 64) | def test_boiling_nucleic_McNelly():
  function test_boiling_nucleic_Forster_Zuber (line 78) | def test_boiling_nucleic_Forster_Zuber():
  function test_boiling_nucleic_Montinsky (line 94) | def test_boiling_nucleic_Montinsky():
  function test_boiling_nucleic_Stephan_Abdelsalam (line 117) | def test_boiling_nucleic_Stephan_Abdelsalam():
  function test_boiling_nucleic_HEDH_Taborek (line 135) | def test_boiling_nucleic_HEDH_Taborek():
  function test_boiling_nucleic_Bier (line 146) | def test_boiling_nucleic_Bier():
  function test_boiling_nucleic_Cooper (line 162) | def test_boiling_nucleic_Cooper():
  function test_Gorenflo (line 178) | def test_Gorenflo():
  function test_h_nucleic (line 207) | def test_h_nucleic():
  function test_qmax_Zuber (line 264) | def test_qmax_Zuber():
  function test_qmax_Serth_HEDH (line 271) | def test_qmax_Serth_HEDH():
  function test_HEDH_Montinsky (line 279) | def test_HEDH_Montinsky():
  function test_qmax_nucleic (line 283) | def test_qmax_nucleic():

FILE: tests/test_boiling_plate.py
  function test_h_boiling_Amalfi (line 28) | def test_h_boiling_Amalfi():
  function test_h_boiling_Lee_Kang_Kim (line 36) | def test_h_boiling_Lee_Kang_Kim():
  function test_h_boiling_Han_Lee_Kim (line 43) | def test_h_boiling_Han_Lee_Kim():
  function test_h_boiling_Huang_Sheer (line 54) | def test_h_boiling_Huang_Sheer():
  function test_h_boiling_Yan_Lin (line 58) | def test_h_boiling_Yan_Lin():

FILE: tests/test_condensation.py
  function test_h_Nusselt_laminar (line 32) | def test_h_Nusselt_laminar():
  function test_h_Boyko_Kruzhilin (line 40) | def test_h_Boyko_Kruzhilin():
  function test_Akers_Deans_Crosser (line 47) | def test_Akers_Deans_Crosser():
  function test_h_kinetic (line 53) | def test_h_kinetic():
  function test_Cavallini_Smith_Zecchin (line 58) | def test_Cavallini_Smith_Zecchin():
  function test_Shah (line 62) | def test_Shah():

FILE: tests/test_conduction.py
  function test_conduction (line 56) | def test_conduction():
  function test_cylindrical_heat_transfer (line 82) | def test_cylindrical_heat_transfer():
  function test_insulation (line 99) | def test_insulation():
  function test_insulation_fuzz (line 143) | def test_insulation_fuzz():

FILE: tests/test_conv_external.py
  function test_Nu_cylinder_Zukauskas (line 48) | def test_Nu_cylinder_Zukauskas():
  function test_Nu_cylinder_Churchill_Bernstein (line 61) | def test_Nu_cylinder_Churchill_Bernstein():
  function test_Nu_cylinder_Sanitjai_Goldstein (line 66) | def test_Nu_cylinder_Sanitjai_Goldstein():
  function test_Nu_cylinder_Fand (line 71) | def test_Nu_cylinder_Fand():
  function test_Nu_cylinder_McAdams (line 76) | def test_Nu_cylinder_McAdams():
  function test_Nu_cylinder_Whitaker (line 81) | def test_Nu_cylinder_Whitaker():
  function test_Nu_cylinder_Perkins_Leppert_1962 (line 88) | def test_Nu_cylinder_Perkins_Leppert_1962():
  function test_Nu_cylinder_Perkins_Leppert_1964 (line 95) | def test_Nu_cylinder_Perkins_Leppert_1964():
  function test_Nu_external_cylinder (line 102) | def test_Nu_external_cylinder():
  function test_Nu_horizontal_plate_laminar_Baehr (line 121) | def test_Nu_horizontal_plate_laminar_Baehr():
  function test_Nu_horizontal_plate_laminar_Churchill_Ozoe (line 129) | def test_Nu_horizontal_plate_laminar_Churchill_Ozoe():
  function test_Nu_horizontal_plate_turbulent_Schlichting (line 133) | def test_Nu_horizontal_plate_turbulent_Schlichting():
  function test_Nu_horizontal_plate_turbulent_Kreith (line 137) | def test_Nu_horizontal_plate_turbulent_Kreith():
  function test_Nu_external_horizontal_plate (line 142) | def test_Nu_external_horizontal_plate():

FILE: tests/test_conv_free_enclosed.py
  function test_Nu_Nusselt_Rayleigh_Holling_Herwig (line 42) | def test_Nu_Nusselt_Rayleigh_Holling_Herwig():
  function test_Nu_Nusselt_Rayleigh_Probert (line 52) | def test_Nu_Nusselt_Rayleigh_Probert():
  function test_Rac_Nusselt_Rayleigh (line 68) | def test_Rac_Nusselt_Rayleigh():
  function test_Rac_Nusselt_Rayleigh_disk (line 76) | def test_Rac_Nusselt_Rayleigh_disk():
  function test_Nu_Nusselt_vertical_Thess (line 90) | def test_Nu_Nusselt_vertical_Thess():
  function test_Nu_Nusselt_Rayleigh_Hollands (line 101) | def test_Nu_Nusselt_Rayleigh_Hollands():
  function test_Rac_Nusselt_Rayleigh_fit_uninsulated (line 107) | def test_Rac_Nusselt_Rayleigh_fit_uninsulated():
  function test_Rac_Nusselt_Rayleigh_fit_insulated (line 131) | def test_Rac_Nusselt_Rayleigh_fit_insulated():
  function test_Rac_Nusselt_Rayleigh_disk_fits (line 159) | def test_Rac_Nusselt_Rayleigh_disk_fits():
  function test_Nu_vertical_helical_coil_Ali (line 191) | def test_Nu_vertical_helical_coil_Ali():

FILE: tests/test_conv_free_immersed.py
  function test_Nu_free_vertical_plate (line 59) | def test_Nu_free_vertical_plate():
  function test_Nu_horizontal_plate_VDI (line 73) | def test_Nu_horizontal_plate_VDI():
  function test_Nu_horizontal_plate_Rohsenow (line 82) | def test_Nu_horizontal_plate_Rohsenow():
  function test_Nu_free_horizontal_plate (line 90) | def test_Nu_free_horizontal_plate():
  function test_Nu_horizontal_plate_McAdams (line 99) | def test_Nu_horizontal_plate_McAdams():
  function test_Nu_vertical_plate_Churchill (line 111) | def test_Nu_vertical_plate_Churchill():
  function test_Nu_sphere_Churchill (line 116) | def test_Nu_sphere_Churchill():
  function test_Nu_vertical_cylinder_Griffiths_Davis_Morgan (line 122) | def test_Nu_vertical_cylinder_Griffiths_Davis_Morgan():
  function test_Nu_vertical_cylinder_Jakob_Linke_Morgan (line 128) | def test_Nu_vertical_cylinder_Jakob_Linke_Morgan():
  function test_Nu_vertical_cylinder_Carne_Morgan (line 134) | def test_Nu_vertical_cylinder_Carne_Morgan():
  function test_Nu_vertical_cylinder_Eigenson_Morgan (line 140) | def test_Nu_vertical_cylinder_Eigenson_Morgan():
  function test_Nu_vertical_cylinder_Touloukian_Morgan (line 147) | def test_Nu_vertical_cylinder_Touloukian_Morgan():
  function test_Nu_vertical_cylinder_McAdams_Weiss_Saunders (line 154) | def test_Nu_vertical_cylinder_McAdams_Weiss_Saunders():
  function test_Nu_vertical_cylinder_Kreith_Eckert (line 162) | def test_Nu_vertical_cylinder_Kreith_Eckert():
  function test_Nu_vertical_cylinder_Hanesian_Kalish_Morgan (line 170) | def test_Nu_vertical_cylinder_Hanesian_Kalish_Morgan():
  function test_Nu_vertical_cylinder_Al_Arabi_Khamis (line 175) | def test_Nu_vertical_cylinder_Al_Arabi_Khamis():
  function test_Nu_vertical_cylinder_Popiel_Churchill (line 180) | def test_Nu_vertical_cylinder_Popiel_Churchill():
  function test_Nu_vertical_cylinder (line 185) | def test_Nu_vertical_cylinder():
  function test_Nu_horizontal_cylinder_Churchill_Chu (line 200) | def test_Nu_horizontal_cylinder_Churchill_Chu():
  function test_Nu_horizontal_cylinder_Kuehn_Goldstein (line 205) | def test_Nu_horizontal_cylinder_Kuehn_Goldstein():
  function test_Nu_horizontal_cylinder_Morgan (line 210) | def test_Nu_horizontal_cylinder_Morgan():
  function test_Nu_horizontal_cylinder (line 216) | def test_Nu_horizontal_cylinder():
  function test_Nu_coil_Xin_Ebadian (line 228) | def test_Nu_coil_Xin_Ebadian():
  function test_Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan (line 235) | def test_Nu_vertical_helical_coil_Prabhanjan_Rennie_Raghavan():

FILE: tests/test_conv_internal.py
  function test_Nu_const (line 67) | def test_Nu_const():
  function test_laminar_entry_region (line 72) | def test_laminar_entry_region():
  function test_turbulent_complicated (line 84) | def test_turbulent_complicated():
  function test_turbulent_simple (line 100) | def test_turbulent_simple():
  function test_turbulent_rough (line 140) | def test_turbulent_rough():
  function test_Morimoto_Hotta (line 165) | def test_Morimoto_Hotta():
  function test_helical_turbulent_Nu_Mori_Nakayama (line 170) | def test_helical_turbulent_Nu_Mori_Nakayama():
  function test_helical_turbulent_Nu_Schmidt (line 185) | def test_helical_turbulent_Nu_Schmidt():
  function test_helical_turbulent_Nu_Xin_Ebadian (line 192) | def test_helical_turbulent_Nu_Xin_Ebadian():
  function test_Nu_laminar_rectangular_Shan_London (line 199) | def test_Nu_laminar_rectangular_Shan_London():
  function test_Nu_conv_internal_methods (line 203) | def test_Nu_conv_internal_methods():
  function test_Nu_conv_internal (line 210) | def test_Nu_conv_internal():

FILE: tests/test_conv_jacket.py
  function test_conv_jacket (line 28) | def test_conv_jacket():

FILE: tests/test_conv_packed_bed.py
  function test_Nu_packed_bed_Gnielinski (line 28) | def test_Nu_packed_bed_Gnielinski():
  function test_Nu_Wakao_Kagei (line 37) | def test_Nu_Wakao_Kagei():
  function test_Nu_Achenbach (line 41) | def test_Nu_Achenbach():
  function test_Nu_KTA (line 45) | def test_Nu_KTA():

FILE: tests/test_conv_plate.py
  function test_Nu_plate_Kumar (line 30) | def test_Nu_plate_Kumar():
  function test_Nu_plate_Martin (line 74) | def test_Nu_plate_Martin():
  function test_Nu_plate_Muley_Manglik (line 82) | def test_Nu_plate_Muley_Manglik():
  function test_Nu_plate_Khan_Khan (line 87) | def test_Nu_plate_Khan_Khan():

FILE: tests/test_conv_supercritical.py
  function test_Nu_McAdams (line 47) | def test_Nu_McAdams():
  function test_Nu_Shitsman (line 52) | def test_Nu_Shitsman():
  function test_Nu_Griem (line 57) | def test_Nu_Griem():
  function test_Nu_Jackson (line 65) | def test_Nu_Jackson():
  function test_Nu_Gupta (line 76) | def test_Nu_Gupta():
  function test_Nu_Swenson (line 83) | def test_Nu_Swenson():
  function test_Nu_Xu (line 90) | def test_Nu_Xu():
  function test_Nu_Mokry (line 97) | def test_Nu_Mokry():
  function test_Nu_Bringer_Smith (line 104) | def test_Nu_Bringer_Smith():
  function test_Nu_Ornatsky (line 109) | def test_Nu_Ornatsky():
  function test_Nu_Gorban (line 116) | def test_Nu_Gorban():
  function test_Nu_Zhu (line 121) | def test_Nu_Zhu():
  function test_Nu_Bishop (line 128) | def test_Nu_Bishop():
  function test_Nu_Yamagata (line 135) | def test_Nu_Yamagata():
  function test_Nu_Kitoh (line 145) | def test_Nu_Kitoh():
  function test_Nu_Krasnoshchekov_Protopopov (line 154) | def test_Nu_Krasnoshchekov_Protopopov():
  function test_Nu_Petukhov (line 159) | def test_Nu_Petukhov():
  function test_Nu_Krasnoshchekov (line 164) | def test_Nu_Krasnoshchekov():

FILE: tests/test_conv_tube_bank.py
  function test_Nu_Grimison_tube_bank_tcks (line 46) | def test_Nu_Grimison_tube_bank_tcks():
  function test_Nu_Grimison_tube_bank (line 62) | def test_Nu_Grimison_tube_bank():
  function test_Gimison_coeffs_regeneration (line 87) | def test_Gimison_coeffs_regeneration():
  function test_ESDU_tube_row_correction (line 99) | def test_ESDU_tube_row_correction():
  function test_ESDU_tube_row_correction_refit (line 108) | def test_ESDU_tube_row_correction_refit():
  function test_ESDU_tube_angle_correction (line 197) | def test_ESDU_tube_angle_correction():
  function test_Zukauskas_tube_row_correction (line 212) | def test_Zukauskas_tube_row_correction():
  function test_Zukauskas_tube_row_correction_refit (line 218) | def test_Zukauskas_tube_row_correction_refit():
  function test_Nu_Zukauskas_Bejan (line 243) | def test_Nu_Zukauskas_Bejan():
  function test_Nu_ESDU_73031 (line 258) | def test_Nu_ESDU_73031():
  function test_Nu_HEDH_tube_bank (line 276) | def test_Nu_HEDH_tube_bank():
  function test_dP_Kern (line 287) | def test_dP_Kern():
  function test_dP_Kern_data (line 299) | def test_dP_Kern_data():
  function test_dP_Zukauskas (line 338) | def test_dP_Zukauskas():
  function test_dP_Zukauskas_dP_dP_staggered_f_spline (line 344) | def test_dP_Zukauskas_dP_dP_staggered_f_spline():
  function test_dP_Zukauskas_dP_staggered_correction_spline (line 453) | def test_dP_Zukauskas_dP_staggered_correction_spline():
  function test_dP_Zukauskas_dP_inline_correction_spline (line 500) | def test_dP_Zukauskas_dP_inline_correction_spline():
  function test_dP_Zukauskas_dP_inline_f_tck_spline (line 534) | def test_dP_Zukauskas_dP_inline_f_tck_spline():
  function test_baffle_correction_Bell (line 639) | def test_baffle_correction_Bell():
  function test_baffle_correction_Bell_fit (line 659) | def test_baffle_correction_Bell_fit():
  function test_baffle_leakage_Bell (line 744) | def test_baffle_leakage_Bell():
  function test_baffle_leakage_Bell_refit (line 768) | def test_baffle_leakage_Bell_refit():
  function test_bundle_bypassing_Bell (line 792) | def test_bundle_bypassing_Bell():
  function test_bundle_bypassing_Bell (line 967) | def test_bundle_bypassing_Bell():
  function test_unequal_baffle_spacing_Bell (line 977) | def test_unequal_baffle_spacing_Bell():
  function test_laminar_correction_Bell (line 981) | def test_laminar_correction_Bell():

FILE: tests/test_conv_two_phase.py
  function test_Davis_David (line 28) | def test_Davis_David():
  function test_Elamvaluthi_Srinivas (line 33) | def test_Elamvaluthi_Srinivas():
  function test_Groothuis_Hendal (line 38) | def test_Groothuis_Hendal():
  function test_Hughmark (line 46) | def test_Hughmark():
  function test_Knott (line 51) | def test_Knott():
  function test_Kudirka_Grosh_McFadden (line 56) | def test_Kudirka_Grosh_McFadden():
  function test_Martin_Sims (line 61) | def test_Martin_Sims():
  function test_Ravipudi_Godbold (line 69) | def test_Ravipudi_Godbold():
  function test_Aggour (line 74) | def test_Aggour():
  function test_h_two_phase (line 82) | def test_h_two_phase():

FILE: tests/test_core.py
  function test_core (line 30) | def test_core():
  function test_is_heating_temperature (line 63) | def test_is_heating_temperature():
  function test_is_heating_property (line 72) | def test_is_heating_property():
  function test_wall_factor (line 97) | def test_wall_factor():
  function test_fin_efficiency_Kern_Kraus (line 118) | def test_fin_efficiency_Kern_Kraus():
  function test_countercurrent_hx_temperature_check (line 160) | def test_countercurrent_hx_temperature_check():

FILE: tests/test_hx.py
  function test_Ntubes_Perrys (line 68) | def test_Ntubes_Perrys():
  function test_Ntubes_Phadkeb (line 82) | def test_Ntubes_Phadkeb():
  function test_Ntubes_Phadkeb_fuzz (line 129) | def test_Ntubes_Phadkeb_fuzz():
  function test_Phadkeb_numbers (line 156) | def test_Phadkeb_numbers():
  function test_Ntubes_HEDH (line 250) | def test_Ntubes_HEDH():
  function test_Ntubes_VDI (line 274) | def test_Ntubes_VDI():
  function test_Ntubes (line 293) | def test_Ntubes():
  function test_effectiveness_NTU (line 321) | def test_effectiveness_NTU():
  function test_effectiveness_NTU_method (line 457) | def test_effectiveness_NTU_method():
  function test_F_LMTD_Fakheri (line 504) | def test_F_LMTD_Fakheri():
  function test_temperature_effectiveness_basic (line 533) | def test_temperature_effectiveness_basic():
  function test_temperature_effectiveness_TEMA_J (line 558) | def test_temperature_effectiveness_TEMA_J():
  function test_temperature_effectiveness_TEMA_H (line 578) | def test_temperature_effectiveness_TEMA_H():
  function test_temperature_effectiveness_TEMA_G (line 630) | def test_temperature_effectiveness_TEMA_G():
  function test_temperature_effectiveness_TEMA_E (line 698) | def test_temperature_effectiveness_TEMA_E():
  function test_temperature_effectiveness_air_cooler (line 746) | def test_temperature_effectiveness_air_cooler():
  function test_temperature_effectiveness_air_cooler_coerce (line 791) | def test_temperature_effectiveness_air_cooler_coerce():
  function test_P_NTU_method (line 800) | def test_P_NTU_method():
  function test_P_NTU_method_backwards (line 871) | def test_P_NTU_method_backwards():
  function test_Pp (line 965) | def test_Pp():
  function test_temperature_effectiveness_plate (line 980) | def test_temperature_effectiveness_plate():
  function test_NTU_from_P_basic (line 1076) | def test_NTU_from_P_basic():
  function test_NTU_from_P_E (line 1199) | def test_NTU_from_P_E():
  function test_NTU_from_P_H (line 1306) | def test_NTU_from_P_H():
  function test_NTU_from_P_G (line 1342) | def test_NTU_from_P_G():
  function test_NTU_from_P_J (line 1401) | def test_NTU_from_P_J():
  function test_NTU_from_P_plate (line 1463) | def test_NTU_from_P_plate():
  function test_DBundle_min (line 1718) | def test_DBundle_min():
  function test_shell_clearance (line 1725) | def test_shell_clearance():
  function test_L_unsupported_max (line 1739) | def test_L_unsupported_max():
  function test_issue_6 (line 1754) | def test_issue_6():

FILE: tests/test_numba.py
  function mark_as_numba (line 41) | def mark_as_numba(func):
  function test_tube_bank (line 48) | def test_tube_bank():
  function test_conv_internal (line 82) | def test_conv_internal():
  function test_conv_free_immersed (line 88) | def test_conv_free_immersed():
  function test_conv_free_enclosed (line 94) | def test_conv_free_enclosed():
  function test_conv_external (line 118) | def test_conv_external():
  function test_core_misc (line 136) | def test_core_misc():
  function test_air_cooler (line 148) | def test_air_cooler():
  function test_boiling_flow (line 190) | def test_boiling_flow():
  function test_radiation (line 220) | def test_radiation():
  function test_conv_jacket (line 226) | def test_conv_jacket():
  function test_condensation (line 238) | def test_condensation():
  function test_boiling_plate (line 255) | def test_boiling_plate():
  function test_Ntubes_Phadkeb (line 274) | def test_Ntubes_Phadkeb():
  function test_boiling_nucleic (line 291) | def test_boiling_nucleic():
  function test_packed_bed (line 313) | def test_packed_bed():
  function test_two_phase (line 321) | def test_two_phase():
  function test_supercritical (line 327) | def test_supercritical():
  function test_conduction (line 361) | def test_conduction():
  function test_hx_tube_bundles (line 370) | def test_hx_tube_bundles():
  function test_hx_data (line 389) | def test_hx_data():
  function test_hx_effectiveness_still_working (line 397) | def test_hx_effectiveness_still_working():
  function test_conv_plate (line 439) | def test_conv_plate():

FILE: tests/test_radiation.py
  function test_radiation (line 29) | def test_radiation():
  function test_solar_spectrum (line 40) | def test_solar_spectrum():
  function test_grey_transmittance (line 50) | def test_grey_transmittance():

FILE: tests/test_units.py
  function assert_pint_allclose (line 33) | def assert_pint_allclose(value, magnitude, units, rtol=1e-7):
  function test_sample_cases (line 37) | def test_sample_cases():
  function test_custom_wraps (line 75) | def test_custom_wraps():
  function test_check_signatures (line 101) | def test_check_signatures():
  function test_parse_numpydoc_variables_units (line 111) | def test_parse_numpydoc_variables_units():

FILE: tests/test_vectorized.py
  function test_LMTD_vect (line 29) | def test_LMTD_vect():
Condensed preview — 100 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,349K chars).
[
  {
    "path": ".github/workflows/build-multiarch.yml",
    "chars": 1767,
    "preview": "name: Build Multiarch\n\non:\n  push:\n    branches: [release]\n  pull_request:\n    branches: [master, release]\n\nconcurrency:"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 4634,
    "preview": "name: Build\n\non:\n  push:\n    branches: [master, release]\n  pull_request:\n    branches: [master, release]\n\nconcurrency:\n "
  },
  {
    "path": ".github/workflows/build_multi_numpy_scipy.yml",
    "chars": 1656,
    "preview": "name: Build-Test-Multi-Scipy-Numpy\non:\n  push:\n    branches: [release]\n  pull_request:\n    branches: [master, release]\n\n"
  },
  {
    "path": ".github/workflows/build_third_party_packagers.yml",
    "chars": 1498,
    "preview": "name: Check Third-Party Packager Compatibility\n\non:\n  push:\n    branches: [release]\n  pull_request:\n    branches: [maste"
  },
  {
    "path": ".github/workflows/publish-pypi.yml",
    "chars": 1699,
    "preview": "name: Publish to PyPI\non:\n  release:\n    types: [published]\n\npermissions:\n  id-token: write\n  contents: read\njobs:\n  tes"
  },
  {
    "path": ".github/workflows/quality.yml",
    "chars": 3104,
    "preview": "name: Quality & Validation\n\non:\n  push:\n    branches: [master, release]\n  pull_request:\n    branches: [master, release]\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 852,
    "preview": "repos:\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    # Ruff version.\n    rev: v0.14.0\n    hooks:\n      # Ru"
  },
  {
    "path": ".readthedocs.yaml",
    "chars": 627,
    "preview": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html f"
  },
  {
    "path": "AUTHORS",
    "chars": 42,
    "preview": "Caleb Bell <Caleb.Andrew.Bell@gmail.com>\n\n"
  },
  {
    "path": "Changelog.md",
    "chars": 4226,
    "preview": "# Changelog\n\n## [Unreleased]\n\n## [1.2.0] - 2025-10-26\n\n### Added\n\n- Project is now PEP 517 compliant and doesn't use dep"
  },
  {
    "path": "Justfile",
    "chars": 19941,
    "preview": "# Use a strict shell for more predictable recipe execution.\n# The \"-c\" is crucial: it tells bash to treat the recipe lin"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1085,
    "preview": "Copyright (C) 2016, Caleb Bell <Caleb.Andrew.Bell@gmail.com>\n\nPermission is hereby granted, free of charge, to any perso"
  },
  {
    "path": "MANIFEST.in",
    "chars": 443,
    "preview": "include LICENSE.txt AUTHORS README.rst pytest.ini\nrecursive-include tests *\nrecursive-include docs *\nrecursive-include _"
  },
  {
    "path": "README.rst",
    "chars": 3663,
    "preview": "==================\nHeat Transfer (ht)\n==================\n\n.. image:: http://img.shields.io/pypi/v/ht.svg?style=flat\n   :"
  },
  {
    "path": "asv.conf.json",
    "chars": 5836,
    "preview": "{\n    // The version of the config file format.  Do not change, unless\n    // you know what you are doing.\n    \"version\""
  },
  {
    "path": "bench/ht function performance comparison.ipynb",
    "chars": 11751,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n "
  },
  {
    "path": "conftest.py",
    "chars": 2461,
    "preview": "import platform\nimport sys\n\n# Detect Python implementation and version\nis_pypy = \"PyPy\" in sys.version\nis_graal = \"Graal"
  },
  {
    "path": "dev/basic_standalone_ht_check.py",
    "chars": 881,
    "preview": "import ht\nfrom ht import *\nimport numpy as np\nimport scipy.integrate\nimport scipy.interpolate\nimport scipy.spatial\nimpor"
  },
  {
    "path": "dev/cx_freeze/cx_freeze_basic_standalone_check_builder.py",
    "chars": 2771,
    "preview": "from cx_Freeze import setup, Executable\n\nbuild_exe_options = {\n    \"packages\": [\"numpy\", \"scipy\", \"ht\", \"fluids\"],\n    \""
  },
  {
    "path": "dev/prerelease.py",
    "chars": 1981,
    "preview": "import os\nimport shutil\nimport sys\n\nif sys.version_info.major != 3 and sys.version_info.minor != 11:\n\traise ValueError(\""
  },
  {
    "path": "docs/conf.py",
    "chars": 10775,
    "preview": "#\n# Heat Transfer documentation build configuration file, created by\n# sphinx-quickstart on Sat Jan  2 17:15:23 2016.\n#\n"
  },
  {
    "path": "docs/ht.air_cooler.rst",
    "chars": 178,
    "preview": "Air cooler sizing and rating (ht.air_cooler)\n============================================\n\n.. automodule:: ht.air_cooler"
  },
  {
    "path": "docs/ht.boiling_flow.rst",
    "chars": 153,
    "preview": "\nFlow boiling (ht.boiling_flow)\n==============================\n\n.. automodule:: ht.boiling_flow\n    :members:\n    :undoc"
  },
  {
    "path": "docs/ht.boiling_nucleic.rst",
    "chars": 213,
    "preview": "Nucleic boiling and critical heat flux (ht.boiling_nucleic)\n===========================================================\n"
  },
  {
    "path": "docs/ht.boiling_plate.rst",
    "chars": 206,
    "preview": "\nBoiling in plate and frame exchangers (ht.boiling_plate)\n========================================================\n\n.. a"
  },
  {
    "path": "docs/ht.condensation.rst",
    "chars": 152,
    "preview": "Condensation (ht.condensation)\n==============================\n\n.. automodule:: ht.condensation\n    :members:\n    :undoc-"
  },
  {
    "path": "docs/ht.conduction.rst",
    "chars": 178,
    "preview": "Conduction and shape factors (ht.conduction)\n============================================\n\n.. automodule:: ht.conduction"
  },
  {
    "path": "docs/ht.conv_external.rst",
    "chars": 169,
    "preview": "External convection (ht.conv_external)\n======================================\n\n.. automodule:: ht.conv_external\n    :mem"
  },
  {
    "path": "docs/ht.conv_free_enclosed.rst",
    "chars": 214,
    "preview": "Free convection to enclosed bodies (ht.conv_free_enclosed)\n==========================================================\n\n."
  },
  {
    "path": "docs/ht.conv_free_immersed.rst",
    "chars": 214,
    "preview": "Free convection to immersed bodies (ht.conv_free_immersed)\n==========================================================\n\n."
  },
  {
    "path": "docs/ht.conv_internal.rst",
    "chars": 169,
    "preview": "Internal convection (ht.conv_internal)\n======================================\n\n.. automodule:: ht.conv_internal\n    :mem"
  },
  {
    "path": "docs/ht.conv_jacket.rst",
    "chars": 185,
    "preview": "Convection to jacketed vessels (ht.conv_jacket)\n===============================================\n\n.. automodule:: ht.conv"
  },
  {
    "path": "docs/ht.conv_packed_bed.rst",
    "chars": 187,
    "preview": "Convection to packed beds (ht.conv_packed_bed)\n==============================================\n\n.. automodule:: ht.conv_p"
  },
  {
    "path": "docs/ht.conv_plate.rst",
    "chars": 225,
    "preview": "Convection to Plate Heat Exchangers (single-phase) (ht.conv_plate)\n====================================================="
  },
  {
    "path": "docs/ht.conv_supercritical.rst",
    "chars": 218,
    "preview": "Convection with supercritical fluids (ht.conv_supercritical)\n==========================================================="
  },
  {
    "path": "docs/ht.conv_tube_bank.rst",
    "chars": 236,
    "preview": "Heat transfer and pressure drop across tube bundles (ht.conv_tube_bank)\n================================================"
  },
  {
    "path": "docs/ht.conv_two_phase.rst",
    "chars": 242,
    "preview": "Non boiling and non condensing two-phase heat transfer (ht.conv_two_phase)\n============================================="
  },
  {
    "path": "docs/ht.core.rst",
    "chars": 150,
    "preview": "Miscellaneous utilities (ht.core)\n=================================\n\n.. automodule:: ht.core\n    :members:\n    :undoc-me"
  },
  {
    "path": "docs/ht.hx.rst",
    "chars": 162,
    "preview": "Heat exchanger sizing and rating (ht.hx)\n========================================\n\n.. automodule:: ht.hx\n    :members:\n "
  },
  {
    "path": "docs/ht.insulation.rst",
    "chars": 236,
    "preview": "Database of insulating and refractory material properties (ht.insulation)\n=============================================="
  },
  {
    "path": "docs/ht.numba.rst",
    "chars": 4005,
    "preview": "Support for Numba (ht.numba)\n============================\n\nBasic module which wraps most of ht functions and classes to "
  },
  {
    "path": "docs/ht.radiation.rst",
    "chars": 171,
    "preview": "Heat transfer by radiation (ht.radiation)\n=========================================\n\n.. automodule:: ht.radiation\n    :m"
  },
  {
    "path": "docs/ht.vectorized.rst",
    "chars": 538,
    "preview": "Support for numpy arrays (ht.vectorized)\n========================================\n\n\nBasic module which wraps all ht func"
  },
  {
    "path": "docs/index.rst",
    "chars": 3703,
    "preview": "ht: Heat Transfer component of Chemical Engineering Design Library (ChEDL)\n============================================="
  },
  {
    "path": "docs/plots/Nu_external_cylinder.py",
    "chars": 1217,
    "preview": "import matplotlib.pyplot as plt\nimport numpy as np\n\nfrom ht.conv_external import Nu_external_cylinder, conv_external_cyl"
  },
  {
    "path": "docs/test_documentation.py",
    "chars": 1734,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2021 Caleb Bell <Caleb.And"
  },
  {
    "path": "docs/tutorial.rst",
    "chars": 11199,
    "preview": "Tutorial\n========\n\nIntroduction\n------------\n\nht is the heat transfer component of the Chemical Engineering Design Libra"
  },
  {
    "path": "ht/air_cooler.py",
    "chars": 42373,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/boiling_flow.py",
    "chars": 33156,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/boiling_nucleic.py",
    "chars": 50731,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, Caleb Bell <Ca"
  },
  {
    "path": "ht/boiling_plate.py",
    "chars": 23584,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2017, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/condensation.py",
    "chars": 14396,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conduction.py",
    "chars": 20414,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019 Cal"
  },
  {
    "path": "ht/conv_external.py",
    "chars": 33257,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_free_enclosed.py",
    "chars": 26128,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2019, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_free_immersed.py",
    "chars": 59092,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_internal.py",
    "chars": 60628,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017 Caleb Bell <Cal"
  },
  {
    "path": "ht/conv_jacket.py",
    "chars": 12120,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_packed_bed.py",
    "chars": 8402,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_plate.py",
    "chars": 14086,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2018, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_supercritical.py",
    "chars": 53746,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/conv_tube_bank.py",
    "chars": 65496,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018 Caleb Bel"
  },
  {
    "path": "ht/conv_two_phase.py",
    "chars": 35577,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018 Caleb Bel"
  },
  {
    "path": "ht/core.py",
    "chars": 21121,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018 Caleb Bel"
  },
  {
    "path": "ht/hx.py",
    "chars": 236330,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018 Caleb Bel"
  },
  {
    "path": "ht/insulation.py",
    "chars": 41510,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/numba.py",
    "chars": 4252,
    "preview": "# type: ignore\n\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2020, 2021,"
  },
  {
    "path": "ht/numba_vectorized.py",
    "chars": 1593,
    "preview": "# type: ignore\n\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2020, Caleb"
  },
  {
    "path": "ht/py.typed",
    "chars": 2,
    "preview": " \n"
  },
  {
    "path": "ht/radiation.py",
    "chars": 9185,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, Caleb Bell <Caleb.An"
  },
  {
    "path": "ht/units.py",
    "chars": 2902,
    "preview": "# type: ignore\n\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2017, Caleb"
  },
  {
    "path": "ht/vectorized.py",
    "chars": 2460,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2017, Caleb Bell <Caleb.An"
  },
  {
    "path": "pyproject.toml",
    "chars": 8483,
    "preview": "[build-system]\nrequires = [\"setuptools>=70.1\", \"wheel\", \"python-minifier\"]\nbuild-backend = \"backend\"\nbackend-path = [\"_c"
  },
  {
    "path": "requirements_security.txt",
    "chars": 39,
    "preview": "numpy>=1.5.0\nscipy>=1.6.0\nfluids>=1.2.0"
  },
  {
    "path": "tests/test_air_cooler.py",
    "chars": 12852,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_boiling_flow.py",
    "chars": 6041,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_boiling_nucleic.py",
    "chars": 13100,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_boiling_plate.py",
    "chars": 3131,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2017, 2018, 2019, Caleb Be"
  },
  {
    "path": "tests/test_condensation.py",
    "chars": 4169,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conduction.py",
    "chars": 6055,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_external.py",
    "chars": 6867,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_free_enclosed.py",
    "chars": 7784,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2019, Caleb Bell <Caleb.An"
  },
  {
    "path": "tests/test_conv_free_immersed.py",
    "chars": 9432,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_internal.py",
    "chars": 9499,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_jacket.py",
    "chars": 3752,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_packed_bed.py",
    "chars": 1886,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_plate.py",
    "chars": 3910,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2018 Caleb Bell <Caleb.And"
  },
  {
    "path": "tests/test_conv_supercritical.py",
    "chars": 5472,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017, 2018, 2019, Ca"
  },
  {
    "path": "tests/test_conv_tube_bank.py",
    "chars": 96697,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017 Caleb Bell <Cal"
  },
  {
    "path": "tests/test_conv_two_phase.py",
    "chars": 3549,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017 Caleb Bell <Cal"
  },
  {
    "path": "tests/test_core.py",
    "chars": 6066,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017 Caleb Bell <Cal"
  },
  {
    "path": "tests/test_hx.py",
    "chars": 76384,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017 Caleb Bell <Cal"
  },
  {
    "path": "tests/test_numba.py",
    "chars": 24333,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2020, 2021, 2022 Caleb Bel"
  },
  {
    "path": "tests/test_radiation.py",
    "chars": 2212,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2016, 2017 Caleb Bell <Cal"
  },
  {
    "path": "tests/test_units.py",
    "chars": 5866,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2017 Caleb Bell <Caleb.And"
  },
  {
    "path": "tests/test_vectorized.py",
    "chars": 1431,
    "preview": "\"\"\"Chemical Engineering Design Library (ChEDL). Utilities for process modeling.\nCopyright (C) 2017 Caleb Bell <Caleb.And"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the CalebBell/ht GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 100 files (1.2 MB), approximately 450.7k tokens, and a symbol index with 518 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!