def setup_simulation(sln, T=T0, P=P0, phi=phi0, f=fuel0, ox=oxidizer0): """ Returns a constant-pressure reactor object and reactor network based upon a ct.Solution object and at optionally specified starting conditions, or predefined defaults :param sln: ct.Solution object specifying mechanism for given simulation :param T: float, optional starting temperature (K) :param P: float, optional starting pressure (Pa) :param phi: float, optional equivalence ratio (-) :param f: dict, optional dict specifying fuel composition :param ox: dict, optional dict specifying oxidizer composition :return tuple: (ct.ConstPressureReactor, ct.ReactorNet) """ sln.set_equivalence_ratio(phi=phi, fuel=f, oxidizer=ox) sln.TP = T, P r1 = ct.ConstPressureReactor(sln) rnet = ct.ReactorNet([r1]) return r1, rnet
def __init__(self, infile='', phaseid='', source=None, thermo=None, species=(), kinetics=None, reactions=(), particle_mass=1.0, state_vec=[], P=101325, rates=True, **kwargs): """Initialize particle object with thermochemical state. Parameters ---------- state : `numpy array or list` Initial thermochemical state of particle. Needs T, P, X. gas : `cantera.Solution` Initial thermochemical state of particle particle_mass : `float` Particle mass Returns ------- None """ super().__init__() if len(state_vec) > 0: self.state = state_vec # if Particle.gas_template == None: # Particle.gas_template = ct.Solution(mech, name=mech[0:-4]) self.mech = infile self.TPX = self.T, P, self.X # self.P = self.state[0]*self.state[1]/self.mean_molecular_weight*ct.gas_constant # P = rho * R * T; self.mass = particle_mass self.age = 0 self.reactor = ct.ConstPressureReactor(self, volume=self.mass / self.density) self.time_history: List[ndarray] = [self.out_state] self.rate_list = [np.hstack([self.age, self.net_production_rates])] self.net = ct.ReactorNet([self.reactor])
def test_ConstPressureReactor(self): phase = ct.Nitrogen() air = ct.Solution('air.xml') phase.TP = 75, 4e5 r1 = ct.ConstPressureReactor(phase) r1.volume = 0.1 air.TP = 500, 4e5 env = ct.Reservoir(air) w2 = ct.Wall(env,r1, Q=250000, A=1) net = ct.ReactorNet([r1]) states = ct.SolutionArray(phase, extra='t') for t in np.arange(0.0, 100.0, 10): net.advance(t) states.append(TD=r1.thermo.TD, t=t) self.assertEqual(states.X[1], 0) self.assertEqual(states.X[-2], 1) for i in range(3,7): self.assertNear(states.T[i], states.T[2])
def test_equilibrium_HP(self): # Adiabatic, constant pressure combustion should proceed to equilibrum # at constant enthalpy and pressure. P0 = 10 * ct.OneAtm T0 = 1100 X0 = 'H2:1.0, O2:0.5, AR:8.0' gas1 = ct.Solution('h2o2.xml') gas1.TPX = T0, P0, X0 r1 = ct.ConstPressureReactor(gas1) net = ct.ReactorNet() net.addReactor(r1) net.advance(1.0) gas2 = ct.Solution('h2o2.xml') gas2.TPX = T0, P0, X0 gas2.equilibrate('HP') self.assertNear(r1.T, gas2.T) self.assertNear(r1.thermo.P, P0) self.assertNear(r1.thermo.density, gas2.density) self.assertArrayNear(r1.thermo.X, gas2.X)
def runMainBurner(phi_main, tau_main, T_fuel=300, T_ox=650, P=25*101325, mech="gri30.xml", slope=0.01, curve=0.01, filename=None): flameGas = premix(phi_main, P=P, mech=mech, T_fuel=T_fuel, T_ox=T_ox) if filename == None: filename = '{0}_{1:.4f}.pickle'.format('phi_main', phi_main) if os.path.isfile(filename): mainBurnerDF = pd.read_parquet(filename) flameTime = mainBurnerDF.index.values else: flame, flameTime = runFlame(flameGas, slope=slope, curve=curve) columnNames = ['x', 'u', 'T', 'n', 'MW'] + ["Y_" + sn for sn in flameGas.species_names] + ["X_" + sn for sn in flameGas.species_names] flameData = np.concatenate( [np.array([flame.grid]), np.array([flame.u]), np.array([flame.T]), np.array([[0] * len(flame.T)]), np.array([[0] * len(flame.T)]), flame.Y, flame.X], axis=0) mainBurnerDF = pd.DataFrame( data=flameData.transpose(), index=flameTime, columns=columnNames) mainBurnerDF.index.name = 'Time' mainBurnerDF['P'] = flame.P mainBurnerDF.to_parquet(filename, compression='gzip') vitiatedProd, flameCutoffIndex, mainBurnerDF = getStateAtTime( mainBurnerDF, flameTime, tau_main) vitReactor = ct.ConstPressureReactor(vitiatedProd, name='MainBurner') return vitReactor, mainBurnerDF
def __init__(self): """Constructor for the SimEnv class. Call SimEnv(...) to create a SimEnv object. Arguments: None """ # super(SimEnv) returns the superclass, in this case gym.Env. Construct a gym.Env instance # gym.Env.__init__(self) # EzPickle.__init__(self) self.dt = DT self.age = 0 self.steps_taken = 0 self.reward = 0 # Create main burner objects self.main_burner_gas = ct.Solution(MECH) self.main_burner_gas.TPX = main_burner_reactor.thermo.TPX self.main_burner_reactor = ct.ConstPressureReactor( self.main_burner_gas) self.main_burner_reservoir = ct.Reservoir( contents=self.main_burner_gas) self.remaining_main_burner_mass = M_air_main + M_fuel_main # Create secondary stage objects self.sec_fuel = ct.Solution(MECH) self.sec_fuel.TPX = T_FUEL, P, {'CH4': 1.0} self.sec_fuel_reservoir = ct.Reservoir(contents=self.sec_fuel) self.sec_fuel_remaining = M_fuel_sec self.sec_air = ct.Solution(MECH) self.sec_air.TPX = T_AIR, P, {'N2': 0.79, 'O2': 0.21} self.sec_air_reservoir = ct.Reservoir(contents=self.sec_air) self.sec_air_remaining = M_air_sec # Create simulation network model self.sec_stage_gas = ct.Solution(MECH) self.sec_stage_gas.TPX = 300, P, {'AR': 1.0} self.sec_stage_reactor = ct.ConstPressureReactor(self.sec_stage_gas) self.sec_stage_reactor.volume = 1e-8 # second stage starts off small, and grows as mass is entrained self.network = ct.ReactorNet( [self.main_burner_reactor, self.sec_stage_reactor]) # Create main and secondary mass flow controllers and connect them to the secondary stage self.mfc_main = ct.MassFlowController(self.main_burner_reservoir, self.sec_stage_reactor) self.mfc_main.set_mass_flow_rate(0) # zero initial mass flow rate self.mfc_air_sec = ct.MassFlowController(self.sec_air_reservoir, self.sec_stage_reactor) self.mfc_air_sec.set_mass_flow_rate(0) self.mfc_fuel_sec = ct.MassFlowController(self.sec_fuel_reservoir, self.sec_stage_reactor) self.mfc_fuel_sec.set_mass_flow_rate(0) # Define action and observation spaces; must be gym.spaces # we're controlling three things: mdot_main, mdot_fuel_sec, mdot_air_sec #TODO: check to see if chosen tau_ent and self.action_space.high definition allow for complete entrainment of mass self.action_space = spaces.Box( low=np.array([0, 0, 0]), high=np.array([ self.remaining_main_burner_mass / tau_ent_main, self.sec_fuel_remaining / tau_ent_sec, self.sec_air_remaining / tau_ent_sec ]), dtype=np.float32) low = np.array([0] * 55 + [-np.finfo(np.float32).max] * 53) high = np.array([3000, 10] + [1] * 53 + [np.finfo(np.float64).max] * 53) num_cols = len(self.sec_stage_gas.state) + \ len(self.sec_stage_gas.net_production_rates) self.observation_space = spaces.Box(low=np.tile(low, (10, 1)), high=np.tile(high, (10, 1)), dtype=np.float64) self.observation_array = self._next_observation(init=True) # Reward variables for T, NO, and CO self.reward_T = 0 self.reward_NO = 0 self.reward_CO = 0
def run_reactor_ss( cti_file, t_array=[528], p_array=[75], v_array=[0.00424], h2_array=[0.75], co2_array=[0.5], rtol=1.0e-11, atol=1.0e-22, reactor_type=0, energy="off", sensitivity=False, sensatol=1e-6, sensrtol=1e-6, reactime=1e5, ): ''' run the reactor to steady state. saves a single CSV output file with one row of data. results saved in new folder marked "steady state" under reactor type ''' import pandas as pd import numpy as np import time import cantera as ct from matplotlib import pyplot as plt import csv import math import os import sys import re import itertools import logging from collections import defaultdict import git try: array_i = int(os.getenv("SLURM_ARRAY_TASK_ID")) except TypeError: array_i = 0 # get git commit hash and message repo = git.Repo("/work/westgroup/lee.ting/cantera/ammonia") git_sha = str(repo.head.commit)[0:6] git_msg = str(repo.head.commit.message)[0:20].replace(" ", "_").replace( "'", "_") # this should probably be outside of function settings = list( itertools.product(t_array, p_array, v_array, h2_array, co2_array)) # constants pi = math.pi # set initial temps, pressures, concentrations temp = settings[array_i][0] # kelvin temp_str = str(temp)[0:3] pressure = settings[array_i][1] * ct.one_atm # Pascals X_h2 = settings[array_i][3] x_h2_str = str(X_h2)[0:3].replace(".", "_") x_CO_CO2_str = str(settings[array_i][4])[0:3].replace(".", "_") if X_h2 == 0.75: X_h2o = 0.05 else: X_h2o = 0 X_co = (1 - (X_h2 + X_h2o)) * (settings[array_i][4]) X_co2 = (1 - (X_h2 + X_h2o)) * (1 - settings[array_i][4]) # normalize mole fractions just in case # X_co = X_co/(X_co+X_co2+X_h2) # X_co2= X_co2/(X_co+X_co2+X_h2) # X_h2 = X_h2/(X_co+X_co2+X_h2) mw_co = 28.01e-3 # [kg/mol] mw_co2 = 44.01e-3 # [kg/mol] mw_h2 = 2.016e-3 # [kg/mol] mw_h2o = 18.01528e-3 # [kg/mol] co2_ratio = X_co2 / (X_co + X_co2) h2_ratio = (X_co2 + X_co) / X_h2 # CO/CO2/H2/H2: typical is concentrations_rmg = {"O2(2)": X_co, "NH3(6)": X_co2, "He": X_h2} # initialize cantera gas and surface gas = ct.Solution(cti_file, "gas") # surf_grab = ct.Interface(cti_file,'surface1_grab', [gas_grab]) surf = ct.Interface(cti_file, "surface1", [gas]) # gas_grab.TPX = gas.TPX = temp, pressure, concentrations_rmg surf.TP = temp, pressure # create gas inlet inlet = ct.Reservoir(gas) # create gas outlet exhaust = ct.Reservoir(gas) # Reactor volume rradius = 35e-3 rlength = 70e-3 rvol = (rradius**2) * pi * rlength # Catalyst Surface Area site_density = (surf.site_density * 1000 ) # [mol/m^2]cantera uses kmol/m^2, convert to mol/m^2 cat_weight = 4.24e-3 # [kg] cat_site_per_wt = (300 * 1e-6) * 1000 # [mol/kg] 1e-6mol/micromole, 1000g/kg cat_area = site_density / (cat_weight * cat_site_per_wt) # [m^3] # reactor initialization if reactor_type == 0: r = ct.Reactor(gas, energy=energy) reactor_type_str = "Reactor" elif reactor_type == 1: r = ct.IdealGasReactor(gas, energy=energy) reactor_type_str = "IdealGasReactor" elif reactor_type == 2: r = ct.ConstPressureReactor(gas, energy=energy) reactor_type_str = "ConstPressureReactor" elif reactor_type == 3: r = ct.IdealGasConstPressureReactor(gas, energy=energy) reactor_type_str = "IdealGasConstPressureReactor" rsurf = ct.ReactorSurface(surf, r, A=cat_area) r.volume = rvol surf.coverages = "X(1):1.0" # flow controllers (Graaf measured flow at 293.15 and 1 atm) one_atm = ct.one_atm FC_temp = 293.15 volume_flow = settings[array_i][2] # [m^3/s] molar_flow = volume_flow * one_atm / (8.3145 * FC_temp) # [mol/s] mass_flow = molar_flow * (X_co * mw_co + X_co2 * mw_co2 + X_h2 * mw_h2 + X_h2o * mw_h2o) # [kg/s] mfc = ct.MassFlowController(inlet, r, mdot=mass_flow) # A PressureController has a baseline mass flow rate matching the 'master' # MassFlowController, with an additional pressure-dependent term. By explicitly # including the upstream mass flow rate, the pressure is kept constant without # needing to use a large value for 'K', which can introduce undesired stiffness. outlet_mfc = ct.PressureController(r, exhaust, master=mfc, K=0.01) # initialize reactor network sim = ct.ReactorNet([r]) # set relative and absolute tolerances on the simulation sim.rtol = 1.0e-11 sim.atol = 1.0e-22 ################################################# # Run single reactor ################################################# # round numbers so they're easier to read # temp_str = '%s' % '%.3g' % tempn cat_area_str = "%s" % "%.3g" % cat_area results_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/steady_state/{temp_str}/results" ) flux_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/steady_state/{temp_str}/flux_diagrams/{x_h2_str}/{x_CO_CO2_str}" ) try: os.makedirs(results_path, exist_ok=True) except OSError as error: print(error) try: os.makedirs(flux_path, exist_ok=True) except OSError as error: print(error) gas_ROP_str = [i + " ROP [kmol/m^3 s]" for i in gas.species_names] # surface ROP reports gas and surface ROP. these values are not redundant. gas_surf_ROP_str = [ i + " surface ROP [kmol/m^2 s]" for i in gas.species_names ] surf_ROP_str = [i + " ROP [kmol/m^2 s]" for i in surf.species_names] gasrxn_ROP_str = [ i + " ROP [kmol/m^3 s]" for i in gas.reaction_equations() ] surfrxn_ROP_str = [ i + " ROP [kmol/m^2 s]" for i in surf.reaction_equations() ] output_filename = ( results_path + f"/Spinning_basket_area_{cat_area_str}_energy_{energy}" + f"_temp_{temp}_h2_{x_h2_str}_COCO2_{x_CO_CO2_str}.csv") outfile = open(output_filename, "w") writer = csv.writer(outfile) writer.writerow([ "T (C)", "P (atm)", "V (M^3/s)", "X_co initial", "X_co2 initial", "X_h2 initial", "X_h2o initial", "CO2/(CO2+CO)", "(CO+CO2/H2)", "T (C) final", "Rtol", "Atol", "reactor type", ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str) # Run Simulation to steady state sim.advance_to_steady_state() # Record steady state data to CSV writer.writerow([ temp, pressure, volume_flow, X_co, X_co2, X_h2, X_h2o, co2_ratio, h2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress)) outfile.close() # save flux diagrams at the end of the run save_flux_diagrams(gas, suffix=flux_path, timepoint="end") save_flux_diagrams(surf, suffix=flux_path, timepoint="end") return
def run_reactor( cti_file, t_array=[528], p_array=[75], v_array=[0.00424], h2_array=[0.75], co2_array=[0.5], rtol=1.0e-11, atol=1.0e-22, reactor_type=0, energy="off", sensitivity=False, sensatol=1e-6, sensrtol=1e-6, reactime=1e5, ): import pandas as pd import numpy as np import time import cantera as ct from matplotlib import pyplot as plt import csv import math import os import sys import re import itertools import logging from collections import defaultdict import git try: array_i = int(os.getenv("SLURM_ARRAY_TASK_ID")) except TypeError: array_i = 0 # get git commit hash and message repo = git.Repo("/work/westgroup/lee.ting/cantera/ammonia/") git_sha = str(repo.head.commit)[0:6] git_msg = str(repo.head.commit.message)[0:20].replace(" ", "_").replace( "'", "_") # this should probably be outside of function settings = list( itertools.product(t_array, p_array, v_array, h2_array, co2_array)) # constants pi = math.pi # set initial temps, pressures, concentrations temp = settings[array_i][0] # kelvin temp_str = str(temp)[0:3] pressure = settings[array_i][1] * ct.one_atm # Pascals X_h2 = settings[array_i][3] x_h2_str = str(X_h2)[0:3].replace(".", "_") x_CO_CO2_str = str(settings[array_i][4])[0:3].replace(".", "_") if X_h2 == 0.75: X_h2o = 0.05 else: X_h2o = 0 X_co = (1 - (X_h2 + X_h2o)) * (settings[array_i][4]) X_co2 = (1 - (X_h2 + X_h2o)) * (1 - settings[array_i][4]) # normalize mole fractions just in case # X_co = X_co/(X_co+X_co2+X_h2) # X_co2= X_co2/(X_co+X_co2+X_h2) # X_h2 = X_h2/(X_co+X_co2+X_h2) mw_co = 28.01e-3 # [kg/mol] mw_co2 = 44.01e-3 # [kg/mol] mw_h2 = 2.016e-3 # [kg/mol] mw_h2o = 18.01528e-3 # [kg/mol] co2_ratio = X_co2 / (X_co + X_co2) h2_ratio = (X_co2 + X_co) / X_h2 # CO/CO2/H2/H2: typical is concentrations_rmg = {"O2(2)": X_co, "NH3(6)": X_co2, "He": X_h2} # initialize cantera gas and surface gas = ct.Solution(cti_file, "gas") # surf_grab = ct.Interface(cti_file,'surface1_grab', [gas_grab]) surf = ct.Interface(cti_file, "surface1", [gas]) # gas_grab.TPX = gas.TPX = temp, pressure, concentrations_rmg surf.TP = temp, pressure # create gas inlet inlet = ct.Reservoir(gas) # create gas outlet exhaust = ct.Reservoir(gas) # Reactor volume rradius = 35e-3 rlength = 70e-3 rvol = (rradius**2) * pi * rlength # Catalyst Surface Area site_density = (surf.site_density * 1000 ) # [mol/m^2]cantera uses kmol/m^2, convert to mol/m^2 cat_weight = 4.24e-3 # [kg] cat_site_per_wt = (300 * 1e-6) * 1000 # [mol/kg] 1e-6mol/micromole, 1000g/kg cat_area = site_density / (cat_weight * cat_site_per_wt) # [m^3] # reactor initialization if reactor_type == 0: r = ct.Reactor(gas, energy=energy) reactor_type_str = "Reactor" elif reactor_type == 1: r = ct.IdealGasReactor(gas, energy=energy) reactor_type_str = "IdealGasReactor" elif reactor_type == 2: r = ct.ConstPressureReactor(gas, energy=energy) reactor_type_str = "ConstPressureReactor" elif reactor_type == 3: r = ct.IdealGasConstPressureReactor(gas, energy=energy) reactor_type_str = "IdealGasConstPressureReactor" rsurf = ct.ReactorSurface(surf, r, A=cat_area) r.volume = rvol surf.coverages = "X(1):1.0" # flow controllers (Graaf measured flow at 293.15 and 1 atm) one_atm = ct.one_atm FC_temp = 293.15 volume_flow = settings[array_i][2] # [m^3/s] molar_flow = volume_flow * one_atm / (8.3145 * FC_temp) # [mol/s] mass_flow = molar_flow * (X_co * mw_co + X_co2 * mw_co2 + X_h2 * mw_h2 + X_h2o * mw_h2o) # [kg/s] mfc = ct.MassFlowController(inlet, r, mdot=mass_flow) # A PressureController has a baseline mass flow rate matching the 'master' # MassFlowController, with an additional pressure-dependent term. By explicitly # including the upstream mass flow rate, the pressure is kept constant without # needing to use a large value for 'K', which can introduce undesired stiffness. outlet_mfc = ct.PressureController(r, exhaust, master=mfc, K=0.01) # initialize reactor network sim = ct.ReactorNet([r]) # set relative and absolute tolerances on the simulation sim.rtol = 1.0e-11 sim.atol = 1.0e-22 ################################################# # Run single reactor ################################################# # round numbers so they're easier to read # temp_str = '%s' % '%.3g' % tempn cat_area_str = "%s" % "%.3g" % cat_area results_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/transient/{temp_str}/results" ) # results_path_csp = ( # os.path.dirname(os.path.abspath(__file__)) # + f"/{git_sha}_{git_msg}/{reactor_type_str}/transient/{temp_str}/results/csp" # ) flux_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/transient/{temp_str}/flux_diagrams/{x_h2_str}/{x_CO_CO2_str}" ) try: os.makedirs(results_path, exist_ok=True) except OSError as error: print(error) # try: # os.makedirs(results_path_csp, exist_ok=True) # except OSError as error: # print(error) try: os.makedirs(flux_path, exist_ok=True) except OSError as error: print(error) gas_ROP_str = [i + " ROP [kmol/m^3 s]" for i in gas.species_names] # surface ROP reports gas and surface ROP. these values might be redundant, not sure. gas_surf_ROP_str = [ i + " surface ROP [kmol/m^2 s]" for i in gas.species_names ] surf_ROP_str = [i + " ROP [kmol/m^2 s]" for i in surf.species_names] gasrxn_ROP_str = [ i + " ROP [kmol/m^3 s]" for i in gas.reaction_equations() ] surfrxn_ROP_str = [ i + " ROP [kmol/m^2 s]" for i in surf.reaction_equations() ] output_filename = ( results_path + f"/Spinning_basket_area_{cat_area_str}_energy_{energy}" + f"_temp_{temp}_h2_{x_h2_str}_COCO2_{x_CO_CO2_str}.csv") # output_filename_csp = ( # results_path_csp # + f"/Spinning_basket_area_{cat_area_str}_energy_{energy}" # + f"_temp_{temp}_h2_{x_h2_str}_COCO2_{x_CO_CO2_str}.csv" # ) outfile = open(output_filename, "w") # outfile_csp = open(output_filename_csp, "w") writer = csv.writer(outfile) # writer_csp = csv.writer(outfile_csp) # Sensitivity atol, rtol, and strings for gas and surface reactions if selected # slows down script by a lot if sensitivity: sim.rtol_sensitivity = sensrtol sim.atol_sensitivity = sensatol sens_species = [ "NH3(6)" ] #change THIS to your species, can add "," and other species # turn on sensitive reactions/species for i in range(gas.n_reactions): r.add_sensitivity_reaction(i) for i in range(surf.n_reactions): rsurf.add_sensitivity_reaction(i) # for i in range(gas.n_species): # r.add_sensitivity_species_enthalpy(i) # for i in range(surf.n_species): # rsurf.add_sensitivity_species_enthalpy(i) for j in sens_species: gasrxn_sens_str = [ j + " sensitivity to " + i for i in gas.reaction_equations() ] surfrxn_sens_str = [ j + " sensitivity to " + i for i in surf.reaction_equations() ] # gastherm_sens_str = [j + " thermo sensitivity to " + i for i in gas.species_names] # surftherm_sens_str = [j + " thermo sensitivity to " + i for i in surf.species_names] sens_list = gasrxn_sens_str + surfrxn_sens_str # + gastherm_sens_str writer.writerow([ "T (C)", "P (atm)", "V (M^3/s)", "X_co initial", "X_co2initial", "X_h2 initial", "X_h2o initial", "CO2/(CO2+CO)", "(CO+CO2/H2)", "T (C) final", "Rtol", "Atol", "reactor type", ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str + sens_list) else: writer.writerow([ "T (C)", "P (atm)", "V (M^3/s)", "X_co initial", "X_co2 initial", "X_h2 initial", "X_h2o initial", "CO2/(CO2+CO)", "(CO+CO2/H2)", "T (C) final", "Rtol", "Atol", "reactor type", ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str) # writer_csp.writerow( # ["iter", "t", "dt", "Density[kg/m3]", "Pressure[Pascal]", "Temperature[K]",] # + gas.species_names # + surf.species_names # ) t = 0.0 dt = 0.1 iter_ct = 0 # run the simulation first_run = True while t < reactime: # save flux diagrams at beginning of run if first_run == True: save_flux_diagrams(gas, suffix=flux_path, timepoint="beginning") save_flux_diagrams(surf, suffix=flux_path, timepoint="beginning") first_run = False t += dt sim.advance(t) # if t % 10 < 0.01: if sensitivity: # get sensitivity for sensitive species i (e.g. methanol) in reaction j for i in sens_species: g_nrxn = gas.n_reactions s_nrxn = surf.n_reactions # g_nspec = gas.n_species # s_nspec = surf.n_species gas_sensitivities = [ sim.sensitivity(i, j) for j in range(g_nrxn) ] surf_sensitivities = [ sim.sensitivity(i, j) for j in range(g_nrxn, g_nrxn + s_nrxn) ] # gas_therm_sensitivities = [sim.sensitivity(i,j) # for j in range(g_nrxn+s_nrxn,g_nrxn+s_nrxn+g_nspec)] # surf_therm_sensitivities = [sim.sensitivity(i,j) # for j in range(g_nrxn+s_nrxn+g_nspec,g_nrxn+s_nrxn+g_nspec+s_nspec)] sensitivities_all = ( gas_sensitivities + surf_sensitivities # + gas_therm_sensitivities ) writer.writerow([ temp, pressure, volume_flow, X_co, X_co2, X_h2, X_h2o, co2_ratio, h2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress) + sensitivities_all) else: writer.writerow([ temp, pressure, volume_flow, X_co, X_co2, X_h2, X_h2o, co2_ratio, h2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress)) # writer_csp.writerow( # [ # iter_ct, # sim.time, # dt, # gas.density, # gas.P, # gas.T, # ] # + list(gas.X) # + list(surf.X) # ) iter_ct += 1 outfile.close() # outfile_csp.close() # save flux diagrams at the end of the run save_flux_diagrams(gas, suffix=flux_path, timepoint="end") save_flux_diagrams(surf, suffix=flux_path, timepoint="end") return
def run_reactor( cti_file, t_array=[548], surf_t_array=[ 548 ], # not used, but will be for different starting temperatures p_array=[1], v_array=[2.771e-10 ], # 14*7*(140e-4)^2*π/2*0.9=0.0002771(cm^3)=2.771e-10(m^3) o2_array=[0.88], nh3_array=[0.066], rtol=1.0e-11, atol=1.0e-22, reactor_type=0, energy="off", sensitivity=False, sensatol=1e-6, sensrtol=1e-6, reactime=1e5, ): # 14 aluminum plates, each of them containing seven semi-cylindrical microchannels of 280 µm width # and 140 µm depth, 9 mm long, arranged at equal distances of 280 µm try: array_i = int(os.getenv("SLURM_ARRAY_TASK_ID")) except TypeError: array_i = 0 # get git commit hash and message rmg_model_path = "../ammonia" repo = git.Repo(rmg_model_path) date = time.localtime(repo.head.commit.committed_date) git_date = f"{date[0]}_{date[1]}_{date[2]}_{date[3]}{date[4]}" git_sha = str(repo.head.commit)[0:6] git_msg = str(repo.head.commit.message)[0:50].replace(" ", "_").replace( "'", "_").replace("\n", "") git_file_string = f"{git_date}_{git_sha}_{git_msg}" # set sensitivity string for file path name if sensitivity: sensitivity_str = "on" else: sensitivity_str = "off" # this should probably be outside of function settings = list( itertools.product(t_array, surf_t_array, p_array, v_array, o2_array, nh3_array)) # constants pi = math.pi # set initial temps, pressures, concentrations temp = settings[array_i][1] # kelvin temp_str = str(temp)[0:3] pressure = settings[array_i][2] * ct.one_atm # Pascals surf_temp = temp X_o2 = settings[array_i][4] x_O2_str = str(X_o2)[0].replace(".", "_") X_nh3 = (settings[array_i][5]) x_NH3_str = str(X_nh3)[0:11].replace(".", "_") X_he = 1 - X_o2 - X_nh3 mw_nh3 = 17.0306e-3 # [kg/mol] mw_o2 = 31.999e-3 # [kg/mol] mw_he = 4.002602e-3 # [kg/mol] o2_ratio = X_nh3 / X_o2 # O2/NH3/He: typical is concentrations_rmg = {"O2(2)": X_o2, "NH3(6)": X_nh3, "He": X_he} # initialize cantera gas and surface gas = ct.Solution(cti_file, "gas") surf = ct.Interface(cti_file, "surface1", [gas]) # initialize temperatures gas.TPX = temp, pressure, concentrations_rmg surf.TP = temp, pressure # change this to surf_temp when we want a different starting temperature for the surface # if a mistake is made with the input, # cantera will normalize the mole fractions. # make sure that we are reporting/using # the normalized values X_o2 = float(gas["O2(2)"].X) X_nh3 = float(gas["NH3(6)"].X) X_he = float(gas["He"].X) # create gas inlet inlet = ct.Reservoir(gas) # create gas outlet exhaust = ct.Reservoir(gas) # Reactor volume number_of_reactors = 1001 rradius = 1.4e-4 #140µm to 0.00014m rtotal_length = 9e-3 #9mm to 0.009m rtotal_vol = (rradius**2) * pi * rtotal_length / 2 rlength = rtotal_length / 1001 # divide totareactor total volume rvol = (rtotal_vol) / number_of_reactors # Catalyst Surface Area site_density = (surf.site_density * 1000 ) # [mol/m^2] cantera uses kmol/m^2, convert to mol/m^2 cat_area_total = rradius * 2 / 2 * pi * rtotal_length # [m^3] cat_area = cat_area_total / number_of_reactors # reactor initialization if reactor_type == 0: r = ct.Reactor(gas, energy=energy) reactor_type_str = "Reactor" elif reactor_type == 1: r = ct.IdealGasReactor(gas, energy=energy) reactor_type_str = "IdealGasReactor" elif reactor_type == 2: r = ct.ConstPressureReactor(gas, energy=energy) reactor_type_str = "ConstPressureReactor" elif reactor_type == 3: r = ct.IdealGasConstPressureReactor(gas, energy=energy) reactor_type_str = "IdealGasConstPressureReactor" # calculate the available catalyst area in a differential reactor rsurf = ct.ReactorSurface(surf, r, A=cat_area) r.volume = rvol surf.coverages = "X(1):1.0" # flow controllers one_atm = ct.one_atm FC_temp = 293.15 volume_flow = settings[array_i][3] # [m^3/s] molar_flow = volume_flow * one_atm / (8.3145 * FC_temp) # [mol/s] mass_flow = molar_flow * (X_nh3 * mw_nh3 + X_o2 * mw_o2 + X_he * mw_he ) # [kg/s] mfc = ct.MassFlowController(inlet, r, mdot=mass_flow) # A PressureController has a baseline mass flow rate matching the 'master' # MassFlowController, with an additional pressure-dependent term. By explicitly # including the upstream mass flow rate, the pressure is kept constant without # needing to use a large value for 'K', which can introduce undesired stiffness. outlet_mfc = ct.PressureController(r, exhaust, master=mfc, K=0.01) # initialize reactor network sim = ct.ReactorNet([r]) # set relative and absolute tolerances on the simulation sim.rtol = 1.0e-8 sim.atol = 1.0e-16 ################################################# # Run single reactor ################################################# # round numbers for filepath strings so they're easier to read # temp_str = '%s' % '%.3g' % tempn cat_area_str = "%s" % "%.3g" % cat_area # if it doesn't already exist, g species_path = (os.path.dirname(os.path.abspath(__file__)) + f"/{git_file_string}/species_pictures") results_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_file_string}/{reactor_type_str}/energy_{energy}/sensitivity_{sensitivity_str}/{temp_str}/results" ) flux_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_file_string}/{reactor_type_str}/energy_{energy}/sensitivity_{sensitivity_str}/{temp_str}/flux_diagrams/{x_O2_str}/{x_NH3_str}" ) # create species folder for species pictures if it does not already exist try: os.makedirs(species_path, exist_ok=True) save_pictures(git_path=rmg_model_path, species_path=species_path) except OSError as error: print(error) try: os.makedirs(results_path, exist_ok=True) except OSError as error: print(error) try: os.makedirs(flux_path, exist_ok=True) except OSError as error: print(error) gas_ROP_str = [i + " ROP [kmol/m^3 s]" for i in gas.species_names] # surface ROP reports gas and surface ROP. these values are not redundant gas_surf_ROP_str = [ i + " surface ROP [kmol/m^2 s]" for i in gas.species_names ] surf_ROP_str = [i + " ROP [kmol/m^2 s]" for i in surf.species_names] gasrxn_ROP_str = [ i + " ROP [kmol/m^3 s]" for i in gas.reaction_equations() ] surfrxn_ROP_str = [ i + " ROP [kmol/m^2 s]" for i in surf.reaction_equations() ] output_filename = ( results_path + f"/Spinning_basket_area_{cat_area_str}_energy_{energy}" + f"_temp_{temp}_O2_{x_O2_str}_NH3_{x_NH3_str}.csv") outfile = open(output_filename, "w") writer = csv.writer(outfile) # Sensitivity atol, rtol, and strings for gas and surface reactions if selected # slows down script by a lot if sensitivity: sim.rtol_sensitivity = sensrtol sim.atol_sensitivity = sensatol sens_species = [ "NH3(6)", "O2(2)", "N2(4)", "NO(5)", "N2O(7)" ] #change THIS to your species, can add "," and other species # turn on sensitive reactions for i in range(gas.n_reactions): r.add_sensitivity_reaction(i) for i in range(surf.n_reactions): rsurf.add_sensitivity_reaction(i) # thermo sensitivities. leave off for now as they can cause solver crashes # for i in range(gas.n_species): # r.add_sensitivity_species_enthalpy(i) # for i in range(surf.n_species): # rsurf.add_sensitivity_species_enthalpy(i) for j in sens_species: gasrxn_sens_str = [ j + " sensitivity to " + i for i in gas.reaction_equations() ] surfrxn_sens_str = [ j + " sensitivity to " + i for i in surf.reaction_equations() ] # gastherm_sens_str = [j + " thermo sensitivity to " + i for i in gas.species_names] # surftherm_sens_str = [j + " thermo sensitivity to " + i for i in surf.species_names] sens_list = gasrxn_sens_str + surfrxn_sens_str # + gastherm_sens_str writer.writerow([ "Distance (mm)", "T (C)", "P (Pa)", "V (M^3/s)", "X_nh3 initial", "X_o2 initial", "X_he initial", "(NH3/O2)", "T (C) final", "Rtol", "Atol", "reactor type", "energy on?" ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str + sens_list) else: writer.writerow([ "Distance (mm)", "T (C)", "P (Pa)", "V (M^3/s)", "X_nh3 initial", "X_o2 initial", "X_he initial", "(NH3/O2)", "T (C) final", "Rtol", "Atol", "reactor type", "energy on?" ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str) t = 0.0 dt = 0.1 iter_ct = 0 # run the simulation first_run = True distance_mm = 0 for n in range(number_of_reactors): # Set the state of the reservoir to match that of the previous reactor gas.TDY = TDY = r.thermo.TDY inlet.syncState() sim.reinitialize() previous_coverages = surf.coverages # in case we want to retry if n > 0: # Add a first row in the CSV with just the feed try: sim.advance_to_steady_state() except ct.CanteraError: t = sim.time sim.set_initial_time(0) gas.TDY = TDY surf.coverages = previous_coverages r.syncState() sim.reinitialize() new_target_time = 0.01 * t logging.warning( f"Couldn't reach {t:.1g} s so going to try {new_target_time:.1g} s" ) try: sim.advance(new_target_time) except ct.CanteraError: outfile.close() raise # save flux diagrams at beginning of run if first_run == True: save_flux_diagrams(gas, suffix=flux_path, timepoint="beginning", species_path=species_path) save_flux_diagrams(surf, suffix=flux_path, timepoint="beginning", species_path=species_path) first_run = False if sensitivity: # get sensitivity for sensitive species i (e.g. methanol) in reaction j for i in sens_species: g_nrxn = gas.n_reactions s_nrxn = surf.n_reactions # g_nspec = gas.n_species # s_nspec = surf.n_species gas_sensitivities = [ sim.sensitivity(i, j) for j in range(g_nrxn) ] surf_sensitivities = [ sim.sensitivity(i, j) for j in range(g_nrxn, g_nrxn + s_nrxn) ] # gas_therm_sensitivities = [sim.sensitivity(i,j) # for j in range(g_nrxn+s_nrxn,g_nrxn+s_nrxn+g_nspec)] # surf_therm_sensitivities = [sim.sensitivity(i,j) # for j in range(g_nrxn+s_nrxn+g_nspec,g_nrxn+s_nrxn+g_nspec+s_nspec)] sensitivities_all = ( gas_sensitivities + surf_sensitivities # + gas_therm_sensitivities ) writer.writerow([ distance_mm, temp, gas.P, volume_flow, X_nh3, X_o2, X_he, o2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, energy, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress) + sensitivities_all, ) else: writer.writerow([ distance_mm, temp, gas.P, volume_flow, X_nh3, X_o2, X_he, o2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, energy, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress)) iter_ct += 1 distance_mm = n * rlength * 1.0e3 # distance in mm outfile.close() # save flux diagrams at the end of the run save_flux_diagrams(gas, suffix=flux_path, timepoint="end", species_path=species_path) save_flux_diagrams(surf, suffix=flux_path, timepoint="end", species_path=species_path) return
# -*- coding: utf-8 -*- """ Created on Sat Mar 31 10:11:24 2018 @author: wilson """ import cantera as ct gas = ct.Solution('gri30.cti') gas2 = ct.Solution('gri30.cti') r = ct.ConstPressureReactor(gas) r.volume = 1 env = ct.Reservoir(gas2) w = ct.Wall(r, env) w.set_heat_flux(2) # w.set_heat_flux is the function to use net = ct.ReactorNet({r}) print(r.thermo.h * r.mass) net.advance(1) print(r.thermo.h * r.mass)
def getStateAtTime(flame, tList, tRequired, mech='gri30.xml'): '''A function that gets the state at a desired point in time of a flame simulation performed using runFlame. Takes in a flame object, its associated time series, and the desired point in time (in seconds). Returns a new Cantera gas object with the desired state, and the corresponding index in the flame at which the point was found. Example usage: gas, t_ind = getStateAtTime(flame, time, t_req)''' mainBurnerDF = flame columnNames = mainBurnerDF.columns.values vel_final = mainBurnerDF['u'].iloc[-1] moleFracs = mainBurnerDF.columns[mainBurnerDF.columns.str.contains('X_')] assert (tRequired > 0) newGas = ct.Solution(mech) if (tRequired >= max(tList)): tau_vit = tRequired - max(tList) newGas.TPX = flame['T'].iloc[-1], flame['P'].iloc[-1], flame[moleFracs].iloc[-1] tau_vit_start = tList[-1] else: dt_list = abs(tList - tRequired) # Find index at which required time is closest to an entry in tList: minIndex = next(ind for ind, val in enumerate(dt_list) if abs(val - min(dt_list)) < 1e-6) flameTime_closestTreq = tList[minIndex] if ((flameTime_closestTreq - tRequired) > 1e-6): newGas.TPX = flame['T'].iloc[minIndex-1], flame['P'].iloc[minIndex-1], flame[moleFracs].iloc[minIndex-1] assert((newGas['NO'].X - flame['X_NO'].iloc[minIndex-1] <= 1e-6)) tau_vit = tRequired - tList[minIndex - 1] tau_vit_start = tList[minIndex - 1] assert(tau_vit > 0)# if the closest flameTime is larger than tRequired, the second closest should be less than, otherwise /that/ would be the closest...? elif ((tRequired - flameTime_closestTreq) > 1e-6): # if closest time is more than 1e-6 s less than tReq newGas.TPX = flame['T'].iloc[minIndex], flame['P'].iloc[minIndex], flame[moleFracs].iloc[minIndex] assert((newGas['NO'].X - flame['X_NO'].iloc[minIndex] <= 1e-6)) tau_vit = tRequired - tList[minIndex] tau_vit_start = tList[minIndex] else: newGas.TPX = flame['T'].iloc[minIndex], flame['P'].iloc[minIndex], flame[moleFracs].iloc[minIndex] tau_vit = 0 assert((newGas['NO'].X - flame['X_NO'].iloc[minIndex] <= 1e-6)) if tau_vit > 0: vitiator = ct.ConstPressureReactor(newGas) vitRN = ct.ReactorNet([vitiator]) dt = 0.00001 * 1e-3 vit_tList = np.arange(0, tau_vit, dt) vitArray = np.array([None] * len(vit_tList) * len(columnNames)).reshape(len(vit_tList), len(columnNames)) # performance note: we don't need to do this. we can simply just advance to the desired tau_main for i in range(0, len(vit_tList)): MWi = vitiator.thermo.mean_molecular_weight vitArray[i, :] = np.hstack([vel_final * dt, vel_final, vitiator.thermo.T, 0, MWi, vitiator.thermo.Y, vitiator.thermo.X, vitiator.thermo.P]) vitRN.advance(vit_tList[i]) vit_tList += tau_vit_start vitDF = pd.DataFrame(data=vitArray, index=vit_tList, columns=columnNames, dtype=np.float64) # print("Vitiator end time:", vit_tList[-1]/1e-3, "milliseconds") vitiator.syncState() '''Call syncState so that newGas has the right state for use in later functions.''' minIndex = -1 # last index in time list mainBurnerDF = mainBurnerDF[np.array(mainBurnerDF.index > 0) & np.array(mainBurnerDF.index <= tRequired)] # print("Initial mainBurnerDF length:", len(mainBurnerDF.index.values)) mainBurnerDF = pd.concat([mainBurnerDF, vitDF]) # print("New mainBurnerDF length:", len(mainBurnerDF.index.values)) else: mainBurnerDF = mainBurnerDF[np.array(mainBurnerDF.index > 0) & np.array(mainBurnerDF.index <= tRequired)] mainBurnerDF['NOppmvd'] = correctNOx(mainBurnerDF['X_NO'].values, mainBurnerDF['X_H2O'].values, mainBurnerDF['X_O2'].values) mainBurnerDF['COppmvd'] = correctNOx(mainBurnerDF['X_CO'].values, mainBurnerDF['X_H2O'].values, mainBurnerDF['X_O2'].values) return newGas, minIndex, mainBurnerDF
def run_reactor( cti_file, t_array=[548], p_array=[1], v_array=[2.7155e-8], #14*7*(140e-4)^2*π/2*0.9=0.02715467 (cm3) o2_array=[0.88], nh3_array=[0.066], rtol=1.0e-11, atol=1.0e-22, reactor_type=0, energy="off", sensitivity=False, sensatol=1e-6, sensrtol=1e-6, reactime=1e5, ): #14 aluminum plates, each of them containing seven semi-cylindrical mi-crochannels of 280 µm width # and 140 µm depth, 9 mm long, arranged at equal distances of 280 µm try: array_i = int(os.getenv("SLURM_ARRAY_TASK_ID")) except TypeError: array_i = 0 # get git commit hash and message repo = git.Repo("/work/westgroup/lee.ting/cantera/ammonia/") git_sha = str(repo.head.commit)[0:6] git_msg = str(repo.head.commit.message)[0:20].replace(" ", "_").replace("'", "_") # this should probably be outside of function settings = list(itertools.product(t_array, p_array, v_array, o2_array, nh3_array)) # constants pi = math.pi # set initial temps, pressures, concentrations temp = settings[array_i][0] # kelvin temp_str = str(temp)[0:3] pressure = settings[array_i][1] * ct.one_atm # Pascals X_o2 = settings[array_i][3] x_O2_str = str(X_o2)[0:3].replace(".", "_") if X_o2 == 0.88: X_he = 0.054 else: X_he = 0 X_nh3 = (1 - (X_o2 + X_he)) * (settings[array_i][4]) x_NH3_str = str(X_nh3)[0:8].replace(".", "_") mw_nh3 = 17.0306e-3 # [kg/mol] mw_o2 = 31.999e-3 # [kg/mol] mw_he = 4.002602e-3 # [kg/mol] o2_ratio = X_nh3 / X_o2 # O2/NH3/He: typical is concentrations_rmg = {"O2(2)": X_o2, "NH3(6)": X_nh3, "He": X_he} # initialize cantera gas and surface gas = ct.Solution(cti_file, "gas") # surf_grab = ct.Interface(cti_file,'surface1_grab', [gas_grab]) surf = ct.Interface(cti_file, "surface1", [gas]) # gas_grab.TPX = gas.TPX = temp, pressure, concentrations_rmg surf.TP = temp, pressure # create gas inlet inlet = ct.Reservoir(gas) # create gas outlet exhaust = ct.Reservoir(gas) # Reactor volume rradius = 1.4e-4 #140µm to 0.00014m rlength = 9e-3 #9mm to 0.009m rvol = (rradius ** 2) * pi * rlength / 2 # Catalyst Surface Area site_density = (surf.site_density * 1000) # [mol/m^2]cantera uses kmol/m^2, convert to mol/m^2 cat_area = rradius * 2 / 2 * pi * rlength # [m^3] #suface site density = 1.86e-9 mol/cm2 = 1.96e-5 mol/m2; molecular weight for Pt = 195.084 g/mol # per kg has 5.125997 moles Pt = 5.125997*6.022e23/1.12e15(cm-2) = 2.756138744e9 cm2/kg = 2.756e5m2/kg # reactor initialization if reactor_type == 0: r = ct.Reactor(gas, energy=energy) reactor_type_str = "Reactor" elif reactor_type == 1: r = ct.IdealGasReactor(gas, energy=energy) reactor_type_str = "IdealGasReactor" elif reactor_type == 2: r = ct.ConstPressureReactor(gas, energy=energy) reactor_type_str = "ConstPressureReactor" elif reactor_type == 3: r = ct.IdealGasConstPressureReactor(gas, energy=energy) reactor_type_str = "IdealGasConstPressureReactor" rsurf = ct.ReactorSurface(surf, r, A=cat_area) r.volume = rvol surf.coverages = "X(1):1.0" # flow controllers (Graaf measured flow at 293.15 and 1 atm) one_atm = ct.one_atm FC_temp = 293.15 volume_flow = settings[array_i][2] # [m^3/s] molar_flow = volume_flow * one_atm / (8.3145 * FC_temp) # [mol/s] mass_flow = molar_flow * (X_nh3 * mw_nh3 + X_o2 * mw_o2 + X_he * mw_he) # [kg/s] mfc = ct.MassFlowController(inlet, r, mdot=mass_flow) # A PressureController has a baseline mass flow rate matching the 'master' # MassFlowController, with an additional pressure-dependent term. By explicitly # including the upstream mass flow rate, the pressure is kept constant without # needing to use a large value for 'K', which can introduce undesired stiffness. outlet_mfc = ct.PressureController(r, exhaust, master=mfc, K=0.01) # initialize reactor network sim = ct.ReactorNet([r]) # set relative and absolute tolerances on the simulation sim.rtol = 1.0e-11 sim.atol = 1.0e-22 ################################################# # Run single reactor ################################################# # round numbers so they're easier to read # temp_str = '%s' % '%.3g' % tempn cat_area_str = "%s" % "%.3g" % cat_area results_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/transient/{temp_str}/results" ) results_path_csp = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/transient/{temp_str}/results/csp" ) flux_path = ( os.path.dirname(os.path.abspath(__file__)) + f"/{git_sha}_{git_msg}/{reactor_type_str}/transient/{temp_str}/flux_diagrams/{x_O2_str}/{x_NH3_str}" ) try: os.makedirs(results_path, exist_ok=True) except OSError as error: print(error) try: os.makedirs(results_path_csp, exist_ok=True) except OSError as error: print(error) try: os.makedirs(flux_path, exist_ok=True) except OSError as error: print(error) gas_ROP_str = [i + " ROP [kmol/m^3 s]" for i in gas.species_names] # surface ROP reports gas and surface ROP. these values might be redundant, not sure. gas_surf_ROP_str = [i + " surface ROP [kmol/m^2 s]" for i in gas.species_names] surf_ROP_str = [i + " ROP [kmol/m^2 s]" for i in surf.species_names] gasrxn_ROP_str = [i + " ROP [kmol/m^3 s]" for i in gas.reaction_equations()] surfrxn_ROP_str = [i + " ROP [kmol/m^2 s]" for i in surf.reaction_equations()] output_filename = ( results_path + f"/Spinning_basket_area_{cat_area_str}_energy_{energy}" + f"_temp_{temp}_O2_{x_O2_str}_NH3_{x_NH3_str}.csv" ) output_filename_csp = ( results_path_csp + f"/Spinning_basket_area_{cat_area_str}_energy_{energy}" + f"_temp_{temp}_h2_{x_O2_str}_NH3_{x_NH3_str}.csv" ) outfile = open(output_filename, "w") outfile_csp = open(output_filename_csp, "w") writer = csv.writer(outfile) writer_csp = csv.writer(outfile_csp) # Sensitivity atol, rtol, and strings for gas and surface reactions if selected # slows down script by a lot if sensitivity: sim.rtol_sensitivity = sensrtol sim.atol_sensitivity = sensatol sens_species = ["NH3(6)"] #change THIS to your species, can add "," and other species # turn on sensitive reactions/species for i in range(gas.n_reactions): r.add_sensitivity_reaction(i) for i in range(surf.n_reactions): rsurf.add_sensitivity_reaction(i) # for i in range(gas.n_species): # r.add_sensitivity_species_enthalpy(i) # for i in range(surf.n_species): # rsurf.add_sensitivity_species_enthalpy(i) for j in sens_species: gasrxn_sens_str = [ j + " sensitivity to " + i for i in gas.reaction_equations() ] surfrxn_sens_str = [ j + " sensitivity to " + i for i in surf.reaction_equations() ] # gastherm_sens_str = [j + " thermo sensitivity to " + i for i in gas.species_names] # surftherm_sens_str = [j + " thermo sensitivity to " + i for i in surf.species_names] sens_list = gasrxn_sens_str + surfrxn_sens_str # + gastherm_sens_str writer.writerow( [ "T (C)", "P (atm)", "V (M^3/s)", "X_nh3 initial", "X_o2 initial", "X_he initial", "(NH3/O2)", "T (C) final", "Rtol", "Atol", "reactor type", ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str + sens_list ) else: writer.writerow( [ "T (C)", "P (atm)", "V (M^3/s)", "X_nh3 initial", "X_o2 initial", "X_he initial", "(NH3/O2)", "T (C) final", "Rtol", "Atol", "reactor type", ] + gas.species_names + surf.species_names + gas_ROP_str + gas_surf_ROP_str + surf_ROP_str + gasrxn_ROP_str + surfrxn_ROP_str ) writer_csp.writerow( ["iter", "t", "dt", "Density[kg/m3]", "Pressure[Pascal]", "Temperature[K]",] + gas.species_names + surf.species_names ) t = 0.0 dt = 0.1 iter_ct = 0 # run the simulation first_run = True while t < reactime: # save flux diagrams at beginning of run if first_run == True: save_flux_diagrams(gas, suffix=flux_path, timepoint="beginning") save_flux_diagrams(surf, suffix=flux_path, timepoint="beginning") first_run = False t += dt sim.advance(t) # if t % 10 < 0.01: if sensitivity: # get sensitivity for sensitive species i (e.g. methanol) in reaction j for i in sens_species: g_nrxn = gas.n_reactions s_nrxn = surf.n_reactions # g_nspec = gas.n_species # s_nspec = surf.n_species gas_sensitivities = [sim.sensitivity(i, j) for j in range(g_nrxn)] surf_sensitivities = [ sim.sensitivity(i, j) for j in range(g_nrxn, g_nrxn + s_nrxn) ] # gas_therm_sensitivities = [sim.sensitivity(i,j) # for j in range(g_nrxn+s_nrxn,g_nrxn+s_nrxn+g_nspec)] # surf_therm_sensitivities = [sim.sensitivity(i,j) # for j in range(g_nrxn+s_nrxn+g_nspec,g_nrxn+s_nrxn+g_nspec+s_nspec)] sensitivities_all = ( gas_sensitivities + surf_sensitivities # + gas_therm_sensitivities ) writer.writerow( [ temp, pressure, volume_flow, X_nh3, X_o2, X_he, o2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress) + sensitivities_all ) else: writer.writerow( [ temp, pressure, volume_flow, X_nh3, X_o2, X_he, o2_ratio, gas.T, sim.rtol, sim.atol, reactor_type_str, ] + list(gas.X) + list(surf.X) + list(gas.net_production_rates) + list(surf.net_production_rates) + list(gas.net_rates_of_progress) + list(surf.net_rates_of_progress) ) writer_csp.writerow( [ iter_ct, sim.time, dt, gas.density, gas.P, gas.T, ] + list(gas.X) + list(surf.X) ) iter_ct += 1 outfile.close() outfile_csp.close() # save flux diagrams at the end of the run save_flux_diagrams(gas, suffix=flux_path, timepoint="end") save_flux_diagrams(surf, suffix=flux_path, timepoint="end") return