def __init__(self,
                 cti_file,
                 init_X,
                 inlet_X,
                 inlet_F,
                 volume,
                 n_cstr=0,
                 P=ct.one_atm,
                 T=298,
                 area=None,
                 K=1e-5,
                 kin_param_to_set=None):
        '''

        parameters:
        cti_file: str,
        the cti file must contain a gas phase named 'gas' and optionally a reactive surface named 'surface'

        init_X: dict, array or list of them
            initial composition of the reactors
    '''
        if not HAS_CANTERA:
            raise SpectroChemPyException(
                'Cantera is not available : please install it before continuing:  \n'
                'conda install -c cantera cantera')

        if area is None:
            add_surface = False
        else:
            add_surface = True

        # copy inlet parameters (for copy)
        self._cti = cti_file
        self._init_X = init_X
        self._inlet_X = inlet_X
        self._inlet_F = inlet_F
        self._volume = volume
        self.T = T
        self.P = P
        self._area = area
        self._K = K
        self._kin_param_to_set = kin_param_to_set

        self.cstr = []  #  list of cstrs
        self.surface = []  # reactor surfaces of cstr's
        self._mfc = []  # mass flow
        self.inlet = []  # reservoirs
        self.event = None
        self._pc = []  # pressure controllers

        if isinstance(self._volume, (float, int)):
            self._volume = self._volume * np.ones((n_cstr)) / n_cstr

        if add_surface and isinstance(area, (float, int)):
            self._area = self.area * np.ones((n_cstr)) / n_cstr
        self.n_cstr = len(volume)

        # first cstr
        initial_gas = ct.Solution(self._cti, 'gas')
        initial_gas.TPX = self.T, self.P, init_X
        self.n_gas_species = len(initial_gas.X)
        self.cstr.append(
            ct.IdealGasReactor(initial_gas, name="R_0", energy='off'))
        self.cstr[0].volume = volume[0]

        if add_surface:
            surface = ct.Interface(self._cti,
                                   phaseid='surface',
                                   phases=[initial_gas])
            if kin_param_to_set is not None:
                modify_surface_kinetics(surface, kin_param_to_set)
            self.n_surface_species = len(surface.X)
            self.surface.append(
                ct.ReactorSurface(kin=surface, r=self.cstr[0], A=area[0]))

        # create and connect inlets to R_0
        if not isinstance(inlet_X, Iterable):
            inlet_X = [inlet_X]
            inlet_F = [inlet_F]

        self._inlet_F = inlet_F

        for i, (X, F) in enumerate(zip(inlet_X, self._inlet_F)):
            inlet_gas = ct.Solution(self._cti, 'gas')
            inlet_gas.TPX = self.T, self.P, X
            self.inlet.append(
                ct.Reservoir(contents=inlet_gas, name=f'inlet_{i}'))
            self._mfc.append(
                ct.MassFlowController(self.inlet[-1],
                                      self.cstr[0],
                                      name=f'MFC_{i}'))

            if not callable(F):
                self._mfc[-1].set_mass_flow_rate(F * inlet_gas.density)
            else:
                # it is tricky to pass non explicit lambda functions to MassFlowControllers
                # the following works while use of self._inlet_F[i](t) generate an error
                # when using reactorNet.advance()

                if i == 0:
                    self._mfc[-1].set_mass_flow_rate(lambda t: self._inlet_F[0]
                                                     (t) * inlet_gas.density)
                elif i == 1:
                    self._mfc[-1].set_mass_flow_rate(lambda t: self._inlet_F[1]
                                                     (t) * inlet_gas.density)
                elif i == 2:
                    self._mfc[-1].set_mass_flow_rate(lambda t: self._inlet_F[2]
                                                     (t) * inlet_gas.density)
                elif i == 3:
                    self._mfc[-1].set_mass_flow_rate(lambda t: self._inlet_F[3]
                                                     (t) * inlet_gas.density)
                elif i == 4:
                    self._mfc[-1].set_mass_flow_rate(lambda t: self._inlet_F[4]
                                                     (t) * inlet_gas.density)

                else:
                    raise ValueError(
                        "variable flow rate(s) must be associated within the first"
                        "five MFC(s)")

        # create other cstrs and link them with the previous one through a pressure controller

        for i in range(1, len(volume)):
            initial_gas = ct.Solution(self._cti, 'gas')
            initial_gas.TPX = self.T, self.P, init_X
            self.cstr.append(
                ct.IdealGasReactor(initial_gas, name="R_0", energy='off'))
            self.cstr[i].volume = volume[i]

            if add_surface:
                surface = ct.Interface(self._cti,
                                       phaseid='surface',
                                       phases=[initial_gas])
                self.n_surface_species = len(surface.X)
                if kin_param_to_set is not None:
                    modify_surface_kinetics(surface, kin_param_to_set)
                self.surface.append(
                    ct.ReactorSurface(kin=surface, r=self.cstr[i], A=area[i]))

            self._pc.append(
                ct.PressureController(self.cstr[i - 1],
                                      self.cstr[i],
                                      master=self._mfc[-1],
                                      K=K))

        # create the event

        event_gas = ct.Solution(self._cti, 'gas')
        event_gas.TPX = self.T, self.P, init_X
        self.event = ct.Reservoir(contents=event_gas, name=f'event')
        self._pc.append(
            ct.PressureController(self.cstr[-1],
                                  self.event,
                                  master=self._mfc[-1],
                                  K=K))

        self.X = np.ones((self.n_cstr, self.n_gas_species))
        self.coverages = np.ones((self.n_cstr, self.n_surface_species))

        for i, (r, s) in enumerate(zip(self.cstr, self.surface)):
            self.X[i, :] = r.thermo.X
            self.coverages[i, :] = s.coverages

        self.net = ct.ReactorNet(self.cstr)
Esempio n. 2
0
    def simulate(self):
        """
        Run all the conditions as a cantera simulation.
        Returns the data as a list of tuples containing: (time, [list of temperature, pressure, and species data]) 
            for each reactor condition
        """
        # Get all the cantera names for the species
        speciesNamesList = [
            getSpeciesIdentifier(species) for species in self.speciesList
        ]
        inertIndexList = [
            self.speciesList.index(species) for species in self.speciesList
            if species.index == -1
        ]

        allData = []
        for condition in self.conditions:

            # First translate the molFrac from species objects to species names
            newMolFrac = {}
            for key, value in condition.molFrac.iteritems():
                newkey = getSpeciesIdentifier(key)
                newMolFrac[newkey] = value

            # Set Cantera simulation conditions
            if condition.V0 is None:
                self.model.TPX = condition.T0.value_si, condition.P0.value_si, newMolFrac
            elif condition.P0 is None:
                self.model.TDX = condition.T0.value_si, 1.0 / condition.V0.value_si, newMolFrac
            else:
                raise Exception(
                    "Cantera conditions in which T0 and P0 or T0 and V0 are not the specified state variables are not yet implemented."
                )

            # Choose reactor
            if condition.reactorType == 'IdealGasReactor':
                canteraReactor = ct.IdealGasReactor(self.model)
            elif condition.reactorType == 'IdealGasConstPressureReactor':
                canteraReactor = ct.IdealGasConstPressureReactor(
                    contents=self.model)
            elif condition.reactorType == 'IdealGasConstPressureTemperatureReactor':
                canteraReactor = ct.IdealGasConstPressureReactor(
                    contents=self.model, energy='off')
            else:
                raise Exception(
                    'Other types of reactor conditions are currently not supported'
                )

            # Run this individual condition as a simulation
            canteraSimulation = ct.ReactorNet([canteraReactor])

            numCtReactions = len(self.model.reactions())
            if self.sensitiveSpecies:
                if ct.__version__ == '2.2.1':
                    print 'Warning: Cantera version 2.2.1 may not support sensitivity analysis unless SUNDIALS was used during compilation.'
                    print 'Warning: Upgrade to newer of Cantera in anaconda using the command "conda update -c rmg cantera"'
                # Add all the reactions as part of the analysis
                for i in range(numCtReactions):
                    canteraReactor.add_sensitivity_reaction(i)
                # Set the tolerances for the sensitivity coefficients
                canteraSimulation.rtol_sensitivity = 1e-4
                canteraSimulation.atol_sensitivity = 1e-6

            # Initialize the variables to be saved
            times = []
            temperature = []
            pressure = []
            speciesData = []
            sensitivityData = []

            # Begin integration
            time = 0.0
            # Run the simulation over 100 time points
            while canteraSimulation.time < condition.reactionTime.value_si:

                # Advance the state of the reactor network in time from the current time to time t [s], taking as many integrator timesteps as necessary.
                canteraSimulation.step(condition.reactionTime.value_si)
                times.append(canteraSimulation.time)
                temperature.append(canteraReactor.T)
                pressure.append(canteraReactor.thermo.P)
                speciesData.append(canteraReactor.thermo[speciesNamesList].X)

                if self.sensitiveSpecies:
                    # Cantera returns mass-based sensitivities rather than molar concentration or mole fraction based sensitivities.
                    # The equation for converting between them is:
                    #
                    # d ln xi = d ln wi - sum_(species i) (dln wi) (xi)
                    #
                    # where xi is the mole fraction of species i and wi is the mass fraction of species i

                    massFracSensitivityArray = canteraSimulation.sensitivities(
                    )
                    if condition.reactorType == 'IdealGasReactor':
                        # Row 0: mass, Row 1: volume, Row 2: internal energy or temperature, Row 3+: mass fractions of species
                        massFracSensitivityArray = massFracSensitivityArray[
                            3:, :]
                    elif condition.reactorType == 'IdealGasConstPressureReactor' or condition.reactorType == 'IdealGasConstPressureTemperatureReactor':
                        # Row 0: mass, Row 1: enthalpy or temperature, Row 2+: mass fractions of the species
                        massFracSensitivityArray = massFracSensitivityArray[
                            2:, :]
                    else:
                        raise Exception(
                            'Other types of reactor conditions are currently not supported'
                        )

                    for i in range(len(massFracSensitivityArray)):
                        massFracSensitivityArray[i] *= speciesData[-1][i]

                    sensitivityArray = np.zeros(
                        len(self.sensitiveSpecies) *
                        len(self.model.reactions()))
                    for index, species in enumerate(self.sensitiveSpecies):
                        for j in range(numCtReactions):
                            sensitivityArray[
                                numCtReactions * index +
                                j] = canteraSimulation.sensitivity(
                                    species.toChemkin(), j)

                            for i in range(len(massFracSensitivityArray)):
                                if i not in inertIndexList:
                                    # massFracSensitivity for inerts are returned as nan in Cantera, so we must not include them here
                                    sensitivityArray[
                                        numCtReactions * index +
                                        j] -= massFracSensitivityArray[i][j]
                    sensitivityData.append(sensitivityArray)

            # Convert speciesData and sensitivityData to a numpy array
            speciesData = np.array(speciesData)
            sensitivityData = np.array(sensitivityData)

            # Resave data into generic data objects
            time = GenericData(label='Time', data=times, units='s')
            temperature = GenericData(label='Temperature',
                                      data=temperature,
                                      units='K')
            pressure = GenericData(label='Pressure', data=pressure, units='Pa')
            conditionData = []
            conditionData.append(temperature)
            conditionData.append(pressure)

            for index, species in enumerate(self.speciesList):
                # Create generic data object that saves the species object into the species object.  To allow easier manipulate later.
                speciesGenericData = GenericData(label=speciesNamesList[index],
                                                 species=species,
                                                 data=speciesData[:, index],
                                                 index=species.index)
                conditionData.append(speciesGenericData)

            reactionSensitivityData = []
            for index, species in enumerate(self.sensitiveSpecies):
                for j in range(numCtReactions):
                    reactionSensitivityGenericData = GenericData(
                        label='dln[{0}]/dln[k{1}]: {2}'.format(
                            species.toChemkin(), j + 1,
                            self.model.reactions()[j]),
                        species=species,
                        reaction=self.model.reactions()[j],
                        data=sensitivityData[:, numCtReactions * index + j],
                        index=j + 1,
                    )
                    reactionSensitivityData.append(
                        reactionSensitivityGenericData)

            allData.append((time, conditionData, reactionSensitivityData))

        return allData
Esempio n. 3
0
def ignUVTimeScales(gas, dt):
    '''
    Function ignUVAll
    ======================================================================
    This function returns the ignition delay and excitation time for the 
    reactor
        gas: cantera phase object
        dt: initial guess for the timestep
        return: (tign,texcite)
    '''
    def findTimescales(t, qdot):
        '''
        Function findTimescales
        ======================================================================
        This function determines the ignition delay and the excitation time 
        (using 5% of qdotMax).
        t: time 
        qdot: volumetric chemical energy release
        return: (tIgn,tExcite,resolution): resolution is given in number
                of timesteps between tIgn and the start of tExcitement
        '''
        qdotMax = np.max(qdot)
        k = len(qdot) - 1
        kMax = 0
        kExcite = 0
        while k >= 0:
            if qdot[k] == qdotMax: kMax = k
            if kMax != 0 and qdot[k] <= qdotMax * 0.05:
                kExcite = k
                break
            k -= 1
        if kMax == 0: Exception("Failed to find the excitation time")
        tExcite = t[kMax] - t[kExcite]
        tIgn = t[kMax]
        return tIgn, tExcite, kMax - kExcite, kExcite

    #find the equilibrium
    (T, p, X) = gas.TPX
    gas.equilibrate('UV')
    try:
        iH2O = gas.species_index('H2O')
        H2O = 'H2O'
    except:
        iH2O = gas.species_index('h2o')
        H2O = 'h2o'
    H2Oeq = gas.Y[iH2O]
    gas.TPX = (T, p, X)
    alpha = 0.95
    #arbitrary percentage
    #initiate the reactor network
    T0, p0, Y0 = gas.TPY
    qdot0 = chemicalPower(gas, 'uv')
    #advance simulation with guessed timestep
    it, itMin, itMax = 0, 64, 1028
    while True:
        if it == 0:
            gas.TPY = T0, p0, Y0
            r = ct.IdealGasReactor(gas)
            sim = ct.ReactorNet([r])
            t0 = 0.0
            time = t0
            t = [t0]
            T = [T0]
            p = [p0]
            Y = [Y0]
            qdot = [qdot0]
            it += 1
        elif (r.thermo[H2O].Y >= (alpha * H2Oeq)) and it >= itMin:
            break
        elif (r.thermo[H2O].Y >= (alpha * H2Oeq)) and it < itMin:
            #not enough data points; resolve more
            it = 0
            dt /= 2.0
        elif it > itMax:
            #too resolved; increase time step
            it = 0
            dt *= 2.0
        else:
            time += dt
            sim.advance(time)
            t.append(time)
            T.append(gas.T)
            p.append(gas.P)
            Y.append(gas.Y)
            qdot.append(chemicalPower(gas, 'uv'))
            it += 1

    #find the ignition delay and the excitement time
    tIgn, tExcite, resolution, exciteIndex = findTimescales(t, qdot)
    N = 50
    while resolution < 50:  #zoom in on excitement time
        N *= 2  #enforced resolution
        kStart = max(exciteIndex - resolution, 0)
        gas.TPY = T[kStart], p[kStart], Y[kStart]
        r = ct.IdealGasReactor(gas)
        sim = ct.ReactorNet([r])
        time = 0.0
        tInitial = t[kStart]
        tFinal = tIgn + 2 * tExcite  #factor of two to make this symmetrical
        dt = (tFinal - tInitial) / float(N)
        t = [tInitial]
        T = [gas.T]
        p = [gas.P]
        Y = [gas.Y]
        qdot = [chemicalPower(gas, 'uv')]
        while time < (tFinal - tInitial):
            time += dt
            sim.advance(time)
            t.append(time + tInitial)
            T.append(gas.T)
            p.append(gas.P)
            Y.append(gas.Y)
            qdot.append(chemicalPower(gas, 'uv'))
        tIgn, tExcite, resolution, exciteIndex = findTimescales(t, qdot)
    return tIgn, tExcite
Esempio n. 4
0
    def run(self, initialTime: float = -1.0, finalTime: float = -1.0):
        if initialTime == -1.0:
            initialTime = self.initialTime
        if finalTime == -1.0:
            finalTime = self.finalTime
        self.timeHistory = None
        self.kineticSensitivities = None  #3D numpy array, columns are reactions with timehistories, depth gives the observable for those histories
        conditions = self.settingShockTubeConditions()
        mechanicalBoundary = conditions[1]
        #same solution for both cp and cv sims
        if mechanicalBoundary == 'constant pressure':
            shockTube = ct.IdealGasConstPressureReactor(
                self.processor.solution, name='R1', energy=conditions[0])
        else:
            shockTube = ct.IdealGasReactor(self.processor.solution,
                                           name='R1',
                                           energy=conditions[0])
        sim = ct.ReactorNet([shockTube])

        columnNames = [
            shockTube.component_name(item) for item in range(shockTube.n_vars)
        ]
        columnNames = ['time'] + ['pressure'] + columnNames
        self.timeHistory = pd.DataFrame(columns=columnNames)

        if self.kineticSens == 1:
            for i in range(self.processor.solution.n_reactions):
                shockTube.add_sensitivity_reaction(i)
            dfs = [pd.DataFrame() for x in range(len(self.observables))]
            tempArray = [
                np.zeros(self.processor.solution.n_reactions)
                for x in range(len(self.observables))
            ]

        t = self.initialTime
        counter = 0
        while t < self.finalTime:
            t = sim.step()
            if mechanicalBoundary == 'constant volume':
                state = np.hstack([
                    t, shockTube.thermo.P, shockTube.mass, shockTube.volume,
                    shockTube.T, shockTube.thermo.X
                ])
            else:
                state = np.hstack([
                    t, shockTube.thermo.P, shockTube.mass, shockTube.T,
                    shockTube.thermo.X
                ])

            self.timeHistory.loc[counter] = state
            if self.kineticSens == 1:
                counter_1 = 0
                for observable, reaction in itertools.product(
                        self.observables,
                        range(self.processor.solution.n_reactions)):
                    tempArray[self.observables.index(
                        observable)][reaction] = sim.sensitivity(
                            observable, reaction)
                    counter_1 += 1
                    if counter_1 % self.processor.solution.n_reactions == 0:
                        dfs[self.observables.index(observable)] = dfs[
                            self.observables.index(observable)].append(
                                ((pd.DataFrame(
                                    tempArray[self.observables.index(
                                        observable)])).transpose()),
                                ignore_index=True)
            counter += 1
        if self.timeHistories != None:
            self.timeHistories.append(self.timeHistory)

        if self.kineticSens == 1:
            numpyMatrixsksens = [
                dfs[dataframe].values for dataframe in range(len(dfs))
            ]
            self.kineticSensitivities = np.dstack(numpyMatrixsksens)
            return self.timeHistory, self.kineticSensitivities
        else:
            return self.timeHistory
Esempio n. 5
0
def JSR_steadystate2(gas,resTime,volume,kinetic_sens=0,physical_sens=0,observables=[],physical_params=['T','P'],energycon='off',pressureValveCoefficient=0.01,maxsimulationTime=1000,initial_conditions_gas=0,tempsens=0):
    # Inlet gas conditions are passed into function in the "gas" object, which is a cantera object
    # Reactor parameters passed into function as resTime and volume.  Residence time and volume of JSR
    #kinetic sens and physical sens are optional parameters which are set to zero by default.  Set them to 1 to
    #calculate sensitivity based on kinetic or physical parameters.  If these are set to 1 you must pass
    #an array of all observables to calculate sensitivities for
    
    simtype='jsr'
    reactorPressure=gas.P
    # This is the "conductance" of the pressure valve and will determine its efficiency in 
    # holding the reactor pressure to the desired conditions. It is an optional parameter
    pressureValveCoefficient=pressureValveCoefficient

    # This parameter will allow you to decide if the valve's conductance is acceptable. If there
    # is a pressure rise in the reactor beyond this tolerance, you will get a warning
    maxPressureRiseAllowed = 0.001

    # Simulation termination criterion
    #maxSimulationTime = maxsimulationTime # seconds.  An optional parameter
    fuelAirMixtureTank = ct.Reservoir(gas)
    exhaust = ct.Reservoir(gas)
    if initial_conditions_gas==0:
        stirredReactor = ct.IdealGasReactor(gas, energy=energycon, volume=volume)
        mdot=stirredReactor.mass/resTime
    else:
        stirredReactor = ct.IdealGasReactor(initial_conditions_gas,energy=energycon,volume=volume)
        dummyReactor = ct.IdealGasReactor(gas,energy=energycon,volume=volume)
        mdot=dummyReactor.mass/resTime
    
    
        
    massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank,downstream=stirredReactor,mdot=mdot)

    pressureRegulator = ct.Valve(upstream=stirredReactor,downstream=exhaust,K=pressureValveCoefficient)

    reactorNetwork = ct.ReactorNet([stirredReactor])
    
    #This block adds kinetic sensitivity parameters for all reactions if desired.  
    if kinetic_sens==1 and bool(observables):
        for i in range(gas.n_reactions):
            stirredReactor.add_sensitivity_reaction(i)
            
    if kinetic_sens==1 and bool(observables)==False:
        print('Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0')
        
    #if physical_sens==1 and bool(observables):
        #print('Placeholder')
    if physical_sens==1 and bool(observables)==False:
        print('Please supply a non-empty list of observables for sensitivity analysis or set physical_sens=0')

    # now compile a list of all variables for which we will store data
    columnNames = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)]
    columnNames = ['pressure'] + columnNames

    # use the above list to create a DataFrame
    timeHistory = pd.DataFrame(columns=columnNames)

    # Start the stopwatch
    tic = time.time()
    
    
    #Names=[]
    #for l in np.arange(stirredReactor.n_vars):
        #Names.append(stirredReactor.component_name(l))
    #global b
    
    #Names = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)]
    #state = np.hstack([stirredReactor.mass, 
                   #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X])
    #print(state)
    #b=pd.DataFrame(data=state).transpose()
    #b.columns=Names
    
    #Establish a matrix to hold sensitivities for kinetic parameters, along with tolerances
    if kinetic_sens==1 and bool(observables):
        #senscolumnNames = ['Reaction']+observables     
        senscolumnNames = observables
        #sensArray = pd.DataFrame(columns=senscolumnNames)
        senstempArray = np.zeros((gas.n_reactions,len(observables)))
        reactorNetwork.rtol_sensitivity = 1.0e-6
        reactorNetwork.atol_sensitivity = 1.0e-6
        
    dk=0.01    
        
    if physical_sens==1 and bool(observables):
        #psenscolumnNames = ['Parameter'] + observables
        #psensArray = pd.DataFrame(columns=senscolumnNames)
        pIndex=[[gas.T],physical_params,observables]
        psenstempArray = np.zeros((len(observables),len(physical_params)))
        tempSol=[]
        conditions=gas.TPX
        for i in np.arange(len(physical_params)):
            if physical_params[i]=='T':
                gas.TPX=conditions[0]+dk,conditions[1],conditions[2]
            if physical_params[i]=='P':
                gas.TPX=conditions[0],conditions[1]+dk,conditions[2]
            
            tempMixTank=ct.Reservoir(gas)
            tempExhaust = ct.Reservoir(gas)
            tempReactor=ct.IdealGasReactor(gas,energy=energycon,volume=volume)
            tempMassFlowCt=ct.MassFlowController(upstream=tempMixTank,downstream=tempReactor,mdot=tempReactor.mass/resTime)
            tempPresReg=ct.Valve(upstream=tempReactor,downstream=tempExhaust,K=pressureValveCoefficient)
            tempNetwork=ct.ReactorNet([tempReactor])
            tempNetwork.advance_to_steady_state()
            tempSol.append(tempReactor.get_state())
            gas.TPX=conditions
    reactorNetwork.rtol=1e-10
    resid=reactorNetwork.advance_to_steady_state(return_residuals=True)
    #print(resid)
    #print(reactorNetwork.rtol)
    final_pressure=stirredReactor.thermo.P
    global b
    b=reactorNetwork.sensitivities()
    if kinetic_sens==1 and bool(observables):
        for k in np.arange(len(observables)):
            for j in np.arange(gas.n_reactions):
                try:
                    senstempArray[j,k]=reactorNetwork.sensitivity(observables[k],j)
                except:
                    senstempArray[j,k]=-1
                
        #sensArray['Reaction']=gas.reaction_equations()        
        #sensArray[observables]=senstempArray.T   
        #temp = sensArray.as_matrix()
        kIndex = [[gas.T],gas.reaction_equations(),senscolumnNames]
        
        
        
    if physical_sens==1 and bool(observables):
        for k in np.arange(len(observables)):
            for j in np.arange(len(['T','P'])):
                psenstempArray[j,k]=np.log10(stirredReactor.get_state()[stirredReactor.component_index(observables[k])])-np.log10(tempSol[j][stirredReactor.component_index(observables[k])])
                psenstempArray[j,k]=np.divide(psenstempArray[j,k],np.log10(dk))
    
    #state = np.hstack([stirredReactor.thermo.P, stirredReactor.mass, 
                   #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) 
        
        # Stop the stopwatch
    toc = time.time()

    print('Simulation Took {:3.2f}s to compute'.format(toc-tic))
    columnNames = []
    #Store solution to a solution array
    #for l in np.arange(stirredReactor.n_vars):
        #columnNames.append(stirredReactor.component_name(l))
    columnNames=[stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)]
    #state=stirredReactor.get_state()
    state=np.hstack([stirredReactor.mass, 
                   stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X])
    data=pd.DataFrame(state).transpose()
    data.columns=columnNames
       
    # We now check to see if the pressure rise during the simulation, a.k.a the pressure valve
    # was okay
    pressureDifferential = timeHistory['pressure'].max()-timeHistory['pressure'].min()
    if(abs(pressureDifferential/reactorPressure) > maxPressureRiseAllowed):
        print("WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve")
        
    if kinetic_sens==1 and bool(observables) and physical_sens!=1:
        modelData=model_data(simtype,kinetic_sens=np.expand_dims(senstempArray,axis=0),Solution=data,Index=kIndex)
        modelData.add_final_pressure(final_pressure)
        modelData.add_solver_time(toc-tic)
        return modelData
    if physical_sens==1 and bool(observables) and kinetic_sens!=1:
        
        modelData=model_data(simtype,physical_sens=np.expand_dims(psenstempArray,axis=0),Solution=data,pIndex=pIndex)
        modelData.add_final_pressure(final_pressure)
        modelData.add_solver_time(toc-tic)
        return modelData
    if physical_sens==1 and bool(observables) and kinetic_sens==1:
        
        modelData=model_data(simtype,physical_sens=np.expand_dims(psenstempArray,axis=0),kinetic_sens=np.expand_dims(senstempArray,axis=0),Solution=data,Index=kIndex,pIndex=pIndex)
        modelData.add_final_pressure(final_pressure)
        modelData.add_solver_time(toc-tic)
        return modelData
    else:
        modelData=model_data(simtype,Solution=data)
        modelData.add_final_pressure(final_pressure)
        modelData.add_solver_time(toc-tic)
        return modelData
Esempio n. 6
0
def define_reactor(feed_temp,
                   feed_pressure,
                   feed_velocity,
                   feed_composition,
                   cat_area_by_rctr_vol,
                   cat_composition,
                   wall_temp,
                   wall_area_by_rctr_vol,
                   length,
                   rctr_type,
                   nodes=None):
    """
    Define Continous Stirred Tank Reactor with a catalyst reacting surface
    by adding appropriate components  to IdealGasReactor class defined in
    cantera. Due to the nature of underlying class definitions in cantera,
    CSTR is defined as a collection of reactor, catlayst surface, inlets,
    outlets, and the corresponding reservoirs. Plug Flow Reactor is defined
    as a concatenation of CSTRs.

    :param feed_temp: Temperature of the reactant feed
    :param feed_pressure: Pressure of the reactant feed
    :param feed_velocity: Velocity of the reactant feed
    :param feed_composition: composition of the reactant feed. Use gas phase
    :param cat_area_by_rctr_vol: Area of catalyst given in inverse length
    :param cat_composition: Composition of catalyst. Use surface phase
    :param wall_temp: External temperature outside outer wall of reactor
    :param wall_area_by_rctr_vol: Area of outer wall given in inverse length
    :param length: Length of the reactor along which feed flows
    :param rctr_type: Type of the reactor. Options are 'CSTR' or 'PFR'
    :param nodes: If reactor type is 'PFR', it is simulated as N CSTRS, where
                    N is given by nodes

    :return: IdealGasRector or ReactorNet with appropriate components
    """
    gas = feed_composition
    gas.TP = feed_temp, feed_pressure

    surf = cat_composition
    surf.TP = gas.TP

    if rctr_type == 'CSTR':
        rctr_len = length
    else:
        rctr_len = length / (nodes - 1)
    width = 1.0  # Arbitrary value

    surf_area = rctr_len * width
    rctr_vol = surf_area / cat_area_by_rctr_vol

    feed_mass_flow_rate = feed_velocity * gas.density * width / \
                          cat_area_by_rctr_vol
    """
    In the python code, the reactor is input to the defintions of
    reactor components.
    Despite the weird way of specifying reactor components in python,
    they get added to the reactor object in the C++ code
    """

    upstream = ct.Reservoir(gas, name='upstream')
    downstream = ct.Reservoir(gas, name='downstream')

    if rctr_type == 'CSTR':
        reactor = ct.IdealGasReactor(gas, energy='off')
        reactor.volume = rctr_vol
        rctr_surf = ct.ReactorSurface(surf, reactor, A=surf_area)
        inlet = ct.MassFlowController(upstream,
                                      reactor,
                                      mdot=feed_mass_flow_rate)
        outlet = ct.PressureController(reactor,
                                       downstream,
                                       master=inlet,
                                       K=1e-5)  #K is small number
        return reactor, upstream, gas

    elif rctr_type == 'PFR':
        reactors = []
        for i in range(nodes):
            r = ct.IdealGasReactor(gas, energy='off')
            r.volume = rctr_vol
            r_srf = ct.ReactorSurface(surf, r, A=surf_area)

            if not i:
                inlet = ct.MassFlowController(upstream,
                                              r,
                                              mdot=feed_mass_flow_rate)
            else:
                inlet = ct.MassFlowController(reactors[i - 1],
                                              r,
                                              mdot=feed_mass_flow_rate)
            outlet = ct.PressureController(r, downstream, master=inlet, K=1e-5)

            reactors.append(r)

        reactor_net = ct.ReactorNet(reactors)
        return reactor_net, upstream, gas
Esempio n. 7
0
gas = ct.Solution(os.getcwd()+'\\Mechanisms\\nheptane\\nheptane.cti')


# Define the reactor temperature and pressure
reactorTemperature = 1000 #Kelvin
reactorPressure = 101325.0 #Pascals

gas.TP = reactorTemperature, reactorPressure

# Define the fuel, oxidizer and set the stoichiometry
gas.set_equivalence_ratio(phi=1.0, fuel='NC7H16', oxidizer={'O2':1.0, 'N2':3.76})

# Create a batch reactor object and add it to a reactor network
# In this example, the batch reactor will be the only reactor
# in the network
r = ct.IdealGasReactor(contents=gas, name='Batch Reactor')
reactorNetwork = ct.ReactorNet([r])

# now compile a list of all variables for which we will store data
stateVariableNames = [r.component_name(item) for item in range(r.n_vars)]

# use the above list to create a DataFrame
timeHistory = pd.DataFrame(columns=stateVariableNames)

def ignitionDelay(df, species):
    """
    This function computes the ignition delay from the occurence of the
    peak in species' concentration.
    """
    return df[species].argmax()
Esempio n. 8
0
def compute_ignition_time_hist_Tb(dict_ZCT,
                                  dict_Yk,
                                  idx_Z,
                                  idx_C,
                                  ctml_file,
                                  pressure=ct.one_atm):
    """
    Compute the ignition time for a given Z, C and T from table
    """

    print(idx_Z, idx_C)

    T_ref = dict_ZCT['T'][idx_Z, idx_C]

    gas = get_cantera_mixture_nodes(dict_ZCT,
                                    dict_Yk,
                                    idx_Z,
                                    idx_C,
                                    ctml_file,
                                    pressure=pressure)

    r = ct.IdealGasReactor(contents=gas, name='Batch Reactor')
    reactor_network = ct.ReactorNet([r])
    reactor_network.set_max_time_step(1e-3)

    # This is a starting estimate. If you do not get an ignition within this time, increase it
    # estimatedIgnitionDelayTime = 10e-3
    # estimatedIgnitionDelayTime = 5
    estimatedIgnitionDelayTime = 1.
    t = 0

    counter = 0
    dict_data = {'t': [], 'T': [], 'OH': []}

    while (t < estimatedIgnitionDelayTime):
        t = reactor_network.step()
        if not counter % 20:
            dict_data['t'].append(t)
            dict_data['T'].append(reactor_network.get_state()[2])
            dict_data['OH'].append(reactor_network.get_state()[8])

        if counter > 10000:
            print(
                "\tmaximum number of iterations reached (%s) for idx : (%s,%s)"
                % (counter, idx_Z, idx_C))
            return np.nan, np.nan, np.nan

        counter += 1
    #
    df_T = pd.DataFrame.from_dict(dict_data)
    T_burnt = df_T['T'].max()

    try:
        # dT_dt = np.gradient(df_T['T'], df_T['t'])
        # idx = np.argmax(dT_dt)
        idx = np.argmax(df_T['OH'])
        ignition_time = df_T['t'][idx]
    except IndexError:
        # plt.plot(df_T['t'], df_T['T'])
        # plt.savefig("Figures/indexErrorMaxOH_idx_z_%s_idx_c_%s.png" % (idx_Z, idx_Z))
        # plt.close()
        ignition_time = np.nan

    if T_burnt <= 1.05 * T_ref:
        print("\tT_burnt < T_ref")
        ignition_time = np.nan

    if ignition_time > 0.9 * estimatedIgnitionDelayTime:
        # ignition_time = estimatedIgnitionDelayTime
        # plt.figure()
        # plt.plot(df_T['t'], df_T['T'])
        # plt.savefig('Figures/tau_ign_hugeTime_%s_%s.png' % (idx_Z, idx_C))
        # plt.close()
        ignition_time = np.nan

    alpha_tmp = ((T_burnt + 1e-6) - T_ref) / T_burnt

    return float(ignition_time), float(T_burnt), float(alpha_tmp)
def ShockTube(ctiFile ,pressure,temperature,conditions,initialTime,finalTime,thermalBoundary,observables=[],physical_params=[], kinetic_sens=0,physical_sens=0):
    #gas = ct.Solution('AramcoMech2.0.cti')
    # 'GRI30-1999.cti'
    gas = ct.Solution(ctiFile)
    gas.TPX = temperature, pressure*101325, conditions
    
    if thermalBoundary == 'adiabatic': 
        shockTube = ct.IdealGasReactor(gas,name = 'R1',energy= 'on')
    elif thermalBoundary == 'isothermal': 
        shockTube = ct.IdealGasReactor(gas,name = 'R1', energy= 'off')
    else:
        raise Exception('Please enter adiabatic or isothermal for the thermal boundary layer')
    
    
    sim=ct.ReactorNet([shockTube])
    

 
    if kinetic_sens==1 and bool(observables)==False:
        raise Exception('Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0')
        
        
    if physical_sens==1 and bool(observables)==False:
        raise Exception('Please supply a non-empty list of observables for sensitivity analysis or set physical_sens=0')
    
    
    if kinetic_sens==1 and bool(observables):
        [shockTube.add_sensitivity_reaction(i) for i in xrange(gas.n_reactions)]
        dfs = [pd.DataFrame() for x in xrange(len(observables))]
        tempArray = [np.zeros(gas.n_reactions) for x in xrange(len(observables))]
                
    
    if physical_sens==1 and bool(observables):
        baseConditions = gas.TPX
                      
        
    
    columnNames = [shockTube.component_name(item) for item in range(shockTube.n_vars)]               
    columnNames = ['time']+['pressure'] + columnNames
    timeHistory = pd.DataFrame(columns = columnNames)
    timeHistorytest= pd.DataFrame(columns = columnNames)
    timeHistorytest2= pd.DataFrame(columns = columnNames)
    timeHistorytest3 = pd.DataFrame(columns = columnNames)
    
   
    

    
    t=initialTime
    counter = 0
    while t < finalTime:
        
        t = sim.step() 
        
        state = np.hstack([t, shockTube.thermo.P, shockTube.mass, 
                   shockTube.volume, shockTube.T, shockTube.thermo.X])
        timeHistory.loc[counter] = state

        if kinetic_sens == 1 and bool(observables):

            newcounter = 0
            for observable,reaction in itertools.product(observables,xrange(gas.n_reactions)):

                tempArray[observables.index(observable)][reaction] = sim.sensitivity(observable, reaction)
               
                newcounter +=1
                
                #print('this is new counter' , newcounter)
                
                if(newcounter%gas.n_reactions == 0):
             
                    print(observable , newcounter)
                    dfs[observables.index(observable)] = dfs[observables.index(observable)].append(((pd.DataFrame(tempArray[observables.index(observable)])).transpose()),ignore_index=True)
                
        counter +=1

    ksensIndex = [timeHistory['time'].as_matrix(),gas.reaction_equations(),observables]
   
    
    numpyMatrixsksens = [dfs[dataframe].as_matrix() for dataframe in xrange(len(dfs))]
    S = np.dstack(numpyMatrixsksens)
    print(np.shape(S))
        
    if physical_sens == 1 and bool(observables):
        dk=.01
        originalPsens = (timeHistory[observables]).applymap(np.log10)
        
        
        
        for numOfPhyParams in xrange(len(physical_params)):
            if physical_params[numOfPhyParams] == 'T':
                gas2 = ct.Solution(ctiFile)
                gas2.TPX = baseConditions[0]+dk,baseConditions[1],baseConditions[2]
                if thermalBoundary == 'adiabatic': 
                    shockTube2 = ct.IdealGasReactor(gas2,name = 'R1',energy= 'on')
                    sim2=ct.ReactorNet([shockTube2])
                if thermalBoundary == 'isothermal':
                    shockTube2 = ct.IdealGasReactor(gas2,name = 'R1',energy= 'off')
                    sim2=ct.ReactorNet([shockTube2])
                    

                for idx, newTime in enumerate(timeHistory['time'].as_matrix()):
                    sim2.advance(newTime)
                    state = np.hstack([newTime, shockTube2.thermo.P, shockTube2.mass, 
                    shockTube2.volume, shockTube2.T, shockTube2.thermo.X])
                    timeHistorytest.loc[idx] = state
                tempT = (timeHistorytest[observables]).applymap(np.log10)
                tempT = (originalPsens.subtract(tempT))/np.log10(dk)
                print(tempT)
                tempTlst = [tempT.ix[:,idx] for idx in xrange(tempT.shape[1])]
                
                 
                
            if physical_params[numOfPhyParams] == 'P':
                gas3 = ct.Solution(ctiFile)
                gas3.TPX = baseConditions[0],baseConditions[1]+dk,baseConditions[2]
                if thermalBoundary == 'adiabatic':
                    shockTube3 = ct.IdealGasReactor(gas3,name = 'R1',energy = 'on')
                    sim3 = ct.ReactorNet([shockTube3])
                if thermalBoundary =='isothermal':
                    shockTube3 = ct.IdealGasReactor(gas3,name = 'R1', energy = 'off')
                    sim3 = ct.ReactorNet([shockTube3])
                    
                for idx2, newTime2 in enumerate(timeHistory['time'].as_matrix()):
                    sim3.advance(newTime)
                    state = np.hstack([newTime2, shockTube3.thermo.P, shockTube3.mass, 
                    shockTube3.volume, shockTube3.T, shockTube3.thermo.X])
                    timeHistorytest2.loc[idx2] = state

                tempP = (timeHistorytest2[observables]).applymap(np.log10)
                tempP = (originalPsens.subtract(tempP))/np.log10(dk)
                tempPlst = [tempP.ix[:,idx] for idx in xrange(tempP.shape[1])]
                
                
                            
            if physical_params[numOfPhyParams] == 'X':
                gas4 = ct.Solution(ctiFile)
                gas4.TPX = baseConditions[0],baseConditions[1],baseConditions[2]+dk
                if thermalBoundary =='adiabatic':
                    shockTube4 = ct.IdealGasReactor(gas4,name = 'R1',energy= 'on')
                    sim4 = ct.ReactorNet([shockTube4])
                if thermalBoundary == 'isothermal':
                    shockTube4 = ct.IdealGasReactor(gas4 ,name = 'R1', energy = 'off')
                    sim4 = ct.ReactorNet([shockTube4]) 
                

                for idx3, newTime3 in enumerate(timeHistory['time'].as_matrix()):
                    sim4.advance(newTime3)
                    state = np.hstack([newTime3, shockTube4.thermo.P, shockTube4.mass, 
                    shockTube4.volume, shockTube4.T, shockTube4.thermo.X])
                    timeHistorytest3.loc[idx3] = state      
      
                tempX = (timeHistorytest3[observables]).applymap(np.log10)
                tempX = (originalPsens.subtract(tempX))/np.log10(dk)
                tempXlst = [tempX.ix[:,idx] for idx in xrange(tempX.shape[1])]                    

    
 
        

    if "T" and "P" and "X" in physical_params:
        t = [tempTlst,tempPlst,tempXlst]
        psensIndex = [timeHistory['time'].as_matrix(),['T','P','X'],observables]
        psensdfs = [pd.concat([t[0][x],t[1][x],t[2][x]],ignore_index = True , axis = 1) for x in xrange(len(tempXlst))]
        numpyMatrixspsens = [psensdfs[dataframe].as_matrix() for dataframe in xrange(len(psensdfs))]
        pS = np.dstack(numpyMatrixspsens)
        
    elif "T" and "P" in physical_params:
        t = [tempTlst,tempPlst]
        psensIndex = [timeHistory['time'].as_matrix(),['T','P'],observables]
        psensdfs = [pd.concat([t[0][x],t[1][x]],ignore_index=True,axis = 1) for x in xrange(len(tempTlst))]                
        numpyMatrixspsens = [psensdfs[dataframe].as_matrix() for dataframe in xrange(len(psensdfs))]
        pS = np.dstack(numpyMatrixspsens)
        print(np.shape(pS))
        
        
    elif "T" and "X" in physical_params:
        t = [tempTlst,tempXlst]
        psensIndex = [timeHistory['time'].as_matrix(),['T','X'],observables]
        psensdfs = [pd.concat([t[0][x],t[1][x]], ignore_index = True, axis = 1) for x in xrange(len(tempTlst))]
        numpyMatrixspsens = [psensdfs[dataframe].as_matrix() for dataframe in xrange(len(psensdfs))]
        pS = np.dstack(numpyMatrixspsens)
        
    elif "P" and "X" in physical_params:
        t = [tempPlst,tempXlst]
        psensIndex = [timeHistory['time'].as_matrix(),['P','X'],observables]
        psensdfs = [pd.concat([t[0][x],t[1][x]], ignore_index = True, axis = 1) for x in xrange(len(tempPlst))]
        numpyMatrixspsens = [psensdfs[dataframe].as_matrix() for dataframe in xrange(len(psensdfs))]
        pS = np.dstack(numpyMatrixspsens)
        
    elif "T" in physical_params:
        t = [tempTlst]
        psensIndex = [timeHistory['time'].as_matrix(),['T'],observables]
        numpyMatrixspsens = [tempTlst[dataframe].as_matrix() for dataframe in xrange(len(tempTlst))]
        pS = np.dstack(numpyMatrixspsens)
        
    elif "P" in physical_params:
        t = [tempPlst]
        psensIndex = [timeHistory['time'].as_matrix(),['P'],observables]
        numpyMatrixspsens = [tempPlst[dataframe].as_matrix() for dataframe in xrange(len(tempPlst))]
        pS = np.dstack(numpyMatrixspsens)
        
    elif "X" in physical_params:
        t = [tempXlst]
        psensIndex = [timeHistory['time'].as_matrix(),['X'],observables]
        numpyMatrixspsens = [tempXlst[dataframe].as_matrix() for dataframe in xrange(len(tempPlst))]
        pS = np.dstack(numpyMatrixspsens)

        

                
 
    
    
                 
    
        
 
        
    if kinetic_sens==1 and bool(observables) and physical_sens==0:  
        results = shock_tube_model_data(kinetic_sens = S, Solution = timeHistory, Index = ksensIndex)
        return results 
        
    if kinetic_sens==0 and bool(physical_params) and physical_sens==1:
        results = shock_tube_model_data(physical_sens = pS, solution = timeHistory, pIndex = psensIndex)
        return results 

    if physical_sens==1 and bool(observables)and kinetic_sens==1:
        results = shock_tube_model_data(kinetic_sens = S, physical_sens = pS, Solution = timeHistory, Index = ksensIndex, pIndex = psensIndex)
        return results 
      
    if physical_sens == 0 and kinetic_sens == 0:
        results = shock_tube_model_data(Solution = timeHistory)
        return results
Esempio n. 10
0
######################################################################################################################
# Set the initial condition of the mixture
P = 1.1017E5 # pressure unit: Pa
T = 299.05 # temperature unit: K
mix_comp = 'IC8H18:1, O2:12.5, AR:75' # mixture composition. Molar ratio.

# Input the ic8 model
mixture = ct.Solution('ic8mech.xml')	# Set the state of the reactive mixture
mixture.TPX = T, P, mix_comp
air = ct.Solution('air.xml')

mixture_unr = ct.Solution('ic8mech_unr.xml')	# Set the state of the unreactive mixture
mixture_unr.TPX = T, P, mix_comp

r1 = ct.IdealGasReactor(mixture)	# Set core zone
r2 = ct.IdealGasReactor(mixture_unr)	# Set boundary layer zone
r3 = ct.IdealGasReactor(mixture_unr)	# Set crevice zone

v1 = ct.Valve(r1,r2)			# Add a valve between the main combustion chamber and the boundary layer
v2 = ct.Valve(r2,r3)			# Add a valve between the boundary layer and the crevice

Env = ct.Reservoir(air)			# Set the environment
w1 = ct.Wall(Env, r1)
w2 = ct.Wall(Env, r2)
w3 = ct.Wall(Env, r3)

######################################################################################################################
#

v1.set_valve_coeff(5E-6)  		# Set mass flow rate for valve1
Esempio n. 11
0
def Engine(fname):
    import sys
    import numpy as np
    import cantera as ct
    import matplotlib.pyplot as plt
    import csv
    import time
    # from scipy.interpolate import interp1d
    import math
    from openpyxl import load_workbook

    def volume(t):
        theta = t * omega + IVC * np.pi / 180  #theta in radian; TDC corresponds to theta=0
        v = V_min * (1 + ((r_c - 1) / 2) *
                     (L / a + 1 - np.cos(theta) -
                      np.sqrt(pow(L / a, 2) - pow(np.sin(theta), 2))))
        return v

    def surfA(t):
        area = np.pi * math.pow(D, 2) / 4
        SA = 2 * area + np.pi * D * volume(t) / area
        return SA

    def vel(t):  #Note - the only input to the passed through function is time
        theta = t * omega + IVC * np.pi / 180  #TDC corresponds to theta=0
        z = np.sqrt(pow(L / a, 2) - pow(np.sin(theta), 2))
        v = omega * (V_min / A_p) * (
            (r_c - 1) / 2) * np.sin(theta) * (1 + np.cos(theta) / z)
        return v

    def qfluxB(
            t):  #Note - the only input to the passed through function is time
        rho = m_t / volume(t)
        T = (unb.mass * unb.thermo.T + bur.mass * bur.thermo.T) / (unb.mass +
                                                                   bur.mass)
        k = k_0 * pow(T, n_k)  #W/m-K, for air
        mu = mu_0 * pow(T, n_mu)  #kg/m-s, for air
        Re = rho * MPS * D / mu
        Nu = Nu_0 * pow(Re, n_Nu)
        h = Nu * k / D
        area = surfA(t) * bur.volume / volume(t)
        q = h * area * (bur.thermo.T - T_w) * HeatOn
        return q

    def qfluxU(
            t):  #Note - the only input to the passed through function is time
        rho = m_t / volume(t)
        T = (unb.mass * unb.thermo.T + bur.mass * bur.thermo.T) / (unb.mass +
                                                                   bur.mass)
        k = k_0 * pow(T, n_k)  #W/m-K, for air
        mu = mu_0 * pow(T, n_mu)  #kg/m-s, for air
        Re = rho * MPS * D / mu
        Nu = Nu_0 * pow(Re, n_Nu)
        h = Nu * k / D
        area = surfA(t) * unb.volume / volume(t)
        q = h * area * (unb.thermo.T - T_w) / A_p * HeatOn
        return q

    def mflow(
            t):  #Note - the only input to the passed through function is time
        theta = t * omega + IVC * np.pi / 180  #TDC corresponds to theta=0
        if (theta >= theta_0r) and (theta <= theta_99r):
            # dxb=1/Deltathetar
            dxb = b * (m + 1) / Deltathetar * pow(
                ((theta - theta_0r) / Deltathetar), m) * math.exp(-b * pow(
                    ((theta - theta_0r) / Deltathetar), m + 1))
        else:
            dxb = 0
        mf = burnflag * m_t * omega * dxb  #mass flow unb to bur

        return mf

    ##  MAIN  - read inputs
    wb = load_workbook(fname)
    ws = wb.active
    r_c = ws['A1'].value  # compression ratio
    L = ws['A2'].value  # con rod length [m]
    D = ws['A3'].value  # bore [m]
    stroke = ws['A4'].value  # stroke [m]
    IVC = ws['A5'].value  # IVC crank angle where TDC = 0
    EVO = ws['A6'].value  # EVO crank angle where TDC = 0
    CrevOn = ws['A7'].value  # Flag to determine whether to use crevice model
    HeatOn = ws[
        'A8'].value  # Flag to determine whether to use heat transfer model
    crevfrac = ws['A9'].value  # crevice volume fraction of minimum volume
    RPM = ws['A10'].value  # engine speed
    b = ws['A11'].value  # Wiebe b parameter
    m = ws['A12'].value  # Wiebe m parameter
    theta_0 = ws['A13'].value  # Wiebe combustion start parameter
    Deltatheta = ws['A14'].value  # Wiebe combustion duration parameter
    eta_c = ws['A15'].value  # combustion efficiency
    T_IVC = ws['A16'].value  # IVC temperature [K]
    P_IVC = ws['A17'].value  # IVC pressure [bar]
    T_w = ws['A18'].value  # wall temperature [K]
    RON = ws['A19'].value  # fuel RON
    MON = ws['A20'].value  # fuel MON
    Phi = ws['A21'].value  # equivalence ratio
    burnflag = ws[
        'A22'].value  # fraction of mass transferred out of unburned zone
    k_0 = ws['A23'].value  # thermal conductivity [W/m-K]
    n_k = ws['A24'].value  # thermal conductivity temperature exponent
    mu_0 = ws['A25'].value  # dynamic viscosity [kg/m-s]
    n_mu = ws['A26'].value  # viscosity temperature exponent
    Nu_0 = ws['A27'].value  # Nusselt number correlation scaling constant
    n_Nu = ws[
        'A28'].value  # Nusselt number correlation Reynolds number exponent
    Chemflag = ws['A29'].value  # End gas chemistry flag; 1=on
    Y_EGR = ws['A30'].value  # EGR mass fraction
    Y_f_ref = ws['A31'].value  # mass fraction of FUEL to the reformer
    Phi_ref = ws['A32'].value  # reformer equivalence ratio
    Ref_comp = ws[
        'A33'].value  # reformer composition: 0=PCI; 1=equilibriu; 2=ideal
    mechanism = ws['A34'].value  # kinetic mechanism

    ## Engine geometry calculations
    a = stroke / 2  # crank radius
    A_p = np.pi * D * D / 4  # piston area [m^2]
    V_disp = A_p * stroke  # displacement volume [m^3]
    V_min = V_disp / (r_c - 1)  # TDC volume [m^3]
    V_max = V_min + V_disp  # BDC volume [m^3]
    MPS = 2 * stroke * RPM / 60  #mean piston speed [m/s]

    ## Engine operating conditions
    theta_0r = theta_0 * np.pi / 180  #start of combustion in radians
    Deltathetar = Deltatheta * np.pi / 180  #combustion duration in radians
    theta_99r = theta_0r + Deltathetar * math.pow(
        -math.log(0.01) / b, 1 / m)  #99% of mass burn for Wiebe
    omega = RPM * 2 * np.pi / 60  #rotation rate in radian per second based on RPM

    T_0 = T_IVC  # K
    P_0 = 1e5 * P_IVC  # Pa
    ct.suppress_thermo_warnings()
    env = ct.Solution('air.xml')
    env.TP = T_w, P_0
    gas = ct.Solution(mechanism)
    x_init = np.zeros(gas.X.shape)

    c4 = ws['C41'].value  #0.01      #n-butane
    c5 = ws['C42'].value  #0.0      #iso-pentane
    c5_2 = ws['C43'].value  #0.04   #n-pentane
    ic8 = ws['C44'].value  #0.93    #Iso-octane
    c6 = ws['C45'].value  #0.0      #added for 1-hexene
    nc7 = ws['C46'].value  #0.0    #n-heptane
    c7_2 = ws['C47'].value  #0.0    #tolune
    tmb = ws['C48'].value  #0.02     #1,2,4 Trimethyl benzene
    eth = ws['C49'].value  #0       #Ethanol

    x_init[gas.species_index('C4H10')] = c4
    x_init[gas.species_index('IC5H12')] = c5
    x_init[gas.species_index('NC5H12')] = c5_2
    x_init[gas.species_index('C6H12-1')] = c6
    x_init[gas.species_index('NC7H16')] = nc7
    x_init[gas.species_index('C6H5CH3')] = c7_2
    x_init[gas.species_index('IC8')] = ic8
    x_init[gas.species_index('T124MBZ')] = tmb
    x_init[gas.species_index('C2H5OH')] = eth
    #    x_init[gas.species_index('NO')]=150e-6  #150 ppm NO

    ## Determine global composition
    nC = (4 * c4) + (5 * c5) + (5 * c5_2) + (6 * c6) + (7 * nc7) + (
        8 * ic8) + (9 * tmb) + (2 * eth) + (7 * c7_2)
    nH = (10 * c4) + (12 * c5) + (12 * c5_2) + (12 * c6) + (16 * nc7) + (
        18 * ic8) + (12 * tmb) + (6 * eth) + (8 * c7_2)
    nO = 1 * eth

    x_init[gas.species_index('O2')] = (nC + nH / 4 - nO / 2) / Phi
    x_init[gas.species_index('N2')] = 3.76 * (nC + nH / 4 - nO / 2) / Phi
    gas.TPX = 300, 1e5, x_init
    y_global = gas.Y

    ## Determine EGR composition
    keep = np.zeros(gas.X.shape)
    keep[gas.species_index('N2')] = 1
    keep[gas.species_index('O2')] = 1
    keep[gas.species_index('CO2')] = 1
    keep[gas.species_index('CO')] = 1
    keep[gas.species_index('H2O')] = 1
    keep[gas.species_index('H2')] = 1
    gas.TPY = 300, 1e5, y_global
    gas.equilibrate('HP')  #find equilibrium concentration
    y_EGR = gas.Y * keep
    # y_EGR[7:86]=0;  #set minor species to zero; based on MECHANISM
    gas.Y = y_EGR  #use Cantera to renormalize mass fraction
    y_EGR = gas.Y

    y_eng = 1 / (1 + Y_EGR) * y_global + +Y_EGR / (1 + Y_EGR) * y_EGR

    gas.TPX = 298, 1e5, x_init
    delta_uf = gas.standard_int_energies_RT * (ct.gas_constant *
                                               298) / gas.molecular_weights

    tmax = (EVO - IVC) / 6 / RPM  # time for one revolution
    step = 8 * (EVO - IVC)
    tim = np.linspace(tmax / step, tmax, step)
    theta = IVC + omega * tim * 180 / np.pi

    ## Cantera reactor setup
    gas.TPY = T_0, P_0, y_eng  #reactants
    # gas.set_multiplier(1)   #option to make reactants inert
    unb = ct.IdealGasReactor(gas)
    if Chemflag == 0:
        unb.chemistry_enabled = False

    gas.TPY = T_0, P_0, y_eng  #products
    gas.equilibrate(
        'HP'
    )  #make the products hot so that mass will burn when it moves into products
    bur = ct.IdealGasReactor(gas)

    r3 = ct.Reservoir(env)  #outside world

    gas.TPY = T_w, P_0, y_eng
    crev = ct.IdealGasReactor(gas)  #crevices
    crev.chemistry_enabled = False  #no reaction in crevices

    piston = ct.Wall(unb, r3, velocity=vel, A=A_p, Q=qfluxU)
    flame = ct.Wall(
        unb, bur, K=0.01,
        A=A_p)  #expansion rate K set to lowest value possible to have const P
    topland = ct.Wall(
        crev, r3, U=1e3)  #heat xfer rate set to keep close to wall temperature
    head = ct.Wall(bur, r3, A=1, Q=qfluxB)

    mfc = ct.MassFlowController(unb, bur, mdot=mflow)
    V1 = ct.Valve(unb, crev)
    V1.set_valve_coeff(1e-6 * CrevOn)
    V2 = ct.Valve(crev, bur)
    V2.set_valve_coeff(1e-6 * CrevOn)

    sim = ct.ReactorNet([unb, bur, crev])
    sim.atol = 1e-12
    sim.rtol = 1e-6
    initvol = 1e-3
    unb.volume = (1 - initvol) * volume(0)
    bur.volume = initvol * volume(0)
    crev.volume = crevfrac * V_min

    m_t = unb.mass + bur.mass + crev.mass

    vol = np.zeros(step)
    T = np.zeros(step)
    vol2 = np.zeros(step)
    P = np.zeros(step)
    outdat = np.zeros((step, 13))
    SpecMatrix = np.zeros((step, gas.X.shape[0]))
    #burnflag=0.82  #flag to stop combustion if unburned mass gets too low
    #    y_old=unb.thermo.Y

    for i in range(step):  #step):
        if math.fmod(i, 32) == 0:
            print(i / step)
        sim.advance(tim[i])
        # burnflag=unb.mass/m_t
        # if unb.mass/m_t < 1-eta_c:
        #     burnflag=0
        outdat[i, 0] = IVC + tim[i] * RPM / 60 * 360
        outdat[i, 1] = bur.thermo.P / 1e5
        outdat[i, 2] = bur.thermo.T
        outdat[i, 3] = bur.mass / m_t
        outdat[i, 4] = qfluxB(tim[i])
        outdat[i, 5] = unb.thermo.T
        outdat[i, 6] = qfluxU(tim[i]) * A_p
        outdat[i, 7] = unb.mass / m_t
        outdat[i, 8] = crev.mass / m_t
        outdat[i, 9] = volume(sim.time)
        outdat[i, 10] = -(unb.kinetics.net_production_rates *
                          unb.thermo.molecular_weights
                          ).dot(delta_uf) * unb.volume * 60 / (RPM * 360)
        outdat[i, 11] = m_t
        outdat[i, 12] = ct.gas_constant / gas.mean_molecular_weight
        outdat[i, 13] = volume(tim[i])

        SpecMatrix[i] = gas.X
#        outdat[i,10]=-(unb.thermo.Y-y_old).dot(delta_uf)/(tim[2]-tim[1])*unb.mass
#        y_old=unb.thermo.Y

    return outdat, SpecMatrix
Esempio n. 12
0
def find_ignition_delay(
    solution,
    conditions=None,
    condition_type='adiabatic-constant-volume',
    output_profile=False,
    output_species=True,
    output_reactions=True,
    output_directional_reactions=False,
    output_rop_roc=False,
    temp_final=965,
    time_final=1000,
    skip_data=150,
):
    """
    This method finds the ignition delay of a cantera solution object with
    an option to return all species and reactions as a pandas.DataFrame object
    which can be stored. 
    
    The method calculates ignition delay by going until the temperature is near
    `temp_final`, and then it locates the maximum change in temperature with
    time, $\frac{\delta T}{\delta t}$. The time value corresponding with the max
    is the ignition delay
    
    This method returns a tuple with ignition delay and the species and 
    reaction data, and the rate of production and consumption (or `None`
    if not specified). 
    
    `solution` = Cantera.Solution object
    `conditions` = tuple of temperature, pressure, and mole fraction initial species
    `condition_type` = string describing the run type, currently only 'adiabatic-constant-volume' supported
    `output_profile` = should the program save simulation results and output them (True),
                        or should it just give the ignition delay (False)
    `output_species` = output a Series of species' concentrations
    `output_reactions` = output a Series of net reaction rates
    `output_directional_reactions` = output a Series of directional reaction rates
    `output_rop_roc` = output a DataFrame of species rates of consumption & production
    `temp_final` = the temperature which the ignition is reported
    `time_final` = the time to cut off the simulation if the temperature never
                    reaches `temp_final`
    `skip_data` = an integer which reduces storing each point of data.
                    storage space scales as 1/`skip_data`
    """
    if conditions is not None:
        solution.TPX = conditions
    if condition_type == 'adiabatic-constant-volume':
        reactor = ct.IdealGasReactor(solution)
        simulator = ct.ReactorNet([reactor])
        solution = reactor.kinetics
    else:
        raise NotImplementedError(
            'only adiabatic constant volume is supported')

    # setup data storage
    outputs = {}
    if output_profile:
        outputs['conditions'] = pd.DataFrame()
        if output_species:
            outputs['species'] = pd.DataFrame()
        if output_reactions:
            outputs['net_reactions'] = pd.DataFrame()
        if output_directional_reactions:
            outputs['directional_reactions'] = pd.DataFrame()
        if output_rop_roc:
            outputs['rop'] = pd.DataFrame()

    # run simulation
    max_time = time_final
    old_time = -1
    old_temp = reactor.T
    max_dTdt = 0
    max_dTdt_time = 0
    data_storage = 1e8  # large number to ensure first data point taken
    while simulator.time < time_final:
        simulator.step()
        if data_storage > skip_data:
            data_storage = 1
            if time_final == max_time and reactor.T > temp_final:
                time_final = simulator.time * 1.01  # go just beyond the final temperature
            if output_profile:
                outputs['conditions'] = outputs['conditions'].append(
                    get_conditions_series(simulator, reactor, solution),
                    ignore_index=True)
                if output_species:
                    outputs['species'] = outputs['species'].append(
                        get_species_series(solution), ignore_index=True)
                if output_reactions:
                    outputs['net_reactions'] = outputs['net_reactions'].append(
                        get_reaction_series(solution), ignore_index=True)
                if output_directional_reactions:
                    outputs['directional_reactions'] = outputs[
                        'directional_reactions'].append(
                            get_forward_and_reverse_reactions_series(solution),
                            ignore_index=True)
                if output_rop_roc:
                    outputs['rop'] = outputs['rop'].append(
                        get_rop_and_roc_series(solution), ignore_index=True)

            # find ignition delay
            dTdt = (reactor.T - old_temp) / (simulator.time - old_time)
            if dTdt > max_dTdt:
                max_dTdt = dTdt
                max_dTdt_time = simulator.time
            old_temp = reactor.T
            old_time = simulator.time
        data_storage += 1
    # set indexes as time
    if output_profile:
        time_vector = outputs['conditions']['time (s)']
        for output in outputs.values():
            output.set_index(time_vector, inplace=True)
    # save ignition_delay
    outputs['ignition_delay'] = max_dTdt_time
    return outputs
Esempio n. 13
0
def run_simulation_till_conversion(
    solution,
    species,
    conversion,
    conditions=None,
    condition_type='adiabatic-constant-volume',
    output_species=True,
    output_reactions=True,
    output_directional_reactions=False,
    output_rop_roc=False,
    skip_data=150,
    atol=1e-15,
    rtol=1e-9,
):
    """
    This method iterates through the cantera solution object and outputs information
    about the simulation as a pandas.DataFrame object.

    This method returns a dictionary with the reaction conditions data, species data,
    net reaction data, forward/reverse reaction data, and the rate of production 
    and consumption (or `None` if a variable not specified) at the specified conversion value.

    `solution` = Cantera.Solution object
    `conditions` = tuple of temperature, pressure, and mole fraction initial 
                species
    `species` = a string of the species label (or list of strings) to be used in conversion calculations
    `conversion` = a float of the fraction conversion to stop the simulation at
    `condition_type` = string describing the run type, currently supports 
                'adiabatic-constant-volume' and 'constant-temperature-and-pressure'
    `output_species` = output a Series of species' concentrations
    `output_reactions` = output a Series of net reaction rates
    `output_directional_reactions` = output a Series of directional reaction rates
    `output_rop_roc` = output a DataFrame of species rates of consumption & production
    `skip_data` = an integer which reduces storing each point of data.
                    storage space scales as 1/`skip_data`
    """
    if conditions is not None:
        solution.TPX = conditions
    if condition_type == 'adiabatic-constant-volume':
        reactor = ct.IdealGasReactor(solution)
    if condition_type == 'constant-temperature-and-pressure':
        reactor = ct.IdealGasConstPressureReactor(solution, energy='off')
    else:
        raise NotImplementedError(
            'only adiabatic constant volume is supported')
    simulator = ct.ReactorNet([reactor])
    solution = reactor.kinetics
    simulator.atol = atol
    simulator.rtol = rtol
    # setup data storage
    outputs = {}
    outputs['conditions'] = pd.DataFrame()
    if output_species:
        outputs['species'] = pd.DataFrame()
    if output_reactions:
        outputs['net_reactions'] = pd.DataFrame()
    if output_directional_reactions:
        outputs['directional_reactions'] = pd.DataFrame()
    if output_rop_roc:
        outputs['rop'] = pd.DataFrame()

    if isinstance(species, str):
        target_species_indexes = [solution.species_index(species)]
    else:  # must be a list or tuple
        target_species_indexes = [solution.species_index(s) for s in species]
    starting_concentration = sum([
        solution.concentrations[target_species_index]
        for target_species_index in target_species_indexes
    ])

    proper_conversion = False
    new_conversion = 0
    skip_count = 1e8
    while not proper_conversion:
        error_count = 0
        while error_count >= 0:
            try:
                simulator.step()
                error_count = -1
            except:
                error_count += 1
                if error_count > 10:
                    print(
                        'Might not be possible to achieve conversion at T={0}, P={1}, with concentrations of {2} obtaining a conversion of {3} at time {4} s.'
                        .format(solution.T, solution.P,
                                zip(solution.species_names, solution.X),
                                new_conversion, simulator.time))
                    raise
        new_conversion = 1 - sum([
            solution.concentrations[target_species_index]
            for target_species_index in target_species_indexes
        ]) / starting_concentration
        if new_conversion > conversion:
            proper_conversion = True

        # save data
        if skip_count > skip_data or proper_conversion:
            skip_count = 0
            outputs['conditions'] = outputs['conditions'].append(
                get_conditions_series(simulator, reactor, solution),
                ignore_index=True)
            if output_species:
                outputs['species'] = outputs['species'].append(
                    get_species_series(solution), ignore_index=True)
            if output_reactions:
                outputs['net_reactions'] = outputs['net_reactions'].append(
                    get_reaction_series(solution), ignore_index=True)
            if output_directional_reactions:
                outputs['directional_reactions'] = outputs[
                    'directional_reactions'].append(
                        get_forward_and_reverse_reactions_series(solution),
                        ignore_index=True)
            if output_rop_roc:
                outputs['rop'] = outputs['rop'].append(
                    get_rop_and_roc_series(solution), ignore_index=True)
        skip_count += 1

    # set indexes as time
    time_vector = outputs['conditions']['time (s)']
    for output in outputs.values():
        output.set_index(time_vector, inplace=True)

    return outputs
Esempio n. 14
0
def run_simulation(solution,
                   times,
                   conditions=None,
                   condition_type='adiabatic-constant-volume',
                   output_species=True,
                   output_reactions=True,
                   output_directional_reactions=False,
                   output_rop_roc=False,
                   atol=1e-15,
                   rtol=1e-9,
                   temperature_values=None):
    """
    This method iterates through the cantera solution object and outputs information
    about the simulation as a pandas.DataFrame object.
    
    This method returns a dictionary with the reaction conditions data, species data,
    net reaction data, forward/reverse reaction data, and the rate of production 
    and consumption (or `None` if a variable not specified). 
    
    `solution` = Cantera.Solution object
    `conditions` = tuple of temperature, pressure, and mole fraction initial 
                species (will be deprecated. Set parameters before running)
    `times` = an iterable of times which you would like to store information in
    `condition_type` = string describing the run type
    `output_species` = output a DataFrame of species' concentrations
    `output_reactions` = output a DataFrame of net reaction rates
    `output_directional_reactions` = output a DataFrame of directional reaction rates
    `output_rop_roc` = output a DataFrame of species rates of consumption & production

    condition_types supported
    #########################
    'adiabatic-constant-volume' - assumes no heat transfer and no volume change
    'constant-temperature-and-pressure' - no solving energy equation or changing
                            rate constants
    'constant-temperature-and-volume' - no solving energy equation but allows
                            for pressure to change with reactions
    'specified-temperature-constant-volume' - the temperature profile specified
                            `temperature_values`, which corresponds to the
                            input `times`, alters the temperature right before
                            the next time step is taken. Constant volume is assumed.
    """
    if conditions is not None:
        solution.TPX = conditions
    if condition_type == 'adiabatic-constant-volume':
        reactor = ct.IdealGasReactor(solution)
    elif condition_type == 'constant-temperature-and-pressure':
        reactor = ct.IdealGasConstPressureReactor(solution, energy='off')
    elif condition_type == 'constant-temperature-and-volume':
        reactor = ct.IdealGasReactor(solution, energy='off')
    elif condition_type == 'specified-temperature-constant-volume':
        reactor = ct.IdealGasReactor(solution, energy='off')
        if temperature_values is None:
            raise AttributeError(
                'Must specify temperature with `temperature_values` parameter')
        elif len(times) != len(temperature_values):
            raise AttributeError(
                '`times` (len {0}) and `temperature_values` (len {1}) must have the same length.'
                .format(len(times), len(temperature_values)))
    else:
        supported_types = [
            'adiabatic-constant-volume', 'constant-temperature-and-pressure',
            'constant-temperature-and-volume',
            'specified-temperature-constant-volume'
        ]
        raise NotImplementedError('only {0} are supported. {1} input'.format(
            supported_types, condition_type))
    simulator = ct.ReactorNet([reactor])
    solution = reactor.kinetics
    simulator.atol = atol
    simulator.rtol = rtol
    # setup data storage
    outputs = {}
    outputs['conditions'] = pd.DataFrame()
    if output_species:
        outputs['species'] = pd.DataFrame()
    if output_reactions:
        outputs['net_reactions'] = pd.DataFrame()
    if output_directional_reactions:
        outputs['directional_reactions'] = pd.DataFrame()
    if output_rop_roc:
        outputs['rop'] = pd.DataFrame()

    for time_index, time in enumerate(times):
        if condition_type == 'specified-temperature-constant-volume':
            solution.TD = temperature_values[time_index], solution.density
            reactor = ct.IdealGasReactor(solution, energy='off')
            simulator = ct.ReactorNet([reactor])
            solution = reactor.kinetics
            simulator.atol = atol
            simulator.rtol = rtol
            if time_index > 0:
                simulator.set_initial_time(times[time_index - 1])
        simulator.advance(time)
        # save data
        outputs['conditions'] = outputs['conditions'].append(
            get_conditions_series(simulator, reactor, solution),
            ignore_index=True)
        if output_species:
            outputs['species'] = outputs['species'].append(
                get_species_series(solution), ignore_index=True)
        if output_reactions:
            outputs['net_reactions'] = outputs['net_reactions'].append(
                get_reaction_series(solution), ignore_index=True)
        if output_directional_reactions:
            outputs['directional_reactions'] = outputs[
                'directional_reactions'].append(
                    get_forward_and_reverse_reactions_series(solution),
                    ignore_index=True)
        if output_rop_roc:
            outputs['rop'] = outputs['rop'].append(
                get_rop_and_roc_series(solution), ignore_index=True)

    # set indexes as time
    time_vector = outputs['conditions']['time (s)']
    for output in outputs.values():
        output.set_index(time_vector, inplace=True)

    return outputs
Esempio n. 15
0
#oxidizer tank
oxidizer_P = 40 * ct.one_atm
oxidizer_T = 293.0
oxidizier_X = 'O2:1.0'
gas.TPX = oxidizer_T, oxidizer_P, oxidizier_X  #oxigen
oxidizer = ct.Reservoir(gas)
oxidizer_mw = gas.mean_molecular_weight
oxidizer_k = gas.cp / gas.cv

#zrodlo zaplonu
gas.TPX = 300.0, ct.one_atm, 'H:1.0'
igniter = ct.Reservoir(gas)

#komora spalania silnika
gas.TPX = 300.0, 1.1 * ct.one_atm, 'O2:1.0'  #komora spalania jest poczatkowo wypelniona azotem
combustion_chamber = ct.IdealGasReactor(gas)
combustion_chamber.volume = 0.0005

#wylot spalin
gas.TPX = 300.0, 1 * ct.one_atm, 'N2:1.0'
exhaust = ct.Reservoir(gas)
exaustP = ct.one_atm


#defining of necessary funciotns
def kappa(gas):
    return gas.cp / gas.cv


def critical_flow(gasin, gasinP, gasinT, gasinmw, k, gasoutP, area):
    R = ct.gas_constant / gasinmw
Esempio n. 16
0
"""
Constant-pressure, adiabatic kinetics simulation with sensitivity analysis
"""

import sys
import numpy as np

import cantera as ct

gri3 = ct.Solution('gri30.xml')
temp = 1500.0
pres = ct.one_atm

gri3.TPX = temp, pres, 'CH4:0.1, O2:2, N2:7.52'
r = ct.IdealGasReactor(gri3)

air = ct.Solution('air.xml')
air.TP = temp, pres
env = ct.Reservoir(air)

# Define a wall between the reactor and the environment, and make it flexible,
# so that the pressure in the reactor is held at the environment pressure.
w = ct.Wall(r, env)
w.expansion_rate_coeff = 1.0e6  # set expansion parameter. dV/dt = KA(P_1 - P_2)
w.area = 1.0

sim = ct.ReactorNet([r])

# enable sensitivity with respect to the rates of the first 10
# reactions (reactions 0 through 9)
for i in range(10):
Esempio n. 17
0
def simulate_isobar_adiabatic(input_file,
                              initial_mole_fractions,
                              *args,
                              record_period=1,
                              rtol=None):
    """
	Constant-pressure, adiabatic kinetics simulation with Cantera: 
	Simulation of chemical kinetics in an ideally stirred, isobar and adiabatic reactor.
	It takes an Cantera input file which defines a phase, chemical species and chemical reactions and runs an isobar
	and adiabatic time dependent kinetics simulation with the defined reaction system.

	The initial concentrations of the chemical species are defined as mole fractions. The given mole fractions
	are taken as relative values and are normalized by Cantera. The absolute concentrations are calculated from
	the thermodynamic state (temperature, pressure etc.) and the thermodynamic model of the phase in the reactor. Typically an ideal gas
	is specified by the input file.
	The initial mole fractions are set by ``initial_mole_fractions`` which is a string
	in the format expected by Cantera as mole fraction initialization. The format is a comma (``,``) separated list
	of substance identifiers with mole fraction values separated by a colon (``:``).

	For example, if a simulation defines a simulation with water ``H2O``, nitrogen ``N2`` and protnated water ions
	``H3O+``, a valid concentration initalization string would be:

	.. code-block:: shell

		'H2O:2.5e+14, N2:2.54e+17, H3O+:1e+10'

	.. note::
		Species can be omitted in the initialization. Omitted species are initalized with no concentration.

	Call signatures:

	.. code-block:: python

		simulate_isobar_adiabatic(input_file, initial_mole_fractions, n_steps, dt, pressure, record_period=1, rtol=None)
		simulate_isobar_adiabatic(input_file, initial_mole_fractions, custom_steps, pressure, record_period=1, rtol=None)


	:param input_file: Path to a configuration (.cti) file
	:type input_file: path
	:param initial_mole_fractions: Inital mole fraction configuration
	:type initial_mole_fractions: str
	:param n_steps: Number of time steps to simulate
	:type n_steps: int
	:param dt: Length of a time step
	:type dt: float
	:param custom_steps: explicit time steps. The simulation will calculate the concentrations for
		the list of explicit time steps
	:type dt: list / array of floats
	:param pressure: Background pressure in the reaction vessel
	:type pressure: float
	:param record_period: The period with which calculated samples are written to the resulting trajectory
		Example: If this parameter is 10 only every 10th sample is written to the trajectory. This parameter is
		intended to keep trajectory sizes controllable for simulations with very fine grained time steps.
	:type record_period:
	:param rtol: Relative tolerance passed to cantera solver
	:type rtol: float
	:return: :class:`kineticsPy.base.trajectory.Trajectory` (a kinetic trajectory object)
	"""

    # Parse / Process arguments:

    # check if input file exists:
    if not os.path.isfile(input_file):
        raise ValueError('The given cantea file input file is not existing')

    if len(args) == 3:
        n_steps = args[0]
        custom_steps = None
        dt = args[1]
        pressure = args[2]
    elif len(args) == 2:
        custom_steps = args[0]
        n_steps = len(custom_steps)
        pressure = args[1]
    else:
        raise ValueError('Wrong number of arguments')

    sol = ct.Solution(input_file)
    air = ct.Solution('air.xml')

    sol.TPX = sol.T, pressure, initial_mole_fractions

    species_names = sol.species_names
    n_species = len(species_names)
    reac = ct.IdealGasReactor(sol)
    env = ct.Reservoir(air)

    # Define a wall between the reactor and the environment, and
    # make it flexible, so that the pressure in the reactor is held
    # at the environment pressure.
    wall = ct.Wall(reac, env)
    wall.expansion_rate_coeff = 1.0e0  # set expansion parameter. dV/dt = KA(P_1 - P_2)
    wall.area = 0.0

    # Initialize simulation.
    sim = ct.ReactorNet([reac])
    if rtol:
        sim.rtol = rtol

    n_rec_steps = int(np.ceil(n_steps / record_period))

    times = np.zeros(n_rec_steps)
    data = np.zeros((n_rec_steps, n_species))

    if custom_steps is None:
        time = 0.0
    else:
        time = custom_steps[0]
    n_recorded = 0
    for n in range(n_steps):
        sim.advance(time)

        if n % record_period == 0:
            times[n_recorded] = time  # time in s
            # .concentrations of a ThermoPhase returns concentrations in [kmol/m^3],
            # we want to use molecules / cm^3 and have to convert:
            data[n_recorded, :] = reac.thermo[
                species_names].concentrations * 6.022E20
            n_recorded += 1
        if n % 30000 == 0:
            print('%5d %10.3e %10.3f %10.3f %14.6e' %
                  (n, sim.time, reac.T, reac.thermo.P, reac.thermo.u))

        if custom_steps is None:
            time += dt
        elif n < n_steps - 1:
            time = custom_steps[n + 1]

    sim_attributes = {'pressure': pressure}
    result = Trajectory(species_names, times, data, sim_attributes)
    return result
Esempio n. 18
0
air.TP = 800, 1.4e+06 #typical temperature and pressure behind HPC
air_in=ct.Reservoir(air)
air_mdot=ct.Quantity(air, mass=20)

fuel = ct.Solution('Dagaut_Ori.cti')
fuel.TPY = 300, 3e+05, 'NC10H22:0.74,PHC3H7:0.15,CYC9H18:0.11'
fuel_in=ct.Reservoir(fuel)
fuel_mdot = ct.Quantity(fuel, mass=1)
fuel_mdot.mass=air_mdot.mass/lambda_air/14.9

#igniter (like in "combustor.py")
fuel.TPX = 1500, 2e+06, 'H:1.0'
igniter = ct.Reservoir(fuel)

fuel.TPX = 1100, 1.2e+6, 'N2:1.0' #combustion chamber already hot, otherwise some mechanims doesn't integrate properly when combustor temperature is below 1000 K
combustor = ct.IdealGasReactor(fuel, energy='on')
combustor.volume = 0.2

#exhaust reservoir
fuel.TPX = 300, ct.one_atm, 'N2:1.0'
exhaust = ct.Reservoir(fuel)

m1 = ct.MassFlowController(fuel_in, combustor, mdot=fuel_mdot.mass)
m2 = ct.MassFlowController(air_in, combustor, mdot=air_mdot.mass)

fwhm = 0.01
amplitude = 0.1
t0 = 0.2
igniter_mdot = lambda t: amplitude * math.exp(-(t-t0)**2 * 4 * math.log(2) / fwhm**2)
m3 = ct.MassFlowController(igniter, combustor, mdot=igniter_mdot)
Esempio n. 19
0
gas = ct.Solution('gri30.xml')

# Create a Reservoir for the inlet, set to a methane/air mixture at a specified
# equivalence ratio
equiv_ratio = 0.5  # lean combustion
gas.TP = 300.0, ct.one_atm
gas.set_equivalence_ratio(equiv_ratio, 'CH4:1.0', 'O2:1.0, N2:3.76')
inlet = ct.Reservoir(gas)

# Create the combustor, and fill it initially with a mixture consisting of the
# equilibrium products of the inlet mixture. This state corresponds to the state
# the reactor would reach with infinite residence time, and thus provides a good
# initial condition from which to reach a steady-state solution on the reacting
# branch.
gas.equilibrate('HP')
combustor = ct.IdealGasReactor(gas)
combustor.volume = 1.0

# Create a reservoir for the exhaust
exhaust = ct.Reservoir(gas)

# Use a variable mass flow rate to keep the residence time in the reactor
# constant (residence_time = mass / mass_flow_rate). The mass flow rate function
# can access variables defined in the calling scope, including state variables
# of the Reactor object (combustor) itself.


def mdot(t):
    return combustor.mass / residence_time

Esempio n. 20
0
    def run_single(self):
        
        gas=self.processor.solution
        reactorPressure=gas.P
        self.reactorPressure=self.processor.solution.P
        pressureValveCoefficient=self.pvalveCoefficient
        maxPressureRiseAllowed=self.maxPrise
        
        print(maxPressureRiseAllowed,self.reactorPressure,pressureValveCoefficient)
        #Build the system components for JSR
        pretic=time.time()
        
        if bool(self.observables) and self.kineticSens==1:
            ###################################################################
            #Block to create temp reactor network to pre-solve JSR without kinetic sens
            ct.suppress_thermo_warnings()
            tempgas=ct.Solution(self.processor.cti_path)
            tempgas.TPX=self.processor.solution.TPX
            tempfuelAirMixtureTank=ct.Reservoir(tempgas)
            tempexhaust=ct.Reservoir(tempgas)
            tempstirredReactor=ct.IdealGasReactor(tempgas,energy=self.energycon,
                                                  volume=self.reactor_volume)
            tempmassFlowController=ct.MassFlowController(upstream=tempfuelAirMixtureTank,
                                                         downstream=tempstirredReactor,
                                                         mdot=tempstirredReactor.mass/self.residence_time)
            tempPressureRegulator=ct.Valve(upstream=tempstirredReactor,downstream=tempexhaust,
                                           K=pressureValveCoefficient)
            
            tempreactorNetwork=ct.ReactorNet([tempstirredReactor])
            tempreactorNetwork.rtol = self.rtol
            tempreactorNetwork.atol = self.atol
            print(self.rtol,self.atol)
            tempreactorNetwork.advance_to_steady_state()
            ###################################################################
            #reactorNetwork.advance_to_steady_state()
            #reactorNetwork.reinitialize()
        
        elif self.kineticSens and bool(self.observables)==False:
            #except:
                print('Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0')        
        pretoc=time.time()
        
        print('Presolving Took {:3.2f}s to compute'.format(pretoc-pretic))        
        fuelAirMixtureTank=ct.Reservoir(self.processor.solution)
        exhaust=ct.Reservoir(self.processor.solution)
        if bool(self.observables) and self.kineticSens==1:
            stirredReactor=ct.IdealGasReactor(tempgas,energy=self.energycon,
                                          volume=self.reactor_volume)
        else:
            stirredReactor=ct.IdealGasReactor(self.processor.solution,energy=self.energycon,
                                          volume=self.reactor_volume)
        #stirredReactor=ct.IdealGasReactor(self.processor.solution,energy=self.energycon,
        #                                  volume=self.reactor_volume)    
        massFlowController=ct.MassFlowController(upstream=fuelAirMixtureTank,
                                                 downstream=stirredReactor,
                                                 mdot=stirredReactor.mass/self.residence_time)
        pressureRegulator=ct.Valve(upstream=stirredReactor,downstream=exhaust,K=pressureValveCoefficient)
        reactorNetwork=ct.ReactorNet([stirredReactor])
        if bool(self.observables) and self.kineticSens==1:
            for i in range(gas.n_reactions):
                stirredReactor.add_sensitivity_reaction(i)
        reactorNetwork.rtol_sensitivity=0.0000001
        reactorNetwork.atol_sensitivity=0.00000001
        print('Sens tols:'+str(reactorNetwork.atol_sensitivity)+', '+str(reactorNetwork.rtol_sensitivity))
        # now compile a list of all variables for which we will store data
        columnNames = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)]
        columnNames = ['pressure'] + columnNames

        # use the above list to create a DataFrame
        timeHistory = pd.DataFrame(columns=columnNames)

        # Start the stopwatch
        tic = time.time()
        reactorNetwork.rtol = self.rtol
        reactorNetwork.atol = self.atol    
        #reactorNetwork.max_err_test_fails= 10000
        #print(reactorNetwork.max_err_test_fails)
        if self.physicalSens==1 and bool(self.observables)==False:
            #except:
                print('Please supply a non-empty list of observables for sensitivity analysis or set physical_sens=0')
        
        #Establish a matrix to hold sensitivities for kinetic parameters, along with tolerances
        if self.kineticSens==1 and bool(self.observables):
            #senscolumnNames = ['Reaction']+observables     
            senscolumnNames = self.observables
            #sensArray = pd.DataFrame(columns=senscolumnNames)
            #senstempArray = np.zeros((gas.n_reactions,len(observables)))
            dfs = [pd.DataFrame() for x in range(len(self.observables))]
            #tempArray = [np.zeros(self.processor.solution.n_reactions) for x in range(len(self.observables))]
            #stirredReactor.thermo.X=tempstirredReactor.thermo.X
                
        
        posttic=time.time()
#        for steps in range(10):
#            reactorNetwork.step()
        reactorNetwork.advance_to_steady_state()
        posttoc=time.time()
        print('Main Solver Took {:3.2f}s to compute'.format(posttoc-posttic))
        final_pressure=stirredReactor.thermo.P
        sens=reactorNetwork.sensitivities()
        #print(sens[:,787])
        #print(gas.species_names)
        #print(self.observables)
        #print(sens)
        if self.kineticSens==1 and bool(self.observables):
            #print((pd.DataFrame(sens[0,:])).transpose())
            #test=pd.concat([pd.DataFrame(),pd.DataFrame(sens[0,:]).transpose()])
            #print(test)
            for k in range(len(self.observables)):
                index=gas.species_names.index(self.observables[k])
                dfs[k] = dfs[k].append(((pd.DataFrame(sens[index+3,:])).transpose()),ignore_index=True)
                #dfs[k]=pd.concat([dfs[k],pd.DataFrame(sens[k,:]).transpose()])
                #dfs[k]=pd.DataFrame(sens[k,:]).transpose()
            #print(dfs)  
        toc = time.time()
        print('Simulation Took {:3.2f}s to compute'.format(toc-tic)+' at T = '+str(stirredReactor.T))
        #print(dfs[0])
        columnNames = []
        #Store solution to a solution array
        #for l in np.arange(stirredReactor.n_vars):
            #columnNames.append(stirredReactor.component_name(l))
        columnNames=[stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)]
        #state=stirredReactor.get_state()
        state=np.hstack([stirredReactor.mass, 
                   stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X])
        data=pd.DataFrame(state).transpose()
        data.columns=columnNames
        pressureDifferential = timeHistory['pressure'].max()-timeHistory['pressure'].min()
        if(abs(pressureDifferential/self.reactorPressure) > maxPressureRiseAllowed):
            #except:
                print("WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve")
        
        if self.kineticSens==1:
            numpyMatrixsksens = [dfs[dataframe].values for dataframe in range(len(dfs))]
            self.kineticSensitivities = np.dstack(numpyMatrixsksens)
            #print(np.shape(self.kineticSensitivities))
            self.solution=data
            return (self.solution,self.kineticSensitivities)
        else:
            self.solution=data
            return (self.solution,[])
Esempio n. 21
0
def combustor():
	
    # use reaction mechanism GRI-Mech 3.0

    gas = ct.Solution('gri30.xml')

    # create a reservoir for the fuel inlet, and set to pure methane.
    fuelX = 'CH4:' + eCH4.get() + ',C2H6:' + eC2H6.get() + ',N2:' + eN2.get()
    i = 0
    while i < len(newFuelParts):
        name = newFuelParts[i].entryName.get()
        x = newFuelParts[i].entryX.get()
        if float(x) > 0:
            fuelX += ',' + name + ':' + x
        i += 1

    
    gas.TPX = float(eFuelT.get()), ct.one_atm, fuelX  
    fuel_in = ct.Reservoir(gas)
    fuel_mw = gas.mean_molecular_weight

    # use predefined function Air() for the air inlet
    air = ct.Solution('air.xml')
    air_in = ct.Reservoir(air)
    air_mw = air.mean_molecular_weight

    # to ignite the fuel/air mixture, we'll introduce a pulse of radicals. The
    # steady-state behavior is independent of how we do this, so we'll just use a
    # stream of pure atomic hydrogen.
    gas.TPX = float(eCombustorT.get()), float(eCombustorP.get()) * ct.one_atm, 'H:1.0'
    igniter = ct.Reservoir(gas)

    # create the combustor, and fill it in initially with N2
    gas.TPX = float(eCombustorT.get()), float(eCombustorP.get()) * ct.one_atm, 'N2:1.0'
    combustor = ct.IdealGasReactor(gas)
    combustor.volume = 1.0

    # create a reservoir for the exhaust
    exhaust = ct.Reservoir(gas)

    # lean combustion, phi = 0.5
    equiv_ratio = float(eRatio.get())

    # compute fuel and air mass flow rates
    factor = 0.1
    air_mdot = factor * 9.52 * air_mw
    fuel_mdot = factor * equiv_ratio * fuel_mw

    # create and install the mass flow controllers. Controllers m1 and m2 provide
    # constant mass flow rates, and m3 provides a short Gaussian pulse only to
    # ignite the mixture
    m1 = ct.MassFlowController(fuel_in, combustor, mdot=fuel_mdot)

    # note that this connects two reactors with different reaction mechanisms and
    # different numbers of species. Downstream and upstream species are matched by
    # name.
    m2 = ct.MassFlowController(air_in, combustor, mdot=air_mdot)

    # The igniter will use a Gaussian time-dependent mass flow rate.
    fwhm = 0.2
    amplitude = 0.1
    t0 = 1.0
    igniter_mdot = lambda t: amplitude * math.exp(-(t-t0)**2 * 4 * math.log(2) / fwhm**2)
    m3 = ct.MassFlowController(igniter, combustor, mdot=igniter_mdot)

    # put a valve on the exhaust line to regulate the pressure
    v = ct.Valve(combustor, exhaust, K=1.0)

    # the simulation only contains one reactor
    sim = ct.ReactorNet([combustor])

    # take single steps to 2 s, writing the results to a CSV file for later
    # plotting
    tfinal = 2.0
    tnow = 0.0
    Tprev = combustor.T
    tprev = tnow
    states = ct.SolutionArray(gas, extra=['t','tres'])

    while tnow < tfinal:
        tnow = sim.step()
        tres = combustor.mass/v.mdot(tnow)
        Tnow = combustor.T
        if abs(Tnow - Tprev) > 1.0 or tnow-tprev > 2e-2:
            tprev = tnow
            Tprev = Tnow
            states.append(gas.state, t=tnow, tres=tres)

    states.write_csv('combustor.csv', cols=('t','T','tres','X'))


    #matplotlib
    plt.figure()
    plt.plot(states.t, states.T)
    plt.xlabel('Time [s]')
    plt.ylabel('Temperature [K]')
    plt.title('Temperature')
    plt.plot()
    plt.savefig('Temperature.png')

    plt.figure()
    plt.plot(states.t, states.P)
    plt.xlabel('Time [s]')
    plt.ylabel('Pressure [Pa]')
    plt.title('Pressure')
    plt.plot()
    plt.savefig('Pressure.png')
    
   
    # ciekawe: x[3] , 5, 6, 33, 35, 36, 37?, 38, 43, 
    # ar - 48, C - 8, CO - 14, CO2 - 15
    plt.figure()
    plt.plot(states.t, states.X[:, [5]])
    plt.xlabel('Time [s]')
    plt.ylabel('Concentration H2O')
    plt.title('H2O')
    plt.plot()
    plt.savefig('XH2O.png')

    plt.figure()
    plt.plot(states.t, states.X[:, [15]])
    plt.xlabel('Time [s]')
    plt.ylabel('Concentration CO2')
    plt.title('CO2')
    plt.plot()
    plt.savefig('XCO2.png')

    plt.figure()
    plt.plot(states.t, states.X[:, [14]])
    plt.xlabel('Time [s]')
    plt.ylabel('Concentration CO')
    plt.title('CO')
    plt.plot()
    plt.savefig('XCO.png')

    plt.figure()
    plt.plot(states.t, states.X[:, [36]])
    plt.xlabel('Time [s]')
    plt.ylabel('Concentration NO2')
    plt.title('NO2')
    plt.plot()
    plt.savefig('XNO2.png')


  


    print('OK!')
Esempio n. 22
0
# Create reservoirs for the two inlet streams and for the outlet stream.  The
# upsteam reservoirs could be replaced by reactors, which might themselves be
# connected to reactors further upstream. The outlet reservoir could be
# replaced with a reactor with no outlet, if it is desired to integrate the
# composition leaving the mixer in time, or by an arbitrary network of
# downstream reactors.
res_a = ct.Reservoir(gas_a)
res_b = ct.Reservoir(gas_b)
downstream = ct.Reservoir(gas_b)

# Create a reactor for the mixer. A reactor is required instead of a
# reservoir, since the state will change with time if the inlet mass flow
# rates change or if there is chemistry occurring.
gas_b.TPX = 300.0, ct.one_atm, 'O2:0.21, N2:0.78, AR:0.01'
mixer = ct.IdealGasReactor(gas_b)

# create two mass flow controllers connecting the upstream reservoirs to the
# mixer, and set their mass flow rates to values corresponding to
# stoichiometric combustion.
mfc1 = ct.MassFlowController(res_a, mixer, mdot=rho_a * 2.5 / 0.21)
mfc2 = ct.MassFlowController(res_b, mixer, mdot=rho_b * 1.0)

# connect the mixer to the downstream reservoir with a valve.
outlet = ct.Valve(mixer, downstream, K=10.0)

sim = ct.ReactorNet([mixer])

# Since the mixer is a reactor, we need to integrate in time to reach steady
# state
sim.advance_to_steady_state()
Esempio n. 23
0
# The plug flow reactor is represented by a linear chain of zero-dimensional
# reactors. The gas at the inlet to the first one has the specified inlet
# composition, and for all others the inlet composition is fixed at the
# composition of the reactor immediately upstream. Since in a PFR model there
# is no diffusion, the upstream reactors are not affected by any downstream
# reactors, and therefore the problem may be solved by simply marching from
# the first to last reactor, integrating each one to steady state.

TDY = gas.TDY
cov = surf.coverages

print('    distance       X_CH4        X_H2        X_CO')

# create a new reactor
gas.TDY = TDY
r = ct.IdealGasReactor(gas, energy='off')
r.volume = rvol

# create a reservoir to represent the reactor immediately upstream. Note
# that the gas object is set already to the state of the upstream reactor
upstream = ct.Reservoir(gas, name='upstream')

# create a reservoir for the reactor to exhaust into. The composition of
# this reservoir is irrelevant.
downstream = ct.Reservoir(gas, name='downstream')

# use a 'Wall' object to implement the reacting surface in the reactor.
# Since walls have to be installed between two reactors/reservoirs, we'll
# install it between the upstream reservoir and the reactor.  The area is
# set to the desired catalyst area in the reactor, and surface reactions
# are included only on the side facing the reactor.
Esempio n. 24
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
Esempio n. 25
0
import os
import csv
import numpy as np

import cantera as ct

#-----------------------------------------------------------------------
# First create each gas needed, and a reactor or reservoir for each one.
#-----------------------------------------------------------------------

# create an argon gas object and set its state
ar = ct.Solution('argon.xml')
ar.TP = 1000.0, 20.0 * ct.one_atm

# create a reactor to represent the side of the cylinder filled with argon
r1 = ct.IdealGasReactor(ar)

# create a reservoir for the environment, and fill it with air.
env = ct.Reservoir(ct.Solution('air.xml'))

# use GRI-Mech 3.0 for the methane/air mixture, and set its initial state
gri3 = ct.Solution('gri30.xml')
gri3.TPX = 500.0, 0.2 * ct.one_atm, 'CH4:1.1, O2:2, N2:7.52'

# create a reactor for the methane/air side
r2 = ct.IdealGasReactor(gri3)

#-----------------------------------------------------------------------------
# Now couple the reactors by defining common walls that may move (a piston) or
# conduct heat
#-----------------------------------------------------------------------------
Esempio n. 26
0
def run_sim(i,solution_object, condition, sys_args='none', info=False,**usr_args):
    """
    Function to run Cantera reactor simulation for autoigntion conditions

    Parameters
    ----------
    i : int
        An integer value that represents which number initial condition the program is on
    solution_object : obj
        Cantera solution object
    condition
        An object contining initial conditions (temperature, pressure, mole fractions)
    info : boolean
        A boolean value that is true if additional features such as plotting or writing csv files will be used.  

    Returns
    ----------
    Output
        Plot of Temp vs Time
        Points of interest
        CSV file
        Hdf5 file
        mass_fractions.hdf5 : [initial_condition]
                                [index]
                                    [Pressure]
                                    [Reaction Rates of Progress]
                                    [Species Mass Fractions]
                                    [Species Net Production Rates Original]
                                    [Temp]
                                    [Time]

        return_obj : obj
            sim result
                .time
                .temp
                .initial_temperature_array
                .sp_data
                .test (h5py object)
                .tau
                .Temp
                .frac

    Example
    -------
        run_sim(gas_solution, points='y', plot='y', initial_sim='y')

    """
    #Set up variables
    func_start_time = tm.time()
    solution = solution_object
    initial_temperature = float(condition.temperature)
    pressure = float(condition.pressure)*float(ct.one_atm)
    
    #Set up species fractions
    frac = ''
    for reactant in condition.species.iteritems():
        if reactant[0] in solution.species_names:
            frac += str(reactant[0]) + ':' + str(reactant[1]) + ','
    frac = frac[:-1]
    
    #Set up variables
    solution.TPX = initial_temperature, pressure, frac #101325 Pa
    species = solution.species()
    reactions = solution.reactions()

    #run sim to find ignition delay from when the tempurature first reaches 400 Kelvin above its original value.
   
    #Set up variables for the simulation.
    reactor = ct.IdealGasReactor(solution) #Set up reactor
    simulation = ct.ReactorNet([reactor])
    current_time = 0.0
    stop_time = 25
    group_index = 0
    times1 = []
    temps = [] #first column is time, second is temperature
    mass = reactor.mass
    sdata = np.zeros([0, len(reactor.Y)])
    production_data = np.zeros([0, len(solution.net_production_rates)])
    state_list = list()
    
    #Prepare mass_fractions.hdf5 file.
    f1 = h5py.File('mass_fractions.hdf5', 'a')
    try:
        group_name = str(initial_temperature) + '_' + str(pressure) + '_' + str(frac)
    except ValueError:
        print "Duplicate initial conditions, or check to make sure mass fractions file isn't in directory. If it is, delete it"""
    individual = f1.create_group(group_name)
    
    #Run simulation 
    timer_start =tm.time()
    while current_time < stop_time:
        group_index += 1
        try:
            current_time = simulation.step()
        except Exception:
            error_string = 'Cantera autoignition_error @ %sK initial temperature' %initial_temperature
            print error_string
            return
        #Store information at this timestep
        times1.append(current_time)
        temps.append(reactor.T)
        species_data = reactor.Y
        grp = individual.create_group(str(group_index))
        grp['Temp'] = reactor.T
        grp['Time'] = current_time
        grp['Pressure'] = reactor.thermo.P
        species_production_rates = reactor.kinetics.net_production_rates
	try:
            net_rates_of_progress = reactor.kinetics.net_rates_of_progress
	except Exception:
	    return 0
        net_rates_of_progress = reactor.kinetics.net_rates_of_progress
        grp.create_dataset('Species Mass Fractions', data=species_data)
        grp.create_dataset('Reaction Rates of Progress', data=net_rates_of_progress)
        grp.create_dataset('Species Net Production Rates Original', data=species_production_rates)

        species_data = species_data[:, np.newaxis].T #translate from [n, 1] to [1,n]
        sdata = np.vstack((sdata, species_data))

        production_rates = np.array(solution.net_production_rates)
        production_rates = production_rates[:, np.newaxis].T
        production_data = np.vstack((production_data, production_rates))
    
    #Organize information collected from the simulation
    sample = get_range(times1, temps, sdata, production_data)
    timer_stop = tm.time()


    #strips all data except that within a 40 point sample range around ignition
    for grp in f1[group_name].keys():
        if int(grp) not in sample.index:
            f1[group_name].__delitem__(str(grp))


    #utility functions.   
    def plot(i):
        import matplotlib.pyplot as plt
        plt.clf()
        #plot combustion point
        #plt.plot(sample.derivative_max[0], sample.derivative_max[1], 'ro', ms=7, label='ignition point')
        #plot initial and final sample points
        #plt.plot(sample.initial_point[0], sample.initial_point[1], 'rx', ms=5, mew=2)
        #plt.plot(sample.final_point[0], sample.final_point[1], 'rx', ms=5, mew=2)
        #plot temp vs time
        plt.plot(sample.times_total, sample.temps_total)
        plt.xlabel('Time (s)')
        plt.title('Mixture Temperature vs Time')
        plt.ylabel('Temperature (K)')
        plt.axis([sample.times_total[0], sample.tau * 2, sample.temps_total[0] - 200, sample.temps_total[len(sample.temps_total) - 1] + 200])
        #plt.legend()
        plt.savefig("fig" + "_ic" + str(i) + ".png", bbox_inches='tight')
	plt.close()

    def writecsv(sdata, i):
        names = str(solution.species_names)
        tt = ['Time (s)', 'Temp (K)']
        names = solution.species_names
        name_array = np.append(tt, names)
        #sdata = sdata.astype('|S10')
        file_data = np.vstack((name_array, sdata))
        #open and write to file
        input_file_name_stripped = os.path.splitext(sys_args.data_file)[0]
        output_file_name = os.path.join(input_file_name_stripped + '_species_data' + 'ic_' + str(i) + '.csv')
        with open(output_file_name, 'wb') as f:
            np.savetxt(f, file_data, fmt=('%+12s'), delimiter=',')
        #os.system('atom '+ output_file_name)

    def writehdf5(sdata,i):
        
        input_file_name_stripped = os.path.splitext(sys_args.data_file)[0]
        output_file_name = os.path.join(input_file_name_stripped + '_ic_' + str(i) + '.csv')
        if not (os.path.exists("./hdf5_files")):
	    os.system("mkdir hdf5_files")
	os.system("cp mass_fractions.hdf5 mass_fractions_" + output_file_name)
        os.system("mv mass_fractions_" + output_file_name + " ./hdf5_files")
	os.system("cp production_rates.hdf5 production_rates_" + output_file_name)
        os.system("mv production_rates_" + output_file_name + " ./hdf5_files")
        #format matrix for hdf5
        #names = str(solution.species_names)
        #tt = ['Time (s)', 'Temp (K)']
        #names = solution.species_names
        #name_array = np.append(tt, names)
        #sdata = sdata.astype('|S10')
        #file_data = np.vstack((name_array, sdata))
        #open and write to file
        #input_file_name_stripped = os.path.splitext(sys_args.data_file)[0]
        #output_file_name = os.path.join(input_file_name_stripped + '_species_data.hdf5')
        #with h5py.File(output_file_name, 'w') as f:
            #Times = f.create_dataset("Times", data=sample.times_total)
            #Temps = f.create_dataset("Temps", data=sample.temps_total)
            #sgroup = f.create_group('Species_Data')
            #for i, sp in enumerate(solution.species_names):
                #sgroup.create_dataset(sp, data=sdata[i])

    def write_ai_times():
        f = open("autoignition_times.txt", "a")
        f.write(str(sample.temps_total[0]) + ", " + str(sample.tau) + "\n")
        f.close()

    def points():
        print("\nTime[s]            Temp[K]        Index        Point")
        print(str(sample.initial_point[0]) +  "       " + str("{0:.2f}".format(sample.initial_point[1]))\
         + "       " + str(sample.initial_point[2]) + "     " + "Initial sample point")
        print(str(sample.tau) + "        " + str("{0:.2f}".format(sample.derivative_max[1])) + "       " + str(sample.derivative_max[2])\
                + "     " + "Ignition point")
        print(str(sample.final_point[0]) +  "       " + str("{0:.2f}".format(sample.final_point[1]))\
         + "       " + str(sample.final_point[2]) + "     " + "Final sample point")

    #terminal use case
    if sys_args is not 'none' and info:
        if sys_args.plot:
            plot(i)
        if sys_args.writecsv:
            writecsv(sample.species_data,i)
        if sys_args.writehdf5:
            writehdf5(sample.species_data,i)
        if sys_args.points:
            points()
        if sys_args.write_ai_times:
            write_ai_times()
    
    #Create and return an object that contains criticial information about the simulation.
    class return_obj:
        def __init__(self, time, temp, sp_data, f1, tau, Temp, frac):
            self.time = time
            self.temp = temp
            self.initial_temperature_array = []
            self.sp_data = sp_data
            self.test = f1
            self.tau = tau
            self.Temp = initial_temperature
            self.frac = frac
            self.tau_array = []
    return return_obj(sample.times, sample.temps, sample.species_data, f1, sample.tau, initial_temperature, frac)
Esempio n. 27
0
injector_close = 365. / 180. * np.pi
injector_mass = 3.2e-5  # kg
injector_t_open = (injector_close - injector_open) / 2. / np.pi / f

# Simulation time and resolution
sim_n_revolutions = 8.
sim_n_timesteps = 100000.

###################################################################

# load reaction mechanism
gas = ct.Solution(reaction_mechanism)

# define initial state
gas.TPX = T_inlet, p_inlet, comp_inlet
r = ct.IdealGasReactor(gas)
# define inlet state
gas.TPX = T_inlet, p_inlet, comp_inlet
inlet = ct.Reservoir(gas)
# define injector state (gaseous!)
gas.TPX = T_injector, p_injector, comp_injector
injector = ct.Reservoir(gas)
# define outlet pressure (temperature and composition don't matter)
gas.TPX = T_ambient, p_outlet, comp_ambient
outlet = ct.Reservoir(gas)
# define ambient pressure (temperature and composition don't matter)
gas.TPX = T_ambient, p_ambient, comp_ambient
ambient_air = ct.Reservoir(gas)

# set up connecting devices
inlet_valve = ct.Valve(inlet, r)
# divide the double for loop into 32*30 parallel jobs
pres_idx = jobid
curr_pres = ig_press_cont[pres_idx]

#for n in range (0,np.size(ig_temp_cont)):# 32 jobs
#print(n)
#for o in range (0,np.size(ig_press_cont)):# 30 jobs
tau_result = np.zeros((32))
temp_result = np.zeros((32))
pres_result = np.zeros((32))
for idx in range(len(ig_temp_cont)):
    curr_temp = ig_temp_cont[idx]
    print("job_id:{} pressure:{} temperature: {}".format(
        jobid, curr_pres, curr_temp))
    gas.TPX = curr_temp, curr_pres, x_init
    r1 = ct.IdealGasReactor(gas)  #Ignition Delay reactor
    sim2 = ct.ReactorNet([r1])
    time = 0  #Initialize Time
    Temp_cur = 0  #ig_temp_cont[n]
    Press_cv = []
    Temp_cv = []
    timeapp = []

    while (Temp_cur < curr_temp + 50 and time < 20e-3):
        time += 1.e-6
        sim2.advance(time)
        time_cur = time  #milliseconds
        Press = r1.thermo.P
        Temp = r1.T
        Temp_cur = Temp
        #            times.append(time_cur)
Esempio n. 29
0
# the first to last reactor, integrating each one to steady state.
# (This approach is anologous to the one presented in 'surf_pfr.py', which
# additionally includes surface chemistry)

#%%
# import the gas model and set the initial conditions
ignition_delay=[]
for T_0 in (i):
	gas2 = ct.Solution(reaction_mechanism)
	gas2.TPX = T_0, pressure, composition_0
	mass_flow_rate2 = u_0 * gas2.density * area
	dz = length / n_steps
	r_vol = area * dz

	# create a new reactor
	r2 = ct.IdealGasReactor(gas2)
	r2.volume = r_vol

	# create a reservoir to represent the reactor immediately 	upstream. Note
	# that the gas object is set already to the state of the 	upstream reactor
	upstream = ct.Reservoir(gas2, name='upstream')

	# create a reservoir for the reactor to exhaust into. The 	composition of
	# this reservoir is irrelevant.
	downstream = ct.Reservoir(gas2, name='downstream')

	# The mass flow rate into the reactor will be fixed by using 	a
	# MassFlowController object.
	m = ct.MassFlowController(upstream, r2,mdot=mass_flow_rate2)

	# We need an outlet to the downstream reservoir. This will 	determine the
Esempio n. 30
0
def JSR_isothermal_stdst(Temps,
                         gas,
                         Pressure,
                         concentrations,
                         residenceTime,
                         reactorVolume,
                         pressureValveCoefficient=0.01,
                         maxsimulationTime=1000):
    stirredReactor_list = []
    gas_List = []
    for i in np.arange(len(Temps)):
        # Inlet gas conditions
        reactorTemperature = Temps[i]  #Kelvin
        reactorPressure = Pressure  #in atm.
        gas.TPX = reactorTemperature, reactorPressure, concentrations
        # Reactor parameters
        # Instrument parameters

        # This is the "conductance" of the pressure valve and will determine its efficiency in
        # holding the reactor pressure to the desired conditions.
        pressureValveCoefficient = pressureValveCoefficient

        # This parameter will allow you to decide if the valve's conductance is acceptable. If there
        # is a pressure rise in the reactor beyond this tolerance, you will get a warning
        maxPressureRiseAllowed = 0.001

        # Simulation termination criterion
        maxSimulationTime = maxsimulationTime  # seconds
        fuelAirMixtureTank = ct.Reservoir(gas)
        exhaust = ct.Reservoir(gas)

        stirredReactor = ct.IdealGasReactor(gas,
                                            energy='off',
                                            volume=reactorVolume)

        massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank,
                                                   downstream=stirredReactor,
                                                   mdot=stirredReactor.mass /
                                                   residenceTime)

        pressureRegulator = ct.Valve(upstream=stirredReactor,
                                     downstream=exhaust,
                                     K=pressureValveCoefficient)

        reactorNetwork = ct.ReactorNet([stirredReactor])

        # now compile a list of all variables for which we will store data
        columnNames = [
            stirredReactor.component_name(item)
            for item in range(stirredReactor.n_vars)
        ]
        columnNames = ['pressure'] + columnNames

        # use the above list to create a DataFrame
        timeHistory = pd.DataFrame(columns=columnNames)

        # Start the stopwatch
        tic = time.time()

        # Set simulation start time to zero
        t = 0
        counter = 1
        while t < maxSimulationTime:
            t = reactorNetwork.step()

            # We will store only every 10th value. Remember, we have 1200+ species, so there will be
            # 1200 columns for us to work with
            if (counter % 10 == 0):
                #Extract the state of the reactor
                state = np.hstack([
                    stirredReactor.thermo.P, stirredReactor.mass,
                    stirredReactor.volume, stirredReactor.T,
                    stirredReactor.thermo.X
                ])

                #Update the dataframe
                timeHistory.loc[t] = state

            counter += 1

        # Stop the stopwatch
        toc = time.time()

        print('Simulation Took {:3.2f}s to compute, with {} steps'.format(
            toc - tic, counter))
        #state = np.hstack([stirredReactor.thermo.P, stirredReactor.mass,
        #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X])
        #for j in np.arange(len(speciesName)):
        #component_final_X[i,j] = stirredReactor.get_state()[stirredReactor.component_index(speciesName[j])]
        #component_final_X[i,j] = state[stirredReactor.component_index(speciesName[j])+1]
        # We now check to see if the pressure rise during the simulation, a.k.a the pressure valve
        # was okay
        pressureDifferential = timeHistory['pressure'].max(
        ) - timeHistory['pressure'].min()
        if (abs(pressureDifferential / reactorPressure) >
                maxPressureRiseAllowed):
            print(
                "WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve"
            )

        stirredReactor_list.append(stirredReactor)

    #%matplotlib notebook

    #plt.style.use('ggplot')
    #plt.style.use('seaborn-pastel')

    #plt.rcParams['axes.labelsize'] = 18
    #plt.rcParams['xtick.labelsize'] = 14
    #plt.rcParams['ytick.labelsize'] = 14
    #plt.rcParams['figure.autolayout'] = True

    #plt.figure()
    #plt.semilogx(timeHistory.index, timeHistory['B2CO'],'-o')
    #plt.xlabel('Time (s)')
    #plt.ylabel(r'Mole Fraction : $X_{CO}$');
    return stirredReactor_list, gas