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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
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