def test_table(self): # Test reported values of gamma by taking cp/cv from Span2000 (page 1410 and below) # Test if the table of the original paper can be reproduced Span2000 # Page 1410 of Span2000 fp = FluidProperties("nitrogen") pressure = 1e5 # 1 bar of pressure # T in, gamma out # gamma not explicitly in table, but cp/cv is put in here instead in_out = ((300, 1.402784445511282, 2), (330, 1.4021113243761996, 2), (450, 1.3956356736242885, 3), (600, 1.3821100917431193, 3), (1000, 1.3411234112341124, 3)) for T, gamma, places in in_out: res_gamma = fp.get_specific_heat_ratio(T=T, p=pressure) self.assertAlmostEqual(res_gamma, gamma, places=places) # Again but with different pressure pressure = 1e6 # 10 bar of pressure # T in, gamma out # gamma not explicitly in table, but cp/cv is put in here instead in_out = ((300, 1.41666666666666672, 2), (330, 1.4126376256582096, 2), (450, 1.400947867298578, 3), (600, 1.3846859238881248, 3), (1000, 1.3419434194341942, 3)) for T, gamma, places in in_out: res_gamma = fp.get_specific_heat_ratio(T=T, p=pressure) self.assertAlmostEqual(res_gamma, gamma, places=places)
def plotGammaCurve(): fp = FluidProperties("water") temperature = np.linspace(start=500, stop=1000, num=200) pressure = np.linspace(start=1.0e5, stop=5.0e5, num=3) gamma = np.zeros((pressure.size, temperature.size)) p_iter = np.nditer(pressure, flags=['c_index']) T_iter = np.nditer(temperature, flags=['c_index']) for p in p_iter: i = p_iter.index T_iter.reset() for T in T_iter: j = T_iter.index gamma[i, j] = fp.get_specific_heat_ratio(T=float(T), p=float(p)) plt.figure() p_iter.reset() for p in p_iter: i = p_iter.index plt.plot(temperature, gamma[i, :], label="{:1.0f} bar".format(p * 1e-5)) plt.xlabel("Temperature - $T$ [K]") plt.ylabel("Specific heat ratio - $\\gamma$ [-] ") plt.grid() plt.legend(title="Pressure") plt.title("Specific heat ratio of water") plt.tight_layout()
def engine_performance_from_F_and_T(F_desired, p_chamber, T_chamber, AR_exit, p_back, fp: FluidProperties): """ Returns Nozzle Inlet State based on IRT Args: F_desired (N): Desired thrust p_chamber (Pa): Chamber thrust/nozzle inlet pressure T_chamber (K): Chamber temperature AR_exit (-): A_exit / A_throat p_back (Pa): Back pressure fp (FluidProperties): Used to access fluid properties Raises: Exception: Raised when no solution is found for given input Returns: dict{A_throat [m^2], m_dot [kg/s]}: Throat area and mass flow """ # Default range for temperature for root-finding algorithm A_low = 1e-22 # [K] A_high = 1e-3 # [K] R = fp.get_specific_gas_constant() # [J/(kg*K)] gamma = fp.get_specific_heat_ratio(T_chamber, p_chamber) # [-] Specific heat ratio # If x is zero, then the desired thrust is found. Gamma is changed depending on temperature as well x = lambda A_t: F_desired - get_engine_performance(p_chamber=p_chamber, T_chamber=T_chamber, A_throat=A_t, \ AR_exit=AR_exit,p_back=p_back, gamma=gamma,R=R)['thrust'] root_result = optimize.root_scalar(x, bracket=[A_low, A_high], xtol=1e-12) if root_result.converged: A_throat = root_result.root # [m^2] else: raise Exception("No solution found for given temperature") # Now the chamber temperature is known, return the unknown parameters ep = get_engine_performance(p_chamber=p_chamber, T_chamber=T_chamber, A_throat=A_throat, \ AR_exit=AR_exit,p_back=p_back, gamma=fp.get_specific_heat_ratio(T_chamber,p_chamber),R=R) # Add throat area to dictionary ep['A_throat'] = A_throat # This is usually an input for get_engine_performance, but now added to make it one complete solution #ep['Isp'] = IRT.effective_ISP(thrust=F_desired,m_dot=ep['m_dot']) # [s] Effective specific impulse return ep
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 }
td = thrusters.thruster_data.Cen2010_6 # Design paramaters fp = FluidProperties(td['propellant']) # Object to access fluid properties with p_c = 5e5#td['p_inlet'] # [bar] Chamber pressure T_c = 600#td['T_chamber_guess'] # [K] Chamber temperature h_channel = td['h_channel'] # [m] Channel/nozzle depth w_throat = td['w_throat'] # [m] Throat width AR_exit = 10 #td['AR_exit'] # [-] Exit area ratio p_back = 0# td['p_back'] # [Pa] Atmospheric pressire print("Chamber temperature: {:3.2f} K".format(T_c)) # Calculate throat area, and propellant properties A_throat = h_channel*w_throat # [m^2] Thrpat area gamma = fp.get_specific_heat_ratio(T=T_c, p=p_c) # [-] Get gamma at specified gas constant R = fp.get_specific_gas_constant() # [J/(kg*K)] Specific gas constant ep = IRT.get_engine_performance(p_chamber=p_c, T_chamber=T_c, A_throat=A_throat, AR_exit=AR_exit, p_back=p_back, gamma=gamma, R=R) print("\n --- IRT predictions --- ") print("Isp: {:3.2f} s".format(ep['Isp'])) print("Gamma: {:1.3f} ".format(gamma)) print("Mass flow: {:2.3f} mg/s".format(ep['m_dot']*1e6)) print("Thrust: {:2.3f} mN".format(ep['thrust']*1e3)) zeta_CF = td['F']/ep['thrust'] Isp_real = td['F']/td['m_dot']/g0 zeta_Isp = Isp_real/ep['Isp'] discharge_factor = td['m_dot']/ep['m_dot'] print('\n --- Experimental values ---')
print("Minimum mass flow: {:2.2f} mg/s".format(m_dot_min * 1e6)) print("Maximum mass flow: {:2.2f} mg/s".format(m_dot_max * 1e6)) # Print results of first point for debugging purposes print("\nResults of first element in each array") print("------ AR = {:3.1f} T_c = {:3.2f} K".format( AR_exit[0], T_chamber[0])) print("Throat area: {} m^2".format(A_throat[0][0])) print("D_h: {} m".format(D_hydraulic_throat[0][0])) print("Throat width: {} m".format(w_throat[0][0])) print("Throat height {} ".format(h_channel)) print("Wetted perimeter: {} m".format(wetted_perimeter[0][0])) print("Mass flow: {} kg/s".format(m_dot[0][0])) print("Throat velocity: {} m/s".format(u_throat[0][0])) print("Gamma inlet {}".format( fp.get_specific_heat_ratio(T=T_chamber[0], p=p_chamber))) print("Gamma throat {} ".format( fp.get_specific_heat_ratio(T=T_throat[0][0], p=p_throat[0][0]))) print("T_throat: {} K".format(T_throat[0][0])) print("p_throat: {} Pa".format(p_throat[0][0])) print("T_exit: {} K".format(T_exit[0][0])) print("p_exit: {} Pa".format(p_exit[0][0])) # Alternative calculatoin of Reynolds number Re_throat_alt = m_dot[0][0] / A_throat[0][0] * D_hydraulic_throat[0][ 0] / mu_throat[0][ 0] # [-] Alternative calc. of Reynodls number m_dot/A instead of rho*u print("ALT RE: {}".format(Re_throat_alt)) print("Re_throat {} ".format(Re_throat[0][0])) fig.legend(title="Exit area ratio $\\frac{A_e}{A_t}$", loc='lower right')