Exemplo n.º 1
0
def process_spins(trajectory_fnms):
    newtrajectory_fnms = []
    logger.info("Pre-processing spins of trajectories")
    for i in range(len(trajectory_fnms)):
        M = Molecule(trajectory_fnms[i])

        # Read in the charge and spin on the whole system.
        srch = lambda s: np.array([
            float(
                re.search(
                    '(?<=%s )[-+]?[0-9]*\.?[0-9]*([eEdD][-+]?[0-9]+)?' % s, c).
                group(0)) for c in M.comms
            if all([k in c for k in ('charge', 'sz')])
        ])
        Chgs = srch('charge')  # An array of the net charge.
        SpnZs = srch('sz')  # An array of the net Z-spin.

        chg, chgpass = extract_int(Chgs,
                                   0.3,
                                   1.0,
                                   label="charge",
                                   verbose=False)
        spn, spnpass = extract_int(abs(SpnZs),
                                   0.3,
                                   1.0,
                                   label="spin-z",
                                   verbose=False)

        nproton = sum([Elements.index(j) for j in M.elem])
        nelectron = nproton + chg
        # Calculate the new spins if spins are inconsistent. Extracts all spin values from trajectory file, and uses
        # the absolute values of all spin/charge-appropriate rounded spin values.
        if not spnpass:
            logger.info("Generating new spins for " + trajectory_fnms[i])
            newspins = []
            for spinnew in SpnZs:
                spinnew = int(round(spinnew))
                if ((nelectron - spinnew) / 2) * 2 != (nelectron - spinnew):
                    continue
                if abs(spinnew) in newspins:
                    continue
                else:
                    newspins.append(abs(spinnew))
            # Write new trajectory files for new spins and add to trajectory list
            for spinrpl in newspins:
                for j in range(len(M.comms)):
                    M.comms[j] = re.sub(
                        '(?<=%s )[-+]?[0-9]*\.?[0-9]*([eEdD][-+]?[0-9]+)?' %
                        "sz", str(float(spinrpl)), M.comms[j])
                newfilename = os.path.splitext(
                    trajectory_fnms[i])[0] + "_SZ" + str(
                        spinrpl) + os.path.splitext(trajectory_fnms[i])[1]
                M.write(newfilename)
                newtrajectory_fnms.append(newfilename)
            # Move original trajectory file to *.parent and exclude it from the trajectory list
            # os.rename(trajectory_fnms[i], trajectory_fnms[i]+".parent")
        else:
            newtrajectory_fnms.append(trajectory_fnms[i])
    return newtrajectory_fnms
Exemplo n.º 2
0
def load_bondorder(R, P):
    """
    Extracts QM bond order information from a calculation.  This
    actually reads the Mayer bond order matrix from a Q-Chem output
    file.  This function looks funny because I implemented it after
    some calculations already started running. :P Therefore it checks
    to see if the ".bnd" file exists, and if not, it creates one.

    Intended to be run in a directory with transition-state.tar.bz2
    obviously.
    
    Parameters
    ----------
    R, P: Molecule
        Molecule objects for the reactant and product.
    """
    for bz2 in ['transition-state.tar.bz2', 'freezing-string.tar.bz2']:
        extract_tar(bz2, [
            'irc_reactant.out', 'irc_product.out', 'irc_reactant.bnd',
            'irc_product.bnd'
        ])
    if not os.path.exists('irc_reactant.bnd'):
        if os.path.exists('irc_reactant.out'):
            M = Molecule('irc_reactant.out')
            if hasattr(M, 'qm_bondorder'):
                bo_reactant = Molecule('irc_reactant.out').qm_bondorder
                np.savetxt('irc_reactant.bnd', bo_reactant, fmt="%8.3f")
            else:
                logger.info("Warning: No QM bond order for reactant")
                bo_reactant = np.zeros((R.na, R.na))
        else:
            logger.info("Warning: No QM bond order for reactant")
            bo_reactant = np.zeros((R.na, R.na))
    else:
        bo_reactant = np.loadtxt('irc_reactant.bnd')
    if not os.path.exists('irc_product.bnd'):
        if os.path.exists('irc_product.out'):
            M = Molecule('irc_product.out')
            if hasattr(M, 'qm_bondorder'):
                bo_product = Molecule('irc_product.out').qm_bondorder
                np.savetxt('irc_product.bnd', bo_product, fmt="%8.3f")
            else:
                logger.info("Warning: No QM bond order for product")
                bo_product = np.zeros((R.na, R.na))
        else:
            logger.info("Warning: No QM bond order for product")
            bo_product = np.zeros((P.na, P.na))
    else:
        bo_product = np.loadtxt('irc_product.bnd')
    R.qm_bondorder = bo_reactant
    P.qm_bondorder = bo_product
Exemplo n.º 3
0
def main():
    # Parse user input.
    args = parse_user_input()
    # Load IRC coordinates.
    M = Molecule(args.xyz)
    M.load_popxyz(args.pop)
    # Rebuild bonds for the reactant (0th frame).
    R = M[0]
    R.build_topology()
    # Rebuild bonds for the product (last frame).
    P = M[-1]
    P.build_topology()
    # Don't draw reaction if the reactant and product are the same.
    if MolEqual(R, P):
        logger.info(
            "Exiting because reactant and product molecules are the same")
        sys.exit()
    # Load Mayer bond order matrices.
    load_bondorder(R, P)
    # Create SVG drawings of the reactant and product.
    canr, strr = make_obmol(R, "reactant")
    canp, strp = make_obmol(P, "product")
    # IRC energy.
    ArcE = np.loadtxt(args.energy)
    E = ArcE[:, 1]
    # "Canonicalize" the reaction direction.
    fwd = True
    if max([len(m.L())
            for m in R.molecules]) > max([len(m.L()) for m in P.molecules]):
        # Reactions should go in the direction of increasing molecule size.
        fwd = False
    elif (max([len(m.L()) for m in R.molecules]) == max(
        [len(m.L()) for m in P.molecules])) and (E[0] < E[-1]):
        # If molecules on both sides are the same size, go in the exothermic direction.
        fwd = False
    if fwd:
        shutil.copy2("irc.nrg", "plot.nrg")
        with open("reaction.can", "w") as f:
            print >> f, "%s>>%s" % (canr, canp)
    else:
        ArcE = ArcE[::-1]
        ArcE[:, 0] *= -1
        ArcE[:, 0] -= ArcE[:, 0][0]
        ArcE[:, 1] -= ArcE[:, 1][0]
        np.savetxt("plot.nrg",
                   ArcE,
                   fmt="% 14.6f",
                   header="Arclength(Ang) Energy(kcal/mol)")
        strr, strp = strp, strr
        E = E[::-1]
        with open("reaction.can", "w") as f:
            print >> f, "%s>>%s" % (canp, canr)
    # This string looks something like C2H2 + H2O -> C2H4O.
    # The funny character is a Unicode entity for the "right arrow"
    strrxn = strr + ' &#10230; ' + strp
    # Create the components of the final image.
    # First write a text box with the chemical equation.
    with open("reaction_text.svg", "w") as f:
        print >> f, svgrect.format(text=strrxn,
                                   frgb="255,210,80",
                                   srgb="255,165,128")
    # Next write a text box with the charge and multiplicity.
    net_charge = int(round(sum(M.qm_mulliken_charges[0])))
    net_mult = int(abs(round(sum(M.qm_mulliken_spins[0]))) + 1)
    with open("chargemult_text.svg", "w") as f:
        print >> f, svgrect.format(text="Charge = %i ; Multiplicity = %i" %
                                   (net_charge, net_mult),
                                   frgb="194,225,132",
                                   srgb="154,205,50")
    # Write a text box with the reaction energy and barrier height.
    with open("energy_text.svg", "w") as f:
        print >> f, svgrect.format(
            text="&#916;E = %.2f kcal ; E&#8336; = %.2f kcal" %
            (E[-1] - E[0], np.max(E)),
            frgb="142,200,255",
            srgb="30,144,255")
    # Run script to generate energy diagram plot (uses Gnuplot).
    _exec("plot-rc.sh", print_command=False)
    # Write a text heading with the location of the calculation on disk.
    with open("folder_text.svg", "w") as f:
        print >> f, svgtext.format(text="Path: %s" % os.getcwd().split(
            os.environ['HOME'])[-1].split("Refinement")[-1].strip('/'))
    # Print some skeleton SVG files (actually part of this script).
    with open("arrow.svg", "w") as f:
        print >> f, svgarrow
    with open("base.svg", "w") as f:
        print >> f, svgbase
    # Finally, compose the image.
    compose(fwd)