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
示例#2
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
    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