Source code for src.utils.functions

import numpy as np
import CoolProp.CoolProp as CP                                                 # for fluids

from model_param import ThermalCoeff
from config_data import AmbientTemp
from typing import Tuple                                                       # for type hints of function arguments


[docs] def calculate_r_conduction(d_outer_m:float, d_inner_m:float, l_m:float, k_w_per_mk:float) -> float: # toplotna upornost - Rth [K/W] """ Calculates the conduction resistance of a cylindrical element. :param d_outer: pipe external diameter in [m] :param d_inner: pipe internal diameter in [m] :param l_m: pipe (section) lenght in [m] :param k_w_per_mk: the material conductivity coefficient in [W/mK] :return r_cond: conduction resistance in [K/W] """ r_cond = (np.log(d_outer_m / d_inner_m)) / (2 * l_m * k_w_per_mk * np.pi) return r_cond
[docs] def calculate_r_convection(d_m:float, l_m:float, h_w_per_m2k:float) -> float: """ Calculates the convection resistance of a cylindrical element. :param d_m: diameter of the surface for the convection in [m] :param l_m: pipe (section) lenght in [m] :param h_w_per_m2k: the material convection coefficient in [W/m2K] :return r_conv: convection resistance in [K/W] """ r_conv = 1 / (d_m * l_m * h_w_per_m2k * np.pi) return r_conv
[docs] def calculate_r_total(d_pipe_inner_m:float, l_m:float, h_mat1_w_per_m2k:float, d_pipe_outer_m:float, k_mat1_w_per_mk:float, d_ins_outer_m:float, k_mat2_w_per_mk:float, h_loc_w_per_m2k:float) -> float: """ Calculates the total thermal resistance of a cylindrical element ---> fluid-pipe-insulation-air. :param d_pipe_inner_m: diameter of the surface for the convection inside the pipe section in [m] :param l_m: pipe section lenght in [m] :param h_mat1_w_per_m2k: the pipe heat transfer coefficient in [W/m2K] :param d_pipe_outer_m: external diameter of the pipe section in [m] :param k_mat1_w_per_mk: the pipe conductivity coefficient in [W/mK] :param d_ins_outer_m: external diameter of the insulation in [m] :param k_mat2_w_per_mk: the insulation conductivity coefficient in [W/mK] :param h_loc_w_per_m2k: heat transfer coefficient based on the location of the pipe section (surface, channel, soil) in [W/m2K] :return r_tot: total thermal resistance in [K/W] """ r1 = calculate_r_convection(d_pipe_inner_m, l_m, h_mat1_w_per_m2k) # convection from the fluid to the pipe material r2 = calculate_r_conduction(d_pipe_outer_m, d_pipe_inner_m, l_m, k_mat1_w_per_mk) # heat transfer through the pipe material r3 = calculate_r_conduction(d_ins_outer_m, d_pipe_outer_m, l_m, k_mat2_w_per_mk) # heat transfer through the insulation material r4 = calculate_r_convection(d_ins_outer_m, l_m, h_loc_w_per_m2k) # convection from the outside surface to ambient air # r_tot = (r1 + r2 + r3 + r4) return r_tot
[docs] def calculate_heat_flow_loss(r_tot:float, t_outer:float, t_inner:float) -> float: """ Calculates the flow of thermal energy loss in [W]. :param r_tot: total thermal resistance of a pipe section in [K/W] :param t_outer: ambient temperature in [K] :param t_inner: temperature of the fluid inside the pipeline in [K] :return qdot_loss: heat loss flow in [W] """ qdot_loss = (t_inner - t_outer) / r_tot return qdot_loss
[docs] def calculate_heat_flow(t_high:float, t_low:float, mdot:float, cp:float) -> float: """ Calculates the flow of thermal energy inside the pipeline [W]. :param t_high: higher temperature in [°C] :param t_low: lower temperature in [°C] :param mdot: mass flow in [kg/s] :param cp: specific heating capacity coefficient in [Ws/kgK] :return qdot: heat flow in [W] """ delta_t = t_high - t_low qdot = mdot * cp * delta_t return qdot
[docs] def calculate_flow_velocity(den_fluid:float, mdot_flow:float, d_pipe_inner:float) -> float: """ Calculates the velocity inside an individual segment of the pipeline (based on mass flow) in [m/s]. :param den_fluid: density of the fluid in [kg/m3] :param mdot_flow: fluid mass flow in [kg/s] :param d_pipe_inner: the internal diameter of the pipeline section in [m] :return v_flow: velocity of flow [m/s] """ v_flow = (4 * mdot_flow) / (np.pi * den_fluid * (d_pipe_inner * d_pipe_inner)) return v_flow
[docs] def calculate_average_flow_velocity(v_flow_m_per_s:list) -> float: """ Calculates the average velocity of the fluid flow in the pipeline in [m/s]. :param v_flow_m_per_s: list of fluid values in each pipe section in [m/s] :return v_avg_m_per_s: average flow velocity in the pipeline in [m/s] """ i = 0 v_sum = 0 for i in range(len(v_flow_m_per_s) - 1): v_sum = v_sum + v_flow_m_per_s[i] v_avg_m_per_s = v_sum / len(v_flow_m_per_s) return v_avg_m_per_s
[docs] def select_heat_transfer_coeff(location:str) -> float: """ Selects the heat transfer coefficient based on the location of the pipeline section. :param location: position of pipeline section :return h_conv: value of the heat transfer coefficient read from data table """ if (location == 'surface'): h_conv = ThermalCoeff.h_surface_w_per_m2k return h_conv elif (location == 'channel'): h_conv = ThermalCoeff.h_channel_w_per_m2k return h_conv elif (location == 'soil'): h_conv = ThermalCoeff.h_soil_w_per_m2k return h_conv else: raise ValueError(f"Unknown location '{location}'. Must be one of: 'soil', 'channel', or 'surface'.")
[docs] def select_ambient_temperature(location:str) -> float: """ Selects the external temperature based on the location of the pipeline section. :param location: position of pipeline section :return t_amb: temperature of the sorounding ambient in [°C] """ if (location == 'surface'): t_amb = AmbientTemp.t_surface_c return t_amb elif (location == 'channel'): t_amb = AmbientTemp.t_channel_c return t_amb elif (location == 'soil'): t_amb = AmbientTemp.t_soil_c return t_amb else: raise ValueError(f"Unknown location '{location}'. Must be one of: 'soil', 'channel', or 'ambient'.")
[docs] def calculate_pipe_internal_diameter(d_pipe_external:float, th_pipe:float) -> float: """ Calculates the internal diameter of a chosen pipe section. :param d_pipe_external: external diameter of the chosen pipe section in [m] :param th_pipe: pipe wall thickness of the chosen section in [m] :return d_pipe_internal: internal diameter of the chosen pipe section in [m] """ d_pipe_internal = d_pipe_external - (2 * th_pipe) return d_pipe_internal
[docs] def calculate_insulation_external_diameter(d_pipe_external:float, th_insulation:float) -> float: """ Calculates the external diameter of the insulation of a chosen pipe section. :param d_pipe_external: external diameter of the chosen pipe section in [m] :param th_insulation: insulation thickness of the chosen section in [m] :return d_insulation_external: external diameter of the chosen section in [m] """ d_insulation_external = d_pipe_external + (2 * th_insulation) return d_insulation_external
[docs] def calculate_insulation_thickness(location:str, d_pipe_nominal:str, th_insulation_dict:dict) -> float: """ Calculates the thickness of pipe section insulation based on its location in [m]. :param location: 'channel', 'surface', or 'soil' :param d_pipe_external: External pipe diameter at the given index in [m] :param th_insulation_dict: Dictionary with thickness data :return: th_insulation: Insulation thickness in [m] """ if location not in th_insulation_dict: raise ValueError(f"Invalid location '{location}'. Expected one of: {list(th_insulation_dict.keys())}.") th_data = th_insulation_dict[location] if d_pipe_nominal not in th_data: raise ValueError( f"Invalid nominal pipe diameter '{d_pipe_nominal}' for location '{location}'. Available sizes: {list(th_data.keys())}") th_insulation = th_data[d_pipe_nominal] / 1000 return th_insulation
[docs] def calculate_output_temperature(t_in:float, t_amb:float, mdot:float, cp:float, r_tot:float, tolerance:float = 0.001) -> Tuple[float, float]: """ Iteratively compute the outlet temperature accounting for energy loss and return the final heat loss. :param t_in: Inlet temperature in [°C] :param t_amb: Ambient temperature in [°C] :param mdot: Mass flow rate in [kg/s] :param cp: Specific heat coefficient in [Ws/kgK] :param r_tot: Total thermal resistance in [K/W] :param tolerance: Convergence tolerance (default 0.01 = 1%) :returns: t_out_c: Final outlet temperature in [°C] qdot_loss: Final heat flow loss in [W] """ qdot_loss_initial = calculate_heat_flow_loss(r_tot, t_amb, t_in) t_out_ref = t_in # setting the initial reference temperature ---> equal to inlet node temperature t_out = t_in - (qdot_loss_initial / (mdot * cp)) # calculates the outlet temperature value for the first iteration qdot_loss = qdot_loss_initial while abs((t_out_ref - t_out) / t_out_ref) > tolerance: # loops until the temperature at the outlet node (T_out) is calculated if t_out <= t_amb: # if the temperature inside the pipe is equal to the temperature around it #t_out_ref = t_out t_out = t_amb break qdot_loss = 0.0 else: if (t_in <= 0) or (t_out <= 0): raise ValueError(f"Temperature values at the inlet ({t_in}°C) and the outlet ({t_out}°C) nodes cannot be zero or negative.") else: try: t_avg = (t_in - t_out) / (np.log(t_in) - np.log(t_out)) # calcualating heat flow loss with the logarithmic mean temperature difference (inside the pipe section!) except ZeroDivisionError: t_avg = (t_in - t_out) / 2 # fallback to arithmetic average to avoid division by 0 qdot_loss = calculate_heat_flow_loss(r_tot, t_amb, t_avg) t_out_ref = t_out # T_ref becomes the current T_out t_out = t_in - (qdot_loss / (mdot * cp)) # a new T_out is calculated (until new T_out < current T_out) return t_out, qdot_loss
# ______________________ FLUIDS _______________________________________________
[docs] def calculate_fluid_density(p:float, t:float, fluid:str = "Water") -> float: """ Defines the fluid density in [kg/m3]. :param p: avrega pressure in the pipeline in [Pa] :param t: temperature in [K] :param fluid: fluid type :return den: density in [kg/m3] """ den = CP.PropsSI("D", "P", p, "T", t, fluid) return den
[docs] def calculate_fluid_specific_heat(p:float, t:float, fluid:str = "Water") -> float: """ Defines the fluid specific heat in [Ws/kgK]. :param p: avrega pressure in the pipeline in [Pa] :param t: temperature in [K] :param fluid: fluid type :return cp: specific heat coefficient in [Ws/kgK] """ cp = CP.PropsSI("C", "P", p, "T", t, fluid) return cp