def f(x): M4, M5 = x MFP5 = mach2mfp(M5, gam) MFP4 = mach2mfp(M4, gam) sqrt_T0_ratio = MFP5 / MFP4 * A_ratio * P0_ratio phi4 = MFP4 / M0rotor * (1 + (gam - 1) / 2 * M4**2)**(1 / (gam - 1)) phi5 = MFP5 / M0rotor * (1 + (gam - 1) / 2 * M5**2)**( 1 / (gam - 1)) * sqrt_T0_ratio psi = 1 + phi5 * tan(beta5) - phi4 * tan(alfa4) P0_ratio_new = (1 + (gam - 1) * psi * M0rotor**2)**(gam / (gam - 1)) err_P0 = P0_ratio_new - P0_ratio err_MFP = MFP4 / MFP5 - A_ratio * P0_ratio**((2 * gam - 1) / (gam)) return P0_ratio_new, err_MFP, psi
def thrust(self, **params): gam_t = self.turbine.gam R_t = self.R_t MFP8 = min(params['MFP5'] * self.turbine.geom['A2'] / self.A8, mach2mfp(1, gam_t)) mdot = params['MFP5'] * self.turbine.geom['A2'] * params[ 'P05'] * gam_t**0.5 / (params['T05'] * R_t)**0.5 M8 = mfp2mach(MFP8, gam_t) P8 = params['P05'] * (1 + (gam_t - 1) / 2 * M8**2)**(-gam_t / (gam_t - 1)) mdot5 = params['MFP5'] * self.turbine.geom['A2'] * params[ 'P05'] * gam_t**0.5 / (params['T05'] * R_t)**0.5 T8 = params['T05'] * (1 + (gam_t - 1) / 2 * M8**2)**(-1) return mdot * M8 * (gam_t * R_t * T8)**0.5 + ( P8 - self.P01 / (1 + (gam_t - 1) * params['M_flight']**2)**0.5) * self.A8
# coding: utf-8 from turbine import * t = TurbineExtendedMap() from mfp2mach import mach2mfp import matplotlib.pyplot as plt MFP_choke = mach2mfp(1, t.gam) sol_cr = t.general_explicit_map({'MFP1': MFP_choke, 'MFP2': MFP_choke}) sol_almost_cr = t.general_explicit_map({'MFP1': 0.4, 'MFP2': 0.4}) print(sol_almost_cr) t.initial_guess = sol_almost_cr.params fig, (ax1, ax2) = plt.subplots(1, 2) ax1.plot(sol_cr.params['MFP1'], sol_cr.params['P0_ratio'], 's') ax1.plot(sol_almost_cr.params['MFP1'], sol_almost_cr.params['P0_ratio'], 's') assert sol_almost_cr.success print(sol_cr) sol = sol_almost_cr for MbMFP in reversed(np.linspace(0, sol_cr.params['MbMFP'])): sol = t.general_explicit_map({ 'MbMFP': MbMFP, 'MFP1': MFP_choke }, initial_guesses=sol.params) ax1.plot(sol.params['MbMFP'], 1 / sol.params['P0_ratio'], '+') ax2.plot(sol.params['MFP1'], sol.params['MFP2'], '+') if not sol.success: break
import numpy as np import matplotlib.pyplot as plt from mfp2mach import mach2mfp import tccsty gam = 1.3 mach = np.linspace(0, 4, 100) mfp = mach2mfp(mach, gam) plt.rcParams.update({ 'axes.spines.left': True, # display axis spines 'axes.spines.bottom': True, 'axes.spines.top': False, 'axes.spines.right': False, 'axes.labelpad': 2, }) plt.figure(figsize=(2, 1.5)) plt.tick_params(bottom=True, top=False, left=True, right=False) plt.plot(mach, mfp, 'k', lw=tccsty.thick) plt.xlim(0, 4) plt.ylim(0, 0.6) plt.xlabel('M') plt.ylabel('MFP') plt.xticks(range(5)) plt.savefig('mfp_fig.pdf') plt.plot(2 * [1], [0, mach2mfp(1, gam)], 'r--') plt.plot([0, 1], 2 * [mach2mfp(1, gam)], 'r--')
def implicit_map( self, M_flight, # flight mach number MFP, MFP3, Mb_c, T0_ratio_c, P0_ratio_c, # compressor mdotf, T04, # burner MFP4, MFP5, Mb_t, T0_ratio_t, P0_ratio_t # turbine ): """ This function implements burner, nozzle and spool dynamics and integrates it with turbine and compressor maps Variable balance: 12 variables - 11 equations -------- 2 free choices (e.g. M_flight and mdotf) """ # Expose constants A8 = self.A8 FHV = self.FHV eta_combustion = self.eta_combustion A5 = self.turbine.geom['A2'] gam_c = self.compressor.gam gam_t = self.turbine.gam cpc = self.cpc cpt = self.cpt T01 = self.T01 P01 = self.P01 R_c = self.R_c R_t = self.R_t ### Dimensionalize everything ### dim_params = self.dimensionalize( M_flight, # flight mach number MFP, MFP3, Mb_c, T0_ratio_c, P0_ratio_c, # compressor mdotf, T04, # burner MFP4, MFP5, Mb_t, T0_ratio_t, P0_ratio_t # turbine ) T02 = dim_params.T02 T03 = dim_params.T03 T05 = dim_params.T05 P02 = dim_params.P02 P03 = dim_params.P03 P04 = dim_params.P04 P05 = dim_params.P05 omega_c = dim_params.omega_c omega_t = dim_params.omega_t mdot2 = dim_params.mdot2 mdot3 = dim_params.mdot3 mdot4 = dim_params.mdot4 mdot5 = dim_params.mdot5 ### CONSTRAINTS ### # * Energy addition in ther burner res_T04 = T03 + mdotf * FHV * eta_combustion / (mdot3 * cpc) - T04 # * Spool has constant speed res_omega = omega_c - omega_t # * Conservation of energy in the spool res_energy = mdot2 * cpc * (T03 - T02) - mdot4 * cpt * (T04 - T05) # * Consevation of mass in the combustor res_mdot = mdot3 + mdotf - mdot4 # * Nozzle exit is either choked or at ambient pressure Pa = P01 / (1 + (gam_c - 1) / 2 * M_flight**2)**(gam_c / (gam_c - 1)) MFP8 = MFP5 * A5 / A8 res_MFP_nozzle = (P05 / Pa - 1) * MFP8 - (P05 / Pa - 1) * mach2mfp( min(1, (2 / (gam_t - 1) * ((P05 / Pa)**( (gam_t - 1) / gam_t) - 1))**0.5) if P05 / Pa > 1 else 0, gam_t) ### remove dimensions of residuals ### # References for removing dimensions of residuals mdotref = (T01 * R_c)**0.5 / (P01 * gam_c**0.5 * self.compressor.geom['A1']) h_ref = cpc * T01 omega_ref = (gam_c * R_c * T01)**0.5 / self.compressor.geom['D2'] #remove dimensions res_omega /= omega_ref res_energy /= h_ref * mdotref res_mdot /= mdotref res_T04 /= T04 ### COMPRESSOR MAP ### res_MFP_c, res_T0_ratio_c, res_P0_ratio_c = self.compressor.implicit_map( MFP, MFP3, Mb_c, T0_ratio_c, P0_ratio_c, tol=1e-13) ### TURBINE MAP ### res_MFP_t, res_T0_ratio_t, res_P0_ratio_t = self.turbine.implicit_map( MFP4, MFP5, Mb_t, T0_ratio_t, P0_ratio_t, tol=1e-13) return (res_omega, res_energy, res_mdot, res_MFP_nozzle, res_T04, res_MFP_c, res_T0_ratio_c, res_P0_ratio_c, res_MFP_t, res_T0_ratio_t, res_P0_ratio_t)
def dimensionalize( self, M_flight, # flight mach number MFP, MFP3, Mb_c, T0_ratio_c, P0_ratio_c, # compressor mdotf, T04, # burner MFP4, MFP5, Mb_t, T0_ratio_t, P0_ratio_t # turbine ): gam_c = self.compressor.gam gam_t = self.turbine.gam T01 = self.T01 P01 = self.P01 R_c = self.R_c R_t = self.R_t ### Dimensionalize everything ### a01 = (gam_c * R_c * T01)**0.5 a04 = (gam_t * R_t * T04)**0.5 T02 = T01 T03 = T02 * T0_ratio_c T04 = T04 T05 = T04 * T0_ratio_t P02 = P01 P03 = P02 * P0_ratio_c P04 = P03 P05 = P03 * P0_ratio_t omega_c = Mb_c * a01 / (self.compressor.geom['D2'] / 2) omega_t = Mb_t * a04 / (self.turbine.geom['D1t'] / 2) mdot2 = MFP * self.compressor.geom['A1'] * P01 * gam_c**0.5 / ( T01 * R_c)**0.5 mdot3 = MFP3 * self.compressor.geom['A2'] * P03 * gam_c**0.5 / ( T03 * R_c)**0.5 mdot4 = MFP4 * self.turbine.geom['A1'] * P04 * gam_t**0.5 / (T04 * R_t)**0.5 mdot5 = MFP5 * self.turbine.geom['A2'] * P05 * gam_t**0.5 / (T05 * R_t)**0.5 #Nozzle MFP8 = min(MFP5 * self.turbine.geom['A2'] / self.A8, mach2mfp(1, gam_t)) M8 = mfp2mach(MFP8, gam_t) P8 = P05 * (1 + (gam_t - 1) / 2 * M8**2)**(-gam_t / (gam_t - 1)) T8 = T05 * (1 + (gam_t - 1) / 2 * M8**2)**(-1) dim_params = DimensionalParameters(T02=T02, T03=T03, T04=T04, T05=T05, T8=T8, P02=P02, P03=P03, P04=P04, P05=P05, P8=P8, omega_c=omega_c, omega_t=omega_t, mdot2=mdot2, mdot3=mdot3, mdot4=mdot4, mdot5=mdot5) return dim_params
def plot_map(self, ax, extent=None, samples=25, grid=False, choke_line=True): if extent is None: xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() else: xmin, xmax, ymin, ymax = extent #Choke line if choke_line: MFP_choke = mach2mfp(1, self.gam) sol = self.general_explicit_map({ 'MFP1': 0.9 * MFP_choke, 'MFP2': 0.9 * MFP_choke }) MbMFP = np.linspace(sol.params['MbMFP'], xmax, samples * 5) pr_choke = np.empty_like(MbMFP) for i, MbMFP_ in enumerate(MbMFP): if sol.success: old_sol = sol sol = self.general_explicit_map( { 'MbMFP': MbMFP_, 'MFP2': MFP_choke }, initial_guesses=old_sol.params) pr_choke[i] = sol.params['P0_ratio'] if sol.success else np.nan ax.plot(MbMFP, 1 / pr_choke, 'k--', linewidth=0.8) #Map MbMFP, P0_ratio = np.meshgrid(np.linspace(xmin, xmax, samples), np.linspace(1 / ymax, 1 / ymin, samples)) params = gridmap(self, MbMFP, P0_ratio, 'MbMFP') params['eff'] = (self.gam - 1) / self.gam * np.log(P0_ratio) / np.log( params['T0_ratio']) if grid: ax.plot(MbMFP, 1 / P0_ratio, 'k,') CS = ax.contour(MbMFP, 1 / P0_ratio, params['Mb'], levels=np.arange(0, 2, 0.1), colors='k', linewidths=tccsty.thick) ax.clabel(CS, CS.levels, fmt='%.1f', rightside_up=False) CS.collections[0].set_label('$M_{bt}$') #CS2 = ax.contour(MbMFP, 1/P0_ratio, params['eff'], colors='k', linewidths=0.4, # levels=[0.5,0.8,0.9,0.95]) #CS2.collections[0].set_label(r'$\eta_p$') #ax.clabel(CS2, CS2.levels, fmt='%.2f') ax.set_xlabel(r"$M_{bt}\text{MFP}_4$") ax.set_ylabel(r"\[\frac{P_{04}}{P_{05}}\]", labelpad=16, rotation=0)
def plot_map(self, ax, P0_min=1, P0_max=4, samples=25, plot=False, grid=False, choke=True): MFP_choke = mach2mfp(1, self.gam) sol_cr = self.general_explicit_map({ 'MFP1': MFP_choke, 'MFP2': MFP_choke }) sol_P1 = self.general_explicit_map({ 'MFP2': MFP_choke, 'P0_ratio': P0_min }) def MFP_max(P0_ratio): MFP = np.interp( P0_ratio, [sol_P1.params['P0_ratio'], sol_cr.params['P0_ratio']], [sol_P1.params['MFP1'], sol_cr.params['MFP1']]) return MFP P0_ratio = np.linspace(P0_min, P0_max, samples) P0_grid = np.empty((samples, samples)) MFP_grid = np.empty_like(P0_grid) for i, p in enumerate(P0_ratio): MFP = np.linspace(1e-6, MFP_max(p), samples) MFP_grid[:, i] = MFP P0_grid[:, i] = p params = gridmap(self, MFP_grid, P0_grid, 'MFP1', plot=plot) params['eff'] = (self.gam - 1) / self.gam * np.log(P0_grid) / np.log( params['T0_ratio']) if grid: ax.plot(MFP_grid, P0_grid, 'k,') #Add choke 20% before maximum rpm if choke: index_cr = np.argmin(params['Mb'], axis=0) for i, i_cr in enumerate(np.ceil(0.8 * index_cr).astype(int)): params['Mb'][:i_cr, i] = np.nan params['eff'][:i_cr, i] = np.nan CS = ax.contour(MFP_grid, P0_grid, params['Mb'], levels=np.arange(0, 2, 0.1), colors='k', linewidths=tccsty.thick) ax.clabel(CS, CS.levels, fmt='%.1f') #CS.collections[0].set_label('$M_{bc}$') CS2 = ax.contour(MFP_grid, P0_grid, params['eff'], colors='k', linewidths=tccsty.thin, levels=np.arange(0.0, 1.01, 0.05)) #CS2.collections[0].set_label(r'$\eta_p$') ax.clabel(CS2, CS2.levels[-5:], fmt='%.2f') ax.plot([MFP_choke, MFP_choke, sol_P1.params['MFP1']], [P0_max, sol_cr.params['P0_ratio'], P0_min], 'k--') #, label='choke limit') ax.set_xlim((0, 0.6)) ax.set_ylim((1, 4)) ax.set_xlabel("MFP") ax.set_ylabel(r"\[\frac{P_{03}}{P_{02}}\]", labelpad=16, rotation=0)