예제 #1
0
def assess_rxn_match(rxn1, rxn_ktp_dct2):
    """ Assess whether the reaction should be flipped. Takes a rxn_name from mech1 and searches
        through all of mech2 in search of a matching rxn. If a matching rxn is found, returns the
        matching rxn name and whether the rxn should be flipped

        Note: it is possible that a poorly constructed mechanism will have more than one instance
        of the same reaction. This function will only return the first instance of any matching
        reaction. However, it will print out a warning if duplicate matching reactions are
        found.

        :param rxn1: rxn key for which a match is being sought
        :type rxn1: tuple (rcts, prds, third_bods)
        :param rxn_ktp_dct2: rxn_ktp_dct for mech2
        :type rxn_ktp_dct2: dict {rxn1: ktp_dct1, rxn2: ...}
        :return matching_rxn: rxn key for the matching reaction
        :rtype: tuple (rcts, prds, third_bods)
        :return rev_rate: whether or not the rate should be reversed
        :rtype: Bool
    """
    # Get all possible orderings of the reactants and products for mech1
    [rcts1, prds1, third_bods1] = rxn1
    third_bod1 = third_bods1[0]
    rcts1_perm = list(itertools.permutations(rcts1, len(rcts1)))
    prds1_perm = list(itertools.permutations(prds1, len(prds1)))

    matching_rxn_name = None
    rev_rate = None
    already_found = False
    for rxn2 in rxn_ktp_dct2.keys():
        [rcts2, prds2, third_bods2] = rxn2
        third_bod2 = third_bods2[0]
        if rcts2 in rcts1_perm and prds2 in prds1_perm and third_bod1 == third_bod2:
            matching_rxn_name = rxn2
            rev_rate = False
            if already_found:
                rxn_name1 = writer_util.format_rxn_name(rxn1)
                rxn_name2 = writer_util.format_rxn_name(rxn2)
                print(
                    f'For the reaction {rxn_name1}, more than one match was found: {rxn_name2}'
                )
                print('This will cause errors!')
            else:
                already_found = True

        if rcts2 in prds1_perm and prds2 in rcts1_perm and third_bod1 == third_bod2:
            matching_rxn_name = rxn2
            rev_rate = True
            if already_found:
                rxn_name1 = writer_util.format_rxn_name(rxn1)
                rxn_name2 = writer_util.format_rxn_name(rxn2)
                print(
                    f'For the reaction {rxn_name1}, more than one match was found: {rxn_name2}'
                )
                print('This will cause errors!')
            else:
                already_found = True

    return matching_rxn_name, rev_rate
예제 #2
0
def write_mismatches(mismatched_rxns):
    """ Write reactions with mismatching rate expressions to a string

        :param mismatched_rxns: list of mismatched reactions with
            params and types of expressions
        :type: dct {rxn1: ((param_tuple1, param_tuple2, ...),
                           [type1, type2, ...], rxn2: ...}
        :return mismatch_str: description of the mismatching reactions
        :rtype: str
    """

    mismatch_str = '\nMISMATCHED REACTIONS\n\n'

    if mismatched_rxns != {}:
        mismatch_str += (
            'The following reactions have mismatched rate expressions\n')
        for rxn, (_, rxn_types) in mismatched_rxns.items():
            rxn_name = format_rxn_name(rxn)
            mismatch_str += rxn_name + ': '
            for type_idx, rxn_type in enumerate(rxn_types):
                if type_idx != 0:
                    mismatch_str += ', '
                mismatch_str += rxn_type
            mismatch_str += '\n'
        mismatch_str += '\n\n'
    else:
        mismatch_str += (
            'No reactions with mismatching rate expressions found\n\n\n')

    return mismatch_str
예제 #3
0
def write_lone_spcs(lone_spcs, threshold):
    """ Write lone species and reactions in which they participate to a string.

        :param lone_spcs: dictionary containing
            each lone species and its reactions
        :type: dct {lone_spc1: [rxn1, rxn2, ...], lone_spc2: ...}
        :param threshold: number of reactions at and below which
            a species is considered "lone"
        :type threshold: int
        :return lone_spcs_str: string with lone species and their reactions
        :rtype: str
    """
    lone_spcs_str = (
        f'\nLONE SPECIES\n\nThese species appear in {threshold} ' +
        'or less reactions\n\n'
    )
    if lone_spcs:
        max_spc_len = max(map(len, list(lone_spcs.keys())))  # longest spc name
        buffer = 5
        lone_spcs_str += (
            'Species' + ' ' * (max_spc_len - 7 + buffer) + 'Reactions\n')
        for spc, rxns in lone_spcs.items():
            lone_spcs_str += (
                '{0:<' + str(max_spc_len + buffer) + 's}').format(spc)
            for rxn_idx, rxn in enumerate(rxns):
                if rxn_idx != 0:  # don't add comma/space before first rxn
                    lone_spcs_str += ', '
                lone_spcs_str += format_rxn_name(rxn)
            lone_spcs_str += '\n'
        lone_spcs_str += '\n\n'
    else:
        lone_spcs_str += 'No lone species found\n\n\n'

    return lone_spcs_str
예제 #4
0
def plot_single_rxn(rxn, ktp_dcts, ratio_dcts, fig, axs, mech_names,
                    format_dct):
    """ Plot a single reaction's k(T,P) values from all mechanisms and the ratio values relative to
        a reference mechanism (the reference mechanism is the first mechanism in the ktp_dct that
        has rate values for that pressure).

        :param rxn: rxn tuple describing the reaction
        :type rxn: tuple (rcts, prds, third_bods)
        :param ktp_dcts: list of ktp_dcts, one for each mechanism (some may be None)
        :type ktp_dcts: list [ktp_dct_mech1, ktp_dct_mech2, ...]
        :param ratio_dcts: list of ratio_dcts, one for each mechanism (some may be None)
        :type ratio_dcts: list [ratio_dct_mech1, ratio_dct_mech1, ...]
        :param fig: pre-allocated figure object
        :type fig: MatPlotLib figure object
        :param axs:
        :type axs: list [ax1, ax2]
        :param mech_names:
        :type mech_names: list [mech_name1, mech_name2]
        :param format_dct: dct containing color and label for each pressure
        :type: dct {pressure1: (color1, label1), pressure2: ...}
        """

    for mech_idx, ktp_dct in enumerate(ktp_dcts):
        if ktp_dct is not None:
            for pressure, (temps, kts) in ktp_dct.items():
                (_color, _label) = format_dct[pressure]
                _label += ', ' + mech_names[mech_idx]

                # Plot the rate constants
                axs[0].plot(1000 / temps,
                            numpy.log10(kts),
                            label=_label,
                            color=_color,
                            linestyle=LINESTYLES[mech_idx])

                # Plot the ratios if they exist
                ratios_plotted = False
                if ratio_dcts[mech_idx] is not None:
                    # putting second "if" below prevents errors
                    if ratio_dcts[mech_idx][pressure] is not None:
                        (_, ratios) = ratio_dcts[mech_idx][pressure]
                        ratios_plotted = True
                        axs[1].plot(1000 / temps,
                                    numpy.log10(ratios),
                                    label=_label,
                                    color=_color,
                                    linestyle=LINESTYLES[mech_idx])
    # Add legend
    axs[0].legend(fontsize=12, loc='upper right')
    if ratios_plotted:
        axs[1].legend(fontsize=12, loc='upper right')

    rxn_name_formatted = writer.format_rxn_name(rxn)
    fig.suptitle(rxn_name_formatted, x=0.5, y=0.94, fontsize=20)

    return fig
예제 #5
0
    def write_dct(spc_dct, max_spc_len, buffer=7):
        """ Write either a source_spcs or sink_spcs dct to a string
        """
        new_str = 'Species' + ' ' * (max_spc_len - 7 + buffer) + 'Reactions\n'
        for spc, rxns in spc_dct.items():
            new_str += ('{0:<' + str(max_spc_len + buffer) + 's}').format(spc)
            for rxn_idx, rxn in enumerate(rxns):
                if rxn_idx != 0:  # don't add comma/space before first rxn
                    new_str += ', '
                new_str += format_rxn_name(rxn)
            new_str += '\n'
        new_str += '\n'

        return new_str
예제 #6
0
def _write_rxn_ktp_dct(rxn_ktp_dct):
    """ Write a rxn_ktp_dct in an easily readable string

    :param rxn_ktp_dct: dictionary containing k(T,P) values for reactions
    :type rxn_ktp_dct: dct {rxn1: ktp_dct1, rxn2: ...}
    :return output_str: string describing the rxn_ktp_dct
    :rtype: str
    """
    output_str = ''
    for rxn, ktp_dct in rxn_ktp_dct.items():
        output_str += format_rxn_name(rxn)
        for pressure, (temps, kts) in ktp_dct.items():
            output_str += f'\nPressure: {pressure} atm\n'
            output_str += '    Temperature (K)\n    '
            for temp in temps:
                output_str += ('{0:<12.1f}'.format(temp))
            output_str += '\n    Rate constant\n    '
            for rate in kts:
                output_str += ('{0:<12.3E}'.format(rate))
        output_str += '\n\n\n'

    return output_str
예제 #7
0
def write_duplicates(duplicate_rxns):
    """ Write reactions with more than 2 duplicate expressions to a string

        :param duplicate_rxns: duplicate reactions and number of expressions
        :type: dct {rxn1: num_of_expressions1, rxn2: ...}
        :return dups_str: description of the duplicate reactions
        :rtype: str
    """

    dups_str = (
        '\nDUPLICATE REACTIONS\n\n' +
        'These reactions have more than 2 rate expressions:\n' +
        '(Number of rate expressions given in parentheses)\n\n'
    )

    if duplicate_rxns != {}:
        for rxn, num_dups in duplicate_rxns.items():
            rxn_name = format_rxn_name(rxn)
            dups_str += f'{rxn_name}     ({num_dups})\n'
    else:
        dups_str += 'No reactions with more than 2 expressions found\n'
    dups_str += '\n\n'

    return dups_str
예제 #8
0
def reactions_block(rxn_param_dct, comments=None):
    """ Writes the reaction block of the mechanism file

        :param rxn_param_dct: dct containing the reaction parameters
        :type rxn_param_dct: dct {rxn:params}
        :return total_rxn_str: str containing the reaction block
        :rtype: str
    """

    # Get the length of the longest reaction name
    max_len = 0
    for rxn, param_dct in rxn_param_dct.items():
        rxn_name = util.format_rxn_name(rxn, param_dct)
        if len(rxn_name) > max_len:
            max_len = len(rxn_name)

    # Loop through each reaction and get the string to write to text file
    total_rxn_str = 'REACTIONS     CAL/MOLE     MOLES\n\n'
    for rxn, param_dct in rxn_param_dct.items():

        # Convert the reaction name from tuple of tuples to string
        # (Note: this includes '+M' or '(+M)' if appropriate)
        rxn_name = util.format_rxn_name(rxn, param_dct)

        if param_dct[3] is not None:  # Chebyshev
            assert param_dct[0] is not None, (
                f'For {rxn}, Chebyshev params included but highP params absent'
            )
            one_atm_params = param_dct[
                0]  # this spot is usually high-P params, but is instead 1-atm for Chebyshev
            alpha = param_dct[3]['alpha_elm']
            tmin = param_dct[3]['t_limits'][0]
            tmax = param_dct[3]['t_limits'][1]
            pmin = param_dct[3]['p_limits'][0]
            pmax = param_dct[3]['p_limits'][1]
            rxn_str = writer_reac.chebyshev(rxn_name,
                                            one_atm_params,
                                            alpha,
                                            tmin,
                                            tmax,
                                            pmin,
                                            pmax,
                                            max_length=max_len)

        elif param_dct[4] is not None:  # PLOG
            plog_dct = param_dct[4]
            rxn_str = writer_reac.plog(rxn_name, plog_dct, max_length=max_len)

        elif param_dct[2] is not None:  # Troe
            assert param_dct[0] is not None, (
                f'For {rxn}, Troe params included, highP params absent')
            assert param_dct[1] is not None, (
                f'For {rxn}, Troe, highP params included, lowP params absent')
            assert param_dct[6] is not None, (
                f'For {rxn}, Troe, highP, lowP params included, (+M) absent')

            highp_params = param_dct[0]
            lowp_params = param_dct[1]
            troe_params = param_dct[2]
            collid_factors = param_dct[5]
            rxn_str = writer_reac.troe(rxn_name,
                                       highp_params,
                                       lowp_params,
                                       troe_params,
                                       collid_factors,
                                       max_length=max_len)

        elif param_dct[1] is not None:  # Lindemann
            assert param_dct[0] is not None, (
                f'For {rxn}, lowP params included, highP params absent')
            assert param_dct[6] is not None, (
                f'For {rxn}, highP, lowP params included, (+M) absent')
            highp_params = param_dct[0]
            lowp_params = param_dct[1]
            collid_factors = param_dct[5]
            rxn_str = writer_reac.lindemann(rxn_name,
                                            highp_params,
                                            lowp_params,
                                            collid_factors,
                                            max_length=max_len)

        else:  # Simple Arrhenius
            assert param_dct[0] is not None, (
                f'For {rxn}, the highP params absent')
            highp_params = param_dct[0]
            collid_factors = param_dct[5]
            rxn_str = writer_reac.arrhenius(rxn_name,
                                            highp_params,
                                            collid_factors,
                                            max_length=max_len)

        if comments:
            # add inline comments on the first line
            if isinstance(comments[rxn], dict):
                if comments[rxn]['cmts_inline'] != '':
                    rxn_str_split = rxn_str.split('\n')
                    rxn_str_split[0] = rxn_str_split[0] + ' ' + comments[rxn][
                        'cmts_inline']
                    # rewrite rxn_str
                    rxn_str = '\n'.join(rxn_str_split)

            # check for comments: header
            if isinstance(comments[rxn], dict):
                total_rxn_str += comments[rxn]['cmts_top']

        total_rxn_str += rxn_str

    total_rxn_str += '\nEND \n'

    return total_rxn_str
예제 #9
0
def plot_single_rxn(rxn, ktp_dcts, ratio_dcts, fig, axs, mech_names,
                    format_dct):
    """ Plot a single reaction's k(T,P) values from all mechanisms and the ratio values relative to
        a reference mechanism (the reference mechanism is the first mechanism in the ktp_dct that
        has rate values for that pressure).

        :param rxn: rxn tuple describing the reaction
        :type rxn: tuple (rcts, prds, third_bods)
        :param ktp_dcts: list of ktp_dcts, one for each mechanism (some may be None)
        :type ktp_dcts: list [ktp_dct_mech1, ktp_dct_mech2, ...]
        :param ratio_dcts: list of ratio_dcts, one for each mechanism (some may be None)
        :type ratio_dcts: list [ratio_dct_mech1, ratio_dct_mech1, ...]
        :param fig: pre-allocated figure object
        :type fig: MatPlotLib figure object
        :param axs:
        :type axs: list [ax1, ax2]
        :param mech_names:
        :type mech_names: list [mech_name1, mech_name2]
        :param format_dct: dct containing color and label for each pressure
        :type: dct {pressure1: (color1, label1), pressure2: ...}
    """

    # Gr

    ratios_plotted = False
    for mech_idx, ktp_dct in enumerate(ktp_dcts):
        if ktp_dct is not None:
            for pressure, (temps, kts) in ktp_dct.items():
                (_color, _label) = format_dct[pressure]
                _label += ', ' + mech_names[mech_idx]

                # Plot the rate constants
                axs[0].plot(1000 / temps,
                            numpy.log10(kts),
                            label=_label,
                            color=_color,
                            linestyle=LINES[mech_idx])

                # Plot the ratios if they exist
                if ratio_dcts[mech_idx] is not None:
                    # if ratio_dcts[mech_idx][pressure] is not None:
                    if pressure in ratio_dcts[mech_idx].keys():
                        (_, ratios) = ratio_dcts[mech_idx][pressure]
                        ratios_plotted = True
                        axs[1].plot(1000 / temps,
                                    numpy.log10(ratios),
                                    label=_label,
                                    color=_color,
                                    linestyle=LINES[mech_idx])
            # Check for the 'max_to_high' case
            # Grab set of temps to calculate ratio
            # BELOW CODE ASSUMES ALL TEMP RANGES IN KTP DCT THE SAME
            ratio_temps = tuple(ktp_dct.values())[0][1]
            if ratio_dcts[mech_idx] is not None:
                if 'max_to_high' in ratio_dcts[mech_idx].keys():
                    (_, ratios) = ratio_dcts[mech_idx]['max_to_high']
                    ratios_plotted = True
                    _color = 'k'
                    _label = 'max to P-indep, ' + mech_names[mech_idx]
                    axs[1].plot(1000 / ratio_temps,
                                numpy.log10(ratios),
                                label=_label,
                                color=_color,
                                linestyle=LINES[mech_idx])

    # Do some formatting
    axs[0].legend(fontsize=12, loc='upper right')
    if ratios_plotted:
        axs[1].legend(fontsize=12, loc='upper right')
    rxn_name_formatted = writer.format_rxn_name(rxn)
    fig.suptitle(rxn_name_formatted, x=0.5, y=0.94, fontsize=20)

    return fig