示例#1
0
def _bond_distance(gra, atm1_key, atm2_key, check=True):
    """ predicted bond distance

    (currently crude, but could easily be made more sophisticated
    """
    atm_sym_dct = atom_symbols(gra)

    if check:
        assert atm2_key in atom_neighbor_keys(gra)[atm1_key]

    atm1_sym = atm_sym_dct[atm1_key]
    atm2_sym = atm_sym_dct[atm2_key]

    if atm1_sym == 'H' or atm2_sym == 'H':
        dist = 1.1 * qcc.conversion_factor('angstrom', 'bohr')
    else:
        dist = 1.5 * qcc.conversion_factor('angstrom', 'bohr')

    return dist
示例#2
0
def _start_zmatrix_from_ring(gra, rng_atm_keys):
    """ generates a z-matrix for a ring
    """
    # the key dictionary can be constructed immediately
    zma_key_dct = {
        atm_key: zma_key
        for zma_key, atm_key in enumerate(rng_atm_keys)
    }

    # now, build the z-matrix
    natms = len(rng_atm_keys)
    atm_sym_dct = atom_symbols(gra)

    dist_val = 1.5 * qcc.conversion_factor('angstrom', 'bohr')
    ang_val = (natms - 2.) * numpy.pi / natms
    dih_val = 0.

    # 1. construct the z-matrix for a 3-atom system
    key_mat = [[None, None, None], [0, None, None], [1, 0, None]]

    name_mat = [[None, None, None], ['R1', None, None], ['R2', 'A2', None]]

    syms = dict_.values_by_key(atm_sym_dct, rng_atm_keys[:3])

    val_dct = {'R1': dist_val, 'R2': dist_val, 'A2': ang_val}

    zma = automol.zmatrix.from_data(syms, key_mat, name_mat, val_dct)

    # 2. append z-matrix rows for the remaining atoms
    for row, rng_atm_key in enumerate(rng_atm_keys):
        if row > 2:
            sym = atm_sym_dct[rng_atm_key]
            dist_name = automol.zmatrix.new_distance_name(zma)
            ang_name = automol.zmatrix.new_central_angle_name(zma)
            dih_name = automol.zmatrix.new_dihedral_angle_name(zma)

            zma = automol.zmatrix.append(zma, sym, [row - 1, row - 2, row - 3],
                                         [dist_name, ang_name, dih_name],
                                         [dist_val, ang_val, dih_val])

    return zma, zma_key_dct
示例#3
0
def connected_heuristic_zmatrix(gra):
    """ stereo-specific coordinates for a connected molecular graph

    (currently unable to handle rings -- fix that)
    """
    assert gra == explicit(gra)

    atm_sym_dct = atom_symbols(gra)

    # this will contain triplets of adjacent atoms from which to continue
    # filling out the z-matrix, after it has been started
    triplets = []

    # 1. start the z-matrix and set the lists of triplets
    rng_atm_keys_lst = rings_atom_keys(gra)
    if not rng_atm_keys_lst:
        # find the first heavy atom in the longest chain (if there isn't one,
        # we are dealing with atomic or molecular hydrogen, which will be
        # captured by the last two cases)
        chain = longest_chain(gra)

        if atm_sym_dct[chain[0]] != 'H':
            chain = list(reversed(chain))

        if len(chain) > 1:
            atm_key = chain[1]
        else:
            atm_key = chain[0]

        # determine the z-matrix of the starting atom and its neighbors
        zma, zma_key_dct, dummy_atm_key, gra = _start_zmatrix_from_atom(
            gra, atm_key)

        # since this is the first heavy atom in the longest chain, we only need
        # to follow one branch from this atom to complete the z-matrix; this
        # will be the branch extending toward the next heavy atom in the chai
        if len(chain) > 3:
            atm1_key, atm2_key, atm3_key = chain[:3]

            # if we inserted a dummy atom on the starting geometry, we should
            # use that as atom 1 in the triplet, rather than
            if dummy_atm_key is not None:
                atm1_key = dummy_atm_key

            triplets = [(atm1_key, atm2_key, atm3_key)]
    elif len(rng_atm_keys_lst) == 1:
        rng_atm_keys, = rng_atm_keys_lst

        zma, zma_key_dct = _start_zmatrix_from_ring(gra, rng_atm_keys)

        triplets += list(mit.windowed(rng_atm_keys[-2:] + rng_atm_keys, 3))
    else:
        # currently, multiple rings are not implemented
        raise NotImplementedError

    # 2. complete the z-matrix by looping over triplets
    for atm1_key, atm2_key, atm3_key in triplets:
        zma, zma_key_dct, gra = _complete_zmatrix_for_branch(
            gra, atm1_key, atm2_key, atm3_key, zma, zma_key_dct)

    # 3. convert to Cartesian geometry for stereo correction
    geo = automol.zmatrix.geometry(zma)
    geo_idx_dct = zma_key_dct
    geo = _stereo_corrected_geometry(gra, geo, geo_idx_dct)

    # 4. convert back to z-matrix, keeping the original z-matrix structure
    vma = automol.zmatrix.var_(zma)
    zma = automol.zmatrix.from_geometry(vma, geo)

    return zma, zma_key_dct
示例#4
0
def _complete_zmatrix_for_branch(gra, atm1_key, atm2_key, atm3_key, zma,
                                 zma_key_dct):
    """ core function for generating geometries; starting from three
    neighboring atoms at the end of the z-matrix, fills out the z-matrix for
    the rest of the branch extending out from the third atom

    returns a z-matrix and a dictionary mapping atom keys to rows of the
    z-matrix
    """
    atm_sym_dct = atom_symbols(gra)
    atm_ngb_keys_dct = atom_neighbor_keys(gra)

    atm1_zma_key = zma_key_dct[atm1_key]
    atm2_zma_key = zma_key_dct[atm2_key]
    atm3_zma_key = zma_key_dct[atm3_key]

    atm4_keys = atm_ngb_keys_dct[atm3_key] - {atm2_key}

    # first handle the linear bond case, inserting a dummy atom
    atm_hyb = resonance_dominant_atom_hybridizations(gra)[atm3_key]
    if atm_hyb == 1 and atm4_keys:
        assert len(atm4_keys) == 1
        atm4_key, = atm4_keys

        # first, insert the dummy atom
        dist4_name = automol.zmatrix.new_distance_name(zma)
        dist4_val = 1.

        ang4_name = automol.zmatrix.new_central_angle_name(zma)
        ang4_val = RIT_ANG

        dih4_name = automol.zmatrix.new_dihedral_angle_name(zma)
        dih4_val = 0.

        gra, dummy_atm_key = add_bonded_atom(gra, 'X', atm3_key, bnd_ord=0)
        dummy_atm_zma_key = automol.zmatrix.count(zma)

        zma_key_dct[dummy_atm_key] = dummy_atm_zma_key

        zma = automol.zmatrix.append(
            zma, 'X', [atm3_zma_key, atm2_zma_key, atm1_zma_key],
            [dist4_name, ang4_name, dih4_name],
            [dist4_val, ang4_val, dih4_val])

        # shift the keys to include the dummy atom
        atm1_key, atm2_key = atm2_key, dummy_atm_key
        atm1_zma_key, atm2_zma_key = atm2_zma_key, dummy_atm_zma_key

        atm4_sym = atm_sym_dct[atm4_key]

        dist4_name = automol.zmatrix.new_distance_name(zma)
        dist4_val = _bond_distance(gra, atm3_key, atm4_key)

        ang4_name = automol.zmatrix.new_central_angle_name(zma)
        ang4_val = RIT_ANG

        dih4_name = automol.zmatrix.new_dihedral_angle_name(zma)
        dih4_val = LIN_ANG

        zma_key_dct[atm4_key] = automol.zmatrix.count(zma)

        zma = automol.zmatrix.append(
            zma, atm4_sym, [atm3_zma_key, atm2_zma_key, atm1_zma_key],
            [dist4_name, ang4_name, dih4_name],
            [dist4_val, ang4_val, dih4_val])

        geo = automol.zmatrix.geometry(zma)

        # recursion 1
        zma, zma_key_dct, gra = _complete_zmatrix_for_branch(
            gra, atm2_key, atm3_key, atm4_key, zma, zma_key_dct)

    # from here on, we can assume a non-linear bond
    else:
        dih_incr = _dihedral_increment(gra, atm1_key, atm2_key, atm3_key)

        fixed_atm4_keys = set(atm4_keys) & set(zma_key_dct)
        if fixed_atm4_keys:
            geo = automol.zmatrix.geometry(zma)
            atm4_zma_keys = list(map(zma_key_dct.__getitem__, fixed_atm4_keys))
            dih4_vals = [
                automol.geom.dihedral_angle(geo, atm1_zma_key, atm2_zma_key,
                                            atm3_zma_key, atm4_zma_key)
                for atm4_zma_key in atm4_zma_keys
            ]
            dih4_val = max(dih4_vals)
        else:
            dih4_val = numpy.pi - dih_incr

        # subtract off the fixed atom 4 keys in case we're dealing with a ring
        for atm4_key in atm4_keys - fixed_atm4_keys:
            atm4_sym = atm_sym_dct[atm4_key]

            dist4_name = automol.zmatrix.new_distance_name(zma)
            dist4_val = _bond_distance(gra, atm3_key, atm4_key)

            ang4_name = automol.zmatrix.new_central_angle_name(zma)
            ang4_val = _bond_angle(gra, atm2_key, atm3_key, atm4_key)

            dih4_name = automol.zmatrix.new_dihedral_angle_name(zma)

            dih4_val += dih_incr
            dih4_val = numpy.mod(dih4_val, 2 * numpy.pi)

            zma_key_dct[atm4_key] = automol.zmatrix.count(zma)

            zma = automol.zmatrix.append(
                zma, atm4_sym, [atm3_zma_key, atm2_zma_key, atm1_zma_key],
                [dist4_name, ang4_name, dih4_name],
                [dist4_val, ang4_val, dih4_val])

            # recursion 2
            zma, zma_key_dct, gra = _complete_zmatrix_for_branch(
                gra, atm2_key, atm3_key, atm4_key, zma, zma_key_dct)

    gra_with_dummies = gra

    return zma, zma_key_dct, gra_with_dummies
示例#5
0
def _start_zmatrix_from_atom(gra, atm_key):
    """ generates a z-matrix for a single atom and its neighbors

    returns a z-matrix and a dictionary mapping atom keys to rows of the
    z-matrix
    """
    atm_ngb_keys_dct = atom_neighbor_keys(gra)

    dummy_atm_key = None

    atm_sym_dct = atom_symbols(gra)

    # sort hydrogens to be first
    atm_ngb_keys = sorted(atm_ngb_keys_dct[atm_key])
    atm_ngb_syms = list(map(atm_sym_dct.__getitem__, atm_ngb_keys))
    srt = formula.argsort_symbols(atm_ngb_syms, syms=('H', 'C'))
    atm_ngb_keys = list(map(atm_ngb_keys.__getitem__, srt))

    atm_keys = [atm_key] + atm_ngb_keys

    zma_key_dct = {
        atm_key: zma_key
        for zma_key, atm_key in enumerate(atm_keys)
    }

    syms = list(map(atm_sym_dct.__getitem__, atm_keys))

    natms = len(atm_keys)

    key_mat = [[None, None, None], [0, None, None], [0, 1, None], [0, 1, 2],
               [0, 1, 2]][:natms]

    name_mat = [[None, None, None], ['R1', None, None], ['R2', 'A2', None],
                ['R3', 'A3', 'D3'], ['R4', 'A4', 'D4']][:natms]

    atm_hyb = resonance_dominant_atom_hybridizations(gra)[atm_key]

    # z-matrix coordinate values
    val_dct = {}

    # determine bond distances
    for dist_name, atm_ngb_key in zip(['R1', 'R2', 'R3', 'R4'], atm_ngb_keys):
        dist_val = _bond_distance(gra, atm_key, atm_ngb_key)
        val_dct[dist_name] = dist_val

    # determine bond angles and dihedral angles
    if atm_hyb == 3:
        # for sp3 atoms, use z-matrix for a tetrahedral structure
        val_dct.update({
            'A2': TET_ANG,
            'A3': TET_ANG,
            'A4': TET_ANG,
            'D3': +TRI_ANG,
            'D4': -TRI_ANG
        })
    elif atm_hyb == 2:
        # for sp2 atoms, use z-matrix for a trigonal planar structure
        val_dct.update({'A2': TRI_ANG, 'A3': TRI_ANG, 'D3': LIN_ANG})
    elif atm_hyb == 1 and natms > 2:
        # for sp1 atoms, uze z-matrix for a linear structure

        # if there's only one neighbor, we don't need to set any angles at all
        assert natms == 3

        # insert a dummy atom
        syms.insert(2, 'X')
        key_mat.append([0, 2, 1])
        name_mat.append(['R3', 'A3', 'D3'])

        # note: we need to insert the dummy atom bond distance at R2 and shift
        # over the current value
        val_dct.update({
            'R2': 1.0,
            'R3': val_dct['R2'],
            'A2': RIT_ANG,
            'A3': RIT_ANG,
            'D3': LIN_ANG
        })

        gra, dummy_atm_key = add_bonded_atom(gra, 'X', atm_key, bnd_ord=0)

        zma_key_dct[dummy_atm_key] = 2
        zma_key_dct[atm_keys[2]] = 3

    vma = automol.vmatrix.from_data(syms, key_mat, name_mat)

    names = automol.vmatrix.names(vma)
    val_dct = {name: val_dct[name] for name in names}

    zma = automol.zmatrix.from_data(syms, key_mat, name_mat, val_dct)

    gra_with_dummies = gra
    return zma, zma_key_dct, dummy_atm_key, gra_with_dummies