In [24]:
%run ../trajectory.ipynb
import csv


ERROR:root:File `'./openrocket_interface.ipynb.py'` not found.
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
~/Code/liquid-engine-analysis/trajectory.ipynb in <module>
      2 # dt is time step [s]
      3 def trajectory(m_prop, mdot, dia, p_e, bounds=(500, 1500), x_init=0, dt=.1,
----> 4                p_ch=p_ch, T_ch=T_ch, ke=ke, Re=Re):
      5     sim = Simulation(m_prop, mdot, dia, p_e, bounds[0], bounds[1], min_throttle, x_init,
      6                      p_ch, T_ch, ke, Re) # get this puppy rolling

NameError: name 'p_ch' is not defined

Propellant Trade Space Analyzer

Purpose

This document is for comparing different configurations of liquid propellant. Before a design optimization can occur, we must specify certain propellant characteristics, as determined by our selection and the outputs of CEArun. However, it is not immediately obvious from just thermodynamic properties how various propellant configurations will effect the rocket's performance as a whole. Hence, this.

Context

In order to compare propellant configurations, we make the ceterus paribus assumption that all other aspects of the LV4 rocket are held constant. The canonical outputs of our Multi-disciplinary Design Optimization are used to dictate the total mass of propellants, total mass flow rates, and exit pressure. The same trajectory simulation that is used in the MDO is used here to numerically integrate the results of launching a rocket with a given propellant configuration.

Parameters

We must explicitly choose the following pieces of information:

  • Chamber Pressure, PSI (assumed identical to at the injector)
  • Oxygen/Fuel ratio
  • Oxidizer material (assumed to be liquid oxygen)
  • Fuel material, water ratio, and holding temperature (assumed 419.15 K)
    • This entails fuel density, kg/m^3, which must be hand-calculated as a weighted average of pure fuel and pure water

The following pieces of information are given by CEArun:

  • Chamber temperature, K
  • Specific heat ratio of propellant (gamma or ke)
  • Molar mass, g/mol
    • The specific gas constant is calculated for us based on molar mass.

Inputs

This script requires only a .csv file with one row for each propellant configuration and one column for each parameter.

Outputs

The relevant outputs of each trajectory simulation are saved as a text file.

Standard Operating Procedure

  1. Read (most of) this document.
  2. Open ./propellants/propellants.csv in a text editor.
  3. Follow formatting example and for each propellant (on its own line) enter in this order (using the specified units):
    • fuel density, O/F ratio, chamber pressure, chamber temperature, specific heat ratio, molar mass, and a string to label the propellant configuration.
  4. Save the .csv file.
  5. Run all code blocks in this document in sequential order.
  6. ???
  7. Profit!

Canonical Design Parameters

This code block is for your reference.


In [2]:
# canonical lv4 design variables
m_prop_can = 123.35069377654865
mdot_can   = 2.6244828455178943
p_e_can    = 42.974369360377864

# canonical propellant configuration, for reference
# combustion gas properties ke, Re, T_ch, determined from CEArun
# with chamber pressure=350 psi, fuel temp=419.15 K, 
#      lox temp=90 K, OF=1.3 for fuel = 64.8% IPA (2propanol) / 35.2% H20
rho_ipa = 849.28   # kg/m^3  Density of 64.8% IPA / 35.2% H20
OF   = 1.3        # O/F ratio, this is somewhat arbitrary but CEA says its good.
p_ch = 350 # chamber pressure, PSI
T_ch = 3097.82 # chamber temperature, K
ke   = 1.1251 # specific heat ratio, propellant (aka gammas)
M    = 23.196 # molar mass
Re   = spec_gas(M) # specific gas constant, propellant

Explanans

First of all, I'm sorry about the hacky use of global variables. They don't cause any problems, but they are unsightly.

test_run takes a given propellant configuration, runs a trajectory simulation, and returns the simulation object.

print_results takes a simulation object and an index number, and saves all the relevant information from the trajectory to a text file in ./propellants/ named by the index.

load_csv takes a string that names a .csv file, and returns an isomorphic multidimensional array.

run_batch takes no explicit arguments and the name of the .csv file as an implicit parameter, and then runs and saves a trajectory simulation for each propellant configuration.

The last code block of this document simply calls run_batch(). The end-user need only concern themself with setting up the .csv file correctly.


In [3]:
def test_run(ipa, OF_param, p_ch, T_ch, ke, M, comment): # OF implicitly invoked, don't worry
    global OF, rho_ipa # I SAID I'M SORRY!!
    rho_ipa = ipa
    OF = OF_param
    Re = spec_gas(M)
    sim = trajectory(m_prop_can, mdot_can, dia, p_e_can,
                     p_ch=p_ch, T_ch=T_ch, ke=ke, Re=Re)
    sim.comment = comment
    sim.OF = OF_param
    sim.ipa = rho_ipa
    sim.M = M
    return sim

In [4]:
# this creates a list of relevant strings from trajectory
def print_results(sim, index):
    text_base = [] # list of lines of strings
    
    np.set_printoptions(precision=3) # this line may be deprecated, i copy-pasted most of this section
    
    text_base.append('INPUTS')
    text_base.append('\nComment: ' + sim.comment)
    text_base.append('\nFuel density                               = %5.3f kg/m^3' % sim.ipa)
    text_base.append('\nO/F ratio                                  = %5.3f ' % sim.OF)
    text_base.append('\nchamber pressure                           = {:.3f} kPa'.format(sim.p_ch/1000))
    text_base.append('\nChamber temperature                        = {:.3f} K'.format(sim.T_ch))
    text_base.append('\nSpecific heat ratio                        = %5.3f' % sim.ke)
    text_base.append('\nMolar mass                                 = %5.3f' % sim.M)
    text_base.append('\nSpecific gas constant                      = %5.3f J/K' % sim.Re)
    text_base.append('\n')
    text_base.append('\nDESIGN PARAMETERS')
    text_base.append('\n-----------------------------')
    text_base.append('\ndesign total propellant mass               = {:.3f} kg'.format(m_prop_can))
    text_base.append('\ndesign mass flow rate                      = {:.3f} kg/s'.format(mdot_can))
    text_base.append('\ndesign nozzle exit pressure                = {:.3f} kPa'.format(p_e_can))
    text_base.append('\n')
    text_base.append("\nENGINE SYSTEM DETAILS")
    text_base.append("\n-----------------------------")
    text_base.append('\ndesign Throat pressure                     = {:.3f} kPa'.format(sim.p_t/1000))
    text_base.append('\ndesign Throat temperature                  = {:.3f} K'.format(sim.T_t))
    text_base.append('\ndesign exit velocity                       = {:.3f} m/s'.format(sim.Ve))
    text_base.append('\ndesign thrust (ground level)               = {:.3f} kN'.format(sim.F[0]/1000))
    text_base.append('\ndesign thrust (vacuum)                     = {:.2f} kN'.format(sim.F[sim.F_index]/1000))
    text_base.append('\ndesign expansion ratio                     = {:.3f}'.format(sim.ex))
    text_base.append('\ndesign Exit area                           = {:.3f} in.^2'.format(sim.A_e/0.0254**2))
    text_base.append('\ndesign throat area                         = {:.3f} in.^2'.format(sim.A_t/0.0254**2))
    text_base.append('\ndesign isp                                 = {:.3f} s'.format(sim.Ve/g_n))
    text_base.append('\ndesign total impulse                       = {:.3f} kN*s'.format(
                                                  sim.t[sim.F_index]*(sim.F[sim.F_index]/1000 + sim.F[0]/1000)/2))
    text_base.append('\ndesign dV                                  = {:.3f} km/s'.format(sim.dV1))
    text_base.append('\nmission time at burnout                    = {:.3f} s'.format(sim.t[sim.F_index]))
    
    text_base.append('\n\nPlumbing Details\n------------------')
    # Mass flow for each propllent
    mdot_o, mdot_f = proportion(mdot_can)
    text_base.append("\nOx flow: . . . . . . . . . . %7.3f kg/s" % mdot_o)
    text_base.append("\nFuel flow:                   %7.3f kg/s" % mdot_f)
    
    # Propellent Mass for each propllent
    mprop_o, mprop_f = proportion(m_prop_can)
    text_base.append("\nOx mass: . . . . . . . . . . . %5.3f kg" % mprop_o)
    text_base.append("\nFuel mass:                     %5.3f kg" % mprop_f)
    
    # dimensions of each tank
    text_base.append("\nTank outer diameters: . . . . . . . %7.3f m" % (2*sim.r))
    text_base.append("\nOx tank length + ullage:      %7.3f m" % sim.l_o)
    text_base.append("\nFuel tank length + ullage:    %7.3f m" % sim.l_f)
    
    # Tank thickness for each tank (mm)
    thickness_o = tank_thickness(Al, sim.r)
    thickness_f = tank_thickness(CF, sim.r)
    text_base.append("\nOx tank thickness:            %5.3f mm" % (thickness_o*1000))
    text_base.append("\nFuel tank thickness:          %5.3f mm" % (thickness_f*1000))
    
    # Mass of each tank
    m_tank_o = tank_mass(sim.l_o, Al, sim.r)
    m_tank_f = tank_mass(sim.l_f, CF, sim.r)
    
    text_base.append("\nOx tank mass: . . . . . . . . %5.3f kg" % m_tank_o)
    text_base.append("\nFuel tank mass:               %5.3f kg" % m_tank_f)
    
    
    text_base.append('\n')
    text_base.append('\nRELEVANT CONSTRAINTS')
    text_base.append('\n-----------------------------')
    text_base.append('\naltitude at apogee (c.f. > {})          = {:.3f} km'.format(
                                                                                cons_alt/1000, sim.alt[-1]/1000))
    text_base.append("\nmax acceleration (c.f. < {})               = {:.3f} gs".format(
                                                                                cons_accel, sim.max_g_force))
    text_base.append('\nTWR at lift off (c.f. > {})                 = {:.3f}'.format(cons_TWR, sim.TWR))
    text_base.append('\nspeed when leaving launch rail (c.f. > {}) = {:.3f} m/s'.format(cons_ls,sim.launch_speed))
    
    with open('propellants/'+str(index)+'_info.txt', 'w') as info:
        for line in text_base:
            info.write(line)

In [5]:
def load_csv(file_name):
    designs = []
    with open(file_name) as text:
        csv_reader = csv.reader(text, delimiter=',')
        counter = 0
        for line in csv_reader:
            if counter != 0:
                designs.append(line)
            counter += 1
    return designs

In [6]:
def run_batch():
    designs = load_csv('propellants/propellants.csv')
    for i, des in enumerate(designs):
        run = test_run(float(des[0]), float(des[1]), float(des[2]),
                       float(des[3]), float(des[4]), float(des[5]), des[6])
        print_results(run, i)

In [7]:
run_batch()