carbatpy.models.components.class_he =================================== .. py:module:: carbatpy.models.components.class_he .. autoapi-nested-parse:: Created on Thu Jul 25 16:42:44 2024 heat-exchanger class @author: welp Attributes ---------- .. autoapisummary:: carbatpy.models.components.class_he.now Classes ------- .. autoapisummary:: carbatpy.models.components.class_he.HeatExchanger Module Contents --------------- .. py:class:: HeatExchanger(fluids, inlet_states, mdot, resolution, ht_method, pl_method) Static heat exchanger model for counterflow configurations. Supports double-pipe and monoblock geometries. Heat exchanger profiles are computed by integrating coupled enthalpy and pressure ODEs along the tube length using ``scipy.integrate.solve_ivp``. Two inlet-state conventions are available: - **One-side IVP** (:meth:`calc_IVP_one_side`): Both inlet states are provided for the *same* side (inlet–outlet pair). Avoids the internal root-finding step and is the recommended approach. - **Two-side IVP** (:meth:`calc_IVP`): True counterflow setup — inlet states are given for *both* fluid inlets (left and right end). Requires an internal root-find to determine the unknown starting conditions. Geometry Options ---------------- - **Double-pipe** (:meth:`geo_double_pipe`): Annular secondary-fluid channel around an inner tube carrying the working fluid. - **Monoblock** (:meth:`geo_monoblock`): Two parallel tubes embedded in a solid conductive block. Thermal coupling is via the block material, using the VDI Heat Atlas shape factor for two eccentric cylinders. :param fluids: Two-element list ``[working_fluid, secondary_fluid]``. Each fluid must expose a ``set_state`` method compatible with carbatpy.fprop. :type fluids: list of fluid objects :param inlet_states: Two-element list ``[state_in_wf, state_in_sf]``. Each state is the standard carbatpy property array (T, p, h, v, s, q, u, …). :type inlet_states: list of array_like :param mdot: Two-element list ``[mdot_wf, mdot_sf]``. Mass flow rates in kg/s. :type mdot: list of float :param resolution: Number of spatial evaluation points along the heat exchanger length. :type resolution: int :param ht_method: Heat transfer correlation for the working fluid passed to ``heat_transfer.alpha_km``. Example: ``"Deng_et_al-2019"``. :type ht_method: str :param pl_method: Pressure-loss correlation for the working fluid passed to ``heat_transfer.alpha_km``. Example: ``"Macdonald_et_al-2016"``. :type pl_method: str .. attribute:: state_array_wf Fluid-property states along the heat exchanger for the working fluid. :type: ndarray, shape (n, n_props) .. attribute:: state_array_sf Fluid-property states along the heat exchanger for the secondary fluid. :type: ndarray, shape (n, n_props) .. attribute:: x Axial positions [m] corresponding to the state arrays. :type: ndarray .. attribute:: alpha_wf Local heat transfer coefficients of the working fluid [W/(m²·K)]. :type: list of float .. attribute:: alpha_sf Local heat transfer coefficients of the secondary fluid [W/(m²·K)]. :type: list of float .. attribute:: dpdl_wf Local pressure-loss gradients of the working fluid [Pa/m]. :type: list of float .. attribute:: dpdl_sf Local pressure-loss gradients of the secondary fluid [Pa/m]. :type: list of float .. attribute:: termination_flag Name of the termination-event function that stopped the ODE solver, or ``None`` if the solver reached the end of the integration interval. :type: str or None .. attribute:: termination_position Axial position [m] at which termination occurred, or ``None``. :type: float or None .. rubric:: Examples Double-pipe condenser solved with the one-side IVP method: >>> import carbatpy as cb >>> from class_he import HeatExchanger >>> fluid1 = cb.init_fluid("Butane * CO2", [0.8, 0.2]) >>> fluid2 = cb.fprop.init_fluid("water", [1]) >>> st1_in = fluid1.set_state([3e6, 1], "PQ") >>> st2_in = fluid2.set_state([st1_in[0] - 10, 3e5], "TP", cb.fprop._TRANS_STRING) >>> he = HeatExchanger( ... fluids=[fluid1, fluid2], ... inlet_states=[st1_in, st2_in], ... mdot=[0.01, 0.01], ... resolution=40, ... ht_method="Deng_et_al-2019", ... pl_method="Macdonald_et_al-2016", ... ) >>> he.geo_double_pipe(16e-3, 18e-3, 22e-3, 40e-3, 40, 15.4) >>> he.calc_IVP_one_side(case="wf_condensed") >>> he.diagram() .. seealso:: :obj:`heat_transfer` Heat transfer and pressure-loss correlations used internally by :meth:`thermal_resistance`. .. py:attribute:: fluids .. py:attribute:: input_state_wf .. py:attribute:: input_state_sf .. py:attribute:: mdot_wf .. py:attribute:: mdot_sf .. py:attribute:: resolution .. py:attribute:: dT_wall :value: 5 .. py:attribute:: ht_method .. py:attribute:: pl_method .. py:method:: geo_monoblock(d_wf, d_sf, r, l, lam_block) Set monoblock heat exchanger geometry. Two parallel circular tubes are embedded in a solid conductive block. The thermal coupling between them is computed via the VDI Heat Atlas (2013) shape factor for two cylinders in an infinite medium (Table E1-3). :param d_wf: Inner diameter of the working-fluid tube [m]. :type d_wf: float :param d_sf: Inner diameter of the secondary-fluid tube [m]. :type d_sf: float :param r: Centre-to-centre distance between the two tubes [m]. :type r: float :param l: Length of the block (and tubes) [m]. :type l: float :param lam_block: Thermal conductivity of the block material [W/(m·K)]. :type lam_block: float .. rubric:: Notes The conduction shape factor for two cylinders in an infinite medium (VDI Heat Atlas 2013, E1 Table 3) is: .. math:: S = \frac{2\pi}{\mathrm{arccosh}\! \left(\frac{r^2 - r_{wf}^2 - r_{sf}^2} {2\, r_{wf}\, r_{sf}}\right)} where :math:`r_{wf} = d_{wf}/2` and :math:`r_{sf} = d_{sf}/2`. .. py:method:: geo_double_pipe(di, Di, da, Da, l, lam_tube) Set double-pipe heat exchanger geometry. The working fluid flows through the inner tube; the secondary fluid flows counter-currently through the annular gap between inner and outer tube. :param di: Inner diameter of the inner tube (working-fluid side) [m]. :type di: float :param Di: Outer diameter of the inner tube [m]. :type Di: float :param da: Inner diameter of the outer tube (secondary-fluid side) [m]. :type da: float :param Da: Outer diameter of the outer tube [m]. :type Da: float :param l: Length of the pipe section [m]. :type l: float :param lam_tube: Thermal conductivity of the inner-tube wall material [W/(m·K)]. :type lam_tube: float .. rubric:: Notes The total heat transfer area referenced to the inner tube surface is set to :math:`A = \pi d_i l`. The thermal resistance network (per unit length) is: .. math:: R_{tot} = \underbrace{\frac{1}{\alpha_{sf}\, \pi D_i}}_{R_{sf}} + \underbrace{\frac{\ln(D_i/d_i)}{2\pi\lambda_{tube}}}_{R_{tube}} + \underbrace{\frac{1}{\alpha_{wf}\, \pi d_i}}_{R_{wf}} .. rubric:: Examples >>> he.geo_double_pipe(16e-3, 18e-3, 22e-3, 40e-3, 40, 15.4) .. py:method:: calc_IVP_one_side(verbose=False, wf_outlet=False, case=None, stop_value=0) Calculate the heat exchanger by integrating the governing ODEs. Both inlet states must be provided for the *same* physical side of the heat exchanger (i.e. an inlet–outlet pair, not two true inlets). The ``wf_outlet`` flag controls which end of the domain is used as the working-fluid inlet. This is the **recommended** calculation method because it avoids the internal root-finding step required by :meth:`calc_IVP`. :param verbose: If ``True``, print the solver status message. :type verbose: bool, default=False :param wf_outlet: Flow-direction flag. - ``False``: working-fluid inlet is at :math:`x = 0`. - ``True``: working-fluid inlet is at :math:`x = l` (outlet side). :type wf_outlet: bool, default=False :param case: Optional early-termination criterion. Supported values: - ``'sf_temperature_min'`` – stop when secondary-fluid temperature falls below ``stop_value`` [K]. - ``'sf_enthalpy_min'`` – stop when secondary-fluid enthalpy falls below ``stop_value`` [J/kg]. - ``'wf_enthalpy_min'`` – stop when working-fluid enthalpy falls below ``stop_value`` [J/kg]. - ``'wf_enthalpy_max'`` – stop when working-fluid enthalpy exceeds ``stop_value`` [J/kg]. - ``'wf_condensed'`` – stop when working fluid is fully condensed (vapour quality ≤ saturated-liquid enthalpy). - ``'wf_evaporated'`` – stop when working fluid is fully evaporated (vapour quality ≥ saturated-vapour enthalpy). :type case: str or None, default=None :param stop_value: Threshold value used by the selected ``case``. Unit depends on the chosen case (K or J/kg). :type stop_value: float, default=0 :returns: Results are stored in ``state_array_wf``, ``state_array_sf``, ``x``, ``alpha_wf``, ``alpha_sf``, ``dpdl_wf``, ``dpdl_sf``, ``termination_flag``, and ``termination_position``. :rtype: None .. rubric:: Examples >>> he.geo_double_pipe(16e-3, 18e-3, 22e-3, 40e-3, 40, 15.4) >>> he.calc_IVP_one_side(verbose=True, case="wf_condensed") >>> print(he.termination_flag) .. py:method:: calc_IVP(verbose=False, case=None, stop_value=0) Calculate the heat exchanger using true counterflow inlet conditions. .. note:: **Not recommended.** Use :meth:`calc_IVP_one_side` instead, which avoids the internal root-finding step and is more robust. Inlet states must be the true inlet states for *both* fluids — one at each end of the heat exchanger. An internal ``scipy.optimize.root`` call iterates on the unknown secondary-fluid state at :math:`x = 0` until the prescribed secondary-fluid inlet condition at :math:`x = l` is matched. :param verbose: If ``True``, print the root-finder result and solver status. :type verbose: bool, default=False :param case: Optional early-termination criterion (see :meth:`calc_IVP_one_side` for supported values). :type case: str or None, default=None :param stop_value: Threshold for the selected ``case``. :type stop_value: float, default=0 :returns: Results are stored in ``state_array_wf``, ``state_array_sf``, ``x``, ``alpha_wf``, ``alpha_sf``, ``dpdl_wf``, and ``dpdl_sf``. :rtype: None :raises ValueError: If the root-finder does not converge. .. py:method:: thermal_resistance(state_wf_x, state_sf_x) Calculate the local total thermal resistance and pressure-loss gradients. Dispatches to the correct resistance model based on ``self.geo_flag`` (``"double_pipe"`` or ``"monoblock"``), appends the local heat transfer coefficients and pressure-loss gradients to the instance lists, and updates ``self.dT_wall``. :param state_wf_x: Local carbatpy property array of the working fluid (T, p, h, …). :type state_wf_x: array_like :param state_sf_x: Local carbatpy property array of the secondary fluid (T, p, h, …). :type state_sf_x: array_like :returns: * **R_ges** (*float*) -- Total thermal resistance per unit length [(m·K)/W]. * **dp_wf** (*float*) -- Pressure-loss gradient of the working fluid [Pa/m]. * **dp_sf** (*float*) -- Pressure-loss gradient of the secondary fluid [Pa/m]. .. rubric:: Notes **Double-pipe** resistance network (per unit length): .. math:: R_{tot} = \frac{1}{\alpha_{sf}\,\pi D_i} + \frac{\ln(D_i/d_i)}{2\pi\lambda_{tube}} + \frac{1}{\alpha_{wf}\,\pi d_i} **Monoblock** resistance network (per unit length): .. math:: R_{tot} = \frac{1}{\alpha_{sf}\,\pi d_{sf}} + \frac{1}{\lambda_{block}\, S} + \frac{1}{\alpha_{wf}\,\pi d_{wf}} with the VDI shape factor :math:`S` from :meth:`geo_monoblock`. .. py:method:: diagram(ordinate=0, second_yaxis=False, save_dir=None, show_plot=False) Plot fluid-property profiles along the heat exchanger length. The abscissa is the axial coordinate :math:`x` [m]. The ordinate is selected by index, matching the column order of ``state_array_wf`` / ``state_array_sf``: - 0: Temperature [K] - 1: Pressure [Pa] - 2: Specific enthalpy [J/kg] - 3: Specific volume [m³/kg] - 4: Specific entropy [J/(kg·K)] - 5: Vapour quality [–] - 6: Specific internal energy [J/kg] - 7: Dynamic viscosity [Pa·s] - 8: Thermal conductivity [W/(m·K)] - 9: Prandtl number [–] - 10: Kinematic viscosity [m²/s] - 11: Molar mass [kg/mol] - 12: Speed of sound [m/s] :param ordinate: Column index of the property to plot (see list above). :type ordinate: int, default=0 :param second_yaxis: If ``True``, working and secondary fluid are plotted on separate y-axes (useful when the two fluids have very different scales). :type second_yaxis: bool, default=False :param save_dir: Directory path for saving the figure as a PNG file. If ``None``, the figure is only displayed. :type save_dir: str or None, default=None :param show_plot: If "True", plots are shown. :type show_plot: bool, default=False :rtype: None .. rubric:: Examples >>> he.calc_IVP_one_side() >>> he.diagram() # temperature profile >>> he.diagram(ordinate=1, second_yaxis=True) # pressure, dual axes >>> he.diagram(ordinate=2, save_dir=r"C:/results") # save enthalpy plot .. py:method:: calculate_arrays(save_dir=None) Export calculation results to CSV files and a termination-event log. Writes three files to ``save_dir``: - ``wf.csv``: Working-fluid state array with columns ``T, p, h, v, s, q, u, alpha_wf, dpdl_wf, tcx, vis``. ``tcx`` and ``vis`` are the saturated-liquid thermal conductivity and viscosity where the vapour quality is ≥ 0; otherwise 0. - ``sf.csv``: Secondary-fluid state array with columns ``T, p, h, v, s, q, u, eta, tcx, Pr, cp, ws, M, alpha_sf, dpdl_sf``. - ``termination_event.txt``: Plain-text log of ``termination_flag`` and ``termination_position``. :param save_dir: Absolute path to an existing directory where the files will be saved. :type save_dir: str :returns: * **df_wf** (*DataFrame*) * **df_wf** (*DataFrame*) .. py:data:: now