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 solve(self): results = FluidSystemCO2Output() results.pp = PowerPlantEnergyOutput() # Find condensation pressure T_condensation = self.params.T_ambient_C + self.params.dT_approach P_condensation = FluidState.getStateFromTQ(T_condensation, 0, self.params.working_fluid).P_Pa + 50e3 dP_pump = 0 dP_downhole_threshold = 1e3 dP_downhole = np.nan dP_loops = 1 dP_solver = Solver() while np.isnan(dP_downhole) or np.abs(dP_downhole) >= dP_downhole_threshold: # Find Injection Conditions P_pump_inlet = P_condensation # Only add pump differential if positive. If negative, add as a throttle at the bottom of the injection well if (dP_pump > 0): P_pump_outlet = P_pump_inlet + dP_pump else: P_pump_outlet = P_pump_inlet T_pump_inlet = T_condensation pump_inlet_state = FluidState.getStateFromPT(P_pump_inlet, T_pump_inlet, self.params.working_fluid) if dP_pump > 0: h_pump_outletS = FluidState.getStateFromPS(P_pump_outlet, pump_inlet_state.s_JK, self.params.working_fluid).h_Jkg h_pump_outlet = pump_inlet_state.h_Jkg + (h_pump_outletS - pump_inlet_state.h_Jkg) / self.params.eta_pump_co2 else: h_pump_outlet = pump_inlet_state.h_Jkg results.pp.w_pump = -1 * (h_pump_outlet - pump_inlet_state.h_Jkg) surface_injection_state = FluidState.getStateFromPh(P_pump_outlet, h_pump_outlet, self.params.working_fluid) results.injection_well = self.injection_well.solve(surface_injection_state) # if dP_pump is negative, this is a throttle after the injection well if (dP_pump < 0): results.injection_well_downhole_throttle = FluidState.getStateFromPh( results.injection_well.state.P_Pa + dP_pump, results.injection_well.state.h_Jkg, self.params.working_fluid) else: results.injection_well_downhole_throttle = FluidState.getStateFromPh( results.injection_well.state.P_Pa, results.injection_well.state.h_Jkg, self.params.working_fluid) results.reservoir = self.reservoir.solve(results.injection_well_downhole_throttle) # find downhole pressure difference (negative means # overpressure dP_downhole = self.params.P_reservoir() - results.reservoir.state.P_Pa dP_pump = dP_solver.addDataAndEstimate(dP_pump, dP_downhole) if np.isnan(dP_pump): dP_pump = 0.5 * dP_downhole if dP_loops > 10: print('GenGeo::Warning::FluidSystemCO2:dP_loops is large: %s'%dP_loops) dP_loops += 1 if results.reservoir.state.P_Pa >= self.params.P_reservoir_max(): raise Exception('GenGeo::FluidSystemCO2:ExceedsMaxReservoirPressure - ' 'Exceeds Max Reservoir Pressure of %.3f MPa!'%(self.params.P_reservoir_max()/1e6)) results.production_well = self.production_well.solve(results.reservoir.state) # Subtract surface frictional losses between production wellhead and surface plant ff = frictionFactor(self.params.well_radius, results.production_well.state.P_Pa, results.production_well.state.h_Jkg, self.params.m_dot_IP, self.params.working_fluid, self.params.epsilon) if self.params.has_surface_gathering_system == True: dP_surfacePipes = ff * self.params.well_spacing / (self.params.well_radius*2)**5 * 8 * self.params.m_dot_IP**2 / results.production_well.state.rho_kgm3 / 3.14159**2 else: dP_surfacePipes = 0 results.surface_plant_inlet = FluidState.getStateFromPh( results.production_well.state.P_Pa - dP_surfacePipes, results.production_well.state.h_Jkg, self.params.working_fluid) # Calculate Turbine Power h_turbine_outS = FluidState.getStateFromPS(P_condensation, results.surface_plant_inlet.s_JK, self.params.working_fluid).h_Jkg h_turbine_out = results.surface_plant_inlet.h_Jkg - self.params.eta_turbine_co2 * (results.surface_plant_inlet.h_Jkg - h_turbine_outS) results.pp.w_turbine = results.surface_plant_inlet.h_Jkg - h_turbine_out if results.pp.w_turbine < 0: raise Exception('GenGeo::FluidSystemCO2:TurbinePowerNegative - Turbine Power is Negative') # heat rejection h_satVapor = FluidState.getStateFromPQ(P_condensation, 1, self.params.working_fluid).h_Jkg h_condensed = FluidState.getStateFromPQ(P_condensation, 0, self.params.working_fluid).h_Jkg if h_turbine_out > h_satVapor: # desuperheating needed results.pp.q_cooler = h_satVapor - h_turbine_out results.pp.q_condenser = h_condensed - h_satVapor T_turbine_out = FluidState.getStateFromPh(P_condensation, h_turbine_out, self.params.working_fluid).T_C dT_range = T_turbine_out - T_condensation else: # no desuperheating results.pp.q_cooler = 0 results.pp.q_condenser = h_condensed - h_turbine_out dT_range = 0 parasiticPowerFraction = CoolingCondensingTower.parasiticPowerFraction(self.params.T_ambient_C, self.params.dT_approach, dT_range, self.params.cooling_mode) results.pp.w_cooler = results.pp.q_cooler * parasiticPowerFraction('cooling') results.pp.w_condenser = results.pp.q_condenser * parasiticPowerFraction('condensing') results.pp.w_net = results.pp.w_turbine + results.pp.w_pump + results.pp.w_cooler + results.pp.w_condenser results.pp.dP_surface = results.surface_plant_inlet.P_Pa - P_condensation results.pp.dP_pump = dP_pump results.pp.state_out = surface_injection_state return results
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