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 testHeatExchanger(self): T_1_in = 50. P_1 = 4.e6 m_dot_1 = 1.5 fluid_1 = 'R245fa' T_2_in = 170. m_dot_2 = 1. fluid_2 = 'Water' P_2 = FluidState.getStateFromTQ(T_2_in, 0, fluid_2).P_Pa + 100e3 dT_pinch = 5. results = heatExchanger(T_1_in, P_1, m_dot_1, fluid_1, T_2_in, P_2, m_dot_2, fluid_2, dT_pinch) self.assertTrue(*testAssert(results.Q_exchanged, 303373.3383, 'testHeatExchanger_Q_exchanged')) self.assertTrue(*testAssert(results.dT_LMTD, 11.48176, 'testHeatExchanger_dT_LMTD')) self.assertTrue(*testAssert(results.T_1_out, 159.2937, 'testHeatExchanger_T_1_out')) self.assertTrue(*testAssert(results.T_2_out, 99.01979, 'testHeatExchanger_T_2_out')) self.assertTrue( *testAssert(results.T_1[3], 71.79053, 'testHeatExchanger_T_1')) self.assertTrue( *testAssert(results.T_2[3], 109.80985, 'testHeatExchanger_T_2')) self.assertTrue( *testAssert(results.Q[3], 45526.17687, 'testHeatExchanger_Q'))
def testProductionWell(self): ### # Testing SemiAnalyticalWell for vertical production well settings ### # Water params = SimulationParameters(working_fluid = 'water', time_years = 10., m_dot_IP=136.) # initial state initial_state = FluidState.getStateFromPT(25.e6, 97., params.working_fluid) well = SemiAnalyticalWell(params, T_e_initial=102.5, dz_total=2500.) wellresult = well.solve(initial_state) self.assertMessages(well.params.working_fluid, (wellresult.state.P_Pa, 1.2372e6), (wellresult.state.T_C, 95.0133), (wellresult.state.h_Jkg, 3.9902e5)) # CO2 well.params.working_fluid = 'CO2' # initial state wellresult = well.solve(initial_state) self.assertMessages(well.params.working_fluid, (wellresult.state.P_Pa, 1.1674e7), (wellresult.state.T_C, 55.9783), (wellresult.state.h_Jkg, 3.7225e5))
def computeSurfacePipeFrictionFactor(self): self.ff_m_dot = self.params.m_dot_IP initial_P = 1e6 + self.params.P_system_min() initial_T = 60. initial_h = FluidState.getStateFromPT(initial_P, initial_T, self.params.working_fluid).h_Jkg self.friction_factor = frictionFactor(self.params.well_radius, initial_P, initial_h, \ self.params.m_dot_IP, self.params.working_fluid, self.params.epsilon)
def testORCCycleTboilFail(self): initialState = FluidState.getStateFromPT(1.e6, 15., 'water') try: results = cycle.solve(initialState, T_boil_C=100., dT_pinch=5.) except Exception as ex: self.assertTrue( str(ex).find('GenGeo::ORCCycleTboil:Tboil_Too_Large') > -1, 'test1_fail_not_found')
def minimizeFunction(self, initial_T): initial_state = FluidState.getStateFromPT(self.initial_P, initial_T, self.fluid_system.fluid) system_state = self.fluid_system.solve(initial_state) diff = (system_state.pp.state.T_C - initial_T) print('find T ', system_state.pp.state.T_C) return diff
def __init__(self): self.params.T_reservoir = lambda: abs(self.params.depth) * abs( self.params.dT_dz) + self.params.T_surface_rock self.params.P_reservoir = lambda: abs(self.params.depth ) * 1000. * self.params.g self.params.P_reservoir_max = lambda: abs(self.params.depth ) * 2500. * self.params.g self.params.P_system_min = lambda: FluidState.getStateFromTQ( self.params.T_reservoir(), 0, self.params.working_fluid).P_Pa + 1e5
def frictionFactor(well_radius, P, h, m_dot, fluid, epsilon): if well_radius == None or P == None or h == None or m_dot == None or fluid == None or epsilon == None: return 0 rho_fluid = FluidState.getStateFromPh(P, h, fluid).rho_kgm3 mu = FluidState.getStateFromPh(P, h, fluid).mu_Pas A_c = np.pi * well_radius**2 V = m_dot / A_c / rho_fluid # Relative Roughness rr = epsilon / (2 * well_radius) # Reynolds Number Re = rho_fluid * V * (2 * well_radius) / mu # Use Haaland (1983) Approximation c1 = -1.8 * np.log10((6.9 / Re) + (rr / 3.7)**1.11) return (1 / c1)**2
def testORCCycleTboil(self): initialState = FluidState.getStateFromPT(1.e6, 150., 'water') results = cycle.solve(initialState, T_boil_C=100., dT_pinch=5.) self.assertTrue(*testAssert(results.state.T_C, 68.36, 'test1_temp')) self.assertTrue(*testAssert(results.w_net, 3.8559e4, 'test1_w_net')) self.assertTrue( *testAssert(results.w_turbine, 4.7773e4, 'test1_w_turbine')) self.assertTrue( *testAssert(results.q_preheater, 1.5778e5, 'test1_q_preheater')) self.assertTrue( *testAssert(results.q_boiler, 1.9380e5, 'test1_q_boiler'))
def testInjectionWellCO2(self): ### # Testing SemiAnalyticalWell for horizontal injection well settings ### # load global physical properties gpp = SimulationParameters(working_fluid = 'co2') gpp.time_years = 10. gpp.dT_dz = 0.06 gpp.well_radius = 0.279 gpp.m_dot_IP = 5. vertical_well = SemiAnalyticalWell(params = gpp, dz_total = -3500., T_e_initial = 15.) # initial state initial_state = FluidState.getStateFromPT(1.e6, 25., gpp.working_fluid) vertical_well_results = vertical_well.solve(initial_state) self.assertMessages(gpp.working_fluid, (vertical_well_results.state.P_Pa, 1.7245e6), (vertical_well_results.state.T_C, 156.08), (vertical_well_results.state.h_Jkg, 6.1802e5)) horizontal_well = SemiAnalyticalWell(params = gpp, dr_total = 3000., T_e_initial = vertical_well.T_e_initial + gpp.dT_dz * abs(vertical_well.dz_total)) # initial state initial_state = FluidState.getStateFromPT(vertical_well_results.state.P_Pa, vertical_well_results.state.T_C, gpp.working_fluid) horizontal_well_results = horizontal_well.solve(initial_state) self.assertMessages(gpp.working_fluid, (horizontal_well_results.state.P_Pa, 1.7235e6), (horizontal_well_results.state.T_C, 212.746), (horizontal_well_results.state.h_Jkg, 6.755e5))
def testInjectionWellWater(self): ### # Testing SemiAnalyticalWell for vertical and horizontal injection well settings ### # load parameters gpp = SimulationParameters(working_fluid = 'water') gpp.time_years = 10. gpp.dT_dz = 0.06 gpp.well_radius = 0.279 gpp.m_dot_IP = 5. vertical_well = SemiAnalyticalWell(params = gpp, dz_total = -3500., T_e_initial = 15.) # initial state initial_state = FluidState.getStateFromPT(1.e6, 25., gpp.working_fluid) vertical_well_results = vertical_well.solve(initial_state) self.assertMessages(gpp.working_fluid, (vertical_well_results.state.P_Pa, 3.533e7), (vertical_well_results.state.T_C, 67.03), (vertical_well_results.state.h_Jkg, 3.0963e5)) horizontal_well = SemiAnalyticalWell(params = gpp, dr_total = 3000., T_e_initial = vertical_well.T_e_initial + gpp.dT_dz * abs(vertical_well.dz_total)) # initial state initial_state = FluidState.getStateFromPT(vertical_well_results.state.P_Pa, vertical_well_results.state.T_C, gpp.working_fluid) horizontal_well_results = horizontal_well.solve(initial_state) self.assertMessages(gpp.working_fluid, (horizontal_well_results.state.P_Pa, 3.533e7), (horizontal_well_results.state.T_C, 121.99), (horizontal_well_results.state.h_Jkg, 5.3712e5))
def solve(self): self.fluid_system.params.initial_P = 1e6 + self.fluid_system.params.P_system_min() self.fluid_system.params.initial_T = 60. initial_T = self.fluid_system.params.initial_T dT_inj = np.nan dT_loops = 1 solv = Solver() while np.isnan(dT_inj) or abs(dT_inj) >= 0.5: initial_state = FluidState.getStateFromPT(self.fluid_system.params.initial_P, initial_T, self.fluid_system.params.working_fluid) system_state = self.fluid_system.solve(initial_state) dT_inj = initial_T - system_state.pp.state.T_C initial_T = solv.addDataAndEstimate(initial_T, dT_inj) if np.isnan(initial_T): initial_T = system_state.pp.state.T_C # add lower bounds if initial_T < 1: initial_T = 1 # add upper bounds T_prod_surface_C = system_state.pump.well.state.T_C if initial_T > T_prod_surface_C and initial_T > 50: initial_T = T_prod_surface_C if dT_loops > 10: print('GenGeo::Warning:FluidSystemWaterSolver:dT_loops is large: %s'%dT_loops) dT_loops += 1 # check if silica precipitation is allowed if self.fluid_system.params.silica_precipitation: # prevent silica precipitation by DiPippo 1985 maxSurface_dT = 89 if (T_prod_surface_C - initial_T) > maxSurface_dT: raise Exception('GenGeo::FluidSystemWaterSolver:ExceedsMaxTemperatureDecrease - ' 'Exceeds Max Temp Decrease of %.3f C to prevent silica precipitation!'%(maxSurface_dT)) else: maxSurface_dT = np.inf return system_state
def testHeatExchangerOptMdot(self): T_1_in = 50. P_1 = 4.e6 fluid_1 = 'R245fa' T_2_in = 170. fluid_2 = 'Water' P_2 = FluidState.getStateFromTQ(T_2_in, 0, fluid_2).P_Pa + 100e3 dT_pinch = 5. T_min = 165. maximizeHeatFromStream = '2' results = heatExchangerOptMdot(T_1_in, P_1, fluid_1, T_2_in, P_2, fluid_2, dT_pinch, T_min, maximizeHeatFromStream) self.assertTrue(*testAssert(results.Q_exchanged, 229529.519513, 'testHeatExchangerOptMdot_Q_exchanged')) self.assertTrue(*testAssert(results.dT_LMTD, 12.601838, 'testHeatExchangerOptMdot_dT_LMTD')) self.assertTrue(*testAssert(results.T_1_out, 163.475171347679, 'testHeatExchangerOptMdot_T_1_out')) self.assertTrue(*testAssert(results.T_2_out, 131.20944977165277, 'testHeatExchangerOptMdot_T_2_out')) self.assertTrue(*testAssert(results.T_1[3], 74.66059924618094, 'testHeatExchangerOptMdot_T_1')) self.assertTrue(*testAssert(results.T_2[3], 137.08472742745016, 'testHeatExchangerOptMdot_T_2')) self.assertTrue(*testAssert(results.Q[3], 34450.560624389356, 'testHeatExchangerOptMdot_Q')) self.assertTrue(*testAssert(results.q_exchanged_1, 229529.51951325324, 'testHeatExchangerOptMdot_q_exchanged_1')) self.assertTrue(*testAssert(results.q_exchanged_2, 167077.85548339653, 'testHeatExchangerOptMdot_q_exchanged_2')) self.assertTrue(*testAssert(results.mdot_ratio, 1.3737878, 'testHeatExchangerOptMdot_mdot_ratio')) self.assertTrue(*testAssert(results.m_dot_1, 1, 'testHeatExchangerOptMdot_m_dot_1')) self.assertTrue(*testAssert(results.m_dot_2, 1.3737878, 'testHeatExchangerOptMdot_m_dot_2'))
def testORCCycleSupercritPboil(self): params = SimulationParameters(orc_fluid = 'R245fa') cycle = ORCCycleSupercritPboil(params = params) initialState = FluidState.getStateFromPT(1.e6, 190., 'water') results = cycle.solve(initialState = initialState, P_boil_Pa = 5e6) self.assertTrue(*testAssert(results.dT_range_CT, 6.8948, 'test1_dT_range_CT')) self.assertTrue(*testAssert(results.w_pump, -1.9676e+03, 'test1_w_pump')) self.assertTrue(*testAssert(results.q_boiler, 1.2030e+05, 'test1_q_boiler')) self.assertTrue(*testAssert(results.w_turbine, 2.4353e+04, 'test1_w_turbine')) self.assertTrue(*testAssert(results.q_recuperator, 9.8023e+03, 'test1_q_recuperator')) self.assertTrue(*testAssert(results.q_desuperheater, -3.0484e+03, 'test1_q_desuperheater')) self.assertTrue(*testAssert(results.q_condenser, -9.4878e+04, 'test1_q_condenser')) self.assertTrue(*testAssert(results.w_cooler, -51.2720, 'test1_w_cooler')) self.assertTrue(*testAssert(results.w_condenser, -2.5484e+03, 'test1_w_condenser')) self.assertTrue(*testAssert(results.w_net, 1.9786e+04, 'test1_w_net')) self.assertTrue(*testAssert(results.state.T_C, 73.7974, 'test1_end_T_C')) self.assertTrue(*testAssert(results.dT_LMTD_boiler, 9.6698, 'test1_dT_LMTD_boiler')) self.assertTrue(*testAssert(results.dT_LMTD_recuperator, 7.4340, 'test1_dT_LMTD_recuperator'))
def testInjectionWellCO2SmallWellR(self): ### # Testing SemiAnalyticalWell for horizontal injection well settings ### # load global physical properties gpp = SimulationParameters(working_fluid = 'co2') gpp.time_years = 10. gpp.dT_dz = 0.06 gpp.well_radius = 0.02 gpp.m_dot_IP = 0.1 vertical_well = SemiAnalyticalWell(params = gpp, dz_total = -3500., T_e_initial = 15.) # initial state initial_state = FluidState.getStateFromPT(1.e6, 25., gpp.working_fluid) vertical_well_results = vertical_well.solve(initial_state) self.assertMessages(gpp.working_fluid, (vertical_well_results.state.P_Pa, 1.1431e6), (vertical_well_results.state.T_C, 222.2246), (vertical_well_results.state.h_Jkg, 6.8717e5))
import unittest import numpy as np from src.porousReservoir import PorousReservoir from utils.depletionCurve import depletionCurve from models.simulationParameters import SimulationParameters from tests.testAssertion import testAssert from utils.fluidState import FluidState reservoir = PorousReservoir(working_fluid='co2', reservoir_thickness=300, permeability=50e-15, time_years=30, m_dot_IP=100) initialState = FluidState.getStateFromPT(25.e6, 40., reservoir.params.working_fluid) class ReservoirDepletionTest(unittest.TestCase): def testDepletionCurve(self): Psi_1 = 2. p1 = -1.9376 p2 = 1.743 p3 = 0.182 gamma = np.round(depletionCurve(Psi_1, p1, p2, p3), 4) self.assertTrue(*testAssert(gamma, 0.2531, 'testDepletionCurve')) def testNoTransient(self): reservoir.modelPressureTransient = False reservoir.modelTemperatureDepletion = False
def solve(self, initial_state): results = FluidSystemWaterOutput() injection_state = FluidState.getStateFromPT(initial_state.P_Pa, initial_state.T_C, initial_state.fluid) # Find necessary injection pressure dP_downhole = np.nan dP_solver = Solver() dP_loops = 1 stop = False while np.isnan(dP_downhole) or abs(dP_downhole) > 10e3: results.injection_well = self.injection_well.solve(injection_state) results.reservoir = self.reservoir.solve( results.injection_well.state) # if already at P_system_min, stop looping if stop: break # find downhole pressure difference (negative means overpressure) dP_downhole = self.params.P_reservoir( ) - results.reservoir.state.P_Pa injection_state.P_Pa = dP_solver.addDataAndEstimate( injection_state.P_Pa, dP_downhole) if np.isnan(injection_state.P_Pa): injection_state.P_Pa = initial_state.P_Pa + dP_downhole if dP_loops > 10: print( 'GenGeo::Warning:FluidSystemWater:dP_loops is large: %s' % dP_loops) dP_loops += 1 # Set Limits if injection_state.P_Pa < self.params.P_system_min(): # can't be below this temp or fluid will flash injection_state.P_Pa = self.params.P_system_min() # switch stop to run injection well and reservoir once more stop = True if results.reservoir.state.P_Pa >= self.params.P_reservoir_max(): raise Exception( 'GenGeo::FluidSystemWater:ExceedsMaxReservoirPressure - ' 'Exceeds Max Reservoir Pressure of %.3f MPa!' % (self.params.P_reservoir_max() / 1e6)) # Production Well (Lower, to production pump) results.production_well1 = self.production_well1.solve( results.reservoir.state) # Upper half of production well results.pump = self.pump.solve(results.production_well1.state, injection_state.P_Pa) # Subtract surface frictional losses between production wellhead and surface plant ff = frictionFactor(self.params.well_radius, results.pump.well.state.P_Pa, results.pump.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.pump.well.state.rho_kgm3 / np.pi**2 else: dP_surfacePipes = 0 results.surface_plant_inlet = FluidState.getStateFromPh( results.pump.well.state.P_Pa - dP_surfacePipes, results.pump.well.state.h_Jkg, self.params.working_fluid) results.pp = self.pp.solve(results.surface_plant_inlet) return results
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 solve(self, initial_state, P_inj_surface): if self.ff_m_dot != self.params.m_dot_IP: self.computeSurfacePipeFrictionFactor() # initilize object with well output results = DownHolePumpOutput() # Pumping and second well d_dP_pump = 1e5 dP_pump = 0 dP_surface = np.nan dP_loops = 1 dP_Solver = Solver() while np.isnan(dP_surface) or abs(dP_surface) > 100: try: if dP_pump > self.params.max_pump_dP: dP_pump = self.params.max_pump_dP P_prod_pump_out = initial_state.P_Pa + dP_pump T_prod_pump_out = initial_state.T_C temp_at_pump_depth = self.well.T_e_initial + self.params.dT_dz * self.params.pump_depth state_in = FluidState.getStateFromPT(P_prod_pump_out, T_prod_pump_out, self.params.working_fluid) results.well = self.well.solve(state_in) # calculate surface pipe friction loss if self.params.has_surface_gathering_system: dP_surface_pipes = self.friction_factor * self.params.well_spacing / \ (2 * self.params.well_radius)**5 * 8 * self.params.m_dot_IP**2 / results.well.state.rho_kgm3 / np.pi**2 else: dP_surface_pipes = 0. dP_surface = (results.well.state.P_Pa - P_inj_surface - dP_surface_pipes) if dP_pump == self.params.max_pump_dP: break # No pumping is needed in this system if dP_pump == 0 and dP_surface >= 0: break dP_pump = dP_Solver.addDataAndEstimate(dP_pump, dP_surface) if np.isnan(dP_pump): dP_pump = -1 * dP_surface # Pump can't be less than zero if dP_pump < 0: dP_pump = 0 # Warn against excessive loops if dP_loops > 10: print('Warning::DownHolePump:dP_loops is large: %s' % dP_loops) dP_loops += 1 except ValueError as error: # Only catch problems of flashing fluid if str(error).find( 'GenGeo::SemiAnalyticalWell:BelowSaturationPressure' ) > -1: dP_pump = dP_pump + d_dP_pump else: raise error # if pump pressure greater than allowable, throw error if dP_pump >= self.params.max_pump_dP: raise Exception( 'GenGeo::DownHolePump:ExceedsMaxProductionPumpPressure - ' 'Exceeds Max Pump Pressure of %.3f MPa!' % (self.params.max_pump_dP / 1e6)) results.w_pump = 0 results.dP_surface_pipes = dP_surface_pipes if dP_pump > 0: results.w_pump = (initial_state.h_Jkg - state_in.h_Jkg) / self.params.eta_pump 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
def solve(self, initialState): results = PorousReservoirResults() A_reservoir = (self.params.well_spacing**2) / 2. # # TODO: fix this. using 365 instead of 365.25 changes the results time_seconds = self.params.time_years * ConversionConstants.secPerYear # from output properties prod_State = FluidState.getStateFromPT(self.params.P_reservoir(), self.params.T_reservoir(), self.params.working_fluid) mu_fluid = (initialState.mu_Pas + prod_State.mu_Pas) / 2 rho_fluid = (initialState.rho_kgm3 + prod_State.rho_kgm3) / 2 cp_fluid = (initialState.cp_JK + prod_State.cp_JK) / 2 # reservoir impedance if self.params.well_spacing <= self.params.well_radius: RI = 0 else: if self.params.wellFieldType == WellFieldType._5Spot_SharedNeighbor \ or self.params.wellFieldType == WellFieldType._5Spot \ or self.params.wellFieldType == WellFieldType._5Spot_Many: A_c_rock = np.log((4 * self.params.well_spacing) / (2 * self.params.well_radius * np.pi)) RI = mu_fluid / rho_fluid / self.params.transmissivity * A_c_rock elif self.params.wellFieldType == WellFieldType.Doublet: A_c_rock = np.log( (self.params.well_spacing) / (2 * self.params.well_radius)) RI = mu_fluid / rho_fluid / self.params.transmissivity / np.pi * A_c_rock else: raise Exception( 'GenGeo::PorousReservoir:unknownReservoirConfiguration - ' 'Unknown Reservoir Configuration') # Calculate heat extracted dT_initial = self.params.T_reservoir() - initialState.T_C res_energy = A_reservoir * self.params.reservoir_thickness * self.params.rho_rock * self.params.c_rock * dT_initial # Model pressure transient (Figure 4.2, Adams (2015)), only for CO2 drying out if self.modelPressureTransient == True and self.params.working_fluid.lower( ) == 'co2': R = self.params.well_spacing nu_inj_fluid = initialState.mu_Pas / initialState.rho_kgm3 tau = time_seconds * nu_inj_fluid / R**2. b = -0.4574 * (abs(R) / abs(self.params.depth))**-0.27 b = np.clip(b, -0.9, -0.4) a = 1.0342 * np.exp(10.989 * b) a = np.clip(a, 0., 0.012) coeff = (a * tau**b + 1) RI = RI * coeff # Model temperature transient (Figure 4.5, Adams (2015)) # First-principles model for all fluids # At time zero, output is the same as no temp depletion if self.modelTemperatureDepletion == True and self.params.time_years > 0.: # p1 coeff1 = self.params.reservoir_thickness * self.params.k_rock * initialState.rho_kgm3 / self.params.m_dot_IP / self.params.rho_rock / self.params.c_rock p1 = -890.97 * coeff1 - 1.30 # limit to regression p1 = np.minimum(p1, -1.3) # p2 p2 = 0.4095 * np.exp(-0.7815 * p1) # p3 R = self.params.well_spacing coeff2 = self.params.k_rock * R * initialState.rho_kgm3 / self.params.rho_rock / initialState.cp_JK / self.params.m_dot_IP # A more realistic, exponential relation is found by fitting the same data # with an exponential, instead of linear, curve. p3 = 1 - (1.4646 * np.exp(-377.3 * coeff2)) # p3 cant be greater than 1 or less than 0. p3 = np.clip(p3, 0., 1.) # psi # numerically integrate to get heat depletion # have roughly one increment a year increments = np.maximum(np.round(self.params.time_years), 1.) dt = time_seconds / increments res_energy_extracted = 0 gamma = 1 for i in range(int(increments)): res_energy_extracted = self.params.m_dot_IP * cp_fluid * gamma * dT_initial * dt + res_energy_extracted results.psi = res_energy_extracted / res_energy gamma = depletionCurve(results.psi, p1, p2, p3) # last gamma is res temp else: # gamma of 1 is undepleted reservoir gamma = 1 res_energy_extracted = self.params.m_dot_IP * cp_fluid * gamma * dT_initial * time_seconds if res_energy == 0: results.psi = 0 else: results.psi = res_energy_extracted / res_energy results.dP = self.params.m_dot_IP * RI end_P_Pa = initialState.P_Pa - results.dP end_T_C = gamma * dT_initial + initialState.T_C results.state = FluidState.getStateFromPT(end_P_Pa, end_T_C, self.params.working_fluid) results.heat = self.params.m_dot_IP * (results.state.h_Jkg - initialState.h_Jkg) return results
def solve(self, initial_state): m_dot = self.params.m_dot_IP * self.m_dot_multiplier # results results = SemiAnalyticalWellResults(self.params.well_segments, self.params.working_fluid) P_f_initial = initial_state.P_Pa T_f_initial = initial_state.T_C time_seconds = self.params.time_years * ConversionConstants.secPerYear # set geometry dz = self.dz_total/self.params.well_segments # m dr = self.dr_total/self.params.well_segments # m dL = (dz**2 + dr**2)**0.5 # m A_c = np.pi * self.params.well_radius**2 # m**2 P_c = np.pi * 2 * self.params.well_radius # m # set states results.T_C_f[0] = T_f_initial # C results.T_C_e[0] = self.T_e_initial # C results.P_Pa[0] = P_f_initial # Pa results.h_Jkg[0] = FluidState.getStateFromPT(results.P_Pa[0], results.T_C_f[0], self.params.working_fluid).h_Jkg results.rho_kgm3[0] = FluidState.getStateFromPT(results.P_Pa[0], results.T_C_f[0], self.params.working_fluid).rho_kgm3 results.v_ms[0] = m_dot / A_c / results.rho_kgm3[0] #m/s # Calculate the Friction Factor # Use Colebrook-white equation for wellbore friction loss. # Calculate for first element and assume constant in remainder ff = frictionFactor(self.params.well_radius, results.P_Pa[0], results.h_Jkg[0], \ m_dot, self.params.working_fluid, self.params.epsilon) alpha_rock = self.params.k_rock/self.params.rho_rock/self.params.c_rock #D rock t_d = alpha_rock*time_seconds/(self.params.well_radius**2) #dim if t_d < 2.8: beta = ((np.pi*t_d)**-0.5 + 0.5 - 0.25*(t_d/np.pi)**0.5 + 0.125*t_d) else: beta = (2/(np.log(4*t_d)-2*0.58) - 2*0.58/(np.log(4*t_d)-2*0.58)**2) # loop over all well segments for i in range(1, self.params.well_segments+1): results.z_m[i] = results.z_m[i-1] + dz # far-field rock temp results.T_C_e[i] = results.T_C_e[0] - results.z_m[i] * self.params.dT_dz # fluid velocity results.v_ms[i] = m_dot / A_c / results.rho_kgm3[i-1] #m/s # Calculate Pressure results.delta_P_loss[i] = ff * dL / ( 2 * self.params.well_radius) * \ results.rho_kgm3[i-1] * \ results.v_ms[i]**2. / 2. #Pa results.rho_kgm3[i] = FluidState.getStateFromPh(results.P_Pa[i-1], results.h_Jkg[i-1], self.params.working_fluid).rho_kgm3 results.P_Pa[i] = results.P_Pa[i-1] - results.rho_kgm3[i] * \ self.params.g * dz - results.delta_P_loss[i] # Throw exception if below saturation pressure of water at previous temperature if self.params.working_fluid.lower() == 'water': P_sat = FluidState.getStateFromTQ(results.T_C_f[i-1], 0, self.params.working_fluid).P_Pa if results.P_Pa[i] < P_sat: raise Exception('GenGeo::SemiAnalyticalWell:BelowSaturationPressure - ' 'Below saturation pressure of water at %s m !' %(results.z_m[i])) h_noHX = results.h_Jkg[i-1] - self.params.g * dz T_noHX = FluidState.getStateFromPh(results.P_Pa[i], h_noHX, self.params.working_fluid).T_C results.cp_JK[i] = FluidState.getStateFromPh(results.P_Pa[i], h_noHX, self.params.working_fluid).cp_JK #Find Fluid Temp if not self.params.useWellboreHeatLoss: results.T_C_f[i] = T_noHX results.h_Jkg[i] = h_noHX results.q[i] = 0. else: # See Zhang, Pan, Pruess, Finsterle (2011). A time-convolution # approach for modeling heat exchange between a wellbore and # surrounding formation. Geothermics 40, 261-266. x = dL * P_c * self.params.k_rock * beta / self.params.well_radius y = m_dot * results.cp_JK[i] if math.isinf(x): results.T_C_f[i] = results.T_C_e[i] else: results.T_C_f[i] = (y * T_noHX + x *results. T_C_e[i]) / (x + y) results.q[i] = y * (T_noHX - results.T_C_f[i]) results.h_Jkg[i] = FluidState.getStateFromPT(results.P_Pa[i], results.T_C_f[i], self.params.working_fluid).h_Jkg # make sure state object is set results.createFinalState() return results
def createFinalState(self): self.state = FluidState.getStateFromPT(self.P_Pa[-1], self.T_C_f[-1], self.fluid)
def solve(self, initialState, T_boil_C=False, dT_pinch=False): T_in_C = initialState.T_C if not T_boil_C: T_boil_C = np.interp( T_in_C, self.data[self.params.opt_mode][self.params.orc_fluid][:, 0], self.data[self.params.opt_mode][self.params.orc_fluid][:, 1]) if not dT_pinch: dT_pinch = np.interp( T_in_C, self.data[self.params.opt_mode][self.params.orc_fluid][:, 0], self.data[self.params.opt_mode][self.params.orc_fluid][:, 2]) # run some checks if T_in_C and T_boil_C are valid if np.isnan(T_in_C): raise Exception( 'GenGeo::ORCCycleTboil:T_in_NaN - ORC input temperature is NaN!' ) if np.isnan(T_boil_C): raise Exception( 'GenGeo::ORCCycleTboil:T_boil_NaN - ORC boil temperature is NaN!' ) if T_boil_C > FluidState.getTcrit(self.params.orc_fluid): raise Exception( 'GenGeo::ORCCycleTboil:Tboil_Too_Large - Boiling temperature above critical point' ) if dT_pinch <= 0: raise Exception( 'GenGeo::ORCCycleTboil:dT_pinch_Negative - dT_pinch is negative!' ) if T_in_C < T_boil_C + dT_pinch: raise Exception( 'GenGeo::ORCCycleTboil:Tboil_Too_Large - Boiling temperature of %s is greater than input temp of %s less pinch dT of %s.' % (T_boil_C, T_in_C, dT_pinch)) # only refresh T_boil_max if orc_fluid has changed from initial if self.params.orc_fluid != self.orc_fluid: self.T_boil_max = maxSubcritORCBoilTemp(self.params.orc_fluid) self.orc_fluid = self.params.orc_fluid if T_boil_C > self.T_boil_max: raise Exception( 'GenGeo::ORCCycleTboil:Tboil_Too_Large - Boiling temperature of %s is greater than maximum allowed of %s.' % (T_boil_C, self.T_boil_max)) T_condense_C = self.params.T_ambient_C + self.params.dT_approach # create empty list to compute cycle of 6 states state = [None] * 6 #State 1 (Condenser -> Pump) #saturated liquid state[0] = FluidState.getStateFromTQ(T_condense_C, 0, self.params.orc_fluid) #State 6 (Desuperheater -> Condenser) #saturated vapor state[5] = FluidState.getStateFromTQ(state[0].T_C, 1, self.params.orc_fluid) # state[5].P_Pa = state[0].P_Pa #State 3 (Preheater -> Boiler) #saturated liquid state[2] = FluidState.getStateFromTQ(T_boil_C, 0, self.params.orc_fluid) #State 4 (Boiler -> Turbine) #saturated vapor state[3] = FluidState.getStateFromTQ(state[2].T_C, 1, self.params.orc_fluid) # state[3].P_Pa = state[2].P_Pa #State 5 (Turbine -> Desuperheater) 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] = FluidState.getStateFromPh(state[0].P_Pa, h_5, self.params.orc_fluid) # #State 2 (Pump -> Preheater) h_2s = FluidState.getStateFromPS(state[2].P_Pa, state[0].s_JK, self.params.orc_fluid).h_Jkg h_2 = state[0].h_Jkg - ( (state[0].h_Jkg - h_2s) / self.params.eta_pump_orc) state[1] = FluidState.getStateFromPh(state[2].P_Pa, h_2, self.params.orc_fluid) results = PowerPlantOutput() # #Calculate orc heat/work w_pump_orc = state[0].h_Jkg - state[1].h_Jkg q_preheater_orc = -1 * (state[1].h_Jkg - state[2].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[4].h_Jkg - state[5].h_Jkg) q_condenser_orc = -1 * (state[5].h_Jkg - state[0].h_Jkg) results.dP_pump_orc = state[1].P_Pa - state[0].P_Pa results.P_boil = state[2].P_Pa # Cooling Tower Parasitic load dT_range = state[4].T_C - state[5].T_C parasiticPowerFraction = CoolingCondensingTower.parasiticPowerFraction( self.params.T_ambient_C, self.params.dT_approach, dT_range, self.params.cooling_mode) w_cooler_orc = q_desuperheater_orc * parasiticPowerFraction('cooling') w_condenser_orc = q_condenser_orc * parasiticPowerFraction( 'condensing') #water (assume pressure 100 kPa above saturation) P_sat = FluidState.getStateFromTQ(T_in_C, 0, 'Water').P_Pa cp = FluidState.getStateFromPT(P_sat + 100e3, T_in_C, 'Water').cp_JK #Water state 11, inlet, 12, mid, 13 exit T_C_11 = T_in_C T_C_12 = T_boil_C + dT_pinch #mdot_ratio = mdot_orc / mdot_water mdot_ratio = cp * (T_C_11 - T_C_12) / q_boiler_orc T_C_13 = T_C_12 - mdot_ratio * q_preheater_orc / cp # check that T_C(13) isn't below pinch constraint if T_C_13 < (state[1].T_C + dT_pinch): # pinch constraint is here, not at 12 # outlet is pump temp plus pinch T_C_13 = state[1].T_C + dT_pinch R = q_boiler_orc / (q_boiler_orc + q_preheater_orc) T_C_12 = T_C_11 - (T_C_11 - T_C_13) * R mdot_ratio = cp * (T_C_11 - T_C_12) / q_boiler_orc #Calculate water heat/work results.q_preheater = mdot_ratio * q_preheater_orc results.q_boiler = mdot_ratio * q_boiler_orc results.q_desuperheater = mdot_ratio * q_desuperheater_orc results.q_condenser = mdot_ratio * q_condenser_orc results.w_turbine = mdot_ratio * w_turbine_orc results.w_pump = mdot_ratio * w_pump_orc results.w_cooler = mdot_ratio * w_cooler_orc results.w_condenser = mdot_ratio * w_condenser_orc results.w_net = results.w_turbine + results.w_pump + results.w_cooler + results.w_condenser # Calculate temperatures results.dT_range_CT = state[4].T_C - state[5].T_C dT_A_p = T_C_13 - state[1].T_C dT_B_p = T_C_12 - state[2].T_C if dT_A_p == dT_B_p: results.dT_LMTD_preheater = dT_A_p else: div = dT_A_p / dT_B_p results.dT_LMTD_preheater = (dT_A_p - dT_B_p) / ( math.log(abs(div)) * np.sign(div)) dT_A_b = T_C_12 - state[2].T_C dT_B_b = T_C_11 - state[3].T_C if dT_A_b == dT_B_b: results.dT_LMTD_boiler = dT_A_b else: div = dT_A_b / dT_B_b results.dT_LMTD_boiler = (dT_A_b - dT_B_b) / (math.log(abs(div)) * np.sign(div)) # return temperature results.state = FluidState.getStateFromPT(initialState.P_Pa, T_C_13, self.params.working_fluid) 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