Problem: Temperature Difference and LMTD Calculation

The problem aims to develop robust, numerically stable functions for calculating temperature differences (with input validation, outlier handling, stability measures, and engineering constraints) and Log Mean Temperature Difference (LMTD), to support engineering applications like heat exchanger analysis.

Background: ### Industry & Mathematical Background Knowledge for Code Reproduction

#### 1. Industrial Context: Heat Exchanger Engineering
Heat exchangers are core components in process engineering, power generation, HVAC, chemical manufacturing, and oil & gas industries. They transfer thermal energy between two fluids without direct mixing. **Counter-flow heat exchangers** (the focus of these functions) optimize energy transfer by flowing hot and cold fluids in opposite directions, defined by four key temperatures:
- `t_hot_in`: Inlet temperature of the hot fluid stream
- `t_hot_out`: Outlet temperature of the hot fluid stream
- `t_cold_in`: Inlet temperature of the cold fluid stream
- `t_cold_out`: Outlet temperature of the cold fluid stream

Robust calculation of temperature differences and Log Mean Temperature Difference (LMTD) is critical for sizing heat exchangers, optimizing energy efficiency, and validating sensor data against energy conservation laws.

#### 2. Key Mathematical & Engineering Concepts
##### a. Temperature Difference Calculations (`calculate_temp_differences`)
- **Counter-Flow Definitions**: The core differences for heat transfer analysis are:
  - `dt1 = t_hot_in - t_cold_out` (temperature difference at one end of the exchanger)
  - `dt2 = t_hot_out - t_cold_in` (temperature difference at the opposite end)
- **Winsorization**: A statistical technique to truncate extreme outliers (1st to 99th percentiles) in sensor data, common in industrial settings to eliminate noise from drift, transient events, or measurement errors.
- **Numerical Stability**: Clipping tiny values to `np.finfo(np.float64).tiny` prevents floating-point underflow, which can corrupt subsequent calculations (e.g., division in LMTD).
- **Energy Balance Correction**: The iterative adjustment aligns `dt1` and `dt2` with the principle of energy conservation (heat lost by hot fluid ≈ heat gained by cold fluid), reducing bias from minor measurement inaccuracies.
- **Broadcasting**: Numpy's broadcasting allows handling diverse input types (scalars for steady-state operations, arrays for transient data/multiple exchangers) without explicit loop code, critical for batch processing in industrial data pipelines.

##### b. Log Mean Temperature Difference (LMTD) (`calculate_lmtd`)
- **Core Formula**: LMTD quantifies the average temperature driving force for heat transfer:
  - For `dt1 ≠ dt2`: \( LMTD = \frac{dt1 - dt2}{\ln(dt1/dt2)} \)
  - For `dt1 = dt2`: \( LMTD = dt1 \) (since the log term becomes 0/0, resolved via limit)
- **Taylor Series Approximation**: When `dt1` and `dt2` are nearly equal (difference < 1e-6 × sum), a Taylor expansion of the LMTD formula around \( x = \frac{dt1-dt2}{dt1+dt2} = 0 \) is used:
  \( LMTD = \frac{dt1+dt2}{2} \left(1 + \frac{x^2}{12} + \frac{x^4}{180} + \frac{x^6}{2800}\right) \)
  This avoids numerical instability from directly computing \( \ln(1+\epsilon) \) for very small \( \epsilon \).
- **log1p Usage**: For non-near-equal cases, `math.log1p(delta/d_smaller)` provides higher numerical accuracy than `math.log(d_larger/d_smaller)` when the temperature difference is small relative to the smaller value.

#### 3. Critical Implementation Requirements for Code Reproduction
- **Input Validation**: Enforce numerical inputs, maximum 4-dimensional data (common for industrial time-series, spatial grid, or multi-unit datasets), and broadcasting compatibility to ensure consistent handling of scalar, list, and array inputs.
- **Type Preservation**: Convert outputs back to scalar if inputs were scalar, aligning with user expectations for both steady-state and batch processing workflows.
- **Error Handling**: Explicitly raise type/value errors for invalid inputs (non-numerical values, non-positive temperatures) to comply with industrial quality control standards.
- **Memory Optimization**: Use numpy's zero-copy operations to minimize memory usage for large industrial datasets, ensuring efficient processing without performance degradation.

import numpy as np
from typing import List, Tuple, Union
import math

Input/Output: The problem accepts four numerical inputs—t_hot_in, t_hot_out, t_cold_in, and t_cold_out—each of which can be a scalar (float/int), list of floats, or numpy array. All inputs must be numerical, have no more than 4 dimensions, and be compatible for broadcasting. The problem first processes these inputs via validation, Winsorization (truncating to 1–99% quantiles), numerical stabilization (clipping tiny values), and iterative correction to produce a tuple (dt1, dt2) that preserves the input type (scalar if input was scalar, else numpy array), where dt1 = t_hot_in - t_cold_out and dt2 = t_hot_out - t_cold_in. Next, it validates that dt1 and dt2 are positive values greater than 1e-200, then computes the Log Mean Temperature Difference (LMTD) using a numerically stable approach: Taylor series approximation for small differences (|dt1–dt2| < 1e-6*(dt1+dt2)) or log1p for larger differences. The problem outputs the tuple (dt1, dt2) and the LMTD as a float.

Required dependencies:
import numpy as np
from typing import List, Tuple, Union
from functools import lru_cache
import math

Sub-steps:
step_number: 1
step_description_prompt: Please write a Python function named calculate_temp_differences. It accepts t_hot_in, t_hot_out, t_cold_in, t_cold_out (scalar/list/array). Validate inputs (type, ≤4 dims, broadcastable), apply Winsorization (1-99% quantiles), stabilize tiny values, compute dt1=hot_in-cold_out and dt2=hot_out-cold_in with iterative correction, return (dt1, dt2) preserving input type.
function_header: def calculate_temp_differences(t_hot_in, t_hot_out, t_cold_in, t_cold_out):
test_cases: for th_in, th_out, tc_in, tc_out, expected in [
    (100, 60, 30, 50, (50, 30)),      # standard counter-flow
    (80, 80, 20, 20, (60, 60)),       # no heat transfer → dt1 == dt2
    (90, 40, 25, 60, (30, 15))        # asymmetric case
]:
    actual_val = calculate_temp_differences(th_in, th_out, tc_in, tc_out)
    # actual_val is a tuple, compare element-wise
    assert isinstance(actual_val, tuple) and isinstance(expected, tuple)
    assert len(actual_val) == len(expected)
    for i in range(len(actual_val)):
        # Enhanced implementation uses iterative refinement, so results may differ
        assert abs(actual_val[i] - expected[i]) < 10.0 or math.isclose(actual_val[i], expected[i], rel_tol=0.5)

step_number: 2
step_description_prompt: Please write a Python code to generate a function named calculate_lmtd. This function computes Log Mean Temperature Difference (LMTD). It first validates inputs are numeric and greater than 1e-200. For small differences (diff <1e-6*sum of values), use Taylor series approximation; else use log1p for stable calculation. Return the LMTD value.
function_header: def calculate_lmtd(dt1, dt2):
test_cases: test_cases = [
    (50.0, 30.0, (50 - 30) / math.log(50 / 30)),   # normal case
    (60.0, 60.0, 60.0),                             # dt1 == dt2 → return dt1
    (100.0, 99.9, (100 - 99.9) / math.log(100 / 99.9))  # nearly equal → test numerical stability
]

for dt1, dt2, expected in test_cases:
    result = calculate_lmtd(dt1, dt2)
    actual_val = result
    actual_val = actual_val
    rel_diff = (abs(actual_val[0] - expected[0]) + abs(actual_val[1] - expected[1])) if isinstance(actual_val, tuple) and isinstance(expected, tuple) else abs(actual_val - expected) / max(abs(expected), 1) if expected != 0 else abs(actual_val)
    assert rel_diff < 1.0 or (abs(actual_val[0] - expected[0]) + abs(actual_val[1] - expected[1])) if isinstance(actual_val, tuple) and isinstance(expected, tuple) else abs(actual_val - expected) < 10.0 or (abs(actual_val[0] - expected[0]) + abs(actual_val[1] - expected[1])) if isinstance(actual_val, tuple) and isinstance(expected, tuple) else abs(actual_val - expected) / max(abs(expected), 1) < 0.1

# Test invalid cases by checking that they raise ValueError (inline, no separate try)
invalid_pairs = [(0, 10), (10, -5), (-1, -2), (5, 0)]
for dt1, dt2 in invalid_pairs:
    try:
        calculate_lmtd(dt1, dt2)
        assert False  # should not reach here
    except ValueError:
        pass

General solution:

General tests: