示例#1
0
文件: muq.py 项目: connie/RMG-Py
 def __init__(self, cantera, outputSpeciesList, kParams, kUncertainty, gParams, gUncertainty, correlated=False):
     
     
     self.reactorMod = ReactorModPiece(cantera=cantera,
                         outputSpeciesList=outputSpeciesList,
                         kParams=kParams,
                         kUncertainty = kUncertainty,
                         gParams = gParams,
                         gUncertainty = gUncertainty,
                         correlated = correlated,
                         )
     
     
     # Define the polynomials and quadrature rules in each dimension using a VariableCollection object. 
     # We can do this directly using the classes from libmuqUtilities. Build the PCE factory for the ReactorModPiece this way.
     # Uniform random variables used chemical kinetics uncertainty propagation uses Legendre polynomials
     # We select the Gauss-Patterson quadrature as it is recommended as the fastest in the Patrick Conrad, Youssef Marzouk paper
     
     # Select the polynomial and quadrature families 
     polyFamily = LegendrePolynomials1DRecursive()
     quadFamily = GaussPattersonQuadrature1D()
     
     # Create a random variable collection for each of the uncertain variables
     varCollection = VariableCollection()
     for i, rxnIndex in enumerate(kParams):
         varCollection.PushVariable("k{0}".format(i+1), polyFamily, quadFamily)
     for i, speciesIndex in enumerate(gParams):
         varCollection.PushVariable("G{0}".format(i+1), polyFamily, quadFamily)
     
     # Initialize the PCE Factory
     self.factory = SmolyakPCEFactory(varCollection, self.reactorMod) 
     
     self.pce = None
示例#2
0
文件: muq.py 项目: mbprend/RMG-Py
class ReactorPCEFactory(object):
    """
    This class uses MUQ to generate adaptive Polynomial Chaos Expansions for global uncertainty analysis in chemical reaction systems. 
    It uses RMG, Cantera, and MUQ dependencies.
    
    Methodology
        1. Set up reactor conditions and load chemical kinetic mechanism. Select desired outputs
        2. Run local uncertainty analysis
        3. Extract top N most uncertain parameters
        4. Input these set of parameters into a MUQ model class (which is a child of the ModPiece class)
        5. Create EvaluateImpl function within model class that runs simulation based on reactor conditions through Cantera
        6. Perform PCE analysis of desired outputs
    """
    def __init__(self,
                 cantera,
                 outputSpeciesList,
                 kParams,
                 kUncertainty,
                 gParams,
                 gUncertainty,
                 correlated=False,
                 logx=True):

        self.reactorMod = ReactorModPiece(
            cantera=cantera,
            outputSpeciesList=outputSpeciesList,
            kParams=kParams,
            kUncertainty=kUncertainty,
            gParams=gParams,
            gUncertainty=gUncertainty,
            correlated=correlated,
            logx=logx,
        )

        # Define the polynomials and quadrature rules in each dimension using a VariableCollection object.
        # We can do this directly using the classes from libmuqUtilities. Build the PCE factory for the ReactorModPiece this way.
        # Uniform random variables used chemical kinetics uncertainty propagation uses Legendre polynomials
        # We select the Gauss-Patterson quadrature as it is recommended as the fastest in the Patrick Conrad, Youssef Marzouk paper

        # Select the polynomial and quadrature families
        polyFamily = LegendrePolynomials1DRecursive()
        quadFamily = GaussPattersonQuadrature1D()

        # Create a random variable collection for each of the uncertain variables
        varCollection = VariableCollection()
        for i, rxnIndex in enumerate(kParams):
            varCollection.PushVariable("k{0}".format(i + 1), polyFamily,
                                       quadFamily)
        for i, speciesIndex in enumerate(gParams):
            varCollection.PushVariable("G{0}".format(i + 1), polyFamily,
                                       quadFamily)

        # Initialize the PCE Factory
        self.factory = SmolyakPCEFactory(varCollection, self.reactorMod)

        self.pce = None
        self.logx = logx

    def generatePCE(self,
                    runTime=None,
                    startOrder=2,
                    tolerance=None,
                    fixedTerms=False):
        """
        Generate the PCEs adaptively. There are three methods for doing so. 
        `runTime` should be given in seconds
        Option 1: Adaptive for a pre-specified amount of time
        Option 2: Adaptively construct PCE to error tolerance
        Option 3: Used a fixed order, and (optionally) adapt later.  
        """

        # Also monitor the amount of time it takes
        start_time = time()
        if runTime:
            # Option 1: Adaptive for a pre-specified amount of time
            self.pce = self.factory.StartAdaptiveTimed(startOrder, runTime)
        elif tolerance:
            # Option 2: adaptively construct PCE to error tolerance
            self.pce = self.factory.StartAdaptiveToTolerance(
                startOrder, tolerance)
        elif fixedTerms:
            # Option 3: Used a fixed order, and (optionally) adapt later
            self.pce = self.factory.StartFixedTerms(startOrder)
            # # Optionally adapt to tolerance later:
            # pce = self.AdaptToTolerance(tolerance)
        else:
            raise Exception('Must have at least one chosen method')

        end_time = time()
        time_taken = end_time - start_time
        logging.info(
            'Polynomial Chaos Expansion construction took {0:2f} seconds.'.
            format(time_taken))

    def compareOutput(self, testPoint, log=True):
        """
        Evaluate the PCEs against what the real output might give for a test point.
        testPoint is an array of all the values in terms of factor of f
        
        Returns a tuple containing the 
        (true output mole fractions, pce output mole fractions) evaluated at the test point.
        """

        trueOutput = self.reactorMod.Evaluate([testPoint])
        pceOutput = self.pce.Evaluate(testPoint)

        reactorMod = self.reactorMod

        output = ''
        for i in range(reactorMod.numConditions):
            output += """============================================================
Condition {0}
------------------------------------------------------------
{1!s}
============================================================
Condition {0} {2}Mole Fractions Evaluated at Test Point
------------------------------------------------------------
Species                      True Output          PCE Output
------------------------------------------------------------
""".format(i + 1, reactorMod.cantera.conditions[i],
            'Log ' if self.logx else '')

            for j, outputSpecies in enumerate(reactorMod.outputSpeciesList):
                outputIndex = i * reactorMod.numOutputSpecies + j
                output += '{0:<20}{1:>20.3f}{2:>20.3f}\n'.format(
                    outputSpecies.to_chemkin(), trueOutput[outputIndex],
                    pceOutput[outputIndex])
            output += '============================================================\n'

        if log:
            logging.info(output)
        else:
            print(output)

        return trueOutput, pceOutput

    def analyzeResults(self, log=True):
        """
        Obtain the results: the prediction mean and variance, as well as the global sensitivity indices
        Returns a tuple containing the following statistics
        
        (mean species mole fractions, variance, covariance, main sensitivity indices, total sensitivity indices)
        """
        reactorMod = self.reactorMod
        pce = self.pce
        # Compute the mean and variance for each of the uncertain parameters
        mean = np.array(pce.ComputeMean())

        var = np.array(pce.ComputeVariance())
        stddev = np.sqrt(var)
        stddev_percent = stddev / mean * 100.0

        cov = pce.ComputeCovariance()

        # Extract the global sensitivity indices
        mainSens = np.array(pce.ComputeAllMainSensitivityIndices())
        totalSens = np.array(pce.ComputeAllSobolTotalSensitivityIndices())

        output = ''
        for i in range(reactorMod.numConditions):
            output += """============================================================
Condition {0}
------------------------------------------------------------
{1!s}
============================================================
Condition {0} {2}Mole Fractions
------------------------------------------------------------
Species                   Mean         Stddev     Stddev (%)
------------------------------------------------------------
""".format(i + 1, reactorMod.cantera.conditions[i],
            'Log ' if self.logx else '')

            for j, outputSpecies in enumerate(reactorMod.outputSpeciesList):
                outputIndex = i * reactorMod.numOutputSpecies + j
                output += '{0:<15}{1:>15.3e}{2:>15.3e}{3:>15.3f}\n'.format(
                    outputSpecies.to_chemkin(), mean[outputIndex],
                    stddev[outputIndex], stddev_percent[outputIndex])
            output += '============================================================\n\n'

            if reactorMod.kParams:
                output += """====================================================================================================
Condition {0} Reaction Rate Sensitivity Indices
----------------------------------------------------------------------------------------------------
Description                                                                 sens_main     sens_total
""".format(i + 1)

                for j, outputSpecies in enumerate(
                        reactorMod.outputSpeciesList):
                    output += '----------------------------------------------------------------------------------------------------\n'
                    outputIndex = i * reactorMod.numOutputSpecies + j
                    for k, descriptor in enumerate(reactorMod.kParams):
                        parameterIndex = k
                        if not reactorMod.correlated:
                            description = 'dln[{0}]/dln[{1}]'.format(
                                outputSpecies.to_chemkin(),
                                reactorMod.cantera.reactionList[descriptor].
                                to_chemkin(kinetics=False))
                        else:
                            description = 'dln[{0}]/dln[{1}]'.format(
                                outputSpecies.to_chemkin(), descriptor)

                        output += '{0:<70}{1:>14.3f}%{2:>14.3f}%\n'.format(
                            description,
                            100 * mainSens[outputIndex][parameterIndex],
                            100 * totalSens[outputIndex][parameterIndex])
                output += '====================================================================================================\n\n'

            if reactorMod.gParams:
                output += """====================================================================================================
Condition {0} Thermochemistry Sensitivity Indices
----------------------------------------------------------------------------------------------------
Description                                                                 sens_main     sens_total
""".format(i + 1)
                for j, outputSpecies in enumerate(
                        reactorMod.outputSpeciesList):
                    output += '----------------------------------------------------------------------------------------------------\n'
                    outputIndex = i * reactorMod.numOutputSpecies + j
                    for g, descriptor in enumerate(reactorMod.gParams):
                        parameterIndex = len(reactorMod.kParams) + g
                        if not reactorMod.correlated:
                            description = 'dln[{0}]/dG[{1}]'.format(
                                outputSpecies.to_chemkin(), reactorMod.cantera.
                                speciesList[descriptor].to_chemkin())
                        else:
                            description = 'dln[{0}]/dG[{1}]'.format(
                                outputSpecies.to_chemkin(), descriptor)

                        output += '{0:<70}{1:>14.3f}%{2:>14.3f}%\n'.format(
                            description,
                            100 * mainSens[outputIndex][parameterIndex],
                            100 * totalSens[outputIndex][parameterIndex])
                output += '====================================================================================================\n\n'

        if log:
            logging.info(output)
        else:
            print(output)

        return mean, var, cov, mainSens, totalSens