Beispiel #1
0
def objects_dct(job_path):
    """ Get the sections for the run block
    """

    # Read the obj section
    run_str = ptt.read_inp_str(job_path, RUN_INP, remove_comments='#')
    obj_str = object_block(run_str)

    # Read the sections of the obj section
    pes_block_str = apf.first_capture(ptt.paren_section('pes'), obj_str)
    spc_block_str = apf.first_capture(ptt.paren_section('spc'), obj_str)

    # Check if the obj section has been specified
    check_obj_spec(obj_str, pes_block_str, spc_block_str)

    # Build the run dictionary
    run_dct = {}
    if pes_block_str is not None:
        run_dct['pes'] = get_pes_idxs(
            ioformat.remove_whitespace(pes_block_str))
    else:
        run_dct['pes'] = []
    if spc_block_str is not None:
        run_dct['spc'] = get_spc_idxs(
            ioformat.remove_whitespace(spc_block_str))
    else:
        run_dct['spc'] = []

    return run_dct
Beispiel #2
0
def build_model_keyword_dct(model_str):
    """ Build a dictionary for all the models keywords
    """
    # Grab the various sections required for each model
    pf_str = apf.first_capture(ptt.paren_section('pf'), model_str)
    es_str = apf.first_capture(ptt.paren_section('es'), model_str)
    etrans_str = apf.first_capture(ptt.paren_section('etransfer'), model_str)
    options_str = apf.first_capture(ptt.paren_section('options'), model_str)
    assert pf_str is not None
    assert es_str is not None
    assert options_str is not None

    # Get the dictionary for each section and check them
    pf_dct = ptt.build_keyword_dct(pf_str)
    es_dct = ptt.build_keyword_dct(es_str)
    etransfer_dct = ptt.build_keyword_dct(etrans_str)
    options_dct = ptt.build_keyword_dct(options_str)
    # assert check_model_dct(keyword_dct)

    # Combine dcts into single model dct
    model_dct = {}
    model_dct['pf'] = pf_dct
    model_dct['es'] = es_dct
    model_dct['etransfer'] = etransfer_dct
    model_dct['options'] = options_dct

    return model_dct
Beispiel #3
0
def chebyshev_parameters(rxn_dstr):
    """ Parses the data string for a reaction in the reactions block
        for the lines containing the Chebyshevs fitting parameters,
        then reads the parameters from these lines.

        :param rxn_dstr: data string for species in reaction block
        :type rxn_dstr: str
        :return params: Chebyshev fitting parameters
        :rtype: dict[param: value]
    """

    temp_pattern = ('TCHEB' + app.zero_or_more(app.SPACE) + app.escape('/') +
                    app.SPACES + app.capturing(app.FLOAT) + app.SPACES +
                    app.capturing(app.FLOAT) + app.zero_or_more(app.SPACE) +
                    app.escape('/'))
    pressure_pattern = ('PCHEB' + app.zero_or_more(app.SPACE) +
                        app.escape('/') + app.SPACES +
                        app.capturing(app.FLOAT) + app.SPACES +
                        app.capturing(app.FLOAT) +
                        app.zero_or_more(app.SPACE) + app.escape('/'))
    alpha_dimension_pattern = ('CHEB' + app.zero_or_more(app.SPACE) +
                               app.escape('/') + app.SPACES +
                               app.capturing(app.INTEGER) + app.SPACES +
                               app.capturing(app.INTEGER) +
                               app.zero_or_more(app.SPACE) + app.escape('/'))
    alpha_elements_pattern = (
        'CHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.series(
            app.capturing(app.SPACES + app.capturing(app.EXPONENTIAL_FLOAT)),
            app.SPACES) + app.zero_or_more(app.SPACE) + app.escape('/'))

    cheb_temps = apf.first_capture(temp_pattern, rxn_dstr)
    cheb_pressures = apf.first_capture(pressure_pattern, rxn_dstr)
    alpha_dims = apf.first_capture(alpha_dimension_pattern, rxn_dstr)
    alpha_elm = apf.all_captures(alpha_elements_pattern, rxn_dstr)
    if not alpha_elm:
        alpha_elm = None

    params_dct = {}
    if all(vals is not None
           for vals in (cheb_temps, cheb_pressures, alpha_dims, alpha_elm)):
        params_dct['t_limits'] = [float(val) for val in cheb_temps]
        params_dct['p_limits'] = [float(val) for val in cheb_pressures]
        params_dct['alpha_dim'] = [int(val) for val in alpha_dims]
        params_dct['alpha_elm'] = [list(map(float, row)) for row in alpha_elm]
    else:
        params_dct = None

    return params_dct
Beispiel #4
0
def inp_zmatrix(output_str):
    """ Reads the Z-Matrix specified in the input from the output file string.
        Returns the Z-Matrix in Bohr and Radians.

        :param output_str: string of the program's output file
        :type output_str: str
        :rtype: automol molecular geometry data structure
    """

    # Block with init geom
    block_ptt = ('molecule' + app.SPACES + app.escape('{') +
                 app.capturing(app.one_or_more(app.WILDCARD, greedy=False)) +
                 app.escape('}'))
    block = apf.first_capture(block_ptt, output_str)

    # Read the matrix and the values from the output
    start_ptt = app.LINE_FILL + app.NEWLINE + app.LINE_FILL + app.NEWLINE
    symbs, key_mat, name_mat, val_mat = ar.zmat.read(block,
                                                     start_ptt=start_ptt)

    # Call the automol constructor
    if all(x is not None for x in (symbs, key_mat, name_mat, val_mat)):
        zma = automol.zmat.from_data(symbs,
                                     key_mat,
                                     val_mat,
                                     name_mat,
                                     one_indexed=True,
                                     angstrom=True,
                                     degree=True)
    else:
        zma = None

    return zma
Beispiel #5
0
def reaction_units(string, start_pattern, units_pattern):
    """ return a block delimited by start and end patterns
    """
    rxn_line_pattern = start_pattern + app.capturing(app.LINE_FILL)
    units_string = apf.first_capture(rxn_line_pattern, string)
    units_lst = apf.all_captures(units_pattern, units_string)

    ckin_ea_units = [
        'CAL/MOLE', 'KCAL/MOLE', 'JOULES/MOLE', 'KJOULES/MOLE', 'KELVINS'
    ]
    ckin_a_units = ['MOLES', 'MOLECULES']

    if units_lst:
        if any(unit in ckin_ea_units for unit in units_lst):
            for unit in ckin_ea_units:
                if unit in units_lst:
                    ea_unit = unit.lower()
        else:
            ea_unit = 'cal/mole'
        if any(unit in ckin_a_units for unit in units_lst):
            for unit in ckin_a_units:
                if unit in units_lst:
                    a_unit = unit.lower()
        else:
            a_unit = 'moles'
        units = (ea_unit, a_unit)
    else:
        units = ('cal/mole', 'moles')

    return units
Beispiel #6
0
def troe(rxn_str):
    """ Parses the data string for a reaction in the reactions block
        for a line containing the Troe fitting parameters,
        then reads the parameters from this line.

        Only gets the 4 Troe-specific parameters: alpha, T***, T*, and T**

        :param rxn_str: raw Chemkin string for a single reaction
        :type rxn_str: str
        :return params: Troe fitting parameters
        :rtype: list(float)
    """

    pattern = (
        'TROE' + app.zero_or_more(app.SPACE) + app.escape('/') +
        app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) +
        app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
        app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
        app.maybe(app.one_or_more(app.SPACE) + app.capturing(app.NUMBER)) +
        app.zero_or_more(app.SPACE) + app.escape('/'))
    cap1 = apf.first_capture(pattern, rxn_str)

    if cap1 is not None:
        params = []
        for val in cap1:
            if val is not None:
                params.append(float(val))
            else:
                params.append(None)
    else:
        params = None

    return params
Beispiel #7
0
 def _interpret_reagent_count(rgt_cnt_str):
     _pattern = (app.STRING_START + app.capturing(app.maybe(app.DIGIT)) +
                 app.capturing(app.one_or_more(app.NONSPACE)))
     cnt, rgt = apf.first_capture(_pattern, rgt_cnt_str)
     cnt = int(cnt) if cnt else 1
     rgts = (rgt, ) * cnt
     return rgts
Beispiel #8
0
def troe_parameters(rxn_dstr):
    """ Parses the data string for a reaction in the reactions block
        for a line containing the Troe fitting parameters,
        then reads the parameters from this line.

        :param rxn_dstr: data string for species in reaction block
        :type rxn_dstr: str
        :return params: Troe fitting parameters
        :rtype: list(float)
    """
    pattern = (
        'TROE' + app.zero_or_more(app.SPACE) + app.escape('/') +
        app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) +
        app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
        app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
        app.maybe(app.one_or_more(app.SPACE) + app.capturing(app.NUMBER)) +
        app.zero_or_more(app.SPACE) + app.escape('/'))
    cap1 = apf.first_capture(pattern, rxn_dstr)

    if cap1 is not None:
        params = []
        for val in cap1:
            if val is not None:
                params.append(float(val))
            else:
                params.append(None)
    else:
        params = None

    return params
Beispiel #9
0
 def _expand_group(group_str):
     if apf.has_match(group_ptt, group_str):
         count, part = ap_cast(apf.first_capture(group_ptt, group_str))
         parts = [part] * count
     else:
         parts = [group_str]
     return parts
Beispiel #10
0
def low_p_parameters(rxn_dstr, ea_units, a_units):
    """ Parses the data string for a reaction in the reactions block
        for a line containing the low-pressure fitting parameters,
        then reads the parameters from this line.

        :param rxn_dstr: data string for species in reaction block
        :type rxn_dstr: str
        :return params: Arrhenius fitting parameters for low-P rates
        :rtype: list(float)
    """

    pattern = ('LOW' + app.zero_or_more(app.SPACE) + app.escape('/') +
               app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) +
               app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
               app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
               app.zero_or_more(app.SPACE) + app.escape('/'))
    cap1 = apf.first_capture(pattern, rxn_dstr)
    if cap1 is not None:
        params = [float(val) for val in cap1]

        # Convert the units of Ea and A
        ea_conv_factor = get_ea_conv_factor(rxn_dstr, ea_units)
        a_conv_factor = get_a_conv_factor(rxn_dstr, a_units)
        params[2] = params[2] * ea_conv_factor
        params[0] = params[0] * a_conv_factor

    else:
        params = None

    return params
Beispiel #11
0
def formula_sublayer(ich):
    """ formula sublayer
    """
    ptt = (_version_pattern() +
           SLASH + app.capturing(_formula_sublayer_pattern()))
    lyr = apf.first_capture(ptt, ich)
    return lyr
Beispiel #12
0
def pressure_region_specification(rxn_dstr):
    """ Parses the data string for a reaction in the reactions block
        for the line containing the chemical equation in order to
        check if a body M is given, indicating pressure dependence.

        :param rxn_dstr: data string for species in reaction block
        :type rxn_dstr: str
        :return pressure_region: type of pressure indicated
        :rtype: str
    """

    pattern = app.capturing(
        _first_line_pattern(rct_ptt=SPECIES_NAMES_PATTERN,
                            prd_ptt=SPECIES_NAMES_PATTERN,
                            param_ptt=app.maybe(COEFF_PATTERN)))
    string = apf.first_capture(pattern, rxn_dstr)

    if string is not None:
        string = string.strip()
        if 'M' in string:
            # Presence of M denotes specific region assumptions
            if '(+M)' in string:
                pressure_region = 'falloff'
            else:
                pressure_region = 'lowp'
        else:
            # No M can be independent or not, depending on subsequent info
            if 'PLOG' in rxn_dstr or 'CHEB' in rxn_dstr:
                pressure_region = 'all'
            else:
                pressure_region = 'indep'
    else:
        pressure_region = None

    return pressure_region
Beispiel #13
0
def third_body(rxn_str):
    """ Parses the data string for a reaction in the reactions block
        for the line containing the chemical equation in order to
        read the names of the third body collider if present

        :param rxn_str: raw Chemkin string for a single reaction
        :type rxn_str: str
        :return trd_body: names of the colliders and corresponding fraction
        :rtype: tuple(str)
    """

    pattern = _first_line_pattern(rct_ptt=app.capturing(SPECIES_NAMES_PATTERN),
                                  prd_ptt=SPECIES_NAMES_PATTERN,
                                  param_ptt=app.maybe(COEFF_PATTERN))
    rgt_str = apf.first_capture(pattern, rxn_str)
    rgt_str = apf.remove(app.LINESPACES, rgt_str)
    rgt_split_paren = apf.split(CHEMKIN_PAREN_PLUS, rgt_str)
    rgt_split_plus = apf.split(app.PLUS, rgt_str)

    if len(rgt_split_paren) > 1:
        trd_body = '(+' + apf.split(CHEMKIN_PAREN_CLOSE,
                                    rgt_split_paren[1])[0] + ')'

    elif 'M' in rgt_split_plus:
        trd_body = '+M'

    else:
        trd_body = None

    trd_body = (trd_body, )

    return trd_body
Beispiel #14
0
def buffer_enhance_factors(rxn_dstr):
    """ get the factors of speed-up from bath gas
    """
    species_char = app.one_of_these([
        app.LETTER, app.DIGIT,
        app.escape('('),
        app.escape(')'), app.UNDERSCORE
    ])
    species_name = app.one_or_more(species_char)

    # Get the line that could have the bath gas buffer enhancements
    bath_line_pattern = (_first_line_pattern(rct_ptt=SPECIES_NAMES_PATTERN,
                                             prd_ptt=SPECIES_NAMES_PATTERN,
                                             coeff_ptt=COEFF_PATTERN) + '\n' +
                         app.capturing(app.LINE))
    bath_string = apf.first_capture(bath_line_pattern, rxn_dstr)

    # Check if this line has bath gas factors or is for something else
    # If factors in string, get factors
    bad_strings = ('DUPLICATE', 'LOW', 'TROE', 'CHEB', 'PLOG')
    if (any(string in bath_string for string in bad_strings)
            and bath_string.strip() != ''):
        factors = None
    else:
        bath_string = '\n'.join(bath_string.strip().split())
        factor_pattern = (app.capturing(species_name) + app.escape('/') +
                          app.capturing(app.NUMBER) + app.escape('/'))
        baths = apf.all_captures(factor_pattern, bath_string)
        factors = {}
        for bath in baths:
            factors[bath[0]] = float(bath[1])

    return factors
Beispiel #15
0
def em_parameters(rxn_dstr):
    """ Parses the data string for a reaction in the reactions block
        for the line containing the chemical equation in order to
        check if a body M is given, indicating pressure dependence.

        :param rxn_dstr: data string for species in reaction block
        :type rxn_dstr: str
        :return pressure_region: type of pressure indicated
        :rtype: str
    """

    pattern = app.capturing(
        _first_line_pattern(rct_ptt=SPECIES_NAMES_PATTERN,
                            prd_ptt=SPECIES_NAMES_PATTERN,
                            param_ptt=COEFF_PATTERN))
    string = apf.first_capture(pattern, rxn_dstr)

    if string is not None:
        string = string.strip()
        if '(+M)' in string:
            params = '(+M)'
        elif 'M' in string:
            params = '+M'
        else:
            params = None

    return params
Beispiel #16
0
def get_mc_nsamp(inp_str, species):
    """ Get the number of samples for the Monte Carlo sampling
    """

    # Set the nsamp pattern
    mc_nsamp_pattern = ('mc_nsamp' + zero_or_more(SPACE) + '=' +
                        zero_or_more(SPACE) +
                        capturing(one_or_more(NONNEWLINE)))

    # Obtain the appropriate species string
    species_str = get_species_str(inp_str, species)

    # Obtain the charge string
    mc_nsamp_str = first_capture(mc_nsamp_pattern, species_str)

    # Set the nsamp (maybe check the ntau variable)
    if mc_nsamp_str is not None:
        mc_nsamp_str = mc_nsamp_str.split()
        if len(mc_nsamp_str) < 4:
            mc_nsamp = [False, 0, 0, 0, 0, int(mc_nsamp_str[0])]
        else:
            mc_nsamp = [True]
            for n in mc_nsamp_str:
                mc_nsamp.append(int(n))
            mc_nsamp.append(12)
    else:
        mc_nsamp = 0
    return mc_nsamp
Beispiel #17
0
def get_mc_tau(inp_str, species):
    """ Get the electric charge of the species
    """

    # Set the charge pattern
    mc_tau_pattern = ('mc_tau' + zero_or_more(SPACE) + '=' +
                      zero_or_more(SPACE) + '"' +
                      capturing(one_or_more(WILDCARD, greedy=False)) + '"')

    # Obtain the appropriate species string
    species_str = get_species_str(inp_str, species)

    # Obtain the geom string
    mc_tau_str = first_capture(mc_tau_pattern, species_str)

    # Set the geom as an integer
    if mc_tau_str is not None:
        if mc_tau_str == 'auto':
            mc_tau_dict = "auto"
        else:
            mc_tau_dict = literal_eval(mc_tau_str)
    else:
        mc_tau_dict = {}

    return mc_tau_dict
Beispiel #18
0
def low_p(rxn_str, ea_units, a_units):
    """ Parses the data string for a reaction in the reactions block
        for a line containing the low-pressure fitting parameters,
        then reads the parameters from this line.

        :param rxn_str: raw Chemkin string for a single reaction
        :type rxn_str: str
        :param ea_units: units of activation energies
        :type ea_units: string
        :param a_units: units of rate constants; either 'moles' or 'molecules'
        :type a_units: str
        :return params: Arrhenius fitting parameters for low-P rates
        :rtype: list(list(float))
    """

    pattern = ('LOW' + app.zero_or_more(app.SPACE) + app.escape('/') +
               app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) +
               app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
               app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) +
               app.zero_or_more(app.SPACE) + app.escape('/'))
    cap1 = apf.first_capture(pattern, rxn_str)
    if cap1 is not None:
        params = [float(val) for val in cap1]

        # Convert the units of Ea and A
        ea_conv_factor = get_ea_conv_factor(ea_units)
        a_conv_factor = get_a_conv_factor(rxn_str, a_units)
        params[2] = params[2] * ea_conv_factor
        params[0] = params[0] * a_conv_factor
        params = [params]  # convert to list inside a list

    else:
        params = None

    return params
Beispiel #19
0
def _charge_layer(ich):
    """ charge layer
    """
    ptt = (_version_pattern() + SLASH + _formula_sublayer_pattern() +
           app.maybe(SLASH + _main_layer_pattern()) + SLASH +
           app.capturing(_charge_layer_pattern()))
    lyr = apf.first_capture(ptt, ich)
    return lyr
Beispiel #20
0
def _block(string, start_pattern, end_pattern):
    """ return a block delimited by start and end patterns
    """
    contents_pattern = app.capturing(
        app.one_or_more(app.WILDCARD, greedy=False))
    pattern = start_pattern + contents_pattern + end_pattern
    contents = apf.first_capture(pattern, string)
    return contents
Beispiel #21
0
def inp_zmatrix(inp_str):
    """ Reads the input z-matrix from the input file string
        Returns the Z-Matrix in Bohr and Radians.

        :param output_str: string of the program's output file
        :type output_str: str
        :rtype: automol molecular geometry data structure
    """

    # Reads the matrix from the beginning of the input
    symbs, key_mat, name_mat = ar.vmat.read(
        inp_str,
        start_ptt=app.padded(app.NEWLINE).join([
            app.escape('comment:'), app.LINE, app.LINE, '']),
        symb_ptt=(ar.par.Pattern.ATOM_SYMBOL +
                  app.not_followed_by(app.SPACES + app.FLOAT) +
                  app.maybe(app.UNSIGNED_INTEGER)),
        key_ptt=app.one_of_these([app.UNSIGNED_INTEGER, app.VARIABLE_NAME]),
        line_end_ptt=app.maybe(app.UNSIGNED_INTEGER),
        last=False)

    # Reads the values from the input
    if all(x is not None for x in (symbs, key_mat, name_mat)):
        if len(symbs) == 1:
            # val_dct = {}
            val_mat = ((None, None, None),)
        else:
            val_dct = ar.setval.read(
                inp_str,
                start_ptt=app.padded(app.NEWLINE).join([
                    app.padded('Variables:', app.NONNEWLINE), '']),
                entry_sep_ptt='',
                entry_start_ptt='',
                sep_ptt=app.maybe(app.LINESPACES).join([
                    app.NEWLINE]),
                last=True)
            val_mat = ar.setval.convert_dct_to_matrix(val_dct, name_mat)

        # Check for the pattern
        # For the case when variable names are used instead of integer keys:
        # (otherwise, does nothing)
        key_dct = dict(map(reversed, enumerate(symbs)))
        key_dct[None] = 0
        key_mat = [
            [key_dct[val]+1 if not isinstance(val, numbers.Real) else val
             for val in row] for row in key_mat]
        symb_ptt = app.STRING_START + app.capturing(ar.par.Pattern.ATOM_SYMBOL)
        symbs = [apf.first_capture(symb_ptt, symb) for symb in symbs]

        # Call the automol constructor
        zma = automol.zmat.from_data(
            symbs, key_mat, val_mat, name_mat,
            one_indexed=True, angstrom=True, degree=True)
    else:
        zma = None

    return zma
Beispiel #22
0
def _get_level_section(input_string, level):
    """ species input
    """

    pattern = ('level' + one_or_more(SPACE) + level +
               capturing(one_or_more(WILDCARD, greedy=False)) + 'end')
    section = first_capture(pattern, input_string)

    return section
Beispiel #23
0
def product_names(rxn_dstr):
    """ product species names
    """
    pattern = _first_line_pattern(rct_ptt=SPECIES_NAMES_PATTERN,
                                  prd_ptt=app.capturing(SPECIES_NAMES_PATTERN),
                                  coeff_ptt=COEFF_PATTERN)
    string = apf.first_capture(pattern, rxn_dstr)
    names = _split_reagent_string(string)
    return names
Beispiel #24
0
def end_block(string, header, name=None, footer=None):
    """ A pattern for a certain block

        rtype: str
    """
    _ptt = end_block_ptt(header, name=name, footer=footer)
    cap = apf.first_capture(_ptt, string)

    return cap if cap is not None else None
Beispiel #25
0
def _get_version_string(output_str):
    """ obtains the string containing the version number
    """

    pattern = ('Version ' + app.capturing(app.one_or_more(app.NONNEWLINE)))

    version_string = apf.first_capture(pattern, output_str)

    return version_string
Beispiel #26
0
def prefix(chi):
    """ Determine the chemical identifier prefix (InChI or AMChI).

        :param chi: ChI string
        :type chi: str
        :rtype: str
    """
    ptt = app.capturing(NONSLASHES) + app.followed_by('=')
    std = apf.first_capture(ptt, chi)
    return std
Beispiel #27
0
def temp_common_default(block_str):
    """ temperature defaults from the thermo block
    """
    pattern = (app.STRING_START + app.UNSIGNED_FLOAT + app.LINESPACES +
               app.capturing(app.UNSIGNED_FLOAT) + app.LINESPACES +
               app.UNSIGNED_FLOAT)
    capture = apf.first_capture(pattern, block_str)
    assert capture
    tmp_com_def = float(capture)
    return tmp_com_def
Beispiel #28
0
def get_key_int(inp_str, lvl, key):
    # Find the value for a given key, make int
    pattern = (key + zero_or_more(SPACE) + '=' + zero_or_more(SPACE) +
               capturing(one_or_more(NONSPACE)))
    # Obtain the appropriate species string
    patt_str = get_lvl_str(inp_str, lvl)
    # Obtain the charge string
    patt_str = first_capture(pattern, patt_str)
    assert patt_str is not None
    return int(patt_str)
Beispiel #29
0
def _get_prog_string(output_string):
    """ obtains the string containing the version name and number
    """

    pattern = app.capturing(('NAME' + app.SPACES + ':' + app.SPACES +
                             app.one_or_more(app.NONNEWLINE)))

    prog_string = apf.first_capture(pattern, output_string)

    return prog_string
Beispiel #30
0
def version(chi):
    """ Determine version number of the ChI string.

        :param chi: ChI string
        :type chi: str
        :rtype: str
    """
    ptt = app.capturing(_version_pattern())
    ver = apf.first_capture(ptt, chi)
    return ver