コード例 #1
0
ファイル: test_cea_obj.py プロジェクト: t963212321/RocketCEA
    def test_get_PcOvPe(self):
        """ test call to get_PcOvPe( Pc=100.0, MR=1.0, eps=40.0) """
        C = CEA_Obj(oxName="LOX", fuelName="MMH", fac_CR=None)
        PcOvPe = C.get_PcOvPe(Pc=100.0, MR=1.0, eps=40.0)

        self.assertAlmostEqual(PcOvPe, 550.3111877518063, places=3)

        del C
コード例 #2
0
    def test_get_PcOvPe(self):
        """ test call to get_PcOvPe( Pc=100.0, MR=1.0, eps=40.0) """
        C = CEA_Obj(oxName="LOX", fuelName="MMH", fac_CR=2.5)
        PcOvPe = C.get_PcOvPe(Pc=100.0, MR=1.0, eps=40.0)

        self.assertAlmostEqual(PcOvPe, 568.3567593758571, places=3)

        del C
コード例 #3
0
    def test_froz_get_PcOvPe(self):
        """ test call to get_PcOvPe( Pc=100.0, MR=1.0, eps=40.0) """
        C = CEA_Obj(oxName="LOX", fuelName="MMH", fac_CR=None)
        PcOvPe = C.get_PcOvPe(Pc=100.0,
                              MR=1.0,
                              eps=40.0,
                              frozen=1,
                              frozenAtThroat=0)

        self.assertAlmostEqual(PcOvPe, 670.4054686991491, places=3)

        del C
コード例 #4
0
class CEA_Obj(object):
    """
    RocketCEA wraps the NASA FORTRAN CEA code to calculate Isp, cstar, and Tcomb
    
    This object wraps the English unit version of CEA_Obj to enable desired user units.    
    """
    def __init__(self,
                 propName='',
                 oxName='',
                 fuelName='',
                 useFastLookup=0,
                 makeOutput=0,
                 isp_units='sec',
                 cstar_units='ft/sec',
                 pressure_units='psia',
                 temperature_units='degR',
                 sonic_velocity_units='ft/sec',
                 enthalpy_units='BTU/lbm',
                 density_units='lbm/cuft',
                 specific_heat_units='BTU/lbm degR',
                 viscosity_units='millipoise',
                 thermal_cond_units='mcal/cm-K-s',
                 fac_CR=None,
                 make_debug_prints=False):
        """::
        
        #: RocketCEA wraps the NASA FORTRAN CEA code to calculate Isp, cstar, and Tcomb
        #: This object wraps the English unit version of CEA_Obj to enable desired user units.
        #: Same as CEA_Obj with standard units except, input and output units can be specified.
        #:  parameter             default             options
        #: isp_units            = 'sec',         # N-s/kg, m/s, km/s
        #: cstar_units          = 'ft/sec',      # m/s
        #: pressure_units       = 'psia',        # MPa, KPa, Pa, Bar, Atm, Torr
        #: temperature_units    = 'degR',        # K, C, F
        #: sonic_velocity_units = 'ft/sec',      # m/s
        #: enthalpy_units       = 'BTU/lbm',     # J/g, kJ/kg, J/kg, kcal/kg, cal/g
        #: density_units        = 'lbm/cuft',    # g/cc, sg, kg/m^3
        #: specific_heat_units  = 'BTU/lbm degR' # kJ/kg-K, cal/g-C, J/kg-K (# note: cal/g K == BTU/lbm degR)
        #: viscosity_units      = 'millipoise'   # lbf-sec/sqin, lbf-sec/sqft, lbm/ft-sec, poise, centipoise
        #: thermal_cond_units   = 'mcal/cm-K-s'  # millical/cm-degK-sec, BTU/hr-ft-degF, BTU/s-in-degF, cal/s-cm-degC, W/cm-degC
        #: fac_CR, Contraction Ratio of finite area combustor (None=infinite)
        #: if make_debug_prints is True, print debugging info to terminal.
        """

        self.isp_units = isp_units
        self.cstar_units = cstar_units
        self.pressure_units = pressure_units
        self.temperature_units = temperature_units
        self.sonic_velocity_units = sonic_velocity_units
        self.enthalpy_units = enthalpy_units
        self.density_units = density_units
        self.specific_heat_units = specific_heat_units
        self.viscosity_units = viscosity_units
        self.thermal_cond_units = thermal_cond_units
        self.fac_CR = fac_CR

        # Units objects for input/output (e.g. Pc and Pamb)
        self.Pc_U = get_units_obj('psia', pressure_units)

        # units of output quantities
        self.isp_U = get_units_obj('sec', isp_units)
        self.cstar_U = get_units_obj('ft/sec', cstar_units)
        self.temperature_U = get_units_obj('degR', temperature_units)
        self.sonic_velocity_U = get_units_obj('ft/sec', sonic_velocity_units)
        self.enthalpy_U = get_units_obj('BTU/lbm', enthalpy_units)
        self.density_U = get_units_obj('lbm/cuft', density_units)
        self.specific_heat_U = get_units_obj('BTU/lbm degR',
                                             specific_heat_units)
        self.viscosity_U = get_units_obj('millipoise', viscosity_units)
        self.thermal_cond_U = get_units_obj('mcal/cm-K-s', thermal_cond_units)

        self.cea_obj = CEA_Obj_default(propName=propName,
                                       oxName=oxName,
                                       fuelName=fuelName,
                                       useFastLookup=useFastLookup,
                                       makeOutput=makeOutput,
                                       fac_CR=fac_CR,
                                       make_debug_prints=make_debug_prints)
        self.desc = self.cea_obj.desc

    def get_IvacCstrTc(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        IspVac, Cstar, Tcomb = self.cea_obj.get_IvacCstrTc(Pc=Pc,
                                                           MR=MR,
                                                           eps=eps)

        IspVac = self.isp_U.dval_to_uval(IspVac)
        Cstar = self.cstar_U.dval_to_uval(Cstar)
        Tcomb = self.temperature_U.dval_to_uval(Tcomb)

        return IspVac, Cstar, Tcomb

    def getFrozen_IvacCstrTc(self,
                             Pc=100.0,
                             MR=1.0,
                             eps=40.0,
                             frozenAtThroat=0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        IspFrozen, Cstar, Tcomb = self.cea_obj.getFrozen_IvacCstrTc(
            Pc=Pc, MR=MR, eps=eps, frozenAtThroat=frozenAtThroat)
        IspFrozen = self.isp_U.dval_to_uval(IspFrozen)
        Cstar = self.cstar_U.dval_to_uval(Cstar)
        Tcomb = self.temperature_U.dval_to_uval(Tcomb)

        return IspFrozen, Cstar, Tcomb

    def get_IvacCstrTc_exitMwGam(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        IspVac, Cstar, Tcomb, mw, gam = self.cea_obj.get_IvacCstrTc_exitMwGam(
            Pc=Pc, MR=MR, eps=eps)

        IspVac = self.isp_U.dval_to_uval(IspVac)
        Cstar = self.cstar_U.dval_to_uval(Cstar)
        Tcomb = self.temperature_U.dval_to_uval(Tcomb)

        return IspVac, Cstar, Tcomb, mw, gam

    def get_IvacCstrTc_ChmMwGam(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        IspVac, Cstar, Tcomb, mw, gam = self.cea_obj.get_IvacCstrTc_ChmMwGam(
            Pc=Pc, MR=MR, eps=eps)

        IspVac = self.isp_U.dval_to_uval(IspVac)
        Cstar = self.cstar_U.dval_to_uval(Cstar)
        Tcomb = self.temperature_U.dval_to_uval(Tcomb)

        return IspVac, Cstar, Tcomb, mw, gam

    def get_IvacCstrTc_ThtMwGam(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        IspVac, Cstar, Tcomb, mw, gam = self.cea_obj.get_IvacCstrTc_ThtMwGam(
            Pc=Pc, MR=MR, eps=eps)

        IspVac = self.isp_U.dval_to_uval(IspVac)
        Cstar = self.cstar_U.dval_to_uval(Cstar)
        Tcomb = self.temperature_U.dval_to_uval(Tcomb)

        return IspVac, Cstar, Tcomb, mw, gam

    def __call__(self, Pc=100.0, MR=1.0, eps=40.0):
        return self.get_Isp(Pc=Pc, MR=MR, eps=eps)

    def get_Isp(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        IspVac = self.cea_obj.get_Isp(Pc=Pc, MR=MR, eps=eps)
        IspVac = self.isp_U.dval_to_uval(IspVac)

        return IspVac

    def get_Cstar(self, Pc=100.0, MR=1.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Cstar = self.cea_obj.get_Cstar(Pc=Pc, MR=MR)
        Cstar = self.cstar_U.dval_to_uval(Cstar)
        return Cstar

    def get_Tcomb(self, Pc=100.0, MR=1.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Tcomb = self.cea_obj.get_Tcomb(Pc=Pc, MR=MR)
        Tcomb = self.temperature_U.dval_to_uval(Tcomb)
        return Tcomb

    def get_PcOvPe(self, Pc=100.0, MR=1.0, eps=40.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_PcOvPe(Pc=Pc, MR=MR, eps=eps)

    def get_eps_at_PcOvPe(self, Pc=100.0, MR=1.0, PcOvPe=1000.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_eps_at_PcOvPe(Pc=Pc, MR=MR, PcOvPe=PcOvPe)

    def get_Throat_PcOvPe(self, Pc=100.0, MR=1.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_Throat_PcOvPe(Pc=Pc, MR=MR)

    def get_MachNumber(self, Pc=100.0, MR=1.0, eps=40.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_MachNumber(Pc=Pc, MR=MR, eps=eps)

    def get_Temperatures(self,
                         Pc=100.0,
                         MR=1.0,
                         eps=40.0,
                         frozen=0,
                         frozenAtThroat=0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        tempList = self.cea_obj.get_Temperatures(Pc=Pc,
                                                 MR=MR,
                                                 eps=eps,
                                                 frozen=frozen,
                                                 frozenAtThroat=frozenAtThroat)

        for i, T in enumerate(tempList):
            tempList[i] = self.temperature_U.dval_to_uval(T)
        return tempList  # Tc, Tthroat, Texit

    def get_SonicVelocities(self, Pc=100.0, MR=1.0, eps=40.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        sonicList = self.cea_obj.get_SonicVelocities(Pc=Pc, MR=MR, eps=eps)

        for i, S in enumerate(sonicList):
            sonicList[i] = self.sonic_velocity_U.dval_to_uval(S)
        return sonicList  # Chamber, Throat, Exit

    def get_Chamber_SonicVel(self, Pc=100.0, MR=1.0, eps=40.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        sonicVel = self.cea_obj.get_Chamber_SonicVel(Pc=Pc, MR=MR, eps=eps)

        sonicVel = self.sonic_velocity_U.dval_to_uval(sonicVel)
        return sonicVel

    def get_Enthalpies(self, Pc=100.0, MR=1.0, eps=40.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia

        hList = self.cea_obj.get_Enthalpies(Pc=Pc, MR=MR, eps=eps)
        for i, H in enumerate(hList):
            hList[i] = self.enthalpy_U.dval_to_uval(H)

        return hList

    def get_SpeciesMassFractions(self,
                                 Pc=100.0,
                                 MR=1.0,
                                 eps=40.0,
                                 frozen=0,
                                 frozenAtThroat=0,
                                 min_fraction=0.000005):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia

        molWtD, massFracD = self.cea_obj.get_SpeciesMassFractions(
            Pc=Pc,
            MR=MR,
            eps=eps,
            frozenAtThroat=frozenAtThroat,
            min_fraction=min_fraction)
        return molWtD, massFracD

    def get_SpeciesMoleFractions(self,
                                 Pc=100.0,
                                 MR=1.0,
                                 eps=40.0,
                                 frozen=0,
                                 frozenAtThroat=0,
                                 min_fraction=0.000005):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia

        molWtD, moleFracD = self.cea_obj.get_SpeciesMoleFractions(
            Pc=Pc,
            MR=MR,
            eps=eps,
            frozenAtThroat=frozenAtThroat,
            min_fraction=min_fraction)
        return molWtD, moleFracD

    def get_Chamber_H(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        H = self.cea_obj.get_Chamber_H(Pc=Pc, MR=MR, eps=eps)
        return self.enthalpy_U.dval_to_uval(H)

    def get_Densities(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        dList = self.cea_obj.get_Densities(Pc=Pc, MR=MR, eps=eps)

        for i, d in enumerate(dList):
            dList[i] = self.density_U.dval_to_uval(d)

        return dList

    def get_Chamber_Density(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        H = self.cea_obj.get_Chamber_Density(Pc=Pc, MR=MR, eps=eps)
        return self.density_U.dval_to_uval(H)

    def get_HeatCapacities(self, Pc=100.0, MR=1.0, eps=40.0, frozen=0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        cpList = self.cea_obj.get_HeatCapacities(Pc=Pc,
                                                 MR=MR,
                                                 eps=eps,
                                                 frozen=frozen)

        for i, cp in enumerate(cpList):
            cpList[i] = self.specific_heat_U.dval_to_uval(cp)

        return cpList

    def get_Chamber_Cp(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Cp = self.cea_obj.get_Chamber_Cp(Pc=Pc, MR=MR, eps=eps)
        return self.specific_heat_U.dval_to_uval(Cp)

    def get_Throat_Isp(self, Pc=100.0, MR=1.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Isp = self.cea_obj.get_Throat_Isp(Pc=Pc, MR=MR)
        Isp = self.isp_U.dval_to_uval(Isp)

        return Isp

    def get_Chamber_MolWt_gamma(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_Chamber_MolWt_gamma(Pc=Pc, MR=MR, eps=eps)

    def get_Throat_MolWt_gamma(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_Throat_MolWt_gamma(Pc=Pc, MR=MR, eps=eps)

    def get_exit_MolWt_gamma(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_exit_MolWt_gamma(Pc=Pc, MR=MR, eps=eps)

    def get_eqratio(self, Pc=100.0, MR=1.0, eps=40.0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        return self.cea_obj.get_eqratio(Pc=Pc, MR=MR, eps=eps)

    def getMRforER(self, ERphi=None, ERr=None):
        return self.cea_obj.getMRforER(ERphi=ERphi, ERr=ERr)

    def get_description(self):
        return self.cea_obj.get_description()

    def estimate_Ambient_Isp(self,
                             Pc=100.0,
                             MR=1.0,
                             eps=40.0,
                             Pamb=14.7,
                             frozen=0,
                             frozenAtThroat=0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Pamb = self.Pc_U.uval_to_dval(Pamb)  # convert user units to psia
        IspAmb, mode = self.cea_obj.estimate_Ambient_Isp(
            Pc=Pc,
            MR=MR,
            eps=eps,
            Pamb=Pamb,
            frozen=frozen,
            frozenAtThroat=frozenAtThroat)

        IspAmb = self.isp_U.dval_to_uval(IspAmb)

        return IspAmb, mode

    def get_PambCf(self, Pamb=14.7, Pc=100.0, MR=1.0, eps=40.0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Pamb = self.Pc_U.uval_to_dval(Pamb)  # convert user units to psia

        CFcea, CF, mode = self.cea_obj.get_PambCf(Pamb=Pamb,
                                                  Pc=Pc,
                                                  MR=MR,
                                                  eps=eps)

        return CFcea, CF, mode

    def getFrozen_PambCf(self,
                         Pamb=0.0,
                         Pc=100.0,
                         MR=1.0,
                         eps=40.0,
                         frozenAtThroat=0):
        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Pamb = self.Pc_U.uval_to_dval(Pamb)  # convert user units to psia

        CFcea, CFfrozen, mode = self.cea_obj.getFrozen_PambCf(
            Pamb=Pamb, Pc=Pc, MR=MR, eps=eps, frozenAtThroat=frozenAtThroat)

        return CFcea, CFfrozen, mode

    def get_Chamber_Transport(self, Pc=100.0, MR=1.0, eps=40.0, frozen=0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Cp, visc, cond, Prandtl = self.cea_obj.get_Chamber_Transport(
            Pc=Pc, MR=MR, eps=eps, frozen=frozen)

        #Cp = Cp * 8314.51 / 4184.0  # convert into BTU/lbm degR
        Cp = self.specific_heat_U.dval_to_uval(Cp)
        visc = self.viscosity_U.dval_to_uval(visc)
        cond = self.thermal_cond_U.dval_to_uval(cond)

        return Cp, visc, cond, Prandtl

    def get_Throat_Transport(self, Pc=100.0, MR=1.0, eps=40.0, frozen=0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Cp, visc, cond, Prandtl = self.cea_obj.get_Throat_Transport(
            Pc=Pc, MR=MR, eps=eps, frozen=frozen)

        #Cp = Cp * 8314.51 / 4184.0  # convert into BTU/lbm degR
        Cp = self.specific_heat_U.dval_to_uval(Cp)
        visc = self.viscosity_U.dval_to_uval(visc)
        cond = self.thermal_cond_U.dval_to_uval(cond)

        return Cp, visc, cond, Prandtl

    def get_Exit_Transport(self, Pc=100.0, MR=1.0, eps=40.0, frozen=0):

        Pc = self.Pc_U.uval_to_dval(Pc)  # convert user units to psia
        Cp, visc, cond, Prandtl = self.cea_obj.get_Exit_Transport(
            Pc=Pc, MR=MR, eps=eps, frozen=frozen)

        Cp = Cp * 8314.51 / 4184.0  # convert into BTU/lbm degR
        Cp = self.specific_heat_U.dval_to_uval(Cp)
        visc = self.viscosity_U.dval_to_uval(visc)
        cond = self.thermal_cond_U.dval_to_uval(cond)

        return Cp, visc, cond, Prandtl
コード例 #5
0
R_specific_comb_in = (R / (12 * c_molweight)
                      )  #Universal Gas Constant for Chamber

#Nozzle Parameters
#Throat
t_area = (mass_flow / pcc) * sqrt(
    (c_temp * R_specific_comb_in) / c_gamma) * (1 + (((c_gamma - 1) / 2)**(
        (c_gamma + 1) / (2 * (c_gamma - 1)))))
#Throat Area (in^2) - refer: https://www.grc.nasa.gov/www/k-12/airplane/rktthsum.html
#Assuming Chamber Temperature and Chamber Pressure are Stagnation Pressures - velocity in CC assumed to be negligible
t_radius = sqrt(t_area / pi)  #Throat Radius (in)
t_diameter = 2 * 2.54 * t_radius  #Throat Diameter (cm)
t_area_cm = t_area * 6.4516  #Throat Area (cm^2)

#Exit
e_pressureratio = ispObj.get_PcOvPe(
    pcc, mr, eps)  #Ratio of Pressure from Combustion Chamber to Exit
e_pressure_actual = pcc / e_pressureratio  #Actual Exit Pressure (psi)
e_area = eps * t_area  #Nozzle Exit Area (in^2)
e_prop = ispObj.get_IvacCstrTc_exitMwGam(pcc, mr, eps)  #Nozzle Exit Properties
e_molweight = e_prop[3]  #Molecular Weight - in lb/lbmol
e_gamma = e_prop[
    4]  #Nozzle Exit Gamma - this parameter changes significantly from the chamber gamma
e_mach = ispObj.get_MachNumber(pcc, mr, eps)  #Exit Mach Number

#Augmented Constants
R_specific_exit = (R / e_molweight)  #Universal Gas Constant for Nozzle Exit

#Thrust Calcs
e_vel_HH = sqrt(
    ((2 * g * e_gamma * R_specific_exit * c_temp) / (e_gamma - 1)) *
    (1 - ((pamb) / (pcc))**((e_gamma - 1) / (e_gamma))))  #H&H Eqn 1.18
コード例 #6
0
ファイル: stream_tubes.py プロジェクト: sonofeft/RocketIsp
class CoreStream:
    """
        Core stream tube of liquid bipropellant thruster.

        :param geomObj: Geometry that describes thruster
        :param effObj: Efficiencies object to hold individual efficiencies
        :param oxName: name of oxidizer (e.g. N2O4, LOX)
        :param fuelName: name of fuel (e.g. MMH, LH2)
        :param MRcore: mixture ratio of core flow (ox flow rate / fuel flow rate)
        :param Pc: psia, chamber pressure
        :param CdThroat: Cd of throat (RocketThruster object may override)
        :param Pamb: psia, ambient pressure (for example sea level is 14.7 psia)
        :param adjCstarODE: multiplier on NASA CEA code value of cstar ODE (default is 1.0)
        :param adjIspIdeal: multiplier on NASA CEA code value of Isp ODE (default is 1.0)
        :param pcentFFC: percent fuel film cooling (if > 0 then add BarrierStream)
        :param ko: entrainment constant (passed to BarrierStream object, range from 0.03 to 0.06)
        :param ignore_noz_sep: flag to force nozzle flow separation to be ignored (USE WITH CAUTION)
        :type geomObj: Geometry
        :type effObj: Efficiencies
        :type oxName: str
        :type fuelName: str
        :type MRcore: float
        :type Pc: float
        :type CdThroat: float
        :type Pamb: float
        :type adjCstarODE: float
        :type adjIspIdeal: float
        :type pcentFFC: float
        :type ko: float
        :type ignore_noz_sep: bool
        :return: CoreStream object
        :rtype: CoreStream 
        
        :ivar FvacTotal: lbf, total vacuum thrust
        :ivar FvacCore: lbf, vacuum thrust due to core stream tube
        :ivar MRthruster: total thruster mixture ratio')
        :ivar IspDel: sec, <=== thruster delivered vacuum Isp ===>
        :ivar Pexit: psia, nozzle exit pressure
        :ivar IspDel_core: sec, delivered Isp of core stream tube
        :ivar IspODF: sec, core frozen Isp
        :ivar IspODK: sec, core one dimensional kinetic Isp
        :ivar IspODE: sec, core one dimensional equilibrium Isp
        :ivar cstarERE: ft/s, delivered core cstar
        :ivar cstarODE: ft/s, core ideal cstar
        :ivar CfVacIdeal: ideal vacuum thrust coefficient
        :ivar CfVacDel: delivered vacuum thrust coefficient
        :ivar CfAmbDel: delivered ambient thrust coefficient
        :ivar wdotTot: lbm/s, total propellant flow rate (ox+fuel)
        :ivar wdotOx: lbm/s, total oxidizer flow rate
        :ivar wdotFl: lbm/s, total fuel flow rate
        :ivar TcODE: degR, ideal core gas temperature
        :ivar MWchm: g/gmole, core gas molecular weight
        :ivar gammaChm: core gas ratio of specific heats (Cp/Cv)
        
    """
    def __init__(
            self,
            geomObj=Geometry(),
            effObj=Efficiencies(),  #ERE=0.98, Noz=0.97), 
            oxName='N2O4',
            fuelName='MMH',
            MRcore=1.9,
            Pc=500,
            CdThroat=0.995,
            Pamb=0.0,
            adjCstarODE=1.0,
            adjIspIdeal=1.0,
            pcentFFC=0.0,
            ko=0.035,
            ignore_noz_sep=False):

        self.geomObj = geomObj
        self.effObj = effObj
        self.oxName = oxName
        self.fuelName = fuelName
        self.MRcore = MRcore
        self.Pc = Pc
        self.Pamb = Pamb  # ambient pressure
        self.noz_mode = ''
        self.CdThroat = CdThroat

        self.CdThroat_method = 'default'
        self.ignore_noz_sep = ignore_noz_sep  # ignore any nozzle separation

        self.adjCstarODE = adjCstarODE  # may want to adjust ODE cstar value
        self.adjIspIdeal = adjIspIdeal  # may want to adjust ODE and ODF Isp values

        # make CEA object
        self.ceaObj = CEA_Obj(oxName=oxName, fuelName=fuelName)

        # ... if pcentFFC > 0.0, then there's barrier cooling
        if pcentFFC > 0.0:
            self.add_barrier = True
        else:
            self.add_barrier = False

        # barrier might need some performance parameters from CoreStream
        self.calc_cea_perf_params()

        if self.add_barrier:
            self.barrierObj = BarrierStream(self, pcentFFC=pcentFFC, ko=ko)
        else:
            self.barrierObj = None

        self.evaluate()

        # get input descriptions and units from doc string
        self.inp_descD, self.inp_unitsD, self.is_inputD = get_desc_and_units(
            self.__doc__)

    def __call__(self, name):
        return getattr(self, name)  # let it raise exception if no name attr.

    def reset_CdThroat(self, value, method_name='RocketIsp', re_evaluate=True):
        """
        reset the value of CdThroat
        If re_evaluate is True, then call self.evaluate() after resetting the efficiency.
        """
        self.CdThroat = value
        self.CdThroat_method = method_name

        if re_evaluate:
            self.evaluate()

    def reset_attr(self, name, value, re_evaluate=True):
        """
        reset the value of any existing attribute of CoreStream instance.
        If re_evaluate is True, then call self.evaluate() after resetting the value of the attribute.
        """
        if hasattr(self, name):
            setattr(self, name, value)
        else:
            raise Exception(
                'Attempting to set un-authorized CoreStream attribute named "%s"'
                % name)

        if name in ['oxName', 'fuelName']:
            # make CEA object
            self.ceaObj = CEA_Obj(oxName=self.oxName, fuelName=self.fuelName)

        if re_evaluate:
            self.evaluate()

    def calc_cea_perf_params(self):
        """Calc basic Isp values from CEA and calc implied IspODK from current effKin value."""

        # calc ideal CEA performance parameters
        self.IspODE, self.cstarODE, self.TcODE, self.MWchm, self.gammaChm = \
                self.ceaObj.get_IvacCstrTc_ChmMwGam( Pc=self.Pc, MR=self.MRcore, eps=self.geomObj.eps)

        self.cstarODE *= self.adjCstarODE
        self.IspODE *= self.adjIspIdeal

        self.IspODF, _, _ = self.ceaObj.getFrozen_IvacCstrTc(
            Pc=self.Pc, MR=self.MRcore, eps=self.geomObj.eps)
        self.IspODF *= self.adjIspIdeal

        # use user effKin to set IspODK (or most recent update)
        self.IspODK = self.IspODE * self.effObj('Kin')
        #self.IspODK = calc_IspODK(self.ceaObj, Pc=self.Pc, eps=self.geomObj.eps,
        #                          Rthrt=self.geomObj.Rthrt,
        #                          pcentBell=self.geomObj.pcentBell,
        #                          MR=self.MRcore)

        # fraction of equilibrium kinetics obtained
        self.fracKin = (self.IspODK - self.IspODF) / (self.IspODE -
                                                      self.IspODF)

        self.Pexit = self.Pc / self.ceaObj.get_PcOvPe(
            Pc=self.Pc, MR=self.MRcore, eps=self.geomObj.eps)

        self.CfVacIdeal = 32.174 * self.IspODE / self.cstarODE

    def evaluate(self):
        """
        Assume that all efficiencies have been set, either by original user value
        or an update by an efficiency model.
        """
        self.effObj.evaluate()
        self.calc_cea_perf_params()

        # make final summary efficiencies
        effNoz = self.effObj('Noz')

        # want a Core-Only ERE in case a barrier calc is done
        effERE_core = self.effObj('ERE')
        if not self.add_barrier:  # if no barrier, user may have input FFC
            effERE_core = effERE_core * self.effObj('FFC')

        cstarERE_core = self.cstarODE * effERE_core

        effIsp_core = effNoz * effERE_core
        self.IspDel_core = effIsp_core * self.IspODE

        if self.add_barrier:
            self.barrierObj.evaluate()

            fAtc = solve_At_split(self.MRcore, self.barrierObj.MRbarrier,
                                  self.barrierObj.pcentFFC / 100.0,
                                  cstarERE_core, self.barrierObj.cstarERE_b)

            self.frac_At_core = fAtc  # core shares throat area with barrier stream

            self.frac_At_barrier = 1.0 - self.frac_At_core
            self.At_b = self.frac_At_barrier * self.geomObj.At

            self.wdotTot_b = self.Pc * self.At_b * self.CdThroat * 32.174 / self.barrierObj.cstarERE_b
            self.wdotOx_b = self.wdotTot_b * self.barrierObj.MRbarrier / (
                1.0 + self.barrierObj.MRbarrier)
            self.wdotFl_b = self.wdotTot_b - self.wdotOx_b

            self.FvacBarrier = self.wdotTot_b * self.barrierObj.IspDel_b
            self.MRthruster = self.MRcore * (1.0 -
                                             self.barrierObj.pcentFFC / 100.0)
        else:
            self.frac_At_core = 1.0  # core gets all of throat area if no barrier stream
            self.frac_At_barrier = 0.0
            self.FvacBarrier = 0.0
            self.MRthruster = self.MRcore

            self.wdotTot_b = 0.0
            self.wdotOx_b = 0.0
            self.wdotFl_b = 0.0

        self.Atcore = self.frac_At_core * self.geomObj.At

        self.wdotTot_c = self.Pc * self.Atcore * self.CdThroat * 32.174 / cstarERE_core
        self.wdotOx_c = self.wdotTot_c * self.MRcore / (1.0 + self.MRcore)
        self.wdotFl_c = self.wdotTot_c - self.wdotOx_c

        self.FvacCore = self.wdotTot_c * self.IspDel_core
        self.FvacTotal = self.FvacCore + self.FvacBarrier

        self.wdotTot = self.wdotTot_c + self.wdotTot_b
        self.wdotOx = self.wdotOx_c + self.wdotOx_b
        self.wdotFl = self.wdotFl_c + self.wdotFl_b

        if self.add_barrier:
            self.wdotFlFFC = (self.barrierObj.pcentFFC / 100.0) * self.wdotFl
            self.wdotFl_cInit = self.wdotFl - self.wdotFlFFC
            self.wdotTot_cInit = self.wdotOx + self.wdotFl_cInit
        else:
            self.wdotFlFFC = 0.0
            self.wdotFl_cInit = self.wdotFl
            self.wdotTot_cInit = self.wdotTot

        self.IspDel = self.FvacTotal / self.wdotTot

        self.IspDelPulse = self.IspDel * self.effObj('Pulse')

        if self.add_barrier:  # if barrier is analysed, assume it is in addition to user input effERE
            effFFC = self.IspDel / self.IspDel_core
            self.effObj.set_value('FFC', effFFC, value_src='barrier calc')

        self.cstarERE = self.cstarODE * self.effObj('ERE')
        #self.cstarDel = self.Pc * self.Atcore * self.CdThroat * 32.174 / self.wdotTot

        # do any nozzle ambient performance calcs here
        if self.Pamb < 0.000001:
            self.IspAmb = self.IspDel
            self.noz_mode = '(Pexit=%g psia)' % self.Pexit
        else:
            CfOvCfvacAtEsep, CfOvCfvac, Cfsep, CfiVac, CfiAmbSimple, CfVac, self.epsSep, self.Psep = \
                 sepNozzleCf(self.gammaChm, self.geomObj.eps, self.Pc, self.Pamb)
            #print('epsSep=%g, Psep=%g'%(self.epsSep, self.Psep))
            #print('========= Pexit=%g'%self.Pexit, '    Psep=%g'%self.Psep, '  epsSep=%g'%self.epsSep)

            if self.Pexit > self.Psep or self.geomObj.eps < self.epsSep or self.ignore_noz_sep:
                # if not separated, use theoretical equation for back-pressure correction
                self.IspAmb = self.IspDel - self.cstarERE * self.Pamb * self.geomObj.eps / self.Pc / 32.174
                #print('---------------->  subtraction term =', self.cstarERE * self.Pamb * self.geomObj.eps / self.Pc / 32.174)
            else:
                # if separated, use Kalt and Badal estimate of ambient thrust coefficient
                # NOTE: there are better, more modern methods available
                IspODEepsSep, CstarODE, Tc = \
                    self.ceaObj.get_IvacCstrTc(Pc=self.Pc, MR=self.MRcore, eps=self.epsSep)
                IspODEepsSep = IspODEepsSep - self.cstarERE * self.Pamb * self.epsSep / self.Pc / 32.174

                effPamb = IspODEepsSep / self.IspODE
                #print('--------------> effPamb=%g'%effPamb, '    IspODEepsSep=%g'%IspODEepsSep, '   IspODE=%g'%self.IspODE)

                self.IspAmb = effPamb * self.IspDel

            #print('========= Pamb=%g'%self.Pamb, '    IspAmb=%g'%self.IspAmb)
            # figure out mode of nozzle operation
            if self.Pexit > self.Psep or self.geomObj.eps < self.epsSep:
                if self.Pexit > self.Pamb + 0.05:
                    self.noz_mode = 'UnderExpanded (Pexit=%g)' % self.Pexit
                elif self.Pexit < self.Pamb - 0.05:
                    self.noz_mode = 'OverExpanded (Pexit=%g)' % self.Pexit
                else:
                    self.noz_mode = 'Pexit=%g' % self.Pexit
            else:
                self.noz_mode = 'Separated (Psep=%g, epsSep=%g)' % (
                    self.Psep, self.epsSep)

        self.Fambient = self.FvacTotal * self.IspAmb / self.IspDel

        self.CfVacDel = self.FvacTotal / (self.geomObj.At * self.Pc
                                          )  # includes impact of CdThroat
        self.CfAmbDel = self.Fambient / (self.geomObj.At * self.Pc
                                         )  # includes impact of CdThroat

    def summ_print(self):
        """
        print to standard output, the current state of CoreStream instance.
        """
        print(self.get_summ_str())

    def get_summ_str(self,
                     alpha_ordered=True,
                     numbered=False,
                     add_trailer=True,
                     fillchar='.',
                     max_banner=76,
                     intro_str=''):
        """
        return string of the current state of CoreStream instance.
        """

        M = self.get_model_summ_obj()

        Me = self.effObj.get_model_summ_obj()
        se = '\n' + Me.summ_str(
            alpha_ordered=False, fillchar=' ', assumptions_first=False)

        if self.add_barrier:
            Mb = self.barrierObj.get_model_summ_obj()
            sb = '\n' + Mb.summ_str(alpha_ordered=alpha_ordered,
                                    numbered=numbered,
                                    add_trailer=add_trailer,
                                    fillchar=fillchar,
                                    max_banner=max_banner,
                                    intro_str=intro_str)
        else:
            sb = ''

        return M.summ_str(alpha_ordered=alpha_ordered,
                          numbered=numbered,
                          add_trailer=add_trailer,
                          fillchar=fillchar,
                          max_banner=max_banner,
                          intro_str=intro_str) + se + sb

    def get_html_str(self, alpha_ordered=True, numbered=False, intro_str=''):
        M = self.get_model_summ_obj()

        Me = self.effObj.get_model_summ_obj()
        se = '\n' + Me.html_table_str(alpha_ordered=False,
                                      assumptions_first=False)

        if self.add_barrier:
            Mb = self.barrierObj.get_model_summ_obj()
            sb = '\n' + Mb.html_table_str(alpha_ordered=alpha_ordered,
                                          numbered=numbered,
                                          intro_str=intro_str)
        else:
            sb = ''


        return M.html_table_str( alpha_ordered=alpha_ordered, numbered=numbered, intro_str=intro_str)\
                + se + sb

    def get_model_summ_obj(self):
        """
        return ModelSummary object for current state of CoreStream instance.
        """

        M = ModelSummary('%s/%s Core Stream Tube' %
                         (self.oxName, self.fuelName))
        M.add_alt_units('psia', ['MPa', 'atm', 'bar'])
        M.add_alt_units('lbf', 'N')
        M.add_alt_units('lbm/s', 'kg/s')
        M.add_alt_units('ft/s', 'm/s')
        M.add_alt_units('sec', ['N-sec/kg', 'km/sec'])
        M.add_alt_units('degR', ['degK', 'degC', 'degF'])

        M.add_param_fmt('Pexit', '%.4f')
        M.add_param_fmt('Pc', '%.1f')

        M.add_out_category('')  # show unlabeled category 1st

        def add_param(name,
                      desc='',
                      fmt='',
                      units='',
                      value=None,
                      category=''):

            if name in self.inp_unitsD:
                units = self.inp_unitsD[name]

            if desc == '' and name in self.inp_descD:
                desc = self.inp_descD[name]

            if value is None:
                value = getattr(self, name)

            if self.is_inputD.get(name, False):
                M.add_inp_param(name, value, units, desc, fmt=fmt)
            else:
                M.add_out_param(name,
                                value,
                                units,
                                desc,
                                fmt=fmt,
                                category=category)

        for name in self.is_inputD.keys():
            if name not in ['pcentFFC', 'ko', 'geomObj', 'effObj']:
                add_param(name)

        # parameters that are NOT attributes OR are conditional
        if self.add_barrier:
            add_param('FvacBarrier',
                      units='lbf',
                      desc='vacuum thrust due to barrier stream tube')

        if self.Pamb > 14.5:
            add_param('Fambient', units='lbf', desc='total sea level thrust')
            add_param('IspAmb', units='sec', desc='delivered sea level Isp')
            M.add_out_comment('Fambient', '%s' % self.noz_mode)
            M.add_out_comment('IspAmb', '%s' % self.noz_mode)
        elif self.Pamb > 0.0:
            add_param('Fambient', units='lbf', desc='total ambient thrust')
            add_param('IspAmb', units='sec', desc='delivered ambient Isp')
            M.add_out_comment('Fambient', '%s' % self.noz_mode)
            M.add_out_comment('IspAmb', '%s' % self.noz_mode)

        if self.effObj('Pulse') < 1.0:
            add_param('IspDelPulse', units='sec', desc='delivered pulsing Isp')

        if self.CdThroat_method != 'default':
            M.add_inp_comment('CdThroat', '(%s)' % self.CdThroat_method)

        if self.add_barrier:
            add_param('wdotFlFFC',
                      units='lbm/s',
                      desc='fuel film coolant flow rate injected at perimeter',
                      category='At Injector Face')
            add_param(
                'wdotFl_cInit',
                units='lbm/s',
                desc='initial core fuel flow rate (before any entrainment)',
                category='At Injector Face')
            add_param(
                'wdotTot_cInit',
                units='lbm/s',
                desc='initial core total flow rate (before any entrainment)',
                category='At Injector Face')

            add_param(
                'wdotTot_b',
                units='lbm/s',
                desc='total barrier propellant flow rate (includes entrained)',
                category='After Entrainment')
            add_param('wdotOx_b',
                      units='lbm/s',
                      desc='barrier oxidizer flow rate (all entrained)',
                      category='After Entrainment')
            add_param('wdotFl_b',
                      units='lbm/s',
                      desc='barrier fuel flow rate (FFC + entrained)',
                      category='After Entrainment')

            add_param(
                'wdotTot_c',
                units='lbm/s',
                desc=
                'total final core propellant flow rate (injected - entrained)',
                category='After Entrainment')
            add_param(
                'wdotOx_c',
                units='lbm/s',
                desc='final core oxidizer flow rate (injected - entrained)',
                category='After Entrainment')
            add_param('wdotFl_c',
                      units='lbm/s',
                      desc='final core fuel flow rate (injected - entrained)',
                      category='After Entrainment')

        #add_param('xxx', units='xxx', desc='xxx')

        return M
コード例 #7
0
class ceaRocket:
    def __init__(self,
                 title,
                 oxidizer,
                 fuel,
                 Pcham,
                 Pambient,
                 Mr,
                 mdot,
                 l_star,
                 cham_d,
                 conv_angle,
                 div_angle,
                 wall_temp,
                 nozzle_type,
                 r1=0.05,
                 r2=0.03,
                 r3=0.025,
                 contourStep=1e-4):
        self.title = title
        self.nozzle_type = nozzle_type
        self.Mr = Mr
        self.ambientP = Pambient
        self.cea = CEA_Obj(oxName=oxidizer, fuelName=fuel)
        chems = ChemistryCEA.create(self.cea,
                                    Pcham,
                                    self.Mr,
                                    pAmbient=Pambient)
        self.inj = None  # injector
        self.cham = chems[0]  # converging starts (end of chamber)
        self.thr = chems[1]  # throat
        self.exit = chems[2]  # exit
        self.mdot = mdot
        self.Lstar = l_star
        self.cham.d = cham_d  #diameter of the chamber
        self.wall_temp = wall_temp
        self.contourPoints = None
        self.contour = None
        self.area_arr = None
        self.mach_arr = []
        self.pressure_arr = None
        self.temp_arr = None
        self.density_arr = None
        self.h_g_arr = []
        self.heat_flux_arr = []
        self.conv_angle = conv_angle
        self.divergence_angle = div_angle
        #self.exit.ae = self.findAe(self.cham.p, self.Mr, self.ambientP)
        #throat
        #self.thr.gam = self.cea.get_Throat_MolWt_gamma(Pc=self.cham.p, MR=self.Mr, eps=self.exit.ae)#fix
        #self.thr.t = None  #fix
        self.total_watts = 0
        self.r1 = r1
        self.r2 = r2
        self.r3 = r3
        self.contourStep = contourStep
        #unit conversions
        #bar to Pa
        self.cham.p = self.cham.p * 100000

        # Specific impulse in seconds
        #self.isp_s = self.exit.isp / 9.8
        self.thr.a = throatAreaEquation(self.mdot, self.cham.p, self.thr.t,
                                        self.thr.rbar, self.thr.gam)
        # p is in atm, conversion constant to Pa, might change to Pa later. area is in m^2

        # Nozzle Exit Area and diameters via Expansion Ratio and
        self.exit.a = self.thr.a * self.exit.aeat
        self.thr.d = 2 * math.sqrt(self.thr.a / math.pi)
        self.exit.d = 2 * math.sqrt(self.exit.a / math.pi)

        # Thrust by fundamental rocket eq F = mdot * exhaust_velocity
        self.thrust = self.mdot * self.exit.isp  # + self.a_noz*(self.p-self.p_amb) not included as sea level expanded

        # Total Chamber Volume via Characteristic Length
        # NOTE: this volume does not take injector and contour volumes into consideration and is theirfor not completly accurate
        self.chamber_volume = self.Lstar * self.thr.a
        self.chamber_length = self.chamber_volume / (math.pi *
                                                     (self.cham.d / 2)**2)
        self.Cstar = self.cham.p * self.thr.a / self.mdot

        # new methods for generating chamber and nozzle contour
        self.contourPoints, self.contour = self.nozzleGeneration()

        # this generates the chamber and nozzle contour that is used for calculations
        #self.my_contourPoints(r1, r2, r3)
        #self.genContour(r1, r2, r3, step)

        # temporarily turn off all convergence dependent functions
        self.area_arr = self.areas(self.contour)
        self.solveMach()
        # self.areaMach()
        self.tempPressureDensity()
        self.calcBartz()
        self.calcHeatFlux()
        self.totalWatts()

    # this generates the points that the gencontour function uses to make functions between
    # the points are referenced from left to right in the graph
    def findAe(self, Pcham, Mr, Pambient):
        aeGuess = 1000 * 2
        step = aeGuess / 2
        guessPress = 0.0
        while (guessPress - Pambient) > 0.01:
            if guessPress < Pambient:
                aeGuess -= step
            else:
                aeGuess += step
            pressRatio = self.cea.get_PcOvPe(Pc=Pcham, MR=Mr, eps=aeGuess)
            guessPress = Pcham / pressRatio
        return aeGuess

    def TOPnozzleCoefficients(
            self, Rn, Re, Xn, thetaN,
            thetaE):  # finds a,b,c for TOP parobolic function
        A = np.array([[2 * Rn, 1, 0], [2 * Re, 1, 0], [Rn**2, Rn, 1]])
        B = np.array([1 / np.tan(thetaN), 1 / np.tan(thetaE), Xn])
        X = np.linalg.solve(A, B)
        return X

    def nozzleGeneration(
        self
    ):  #this function creates contour points and functions for the chamber and nozzle geometry
        #this first section sets up points for the chamber and throat.  with the left being the chamber the right being the exit, the points go in order of a, b, c, d, o, n, e
        r1 = self.r1 * self.thr.d / 2
        r2 = self.r2 * self.thr.d / 2
        o = [0, self.thr.d / 2]
        d = [
            -r2 * np.sin(self.conv_angle),
            o[1] + r2 * (1 - np.cos(self.conv_angle))
        ]
        c = [None, self.cham.d / 2 - (r1 * (1 - np.cos(self.conv_angle)))]
        c[0] = d[0] - (c[1] - d[1]) * (np.sin(math.pi / 2 - self.conv_angle) /
                                       np.sin(self.conv_angle))
        b = [c[0] - (r1 * np.sin(self.conv_angle)), self.cham.d / 2]
        a = [b[0] - self.chamber_length, self.cham.d / 2]

        if self.nozzle_type == 'conical':  # this sets the points and equations for a conical nozzle
            r3 = self.r3 * self.thr.d / 2
            n = [
                r3 * np.sin(self.divergence_angle),
                o[1] + r3 * np.sin(1 - np.cos(self.divergence_angle))
            ]
            e = [
                n[0] + ((self.exit.d / 2) - n[1]) *
                np.sin(math.pi / 2 - self.divergence_angle) /
                np.sin(self.divergence_angle), self.exit.d / 2
            ]
            #print('n variable: {}'.format(n))
            #print('e variable: {}'.format(e))
            contourPoints = [a, b, c, d, o, n, e]  # temporary
            nozzleCurve = lambda x: (
                (contourPoints[5][1] - contourPoints[6][1]) /
                (contourPoints[5][0] - contourPoints[6][0])) * (
                    x - contourPoints[5][0]) + contourPoints[5][1]

        elif self.nozzle_type == 'bell80':  # this sets the points and equations for an 80% bell nozzle
            r3 = self.r3 * self.thr.d / 2
            thetaE = 7 * np.pi / 180  # theta values found in table... hard coded temporarily
            thetaN = 33 * np.pi / 180
            n = [r3 * np.sin(thetaN), o[1] + r3 * np.sin(1 - np.cos(thetaN))]
            e = [
                0.8 * (((math.sqrt(self.exit.aeat) - 1) * self.thr.d / 2 /
                        np.tan(15 * np.pi / 180))), self.exit.d / 2
            ]  # specificly for 80% bell nozzles
            contourPoints = [a, b, c, d, o, n, e]  # temporary
            A = np.array([[2 * n[1], 1, 0], [2 * e[1], 1, 0],
                          [n[1]**2, n[1], 1]])
            B = np.array([1 / np.tan(thetaN), 1 / np.tan(thetaE), n[0]])
            X = np.linalg.solve(A, B)
            aa = X[0]
            bb = X[1]
            cc = X[2]
            #print('exit r:{}'.format(self.exit.d/2))
            #print('thr r:{}'.format(self.thr.d/2))
            #print('exit area ratio:{}'.format(self.exit.ae))
            #print('n variable: {}'.format(n))
            #print('e variable: {}'.format(e))
            #print('A variable: {}'.format(A))
            #print('B variable: {}'.format(B))
            #print('X variable: {}'.format(X))
            #print('a: {}\nb: {}\nc: {}'.format(aa,bb,cc))
            nozzleCurve = lambda x: (-X[1] + (X[1]**2 - 4 * X[0] * (X[
                2] - x))**0.5) / (2 * X[0])  # might need to change sign

        elif self.nozzle_type == 'dualbell':  #work in progress, this sets the points and equations for a duel bell nozzle, in this there is an extra point 'm' between the n and e points
            r3 = self.r3 * self.thr.d / 2
            thetaE1 = 7 * np.pi / 180  # theta values found in table... hard coded temporarily
            thetaN1 = 33 * np.pi / 180
            thetaE2 = 7 * np.pi / 180
            thetaN2 = 33 * np.pi / 180
            curvePercent1 = .7
            curvePercent2 = .8
            curve1ae = None  #low altitude optimized area ratio
            curve2ae = self.exit.aeat
            curve1rad = None  #low altitude optimized radius
            curve2rad = self.exit.d / 2
            n = [r3 * np.sin(thetaN1), o[1] + r3 * np.sin(1 - np.cos(thetaN1))]
            m = [
                curvePercent1 *
                (((math.sqrt(curve1ae) - 1) * self.thr.d / 2 / np.tan(15))),
                curve1rad
            ]
            e = [
                curvePercent2 *
                (((math.sqrt(curve2ae) - 1) * self.thr.d / 2 / np.tan(15))),
                curve2rad
            ]
            contourPoints = [a, b, c, d, o, n, m, e]  # temporary
            #X1 = TOPnozzleCoefficients(n[1], m[1], n[0], thetaN1, thetaE1)
            A = np.array([[2 * n[1], 1, 0], [2 * m[1], 1, 0],
                          [n[1]**2, n[1], 1]])
            B = np.array([1 / np.tan(thetaN1), 1 / np.tan(thetaE1), n[0]])
            X1 = np.linalg.solve(A, B)
            nozzleCurve1 = lambda x: (-X1[1] + math.sqrt(X1[1]**2 - 4 * X1[
                0] * (X1[2] - x))) / 2 * X1[0]
            A = np.array([[2 * m[1], 1, 0], [2 * e[1], 1, 0],
                          [n[1]**2, n[1], 1]])
            B = np.array([1 / np.tan(thetaN2), 1 / np.tan(thetaE2), m[0]])
            X2 = np.linalg.solve(A, B)
            nozzleCurve2 = lambda x: (-X2[1] + math.sqrt(X2[1]**2 - 4 * X2[
                0] * (X2[2] - x))) / 2 * X2[0]

        else:  #this runs if the nozzle type input does not match any of the above nozzle types
            print("invalid nozzle type")

        functions = [
            lambda x: contourPoints[0][1], lambda x: np.sqrt(r1**2 - (
                x - contourPoints[1][0])**2) + contourPoints[1][1] - r1,
            lambda x: ((contourPoints[2][1] - contourPoints[3][1]) /
                       (contourPoints[2][0] - contourPoints[3][0])) *
            (x - contourPoints[2][0]) + contourPoints[2][1],
            lambda x: -np.sqrt(r2**2 - (x - contourPoints[4][0])**2
                               ) + contourPoints[4][1] + r2,
            lambda x: -np.sqrt(r3**2 - (x + contourPoints[4][0])**2
                               ) + contourPoints[4][1] + r3, nozzleCurve
        ]
        num = np.int32(
            np.rint((contourPoints[6][0] - contourPoints[0][0]) /
                    self.contourStep))
        x = np.array([])
        y = np.array([])
        for i, fun in enumerate(functions):
            temp_x = np.linspace(contourPoints[i][0], contourPoints[i + 1][0],
                                 num)
            f = np.vectorize(fun)
            #if i == 5:
            #    print('x:{}\ty:{}'.format(temp_x,f(temp_x)))
            y = np.append(y, f(temp_x))
            x = np.append(x, temp_x)
        contour = np.array([x, y])

        #exports contour points for use in cad
        locs = ['inj', 'b', 'c', 'd', 'o', 'n', 'e']
        xy = ['x', 'y']
        txtout = open('dims.txt', 'w')
        for i in range(len(contourPoints)):
            for j in range(2):
                txtout.write('"{0}_{1}"= {2}\n'.format(
                    locs[i], xy[j], contourPoints[i][j] / 0.0254))

        return contourPoints, contour

    '''
    def my_contourPoints(self, r1=0.05, r2=0.03, r3=0.025):
        o = [0,self.thr.d / 2]
        d = [-r2 * np.sin(self.conv_angle),o[1] + r2 * (1 - np.cos(self.conv_angle))]
        n = [r3 * np.sin(self.divergence_angle), o[1] + r3 * np.sin(1 - np.cos(self.divergence_angle))]
        e = [n[0] + ((self.exit.d / 2) - n[1]) * np.sin(math.pi/2 - self.divergence_angle)/np.sin(self.divergence_angle), self.exit.d / 2]
        a = [None, self.cham.d / 2]
        b = [None, self.cham.d / 2]
        c = [None,self.cham.d / 2 - (r1 * (1 - np.cos(self.conv_angle)))]
        c[0] = d[0] - (c[1] - d[1]) * (np.sin(math.pi/2 - self.conv_angle)/np.sin(self.conv_angle))
        b[0] = c[0] - (r1 * np.sin(self.conv_angle))
        a[0] = b[0] - self.chamber_length
        self.contourPoints = [a, b, c, d, o, n, e]
        locs = ['inj', 'b', 'c', 'd', 'o', 'n', 'e']
        xy = ['x', 'y']

        txtout = open('dims.txt','w')
        for i in range(len(self.contourPoints)):
            for j in range(2):
                txtout.write('"{0}_{1}"= {2}\n'.format(locs[i], xy[j], self.contourPoints[i][j]/0.0254))

    def genContour(self, r1=0.05, r2=0.03, r3=0.025, step=1e-4): 
        # This is the function that draws the discrete contour
        # these functions are referenced from left to right of the graph
        functions = [
            lambda x: self.contourPoints[0][1],
            lambda x: np.sqrt(r1 ** 2 - (x - self.contourPoints[1][0]) ** 2) + self.contourPoints[1][1] - r1,
            lambda x: ((self.contourPoints[2][1] - self.contourPoints[3][1]) / (self.contourPoints[2][0] - self.contourPoints[3][0]))  * (x - self.contourPoints[2][0]) + self.contourPoints[2][1],
            lambda x: -np.sqrt(r2 ** 2 - (x - self.contourPoints[4][0]) ** 2) + self.contourPoints[4][1] + r2, 
            lambda x: -np.sqrt(r3 ** 2 - (x + self.contourPoints[4][0]) ** 2) + self.contourPoints[4][1] + r3, 
            lambda x: ((self.contourPoints[5][1] - self.contourPoints[6][1]) / (self.contourPoints[5][0] - self.contourPoints[6][0]))  * (x - self.contourPoints[5][0]) + self.contourPoints[5][1] 
        ]
            # 1: straight line
            # 2: circle
            # 3: straight line
            # 4: circle
            # 5: circle
            # 6: straight line

        num = np.int32(np.rint((self.contourPoints[6][0] - self.contourPoints[0][0]) / step))
        x = np.array([])
        y = np.array([])

        for i, fun in enumerate(functions):
            temp_x = np.linspace(self.contourPoints[i][0], self.contourPoints[i + 1][0], num)
            f = np.vectorize(fun)
            y = np.append(y, f(temp_x))
            x = np.append(x, temp_x)

        self.contour = np.array([x, y])
    '''

    #array functions:
    #this finds area over the contour
    def areas(self, contour):
        area_arr = contour.copy()
        area_arr[1, :] = (area_arr[1, :]**
                          2) * np.pi  # this is just pi*r^2 in array form
        return area_arr

    def solveMach(self):
        def solveMatchForAreaRatio(area_ratio, mach_guess=0.7):
            def machEqn(mach):
                # return mach * area_ratio + 10
                return (2 / (self.thr.gam + 1) *
                        (1 + (self.thr.gam - 1) / 2 * mach**2))**(
                            (self.thr.gam + 1) /
                            (2 * (self.thr.gam - 1))) - mach * area_ratio

            return fsolve(machEqn, mach_guess)

        last = 0.7
        for [x, area] in self.area_arr.transpose():
            if x > 0:
                last = last + 1
            [mach] = solveMatchForAreaRatio(area / self.thr.a, last)
            self.mach_arr.append([x, mach])
            last = mach
        self.mach_arr = np.array(self.mach_arr).transpose()

    def temp_eq(self, mach):  #NOTE: stagnation values need improvment
        gam = self.thr.gam
        t_stag = self.cham.t * (1 + ((gam - 1) / 2 * self.cham.mach**2))
        # Note: ok technically, yes, the stagnation temperature needs to account for
        # gas velocity, but in our assumptions, t_0 assumed == t_cham as given by CEA
        #t_stag = self.inj.t

        myreturn = t_stag * (1 + ((gam - 1) / 2 * mach**2))**(-1)
        return myreturn

    def pressure_eq(self, mach):
        gam = self.thr.gam  # CHECK THIS!!!
        p_stag = self.cham.p * (1 + (
            (gam - 1) / 2 * self.cham.mach**2))**(gam / (gam - 1))
        #p_stag = self.inj.p
        myreturn = p_stag * (1 + ((gam - 1) / 2 * mach**2))**(-gam / (gam - 1))
        return myreturn

    def density_eq(self, mach):  #need to find chamber density
        gam = self.cham.gam  # CHECK THIS!!!
        d_stag = self.cham.rho * (1 + (
            (gam - 1) / 2 * self.cham.mach**2))**(1 / (gam - 1))
        myreturn = d_stag * (1 + ((gam - 1) / 2 * mach**2))**(-1 / (gam - 1))
        return myreturn

    def tempPressureDensity(self):
        self.pressure_arr = self.mach_arr.copy()
        self.temp_arr = self.mach_arr.copy()
        #self.density_arr = self.mach_arr.copy()
        count = 0
        for mach in self.mach_arr[1, :]:
            self.temp_arr[1, count] = self.temp_eq(mach)
            self.pressure_arr[1, count] = self.pressure_eq(mach)
            #self.density_arr[1,count] = self.density_eq(mach)
            count += 1

    def filewrite(self, filename):
        output = open(filename, "w")
        offset = self.contour[0, 0]
        for i in range(len(self.contour[0])):
            self.contour[0, i] += -offset
        output.write("X,Y,MACH,TEMP,Pressure,h_g,FLUX\n")
        for i in range(len(self.contour[1, :])):
            output.write(
                "{:.4f},{:.4f},{:.4f},{:.4f},{:.4f},{:.4f},{:.4f}\n".format(
                    self.contour[0, i], self.contour[1, i], self.mach_arr[1,
                                                                          i],
                    self.temp_arr[1, i], self.pressure_arr[1, i],
                    self.h_g_arr[1, i], self.heat_flux_arr[1, i]))
        output.close()

    def calcBartz(self):
        self.h_g_arr = self.contour.copy()
        for i in range(len(self.h_g_arr[0])):
            self.h_g_arr[1, i] = bartz(self.thr.d, self.cham.p, self.Cstar,
                                       self.contour[1, i] * 2,
                                       self.cham.cp * 1000, 0.85452e-4,
                                       self.temp_arr[1, i], self.wall_temp)

    def calcHeatFlux(self):
        self.heat_flux_arr = self.h_g_arr.copy()
        for i in range(len(self.heat_flux_arr[0])):
            self.heat_flux_arr[1,
                               i] = self.h_g_arr[1, i] * (self.temp_arr[1, i] -
                                                          self.wall_temp)

    def totalWatts(self):
        for i in range(len(self.heat_flux_arr[0]) - 1):
            self.total_watts = self.total_watts + abs(self.area_arr[
                0, i + 1] - self.area_arr[0, i]) * self.heat_flux_arr[1, i]

    ##################################################################
    def variablesDisplay(self):
        print("{}{}:{}".format('\033[33m', self.title, '\033[0m'))
        print("Chamber Length: {0:.2f} in".format(self.chamber_length /
                                                  0.0254))
        print("Chamber Diameter: {0:.2f} in".format(self.cham.d / 0.0254))
        print("Exit Diameter: {0:.2f} in".format(self.exit.d / 0.0254))
        print("Throat Diameter: {0:.2f} in".format(self.thr.d / 0.0254))
        print("Total Length: {0:.2f} in".format(
            (self.contourPoints[6][0] - self.contourPoints[0][0]) / 0.0254))
        print("Volume: {0:.2f} cc".format(self.chamber_volume * 1000000))
        print("Thrust: {0:.2f} N".format(self.thrust))
        print("Chamber heat flux constant: {0:.2f} W/m^2K".format(
            self.h_g_arr[1, 1]))
        print("Chamber heat flux W/m^2: {0:.2f} W/m^2".format(
            self.heat_flux_arr[1, 1]))
        print("Total Watts: {0:.2f} W".format(self.total_watts))
        print("Mass Flow Rate: {0:.2f} mdot".format(self.mdot))
        print("chamber pressure: {0:.2f} bar".format(self.pressure_arr[1, 1] /
                                                     100000))
        print()

    def graphDisplay(self, pressure_units='bar', distance_units='in'):
        #temperature units?
        if (pressure_units == 'bar'):
            Pcon = 100000  #bar
        elif (pressure_units == 'atm'):
            Pcon = 101325
        elif (pressure_units == 'psi'):
            Pcon = 6894.76
        else:
            Pcon = 1

        if (distance_units == 'in'):
            Dcon = 39.3701
        elif (distance_units == 'cm'):
            Dcon = 100
        elif (distance_units == 'mm'):
            Dcon = 1000
        else:
            Dcon = 1

        self.contour = self.contour * Dcon

        fig, axs = plt.subplots(2, 1, figsize=(8, 10.5))

        axs[0].set_title("Nozzle Geometry")
        axs[0].plot(self.contour[0], self.contour[1], label="self Contour")
        axs[0].set(xlabel="Axial Position (in)", ylabel="Radial Distance (in)")
        axs[0].axis('equal')

        secaxs = axs[0].twinx()
        secaxs.plot(self.mach_arr[0] * Dcon,
                    self.mach_arr[1],
                    label="Mach Number",
                    color="green")
        secaxs.set(ylabel="Mach Number (M)")
        axs[0].legend(loc=(0, 1))
        secaxs.legend(loc=(0.75, 1))

        axs[1].plot(self.temp_arr[0],
                    self.temp_arr[1],
                    color="orange",
                    label="Temperature")
        axs[1].set(ylabel="Gas Core Temperature (K)")

        secaxs1 = axs[1].twinx()
        secaxs1.plot(self.pressure_arr[0],
                     self.pressure_arr[1] * Pcon,
                     color="purple",
                     label="Pressure")
        secaxs1.set(ylabel="Pressure (bar)", xlabel="Axial Position (m)")
        axs[1].legend(loc=(0, 1))
        secaxs1.legend(loc=(0.8, 1))

        #---------------------------------------------------------
        fig2, axs2 = plt.subplots(2, 1, figsize=(8, 10.5))
        axs2[0].set_title("Nozzle Geometry")
        axs2[0].plot(self.contour[0], self.contour[1])
        axs2[0].set(xlabel="Axial Position (in)",
                    ylabel="Radial Distance (in)")
        axs2[0].axis('equal')

        axs2[1].plot(self.h_g_arr[0], self.h_g_arr[1], label="h", color="blue")
        axs2[1].set(ylabel="Coefficient of Heat Transfer (W/m^2*K)",
                    xlabel="Axial Position (m)")

        sax = axs2[1].twinx()
        sax.plot(self.heat_flux_arr[0],
                 self.heat_flux_arr[1],
                 label="flux",
                 color="g")
        sax.set(ylabel="Heat Flux Rate (W/m^2)", xlabel="Axial Position (m)")
        axs2[1].legend(loc=(0, 1))
        sax.legend(loc=(0.8, 1))

        self.contour = self.contour / Dcon
        self.filewrite("dataTest.txt")

        plt.show()