def maxSubcritORCBoilTemp(orc_fluid): P_crit = FluidState.getPcrit(orc_fluid) # assume min pressure is condensing at 0C P_min = FluidState.getStateFromTQ(0, 1, orc_fluid).P_Pa dP = (P_crit - P_min) / 1000 # find pressure with maximum entropy P = np.arange(P_min, P_crit, dP) s = FluidState.getStateFromPQ(P, 1, orc_fluid).s_JK max_s = np.max(s) row_index = np.argmax(s, axis=0) max_P = P[row_index] return FluidState.getStateFromPS(max_P, max_s, orc_fluid).T_C
def heatExchanger(T_1_in, P_1, m_dot_1, fluid_1, T_2_in, P_2, m_dot_2, fluid_2, dT_pinch): results = HeatExchangerResults() # Only tested where Temp1 < Temp2 if T_1_in > T_2_in: # throw(MException('HeatExchanger:BadTemps','Temp 1 is greater than Temp 2!')); # No heat exchanged results.T_1_out = T_1_in results.T_2_out = T_2_in return results if m_dot_1 <= 0 or m_dot_2 <= 0: raise Exception( 'GenGeo::HeatExchanger:NegativeMassFlow - Negative Massflow in Heat Exchanger' ) increments = 20 direction = np.sign(T_1_in - T_2_in) # Check the phase on incoming fluid P_crit_1 = FluidState.getPcrit(fluid_1) if P_1 < P_crit_1: T_sat_1 = FluidState.getStateFromPQ(P_1, 1, fluid_1).T_C if T_sat_1 == T_1_in or T_sat_1 == T_2_in: raise Exception( 'GenGeo::HeatExchanger:TwoPhaseFluid - Fluid 1 enters or leaves two-phase!' ) P_crit_2 = FluidState.getPcrit(fluid_2) if P_2 < P_crit_2: T_sat_2 = FluidState.getStateFromPQ(P_2, 1, fluid_2).T_C if T_sat_2 == T_1_in or T_sat_2 == T_2_in: raise Exception( 'GenGeo::HeatExchanger:TwoPhaseFluid - Fluid 2 enters or leaves two-phase!' ) h_1_in = FluidState.getStateFromPT(P_1, T_1_in, fluid_1).h_Jkg T_1_max = T_2_in h_1_max = FluidState.getStateFromPT(P_1, T_1_max, fluid_1).h_Jkg T_1_max_practical = T_2_in + direction * dT_pinch h_1_max_practical = FluidState.getStateFromPT(P_1, T_1_max_practical, fluid_1).h_Jkg h_2_in = FluidState.getStateFromPT(P_2, T_2_in, fluid_2).h_Jkg T_2_max = T_1_in h_2_max = FluidState.getStateFromPT(P_2, T_2_max, fluid_2).h_Jkg T_2_max_practical = T_1_in - direction * dT_pinch h_2_max_practical = FluidState.getStateFromPT(P_2, T_2_max_practical, fluid_2).h_Jkg Q_1_max = abs(m_dot_1 * (h_1_in - h_1_max)) Q_2_max = abs(m_dot_2 * (h_2_in - h_2_max)) Q_1_max_practical = abs(m_dot_1 * (h_1_in - h_1_max_practical)) Q_2_max_practical = abs(m_dot_2 * (h_2_in - h_2_max_practical)) if abs(Q_1_max) < abs(Q_2_max): # limitingFluid = 1; Q_max = Q_1_max Q_max_practical = Q_1_max_practical else: # limtingFluid = 2; Q_max = Q_2_max Q_max_practical = Q_2_max_practical results.Q_exchanged = Q_max_practical ddT_pinch = 1 length = increments + 1 results.Q = np.zeros(length) h_1 = np.zeros(length) results.T_1 = np.zeros(length) h_2 = np.zeros(length) results.T_2 = np.zeros(length) dT = np.zeros(length) UA = np.zeros(length) while ddT_pinch > 0.1: dQ = results.Q_exchanged / increments results.Q[0] = 0. h_1[0] = h_1_in results.T_1[0] = T_1_in h_2[0] = (Q_2_max - results.Q_exchanged) / m_dot_2 + h_2_max results.T_2[0] = FluidState.getStateFromPh(P_2, h_2[0], fluid_2).T_C dT[0] = direction * (results.T_1[0] - results.T_2[0]) UA[0] = dQ / dT[0] for i in range(1, increments + 1): results.Q[i] = results.Q[i - 1] + dQ h_1[i] = h_1[i - 1] + dQ / m_dot_1 h_2[i] = h_2[i - 1] + dQ / m_dot_2 results.T_1[i] = FluidState.getStateFromPh(P_1, h_1[i], fluid_1).T_C results.T_2[i] = FluidState.getStateFromPh(P_2, h_2[i], fluid_2).T_C dT[i] = direction * (results.T_1[i] - results.T_2[i]) UA[i] = dQ / dT[i] min_dT = min(dT) ddT_pinch = dT_pinch - min_dT # Adjust Q_exchanged # Use proportional error approach change = ddT_pinch / (T_2_in - T_1_in) results.Q_exchanged = (1 - change) * results.Q_exchanged results.dT_LMTD = results.Q_exchanged / sum(UA) effectiveness = results.Q_exchanged / Q_max results.T_1_out = results.T_1[-1] results.T_2_out = results.T_2[0] # Check the phase on leaving fluid if P_1 < P_crit_1: T_sat_1 = FluidState.getStateFromPQ(P_1, 1, fluid_1).T_C if T_sat_1 > T_1_in and T_sat_1 < results.T_1_out: print('Caution: Fluid 1 is phase changing in heat exchanger') if T_sat_1 == results.T_1_out: raise Exception( 'GenGeo::HeatExchanger:TwoPhaseFluid - Fluid 1 leaves two-phase!' ) if P_2 < P_crit_2: T_sat_2 = FluidState.getStateFromPQ(P_2, 1, fluid_2).T_C if T_sat_2 > T_2_in and T_sat_2 < results.T_2_out: print('Caution: Fluid 2 is phase changing in heat exchanger') if T_sat_2 == results.T_2_out: raise Exception( 'GenGeo::HeatExchanger:TwoPhaseFluid - Fluid 2 leaves two-phase!' ) return results
def solve(self, initialState, P_boil_Pa=False): T_in_C = initialState.T_C if not P_boil_Pa: P_boil_Pa = np.interp(T_in_C, self.data[:, 0], self.data[:, 1]) # Critical point of R245fa # if Pboil is below critical, throw error if P_boil_Pa < FluidState.getPcrit(self.params.orc_fluid): raise Exception( 'GenGeo::ORCCycleSupercritPboil:lowBoilingPressure - Boiling Pressure Below Critical Pressure' ) # The line of minimum entropy to keep the fluid vapor in turbine is # entropy at saturated vapor at 125C. So inlet temp must provide this # minimum entropy. s_min = FluidState.getStateFromTQ(125., 1, self.params.orc_fluid).s_JK T_min = FluidState.getStateFromPS(P_boil_Pa, s_min, self.params.orc_fluid).T_C if (T_in_C - self.params.dT_pinch) < T_min: raise Exception( 'GenGeo::ORCCycleSupercritPboil:lowInletTemp - Inlet Temp below %.1f C for Supercritical Fluid' % (T_min + self.params.dT_pinch)) T_condense_C = self.params.T_ambient_C + self.params.dT_approach # create empty list to compute cycle of 7 states state = [None] * 7 #State 1 (Condenser -> Pump) #saturated liquid state[0] = FluidState.getStateFromTQ(T_condense_C, 0, self.params.orc_fluid) # State 7 (Desuperheater -> Condenser) # saturated vapor state[6] = FluidState.getStateFromTQ(state[0].T_C, 1, self.params.orc_fluid) # State 2 (Pump -> Recuperator) h_2s = FluidState.getStateFromPS(P_boil_Pa, state[0].s_JK, self.params.orc_fluid).h_Jkg h2 = state[0].h_Jkg - ( (state[0].h_Jkg - h_2s) / self.params.eta_pump_orc) state[1] = FluidState.getStateFromPh(P_boil_Pa, h2, self.params.orc_fluid) # water (assume pressure 100 kPa above saturation) P_water = FluidState.getStateFromTQ(T_in_C, 0, 'Water').P_Pa + 100e3 # Guess orc_in fluid is state[1].T_C state[2] = FluidState.getStateFromPT(state[1].P_Pa, state[1].T_C, self.params.orc_fluid) # Water in temp is T_in_C T_C_11 = T_in_C P_4 = state[1].P_Pa # initialize state 2 with pressure state 2 and dummy temperature state[3] = FluidState.getStateFromPT(P_4, 15., self.params.orc_fluid) # initialize state 3 with pressure state 1 and dummy enthalpy state[4] = FluidState.getStateFromPh(state[0].P_Pa, 1e4, self.params.orc_fluid) # initialize state 6 with pressure state 1 and dummy temperature state[5] = FluidState.getStateFromPT(state[0].P_Pa, 15., self.params.orc_fluid) results = PowerPlantOutput() dT = 1 while abs(dT) >= 1: # State 4 (Boiler -> Turbine) # Input orc/geo heat exchanger opt_heatExchanger_results = heatExchangerOptMdot( state[2].T_C, P_4, self.params.orc_fluid, T_C_11, P_water, 'Water', self.params.dT_pinch, T_min) # state[3] = FluidStateFromPT(P_4, opt_heatExchanger_results.T_1_out, self.params.orc_fluid) state[3].T_C = opt_heatExchanger_results.T_1_out #State 5 (Turbine -> Recuperator) h_5s = FluidState.getStateFromPS(state[0].P_Pa, state[3].s_JK, self.params.orc_fluid).h_Jkg h_5 = state[3].h_Jkg - self.params.eta_turbine_orc * ( state[3].h_Jkg - h_5s) state[4].h_Jkg = h_5 # state[4] = FluidStateFromPh(state[0].P_Pa(), h_5, self.params.orc_fluid) # State 3 (Recuperator -> Boiler) # State 6 (Recuperator -> Desuperheater) # Assume m_dot for each fluid is 1, then output is specific heat # exchange heatExchanger_results = heatExchanger(state[1].T_C, state[1].P_Pa, 1, self.params.orc_fluid, state[4].T_C, state[0].P_Pa, 1, self.params.orc_fluid, self.params.dT_pinch) # state[2] = FluidStateFromPT(state[2].P_Pa, state[2].T_C, self.params.orc_fluid) # state[5] = FluidStateFromPT(state[0].P_Pa, heatExchanger_results.T_2_out, self.params.orc_fluid) state[5].T_C = heatExchanger_results.T_2_out dT = state[2].T_C - heatExchanger_results.T_1_out state[2].T_C = heatExchanger_results.T_1_out #Calculate orc heat/work w_pump_orc = state[0].h_Jkg - state[1].h_Jkg q_boiler_orc = -1 * (state[2].h_Jkg - state[3].h_Jkg) w_turbine_orc = state[3].h_Jkg - state[4].h_Jkg q_desuperheater_orc = -1 * (state[5].h_Jkg - state[6].h_Jkg) q_condenser_orc = -1 * (state[6].h_Jkg - state[0].h_Jkg) # Cooling Tower Parasitic load results.dT_range_CT = state[5].T_C - state[6].T_C parasiticPowerFraction = CoolingCondensingTower.parasiticPowerFraction( self.params.T_ambient_C, self.params.dT_approach, results.dT_range_CT, self.params.cooling_mode) w_cooler_orc = q_desuperheater_orc * parasiticPowerFraction('cooling') w_condenser_orc = q_condenser_orc * parasiticPowerFraction( 'condensing') #Calculate water heat/work results.w_pump = opt_heatExchanger_results.mdot_ratio * w_pump_orc results.q_boiler = opt_heatExchanger_results.mdot_ratio * q_boiler_orc results.w_turbine = opt_heatExchanger_results.mdot_ratio * w_turbine_orc results.q_recuperator = opt_heatExchanger_results.mdot_ratio * heatExchanger_results.Q_exchanged results.q_desuperheater = opt_heatExchanger_results.mdot_ratio * q_desuperheater_orc results.q_condenser = opt_heatExchanger_results.mdot_ratio * q_condenser_orc results.w_cooler = opt_heatExchanger_results.mdot_ratio * w_cooler_orc results.w_condenser = opt_heatExchanger_results.mdot_ratio * w_condenser_orc results.w_net = results.w_turbine + results.w_pump + results.w_cooler + results.w_condenser results.end_T_C = opt_heatExchanger_results.T_2_out results.dT_LMTD_boiler = opt_heatExchanger_results.dT_LMTD results.dT_LMTD_recuperator = heatExchanger_results.dT_LMTD # return temperature results.state = FluidState.getStateFromPT( initialState.P_Pa, opt_heatExchanger_results.T_2_out, self.params.working_fluid) return results