コード例 #1
0
def test_reaction_block():
    """ Tests the reaction_block function
    """
    reaction_block_good = mechanism.reaction_block(REACTION_BLOCK_GOOD)
    reaction_block_bad = mechanism.reaction_block(REACTION_BLOCK_BAD)
    assert reaction_block_good == '\nCH4+H=CH3+H2\n'
    assert reaction_block_bad is None
コード例 #2
0
ファイル: combine.py プロジェクト: sjklipp/interfaces
def build_reaction_name_dcts(mech1_str,
                             mech2_str,
                             t_ref,
                             temps,
                             pressures,
                             ignore_reverse=True,
                             remove_bad_fits=False):
    """ Parses the strings of two mechanism files and calculates
        rate constants [k(T,P)]s at an input set of temperatures and pressures.

        :param mech1_str: string of mechanism 1 input file
        :type mech1_str: str
        :param mech2_str: string of mechanism 2 input file
        :type mech2_str: str
        :param t_ref: Reference temperature (K)
        :type t_ref: float
        :param temps: List of Temperatures (K)
        :type temps: numpy.ndarray
        :return mech1_ktp_dct: rate constants for mechanism 1
        :rtype: dict[pressure: rates]
        :return mech2_ktp_dct: rate constants for mechanism 2
        :rtype: dict[pressure: rates]
    """

    mech1_reaction_block = remove_whitespace(
        mech_parser.reaction_block(mech1_str))
    mech1_units = reaction_units(mech1_str)
    mech1_ktp_dct = rates.mechanism(mech1_reaction_block,
                                    mech1_units,
                                    t_ref,
                                    temps,
                                    pressures,
                                    ignore_reverse=ignore_reverse,
                                    remove_bad_fits=remove_bad_fits)

    if mech2_str:
        mech2_reaction_block = remove_whitespace(
            mech_parser.reaction_block(mech2_str))
        mech2_units = reaction_units(mech2_str)
        mech2_ktp_dct = rates.mechanism(mech2_reaction_block,
                                        mech2_units,
                                        t_ref,
                                        temps,
                                        pressures,
                                        ignore_reverse=ignore_reverse,
                                        remove_bad_fits=remove_bad_fits)
    else:
        mech2_ktp_dct = {}

    return mech1_ktp_dct, mech2_ktp_dct
コード例 #3
0
def load_rxn_ktp_dcts_chemkin(mech_filenames, direc, temps, pressures):
    """ Read one or more Chemkin-formatted mechanisms files and calculate rates at the indicated
        pressures and temperatures. Return a list of rxn_ktp_dcts.

        :param mech_filenames: filenames containing Chemkin-formatted kinetics information
        :type mech_filenames: list [filename1, filename2, ...]
        :param direc: directory with file(s) (all files must be in the same directory)
        :type direc: str
        :param temps: temperatures at which to do calculations (Kelvin)
        :type temps: list [float]
        :param pressures: pressures at which to do calculations (atm)
        :type pressures: list [float]
        :return rxn_ktp_dcts: list of rxn_ktp_dcts
        :rtype: list of dcts [rxn_ktp_dct1, rxn_ktp_dct2, ...]
    """
    rxn_ktp_dcts = []
    for mech_filename in mech_filenames:
        mech_str = parser.read_file(direc, mech_filename)
        ea_units, a_units = parser_mech.reaction_units(mech_str)
        rxn_block_str = parser_mech.reaction_block(mech_str)
        rxn_param_dct = parser_rxn.param_dct(rxn_block_str, ea_units, a_units)
        rxn_ktp_dct = calc_rates.eval_rxn_param_dct(rxn_param_dct, pressures,
                                                    temps)
        rxn_ktp_dcts.append(rxn_ktp_dct)

    return rxn_ktp_dcts
コード例 #4
0
def parse_pes_dct(mech_str):
    """ Parses a PES dct

        :param mech_str: raw string from reading a Chemkin file
        :type mech_str: str
        :return rxn_param_dct: rxn_param_dct object
        :rtype: dct {rxn1: param_tuple1, rxn2: ...}
    """

    rxn_block_str = parser_mech.reaction_block(mech_str)
    pes_dct = parser_rxn.get_pes_dct(rxn_block_str)

    return pes_dct
コード例 #5
0
def parse_rxn_param_dct(mech_str):
    """ Parses a raw Chemkin mechanism string and yields a rxn_param_dct

        :param mech_str: raw string from reading a Chemkin file
        :type mech_str: str
        :return rxn_param_dct: rxn_param_dct object
        :rtype: dct {rxn1: param_tuple1, rxn2: ...}
    """

    ea_units, a_units = parser_mech.reaction_units(mech_str)
    rxn_block_str = parser_mech.reaction_block(mech_str)
    rxn_param_dct = parser_rxn.get_rxn_param_dct(rxn_block_str, ea_units,
                                                 a_units)

    return rxn_param_dct
コード例 #6
0
def align_mechs(mech_filenames, spc_csv_filenames, thermo_filenames, temps, pressures):
    """ Calculates kTP dictionaries for any number of mechanisms and then
        flips the reactions within the mechanisms to all be in the same direction


    """
    assert len(mech_filenames) == len(spc_csv_filenames) == len(thermo_filenames), (
        f"""The lengths of the mechanism, spc_csv, and thermo filename inputs should 
            all be the same, but are instead {len(mech_filenames)}, {len(spc_csv_filenames)}, 
            and {len(thermo_filenames)}, respectively."""
        )
    num_mechs = len(mech_filenames)
    
    # Load rxn_ktp_dcts, spc_dcts, and thermo_dcts
    rxn_ktp_dcts = []
    rxn_param_dcts = []
    spc_dcts = []
    thermo_dcts = []
    for idx in range(num_mechs):
        # Get the rxn_ktp_dct and the rxn_param_dct
        mech_str = parser.ptt.read_inp_str(JOB_PATH,mech_filenames[idx],remove_comments=False)
        rxn_block_str = parser_mech.reaction_block(mech_str)
        rxn_param_dct = parser_rxn.param_dct(rxn_block_str)
        rxn_ktp_dct = calc_rates.eval_rxn_param_dct(rxn_param_dct, pressures, temps)
        rxn_ktp_dcts.append(rxn_ktp_dct)
        rxn_param_dcts.append(rxn_param_dct)

        # Get the spc_dct
        spc_csv_str = parser.ptt.read_inp_str(JOB_PATH, spc_csv_filenames[idx], remove_comments=False)
        spc_dct = parser_spc.build_spc_dct(spc_csv_str, 'csv')
        spc_dcts.append(spc_dct)

        # Get the thermo_dct
        thermo_str = parser.ptt.read_inp_str(JOB_PATH,mech_filenames[idx],remove_comments=False)
        thermo_block_str = parser_mech.thermo_block(thermo_str)
        thermo_dct = calc_thermo.mechanism(thermo_block_str, temps)
        thermo_dcts.append(thermo_dct)

    # Get the renamed dictionaries
    renamed_rxn_ktp_dcts = []
    renamed_rxn_param_dcts = []
    renamed_spc_dcts = []
    renamed_thermo_dcts = []
    for mech_idx in range(num_mechs-1):
        # If on first mech, don't rename, just copy
        if mech_idx == 0:
            renamed_rxn_ktp_dcts.append(rxn_ktp_dcts[mech_idx])
            renamed_rxn_param_dcts.append(rxn_param_dcts[mech_idx])
            renamed_spc_dcts.append(spc_dcts[mech_idx])            
            renamed_thermo_dcts.append(thermo_dcts[mech_idx])
        # Loop through the mechs remaining after the current one
        for idx2 in range(mech_idx+1, num_mechs):
            # If on the first time through, do things a bit differently
            if mech_idx == 0:
                # Get the instructions on renaming species, aka the rename_spc_dct
                _, rename_spc_dct = calc_combine.combine_species(spc_dcts[mech_idx], spc_dcts[idx2])

                # Rename the species in the various dictionaries
                renamed_rxn_ktp_dct = calc_combine.rename_species(rxn_ktp_dcts[idx2], rename_spc_dct, target_type='rxn')  
                renamed_rxn_param_dct = calc_combine.rename_species(rxn_param_dcts[idx2], rename_spc_dct, target_type='rxn')  
                renamed_spc_dct = calc_combine.rename_species(spc_dcts[idx2], rename_spc_dct, target_type='spc')  
                renamed_thermo_dct = calc_combine.rename_species(thermo_dcts[idx2], rename_spc_dct, target_type='thermo')
                
                # Store the results
                renamed_rxn_ktp_dcts.append(renamed_rxn_ktp_dct)
                renamed_rxn_param_dcts.append(renamed_rxn_param_dct)
                renamed_spc_dcts.append(renamed_spc_dct)
                renamed_thermo_dcts.append(renamed_thermo_dct)

            else:
                # Get the instructions on renaming species, aka the rename_spc_dct
                _, rename_spc_dct = calc_combine.combine_species(renamed_spc_dcts[mech_idx], renamed_spc_dcts[idx2])

                # Rename the species in the various dictionaries
                renamed_rxn_ktp_dct = calc_combine.rename_species(renamed_rxn_ktp_dcts[idx2], rename_spc_dct, target_type='rxn')  
                renamed_rxn_param_dct = calc_combine.rename_species(renamed_rxn_param_dcts[idx2], rename_spc_dct, target_type='rxn')  
                renamed_spc_dct = calc_combine.rename_species(renamed_spc_dcts[idx2], rename_spc_dct, target_type='spc')  
                renamed_thermo_dct = calc_combine.rename_species(renamed_thermo_dcts[idx2], rename_spc_dct, target_type='thermo')

                # Store the results                
                renamed_rxn_ktp_dcts[idx2] = renamed_rxn_ktp_dct
                renamed_rxn_param_dcts[idx2] = renamed_rxn_param_dct
                renamed_spc_dcts[idx2] = renamed_spc_dct
                renamed_thermo_dcts[idx2] = renamed_thermo_dct
    
    # Get the em_param_dcts                    
    renamed_em_param_dcts = []
    for dct in renamed_rxn_param_dcts:
        renamed_em_param_dct = get_em_param_dct(dct)
        renamed_em_param_dcts.append(renamed_em_param_dct)

    # Loop over each reaction and reverse if necessary
    aligned_rxn_ktp_dcts = []
    aligned_em_param_dcts = []
    for mech_idx in range(num_mechs-1):
        if mech_idx == 0:
            aligned_rxn_ktp_dcts.append(renamed_rxn_ktp_dcts[mech_idx])
            aligned_em_param_dcts.append(renamed_em_param_dcts[mech_idx])
        for idx2 in range(mech_idx+1, num_mechs):    
            if mech_idx == 0:
                aligned_rxn_ktp_dct = reverse_ktp_dct(renamed_rxn_ktp_dcts[mech_idx], renamed_rxn_ktp_dcts[idx2],
                    renamed_rxn_param_dcts[mech_idx], renamed_rxn_param_dcts[idx2], renamed_thermo_dcts[mech_idx], temps)
                    
                aligned_rxn_ktp_dcts.append(aligned_rxn_ktp_dct)

            else:
                aligned_rxn_ktp_dct = reverse_ktp_dct(renamed_rxn_ktp_dcts[mech_idx], renamed_rxn_ktp_dcts[idx2],
                    renamed_rxn_param_dcts[mech_idx], renamed_rxn_param_dcts[idx2], renamed_thermo_dcts[mech_idx], temps)
                # Note: the preceding line does not necessarily use the thermo of the first mechanism; it only is sure
                # to use the thermo of the mechanism currently being used as the reference

                aligned_rxn_ktp_dcts[idx2] = aligned_rxn_ktp_dct

    # Sort the aligned dictionaries into a single output
    # Loop over each mechanism
    combined_rxn_ktp_dct = {}
    for mech_idx, dct in enumerate(aligned_rxn_ktp_dcts):

        # Loop over each rxn in the mechanism
        for rxn, ktp_dct in dct.items():

            # If the reaction does not yet exist, add it
            if rxn not in combined_rxn_ktp_dct.keys():
                ktp_dct_list = [None] * mech_idx
                combined_rxn_ktp_dct[rxn] = ktp_dct_list.append(ktp_dct)

                # Also, add the em_param
                

            # If the reaction already exists, append the new ktp_dct
            else:
                ktp_dct_list = combined_rxn_ktp_dct[rxn]  # get the current list of ktp_dcts

                # If any of the previous entries were blank, add None entries to fill
                if len(ktp_dct_list) < mech_idx:
                     ktp_dct_list.extend([None] * (mech_idx - len(ktp_dct_list)))
                                
                ktp_dct_list.append(ktp_dct) 
            
    # Clean up the combined dct in two ways: 
    for rxn, ktp_dct_list in combined_rxn_ktp_dct.items():
        # 1: Add None entries so that all are the same length
        if len(ktp_dct_list) < num_mechs:
            ktp_dct_list.extend([None] * (num_mechs - len(ktp_dct_list))) 
            combined_rxn_ktp_dct[rxn] = ktp_dct_list
        # 2: Create the em_param_dct, which denotes whether a reaction has 
        
            
    return combined_rxn_ktp_dct
コード例 #7
0
ファイル: compare.py プロジェクト: snelliott/mechanalyzer
def align_mechs(mech_filenames,
                thermo_filenames,
                spc_csv_filenames,
                temps,
                pressures,
                rev_rates=True,
                remove_loners=True,
                write_file=False,
                print_output=False):
    """ Takes any number of mechanisms and renames the species to all be the same,
        reverses any reactions to be in the same direction, and outputs a dictionary
        of all the reaction names and the corresponding k(T,P) dictionaries.

        :param mech_filenames: names of the Chemkin-formatted mechanism text files
        :type mech_filenames: list
        :param thermo_filenames: names of the Chemkin-formatted thermo text files; if the thermo
            info is contained in the mechanism.txt, just put the same filename here
        :type thermo_filenames: list
        :param spc_csv_filenames: names of the spc_csv filenames, which should have the following info:
            names, SMILES, inchi, multiplicity, charge, sensitivity (sensitivity not required)
        :type spc_csv_filenames: list
        :param temps: array of temperatures in Kelvin
        :type temps: Numpy 1-D array
        :param pressures: array of pressures in atm
        :type pressures: Numpy 1-D arrayi
        :param rev_rates: whether or not to reverse reactions; thermo filenames not required if False
        :type rev_rates: Bool
        :return aligned_rxn_ktp_dcts: dictionary of k(T,P) values for each mechanism, all under the same
            reaction name index
        :rtype: dict {((rcts), (prds)): [{ktp_dct1, ktp_dct2, ...], ...}
        :return aligned_rxn_em_dcts: dictionary of Boolean parameters indicating whether or not the rxn
            has a '+ M' term (i.e., third body) associated with it
        :rtype: dict {((rcts), (prds)): Boolean, ...}
    """
    if rev_rates:
        assert len(mech_filenames) == len(spc_csv_filenames) == len(
            thermo_filenames
        ), (f"""The lengths of the mechanism, spc_csv, and thermo filename inputs should 
                all be the same, but are instead {len(mech_filenames)}, {len(spc_csv_filenames)}, 
                and {len(thermo_filenames)}, respectively.""")
    else:
        assert len(mech_filenames) == len(spc_csv_filenames), (
            f"""The lengths of the mechanism and spc_csv inputs should 
                all be the same, but are instead {len(mech_filenames)} and {len(spc_csv_filenames)}, 
                respectively.""")

    num_mechs = len(mech_filenames)

    # Load rxn_ktp_dcts, spc_ident_dcts, and spc_thermo_dcts
    rxn_ktp_dcts = []
    rxn_param_dcts = []
    spc_ident_dcts = []
    spc_thermo_dcts = []
    for idx in range(num_mechs):
        # Get the rxn_ktp_dct and the rxn_param_dct
        mech_str = parser.ptt.read_inp_str(JOB_PATH,
                                           mech_filenames[idx],
                                           remove_comments=False)
        ea_units, a_units = parser_mech.reaction_units(mech_str)
        rxn_block_str = parser_mech.reaction_block(mech_str)
        rxn_param_dct = parser_rxn.param_dct(rxn_block_str, ea_units, a_units)
        rxn_ktp_dct = calc_rates.eval_rxn_param_dct(rxn_param_dct, pressures,
                                                    temps)
        rxn_ktp_dcts.append(rxn_ktp_dct)
        rxn_param_dcts.append(rxn_param_dct)

        # Get the spc_dct
        spc_csv_str = parser.ptt.read_inp_str(JOB_PATH,
                                              spc_csv_filenames[idx],
                                              remove_comments=False)
        spc_dct = parser_spc.build_spc_dct(spc_csv_str, 'csv')
        spc_ident_dcts.append(spc_dct)

        # Get the thermo_dct
        if rev_rates:
            thermo_str = parser.ptt.read_inp_str(JOB_PATH,
                                                 thermo_filenames[idx],
                                                 remove_comments=False)
            thermo_block_str = parser_mech.thermo_block(thermo_str)
            spc_nasa7_dct = parser_thermo.create_spc_nasa7_dct(
                thermo_block_str)
            spc_thermo_dct = calc_thermo.create_spc_thermo_dct(
                spc_nasa7_dct, temps)
            spc_thermo_dcts.append(spc_thermo_dct)

    # Get the renamed dictionaries
    renamed_rxn_ktp_dcts, rename_instructions_lst = rename_dcts(
        rxn_ktp_dcts, 'rxn', spc_ident_dcts)
    renamed_rxn_param_dcts, _ = rename_dcts(rxn_param_dcts, 'rxn',
                                            spc_ident_dcts)
    if rev_rates:
        renamed_spc_thermo_dcts, _ = rename_dcts(spc_thermo_dcts, 'spc',
                                                 spc_ident_dcts)

    # Get the em_param_dcts
    renamed_rxn_em_dcts = []
    for dct in renamed_rxn_param_dcts:
        renamed_rxn_em_dct = get_rxn_em_dct(dct)
        renamed_rxn_em_dcts.append(renamed_rxn_em_dct)

    # Loop over each reaction and reverse if necessary
    # This creates the reversed dct, which has uniform species names and uniform reactant/product ordering
    reversed_rxn_ktp_dcts = copy.copy(renamed_rxn_ktp_dcts)
    reversed_rxn_em_dcts = copy.copy(renamed_rxn_em_dcts)
    for mech_idx in range(num_mechs - 1):
        for idx2 in range(mech_idx + 1, num_mechs):

            # If indicated, reverse the rxn_ktp_dcts and rxm_em_dcts
            if rev_rates:
                reversed_rxn_ktp_dct = reverse_rxn_ktp_dct(
                    renamed_rxn_ktp_dcts[mech_idx], renamed_rxn_ktp_dcts[idx2],
                    renamed_rxn_param_dcts[mech_idx],
                    renamed_rxn_param_dcts[idx2],
                    renamed_spc_thermo_dcts[mech_idx], temps, rev_rates)
                reversed_rxn_em_dct = reverse_rxn_em_dct(
                    renamed_rxn_em_dcts[idx2],
                    renamed_rxn_param_dcts[mech_idx],
                    renamed_rxn_param_dcts[idx2], rev_rates)

            # Otherwise, just rename so the reactants and products are in the same order
            else:
                reversed_rxn_ktp_dct = reverse_rxn_ktp_dct(
                    renamed_rxn_ktp_dcts[mech_idx], renamed_rxn_ktp_dcts[idx2],
                    renamed_rxn_param_dcts[mech_idx],
                    renamed_rxn_param_dcts[idx2], [], temps, rev_rates)
                reversed_rxn_em_dct = reverse_rxn_em_dct(
                    renamed_rxn_em_dcts[idx2],
                    renamed_rxn_param_dcts[mech_idx],
                    renamed_rxn_param_dcts[idx2], rev_rates)

            reversed_rxn_ktp_dcts[idx2] = reversed_rxn_ktp_dct
            reversed_rxn_em_dcts[idx2] = reversed_rxn_em_dct

    # Sort the reversed dictionaries into a single output
    # Loop over each mechanism
    aligned_rxn_ktp_dct = {}
    aligned_rxn_em_dct = {}
    for mech_idx, dct in enumerate(reversed_rxn_ktp_dcts):
        for rxn, ktp_dct in dct.items():

            # If the reaction does not yet exist, add it
            if rxn not in aligned_rxn_ktp_dct.keys():
                ktp_dct_list = [
                    None
                ] * mech_idx  # add empty entries to account for previous mechs that are missing
                ktp_dct_list.append(ktp_dct)
                aligned_rxn_ktp_dct[rxn] = ktp_dct_list
                try:
                    aligned_rxn_em_dct[rxn] = reversed_rxn_em_dcts[mech_idx][
                        rxn]  # add the em_param
                except KeyError:
                    print(
                        f'There was a key error in mech idx {mech_idx} for the rxn {rxn}'
                    )

            # If the reaction already exists, append the new ktp_dct
            else:
                ktp_dct_list = aligned_rxn_ktp_dct[
                    rxn]  # get the current list of ktp_dcts
                # If any of the previous entries were blank, add None entries to fill
                if len(ktp_dct_list) < mech_idx:
                    ktp_dct_list.extend([None] *
                                        (mech_idx - len(ktp_dct_list)))
                ktp_dct_list.append(ktp_dct)

    # Clean up the aligned dct
    for rxn, ktp_dct_list in aligned_rxn_ktp_dct.items():
        # Add None entries so that all are the same length
        if len(ktp_dct_list) < num_mechs:
            ktp_dct_list.extend([None] * (num_mechs - len(ktp_dct_list)))
            aligned_rxn_ktp_dct[rxn] = ktp_dct_list

    if print_output:
        print('renamed_rxn_param_dcts', renamed_rxn_param_dcts)
        print('renamed_rxn_em_dcts', renamed_rxn_em_dcts)
        print('reversed_rxn_ktp_dcts', reversed_rxn_ktp_dcts)
        print('reversed_rxn_em_dcts', reversed_rxn_em_dcts)
        print('aligned_rxn_ktp_dct', aligned_rxn_ktp_dct)
        print('aligned_rxn_em_dct', aligned_rxn_em_dct)

    if write_file:
        with open("align_mechs_output.txt", "w") as f:
            for mech_idx, rename_instructions in enumerate(
                    rename_instructions_lst):
                f.write(
                    f"Rename instructions for converting mech {mech_idx+2} to mech {mech_idx+1}:\n"
                )
                f.write(
                    f"First column: mech {mech_idx+2} name, Second column: mech {mech_idx+1} name\n"
                )
                for spc_name2, spc_name1 in rename_instructions.items():
                    f.write(
                        ('{0:<1s}, {1:<5s}\n').format(spc_name2, spc_name1))
                f.write("\n\n")

            # Write the aligned_rxn_ktp_dct
            f.write(f"Combined_rxn_ktp_dct\n\n")
            f.write(
                ('{0:<64s}{1:<15s}{2:<15s}').format('Rxn name', 'In mech 1?',
                                                    'In mech 2?'))
            for rxn, ktp_dct_lst in aligned_rxn_ktp_dct.items():
                rxn_name = format_rxn_name(rxn, aligned_rxn_em_dct[rxn])
                present = []
                for entry in ktp_dct_lst:
                    if entry:
                        present.append('Yes')
                    else:
                        present.append('No')

                f.write(('\n{0:<64s}{1:<15s}{2:<15s}').format(
                    rxn_name, present[0], present[1]))

            f.close()

    if remove_loners:
        aligned_rxn_ktp_dct, aligned_rxn_em_dct = remove_lone_reactions(
            aligned_rxn_ktp_dct, aligned_rxn_em_dct)

    return aligned_rxn_ktp_dct, aligned_rxn_em_dct
コード例 #8
0
ファイル: check_mech.py プロジェクト: Auto-Mech/mechanalyzer
import numpy as np
import sys
import ioformat.pathtools as fileio
from chemkin_io.parser import mechanism as parser_mech
from chemkin_io.parser import reaction as parser_rxn
from mechanalyzer.calculator import rates as calc_rates
from mechanalyzer.builder import checker

# INPUTS
mech_filename = 'chomech_v11a_ascii_cleaned.inp'
temps = np.linspace(300, 3000, 28)
pressures = np.array([1, 10, 100])
k_thresholds = [1e11, 1e15, 1e22]
rxn_num_threshold = 2
output_filename = 'mech_check.txt'

# Load dcts
JOB_PATH = sys.argv[1]
mech_str = fileio.read_file(JOB_PATH, mech_filename)
ea_units, a_units = parser_mech.reaction_units(mech_str)
rxn_block_str = parser_mech.reaction_block(mech_str)
RXN_PARAM_DCT = parser_rxn.param_dct(rxn_block_str, ea_units, a_units)
RXN_KTP_DCT = calc_rates.eval_rxn_param_dct(RXN_PARAM_DCT, pressures, temps)

output_str = checker.run_all_checks(RXN_PARAM_DCT, RXN_KTP_DCT, k_thresholds,
                                    rxn_num_threshold)
fileio.write_file(output_str, JOB_PATH, output_filename)