def chartA(t, wv, A, t_range=np.arange(-10, 50, 5), w_range=np.arange(0, 0.030, 0.01)): """ Parameters ---------- t : np.array, no. equal to no. points in the psy-chart temperatures, °C wv: np.array, wv.shape = t.shape weight vapor, kg/kg_da A : np.array [no. processes, no. points = no. temperatures] adjancy matrix: -1 flow our of node, 1 flow in node, 0 no connection t_range : np.arange range of temperature the default is np.arange(-10, 50, 0.1). temperature vector t = np.arange(-10, 50, 0.1) w_range : np.arange humidity ration vector The default is np.arange(0, 0.030, 0.01). Returns ------- None. """ import matplotlib.pyplot as plt import psychro as psy fig = plt.figure() ax = fig.add_subplot(111) ax.yaxis.tick_right() plt.xlabel(r'Temperature $\theta$ [°C]') ax.yaxis.set_label_position("right") plt.ylabel(r'Humidity ratio w [kg/kg]') plt.grid(True) plt.plot(t_range, psy.w(t_range, 1), linewidth=2) # saturation curve # Plot relative humidity curves for phi in np.arange(0, 1, 0.2): w4t = psy.w(t_range, phi) plt.plot(t_range, w4t, linewidth=0.5) phi100 = phi * 100 s_phi = "%3.0f" % phi100 ax.annotate(s_phi + ' %', xy=(t_range[-1] - 3, w4t[-1])) for k in range(0, A.shape[0]): tk = np.nonzero(A[k, :]) wk = np.nonzero(A[k, :]) plt.plot(t[tk], wv[wk], linewidth=3) # processes # plot no. point for j in range(0, np.shape(tk)[1]): plt.text(t[tk][j], wv[tk][j], str(tk[0][j])) return None
def psy_chart(self, x, θo, φo): """ Plot results on psychrometric chart. Parameters ---------- x : θM, wM, θs, ws, θC, wC, θS, wS, θI, wI, QtCC, QsCC, QlCC, QsHC, QsTZ, QlTZ results of self.solve_lin or self.m_ls θo, φo outdoor point Returns ------- None. """ # Processes on psychrometric chart wo = psy.w(θo, φo) # Points: O, s, S, I θ = np.append(θo, x[0:10:2]) w = np.append(wo, x[1:10:2]) # Points O s S I Elements A = np.array([ [-1, 1, 0, 0, 0, 1], # MX1 [0, -1, 1, 0, 0, 0], # CC [0, 0, -1, 1, -1, 0], # MX2 [0, 0, 0, -1, 1, 0], # HC [0, 0, 0, 0, -1, 1] ]) # TZ psy.chartA(θ, w, A) θ = pd.Series(θ) w = 1000 * pd.Series(w) # kg/kg -> g/kg P = pd.concat([θ, w], axis=1) # points P.columns = ['θ [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 'θ [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[10:], index=['QtCC', 'QsCC', 'QlCC', 'QsHC', 'QsTZ', 'QlTZ']) # Q.columns = ['kW'] pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T / 1000, 'kW') return None
def chart(t, w, t_range=np.arange(-10, 50, 0.1), w_range=np.arange(0, 0.030, 0.0001)): """ Parameters ---------- t_range : temperature vector t = np.arange(-10, 50, 0.1) w_range : humidity ration vector w = np.arange(0, 0.030, 0.0001) Returns ------- None. Psycrometric chart """ import matplotlib.pyplot as plt import psychro as psy fig = plt.figure() ax = fig.add_subplot(111) ax.yaxis.tick_right() plt.xlabel(r'Temperature $\theta$ [°C]') ax.yaxis.set_label_position("right") plt.ylabel(r'Humidity ratio w [kg/kg]') plt.grid(True) plt.plot(t_range, psy.w(t_range, 100), linewidth=2) # saturation curve # Plot relative humidity curves for phi in np.arange(0, 100, 20): w4t = psy.w(t_range, phi) plt.plot(t_range, w4t, linewidth=0.5) s_phi = "%3.0f" % phi ax.annotate(s_phi + ' %', xy=(t_range[-1] - 3, w4t[-1])) plt.plot(t, w, linewidth=3) # processes return None
def solve_lin(self, θs0): """ Finds saturation point on saturation curve ws = f(θs). Solves iterativelly *lin_model(θs0)*: θs -> θs0 until ws = psy(θs, 1). Parameters ---------- θs0 initial guess saturation temperature Method from object --------------------- *self.lin_model(θs0)* Returns (16 unknowns) --------------------- x of *self.lin_model(self, θs0)* """ Δ_ws = 10e-3 # kg/kg, initial difference to start the iterations while Δ_ws > 0.01e-3: x = self.lin_model(θs0) Δ_ws = abs(psy.w(x[2], 1) - x[3]) # psy.w(θs, 1) = ws θs0 = x[2] # actualize θs0 return x
def RecAirCAV(alpha=1, beta=0.1, tS=30, tIsp=18, phiIsp=0.49, tO=-1, phiO=1, Qsa=0, Qla=0, mi=2.18, UA=935.83): """ Model: Heating and adiabatic humidification Recycled air CAV Constant Air Volume: mass flow rate calculated for design conditions maintained constant in all situations INPUTS: alpha mixing ratio of outdoor air, - beta by-pass factor of the adiabatic humidifier, - tS supply air, °C tIsp indoor air setpoint, °C phiIsp indoor relative humidity set point, - tO outdoor temperature for design, °C phiO outdoor relative humidity for design, - Qsa aux. sensible heat, W Qla aux. latente heat, W mi infiltration massflow rate, kg/s UA global conductivity bldg, W/K System: MX1: Mixing box HC1: Heating Coil AH: Adiabatic Humidifier MX2: Mixing in humidifier model HC2: Reheating coil TZ: Thermal Zone BL: Building Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 0..5 6 unknown points (temperature, humidity ratio) <----|<-------------------------------------------| | | | |-------| | -o->MX1--0->HC1--1->| MX2--3->HC2--4->TZ--5-| | | | | || | | |->AH-2-| | BL | | | | | |<-----Kt----|<-t5 |<------------------------------Kw----|<-w5 16 Unknowns 0..5: 2*6 points (temperature, humidity ratio) QsHC1, QsHC2, QsTZ, QlTZ Returns ------- None """ plt.close('all') wO = psy.w(tO, phiO) # hum. out # Mass flow rate for design conditions # Supplay air mass flow rate # QsZ = UA*(tO - tIsp) + mi*c*(tO - tIsp) # m = - QsZ/(c*(tS - tIsp) # where # tO, wO = -1, 3.5e-3 # outdoor # tS = 30 # supply air # mid = 2.18 # infiltration QsZ = UA*(tOd - tIsp) + mid*c*(tOd - tIsp) m = - QsZ/(c*(tS - tIsp)) print(f'm = {m: 5.3f} kg/s constant for design conditions:') print(f' [tSd = {tS: 3.1f} °C, mi = 2.18 kg/S, tO = -1°C, phi0 = 100%]') # Model x = ModelRecAir(m, alpha, beta, tS, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) t = np.append(tO, x[0:12:2]) w = np.append(wO, x[1:12:2]) # Adjancy matrix # Points o 0 1 2 3 4 5 Elements A = np.array([[-1, 1, 0, 0, 0, 0, -1], # MX1 [0, -1, 1, 0, 0, 0, 0], # HC1 [0, 0, -1, 1, 0, 0, 0], # AH [0, 0, -1, -1, 1, 0, 0], # MX2 [0, 0, 0, 0, -1, 1, 0], # HC2 [0, 0, 0, 0, 0, -1, 1]]) # TZ psy.chartA(t, w, A) t = pd.Series(t) w = 1000*pd.Series(w) P = pd.concat([t, w], axis=1) # points P.columns = ['t [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 't [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[12:], index=['QsHC1', 'QsHC2', 'QsTZ', 'QlTZ']) # Q.columns = ['kW'] pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T/1000, 'kW') return None
def β_ls(self, value, sp): """ Bypass β controls supply temperature θS or indoor humidity wI. Finds β which solves value = sp, i.e. minimizes ε = value - sp. Uses *scipy.optimize.least_squares* to solve the non-linear system. Parameters ---------- value string: 'θS' od 'wI' type of controlled variable sp float: value of setpoint Calls ----- *ε(m)* gives (value - sp) to be minimized for m Returns (16 unknowns) --------------------- x given by *self.lin_model(self, θs0)* """ from scipy.optimize import least_squares def ε(β): """ Gives difference ε = (values - sp) function of β ε calculated by self.solve_lin(ts0) β bounds=(0, 1) Parameters ---------- β : by-pass factor of the cooling coil From object Method: self.solve.lin(θs0) Variables: self.actual <- m (used in self.solve.lin) Returns ------- ε = value - sp: difference between value and its set point """ self.actual[2] = β x = self.solve_lin(θs_0) if value == 'θS': θS = x[6] # supply air return abs(sp - θS) elif value == 'φI': wI = x[9] # indoor air return abs(sp - wI) else: print('ERROR in ε(β): value not in {"θS", "wI"}') β0 = self.actual[2] # initial guess β0 = 0.1 if value == 'φI': self.actual[4] = 0 sp = psy.w(self.actual[7], sp) # gives m for min(θSsp - θS); θs_0 is the initial guess of θs res = least_squares(ε, β0, bounds=(0, 1)) if res.cost < 1e-5: β = float(res.x) # print(f'm = {m: 5.3f} kg/s') else: print('RecAirVBP: No solution for β') self.actual[2] = β x = self.solve_lin(θs_0) return x
def m_ls(self, value, sp): """ Mass flow rate m controls supply temperature θS or indoor humidity wI. Finds m which solves value = sp, i.e. minimizes ε = value - sp. Uses *scipy.optimize.least_squares* to solve the non-linear system. Parameters ---------- value string: 'θS' od 'wI' type of controlled variable sp float: value of setpoint Calls ----- *ε(m)* gives (value - sp) to be minimized for m Returns (16 unknowns) --------------------- x given by *self.lin_model(self, θs0)* """ from scipy.optimize import least_squares def ε(m): """ Gives difference ε = (values - sp) function of m ε calculated by self.solve_lin(ts0) m bounds=(0, m_max); m_max hard coded (global variable) Parameters ---------- m : mass flow rate of dry air From object Method: self.solve.lin(θs0) Variables: self.actual <- m (used in self.solve.lin) Returns ------- ε = value - sp: difference between value and its set point """ self.actual[0] = m x = self.solve_lin(θs_0) if value == 'θS': θS = x[6] # supply air return abs(sp - θS) elif value == 'φI': wI = x[9] # indoor air return abs(sp - wI) else: print('ERROR in ε(m): value not in {"θS", "wI"}') m0 = self.actual[0] # initial guess if value == 'φI': self.actual[4] = 0 sp = psy.w(self.actual[7], sp) # gives m for min(θSsp - θS); θs_0 is the initial guess of θs res = least_squares(ε, m0, bounds=(0, m_max)) if res.cost < 0.1e-3: m = float(res.x) # print(f'm = {m: 5.3f} kg/s') else: print('RecAirVAV: No solution for m') self.actual[0] = m x = self.solve_lin(θs_0) return x
def AllOutAirVAV(tSsp=30, tIsp=18, phiIsp=0.5, tO=-1, phiO=1, Qsa=0, Qla=0, mi=2.12, UA=935.83): """ All out air Heating & Vapor humidification VAV Variable Air Volume: mass flow rate calculated to have const. supply temp. INPUTS: tS supply air °C tIsp indoor air setpoint °C phiIsp - tO outdoor temperature for design °C phiO outdoor relative humidity for design - Qsa aux. sensible heat W Qla aux. latente heat W mi infiltration massflow rate kg/s UA global conductivity bldg W/K System: HC: Heating Coil VH: Vapor Humidifier TZ: Thermal Zone BL: Building Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 10 Unknowns 0, 1, 2 points (temperature, humidity ratio) QsHC, QlVH, QsTZ, QlTZ --o->HC--0->VH--F-----1-->TZ--2--> / / | | || | | | | | BL | | | |_Kt1_| | | | | | |<----Kw---------|-w2 |<------------Kt---------|-t2 Mass-flow rate (VAV) I-controller: start with m = 0 measure the supply temperature while -(tSsp - tS)>0.01, increase m (I-controller) """ plt.close('all') wO = psy.w(tO, phiO) # outdoor mumidity ratio # Mass flow rate DtS, m = 2, 0 # initial temp; diff; flow rate while DtS > 0.01: m = m + 0.01 # mass-flow rate I-controller # Model x = ModelAllOutAir(m, tSsp, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) tS = x[2] DtS = -(tSsp - tS) print('Winter All_out_air VAV') print(f'm = {m: 5.3f} kg/s') # Processes on psychrometric chart t = np.append(tO, x[0:5:2]) w = np.append(wO, x[1:6:2]) # Points o 0 1 2 Elements A = np.array([ [-1, 1, 0, 0], # HC [0, -1, 1, 0], # VH [0, 0, 1, -1] ]) # TZ psy.chartA(t, w, A) t = pd.Series(t) w = 1000 * pd.Series(w) P = pd.concat([t, w], axis=1) # points P.columns = ['t [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 't [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[6:], index=['QsHC', 'QlVH', 'QsTZ', 'QlTZ']) # Q.columns = ['kW'] pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T / 1000, 'kW') return None
def lin_model(self, θs0): """ Linearized model. Solves a set of 16 linear equations. Saturation curve is linearized in θs0. s-point (θs, ws): - is on a tangent to φ = 100 % in θs0; - is **not** on the saturation curve (Apparatus Dew Point ADP). Parameter from function call ---------------------------- θs0 °C, temperature for which the saturation curve is liniarized Parameters from object --------------------- m, mo, θo, φo, θIsp, φIsp, β, mi, UA, Qsa, Qla = self.actual Equations (16) ------------- +-------------+-----+----+-----+----+----+----+----+----+ | Element | MX1 | CC | MX2 | HC | TZ | BL | Kθ | Kw | +=============+=====+====+=====+====+====+====+====+====+ | N° equations| 2 | 4 | 2 | 2 | 2 | 2 | 1 | 1 | +-------------+-----+----+-----+----+----+----+----+----+ Returns (16 unknowns) --------------------- x : θM, wM, θs, ws, θC, wC, θS, wS, θI, wI, QtCC, QsCC, QlCC, QsHC, QsTZ, QlTZ """ """ <=4================================m========================== || || 4 (m-mo) =======0======= || || || (1-β)m || || θo,φo=>[MX1]==0==|| [MX2]==2==[HC]==F==3==>[TZ]==4==|| mo || || / / // | ===0=[CC]==1=== s m sl | /\\ βm | || | t sl | [BL]<-mi | | | // | | | sl | | | | | |<------[K]-----------+<-wI |<------------------------[K]-----------+<-θI """ m, mo, β, Kθ, Kw, θo, φo, θIsp, φIsp, mi, UA, Qsa, Qla = self.actual wo = psy.w(θo, φo) # hum. out A = np.zeros((16, 16)) # coefficents of unknowns b = np.zeros(16) # vector of inputs # MX1 A[0, 0], A[0, 8], b[0] = m * c, -(m - mo) * c, mo * c * θo A[1, 1], A[1, 9], b[1] = m * l, -(m - mo) * l, mo * l * wo # CC A[2, 0], A[2, 2], A[2, 11], b[2] = (1 - β) * m * c, -(1 - β) * m * c,\ 1, 0 A[3, 1], A[3, 3], A[3, 12], b[3] = (1 - β) * m * l, -(1 - β) * m * l,\ 1, 0 A[4, 2], A[4, 3], b[4] = psy.wsp(θs0), -1,\ psy.wsp(θs0) * θs0 - psy.w(θs0, 1) A[5, 10], A[5, 11], A[5, 12], b[5] = -1, 1, 1, 0 # MX2 A[6, 0], A[6, 2], A[6, 4], b[6] = β * m * c, (1 - β) * m * c,\ - m * c, 0 A[7, 1], A[7, 3], A[7, 5], b[7] = β * m * l, (1 - β) * m * l,\ - m * l, 0 # HC A[8, 4], A[8, 6], A[8, 13], b[8] = m * c, -m * c, 1, 0 A[9, 5], A[9, 7], b[9] = m * l, -m * l, 0 # TZ A[10, 6], A[10, 8], A[10, 14], b[10] = m * c, -m * c, 1, 0 A[11, 7], A[11, 9], A[11, 15], b[11] = m * l, -m * l, 1, 0 # BL A[12, 8], A[12, 14], b[12] = (UA + mi * c), 1, (UA + mi * c) * θo + Qsa A[13, 9], A[13, 15], b[13] = mi * l, 1, mi * l * wo + Qla # Kθ indoor temperature controller A[14, 8], A[14, 10], b[14] = Kθ, 1, Kθ * θIsp # Kw indoor humidity ratio controller A[15, 9], A[15, 13], b[15] = Kw, 1, Kw * psy.w(θIsp, φIsp) x = np.linalg.solve(A, b) return x
m=self.actual[0], mo=self.actual[1], β=self.actual[2])) self.psy_chart(x, θo, φo) return x # TESTS: uncomment # Recyclage p. 6/19 Kθ, Kw = 1e10, 0 # Kw can be 0 β = 0 m, mo = 3.093, 0.94179 θo, φo = 32, 0.5 θIsp, φIsp = 26, 0.5 mi = 15e3 / (l * (psy.w(θo, φo) - psy.w(θIsp, φIsp))) # kg/s UA = 45e3 / (θo - θIsp) - mi * c # W/K Qsa, Qla = 0, 0 # W print(f'QsTZ = {(UA + mi * c) * (θo - θIsp): ,.1f} W') print(f'QlTZ = {mi * l * (psy.w(θo, φo) - psy.w(θIsp, φIsp)): ,.1f} W') parameters = m, mo, β, Kθ, Kw inputs = θo, φo, θIsp, φIsp, mi, UA, Qsa, Qla cool = MxCcRhTzBl(parameters, inputs) # # 1. CAV # print('\nCAV: m given') # cool.CAV_wd() # cool.CAV_wd(θo, φo, θIsp, φIsp, mi, UA, Qsa, Qla) # # 2.
length = 20 # m width = 30 # m height = 3.5 # m persons = 100 # m sens_heat_person = 60 # W / person latent_heat_person = 40 # W / person load_m2 = 20 # W/m2 solar_m2 = 150 # W/m2 of window area ACH = 1 # Air Cnhnages per Hour U_wall = 0.4 # W/K, overall heat transfer coeff. walls U_window = 3.5 # W/K, overall heat transfer coeff. windows θo, φo = 32, 0.5 # outdoor temperature & relative humidity θI, φI = 26, 0.5 # indoor temperature & relative humidity wo = psy.w(θo, φo) wI = psy.w(θI, φI) floor_area = length * width surface_envelope = 2 * (length + width) * height + floor_area surface_wall = 0.9 * surface_envelope surface_window = surface_envelope - surface_wall # building conductance, W/K UA = U_wall * surface_wall + U_window * surface_window # infiltration mass flow rate, kg/s mi = ACH * surface_envelope * height / 3600 * ρ # gains, W solar_gains = solar_m2 * surface_window
def RecAirVAV(alpha=0.5, tSsp=30, tIsp=18, phiIsp=0.5, tO=-1, phiO=1, Qsa=0, Qla=0, mi=2.12, UA=935.83): """ CAV Variable Air Volume: mass flow rate calculated s.t. he supply temp. is maintained constant in all situations INPUTS: INPUTS: m mass flow of supply dry air kg/s alpha mixing ratio of outdoor air tS supply air °C tIsp indoor air setpoint °C phiIsp - tO outdoor temperature for design °C phiO outdoor relative humidity for design - Qsa aux. sensible heat W Qla aux. latente heat W mi infiltration massflow rate kg/s UA global conductivity bldg W/K System (CAV & m introduced by the Fan is cotrolled by tS ) HC: Heating Coil VH: Vapor Humidifier TZ: Thermal Zone F: Supply air fan Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 12 Unknowns 0, 1, 2, 3 points (temperature, humidity ratio) QsHC, QlVH, QsTZ, QlTZ <----|<--------------------------------| | | -o->MX--0->HC--1->VH--F-----2-->TZ--3--> / / | | || | | | | | BL | | | | | | | | |_Kt2_|_t2 | | | | | |_____Kw_________|_w3 |_____________Kt_________|_t3 Mass-flow rate (VAV) I-controller: start with m = 0 measure the supply temperature while -(tSsp - tS)>0.01, increase m (I-controller) """ plt.close('all') wO = psy.w(tO, phiO) # hum. out # Mass flow rate DtS, m = 2, 0 # initial temp; diff; flow rate while DtS > 0.01: m = m + 0.01 # Model x = ModelRecAir(m, alpha, tSsp, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) tS = x[4] DtS = -(tSsp - tS) print('Winter Rec_air VAV') print(f'm = {m: 5.3f} kg/s') # Processes on psychrometric chart # Points o 0 1 2 3 Elements A = np.array([ [-1, 1, 0, 0, -1], # MX [0, -1, 1, 0, 0], # HC [0, 0, -1, 1, 0], # VH [0, 0, 0, -1, 1] ]) # TZ t = np.append(tO, x[0:8:2]) print(f'wO = {wO:6.5f}') w = np.append(wO, x[1:8:2]) psy.chartA(t, w, A) t = pd.Series(t) w = 1000 * pd.Series(w) P = pd.concat([t, w], axis=1) # points P.columns = ['t [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 't [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[8:], index=['QsHC', 'QlVH', 'QsTZ', 'QlTZ']) pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T / 1000, 'kW') return None
def ModelAllOutAir(m, tS, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA): """ Model: All outdoor air CAV Constant Air Volume: mass flow rate given control of indoor condition (t2, w2) INPUTS: m mass flow of supply dry air kg/s tS supply air °C tIsp indoor air setpoint °C phiIsp - tO outdoor temperature for design °C phiO outdoor relative humidity for design - Qsa aux. sensible heat W Qla aux. latente heat W mi infiltration massflow rate kg/s UA global conductivity bldg W/K OUTPUTS: x vector 10 elements: t0, w0, t1, w1, t2, w2, QsHC, QlVH, QsTZ, QlTZ System: HC: Heating Coil VH: Vapor Humidifier TZ: Thermal Zone BL: Building Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 10 Unknowns 0, 1, 2 points (temperature, humidity ratio) QsHC, QlVH, QsTZ, QlTZ --o->HC--0->VH--1->TZ--2--> | | || | | | BL | | | | | |<----Kw--|-w2 |<------------Kt--|-t2 """ Kt, Kw = 1e10, 1e10 # controller gain wO = psy.w(tO, phiO) # outdoor mumidity ratio wIsp = psy.w(tIsp, phiIsp) # indoor mumidity ratio # Model A = np.zeros((10, 10)) # coefficents of unknowns b = np.zeros(10) # vector of inputs # HC heating coil A[0, 0], A[0, 6], b[0] = m * c, -1, m * c * tO A[1, 1], b[1] = m * l, m * l * wO # VA vapor humidifier A[2, 0], A[2, 2], b[2] = -m * c, m * c, 0 A[3, 1], A[3, 3], A[3, 7], b[3] = -m * l, m * l, -1, 0 # TZ thermal zone A[4, 2], A[4, 4], A[4, 8], b[4] = -m * c, m * c, -1, 0 A[5, 3], A[5, 5], A[5, 9], b[5] = -m * l, m * l, -1, 0 # BL building A[6, 4], A[6, 8], b[6] = UA + mi * c, 1, (UA + mi * c) * tO + Qsa A[7, 5], A[7, 9], b[7] = mi * l, 1, mi * l * wO + Qla # Kt indoor temperature controller A[8, 4], A[8, 8], b[8] = Kt, 1, Kt * tIsp # Kw indoor hum.ratio controller A[9, 5], A[9, 9], b[9] = Kw, 1, Kw * wIsp # Solution x = np.linalg.solve(A, b) return x
def RecAirCAV(alpha=0.5, tS=30, tIsp=18, phiIsp=0.5, tO=-1, phiO=1, Qsa=0, Qla=0, mi=2.12, UA=935.83): """ CAV Constant Air Volume: mass flow rate calculated for design conditions maintained constant in all situations INPUTS: alpha mixing ratio of outdoor air tS supply air °C tIsp indoor air setpoint °C phiIsp - tO outdoor temperature for design °C phiO outdoor relative humidity for design - Qsa aux. sensible heat W Qla aux. latente heat W mi infiltration massflow rate kg/s UA global conductivity bldg W/K System: HC: Heating Coil VH: Vapor Humidifier TZ: Thermal Zone Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 12 Unknowns 0, 1, 2, 3 points (temperature, humidity ratio) QsHC, QlVH, QsTZ, QlTZ <-3--|<-------------------------| | | -o->MX--0->HC--1->VH--2->TZ--3--> / / || | | | BL | | | | | |_____Kw__|_w3 |_____________Kt__|_t3 """ plt.close('all') wO = psy.w(tO, phiO) # hum. out # Mass flow rate for design conditions # Supplay air mass flow rate # QsZ = UA*(tO - tIsp) + mi*c*(tO - tIsp) # m = - QsZ/(c*(tS - tIsp) # where # tOd, wOd = -1, 3.5e-3 # outdoor # tS = 30 # supply air # mid = 2.18 # infiltration QsZ = UA * (tOd - tIsp) + mid * c * (-1 - tIsp) m = -QsZ / (c * (tS - tIsp)) print('Winter Recirculated_air CAV') print(f'm = {m: 5.3f} kg/s constant (from design conditions)') print(f'Design conditions tS = {tS: 3.1f} °C,' f'mi = {mid:3.1f} kg/s, tO = {tOd:3.1f} °C, ' f'tI = {tIsp:3.1f} °C') # Model x = ModelRecAir(m, alpha, tS, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) # (m, tS, mi, tO, phiO, alpha) # Processes on psychrometric chart # Points o 0 1 2 3 Elements A = np.array([ [-1, 1, 0, 0, -1], # MX [0, -1, 1, 0, 0], # HC [0, 0, -1, 1, 0], # VH [0, 0, 0, -1, 1] ]) # TZ t = np.append(tO, x[0:8:2]) print(f'wO = {wO:6.5f}') w = np.append(wO, x[1:8:2]) psy.chartA(t, w, A) t = pd.Series(t) w = 1000 * pd.Series(w) P = pd.concat([t, w], axis=1) # points P.columns = ['t [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 't [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[8:], index=['QsHC', 'QlVH', 'QsTZ', 'QlTZ']) pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T / 1000, 'kW') return None
def ModelRecAir(m, alpha, tS, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA): """ Model: Heating and vapor humidification Recycled air CAV Constant Air Volume: mass flow rate calculated for design conditions maintained constant in all situations INPUTS: m mass flow of supply dry air kg/s alpha mixing ratio of outdoor air tS supply air °C tIsp indoor air setpoint °C phiIsp - tO outdoor temperature for design °C phiO outdoor relative humidity for design - Qsa aux. sensible heat W Qla aux. latente heat W mi infiltration massflow rate kg/s UA global conductivity bldg W/K OUTPUTS: x vector 12 elements: t0, w0, t1, w1, t2, w2, t3, w3, QsHC, QlVH, QsTZ, QlTZ System: MX: Mixing Box HC: Heating Coil VH: Vapor Humidifier TZ: Thermal Zone BL: Buildings Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 12 Unknowns 0, 1, 2, 3 points (temperature, humidity ratio) QsHC, QlVH, QsTZ, QlTZ <-3--|<-------------------------| | | -o->MX--0->HC--1->VH--2->TZ--3--> / / || | | | BL | | | | | |<----Kw--|-w3 |<------------Kt--|-t3 """ Kt, Kw = 1e10, 1e10 # controller gain wO = psy.w(tO, phiO) # hum. out wIsp = psy.w(tIsp, phiIsp) # hum. in set point # Model A = np.zeros((12, 12)) # coefficents of unknowns b = np.zeros(12) # vector of inputs # MX mixing box A[0, 0], A[0, 6], b[0] = m * c, -(1 - alpha) * m * c, alpha * m * c * tO A[1, 1], A[1, 7], b[1] = m * l, -(1 - alpha) * m * l, alpha * m * l * wO # HC hearing coil A[2, 0], A[2, 2], A[2, 8], b[2] = m * c, -m * c, 1, 0 A[3, 1], A[3, 3], b[3] = m * l, -m * l, 0 # VH vapor humidifier A[4, 2], A[4, 4], b[4] = m * c, -m * c, 0 A[5, 3], A[5, 5], A[5, 9], b[5] = m * l, -m * l, 1, 0 # TZ thermal zone A[6, 4], A[6, 6], A[6, 10], b[6] = m * c, -m * c, 1, 0 A[7, 5], A[7, 7], A[7, 11], b[7] = m * l, -m * l, 1, 0 # BL building A[8, 6], A[8, 10], b[8] = (UA + mi * c), 1, (UA + mi * c) * tO + Qsa A[9, 7], A[9, 11], b[9] = mi * l, 1, mi * l * wO + Qla # Kt indoor temperature controller A[10, 6], A[10, 8], b[10] = Kt, 1, Kt * tIsp # Kw indoor humidity controller A[11, 7], A[11, 9], b[11] = Kw, 1, Kw * wIsp # Solution x = np.linalg.solve(A, b) return x
def RecAirVAV(alpha=1, beta=0.1, tSsp=30, tIsp=18, phiIsp=0.49, tO=-1, phiO=1, Qsa=0, Qla=0, mi=2.18, UA=935.83): """ Created on Fri Apr 10 13:57:22 2020 Heating & Adiabatic humidification & Re-heating Recirculated air VAV Variable Air Volume: mass flow rate calculated to have const. supply temp. INPUTS: alpha mixing ratio of outdoor air, - beta by-pass factor of the adiabatic humidifier, - tS supply air, °C tIsp indoor air setpoint, °C phiIsp indoor relative humidity set point, - tO outdoor temperature for design, °C phiO outdoor relative humidity for design, - Qsa aux. sensible heat, W Qla aux. latente heat, W mi infiltration massflow rate, kg/s UA global conductivity bldg, W/K System: MX1: Mixing box HC1: Heating Coil AH: Adiabatic Humidifier MX2: Mixing in humidifier model HC2: Reheating coil TZ: Thermal Zone BL: Building Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 0..5 unknown points (temperature, humidity ratio) <----|<-------------------------------------------------| | | | |-------| | -o->MX1--0->HC1--1->| MX2--3->HC2--F-----4->TZ--5-| | |->AH-2-| | | | || | | | |-Kt4-| BL | | | | | |<-----Kt----------|<-t5 |<------------------------------Kw----------|<-w5 16 Unknowns 0..5: 2*6 points (temperature, humidity ratio) QsHC1, QsHC2, QsTZ, QlTZ """ from scipy.optimize import least_squares def Saturation(m): """ Used in VAV to find the mass flow which solves tS = tSsp Parameters ---------- m : mass flow rate of dry air Returns ------- tS - tSsp: difference between supply temp. and its set point """ x = ModelRecAir(m, alpha, beta, tSsp, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) tS = x[8] return (tS - tSsp) plt.close('all') wO = psy.w(tO, phiO) # hum. out # Mass flow rate res = least_squares(Saturation, 5, bounds=(0, 10)) if res.cost < 1e-10: m = float(res.x) else: print('RecAirVAV: No solution for m') print(f'm = {m: 5.3f} kg/s') x = ModelRecAir(m, alpha, beta, tSsp, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) # DtS, m = 2, 1 # initial temp; diff; flow rate # while DtS > 0.01: # m = m + 0.01 # # Model # x = ModelRecAir(m, tSsp, mi, tO, phiO, alpha, beta) # tS = x[8] # DtS = -(tSsp - tS) t = np.append(tO, x[0:12:2]) w = np.append(wO, x[1:12:2]) # Adjancy matrix # Points o 0 1 2 3 4 5 Elements A = np.array([[-1, 1, 0, 0, 0, 0, -1], # MX1 [0, -1, 1, 0, 0, 0, 0], # HC1 [0, 0, -1, 1, 0, 0, 0], # AH [0, 0, -1, -1, 1, 0, 0], # MX2 [0, 0, 0, 0, -1, 1, 0], # HC2 [0, 0, 0, 0, 0, -1, 1]]) # TZ psy.chartA(t, w, A) t = pd.Series(t) w = 1000*pd.Series(w) P = pd.concat([t, w], axis=1) # points P.columns = ['t [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 't [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[12:], index=['QsHC1', 'QsHC2', 'QsTZ', 'QlTZ']) # Q.columns = ['kW'] pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T/1000, 'kW') return None
def ModelRecAir(m, alpha, beta, tS, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA): """ Model: Heating and adiabatic humidification Recycled air CAV Constant Air Volume: mass flow rate calculated for design conditions maintained constant in all situations INPUTS: m mass flow of supply dry air, kg/s alpha mixing ratio of outdoor air, - beta by-pass factor of the adiabatic humidifier, - tS supply air, °C tIsp indoor air setpoint, °C phiIsp indoor relative humidity set point, - tO outdoor temperature for design, °C phiO outdoor relative humidity for design, - Qsa aux. sensible heat, W Qla aux. latente heat, W mi infiltration massflow rate, kg/s UA global conductivity bldg, W/K System: MX1: Mixing box HC1: Heating Coil AH: Adiabatic Humidifier MX2: Mixing in humidifier model HC2: Reheating coil TZ: Thermal Zone BL: Building Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 0..5 unknown points (temperature, humidity ratio) <----|<-------------------------------------------| | | | |-------| | -o->MX1--0->HC1--1->| MX2--3->HC2--4->TZ--5-| / | | / || | | |->AH-2-| | BL | | | | | |<-----Kt----|<-t5 |<------------------------------Kw----|<-w5 Returns ------- x vector 16 elem.: t0, w0, t1, w1, t2, w2, t3, w3, t4, w4, t5, w5,... QHC1, QHC2, QsTZ, QlTZ """ Kt, Kw = 1e10, 1e10 # controller gain wO = psy.w(tO, phiO) # hum. out wIsp = psy.w(tIsp, phiIsp) # indoor mumidity ratio # Model ts0, Del_ts = tS, 2 # initial guess saturation temp. A = np.zeros((16, 16)) # coefficents of unknowns b = np.zeros(16) # vector of inputs while Del_ts > 0.01: # MX1 A[0, 0], A[0, 10], b[0] = m*c, -(1 - alpha)*m*c, alpha*m*c*tO A[1, 1], A[1, 11], b[1] = m*l, -(1 - alpha)*m*l, alpha*m*l*wO # HC1 A[2, 0], A[2, 2], A[2, 12], b[2] = m*c, -m*c, 1, 0 A[3, 1], A[3, 3], b[3] = m*l, -m*l, 0 # AH A[4, 2], A[4, 3], A[4, 4], A[4, 5], b[4] = c, l, -c, -l, 0 A[5, 4], A[5, 5] = psy.wsp(ts0), -1 b[5] = psy.wsp(ts0)*ts0 - psy.w(ts0, 1) # MX2 A[6, 2], A[6, 4], A[6, 6], b[6] = beta*m*c, (1-beta)*m*c, -m*c, 0 A[7, 3], A[7, 5], A[7, 7], b[7] = beta*m*l, (1-beta)*m*l, -m*l, 0 # HC2 A[8, 6], A[8, 8], A[8, 13], b[8] = m*c, -m*c, 1, 0 A[9, 7], A[9, 9], b[9] = m*l, -m*l, 0 # TZ A[10, 8], A[10, 10], A[10, 14], b[10] = m*c, -m*c, 1, 0 A[11, 9], A[11, 11], A[11, 15], b[11] = m*l, -m*l, 1, 0 # BL A[12, 10], A[12, 14], b[12] = (UA+mi*c), 1, (UA+mi*c)*tO + Qsa A[13, 11], A[13, 15], b[13] = mi*l, 1, mi*l*wO + Qla # Kt & Kw A[14, 10], A[14, 12], b[14] = Kt, 1, Kt*tIsp A[15, 11], A[15, 13], b[15] = Kw, 1, Kw*wIsp x = np.linalg.solve(A, b) Del_ts = abs(ts0 - x[4]) ts0 = x[4] return x
def AllOutAirCAV(tS=30, tIsp=18, phiIsp=0.5, tO=-1, phiO=1, Qsa=0, Qla=0, mi=2.12, UA=935.83): """ All out air CAV Constant Air Volume: mass flow rate calculated for design conditions maintained constant in all situations INPUTS: m mass flow of supply dry air kg/s tS supply air °C tIsp indoor air setpoint °C phiIsp - tO outdoor temperature for design °C phiO outdoor relative humidity for design - Qsa aux. sensible heat W Qla aux. latente heat W mi infiltration massflow rate kg/s UA global conductivity bldg W/K System: HC: Heating Coil VH: Vapor Humidifier TZ: Thermal Zone BL: Building Kw: Controller - humidity Kt: Controller - temperature o: outdoor conditions 10 Unknowns 0, 1, 2 points (temperature, humidity ratio) QsHC, QlVH, QsTZ, QlTZ --o->HC--0->VH--1->TZ--2--> / / || | | | BL | | | | | |<----Kw--|-w2 |<------------Kt--|-t2 """ plt.close('all') wO = psy.w(tO, phiO) # hum. out # Mass flow rate for design conditions # tOd = -1 # outdoor design conditions # mid = 2.18 # infiltration design QsZ = UA * (tOd - tIsp) + mid * c * (tOd - tIsp) m = -QsZ / (c * (tS - tIsp)) print('Winter All_out_air CAV') print(f'm = {m: 5.3f} kg/s constant (from design conditions)') print(f'Design conditions tS = {tS: 3.1f} °C,' f'mi = {mid:3.1f} kg/s, tO = {tOd:3.1f} °C, ' f'tI = {tIsp:3.1f} °C') # Model x = ModelAllOutAir(m, tS, tIsp, phiIsp, tO, phiO, Qsa, Qla, mi, UA) # Processes on psychrometric chart t = np.append(tO, x[0:5:2]) w = np.append(wO, x[1:6:2]) # Adjancy matrix: rows=lines; columns=points # Points O 0 1 2 Elements A = np.array([ [-1, 1, 0, 0], # HC [0, -1, 1, 0], # VH [0, 0, 1, -1] ]) # TZ psy.chartA(t, w, A) t = pd.Series(t) w = 1000 * pd.Series(w) P = pd.concat([t, w], axis=1) # points P.columns = ['t [°C]', 'w [g/kg]'] output = P.to_string(formatters={ 't [°C]': '{:,.2f}'.format, 'w [g/kg]': '{:,.2f}'.format }) print() print(output) Q = pd.Series(x[6:], index=['QsHC', 'QlVH', 'QsTZ', 'QlTZ']) # Q.columns = ['kW'] pd.options.display.float_format = '{:,.2f}'.format print() print(Q.to_frame().T / 1000, 'kW') return x