Source code for component.solar.parabolic_trough_collector

# -*- coding: utf-8 -*-
"""
Created on Tue Jul 30 14:32:39 2024

@author: Basile

source : 
Semi-empirical correlation to model heat losses 
along solar parabolic trough collectors 
Rémi Dickes, Vincent Lemort and Sylvain Quoilin 
https://hdl.handle.net/2268/182680

"""

import __init__
#import component.solar.parabolictroughcollector as parabolictroughcollector
import CoolProp.CoolProp as CP
import numpy as np
from component.base_component import BaseComponent

from connector.mass_connector import MassConnector
from connector.heat_connector import HeatConnector


[docs] class PTCollector(BaseComponent): """ **Component**: Parabolic Trough Collector **Model**: Semi empirical **Description** This model determines the thermal power absorbed by a parabolic trough collector and its heat transfer fluid exhaust specific enthalpy . **Assumptions**: - Heat losses to the ambiant are considered. **Connectors**: su (MassConnector): Mass connector for the suction side. ex (MassConnector): Mass connector for the exhaust side. Q_amb (HeatConnector): Heat connector to the ambiant. **Parameters**: coll_eff: Collector efficieny [-] a [1..9]: semi empirical parameters n_disc: number of discretisations in the lenght of the heat collection element. [-] envel_tau: transmittance of the heat collection element envelop. [-] alpha_r: Receiver absorptivity. [-] refl_m: Mirror reflectivity. [-] epsilon_r: Receiver emittance @ 400°C. [-] eta_other: Other efficiency to be accounted for in the calculation of the overall optical efficiency, different than the transmittance and absorbtivity of the receiver and the mirror reflectivity. [-] V_w_max_tr: maximum wind speed (stowed). [m/s] V_w_max_st: minimum wind speed (stowed). [m/s] Vdot_min: minimum recommended flowrate. [m^3/s] Vdot_max: maximum recommended flowrate. [m^3/s] T_f_min: Minimum operating fluid temperature. [K] T_f_max: Maximum operating fluid temperature. [K] Geometrical parameters: L: length of the parabolic trough collector. [m] W: Width of the parabolic trough collector. [m] A: Area of the parabolic trough collector. [m^2] A_r: Reflective area. [m^2] m: Collector weight. [kg] L_f: Focal Length. [m] Tube_OD: Tube external diameter. [m] Tube_V: Tube volume. [m^3] **Inputs**: fluid: Fluid at the suction of the PT collector. [-] m_dot: Mass flow rate in the PT collector (at suction). [kg/s] T_su: Temperature at the suction. [K] P_su: Pressure at the suction. [Pa] v_wind: Wind speed on the PT collector's heat collection element. [m/s] T_amb: Ambiant temperature. [K] Theta: Incidence angle. [rad] DNI: Direct Normal Irradiance. [W/m^2] **Ouputs**: h_ex: Exhaust side specific enthalpy. [J/kg] Q_dot: Thermal Power absorbed by the PT collector. [W] """ def __init__(self): super().__init__() self.su = MassConnector() self.ex = MassConnector() self.Q_amb = HeatConnector() self.v_wind = None self.Theta = None self.DNI = None def get_required_inputs(self): self.sync_inputs() # Return a list of required inputs # DNI : Direct Natural Irradiation # Incidence angle return ['P_su', 'm_dot', 'T_su', 'T_amb', 'DNI', 'Theta', 'v_wind', 'fluid'] def get_required_parameters(self): return [ 'coll_eff', 'L', 'W', 'A', 'A_r', 'm', 'L_f', 'Tube_OD', 'Tube_V', 'alpha_r', 'refl_m', 'epsilon_r', 'envel_tau', 'eta_other', 'V_w_max_tr', 'V_w_max_st', 'Vdot_min', 'Vdot_max', 'T_f_min', 'T_f_max', 'a', 'n_disc' ] def heat_losses(self, k): "Calibrated for soponova_microcsp collector" T_amb = self.Q_amb.T_cold - 273.15 # °C T_htf = self.T[k] - 273.15 # °C Terms = [] Terms.append(self.params['a'][0]) Terms.append(self.params['a'][1]*(T_htf - T_amb)) Terms.append(self.params['a'][2]*(T_htf - T_amb)**2) Terms.append(self.DNI*np.cos(self.Theta)*(self.params['a'][3]*T_htf**2)) Terms.append(self.DNI*np.cos(self.Theta)*(self.params['a'][4]*np.sqrt(self.v_wind))) Terms.append(self.params['a'][5]*T_htf**3) Terms.append(self.v_wind*self.params['a'][6]) Terms.append(self.v_wind*self.params['a'][7]*(T_htf - T_amb)) Terms.append(np.sqrt(self.v_wind)*self.params['a'][8]) Terms.append(np.sqrt(self.v_wind)*self.params['a'][9]*(T_htf - T_amb)) Terms_np = np.array(Terms) # W/m (of line collector) return sum(Terms_np) def Q_dot_abs(self, k): # Sun absorbed power eta_opt = self.params['eta_other']*self.params['alpha_r']*self.params['refl_m']*self.params['envel_tau'] Q_dot_sun_raw = self.DNI*np.cos(self.Theta)*self.params['W']# *eta_opt # self.L_disc Q_dot_sun = Q_dot_sun_raw*eta_opt Opt_losses = Q_dot_sun_raw*(1-eta_opt) # Heat loss power HL = self.heat_losses(k) Q_dot_loss = HL #*self.L_disc Q_dot_abs = Q_dot_sun - Q_dot_loss return Q_dot_abs def solve(self): self.check_calculable() self.check_parametrized() self.AS = CP.AbstractState('HEOS', self.su.fluid) if self.calculable and self.parametrized: self.v_wind = self.inputs['v_wind'] self.Theta = self.inputs['Theta'] self.DNI = self.inputs['DNI'] self.T = np.zeros(self.params['n_disc']+1) self.p = np.zeros(self.params['n_disc']+1) self.h = np.zeros(self.params['n_disc']+1) self.Q_dot_disc = np.zeros(self.params['n_disc']) self.Q_dot_abs_disc = np.zeros(self.params['n_disc']) self.eta_coll = np.zeros(self.params['n_disc']) self.T[0] = self.su.T self.p[0] = self.su.p self.h[0] = self.su.h self.L_disc = self.params['L']/self.params['n_disc'] for i in range(len(self.Q_dot_disc)): self.Q_dot_disc[i] = self.DNI*np.cos(self.Theta)*self.L_disc*self.params['W'] self.Q_dot_abs_disc[i] = self.Q_dot_disc[i]*self.params['coll_eff'](self.DNI, (self.T[i] - self.Q_amb.T_cold)) self.eta_coll[i] = self.params['coll_eff'](self.DNI, (self.T[i] - self.Q_amb.T_cold)) self.h[i+1] = self.h[i] + self.Q_dot_abs_disc[i]/self.su.m_dot self.p[i+1] = self.p[i] self.AS.update(CP.HmassP_INPUTS, self.h[i+1], self.p[i+1]) self.T[i+1] = self.AS.T() self.Q_dot = sum(self.Q_dot_abs_disc) self.ex.set_fluid(self.su.fluid) self.ex.set_m_dot(self.su.m_dot) self.ex.set_h(self.h[-1]) self.ex.set_p(self.p[-1]) self.solved = True return else: if not self.calculable: print("Input of the component not completely known. Required inputs:") for input in self.get_required_inputs(): if input not in self.inputs: print(f" - {input}") if not self.parametrized: print("Parameters of the component not completely known. Required parameters:") for param in self.get_required_parameters(): if param not in self.params: print(f" - {param}") def print_results(self): if self.defined: print("=== Heat Exchanger Results ===") print(f" - H_ex: fluid={self.ex['H'].fluid}, T={self.ex['H'].T}, p={self.ex['H'].p}, m_dot={self.ex['H'].m_dot}") print(f" - C_ex: fluid={self.ex['C'].fluid}, T={self.ex['C'].T}, p={self.ex['C'].p}, m_dot={self.ex['C'].m_dot}") print(f" - Q_dot: temperature_in={self.Q_dot}") else: print("Heat Exchanger component is not defined. Ensure it is solved first.")
#-------------------------------------------------------------