def NPR_shock_at_exit(AsAc): """Compute Nozzle Pressure Ratio to get a choked, supersonic regime but shock at exit in a nozzle with As/Ac diffuser Args: AsAc ([real]): ratio of exit over throat surfaces Returns: [real]: Nozzle Pressure ratio (inlet total pressure over exit static pressure) """ Msup = mf.MachSup_Sigma(AsAc) Msh = sw.downstream_Mn(Msup) return Is.PtPs_Mach(Msh) / sw.Pi_ratio(Msup)
def _rankinehugoniot_from_ushock(self, ushock): """ returns Rankine-Hugoniot state, given a shock velocity ..warning:: this function is made private because there is no test about upstream/downstream consistency nor the right ushock range (greater than u+a or lesser than u-a """ loc_Mn = (self.u-ushock)/self.asound() # this Mach number is signed rho_ratio = sw.Rho_ratio(loc_Mn, self._gamma) return unsteady_state(self.rho * rho_ratio, ushock + (self.u-ushock)/rho_ratio, self.p * sw.Ps_ratio(loc_Mn, self._gamma), gamma=self._gamma)
def Madapt_from_AsAc_NPR(AsAc, NPR): """ Computes Mach number for pressure adapted flow of a nozzle given As/Ac and NPR This method checks the NPR to define regime and computes Mach number in jet. The switch between overexpanded jet and underexpanded jet is :param AsAc: ratio of section at exit over throat :param NPR: ratio of total pressure at inlet over 'expected' static pressure at exit :return: result Mach number at exit :Example: >>> print round(Ms_from_AsAc_NPR(2.636, 1.5), 8) # case with shock in diffuser 0.32586574 .. seealso:: .. note:: NOT available for array (numpy) computations """ NPR0, NPRsw, NPR1, Msub, Msh, Msup = _NPR_Ms_list(AsAc) if (NPR < NPR0): Ms = Is.Mach_PtPs(NPR) elif (NPR > NPR1): # under expanded flow Ms = Is.Mach_PtPs(NPR) elif (NPR > NPRsw): # shock wave in jet Ms = sw.downstreamMach_Mach_ShockPsratio(Msup, NPR1 / NPR) else: gmu = defg._gamma - 1. K = NPR / AsAc / ((defg._gamma + 1.) / 2)**( (defg._gamma + 1.) / 2 / gmu) Ms = np.sqrt((np.sqrt(1. + 2. * gmu * K * K) - 1.) / gmu) return Ms
def set_NPR(self, NPR): """ Define Nozzle Pressure Ratio (inlet Ptot over outlet Ps) for this case Define Nozzle pressure ratio and compute Mach number, Ptot and Ps according to nozzle regime :param NPR: NPR value (>1) """ self._Pt = np.ones_like(self.AxoAc) if NPR < self.NPR0: _Ms = Is.Mach_PtPs(NPR, gamma=self.gamma) self._M = mf.MachSub_Sigma(self.AxoAc / self.AsoAc * mf.Sigma_Mach(_Ms), gamma=self.gamma) self._Ps = self._Pt / Is.PtPs_Mach(self._M, gamma=self.gamma) else: self._M = np.ones_like(self.AxoAc) self._M[:self.ithroat + 1] = mf.MachSub_Sigma( self.AxoAc[:self.ithroat + 1], gamma=self.gamma) self._M[self.ithroat + 1:] = mf.MachSup_Sigma( self.AxoAc[self.ithroat + 1:], gamma=self.gamma) if NPR < self.NPRsw: # analytical solution for Ms, losses and upstream Mach number of shock wave Ms = Ms_from_AsAc_NPR(self.AsoAc, NPR) Ptloss = Is.PtPs_Mach(Ms) / NPR Msh = sw.Mn_Pi_ratio(Ptloss) # redefine curves starting from 'ish' index (closest value of Msh in supersonic flow) ish = np.abs(self._M - Msh).argmin() self._M[ish:] = mf.MachSub_Sigma( self.AxoAc[ish:] * mf.Sigma_Mach(Ms) / self.AsoAc) self._Pt[ish:] = Ptloss self._Ps = self._Pt / Is.PtPs_Mach(self._M)
def plot_theta_sigma(mach, gamma=defg._gamma, npts=100, curve='both', devmax=False, sonic=False, color='k', linestyle='-', **kwargs): """ Plot shock polar curve in deviation / shock angle axes Long comment :param mach: upstream Mach number :param gamma: specific heat ratio, default from aerokit.common.defaultgas :param npts: number of computed points, curve accuracy :param curve: choose which curve to plot (left, right or both) :return: :Example: .. seealso:: .. note:: """ sig = np.linspace(deg.asin(1. / mach), 90., npts + 1) dev = sw.deflection_Mach_sigma(mach, sig, gamma) if curve in ['right', 'both']: plt.plot(dev, sig, color=color, linestyle=linestyle, **kwargs) if curve in ['left', 'both']: plt.plot(-dev, sig, color=color, linestyle=linestyle, **kwargs) if devmax: thet = sw.dev_Max(mach, gamma=gamma) sig = sw.sigma_DevMax(mach, gamma=gamma) if curve in ['right', 'both']: plt.plot(thet, sig, 'ro', alpha=0.9) if curve in ['left', 'both']: plt.plot(-thet, sig, 'ro', alpha=0.9) if sonic: thet = sw.dev_Sonic(mach, gamma=gamma) sig = sw.sigma_Sonic(mach, gamma=gamma) if curve in ['right', 'both']: plt.plot(thet, sig, 'ko', markerfacecolor='white') if curve in ['left', 'both']: plt.plot(-thet, sig, 'ko', markerfacecolor='white')
def test_shockwave_Ps(): assert sw.Ps_ratio(1.) == 1. assert sw.Ps_ratio(1.1) == pytest.approx(1.245) assert sw.Ps_ratio(2.) == pytest.approx(4.5) assert sw.Ps_ratio(1., gamma=1.3) == 1. assert sw.Ps_ratio(1.1, gamma=1.3) == pytest.approx(1.2373913043478264) assert sw.Ps_ratio(2., gamma=1.3) == pytest.approx(4.391304347826088)
def test_shockwave_Mn1(): assert sw.downstream_Mn(1.) == 1. assert sw.downstream_Mn(1.1) == pytest.approx(0.9117704213259055) assert sw.downstream_Mn(2.) == pytest.approx(0.5773502691896257) assert sw.downstream_Mn(1., gamma=1.3) == 1. assert sw.downstream_Mn(1.1, gamma=1.3) == pytest.approx(0.911201472607656) assert sw.downstream_Mn(2., gamma=1.3) == pytest.approx(0.5628780357842335)
def test_shockwave_Ts(): assert sw.Ts_ratio(1.) == 1. assert sw.Ts_ratio(1.1) == pytest.approx(1.0649380165289257) assert sw.Ts_ratio(2.) == pytest.approx(1.6875) assert sw.Ts_ratio(1., gamma=1.3) == 1. assert sw.Ts_ratio(1.1, gamma=1.3) == pytest.approx(1.0506488150103892) assert sw.Ts_ratio(2., gamma=1.3) == pytest.approx(1.527410207939509)
def test_shockwave_Rho(): assert sw.Rho_ratio(1.) == 1. assert sw.Rho_ratio(1.1) == pytest.approx(1.1690821256038648) assert sw.Rho_ratio(2.) == pytest.approx(2.666666666666667) assert sw.Rho_ratio(1., gamma=1.3) == 1. assert sw.Rho_ratio(1.1, gamma=1.3) == pytest.approx(1.1777401608125266) assert sw.Rho_ratio(2., gamma=1.3) == pytest.approx(2.875)
def _NPR_Ms_list(AsAc): """ Computes all NPR limits and associated exit Mach number internal function :param AsAc: ratio of section at exit over throat :return: result NPR and Mach numbers :Example: >>> import aerokit.aero.MassFlow as mf ; mf.Sigma_Mach(Is.Mach_PtPs(np.array(_NPR_Ms_list(2.)[:3:2]))) array([ 2., 2.]) .. seealso:: NPR_choked_subsonic(), NPR_choked_supersonic(), NPR_shock_at_exit() .. note:: available for scalar or array (numpy) computations """ Msub = mf.MachSub_Sigma(AsAc) NPR0 = Is.PtPs_Mach(Msub) Msup = mf.MachSup_Sigma(AsAc) Msh = sw.downstream_Mn(Msup) NPRsw = Is.PtPs_Mach(Msh) / sw.Pi_ratio(Msup) NPR1 = Is.PtPs_Mach(Msup) return NPR0, NPRsw, NPR1, Msub, Msh, Msup
def set_NPR(self, NPR): """ Define Nozzle Pressure Ratio (inlet Ptot over outlet Ps) for this case :param NPR: NPR value (>1) """ self.NPR = NPR defg.set_gamma(self._gam) if NPR < self.NPR0: # flow is fully subsonic _Ms = Is.Mach_PiPs(NPR) _M = mf.MachSub_Sigma(self.section * mf.Sigma_Mach(_Ms) / self.section[-1]) _Pt = 0. * _M + NPR _Ps = _Pt / Is.PiPs_Mach(_M) else: # compute Mach, assumed to be subsonic before throat, supersonic after _Minit = 0. * self.section + .5 _Minit[self.ithroat:] = 2. _M = mf.Mach_Sigma(self.section / self.section[self.ithroat], Mach=_Minit) _Pt = NPR + 0. * _M # CHECK, there is a shock # analytical solution for Ms, losses and upstream Mach number of shock wave Ms = nz.Ms_from_AsAc_NPR(self.AsoAc, NPR) Ptloss = Is.PiPs_Mach(Ms) / NPR Msh = sw.Mn_Pi_ratio(Ptloss) # if NPR < self.NPRsw: # throat is choked, there may be a shock # redefine curves starting from 'ish' index (closest value of Msh in supersonic flow) ish = np.abs(_M - Msh).argmin() _M[ish:] = mf.MachSub_Sigma( self.section[ish:] * mf.Sigma_Mach(Ms) / self.section[-1]) _Pt[ish:] = Ptloss * NPR _Ps = _Pt / Is.PiPs_Mach(_M) # self._M = _M self._Pt = _Pt self._Ps = _Ps return
def set_NPR(NPR): if NPR < NPR0: _Ms = Is.Mach_PiPs(NPR, gamma=self.model.gamma) self._M = mf.MachSub_Sigma(self.AsoAc * mf.Sigma_Mach(Ma_col) / self.AsoAc[-1], gamma=self.model.gamma) self._Pt = 0. * coord_x + 1. self._Ps = _Pt / Is.PiPs_Mach(self._M, gamma=self.model.gamma) elif NPR < NPRsw: _M = mf.Mach_Sigma(Noz_AoAc, Mach=_Minit) # # analytical solution for Ms, losses and upstream Mach number of shock wave Ms = nz.Ms_from_AsAc_NPR(target_AoAc, NPR) Ptloss = Is.PiPs_Mach(Ms) / NPR Msh = sw.Mn_Pi_ratio(Ptloss) # # redefine curves starting from 'ish' index (closest value of Msh in supersonic flow) ish = np.abs(_M - Msh).argmin() _M[ish:] = mf.MachSub_Sigma(Noz_AoAc[ish:] * mf.Sigma_Mach(Ms) / target_AoAc) _Pt[ish:] = Ptloss _Ps = _Pt / Is.PiPs_Mach(_M)
def test_Mn_Ps(): assert sw.Ps_ratio(2.) == pytest.approx(4.5) assert sw.Mn_Ps_ratio(sw.Ps_ratio(3.)) == pytest.approx(3.)
fsol = solver.solve(finit, cfl, stop={"maxit": nit_super}, monitors=monitors) fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(14, 4)) monitors["residual"]["output"].semilogplot_it(ax=ax1) fsol[-1].plot('mach', style='-', axes=ax2) fsol[-1].plot('ptot', style='-', axes=ax3) finit.plot('mach', style='--', axes=ax2) finit.plot('ptot', style='--', axes=ax3) fig.show() # if verbose: solver.show_perf() res["init_residual"] = monitors["residual"]["output"]._value[-1] res["init_M9"] = fsol[-1].phydata("mach")[-1] # RESTART bcR = {"type": bctype, "p": 1.1 * sw.Ps_ratio(Msup, gam)} rhs = modeldisc.fvm(model, meshsim, muscl(vanleer), bcL=bcL, bcR=bcR) solver = rk3ssp(meshsim, rhs) try: fsol = solver.solve(fsol[-1], cfl, stop={"maxit": nit_tot}, monitors=monitors) solver.show_perf() res["simu_residual"] = monitors["residual"]["output"]._value[-1] res["simu_M9"] = fsol[-1].phydata("mach")[-1] # if verbose: print(res) except Exception: res["simu_residual"] = 1.0e9 res["simu_M9"] = 0.0
def plot_theta_pressure(mach, gamma=defg._gamma, npts=100, thet_init=0., p_init=1., curve='both', devmax=False, sonic=False, color='k', linestyle='-', ax=plt, **kwargs): """ Plot shock polar curve in deviation / pressure ratio axes Long comment :param mach: upstream Mach number :param gamma: specific heat ratio, default from aerokit.common.defaultgas :param npts: number of computed points, curve accuracy :param thet_init: upstream angle (shift the curve by this angle), default 0. :param p_init: reference pressure (shift the curve by this ratio), default 1. :param curve: choose which curve to plot (left, right or both) :return: :Example: .. seealso:: .. note:: """ sig = np.linspace(deg.asin(1. / mach), 90., npts + 1) dev = sw.deflection_Mach_sigma(mach, sig, gamma) ps = p_init * sw.Ps_ratio( mach * deg.sin(sig), gamma) # pressure ratio only depends on normal Mach number if curve in ['right', 'both']: ax.plot(thet_init + dev, ps, color=color, linestyle=linestyle, **kwargs) if curve in ['left', 'both']: ax.plot(thet_init - dev, ps, color=color, linestyle=linestyle, **kwargs) if devmax: thet = sw.dev_Max(mach, gamma=gamma) sig = sw.sigma_DevMax(mach, gamma=gamma) ps = sw.Ps_ratio(mach * deg.sin(sig), gamma=gamma) if curve in ['right', 'both']: ax.plot(thet_init + thet, p_init * ps, 'ro', alpha=0.9) if curve in ['left', 'both']: ax.plot(thet_init - thet, p_init * ps, 'ro', alpha=0.9) if sonic: thet = sw.dev_Sonic(mach, gamma=gamma) sig = sw.sigma_Sonic(mach, gamma=gamma) ps = sw.Ps_ratio(mach * deg.sin(sig), gamma=gamma) if curve in ['right', 'both']: ax.plot(thet_init + thet, p_init * ps, 'ko', markerfacecolor='white') if curve in ['left', 'both']: ax.plot(thet_init - thet, p_init * ps, 'ko', markerfacecolor='white')
FSalpha = np.log10(np.logspace(1., alphamax, npts + 1)) FSm4 = ray.SupMach_TiTicri(FSalpha / alphamax * ray.Ti_Ticri(M4max, gam), gam) FSpi4 = ray.Pi_Picri(FSm4, gam) / ray.Pi_Picri(M3sup, gam) FSpi3 = np.ones(npts + 1) FSm3 = M3sup * np.ones(npts + 1) ax[0].plot(FSalpha, FSpi3, '-', color='#ff0000') ax[1].plot(FSalpha, FSpi4, '-', color='#ff0000') ax[2].plot(FSalpha, FSm3, '-', color='#ff0000') # --- (CW) conventional working state # M8 is sonic so M4 is known CWm4 = mf.Mach_Sigma(A3A2 / A8A2, .1, gam) # look for subsonic value CWm3low = sw.downstream_Mn(M3sup, gam) alphamin = ray.Ti_Ticri(CWm4, gam) / ray.Ti_Ticri(CWm3low, gam) CWm3high = mf.Mach_Sigma(A3A2 * mf.Sigma_Mach(sw.downstream_Mn(M2, gam), gam), .1, gam) alphamax = ray.Ti_Ticri(CWm4, gam) / ray.Ti_Ticri(CWm3high, gam) print(" unstart of inlet throat for Ti4/Ti0 = %6.3f" % (alphamax)) print(" fully supersonic flow for Ti4/Ti0 = %6.3f" % (alphamin)) CWalpha = np.log10(np.logspace(alphamin, alphamax, npts + 1)) CWm3 = ray.SubMach_TiTicri(ray.Ti_Ticri(CWm4, gam) / CWalpha, gam) CWpi3 = mf.Sigma_Mach(CWm3, gam) / mf.Sigma_Mach(M2, gam) / A3A2 CWpi4 = CWpi3 * ray.Pi_Picri(CWm4, gam) / ray.Pi_Picri(CWm3, gam) CWm4 = np.ones(npts + 1) * CWm4 ax[0].plot(CWalpha, CWpi3, '-', color='#bb0000') ax[1].plot(CWalpha, CWpi4, '-', color='#bb0000')
def test_conical_shock_mach(): mach = sw.conical_Mach_walldeflection_sigma(30., 45.) assert mach == pytest.approx(2.2376, rel=1.e-4) # solution of iterative process
def test_conical_shock_sigma(): sig = sw.conical_sigma_Mach_walldeflection(2., 30.) assert sig == pytest.approx(48.079078, rel=1.e-4) # solution of iterative process
def test_conical_shock_deflection(): dev = sw.conical_deflection_Mach_sigma(2., 35.) assert dev == pytest.approx(16.5322, rel=1.e-4) # solution of iterative process
def test_polar_iterative_vs_cubic_strong(M0, dev): sig_it = sw.sigma_Mach_deflection(M0, dev, init=80.) sig_cub = sw.strongsigma_Mach_deflection(M0, dev) assert sig_it == pytest.approx(sig_cub)
def test_shockwave_Mn1_involutive_numpy(): m = np.linspace(1., 10., 30) np.testing.assert_allclose(m, sw.downstream_Mn(sw.downstream_Mn(m)))
def test_polar_iterative_vs_cubic_weak(M0, dev): sig_it = sw.sigma_Mach_deflection(M0, dev) # default is weak shock sig_cub = sw.weaksigma_Mach_deflection(M0, dev) assert sig_it == pytest.approx(sig_cub)
def test_Ptshock(): assert sw.Pt_ratio(2.) == sw.Pi_ratio(2.)