def kv_valve_dp(pObj, Kv=1.0, wdotPPS=0.5, TdegR=530.0, Ppsia=1000.0): """ Calculate valve pressure drop for a valve with a known metric flow coefficient, Kv. Metric flow coefficient (Kv) is the amount of water (in m**3/hr) at 4 degC that will flow through a fully open valve with a difference of 1 bar between the inlet and the outlet. :param pObj: propellant object :param Kv: valve flow coefficient :param wdotPPS: propellant mass flow rate in line, lbm/sec :param TdegR: propellant temperature of propellant, degR :param Ppsia: propellant inlet pressure to valve, psia :type pObj: Propellant :type Kv: float :type wdotPPS: float :type TdegR: float :type Ppsia: float :return: propellant pressure drop , psid :rtype: float """ # Note, could have converted Kv into Cv #Cv = 1.1560992283526375 * Kv #return cv_valve_dp( pObj, Cv=Cv, wdotPPS=wdotPPS, TdegR=TdegR, Ppsia=Ppsia) SG = pObj.SG_compressed(TdegR, Ppsia) rho = get_value(SG, 'SG', 'lbm/in**3') Qm3h = get_value(wdotPPS / rho, 'inch**3/s', 'm**3/hr') dPbar = SG * (Qm3h / Kv)**2 deltaP = get_value(dPbar, 'bar', 'psid') #print('Qm3h=%g'%Qm3h, ' SG=%g'%SG, ' dPbar=%g'%dPbar, ' Kv=%g'%Kv, ' deltaP=%g'%deltaP) return deltaP
def cv_valve_dp(pObj, Cv=1.0, wdotPPS=0.5, TdegR=530.0, Ppsia=1000.0): """ Calculate valve pressure drop for a valve with a known imperial flow coefficient, Cv. Imperial flow coefficient (Cv) is the amount of water (in gallons per minute) at 60 degF that will flow through a fully open valve with a difference of 1 psi between the inlet and the outlet. :param pObj: propellant object :param Cv: valve flow coefficient :param wdotPPS: propellant mass flow rate in line, lbm/sec :param TdegR: propellant temperature of propellant, degR :param Ppsia: propellant inlet pressure to valve, psia :type pObj: Propellant :type Cv: float :type wdotPPS: float :type TdegR: float :type Ppsia: float :return: propellant pressure drop , psid :rtype: float """ SG = pObj.SG_compressed(TdegR, Ppsia) rho = get_value(SG, 'SG', 'lbm/in**3') Qgpm = get_value(wdotPPS / rho, 'inch**3/s', 'gpm') deltaP = SG * (Qgpm / Cv)**2 #print('Qgpm=%g'%Qgpm, ' SG=%g'%SG, ' deltaP=%g'%deltaP, ' Cv=%g'%Cv) return deltaP
def Visc_compressed(self, TdegR, Ppsia): r'''Adjusts viscosity of a liquid for high pressure using an empirical formula developed by Lucas. This code is modified from thermo package: https://thermo.readthedocs.io/en/latest/index.html also see: equation 9-9.1 in 5th Ed. of Gases and Liquids. :param TdegR: temperature in degR :param Ppsia: pressure in psia :type TdegR: float :type Ppsia: float :return: viscosity at (TdegR, Ppsia) in poise ''' # need to use thermo internal units of degK, Pa T = get_value(TdegR, 'degR', 'degK') Tc = get_value(self.Tc, 'degR', 'degK') P = get_value(Ppsia, 'psia', 'Pa') Pc = get_value(self.Pc, 'psia', 'Pa') P_sat = get_value(self.PvapAtTdegR(TdegR), 'psia', 'Pa') Tr = T / Tc mu_sat = self.ViscAtTr(Tr) if P <= P_sat: return mu_sat C = -0.07921+2.1616*Tr - 13.4040*Tr**2 + 44.1706*Tr**3 - 84.8291*Tr**4 \ + 96.1209*Tr**5-59.8127*Tr**6+15.6719*Tr**7 D = 0.3257 / ((1.0039 - Tr**2.573)**0.2906) - 0.2086 A = 0.9991 - 4.674E-4 / (1.0523 * Tr**-0.03877 - 1.0513) dPr = (P - P_sat) / Pc if dPr < 0: dPr = 0 return (1. + D * (dPr / 2.118)**A) / (1. + C * self.omega * dPr) * mu_sat
def SG_compressedCZ1(self, TdegR, Ppsia): r'''Calculates compressed-liquid specific gravity, using the Chang Zhao method. This code is derived from equation 4-12.3 in 5th Ed. of Gases and Liquids. :param TdegR: temperature in degR :param Ppsia: pressure in psia :type TdegR: float :type Ppsia: float :return: specific gravity at (TdegR, Ppsia) in g/ml ''' a0 = -170.335 a1 = -28.578 a2 = 124.809 a3 = -55.5393 a4 = 130.01 b0 = 0.164813 b1 = -0.0914427 C = exp(1) D = 1.00588 # convert to internal units from thermo T = get_value(TdegR, 'degR', 'degK') Tc = get_value(self.Tc, 'degR', 'degK') P = get_value(Ppsia, 'psia', 'bar') Pc = get_value(self.Pc, 'psia', 'bar') Psat = get_value(self.PvapAtTdegR(TdegR), 'psia', 'bar') SGsat = self.SGLiqAtTdegR(TdegR) if P < Psat: print( 'Warning in SG_compressed... Pressure below Psat for T=%g degR, P=%g psia' % (TdegR, Ppsia)) return SGsat Vs = self.MolWt / SGsat # [cm^3/mol] Tr = T / Tc if Tr > 0.94: print( 'Warning in SG_compressed... Reduced Temperature > 0.95 Tr=%g ' % Tr) A = a0 + a1 * Tr + a2 * Tr**3 + a3 * Tr**6 + a4 / Tr B = b0 + self.omega * b1 numer = A * Pc + C**((D - Tr)**B) * (P - Psat) denom = A * Pc + C * (P - Psat) try: V_dense = Vs * numer / denom # [cm^3/mol] # convert to SG SG_dense = self.MolWt / V_dense # g/cm^3 except: print('WARNING... SG_compressed has error at T=%g R, P=%g psia' % (TdegR, Ppsia)) return None return SG_dense
def SG_compressedCOSTALD(self, TdegR, Ppsia): r'''Calculates compressed-liquid specific gravity, using the COSTALD CSP method. This code is modified from thermo package: https://thermo.readthedocs.io/en/latest/index.html :param TdegR: temperature in degR :param Ppsia: pressure in psia :type TdegR: float :type Ppsia: float :return: specific gravity at (TdegR, Ppsia) in g/ml ''' a = -9.070217 b = 62.45326 d = -135.1102 f = 4.79594 g = 0.250047 h = 1.14188 j = 0.0861488 k = 0.0344483 # convert to internal units from thermo T = get_value(TdegR, 'degR', 'degK') Tc = get_value(self.Tc, 'degR', 'degK') P = get_value(Ppsia, 'psia', 'Pa') Pc = get_value(self.Pc, 'psia', 'Pa') Psat = get_value(self.PvapAtTdegR(TdegR), 'psia', 'Pa') SGsat = self.SGLiqAtTdegR(TdegR) if P < Psat: print( 'Warning... Pressure below saturation pressure for T=%g degR, P=%g psia' % (TdegR, Ppsia)) return SGsat Vs = self.MolWt / SGsat # [cm^3/mol] tau = 1 - T / Tc e = exp(f + g * self.omega + h * self.omega**2) C = j + k * self.omega B = Pc * (-1 + a * tau**(1 / 3.) + b * tau**(2 / 3.) + d * tau + e * tau**(4 / 3.)) try: V_dense = Vs * (1 - C * log((B + P) / (B + Psat))) # [cm^3/mol] # convert to SG SG_dense = self.MolWt / V_dense # g/cm^3 except: print('WARNING... SG_compressed has error at T=%g R, P=%g psia' % (TdegR, Ppsia)) return None return SG_dense
def calc_tank_volume( pObj, kg_expelled=50.0, TmaxC=50.0, expPcent=98.0, ullPcent=3.0): """ Calculate the volume of a propellant tank given operating requirements. :param pObj: propellant object :param kg_expelled: mass of expelled propellant in kg :param TmaxC: max operating/storage/transport temperature in deg C :param expPcent: expulsion efficiency in percent :param ullPcent: percent of total tank volume that is ullage at TmaxC :type pObj: Propellant :type kg_expelled: float :type TmaxC: float :type expPcent: float :type ullPcent: float :return: (volume of tank (ml), loaded propellant mass (kg), residual propellant mass (kg)) :rtype: (float, float, float) """ # put temperature units into degR Thot = get_value( TmaxC, 'degC', 'degR' ) SGhot = pObj.SGLiqAtTdegR( Thot ) cc_expelled = kg_expelled * 1000.0 / SGhot # residual and ullage fractions apply to entire tank volume, not just propellant volume expulsionEff = expPcent / 100.0 residualFraction = 1.0 - expulsionEff ullageFraction = ullPcent / 100.0 cc_Total = cc_expelled / (1.0 - residualFraction - ullageFraction) kg_loaded = cc_Total * (1.0 - ullageFraction) * SGhot / 1000.0 kg_residual = kg_loaded - kg_expelled return cc_Total, kg_loaded, kg_residual
def calib_valve_dp(pObj, wdotPPS=0.5, TdegR=530.0, Ppsia=1000.0, refWaterWdot=0.214, refWaterDP=30.0): """ Calculate valve pressure drop for a valve that has been calibrated with a water flow test. :param pObj: propellant object :param wdotPPS: propellant mass flow rate in line, lbm/sec :param TdegR: propellant temperature of propellant, degR :param Ppsia: propellant inlet pressure to valve, psia :param refWaterWdot: reference water flow rate, lbm/sec :param refWaterDP: reference water pressure drop, psid :type pObj: Propellant :type wdotPPS: float :type TdegR: float :type Ppsia: float :type refWaterWdot: float :type refWaterDP: float :return: propellant pressure drop , psid :rtype: float """ SG = pObj.SG_compressed(TdegR, Ppsia) Dens = get_value(SG, 'SG', 'lbm/ft**3') Kw = sqrt(refWaterWdot**2 / refWaterDP) # calculate pressure drop deltaP = wdotPPS**2 * 62.4 / Dens / Kw**2 return deltaP
def calc_inj_velocity(pObj, dPpsia=50.0, TdegR=530.0, Ppsia=1000.0): """ Calculate the injection velocity of a propellant. :param pObj: propellant object :param dPpsia: pressure drop across orifice, psid :param TdegR: temperature of propellant, degR :param Ppsia: inlet pressure to orifice, psia :type pObj: Propellant :type dPpsia: float :type TdegR: float :type Ppsia: float :return: injection velocity, ft/sec :rtype: float """ Pave = Ppsia - dPpsia / 2.0 SGave = pObj.SG_compressed(TdegR, Pave) rho = get_value(SGave, 'SG', 'lbm/in**3') in_per_sec = sqrt(24.0 * 32.174 * dPpsia / rho) # in/sec ft_per_sec = in_per_sec / 12.0 return ft_per_sec
def calc_orifice_flow_rate(pObj, CdOrf=0.75, DiamInches=0.01, dPpsia=50.0, TdegR=530.0, Ppsia=1000.0): """ Calculate mass flow rate through a single injector orifice. :param pObj: propellant object :param CdOrf: discharge coefficient of orifice :param DiamInches: diameter of orifice, inch :param dPpsia: pressure drop across orifice, psid :param TdegR: temperature of propellant, degR :param Ppsia: inlet pressure to orifice, psia :type pObj: Propellant :type CdOrf: float :type DiamInches: float :type dPpsia: float :type TdegR: float :type Ppsia: float :return: mass flow rate of single orifice, lbm/sec :rtype: float """ Pave = Ppsia - dPpsia / 2.0 SGave = pObj.SG_compressed(TdegR, Pave) rho = get_value(SGave, 'SG', 'lbm/in**3') in_per_sec = sqrt(24.0 * 32.174 * dPpsia / rho) # in/sec wdotOrif = in_per_sec * rho * CdOrf * DiamInches**2 * pi / 4.0 return wdotOrif # lbm/sec
def calc_line_id_dp( pObj, TdegR=530.0, Ppsia=1000.0, wdotPPS=0.5, velFPS=13.0, roughness=5.0E-6, Kfactors=2.0, len_inches=50.0): """ Calculate the inner diameter and pressure drop in propellant line. :param pObj: propellant object :param TdegR: temperature of propellant, degR :param Ppsia: inlet pressure to orifice, psia :param wdotPPS: mass flow rate in line, lbm/sec :param velFPS: velocity of liquid in line, ft/sec :param roughness: line roughness, inches :param Kfactors: number of velocity heads lost due to bends, valves, etc. :param len_inches: length of line, inches :type pObj: Propellant :type TdegR: float :type Ppsia: float :type wdotPPS: float :type velFPS: float :type roughness: float :type Kfactors: float :type len_inches: float :return: tuple of inside diameter and pressure drop (dinsid, deltaP), (inch, psid) :rtype: (float, float) """ SG = pObj.SG_compressed( TdegR, Ppsia ) rho = get_value( SG, 'SG', 'lbm/in**3' ) Dens = get_value( SG, 'SG', 'lbm/ft**3' ) Q = wdotPPS / rho Ac = Q / (velFPS * 12.0) rinsid = sqrt( Ac / pi ) dinsid = rinsid * 2.0 # calculate pressure drop visc = pObj.Visc_compressed(TdegR, Ppsia) mu = get_value( visc, 'poise', 'lbm/s/ft') ReNum= 144.0 * rho * velFPS * dinsid / mu ff = colebrook_ffact(roughness, dinsid, ReNum) deltaP = (ff * len_inches/dinsid + Kfactors) * wdotPPS**2 / (dinsid**4 * 0.27622 * Dens) return dinsid, deltaP
def calc_line_vel_dp( pObj, TdegR=530.0, Ppsia=1000.0, wdotPPS=0.5, IDinches=0.335, roughness=5.0E-6, Kfactors=2.0, len_inches=50.0): """ Calculate the line velocity and pressure drop in propellant line. :param pObj: propellant object :param TdegR: temperature of propellant, degR :param Ppsia: inlet pressure to orifice, psia :param wdotPPS: mass flow rate in line, lbm/sec :param IDinches: inside diameter of line, in :param roughness: line roughness, inches :param Kfactors: number of velocity heads lost due to bends, valves, etc. :param len_inches: length of line, inches :type pObj: Propellant :type TdegR: float :type Ppsia: float :type wdotPPS: float :type velFPS: float :type roughness: float :type Kfactors: float :type len_inches: float :return: tuple of velocity and pressure drop (velFPS, deltaP), (ft/s, psid) :rtype: (float, float) """ SG = pObj.SG_compressed( TdegR, Ppsia ) rho = get_value( SG, 'SG', 'lbm/in**3' ) Dens = get_value( SG, 'SG', 'lbm/ft**3' ) Q = wdotPPS / rho # in**3/s Ac = pi * IDinches**2 / 4.0 # in**2 velFPS = Q / (Ac * 12.0) # ft/sec # calculate pressure drop visc = pObj.Visc_compressed(TdegR, Ppsia) # poise mu = get_value( visc, 'poise', 'lbm/s/ft') # lbm/s-ft ReNum= 144.0 * rho * velFPS * IDinches / mu ff = colebrook_ffact(roughness, IDinches, ReNum) deltaP = (ff * len_inches/IDinches + Kfactors) * wdotPPS**2 / (IDinches**4 * 0.27622 * Dens) return velFPS, deltaP
if __name__ == "__main__": from rocketprops.rocket_prop import get_prop print(""" ...Calculate the propellant injection velocity...""") pObj = get_prop('N2O4') TdegR = 530.0 Ppsia = 1000.0 dPpsia = 50.0 ft_per_sec = calc_inj_velocity(pObj, dPpsia=dPpsia, TdegR=TdegR, Ppsia=Ppsia) print('ft_per_sec =', ft_per_sec, '=', get_value(ft_per_sec, 'ft/s', 'm/s'), 'm/s') # check calc in metric Pave = Ppsia - dPpsia / 2.0 SGave = pObj.SG_compressed(TdegR, Pave) gh_m = get_value(dPpsia, 'psia', 'N/m**2') / get_value( SGave, 'SG', 'kg/m**3') m_per_sec = sqrt(2 * gh_m) print(' gh_m=%g' % gh_m, ' m_per_sec =', m_per_sec, 'm/s') print(""" ...Calculate the propellant orifice mass flow rate...""") wdot = calc_orifice_flow_rate(pObj, CdOrf=0.75, DiamInches=0.01,
log10x = log10(1 + dp / (2.8615834935979536e-06 + psat) ) # Fit Standard Deviation = 0.037367390648883274 dsg_o_sgL.append(2.340023173056651 * log10x + -3.4531382789431726 * log10x**2 + 1.6738282493779768 * log10x**3) sgratio_terp = InterpProp(trL, dsg_o_sgL, extrapOK=True) SGratio = sgratio_terp(Tr) SGsat = self.SGLiqAtTr(Tr) SG = SGsat / (1.0 - SGratio) return SG if __name__ == '__main__': from rocketprops.unit_conv_data import get_value C = Prop() print('T = %g R = %g K' % (C.T, C.T / 1.8)) print('SurfaceTension = %g lbf/in' % C.surf, ' = ', get_value(C.surf, 'lbf/in', 'mN/m'), 'mN/m') print() print(' Tr_data_range =', C.Tr_data_range()) print(' T_data_range=', C.T_data_range()) print(' P_data_range=', C.P_data_range()) print('omega =%s' % C.omega) C.plot_sat_props()
def __init__(self, coreObj, # CoreStream object Tox=None, Tfuel=None, elemEm=0.8, fdPinjOx=0.25, fdPinjFuel=0.25, dpOxInp=None, dpFuelInp=None, setNelementsBy='acoustics', # can be "acoustics", "elem_density", "input" elemDensInp=5, NelementsInp=100, OxOrfPerEl=1.0, FuelOrfPerEl=1.0, lolFuelElem=False, setAcousticFreqBy='mode', # can be "mode" or "freq" desAcousMode='3T', desFreqInp=5000, CdOxOrf=0.75, CdFuelOrf=0.75, dropCorrOx=0.33, dropCorrFuel=0.33, DorfMin=0.008, LfanOvDorfOx=20.0, LfanOvDorfFuel=20.0): """ Injector object holds basic information about the injector. Injector design features are calculated including chamber losses due to the injector, Em, Mix and Vap. """ self.coreObj = coreObj self.geomObj = coreObj.geomObj # build propellant objects self.oxProp = get_prop( self.coreObj.oxName ) self.fuelProp = get_prop( self.coreObj.fuelName ) self.TminOx, self.TmaxOx = self.oxProp.T_data_range() self.TminFuel, self.TmaxFuel = self.fuelProp.T_data_range() self.Tox_warning = '' self.Tfuel_warning = '' if Tox is None: Tox = min(530.0, self.oxProp.Tnbp) else: Tox, self.Tox_warning = temperature_clamp(Tox, 'Tox', self.TminOx, self.TmaxOx) self.Tox = Tox if Tfuel is None: Tfuel = min(530.0, self.fuelProp.Tnbp) else: Tfuel,self.Tfuel_warning = temperature_clamp(Tfuel, 'Tfuel', self.TminFuel, self.TmaxFuel) self.Tfuel = Tfuel self.elemEm = min(1.0, elemEm) # intra-element mixing parameter for injector self.fdPinjOx = fdPinjOx self.fdPinjFuel = fdPinjFuel self.dpOxInp = dpOxInp self.dpFuelInp = dpFuelInp self.setNelementsBy = setNelementsBy.lower() # just in case user screw up. self.used_Nelem_criteria = self.setNelementsBy # assume for now that intention is satisfied self.elemDensInp = elemDensInp self.NelementsInp = NelementsInp self.OxOrfPerEl = OxOrfPerEl self.FuelOrfPerEl = FuelOrfPerEl self.lolFuelElem = lolFuelElem if lolFuelElem: self.strouhal_mult = 0.1 # LOL element uses 0.1 strouhal multiplier else: self.strouhal_mult = 0.2 # unlike element uses 0.2 strouhal multiplier self.desAcousMode = desAcousMode if desAcousMode in modeSvnD: self.desAcousMult = modeSvnD[ desAcousMode ] else: self.desAcousMult = float( desAcousMode ) # let it raise exception if not a float self.desFreqInp = desFreqInp self.setAcousticFreqBy = setAcousticFreqBy.lower() # can be "mode" or "freq" self.CdOxOrf = CdOxOrf self.CdFuelOrf = CdFuelOrf self.dropCorrOx = dropCorrOx self.dropCorrFuel = dropCorrFuel self.DorfMin = DorfMin self.LfanOvDorfOx = LfanOvDorfOx self.LfanOvDorfFuel = LfanOvDorfFuel # get oxidizer propellant properties self.sgOx = self.oxProp.SG_compressed( Tox, self.coreObj.Pc ) # g/ml self.dHvapOx = self.oxProp.HvapAtTdegR( Tox ) # BTU/lbm self.surfOx = self.oxProp.SurfAtTdegR( Tox ) # lbf/in self.viscOx = self.oxProp.ViscAtTdegR( Tox ) # poise self.viscOx = get_value( self.viscOx, 'poise', 'lbm/s/ft') self.MolWtOx = self.oxProp.MolWt #print('sgOx=',self.sgOx) # get fuel propellant properties self.sgFuel = self.fuelProp.SG_compressed( Tfuel, self.coreObj.Pc ) # g/ml self.dHvapFuel = self.fuelProp.HvapAtTdegR( Tfuel ) # BTU/lbm self.surfFuel = self.fuelProp.SurfAtTdegR( Tfuel ) # lbf/in self.viscFuel = self.fuelProp.ViscAtTdegR( Tfuel ) # poise self.viscFuel = get_value( self.viscFuel, 'poise', 'lbm/s/ft') self.MolWtFuel = self.fuelProp.MolWt #print('sgFuel=',self.sgFuel) # --------- start vaporization calcs -------- self.rhoOx = rho = get_value( self.sgOx, 'SG', 'lbm/in**3' ) self.rhoFuel = rho = get_value( self.sgFuel, 'SG', 'lbm/in**3' ) self.calc_element_attr() # e.g. Nelements, injection velocities, elements diam, etc. #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 get_model_summ_obj(self): """ return ModelSummary object for current state of Injector instance. """ M = ModelSummary( '%s/%s Injector'%(self.coreObj.oxName, self.coreObj.fuelName) ) M.add_alt_units('psia', ['MPa','atm','bar']) M.add_alt_units('psid', ['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_alt_units('Hz', 'kHz') M.add_alt_units('elem/in**2', 'elem/cm**2') M.add_alt_units('in', ['mil','mm']) M.add_alt_units('mil', ['micron','mm']) M.add_alt_units( 'g/ml', ['lbm/inch**3', 'lbm/ft**3'] ) M.add_alt_units( 'lbf/in', ['N/m', 'mN/m', 'dyne/cm'] ) M.add_alt_units('poise', ['cpoise', 'Pa*s', 'lbm/hr/ft']) M.add_alt_units( 'BTU/lbm', ['cal/g', 'J/g'] ) M.add_alt_units('in**2', 'cm**2') category_setD = {} # index=category name, value=set of parameter names in category category_setD['Ox Properties'] = set() category_setD['Fuel Properties'] = set() M.add_inp_category( '' ) # show unlabeled category 1st M.add_out_category( '' ) # show unlabeled category 1st for name in self.is_inputD.keys(): if name.lower().find('ox') >= 0: category_setD['Ox Properties'].add( name ) if name.lower().find('fuel') >= 0: category_setD['Fuel Properties'].add( name ) # dpOx dpFuel def get_cat( name ): for cn, nameset in category_setD.items(): if name in nameset: return cn return '' specialFmtD = {'DorfFuel':'%.4f', 'DorfOx':'%.4f', 'DorfMin':'%.4f'} # function to add parameters from __doc__ string to ModelSummary def add_param( name, desc='', fmt='', units='', value=None): 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: try: value = getattr( self, name ) except: return if fmt=='': fmt = specialFmtD.get( name, '' ) if self.is_inputD.get(name, False): M.add_inp_param( name, value, units, desc, fmt=fmt, category=get_cat(name)) else: M.add_out_param( name, value, units, desc, fmt=fmt, category=get_cat(name)) # build a list of __doc__ parameters that should be ignored by ModelSummary ignoreL = ['coreObj', 'geomObj', 'effObj'] # ignore some Nelemens inputs if they do not apply if self.setNelementsBy == "acoustics": ignoreL.extend( ['elemDensInp', 'NelementsInp'] ) if self.setAcousticFreqBy == "mode": ignoreL.append( 'desFreqInp' ) else: ignoreL.append( 'desAcousMode' ) else: ignoreL.extend( ['desAcousMode', 'desFreqInp', 'DorfFlForHzLim'] ) # ignore delta P inputs if they do not apply if self.dpFuelInp is None: ignoreL.append( 'dpFuelInp' ) else: ignoreL.append( 'fdPinjFuel' ) if self.dpOxInp is None: ignoreL.append( 'dpOxInp' ) else: ignoreL.append( 'fdPinjOx' ) # iterate through __doc__ parameters and add them to ModelSummary for name in self.is_inputD.keys(): if name not in ignoreL: add_param( name ) # some conditional output NOT in :ivar xxx: section #if self.calc_effVap: if hasattr(self, 'rDropOx'): # only print internal vaporization values if calc'd #M.add_out_param( name, value, units, desc, fmt=fmt, category=get_cat(name)) M.add_out_param('rDropOx', get_value(self.rDropOx,'inch','mil'), 'mil', 'median ox droplet radius', fmt='%.4f', category='Vaporization') M.add_out_param('rDropFuel', get_value(self.rDropFuel,'inch','mil'), 'mil', 'median fuel droplet radius', fmt='%.4f', category='Vaporization') M.add_out_param('chamShapeFact', self.ShapeFact, '', 'chamber shape factor', fmt='%.4f', category='Vaporization') M.add_out_param('genVapLenOx', self.genVapLenOx, '', 'Priem generalized vaporization length of oxidizer', fmt='%.2f', category='Vaporization') M.add_out_param('genVapLenFuel', self.genVapLenFuel, '', 'Priem generalized vaporization length of fuel', fmt='%.2f', category='Vaporization') M.add_out_param('fracVapOx', self.fracVapOx, '', 'fraction of vaporized oxidizer', fmt='%.4f', category='Vaporization') M.add_out_param('fracVapFuel', self.fracVapFuel, '', 'fraction of vaporized fuel', fmt='%.4f', category='Vaporization') M.add_out_param('mrVap', self.mrVap,'', 'vaporized mixture ratio', fmt='%.4f', category='Vaporization') # some stability parameters #M.add_out_param( name, value, units, desc, fmt=fmt, category=get_cat(name)) M.add_out_param('tauOx', self.tauOx*1000.0 , 'ms','oxidizer lag time (tau/tResid=%g)'%self.tauOvResOx, category='Combustion Stability') M.add_out_param('tauFuel', self.tauFuel*1000.0 , 'ms','fuel lag time (tau/tResid=%g)'%self.tauOvResFuel, category='Combustion Stability') M.add_out_param('tResid', self.tResid*1000.0 , 'ms','residual time in chamber', fmt='%.4f', category='Combustion Stability') M.add_out_param('fdPinjOxReqd', self.fdPinjOxReqd, '', 'minimum required oxidizer dP/Pc', category='Combustion Stability') M.add_out_param('fdPinjFuelReqd', self.fdPinjFuelReqd, '', 'minimum required fuel dP/Pc', category='Combustion Stability') M.add_out_param(' cham sonicVel', self.sonicVel, 'ft/s', 'approximate gas sonic velocity in chamber', category='Combustion Stability') # show the acoustic modes in chamber # calc 3T and 1L for printout only f3T = modeSvnD['3T'] * self.sonicVel / pi / (self.geomObj.Dinj/12.0) freq1L = self.sonicVel * 12.0 / 2.0 / self.geomObj.Lcham modeL = [(freq1L,'1L')] # list of (freq, name) modeL.append( ( 0.8 * modeSvnD['1T'] * self.sonicVel / pi / (self.geomObj.Dinj/12.0), '80% of 1T') ) modeL.append( ( 0.8 * modeSvnD['1R'] * self.sonicVel / pi / (self.geomObj.Dinj/12.0), '80% of 1R') ) modeL.append( ( 0.8 * modeSvnD['3T'] * self.sonicVel / pi / (self.geomObj.Dinj/12.0), '80% of 3T') ) modeL.append( ( 0.999999 * modeSvnD['3T'] * self.sonicVel / pi / (self.geomObj.Dinj/12.0), ' 3T') ) modeL.append( (self.des_freq, '=====> DESIGN') ) for name, mult in modeSvnD.items(): modeL.append( (mult * self.sonicVel / pi / (self.geomObj.Dinj/12.0), name) ) modeL.sort() M.add_out_category('Acoustic Modes', allowsort=False) for (freq, name) in modeL: comment = modeCommentD.get( name, '') M.add_out_param(name, '%i'%int(freq), 'Hz', comment, category='Acoustic Modes') #M.add_out_param( name, value, units, desc, fmt=fmt, category=get_cat(name)) # -------------------- WARNING AND ASSUMPTION ADDITIONS -------------------------- # maybe add some warnings and assumptions mode, mode_freq, mode_msg = self.get_closest_mode() if self.coreObj.add_barrier: M.add_assumption( 'NOTE: Injector elements are designed by Initial Core Flow ONLY.' ) M.add_assumption( ' Fuel Film Cooling orifices must be designed separately.' ) M.add_assumption( 'NOTE: number of elements set by '+ self.setNelementsBy ) # parameters that are NOT attributes OR are conditional if self.setNelementsBy == "acoustics": if self.setAcousticFreqBy == "mode": if self.desAcousMode in modeSvnD: msg = self.desAcousMode #+ ' where: Svn mult = %g'%self.desAcousMult else: msg = 'Svn multiplier = %g'%self.desAcousMult elif self.setAcousticFreqBy == "freq": msg = 'freq=%g Hz'%self.desFreqInp M.add_assumption(' Acoustic frequency set by %s'%msg) mil = get_value(self.DorfFlForHzLimit,'inch','mil') if self.DorfFuel < self.DorfFlForHzLimit * 0.999: M.add_warning( 'WARNING... Fuel Orifice Diameter is Less Than D/V Requirement of %.1f mil'%mil ) else: M.add_assumption( 'Fuel Orifice Diameter Meets Stability Requirement of >= %.1f mil'%mil ) elif self.setNelementsBy == "input": M.add_assumption(' Number of elements = %g'%self.NelementsInp) elif self.setNelementsBy == "elem_density": M.add_assumption(' Element Density = %g elem/in**2'%self.elemDensInp) if self.used_Nelem_criteria != self.setNelementsBy: M.add_warning( 'WARNING... Number of elements set by: ' + self.used_Nelem_criteria ) M.add_warning( ' original intent was: ' + self.setNelementsBy ) M.add_assumption( 'Chamber design frequency set by: ' + self.used_Nelem_criteria +\ ' to: %g Hz,'%round(self.des_freq) + mode_msg ) if self.des_freq > f3T * 1.01: M.add_warning( 'WARNING... Design frequency is above recommended 3T limit of %i Hz'%int(f3T) ) if self.DorfOx < self.DorfMin * 0.999: mil = get_value(self.DorfMin,'inch','mil') M.add_warning( 'WARNING... Oxidizer Orifice Diameter is Less Than minimum limit of %.1f mil'%mil ) if self.DorfFuel < self.DorfMin * 0.999: mil = get_value(self.DorfMin,'inch','mil') M.add_warning( 'WARNING... Fuel Orifice Diameter is Less Than minimum limit of %.1f mil'%mil ) if self.Tox_warning: M.add_warning( self.Tox_warning ) if self.Tfuel_warning: M.add_warning( self.Tfuel_warning ) if self.dpOx/self.coreObj.Pc < self.fdPinjOxReqd: M.add_warning('WARNING... Oxidizer pressure drop is below stability requirement') M.add_warning(' Oxidizer dP/Pc=%g, should be >= %g'%(self.dpOx/self.coreObj.Pc, self.fdPinjOxReqd) ) if self.dpFuel/self.coreObj.Pc < self.fdPinjFuelReqd: M.add_warning('WARNING... Fuel pressure drop is below stability requirement') M.add_warning(' Fuel dP/Pc=%g, should be >= %g'%(self.dpFuel/self.coreObj.Pc, self.fdPinjFuelReqd) ) #if self.dpOxInp is None: # M.add_assumption('dPinjector oxidizer set by fdPinjOx = %g'%self.fdPinjOx) #else: # M.add_assumption('dPinjector oxidizer set by dpOxInp = %.1f psia'%self.dpOxInp) #if self.dpFuelInp is None: # M.add_assumption('dPinjector fuel set by fdPinjFuel = %g'%self.fdPinjFuel) #else: # M.add_assumption('dPinjector fuel set by dpFuelInp = %.1f psia'%self.dpFuelInp) return M
try: self.log10visc_terp = InterpProp(self.trL, self.log10viscL, extrapOK=False) except: pass try: self.cond_terp = InterpProp(self.trL, self.condL, extrapOK=False) except: pass self.cp_terp = InterpProp(self.trL, self.cpL, extrapOK=False) self.hvap_terp = InterpProp(self.trL, self.hvapL, extrapOK=False) self.surf_terp = InterpProp(self.trL, self.surfL, extrapOK=False) self.SG_liq_terp = InterpProp(self.trL, self.SG_liqL, extrapOK=False) self.log10SG_vap_terp = InterpProp(self.trL, self.log10SG_vapL, extrapOK=False) if __name__ == '__main__': from rocketprops.unit_conv_data import get_value C = Prop() print('T = %g R = %g K'%( C.T, C.T/1.8 )) print('SurfaceTension = %g lbf/in'%C.surf, ' = ', get_value(C.surf, 'lbf/in', 'mN/m'), 'mN/m' ) print() print(' Tr_data_range =', C.Tr_data_range()) print(' T_data_range=',C.T_data_range()) print(' P_data_range=', C.P_data_range()) print( 'omega =%s'%C.omega ) C.plot_sat_props()
if __name__ == "__main__": from rocketprops.rocket_prop import get_prop """ Calculate the required volume of a Hydrazine (N2H4) tank. Assume: required usable propellant is 50 kg vehicle max operating/storage/transport temperature is 50 deg C. minimum ullage volume is 3%. expulsion efficiency = 98%. """ pObj = get_prop('hydrazine') cc_Total, kg_loaded, kg_residual = calc_tank_volume( pObj, kg_expelled=50.0, TmaxC=50.0, expPcent=98.0, ullPcent=3.0 ) print('cc_Total = %g cc'%cc_Total) print('loaded propellant mass = %g kg'%kg_loaded ) print('residual propellant mass = %g kg'%kg_residual ) print('loaded / expelled mass =', kg_loaded / 50.0) Thot = get_value( 50.0, 'degC', 'degR' ) SGhot = pObj.SGLiqAtTdegR( Thot ) print('kg residual = SGhot * 0.02 * cc_Total / 1000 =', SGhot * 0.02 * cc_Total / 1000, 'kg' ) cc_expelled = 50.0 * 1000.0 / SGhot print('cc_expelled =',cc_expelled, ' mass expelled = %g g'%(SGhot*cc_expelled,) ) print('cc_expelled / cc_Total =', cc_expelled / cc_Total)
sg_ecL = [] errL = [] terrL = [] Tcutoff = p.TdegRAtPsat(P) for T in range( int(Tmin), int(Tmax), 1 ): if T < Tcutoff: #sg_rp = p.SG_compressedCOSTALD( T, P) #sg_rp = p.SG_compressed( T, P) #sg_rp = p.SG_compressedCZ2( T, P ) sg_rp = p.SG_compressedNasrfar(T, P) ec.setTP( T=T, P=P ) sg_ec = get_value( ec.rho, 'lbm/in**3', 'SG' ) if sg_rp is not None and sg_ec is not None: sg_rpL.append( sg_rp ) sg_ecL.append( sg_ec ) tL.append( T / p.Tc ) terrL.append( T / p.Tc ) errL.append( 100.0*(sg_rp - sg_ec)/ sg_ec ) plt.plot( tL, sg_rpL, '-', label='RocketProps P=%g'%P, color=COLORL[i%len(COLORL)]) plt.plot( tL, sg_ecL, '--', label='CoolProp P=%g'%P, color=COLORL[i%len(COLORL)]) i += 1 errLL.append( (terrL, errL, P) ) plt.legend() plt.grid()
def SG_compressedNasrfar(self, TdegR, Ppsia): r'''Calculates compressed-liquid specific gravity, using the Nasrfar Moshfeghian method from Journal of Molecular Liquids 160 (2011) 94-102 :param TdegR: temperature in degR :param Ppsia: pressure in psia :type TdegR: float :type Ppsia: float :return: specific gravity at (TdegR, Ppsia) in g/ml ''' Pr = Ppsia / self.Pc Tr = TdegR / self.Tc Psat = self.PvapAtTdegR(TdegR) Psatr = Psat / self.Pc SGsat = self.SGLiqAtTdegR(TdegR) if Ppsia < Psat: print( 'Warning in SG_compressed... Pressure below Psat for T=%g degR, P=%g psia' % (TdegR, Ppsia)) return SGsat Vs = self.MolWt / SGsat # [cm^3/mol] j0 = 1.3168E-3 j1 = 3.4448E-2 j2 = 5.4131E-2 L = 9.6840E-2 M = 8.6761E-6 f0 = 48.8756 G = 0.7185 I = 3.4031E-5 c0 = 5.5526 c1 = -2.7659 Om0 = 7.9019E-2 Om1 = -2.8431E-2 R = 8.3144598 # m^3-Pa / mol-K J = j0 + j1 * (1 - Tr)**(1. / 3.) + j2 * (1 - Tr)**(2. / 3.) F = f0 * (1 - Tr) C = c0 + c1 * self.omega TcK = get_value(self.Tc, 'degR', 'degK') PcMPa = get_value(self.Pc, 'psia', 'MPa') Om = Om0 + Om1 * self.omega Vinf = Om * R * TcK / PcMPa # cm^3/mol if Tr > 0.94: print( 'Warning in SG_compressed... Reduced Temperature > 0.95 Tr=%g ' % Tr) dpr = max(0, Pr - Psatr) numer = J + L * dpr + M * dpr**3 denom = F + G * dpr + I * dpr**3 vrat = C * numer / denom V_dense = vrat * (Vinf - Vs) + Vs # [cm^3/mol] SG_dense = self.MolWt / (V_dense) # g/cm^3 return SG_dense
sg_p1L = [] sg_p2L = [] sg_p3L = [] sg_p4L = [] Psat_ec, DliqSat, DgasSat = ec.getSatPandDens( Tr * ec.Tc ) Psat = p.PvapAtTr( Tr ) dpL = [dp for dp in range(0,6000, 100)] for dp in dpL: T = Tr * p.Tc P = Psat + dp + 1 P_ec = Psat_ec + dp + 1 ec.setTP( T=Tr*ec.Tc, P=P_ec ) sg_ecL.append( get_value( ec.rho, 'lbm/in**3', 'SG' ) ) sg_p1L.append( p.SG_compressedCOSTALD( T, P) ) sg_p2L.append( p.SG_compressedCZ1( T, P) ) sg_p3L.append( p.SG_compressedCZ2( T, P ) ) sg_p4L.append( p.SG_compressedNasrfar(T, P) ) fig = plt.figure() plt.title( p.pname + ' SG Compressed Comparison at Tr=%g'%Tr ) plt.plot( dpL, sg_ecL, '--', label='CoolProp', linewidth=5) plt.plot( dpL, sg_p1L, '--', label='COSTALD-Default', linewidth=3) plt.plot( dpL, sg_p2L, '-', label='CZ1') plt.plot( dpL, sg_p3L, '-', label='CZ2') plt.plot( dpL, sg_p4L, '-', label='Nasrfar')
__copyright__ = 'Copyright (c) 2020 Charlie Taylor' __license__ = 'GPL-3' exec(open(os.path.join( here, '_version.py')).read()) # creates local __version__ variable __email__ = "*****@*****.**" __status__ = "4 - Beta" # "3 - Alpha", "4 - Beta", "5 - Production/Stable" # # import statements here. (built-in first, then 3rd party, then yours) # from math import exp, log import importlib from rocketprops.unit_conv_data import get_value from rocketprops.prop_names import prop_names TREF_K = get_value(20.0, 'degC', 'degK') def get_prop(name, suppress_warning=False): """ Return a Propellant object for the named propellant. :param name: name of propellant (for example "N2O4" or "LOX") :param suppress_warning: if True, then do not print warnings. :type name: string :type suppress_warning: boolean :return: Propellant object for named propellant :rtype: Propellant """ pname = prop_names.get_primary_name(name) if pname is None: