Beispiel #1
0
def prepare_single_phase_liquid(T_inlet, steps, p_ref, m_dot, fp: FluidProperties):
    """ Prepare numpy arrays for calculating channel length in a liquid single-phase section of a channel.
    NOTE: This is done to avoid recalculating arrays that are not dependent on channel geometry, therefore speeding up optimizations.\
        After all, during optimization the geometry is what varies.\
        Also it also ensure that temperature endpoint and enthalpy cleanly match with saturation temperature in the correct phase

    Args:
        T_inlet (K): Inlet temperature
        steps (-): Amount steps of dT taken to reach saturation temperature T_sat (dT = (T_sat-T_inlet)/2)
        p_ref (Pa): Pressure assumed constant along channel, equal to inlet pressure
        m_dot (kg/s): Mass flow
        fp (FluidProperties): Object to access propellant properties with
    """
    T_sat = fp.get_saturation_temperature(p=p_ref) # [K] Saturation temperature
    assert ( T_inlet < T_sat) # Check input
    assert (steps > 1)

    # Temperature and other intermediate variable in channel section i=0...n
    T, dT = np.linspace(start=T_inlet, stop=T_sat, num=steps,retstep=True) # [K] Temperature T_i (also returns steps between sections)
    # The reference temperature for heat transfer calculations
    # The first value [0] should not be important. The heat transfer calculated at i is between i-1 and i
    #  So, from T[i-1] to T[i]. So, if there reference temperature is the average dT/2 must SUBTRACTED
    #T_ref = T - dT/2 # [K] Reference temperature for heat transfer calculations

    ## Get all thermodynamic values that can be precalculated
    # NOTE: all last values must be replaced with the correct values for the saturated liquid state
    # Before the values are replaced, sometimes an error is thrown because the values are close to the saturation point
    # That, or NaNs and infinites show up. This shouldn't be a problem, unless the second-to-last points also start getting close to the saturation point
    
    # Enthalpy 
    h = fp.get_enthalpy(T=T, p=p_ref) # [J/kg] Enthalpy
    h[-1] = fp.get_saturation_enthalpy_liquid(p=p_ref) # [J/kg] Saturation enthalpy at T_n = T_sat
    # Heating power required in section to increase temp by dT. Use enthalpy difference
    delta_h = delta_enthalpy_per_section(h=h) # [J/kg] Enthalpy difference per section
    Q_dot = required_power(m_dot=m_dot, delta_h=delta_h) # [W]

    # Density
    rho = fp.get_density(T=T, p=p_ref) # [kg/m^3] Density
    rho[-1] = fp.get_liquid_density_at_psat(p_sat=p_ref) # [kg/m^3] Saturation density
    # Prandtl number
    Pr = fp.get_Prandtl(T=T, p=p_ref) # [-] Prandtl number
    Pr[-1] = fp.get_saturation_Prandtl_liquid(p_sat=p_ref) # [-] Saturation Prandtl
    # Thermal conductivity 
    kappa = fp.get_thermal_conductivity(T=T, p=p_ref) # [W/(m*K)] Conductivity
    kappa[-1] = fp.get_liquid_saturation_conductivity(p_sat=p_ref) # [W/(m*K)] Saturation conductivity
    # Viscosity
    mu = fp.get_viscosity(T=T, p=p_ref) # [Pa*s] Viscosity
    mu[-1] = fp.get_liquid_saturation_viscosity(p_sat=p_ref) # [Pa*s] Saturation viscosity
    return {\
        "T":T, # [K]
        "dT": dT, # [K]
        "rho": rho, # [kg/m^3]
        "h": h, # [J/kg]
        "Q_dot": Q_dot, # [W]
        "Pr": Pr, # [-]
        "kappa": kappa, # [W/(m*K)]
        "mu": mu, # [Pa*s]
        }
Beispiel #2
0
    def test_table(self):
        # Table V from Lemmon2004 has density as inputs, which is not interesting for our purposes, so first the pressure must be obtained

        fp = FluidProperties("nitrogen")

        # T in, Density in mol/dm^3, viscosity out Pa*s
        in_out = ((100, 25.0, 79.7418e-6, 10), (200, 10.0, 21.0810e-6, 10),
                  (300, 5.0, 20.7430e-6, 10))

        molar_mass = 0.02801348  # [kg/mol] also from Span2000

        for T, rho, mu, places in in_out:
            p = fp.get_pressure(T=T, rho=rho * molar_mass * 1e3)
            res_mu = fp.get_viscosity(T=T, p=p)
            self.assertAlmostEqual(res_mu, mu, places=places)
Beispiel #3
0
def prepare_single_phase_gas(T_outlet, steps, p_ref, m_dot, fp: FluidProperties):

    T_sat = fp.get_saturation_temperature(p=p_ref) # [K] Saturation temperature
    assert (T_outlet > T_sat)
    assert (steps > 1)

    # Temperature and other intermediate variable in channel section i=0...n
    T, dT = np.linspace(start=T_sat, stop=T_outlet, num=steps, retstep=True) # [K] Temperature T_i
    # The reference temperature for heat transfer calculations
    # The first value [0] should not be important. The heat transfer calculated at i is between i-1 and i
    #  So, from T[i-1] to T[i]. So, if there reference temperature is the average dT/2 must SUBTRACTED
    #T_ref = T - dT/2 # [K] Reference temperature for heat transfer calculations

    ## Get all thermodynamic values that can be precalculated
    # NOTE: all first values must be replaced with the correct values for the saturated gas state
    # Before the values are replaced, sometimes an error is thrown because the values are close to the saturation point
    # That, or NaNs and infinites show up. This shouldn't be a problem, unless the second-to-last points also start getting close to the saturation point
    
    # Enthalpy 
    h = fp.get_enthalpy(T=T, p=p_ref) # [J/kg] Enthalpy
    h[0] = fp.get_saturation_enthalpy_gas(p=p_ref) # [J/kg] Saturation enthalpy at T_n = T_sat
    # Heating power required in section to increase temp by dT. Use enthalpy difference
    delta_h = delta_enthalpy_per_section(h=h) # [J/kg] Enthalpy difference per section
    Q_dot = required_power(m_dot=m_dot, delta_h=delta_h) # [W]

    # Density
    rho = fp.get_density(T=T, p=p_ref) # [kg/m^3] Density
    rho[0] = fp.get_vapour_density_at_psat(p_sat=p_ref) # [kg/m^3] Saturation density
    # Prandtl number
    Pr = fp.get_Prandtl(T=T, p=p_ref) # [-] Prandtl number
    Pr[0] = fp.get_saturation_Prandtl_gas(p_sat=p_ref) # [-] Saturation Prandtl
    # Thermal conductivity 
    kappa = fp.get_thermal_conductivity(T=T, p=p_ref) # [W/(m*K)] Conductivity
    kappa[0] = fp.get_gas_saturation_conductivity(p_sat=p_ref) # [W/(m*K)] Saturation conductivity
    # Viscosity
    mu = fp.get_viscosity(T=T, p=p_ref) # [Pa*s] Viscosity
    mu[0] = fp.get_gas_saturation_viscosity(p_sat=p_ref) # [Pa*s] Saturation viscosity
    return {\
        "T":T, # [K]
        "dT": dT, # [K]
        "rho": rho, # [kg/m^3]
        "h": h, # [J/kg]
        "Q_dot": Q_dot, # [W]
        "Pr": Pr, # [-]
        "kappa": kappa, # [W/(m*K)]
        "mu": mu, # [Pa*s]
        }
Beispiel #4
0
def Rajeev_complete(p_chamber, T_chamber, w_throat, h_throat, throat_roc,
                    AR_exit, p_back, divergence_half_angle,
                    fp: FluidProperties, is_cold_flow):
    """ Function that implements all corrections proposed by Makhan2018

    Args:
        p_chamber (Pa): Chamber pressure
        T_chamber (K): Chamber temperature
        w_throat (m): Throat width
        h_throat (m): Throat heigh (or channel depth)
        throat_roc (m): Throat radius of curvature
        AR_exit (-): Area ratio of nozzle exit area divided by throat area
        p_back (Pa): Back pressure
        divergence_half_angle (rad): Divergence half angle of nozzle 
        fp (FluidProperties): Object to access fluid properties
        is_cold_flow (bool): Reynolds number is adjusted depending on whether the chamber is heated or cooled

    Raises:
        ValueError: Is raised for hot flow, since no verification is done yet on that equation
    """

    # Get the (assumed to be) constant fluid properties
    gamma = fp.get_specific_heat_ratio(T=T_chamber,
                                       p=p_chamber)  # [-] Specific heat ratio
    R = fp.get_specific_gas_constant()  # [J/kg] Specific gas constant
    # Report calculated values for verification and comparison purposes
    print("Gamma: {:1.4f}".format(gamma))
    print("R: {:3.2f} J/kg\n".format(R))

    # Calculate basic peformance parameters
    A_throat = w_throat * h_throat  # [m] Throat area

    ## IDEAL PERFORMANCE
    # First get ideal performance, and check if the nozzle is properly expanded.
    ep = IRT.get_engine_performance(p_chamber=p_chamber,
                                    T_chamber=T_chamber,
                                    A_throat=A_throat,
                                    AR_exit=AR_exit,
                                    p_back=p_back,
                                    gamma=gamma,
                                    R=R)

    # Report ideal performance
    print("Thrust: {:.2f} mN".format(ep['thrust'] * 1e3))
    print("Isp_ideal: {:.1f} s".format(ep['thrust'] / ep['m_dot'] / 9.80655))
    print("Mass flow: {:.3f} mg/s".format(ep['m_dot'] * 1e6))

    m_dot_ideal = ep['m_dot']  # [kg/s] Ideal mass flow
    #F_ideal = ep['thrust'] # [N] Ideal thrust

    ## CALCULATING THE CORRECTION FACTORS

    # Calculate the divergence loss and report it
    CF_divergence_loss = divergence_loss_conical_2D(
        alpha=divergence_half_angle)
    print("\n -- DIVERGENCE LOSS for {:2.2f} deg divergence half-angle".format(
        math.degrees(divergence_half_angle)))
    print(
        "  Divergence loss (2D concical): {:.5f} ".format(CF_divergence_loss))

    # Calculate the viscous loss

    # To determine the Reynolds number at the throat, the hydraulic diameter at the throat and nozzle conditions must be determined
    # Get hydraulic diameter of the nozzle from the wetted perimeter and nozzle area
    wetted_perimeter_throat = 2 * (w_throat + h_throat
                                   )  # [m] Wetted perimeter throat
    Dh_throat = hydraulic_diameter(A=A_throat,
                                   wetted_perimeter=wetted_perimeter_throat
                                   )  # [m] Hydraulic diameter at throat
    p_throat = p_chamber / IRT.pressure_ratio(
        M=1, gamma=gamma)  # [Pa] pressure in throat
    T_throat = T_chamber / IRT.temperature_ratio(
        M=1, gamma=gamma)  # [K] Temperature in throat
    viscosity_throat = fp.get_viscosity(T=T_throat, p=p_throat)
    # Throat reynolds based on ideal mass flow?
    Re_throat = reynolds(m_dot=m_dot_ideal,
                         A=A_throat,
                         D_hydraulic=Dh_throat,
                         viscosity=viscosity_throat)
    if is_cold_flow:
        Re_throat_wall = Reynolds_throat_wall_cold(reynolds_throat=Re_throat)
    else:
        Re_throat_wall = Reynolds_throat_wall_hot(reynolds_throat=Re_throat)
    print("\n-- THROAT CONDITIONS --")
    print("  p = {:2.4f} bar,     T = {:4.2f} K".format(
        p_throat * 1e-5, T_throat))
    print("  mu = {:2.4f} [microPa*s]  Dh = {:3.4f} [microm]".format(
        viscosity_throat * 1e6, Dh_throat * 1e6))
    print(" Reynolds: {:6.6f} ".format(Re_throat))

    CF_viscous_loss = viscous_loss(area_ratio=AR_exit,
                                   reynolds_throat_wall=Re_throat_wall)
    print(" CF_viscous_loss: {:1.5f}".format(CF_viscous_loss))

    # Calculating throat boundary layer loss, which causes a reduction in effective throat area/mass flow
    Cd_throat_boundary_loss = throat_boundary_loss(gamma=gamma,
                                                   reynolds_throat=Re_throat,
                                                   throat_radius=0.5 *
                                                   Dh_throat,
                                                   throat_roc=throat_roc)
    print("\n-- DISCHARGE FACTOR --")
    print("  Throat boundary layer: {:1.4f}".format(Cd_throat_boundary_loss))

    ## APPLYING THE CORRECTION FACTORS
    # Now all these loss factors must be combined into a new "real" thrust
    # The divergence loss only applies to the jet/momentum thrust and not the pressure, so jet thrust is needed
    # This is equal to the exit velocity times corrected mass flow. The returned exit velocity does not include pressure terms!

    # First we must know the corrected mass flow
    m_dot_real = ep['m_dot'] * Cd_throat_boundary_loss  # [kg/s]
    # Secondly, we must know the pressure thrust to add to the jet thrust again
    F_pressure = IRT.pressure_thrust(p_chamber=p_chamber,
                                     p_back=p_back,
                                     A_throat=A_throat,
                                     AR=AR_exit,
                                     gamma=gamma)
    F_divergence = m_dot_real * ep[
        'u_exit'] * CF_divergence_loss + F_pressure  # [N] Thrust decreased by divergence loss, pressure term must be added again, since divergence only applies to jet thrust
    # This jet thrust is then again corrected by viscous losses, which are subtracted from the current CF
    CF_jet_divergence = F_divergence / (
        p_chamber * A_throat
    )  # [-] Thrust coefficient after taking into account discharge factor and divergence loss
    CF_real_final = CF_jet_divergence - CF_viscous_loss  # [-] The final thrust coefficient, also taking into account viscous loss
    F_real = CF_real_final * p_chamber * A_throat  # [N] Real thrust, after taking into account of all the three proposed correction factors

    # Report "real" results
    print("\n === CORRECTED PERFORMANCE PARAMETERS === ")
    print("  Real mass flow: {:3.4f} mg/s".format(m_dot_real * 1e6))
    print("  CF with divergence loss {:1.5f}".format(CF_jet_divergence))
    print("  Real CF: {:1.5f}".format(CF_real_final))
    print("  Real thrust: {:2.4f} mN".format(F_real * 1e3))
    print("  Real Isp: {:3.2f}".format(F_real / m_dot_real / 9.80655))

    return {
        'm_dot_real': m_dot_real,
        'm_dot_ideal': ep['m_dot'],
        'F_real': CF_real_final
    }
Beispiel #5
0
import thermo.convection as conv
import basic.chamber

# Object to retrievere propellant properties from
fp = FluidProperties("water")

# Reference state for calculations
p = 5e5  # [Pa]
T_liquid = 300  # [K]
T_gas = 500  # [K]
m_dot = 1e-6  # [kg/s] For Kandlikar relation.
#h_c = 100e-6 # [m] Channel depth/height

# Density is needed to reverse calculate the reference length for Re, so it can be applied in other flow similarity parameters
rho_liquid = fp.get_density(T=T_liquid, p=p)
mu_liquid = fp.get_viscosity(T=T_liquid, p=p)

# Range of Reynolds numbers to consider
Reynolds = np.logspace(start=0, stop=2.8)
D_hydraulic = np.logspace(start=-4.93, stop=-2.93)
A_channel = D_hydraulic**2
Reynolds_2 = fp.get_Reynolds_from_mass_flow(T=T_liquid,
                                            p=p,
                                            L_ref=D_hydraulic,
                                            m_dot=m_dot,
                                            A=A_channel)
# Range of hydraulic diameter that fit this Reynolds number
#w_channel = 2*m_dot/( Reynolds_100 * mu_liquid ) - h_c # [m]
#A_channel = h_c * w_channel # [m^2]
#D_hydraulic = basic.chamber.hydraulic_diameter_rectangular(w_channel=w_channel,h_channel=h_c) # [m]
Beispiel #6
0
        m_dot[it_AR.index][it_T.index] = ep['m_dot']  # [kg/s] Mass flow
        A_throat[it_AR.index][it_T.index] = ep['A_throat']  # [m^2] Throat area
        Isp[it_AR.index][it_T.index] = ep['Isp']  # [s] Specific impulse
        h_chamber[it_AR.index][it_T.index] = fp.get_enthalpy(
            T=float(T), p=p_chamber)  # [J/kg] Enthalpy at chamber inlet
        # Throat stuff
        T_throat[it_AR.index][it_T.index] = ep[
            'T_throat']  # [K] Throat temperature
        p_throat[it_AR.index][it_T.index] = ep[
            'p_throat']  # [Pa] Throat pressure
        u_throat[it_AR.index][it_T.index] = ep[
            'u_throat']  # [K] Throat velocity
        Pr_throat[it_AR.index][it_T.index] = fp.get_Prandtl(
            T=ep['T_throat'], p=ep['p_throat'])  # [-] Prandtl number in throat
        mu_throat[it_AR.index][it_T.index] = fp.get_viscosity(
            T=ep['T_throat'],
            p=ep['p_throat'])  # [Pa*s] Dynamic viscosity in the throat
        # Nozzle exit stuff
        T_exit[it_AR.index][it_T.index] = ep[
            'T_exit']  # [K] Nozzle exit temperature
        p_exit[it_AR.index][it_T.index] = ep[
            'p_exit']  # [Pa] Nozzle exit pressure

# Calculate ideal power
h_inlet = fp.get_enthalpy(T=T_inlet,
                          p=p_chamber)  # [J/kg] Enthalpy at chamber inlet
delta_h = h_chamber - h_inlet  # [J/kg] Enthalpy change between inlet and chamber
Pt_ideal = m_dot * delta_h  # [W] Total ideal power consumption (no heat losses)

## Plot the results