Beispiel #1
0
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
Beispiel #2
0
    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])
Beispiel #3
0
    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])
Beispiel #4
0
    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)
Beispiel #5
0
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
Beispiel #7
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
Beispiel #8
0
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
Beispiel #9
0
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)
Beispiel #11
0
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