def main(): #build some gas objects print "pure He driver gas" state4 = Gas({'He': 1.0}, inputUnits='moles', outputUnits='moles') state4.set_pT(2.79e+07, 2700.0) print "state1:" state4.write_state(sys.stdout) print "Air test gas" state1 = Gas({ 'Air': 1.0, }) state1.set_pT(3000.0, 300.0) print "state1:" state1.write_state(sys.stdout) # print "Air accelerator gas" state5 = Gas({'Air': 1.0}) state5.set_pT(10.0, 300.0) print "state10:" state5.write_state(sys.stdout) print "Steady expansion of driver" #for 100%He condition mach number terminating steady expansion is 2.15 #need to work out the corresponding pressure for this to put into #the function M4dash = 2.15 #mach number terminating steady expansion (state4dash, V4dash) = expand_from_stagnation(1.0 / (p0_p(M4dash, state4.gam)), state4) print "state4dash:" state4dash.write_state(sys.stdout) print "V4dash = {0} m/s".format(V4dash) #I think we need to start guessing shock speeds to be able to keep going here print "Start working on the shock into the test gas" print "Guess US1 of 5080 m/s" state2 = state1.clone() V2, V2g = normal_shock(state1, 5080.0, state2) print "state2:" state2.write_state(sys.stdout) print "Checks:" print "p2/p1=", state2.p / state1.p print "rho2/rho1=", state2.rho / state1.rho print "T2/T1=", state2.T / state1.T print "V2g = {0} m/s".format(V2g) print "Unsteady expansion from state 4dash to state 3" print "Well, giving it a go for now anyway" (V3, state3) = finite_wave_dp('cplus', V4dash, state4dash, state2.p, steps=100) print "state3:" state3.write_state(sys.stdout) print "V3 = {0} m/s".format(V3) print "Start working on the shock into the accelerator gas" print "Guess US2 of 11100 m/s" state6 = state5.clone() V6, V6g = normal_shock(state5, 11100.0, state6) print "state6:" state6.write_state(sys.stdout) print "Checks:" print "p2/p1=", state6.p / state5.p print "rho2/rho1=", state6.rho / state5.rho print "T2/T1=", state6.T / state5.T print "V6g = {0} m/s".format(V6g) print "Unsteady expansion from state 2 to state 7" print "Well, giving it a go for now anyway" (V7, state7) = finite_wave_dp('cplus', V2g, state2, state6.p, steps=100) print "state7:" state7.write_state(sys.stdout) print "V7 = {0} m/s".format(V7) M7 = V7 / (state7.gam * state7.R * state7.T)**(0.5) print "M7 = {0}".format(M7) """
def poshax_python( p_inf, T_inf, reactants, inputUnits, species_list, no_of_temperatures, reaction_file, outputUnits='massf', u_inf=None, M_inf=None, energy_exchange_file=None, gas_model_file='gas_model', cfg_file='cfg_file.cfg', output_file='output_file.data', source_term_coupling='loose', dx=1.0e-16, adaptive_dx='false', final_x=5.0e-3, plot=True, save_figures=True, temp_result_fig_name='temp_result', species_result_fig_name='species_result', freestream_normalised_result_fig_name='freestream_normalised_result', eq_normalised_result_fig_name='equilibrium_normalised_result', title=None, log_x_axis=False, plot_x_limits=None): """ :param p_inf: Freestream pressure (Pa) :param T_inf: Freestream temperature (K) :param reactants: Reactants dictionary which will be sent to cfypylib's cea2 Gas object. So it must conform with the required syntax!! an example is {'N2':0.79,'O2':0.21} for 'cfd air' specified by moles, but check the cea2_gas info for more info about the required format. inputUnits for this dictionary can be either in 'moles' or 'massf' with the standard specified in the next input :param inputUnits: inputUnits string to be again set to the cea2 Gas object. Should be either 'moles' or 'massf' :param species_list: Species list for POSHAX!! NOT CEA!! so ions and electons will be N2_plus, e_minus This data will be parsed to cea as the input state will only use these species, but the code will automatically perform the conversion need to send this list to CEA. This list is for sending to the program whcih creates the gas file for poshax :param no_of_temperatures: Number of temperatures. Integer input of 1,2, or 3. More than one temperature models require the specification of an energy exchange .lua file. (See below.) :param reaction_file: String pointing to the location of the reaction scheme. :param u_inf: Freestream velocity (m/s). Defaults to None, but either u_inf or M_inf must be specified. :param M_inf: Freestream Mach number. Defaults to None, but either u_inf or M_inf must be specified. :param energy_exchange_file: String pointing to the location of the energy exchange scheme for multi temperature models. Defaults to None, but the code will not run with a multi temperature model if this file is not found. :param gas_model_file: String name of the gas model file which the code will create as a .inp file which the program gasfile uses to make the .lua gasfile for poshax. Defaults to 'gas_model'. :param cfg_file: String name of the poshax cfg file. Defaults to 'cfg_file.cfg'. :param output_file: String name of the poshax output file. Defaults to 'output_file.data'. :param source_term_coupling: poshax source term coupling file. Defaults to 'loose'. :param dx: poshax dx (in m) defaults to 1.0e-16 m. :param adaptive_dx: Whether to use adaptive_dx with poshax. Defaults to 'true'. :param final_x: poshax final x value (in m). Defaults to 5.0e-3. :param plot: Whether to plot the result or not. Defaults to True. Will make four plots. A temperature plot, mass fraction plot of all species, result normalised by freestream values, and a result normalised by equilibrium values. :param save_figures: Whether to save figures or not. Defaults to True. results are saved in eps, png, and pdf formats. :param temp_result_fig_name: Temperature result plot file name. Defaults to 'temp_result'. :param species_result_fig_name: Species result plot file name. Defaults to 'species_result'. :param freestream_normalised_result_fig_name: Freestream normalised results plot file name. Defaults to 'freestream_normalised_result'. :param eq_normalised_result_fig_name: Equilibrium normalised results plot file name. Defaults to 'equilibrium_normalised_result'. :param title: allows a title to be added to the plots. Defaults to None. Is a single title so, for example, the condition being simulated can be added to the title. :param log_x_axis: Plots the figure with the x axis on a log scale. Defaults to False. plot_x_limits = None :return: Returns a Python dictionary version of the poshax results file. """ print '-' * 60 print "Running poshax python version {0}".format(VERSION_STRING) # do any input checking which is required # probably don't need to do everything in the world here, # but just some important things... # probably want to check that p, T, and u values, if not isinstance(p_inf, (float, int)): raise Exception, "poshax_python(): 'p_inf' input ({0}) is not a float or an int.".format( p_inf) if not isinstance(T_inf, (float, int)): raise Exception, "poshax_python(): 'T_inf' input ({0}) is not a float or an int.".format( T_inf) if not u_inf and not M_inf: raise Exception, "poshax_python(): Simulation must have either a 'u_inf' or 'M_inf' value." if u_inf and M_inf: raise Exception, "poshax_python(): Simulation cannot have have both a 'u_inf' and an 'M_inf' value." if u_inf and not isinstance(u_inf, (float, int)): raise Exception, "poshax_python(): 'u_inf' input ({0}) is not a float or an int.".format( u_inf) if M_inf and not isinstance(M_inf, (float, int)): raise Exception, "poshax_python(): 'M_inf' input ({0}) is not a float or an int.".format( M_inf) if not isinstance(reactants, dict): raise Exception, "poshax_python(): 'reactants' input ({0}) is not a dictionary.".format( reactants) if inputUnits not in ['moles', 'massf']: raise Exception, "poshax_python(): 'inputUits' input ({0}) is not either 'moles' or 'massf'.".format( inputUnits) if not isinstance(reaction_file, str): raise Exception, "poshax_python(): 'reaction_file' input ({0}) is not a string.".format( reaction_file) if not os.path.exists(reaction_file): raise Exception, "poshax_python(): 'reaction_file' input ({0}) does not appear to exist in the specified location.".format( reaction_file) if no_of_temperatures > 1 and not energy_exchange_file: raise Exception, "poshax_python(): Multi temperature model has been specified without an energy exchange file." if no_of_temperatures > 1: if not isinstance(energy_exchange_file, str): raise Exception, "poshax_python(): 'energy_exchange_file:' input ({0}) is not a string.".format( energy_exchange_file) if not os.path.exists(energy_exchange_file): raise Exception, "poshax_python(): 'reaction_file' input ({0}) does not appear to exist in the specified location.".format( energy_exchange_file) if not isinstance(dx, float): raise Exception, "poshax_python(): 'dx' input ({0}) is not a float.".format( dx) if not isinstance(final_x, float): raise Exception, "poshax_python(): 'final_x' input ({0}) is not a float.".format( final_x) print '-' * 60 print "User specified input values are:" if no_of_temperatures == 1: model = "thermally perfect gas" elif no_of_temperatures == 2: model = "two temperature gas" elif no_of_temperatures == 3: model = "three temperature gas" print "Gas state input settings:" print "p_inf = {0} Pa, T_inf = {1} K, u_inf = {2} m/s".format( p_inf, T_inf, u_inf) print "Reactants for start of CEA inflow calculation are:" print '{0} (by {1})'.format(reactants, inputUnits) print "Species in the calculation are:" print species_list print "Note: the CEA equilibrium inflow calculation will use only these species." print "This means that the inflow may be unrealistic if the species do not match " print "the real state of the gas at the user specified temperature and pressure" print '-' * 60 print "poshax settings:" print "dx = {0} m, final_x = {1} m ({2} mm), with adaptive_dx set to {3}".format( dx, final_x, final_x * 1000, adaptive_dx) print "calculation uses a {0} model, with {1} source term coupling.".format( model, source_term_coupling) print "Reaction scheme file is {0}".format(reaction_file) if no_of_temperatures > 1: print "Energy exchange file is {0}.".format(energy_exchange_file) print "Gas model file to be created will be called {0}.lua.".format( gas_model_file) print "poshax input file to be created will be called {0}.".format( cfg_file) print "poshax output / results file to be created will be called {0}".format( output_file) # as cea wants a slightly different form from poshax... species_list_for_cea = [] for species in species_list: if '_plus' in species: # copy the original species here so we don't mess with the old one!! species_copy = copy.copy(species) species_list_for_cea.append(species_copy.replace('_plus', '+')) elif '_minus' in species: # copy the original species here so we don't mess with the old one!! species_copy = copy.copy(species) species_list_for_cea.append(species_copy.replace('_minus', '-')) else: # just append the value... species_list_for_cea.append(species) print '-' * 60 print "Creating user specified equilibrium gas inflow using CEA:" # get mass fractions from CEA as poshax needs massf inflow state1 = Gas(reactants=reactants, with_ions=True, inputUnits=inputUnits, outputUnits=outputUnits, onlyList=species_list_for_cea) state1.set_pT(p_inf, T_inf) #Pa, K # print state to the screen... state1.write_state(sys.stdout) print '-' * 60 print "Preparing the gas .inp file from user specifications..." with open('{0}.inp'.format(gas_model_file), 'w') as gas_file: gas_file.write('model = "{0}"'.format(model) + '\n') species_line = 'species = {' for species in species_list: if species != species_list[-1]: species_line += "'{0}', ".format(species) else: species_line += "'{0}'".format(species) # now add the end bracket... species_line += '}' gas_file.write(species_line + '\n') gas_file.close() print "Running gasfile program to generate .lua gasfile." subprocess.check_call([ "gasfile", "{0}.inp".format(gas_model_file), "{0}.lua".format(gas_model_file) ]) print "Gas file prepared successfully." # now we need to build the .cfg file... print '-' * 60 print "Building poshax .cfg file from user specifications" with open(cfg_file, 'w') as cfg_file_ojbect: # add in all of the controls... cfg_file_ojbect.write('[controls]' + '\n') cfg_file_ojbect.write( 'source_term_coupling = {0}'.format(source_term_coupling) + '\n') cfg_file_ojbect.write('dx = {0}'.format(dx) + '\n') if adaptive_dx: # remember that a value of 'false' would still be a string... cfg_file_ojbect.write('adaptive_dx = {0}'.format(adaptive_dx) + '\n') cfg_file_ojbect.write('final_x = {0}'.format(final_x) + '\n') cfg_file_ojbect.write('output_file = {0}'.format(output_file) + '\n') cfg_file_ojbect.write('species_output = {0}'.format(outputUnits) + '\n') # the models... cfg_file_ojbect.write('[models]' + '\n') # a version of this without the .lua was defined above... cfg_file_ojbect.write( 'gas_model_file = {0}.lua'.format(gas_model_file) + '\n') cfg_file_ojbect.write('reaction_file = {0}'.format(reaction_file) + '\n') if no_of_temperatures > 1: cfg_file_ojbect.write( 'energy_exchange_file = {0}'.format(energy_exchange_file) + '\n') # initial conditions... cfg_file_ojbect.write('[initial-conditions]' + '\n') cfg_file_ojbect.write('p_inf = {0}'.format(p_inf) + '\n') if no_of_temperatures == 1: cfg_file_ojbect.write('T_inf = {0}'.format(T_inf) + '\n') elif no_of_temperatures == 2: cfg_file_ojbect.write('T_inf = {0}, {0}'.format(T_inf) + '\n') elif no_of_temperatures == 3: cfg_file_ojbect.write('T_inf = {0}, {0}, {0}'.format(T_inf) + '\n') if u_inf: cfg_file_ojbect.write('u_inf = {0}'.format(u_inf) + '\n') if M_inf: cfg_file_ojbect.write('M_inf = {0}'.format(M_inf) + '\n') # now go through species and we'll make a species line if outputUnits == 'moles': species_line = 'molef_inf = ' elif outputUnits == 'massf': species_line = 'massf_inf = ' for species in species_list: if species in state1.species: if species != species_list[-1]: species_line += '{0}, '.format(state1.species[species]) else: species_line += '{0}'.format(state1.species[species]) else: if species != species_list[-1]: species_line += '0.0, ' else: species_line += '0.0' cfg_file_ojbect.write(species_line + '\n') cfg_file_ojbect.close() print "Cfg file building successfully" print '-' * 60 print "Now running poshax program..." exit_code = subprocess.check_call(["poshax3.x", cfg_file]) if exit_code == 0: print "poshax ran successfully" else: print "poshax appears to have had some issues. check your inputs!" print '-' * 60 print "Performing equilibrium normal shock calculation to compare with poshax result." state2 = state1.clone() if not u_inf: u_inf = M_inf * state1.a V2, V2g = normal_shock(state1, u_inf, state2) print "Post-shock equilibrium state is :" print "post-shock velocity = {0} m/s".format(V2) state2.write_state(sys.stdout) # now opening the result so it can be returned... no_of_species = len(species_list) species_start_line = 6 + no_of_temperatures # 2 intro lines, x, p, rho, u lines_to_skip = 6 + no_of_temperatures + no_of_species # 2 intro lines, x, p, rho, u # I wanted to call my variable output_file (I nromally called the string output_filename) # but wanred to try to keep my variables consistent with poshax... so now I have results_file! with open(output_file, "r") as results_file: results_dict = {} columns = [] output_species_list = [] # start by grabbing columns names which wek now columns.append('x') if no_of_temperatures == 1: T_list = ['T'] elif no_of_temperatures == 2: T_list = ['T_trans_rotational', 'T_vibrational_electronic'] elif no_of_temperatures == 3: T_list = [ 'T_trans_rotational', 'T_vibrational_electronic', 'T_electron' ] columns += T_list columns += ['p', 'rho', 'u'] # we'll get the rest of the columns from species... for i, row in enumerate(results_file): if i >= species_start_line and i < lines_to_skip: # we could just get the species here, but good to keep it this way # so we can abstract the plotting into a function in the future... # (i.e. so it could run with no knowledge of the species beforehand, just the number of them... # here we want to split the line about the '-' so we can get the final value # need to use .strip() to get rid of the new line character on teh end too... split_row = row.strip().split('-') # last should be what we want... columns.append(split_row[-1]) output_species_list.append(split_row[-1]) elif i >= lines_to_skip: # to skip the header and know when the data starts... if i == lines_to_skip: # we need to add our columns to the dictionary! for column in columns: results_dict[column] = [] # now we start filling in data... split_row = row.split() # go through the split row and fill the data away in the right columns.. for column, value in zip(columns, split_row): results_dict[column].append(float(value)) results_dict['output_species_list'] = output_species_list if plot: print '-' * 60 print "Now plotting the result" import matplotlib.pyplot as plt import numpy as np # this is from shot_class_plotter.py, even if we don't use all of these... font_sizes = { 'title_size': 18, 'label_size': 18, 'annotation_size': 10, 'legend_text_size': 12, 'tick_size': 13, 'marker_size': 7, 'main_title_size': 20, 'marker': 'o', 'capsize': 4 } #--------------------------------------------------------------------------- # temp plot fig, ax = plt.subplots() x_list = np.array(results_dict['x']) * 1000.0 # convert to mm for T in T_list: ax.plot(x_list, results_dict[T], label=T) # add eq temp from CEA... ax.plot([x_list[0], x_list[-1]], [state2.T, state2.T], label='eq T from CEA') ax.set_xlabel('post-shock distance (mm)') ax.set_ylabel('temperature (K)') if log_x_axis: ax.set_xscale('log') if plot_x_limits: ax.set_xlim(plot_x_limits) plt.legend(loc='best') if title: plt.title(title) if save_figures: plt.savefig(temp_result_fig_name + '.png', dpi=300) plt.savefig(temp_result_fig_name + '.eps', dpi=300) plt.savefig(temp_result_fig_name + '.pdf', dpi=300) plt.show() #------------------------------------------------------------------------- # now do massf plot species_fig, species_ax = plt.subplots() for i, species in enumerate(output_species_list): # use solid lines if we are line 7 or less # change over for lines past this # this gives maximum 12 species, I guess, before overlap # works with earlier version of matplotlib will only 7 colours too.. if i < 7: linestyle = '-' else: linestyle = '--' # change the _plus or _minus to + or - here to make the labels nicer... if '_plus' in species: species_label = species.replace('_plus', '+') elif '_minus' in species: species_label = species.replace('_minus', '-') else: species_label = species species_ax.plot(x_list, results_dict[species], linestyle=linestyle, label=species_label) species_ax.set_xlabel('post-shock distance (mm)') if outputUnits == 'moles': species_ax.set_ylabel('moles per original mole') elif outputUnits == 'massf': species_ax.set_ylabel('mass fraction of species') if log_x_axis: species_ax.set_xscale('log') if plot_x_limits: species_ax.set_xlim(plot_x_limits) plt.legend(loc='best') if title: plt.title(title) if save_figures: plt.savefig(species_result_fig_name + '.png', dpi=300) plt.savefig(species_result_fig_name + '.eps', dpi=300) plt.savefig(species_result_fig_name + '.pdf', dpi=300) plt.show() # ------------------------------------------------------------------- # now do the freestream normalised plot freestream_normalised_fig, freestream_normalised_ax = plt.subplots() for T in T_list: freestream_normalised_ax.plot(x_list, np.array(results_dict[T]) / T_inf, label=T) # now do p, rho, and u # I commented out pressure here as it changes by too much and messes with the plot #freestream_normalised_ax.plot(x_list, np.array(results_dict['p'])/p_inf, label = 'p') # have to get rho from state 1 as we didn't need to specify it for inflow calculation... freestream_normalised_ax.plot(x_list, np.array(results_dict['rho']) / state1.rho, label='rho') freestream_normalised_ax.plot(x_list, np.array(results_dict['u']) / u_inf, label='u') # add eq values from CEA freestream_normalised_ax.plot([x_list[0], x_list[-1]], [state2.T / T_inf, state2.T / T_inf], label='eq T from CEA') freestream_normalised_ax.plot( [x_list[0], x_list[-1]], [state2.rho / state1.rho, state2.rho / state1.rho], label='eq rho from CEA') freestream_normalised_ax.plot([x_list[0], x_list[-1]], [V2 / u_inf, V2 / u_inf], label='eq u from CEA') freestream_normalised_ax.set_xlabel('post-shock distance (mm)') freestream_normalised_ax.set_ylabel( 'quantity normalised by freestream value') if log_x_axis: freestream_normalised_ax.set_xscale('log') if plot_x_limits: freestream_normalised_ax.set_xlim(plot_x_limits) plt.legend(loc='best') if title: plt.title(title) if save_figures: plt.savefig(freestream_normalised_result_fig_name + '.png', dpi=300) plt.savefig(freestream_normalised_result_fig_name + '.eps', dpi=300) plt.savefig(freestream_normalised_result_fig_name + '.pdf', dpi=300) plt.show() #------------------------------------------------------------------------ # now do the equilibrium normalised plot eq_normalised_fig, eq_normalised_ax = plt.subplots() for T in T_list: eq_normalised_ax.plot(x_list, np.array(results_dict[T]) / state2.T, label=T) # now do p, rho, and u eq_normalised_ax.plot(x_list, np.array(results_dict['p']) / state2.p, label='p') eq_normalised_ax.plot(x_list, np.array(results_dict['rho']) / state2.rho, label='rho') eq_normalised_ax.plot(x_list, np.array(results_dict['u']) / V2, label='u') eq_normalised_ax.set_xlabel('post-shock distance (mm)') eq_normalised_ax.set_ylabel( 'quantity normalised by equilibrium value from CEA') if log_x_axis: eq_normalised_ax.set_xscale('log') if plot_x_limits: eq_normalised_ax.set_xlim(plot_x_limits) plt.legend(loc='best') if title: plt.title(title) if save_figures: plt.savefig(eq_normalised_result_fig_name + '.png', dpi=300) plt.savefig(eq_normalised_result_fig_name + '.eps', dpi=300) plt.savefig(eq_normalised_result_fig_name + '.pdf', dpi=300) plt.show() return results_dict
def main(): """top level function!""" #dictionaries to store useful stuff # pressure, mach number, temperature, sound speed, velocity, density, R, gamma P={};M={};T={};a={};V={};rho={};RU2={};G={} #a couple of switches ref = 0 #ref = 1 allows shock reflection at D2 stand = 1 #if stand = 1, calculate pressure for standing shock in region 2 REV = 0 #inputs (asks user for some of these inputs) print 'Driver condition' piston = is_valid(raw_input('Piston configuration? {0}'.format(pistons.keys())),pistons.keys()) primary = is_valid(str(raw_input('Percentage of Helium in the primary driver? {0} '.format(primary_driver.keys()))),primary_driver.keys()) GP = primary_driver[primary][0]; RP = primary_driver[primary][1] secondary_input = is_valid(str(raw_input('Is the secondary driver being used (y/n)? ')),['y','n']) if secondary_input == 'y': secondary = True else: secondary = None test_gas = is_valid(raw_input('Test gas? {0} '.format(gases.keys())), gases.keys()) if secondary: GS = gases['He'][0]; RS = gases['He'][1] GT = gases[test_gas][0]; RT = gases[test_gas][1] #air as accelorator gas GA = gases['air'][0]; RA = gases['air'][1] filename = raw_input('filename? ') if filename == '': filename = 'x2run.txt' print 'Enter shock speeds (in m/s) below to find solution.' if secondary: Vsd = int(raw_input('Vsd? ')) Vs1 = int(raw_input('Vs1? ')) Vs2 = int(raw_input('Vs2? ')) T0 = 300.0;T['s4'] = pistons[piston][0]; P['s4'] = pistons[piston][1] #ambient + reservoir temp (K), reservoir pressure (Pa) (this is the current lightweight piston driver condition used for all shots in X2) M['s3s'] = primary_driver[str(primary)][2] #Mach numbers terminating steady expansions (used for orifice plate) set M[11] to 0 for constant area shock tunnel (not normally used), M[11] =1 is no orifice plate, sonic throat. normal configuration, theoretical optimum. set M[11] to 2.15 to use pure He driver orifice plate #put gammas into dictionaries if secondary: G['sd1'] = GS; G['sd2'] = GS; G['sd3'] = GP; G['s3'] = GS else: G['s3'] = GP G['s4'] = GP; G['s3s'] = GP; G['s1'] = GT; G['s2'] = GT G['s7'] = GT; G['s5'] = GA; G['s6'] = GA; G['s13'] = GA; G['s8'] = GT #add ambient temperature to arrays where required T['sd1' ]= T0; T['s1'] = T0; T['s5'] = T0 M['s4'] = 0; V['s4']= 0; rho['s4'] = 0 arbitrary = True #dummy variable while arbitrary: #output file creation output = open(filename,"w") #----------------- starting off ---------------------- #calculate the sound speeds known if secondary: a['sd1'] = math.sqrt(T['sd1']*GS*RS) a['s4'] = math.sqrt(T['s4']*GP*RP) a['s1'] = math.sqrt(T['s1']*GT*RT) a['s5'] = math.sqrt(T['s5']*GA*RA) a['s3s'] = a['s4']*IP(M['s3s'],GP)**((GP-1.0)/2.0/GP) P['s3s'] = P['s4']*IP(M['s3s'],GP) V['s3s'] = a['s3s']*M['s3s'] T['s3s'] = T['s4']*(a['s3s']/a['s4'])**2.0 rho['s3s'] = P['s3s']/RP/T['s3s'] RU2['s3s'] = rho['s3s']*V['s3s']**2.0 #---------------------- secondary driver ------------------------------ if secondary: #if secondary driver is in use #conditions behind shock Msd = Vsd/a['sd1'] V['sd2'] = Vsd*(1.0-u2_u1(Msd,GS)) T['sd2'] = T['sd1']*T2_T1(Msd,GS) a['sd2'] = math.sqrt(T['sd2']*GS*RS) M['sd2'] = V['sd2']/a['sd2'] #unsteady expansion using Vsd2 = Vsd3 V['sd3'] = V['sd2'] a['sd3'] = ad(GP, a['s3s'], V['s3s'], V['sd3']) P['sd3'] = P['s3s']*isen((a['sd3']/a['s3s']),GP) T['sd3'] = T['s3s']*(a['sd3']/a['s3s'])**2.0 M['sd3'] = V['sd3']/a['sd3'] rho['sd3'] = P['sd3']/RP/T['sd3'] RU2['sd3']=rho['sd3']*V['sd3']**2.0 #can now get conditions at state sd2 now we have Psd3 P['sd2'] = P['sd3'] rho['sd2'] = P['sd2']/RS/T['sd2'] RU2['sd2'] = rho['sd2']*V['sd2']**2.0 #and then finally the fill pressure... P['sd1'] = P['sd2']/p2_p1(Msd,GS) M['sd1']=0; V['sd1']=0; rho['sd1'] = P['sd1']/RS/T['sd1']; RU2['sd1'] = 0 #-------------------- shock tube ------------------------------ #conditions behind shock Ms1 = Vs1/a['s1'] V['s2'] = Vs1*(1.0 - u2_u1(Ms1,GT)) T['s2'] = T['s1']*T2_T1(Ms1,GT) a['s2'] = math.sqrt(T['s2']*GT*RT) M['s2'] = V['s2']/a['s2'] #unsteady expansion using Vs2 = Vs3 V['s3'] = V['s2'] if secondary: #expansion of secondary driver gas into shock tube a['s3'] = ad(GS, a['sd2'], V['sd2'], V['s3']) P['s3'] = P['sd2']*isen((a['s3']/a['sd2']),GS) T['s3'] = T['sd2']*(a['s3']/a['sd2'])**2.0 rho['s3'] = P['s3']/RS/T['s3'] else: #expansion of primary driver gas into shock tube a['s3'] = ad(GP, a['s3s'], V['s3s'], V['s3']) P['s3'] = P['s3s']*isen((a['s3']/a['s3s']),GP) T['s3'] = T['s3s']*(a['s3']/a['s3s'])**2.0 rho['s3'] = P['s3']/RP/T['s3'] M['s3'] = V['s3']/a['s3'] RU2['s3']=rho['s3']*V['s3']**2.0 #can now get conditions at state s2 now we have Ps3 P['s2'] = P['s3'] rho['s2'] = P['s2']/RT/T['s2'] RU2['s2'] = rho['s2']*V['s2']**2.0 #and then finally the fill pressure... P['s1'] = P['s2']/p2_p1(Ms1,GT) M['s1']=0; V['s1']=0; rho['s1'] = P['s1']/RT/T['s1']; RU2['s1'] = 0 #------------- acceleration tube ------------------------------- #conditions behind shock Ms2 = Vs2/a['s5'] V['s6'] = Vs2*(1.0 - u2_u1(Ms2,GA)) T['s6'] = T['s5']*T2_T1(Ms2,GA) a['s6'] = math.sqrt(T['s6']*GA*RA) M['s6'] = V['s6']/a['s6'] #unsteady expansion using Vs6 = Vs7 V['s7'] = V['s6'] a['s7'] = ad(GT, a['s2'], V['s2'], V['s7']) P['s7'] = P['s2']*isen((a['s7']/a['s2']),GT) T['s7'] = T['s2']*(a['s7']/a['s2'])**2.0 rho['s7'] = P['s7']/RA/T['s7'] M['s7'] = V['s7']/a['s7'] RU2['s7']=rho['s7']*V['s7']**2.0 #can now get conditions at state s6 now we have Ps7 P['s6'] = P['s7'] rho['s6'] = P['s6']/RA/T['s6'] RU2['s6'] = rho['s6']*V['s6']**2.0 #and then finally the fill pressure... P['s5'] = P['s6']/p2_p1(Ms2,GA) M['s5']=0; V['s5']=0; rho['s5'] = P['s5']/RA/T['s5']; RU2['s5'] = 0 #--------------- nozzle calculations-------------------- #currently using area ratio of 2.5! M['s8'] = nozzleMfinder (GT, 2.5 , M['s7'], M['s7']+5.0) T['s8'] = T['s7']*nozzleTratio(M['s7'],M['s8'],GT) P['s8'] = P['s7']*nozzlepratio(M['s7'],M['s8'],GT) V['s8'] = V['s7']*(M['s8']/M['s7'])*(T['s8']/T['s7'])**(1.0/2.0) rho['s8'] = rho['s7']*nozzlerhoratio(M['s7'],M['s8'],GT) a['s8'] = V['s8']/M['s8'] #------------ normal shock calculation over the model ----------- #NOTE: this part of the code actually does use chemistry for the equilibrium part... #it also uses the states dictionary syntax from pitot, so I can use the pitot printing function #build states dictionary: states = {} #first build a gas object at the nozzle conditions states['s8'] = gases[test_gas][2].clone() states['s8'].set_pT(P['s8'],T['s8']) #and then the two different state 10 conditions that we will then shock states['s10f'] = states['s8'].clone() states['s10e'] = states['s8'].clone() #do a frozen and then an equilibrium normal shock on these conditions: V10f, V10fg = shock_ideal(states['s8'],V['s8'],states['s10f']) V['s10f'] = V10fg; M['s10f'] = V['s10f']/states['s10f'].son V10e, V10eg = normal_shock(states['s8'],V['s8'],states['s10e']) V['s10e'] = V10eg; M['s10e'] = V['s10e']/states['s10e'].son #------------------------ output ------------------------------------- #pillaged from new pitot! print " " version_printout = "Pitot Classic Version: {0}".format(VERSION_STRING) print version_printout output.write(version_printout + '\n') if secondary: description_sd = 'sd1 is secondary driver fill.' print description_sd output.write(description_sd + '\n') description_1 = 'state 1 is shock tube fill. state 5 is acceleration tube fill.' print description_1 output.write(description_1 + '\n') description_2 = 'state 7 is expanded test gas entering the nozzle.' print description_2 output.write(description_2 + '\n') description_3 = 'state 8 is test gas exiting the nozzle (using area ratio of {0}).'.format(2.5) print description_3 output.write(description_3 + '\n') description_4 = 'state 10f is frozen shocked test gas flowing over the model.' print description_4 output.write(description_4 + '\n') description_5 = 'state 10e is equilibrium shocked test gas flowing over the model.' print description_5 output.write(description_5 + '\n') test_gas_used = 'Test gas is {0} (gamma = {1}, R = {2}, {3}).'.format(test_gas,GT,RT,gases[test_gas][2].reactants) print test_gas_used output.write(test_gas_used + '\n') driver_gas_used = 'Driver gas has {0}%He (gamma = {1}, R = {2})'.format(primary,GP,RP) print driver_gas_used output.write(driver_gas_used + '\n') if secondary: secondary_shockspeeds = "Vsd = {0:.2f} m/s, Msd = {1:.2f}".format(Vsd,Msd) print secondary_shockspeeds output.write(secondary_shockspeeds + '\n') shockspeeds = "Vs1= {0:.2f} m/s, Ms1= {1:.2f} ,Vs2= {2:.2f} m/s, Ms2= {3:.2f}".format(Vs1,Ms1,Vs2,Ms2) print shockspeeds #prints above line in console output.write(shockspeeds + '\n') #writes above line to output file (input to write command must be a string) key = "{0:6}{1:11}{2:9}{3:6}{4:9}{5:6}{6:9}{7:8}{8:9}".format("state","P","T","a","V","M","rho","pitot","stgn") print key output.write(key + '\n') units = "{0:6}{1:11}{2:9}{3:6}{4:9}{5:6}{6:9}{7:9}{8:9}".format("","Pa","K","m/s","m/s","","m^3/kg","kPa","MPa") print units output.write(units + '\n') #new dictionaries here to add pitot and stagnation pressure calcs pitot_pressure = {} #pitot pressure dict p0 = {} #stagnation pressure dict def condition_printer(it_string): """Prints the values of a specified condition to the screen and to the output file. I made a function of this so I didn't have to keep pasting the code in.""" if M.has_key(it_string): if M[it_string] == 0: pitot_pressure[it_string] = 0 p0[it_string] = 0 else: pitot_pressure[it_string] = P[it_string]*pitot(M[it_string],G[it_string])/1000.0 p0[it_string] = p0_p(M[it_string], G[it_string])*P[it_string]/1.0e6 conditions = "{0:<6}{1:<11.7g}{2:<9.1f}{3:<6.0f}{4:<9.1f}{5:<6.2f}{6:<9.4f}{7:<8.0f}{8:<9.1f}"\ .format(it_string, P[it_string], T[it_string], a[it_string], V[it_string],M[it_string],rho[it_string], pitot_pressure[it_string], p0[it_string]) print conditions output.write(conditions + '\n') def condition_printer_pitot(it_string): """The states based condition printer from pitot, used for the normal shock stuff at the end of the tunnel!""" if states.has_key(it_string): if M[it_string] == 0: pitot_pressure[it_string] = 0 p0[it_string] = 0 else: pitot_pressure[it_string] = pitot_p(states[it_string].p,M[it_string],states[it_string].gam)/1000.0 p0[it_string] = p0_p(M[it_string], states[it_string].gam)*states[it_string].p/1.0e6 conditions = "{0:<6}{1:<11.7}{2:<9.1f}{3:<6.0f}{4:<9.1f}{5:<6.2f}{6:<9.4f}{7:<8.0f}{8:<9.1f}"\ .format(it_string, states[it_string].p, states[it_string].T, states[it_string].son,V[it_string],M[it_string], states[it_string].rho, pitot_pressure[it_string], p0[it_string]) print conditions output.write(conditions + '\n') #print the driver related stuff first condition_printer('s4') condition_printer('s3s') if secondary: #need a separate printing thing for the secondary driver for i in range(1,4): #will do 1 - 3 it_string = 'sd{0}'.format(i) condition_printer(it_string) for i in range(1,4): #shock tube stuff it_string = 's{0}'.format(i) condition_printer(it_string) for i in range(5,9): #acc tube and nozzle if it's there it_string = 's{0}'.format(i) condition_printer(it_string) #do the conditions over the model condition_printer_pitot('s10f') condition_printer_pitot('s10e') Cp_test_gas = (gases[test_gas][0]*gases[test_gas][1])/(gases[test_gas][0]-1.0) stagnation_enthalpy = (((0.5)*V['s7']**2.0)+(Cp_test_gas*T['s7'])) #MJ/kg stag_enth = 'The stagnation enthalpy (Ht) at the end of the acceleration tube {0:<.5g} MJ/kg.'.format(stagnation_enthalpy/1.0e6) print stag_enth output.write(stag_enth + '\n') #calculate flight equivalent speed u_eq = math.sqrt(2.0*stagnation_enthalpy) #supposedly why this is is discussed in Bianca's thesis u_eq_print = 'The flight equivalent speed is {0:<.5g} m/s.'.format(u_eq) print u_eq_print output.write(u_eq_print + '\n') #added ability to get the species in the post-shock condition show_species = True if show_species: species1 = 'species in the shock layer at equilibrium:' print species1 output.write(species1 + '\n') species2 = '{0}'.format(states['s10e'].species) print species2 output.write(species2 + '\n') output.close() #------------------------------ user commands--------------------------- if secondary: change_tuple = ('Vsd','Vs1','Vs2', 'quit') else: change_tuple = ('Vs1','Vs2', 'quit') change = is_valid(raw_input('What do you want to change? {0}'.format(change_tuple)), change_tuple) if change == 'quit': print "Removing temporary files and leaving the program." if os.path.isfile('thermo.inp'): os.remove('thermo.inp') if os.path.isfile('thermo.out'): os.remove('thermo.out') if os.path.isfile('thermo.lib'): os.remove('thermo.lib') if os.path.isfile('tmp.inp'): os.remove('tmp.inp') if os.path.isfile('tmp.out'): os.remove('tmp.out') if os.path.isfile('trans.inp'): os.remove('trans.inp') if os.path.isfile('trans.out'): os.remove('trans.out') if os.path.isfile('trans.lib'): os.remove('trans.lib') arbitrary == False break #quit() elif change == 'Vsd': difference = int(raw_input('How much do you want to change it by? ')) Vsd += difference print 'Vsd = {0}m/s'.format(Vsd) elif change == 'Vs1': difference = int(raw_input('How much do you want to change it by? ')) Vs1 += difference print 'Vs1 = {0}m/s'.format(Vs1) else: difference = int(raw_input('How much do you want to change it by? ')) Vs2 += difference print 'Vs2 = {0}m/s'.format(Vs2)
def main(): # These are the inputs! primary_shock = 3376.0 # m/s secondary_shock = 6580.0 # m/s nozzle_exp_ratio = 7.5 conehead_pressure = 26642.0 # Pa # Set up the shock tube test gas print "Titan gas" state1 = Gas({ 'N2': 0.95, 'CH4': 0.05 }, inputUnits='moles', outputUnits='moles') state1.set_pT(25400.0, 300.0) print "state1:" state1.write_state(sys.stdout) # Set up the acceleration tube gas print "Air accelerator gas" state10 = Gas({'Air': 1.0}) state10.set_pT(68.7, 300.0) print "state10:" state10.write_state(sys.stdout) # Compute the primary shock here print "Incident shock" state2 = state1.clone() V2, V2g = normal_shock(state1, primary_shock, state2) print "V2=", V2, "Vg=", V2g print "state2:" state2.write_state(sys.stdout) # For the unsteady expansion of the test gas, regulation of the amount # of expansion is determined by the shock-processed accelerator gas. print "\nNow do unsteady expansion..." V5g, state5 = finite_wave_dv('cplus', V2g, state2, secondary_shock) # Write out expanded test gas conditions print "\nExpanded test gas, at end of acceleration tube:" print "V5g=", V5g print "state5:" state5.write_state(sys.stdout) # Now lets put in the nozzle with a steady flow area change V6g, state6 = steady_flow_with_area_change(state5, V5g, nozzle_exp_ratio) # Print out nozzle expanded flow properties print "\nNozzle exit gas conditions for an area ratio of:", nozzle_exp_ratio print "V6g:", V6g print "state6:" state6.write_state(sys.stdout) # Try and work out my conehead pressure for this particular flow condition # First I need to work out the shock angle shock_angle = beta_cone(state6, V6g, math.radians(15)) print "\nShock angle over cone:", math.degrees(shock_angle) # Reverse the process to get the flow state behind the shock and check the surface angle is correct delta_s, vel_cond, state7 = theta_cone(state6, V6g, shock_angle) print "Surface angle should be the same.....: 15deg = ", math.degrees( delta_s), "deg" print "\nConehead surface conditions:" state7.write_state(sys.stdout) # Need to check whether the pressure are the same print "\nComputed conehead pressure should be the same as the experimental pressure" print "Computed:", state7.p, "Experimental:", conehead_pressure print "If pressure are not the same 'tweak' the nozzle_exp_ratio until they match" # Grab the total conditions total6 = total_condition(state6, V6g) # Now print my total conditions print "\nTotal conditions of nozzle exit test gas:" print "total6:" total6.write_state(sys.stdout) # And finally.... let's output a 'flight equivalent' speed flight_eq = math.sqrt(2 * total6.h) print "\nFlight equivalent speed:", flight_eq print "Done." return
def main(): print "Titan gas" state1 = Gas({ 'N2': 0.95, 'CH4': 0.05 }, inputUnits='moles', outputUnits='moles') state1.set_pT(2600.0, 300.0) print "state1:" state1.write_state(sys.stdout) # print "Air accelerator gas" state10 = Gas({'Air': 1.0}) state10.set_pT(10.0, 300.0) print "state10:" state10.write_state(sys.stdout) # print "Incident shock" state2 = state1.clone() V2, V2g = normal_shock(state1, 4100.0, state2) print "V2=", V2, "Vg=", V2g, "expected 3670.56" print "state2:" state2.write_state(sys.stdout) print "Checks:" print "p2/p1=", state2.p / state1.p, "expected 166.4" print "rho2/rho1=", state2.rho / state1.rho, "expected 9.5474" print "T2/T1=", state2.T / state1.T, "expected 14.9" # print "\nNow do unsteady expansion..." # For the unsteady expansion of the test gas, regulation of the amount # of expansion is determined by the shock-processed accelerator gas. # Across the contact surface between these gases, the pressure and velocity # have to match so we set up some trials of various pressures and check # that velocities match. def error_in_velocity(p5p2, state2=state2, V2g=V2g, state10=state10): "Compute the velocity mismatch for a given pressure ratio across the expansion." # Across the expansion, we get a test-gas velocity, V5g. V5g, state5 = finite_wave_dp('cplus', V2g, state2, p5p2 * state2.p) # Across the contact surface, p20 == p5 p20 = p5p2 * state2.p print "current guess for p5 and p20=", p20 V10, V20, V20g, state20 = normal_shock_p2p1(state10, p20 / state10.p) return ( V5g - V10 ) / V5g # V10 was V20g - lab speed of accelerator gas - we now make the assumption that this is the same as the shock speed p5p2 = secant(error_in_velocity, 0.01, 0.011, tol=1.0e-3) print "From secant solve: p5/p2=", p5p2 # It would have been faster and the code closer to Hadas' spreadsheet if we had # stepped down in pressure until we found the point where the velocities matched. # The expansion along the u+a wave would have appeared in the code here. V5g, state5 = finite_wave_dp('cplus', V2g, state2, p5p2 * state2.p) print "Expanded test gas, at end of acceleration tube:" print "V5g=", V5g print "state5:" state5.write_state(sys.stdout) V10, V20, V20g, state20 = normal_shock_p2p1(state10, state5.p / state10.p) print V10 print "Done." return