Example #1
0
 def as_jmol(self, index, output='extent'):
     pse = PeriodicTable()
     coords = self.data.atomcoords[index]
     with open(output + '.xyz', 'w') as fout:
         fout.write('%d\n\n' % len(coords))
         for atomno, coord in zip(self.data.atomnos, coords):
             fout.write('%s %f %f %f\n' %
                        (tuple(pse.element[atomno]) + tuple(coord)))
     with open(output + '.spt', 'w') as fout:
         fout.write('load "%s.xyz"\n' % output)
         fout.write('draw POLYGON ')
         fout.write('8 ')
         origin = self.lower_limit
         diagonal = self.upper_limit - self.lower_limit
         vertices = []
         for i in ((0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1),
                   (1, 0, 1), (1, 1, 1), (0, 1, 1)):
             vertex = origin + np.asarray(i) * diagonal
             vertices.append(vertex)
             fout.write('{%f %f %f} ' % tuple(vertex))
         fout.write(
             '6 [0 1 2  3] [2 3 0  3] [0 4 5  3] [1 5 6  3] [2 6 7  3] [3 7 4  3] '
         )
         fout.write('mesh nofill\n')
     with open(output + '_vertices.xyz', 'w') as fout:
         fout.write('%d\n\n' % len(vertices))
         for v in vertices:
             fout.write('C %f %f %f\n' % tuple(v))
Example #2
0
def make_chemical_formula(d):
    periodic_obj = PeriodicTable()
    try:
        atom_dict = {}
        atomsymbols = []
        for x in d["attributes"]["atomnos"]:
            atomsymbols.append(periodic_obj.element[x])
            if x in atom_dict:
                atom_dict[x] += 1
            else:
                atom_dict[x] = 1
        atom_arr = []
        for x in atom_dict:
            atom_arr.append({"atomno": x, "count": atom_dict[x]})
        atom_arr.sort(key=lambda x: x["atomno"])
        formula_dict = {}
        formula_str = ""
        for x in atom_arr:
            elem = periodic_obj.element[x["atomno"]]
            formula_dict[elem] = x["count"]
            formula_str = formula_str + elem + " " + str(x["count"]) + " "
        d["formula"] = formula_dict
        d["formula_string"] = formula_str[:-1]
        d["attributes"]["atomsymbols"] = atomsymbols
        massnos = [round(x) for x in d["attributes"]["atommasses"]]
        d["attributes"]["massnos"] = massnos
    except:
        pass
Example #3
0
    def __init__(self, ccdata, jobfilename=None, terse=False,
                 *args, **kwargs):
        """Initialize the Writer object.

        This should be called by a subclass in its own __init__ method.

        Inputs:
          ccdata - An instance of ccData, parsed from a logfile.
          jobfilename - The filename of the parsed logfile.
          terse - Whether to print the terse version of the output file - currently limited to cjson/json formats
        """

        self.ccdata = ccdata
        self.jobfilename = jobfilename
        self.terse = terse

        self.pt = PeriodicTable()

        # Open Babel isn't necessarily present.
        if has_openbabel:
            # Generate the Open Babel/Pybel representation of the molecule.
            # Used for calculating SMILES/InChI, formula, MW, etc.
            self.obmol, self.pbmol = self._make_openbabel_from_ccdata()
            self.bond_connectivities = self._make_bond_connectivity_from_openbabel(self.obmol)

        self._check_required_attributes()
Example #4
0
def xyzfile(xyzfile, ccxyz=False):
    """Parse xyzfile to ccData or ccData_xyz object"""
    if not type(xyzfile) == str:
        print(xzyfile, "is not a xyzfilename")
        raise

    attributes = {}
    ptable = PeriodicTable()

    with open(xyzfile, 'r') as handle:
        lines = handle.readlines()

        charge, mult = _chargemult(lines[1])

        geometry = [x.split() for x in lines[2:]]
        coordinates = [x[1:] for x in geometry]
        atomnos = [ptable.number[x[0]] for x in geometry]
        attributes['atomcoords'] = [np.array(coordinates)]
        attributes['atomnos'] = np.array(atomnos)
        attributes['natom'] = len(atomnos)
        elements = [pt.Element[x] for x in atomnos]
        attributes['atommasses'] = [pt.Mass[x] for x in elements]

        if ccxyz:
            # Custom ccData_xyz attributes
            elements = [x[0] for x in geometry]
            attributes['elements'] = elements
            attributes['comment'] = lines[1]
            attributes['filename'] = os.path.split(xyzfile.rstrip())[1]
            ccObject = ccData_xyz(attributes=attributes)
        else:
            ccObject = ccData(attributes=attributes)

    return ccObject
def extract_xyz_geometries(xyz_file):
    """Extract xyz geometries from files in QM9"""
    coordinates = list()
    atoms = list()

    # Open the file, then retrieve the first two lines for the properties included.
    # With natoms, read all useful lines to get the coordinates, splitting them between
    # atom nuclei on one list and xyz coordinates on the other. See qm9_readme for details
    n_atoms = int(xyz_file.readline())
    xyz_file.readline()  # Property line. Do not use.

    for line in xyz_file.readlines()[0:n_atoms]:
        line = line.split(b"\t")
        line = [elem.decode("utf-8") for elem in line]
        atoms.append(str(line[0]))
        coords = line[1:4]
        for j, word in enumerate(coords):
            if "*^" in word:  # Some values are written as powers of 10: e.g. 1.999*^-6.
                values = re.split(r"\*\^", word)
                coords[j] = float(values[0]) * pow(10, int(values[1]))
            else:
                coords[j] = float(word)

        coordinates.append(coords)

    # Cleanup atoms list thanks to cclib PeriodicTable, convert them to atomic number
    periodic_table = PeriodicTable()
    elements_list = [periodic_table.number[atom] for atom in atoms]

    # Build the Molecule object and return it
    molecule = Molecule(coordinates, elements_list)
    return molecule
Example #6
0
def makepyscf(data, charge=0, mult=1):
    """Create a Pyscf Molecule."""
    _check_pyscf(_found_pyscf)
    mol = gto.Mole(
        atom=[
            ["{}".format(data.atomnos[i]), data.atomcoords[-1][i]]
            for i in range(data.natom)
        ],
        unit="Angstrom",
        charge=charge,
        multiplicity=mult,
    )
    inputattr = data.__dict__
    pt = PeriodicTable()
    if "gbasis" in inputattr:
        basis = {}  # object for internal PySCF format
        uatoms, uatoms_idx = np.unique(
            data.atomnos, return_index=True
        )  # find unique atoms
        for idx, i in enumerate(uatoms_idx):
            curr_atom_basis = data.gbasis[i]
            for jdx, j in enumerate(curr_atom_basis):
                curr_l = j[0]
                curr_e_prim = j[1]
                new_list = [l_sym2num["{}".format(curr_l)]]
                new_list += curr_e_prim
                if not "{}".format(pt.element[uatoms[idx]]) in basis:
                    basis["{}".format(pt.element[uatoms[idx]])] = [new_list]
                else:
                    basis["{}".format(pt.element[uatoms[idx]])].append(new_list)
        mol.basis = basis
    return mol
    def build_footer(self):
        """
            Builds the bottom part used for the Gaussian calculation.

            List of strings.
            """
        footer = []

        # Basis set is the same for all elements. No ECP either.
        # Remove duplicates, and convert to element name
        periodic_table = PeriodicTable()
        elements = [periodic_table.element[el] for el in list(set(self.molecule.elements_list))]

        elements = " ".join(elements)
        basisset = self.gaussian_args["basisset"]
        footer.append(elements + " 0")
        footer.append(basisset)
        footer.append("****")
        footer.append("")

        # footer.append("$NBO")
        # # NBO_FILES should be updated to something more useful
        # footer.append("FILE=NBO_FILES")
        # footer.append("PLOT")
        # footer.append("$END")

        logging.debug("Footer: \n %s", "\n".join(footer))
        return footer
Example #8
0
 def testcorrect(self):
     """Is coreelectrons equal to what it should be?"""
     pt = PeriodicTable()
     ans = []
     for x in self.data.atomnos:
         ans.append(self.coredict[pt.element[x]])
     ans = numpy.array(ans, "i")
     self.assertArrayEquals(self.data.coreelectrons, ans)
Example #9
0
    def print_gzmat(self):
        """Print Gaussian Z-Matrix Format
        e.g.

        0  3
        C
        O  1  r2
        C  1  r3  2  a3
        Si 3  r4  1  a4  2  d4
        ...
        Variables:
        r2= 1.1963
        r3= 1.3054
        a3= 179.97
        r4= 1.8426
        a4= 120.10
        d4=  96.84
        ...
        """
        pt = PeriodicTable()

        print('#', self.filename, "\n")
        print(self.comment)

        print(self.comment, end='')
        for i in range(len(self.atomnos)):
            idx = str(i + 1) + " "
            if i >= 3:
                print(pt.element[self.atomnos[i]], "",
                      self.connectivity[i] + 1, " r" + idx,
                      self.angleconnectivity[i] + 1, " a" + idx,
                      self.dihedralconnectivity[i] + 1, " d" + idx.rstrip())
            elif i == 2:
                print(pt.element[self.atomnos[i]], "",
                      self.connectivity[i] + 1, " r" + idx,
                      self.angleconnectivity[i] + 1, " a" + idx.rstrip())
            elif i == 1:
                print(pt.element[self.atomnos[i]], "",
                      self.connectivity[i] + 1, " r" + idx.rstrip())
            elif i == 0:
                print(pt.element[self.atomnos[i]])

        print("Variables:")

        for i in range(1, len(self.atomnos)):
            idx = str(i + 1) + "="
            if i >= 3:
                print("%s" % "r" + idx, "%6.4f" % self.distances[i])
                print("%s" % "a" + idx, "%6.2f" % self.angles[i])
                print("%s" % "d" + idx, "%6.2f" % self.dihedrals[i])
            elif i == 2:
                print("%s" % "r" + idx, "%6.4f" % self.distances[i])
                print("%s" % "a" + idx, "%6.2f" % self.angles[i])
            elif i == 1:
                print("%s" % "r" + idx, "%6.4f" % self.distances[i])
Example #10
0
    def xyz_geometry(self):
        """Returns geometry in XYZ format"""
        periodic_table = PeriodicTable()

        xyz_geometry = [
            " ".join([periodic_table.element[self.elements_list[i]].ljust(5)] +
                     ["{:.6f}".format(s).rjust(25) for s in atom])
            for i, atom in enumerate(self.coordinates)
        ]

        return xyz_geometry
def list_elements(input_file):
    """
    Return a list of all unique elements used in the computation.

    The list will look like:
    ['C', 'H', 'N', 'P']
    """
    file = ccread(input_file)
    atoms = dict.fromkeys(file.atomnos.tolist())
    periodic_table = PeriodicTable()
    atom_list = [periodic_table.element[i] for i in atoms]
    return atom_list
def atom_types(input_file):
    """
    Return a list of all atom types in the right order.

    The list will look like:
    ['C', 'H', 'H', 'H', 'C', 'N', 'P']
    """
    file = ccread(input_file)
    atoms = file.atomnos.tolist()
    periodic_table = PeriodicTable()
    atom_list = [periodic_table.element[i] for i in atoms]
    return atom_list
Example #13
0
 def _makeatomnames(self):
     """Create unique names for the atoms"""
     pt = PeriodicTable()
     d = {}
     names = []
     for atomno in self.atomnos:
         if atomno in d:
             d[atomno] += 1
         else:
             d[atomno] = 1
         names.append(pt.element[atomno] + str(d[atomno]))
     self.atomnames = names
Example #14
0
def XYZ_data(d):
    periodic_obj = PeriodicTable()
    xyz_data = ""
    try:
        xyz_data += str(d["natom"]) + "\n\n"
        for atom_row in list(zip(d["atomnos"], d["atomcoords"][0])):
            elem = periodic_obj.element[atom_row[0]]
            coords_text = " ".join(list(map(str, atom_row[1])))
            xyz_data += elem + " " + coords_text + "\n"
    except:
        xyz_data = ""
    return xyz_data
Example #15
0
def makebiopython(atomcoords, atomnos):
    """Create a list of BioPython Atoms.

    This creates a list of BioPython Atoms suitable for use by
    Bio.PDB.Superimposer, for example.
    """
    pt = PeriodicTable()
    bioatoms = []
    for coords, atomno in zip(atomcoords, atomnos):
        symbol = pt.element[atomno]
        bioatoms.append(
            Atom(symbol, coords, 0, 0, 0, symbol, 0, symbol.upper()))
    return bioatoms
Example #16
0
def makebiopython(atomcoords, atomnos):
    """Create a list of BioPython Atoms.

    This creates a list of BioPython Atoms suitable for use by
    Bio.PDB.Superimposer, for example.
    """
    if not _found_biopython:
        raise ImportError("You must install `biopython` to use this function")
    pt = PeriodicTable()
    bioatoms = []
    for coords, atomno in zip(atomcoords, atomnos):
        symbol = pt.element[atomno]
        bioatoms.append(Atom(symbol, coords, 0, 0, 0, symbol, 0, symbol.upper()))
    return bioatoms
Example #17
0
def multixyzfile(multixyzfile):
    """Parse multixyzfile to list of ccData objects"""
    assert type(multixyzfile) == str

    attributeslist = []

    ptable = PeriodicTable()

    # Check that the file is not empty, if it is not, parse away!
    if os.stat(multixyzfile).st_size == 0:
        raise EOFError(multixyzfile + " is empty")
    else:
        with open(multixyzfile, 'r') as handle:
            attributeslist = []
            lines = handle.readlines()
            filelength = len(lines)
            idx = 0
            while True:
                attributes = {}
                atomcoords = []
                atomnos = []

                # Get number of atoms and charge/mult from comment line
                numatoms = int(lines[idx])
                charge, mult = _chargemult(lines[idx + 1])

                for line in lines[idx + 2:numatoms + idx + 2]:
                    atomgeometry = [x for x in line.split()]
                    atomnos.append(ptable.number[atomgeometry[0]])
                    atomcoords.append([float(x) for x in atomgeometry[1:]])
                idx = numatoms + idx + 2
                attributes['charge'] = charge
                attributes['mult'] = mult
                attributes['atomcoords'] = [np.array(atomcoords)]
                attributes['atomnos'] = np.array(atomnos)
                attributeslist.append(attributes)

                # Break at EOF
                if idx >= filelength:
                    break

        print('Number of conformers parsed:', len(attributeslist))

        ccdatas = [ccData(attributes=attrs) for attrs in attributeslist]
        return ccdatas
Example #18
0
def main():
    pse = PeriodicTable()
    formula = {}
    log = ccopen(sys.argv[1])
    data = log.parse()
    for atom in data.atomnos:
        formula[atom] = formula.setdefault(atom, 0) + 1
    string = []
    exclude = set()
    if 6 in formula:
        string.append('C%d' % formula[6])
        exclude.add(6)
        if 1 in formula:
            string.append('H%d' % formula[1])
            exclude.add(1)
    elements = set(formula.keys()) - exclude
    for element in sorted([pse.element[atom] for atom in elements]):
        string.append(element + str(formula[pse.number[element]]))
    print(' '.join(string))
Example #19
0
def make_formula_string(elems, elem_counts=[]):
    periodic_obj = PeriodicTable()
    formula_arr = []
    if type(elems) is dict:
        for x in elems:
            formula_arr.append({
                "atomno": periodic_obj.number[x],
                "symbol": x,
                "count": elems[x]
            })
    else:
        for (x, y) in zip(elems, elem_counts):
            formula_arr.append({
                "atomno": periodic_obj.number[x],
                "symbol": x,
                "count": y
            })
    formula_arr.sort(key=lambda x: x["atomno"])
    formula_arr = [x["symbol"] + " " + str(x["count"]) for x in formula_arr]
    formula_string = " ".join(formula_arr)
    return formula_string
Example #20
0
def makebiopython(atomcoords, atomnos):
    """Create a list of BioPython Atoms.

    This creates a list of BioPython Atoms suitable for use
    by Bio.PDB.Superimposer, for example.

    >>> import numpy
    >>> from Bio.PDB.Superimposer import Superimposer
    >>> atomnos = numpy.array([1,8,1],"i")
    >>> a = numpy.array([[-1,1,0],[0,0,0],[1,1,0]],"f")
    >>> b = numpy.array([[1.1,2,0],[1,1,0],[2,1,0]],"f")
    >>> si = Superimposer()
    >>> si.set_atoms(makebiopython(a,atomnos),makebiopython(b,atomnos))
    >>> print si.rms
    0.29337859596
    """
    pt = PeriodicTable()
    bioatoms = []
    for coords, atomno in zip(atomcoords, atomnos):
        bioatoms.append(Atom(pt.element[atomno], coords, 0, 0, 0, 0, 0))
    return bioatoms
Example #21
0
    def stoichiometry(self):
        """Return the stoichemistry of the object according to the Hill system"""
        cclib_pt = PeriodicTable()
        elements = [cclib_pt.element[ano] for ano in self.data.atomnos]
        counts = {el: elements.count(el) for el in set(elements)}

        formula = ""
        elcount = lambda el, c: "%s%i" % (el, c) if c > 1 else el
        if 'C' in elements:
            formula += elcount('C', counts['C'])
            counts.pop('C')
            if 'H' in elements:
                formula += elcount('H', counts['H'])
                counts.pop('H')
        for el, c in sorted(counts.items()):
            formula += elcount(el, c)

        if getattr(self.data, 'charge', 0):
            magnitude = abs(self.data.charge)
            sign = "+" if self.data.charge > 0 else "-"
            formula += "(%s%i)" % (sign, magnitude)
        return formula
Example #22
0
    def __init__(self, attributes={}):
        """Adding some new attributes for xyzfiles"""

        self.newcoords = None
        self.distancematrix = None

        # Internal Coordinate Connectivity
        self.connectivity = None
        self.angleconnectivity = None
        self.dihedralconnectivity = None

        # Internal Coordinates
        self.distances = None
        self.angles = None
        self.dihedrals = None

        self._attrtypes['comment'] = str
        self._attrlist.append('comment')
        self._attrtypes['filename'] = str
        self._attrlist.append('filename')
        self._attrtypes['elements'] = list
        self._attrlist.append('elements')

        #self._attrtypes['distancematrix'] = np.ndarray
        #self._attrlist.append('distancematrix')
        #self._attrtypes['connectivity'] = list
        #self._attrlist.append('connectivity')

        super(ccData_xyz, self).__init__(attributes=attributes)

        # Initialize new data types if attributes were parsed as an original ccdata_xyz
        if not hasattr(self, 'elements'):
            pt = PeriodicTable()
            self.comment = '\n'
            self.filename = ''
            self.elements = []
            for atomno in self.atomnos:
                self.elements.append(pt.element[atomno])
Example #23
0
def makepyscf(data, charge=0, mult=1):
    """Create a Pyscf Molecule."""
    _check_pyscf(_found_pyscf)
    inputattrs = data.__dict__
    required_attrs = {"atomcoords", "atomnos"}
    missing = [x for x in required_attrs if not hasattr(data, x)]
    if missing:
        missing = " ".join(missing)
        raise MissingAttributeError(
            "Could not create pyscf molecule due to missing attribute: {}".
            format(missing))
    mol = gto.Mole(
        atom=[["{}".format(data.atomnos[i]), data.atomcoords[-1][i]]
              for i in range(data.natom)],
        unit="Angstrom",
        charge=charge,
        multiplicity=mult)
    inputattr = data.__dict__
    pt = PeriodicTable()
    if "gbasis" in inputattr:
        basis = {}  # object for internal PySCF format
        uatoms, uatoms_idx = np.unique(data.atomnos,
                                       return_index=True)  # find unique atoms
        for idx, i in enumerate(uatoms_idx):
            curr_atom_basis = data.gbasis[i]
            for jdx, j in enumerate(curr_atom_basis):
                curr_l = j[0]
                curr_e_prim = j[1]
                new_list = [l_sym2num["{}".format(curr_l)]]
                new_list += curr_e_prim
                if not "{}".format(pt.element[uatoms[idx]]) in basis:
                    basis["{}".format(pt.element[uatoms[idx]])] = [new_list]
                else:
                    basis["{}".format(
                        pt.element[uatoms[idx]])].append(new_list)
        mol.basis = basis
        mol.cart = True
    return mol
Example #24
0
    def __init__(self, ccdata, jobfilename=None,
                 *args, **kwargs):
        """Initialize the Writer object.

        This should be called by a subclass in its own __init__ method.

        Inputs:
          ccdata - An instance of ccData, parsed from a logfile.
          jobfilename - The filename of the parsed logfile.
        """

        self.ccdata = ccdata
        self.jobfilename = jobfilename

        self.pt = PeriodicTable()
        self.elements = [self.pt.element[Z] for Z in self.ccdata.atomnos]

        # Open Babel isn't necessarily present.
        if has_openbabel:
            # Generate the Open Babel/Pybel representation of the molecule.
            # Used for calculating SMILES/InChI, formula, MW, etc.
            self.obmol, self.pbmol = self._make_openbabel_from_ccdata()
            self.bond_connectivities = self._make_bond_connectivity_from_openbabel(self.obmol)
Example #25
0
def get_chromophore_info(calcdir, basis="None"):
    """
    calcdir: directory the output file resides in
    """
    outfile = glob.glob("{}/*.out".format(calcdir))[0]
    results = QChem(outfile).parse()
    n_states = len(results.etenergies)
    if False in results.etconv:
        print("Not converged!")
    # Reads in the TXT files with the transition density matrix
    ptr_files = sorted(glob.glob("{}/*.txt".format(calcdir)))[:n_states]
    ptr = []
    periodicT = PeriodicTable()
    for ptr_f in ptr_files:
        ptr_mat = np.loadtxt(ptr_f)
        assert ptr_mat.shape[0] == ptr_mat.shape[1]
        assert ptr_mat.ndim == 2
        ptr.append(ptr_mat)
    chrom = Chromophore(n_states, basis, results.atomcoords[0],
                        [periodicT.element[x]
                         for x in results.atomnos], results.charge,
                        convertor(results.etenergies, "eV", "hartree"),
                        results.etoscs, ptr, results.ettransdipmoms)
    return chrom
Example #26
0
    def __init__(self, source, *args, **kwargs):
        super().__init__(source, *args, **kwargs)

        self.pt = PeriodicTable()
Example #27
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Stdlib
from __future__ import division, print_function
import os
from cclib.parser.utils import convertor, PeriodicTable

PERIODIC_TABLE = PeriodicTable()

def new_filename(path):
    i = 0
    name, ext = os.path.splitext(path)
    while os.path.isfile(path):
        i += 1
        path = '{}_{}{}'.format(name, i, ext)
    return path
    """

    s = '{:3s} {:15.10f} {:15.10f} {:15.10f}'
    molecule_section = ['{} {}'.format(charge, multiplicity)]

    for element, coords, in zip(elements, geometry):
        molecule_section.append(s.format(element, *coords))

    return molecule_section


if __name__ == '__main__':

    args = getargs()

    pt = PeriodicTable()

    for outputfilename in args.outputfilename:

        job = cclib.io.ccopen(outputfilename)
        assert isinstance(job, cclib.parser.qchemparser.QChem)
        try:
            data = job.parse()
        # this is to deal with the Q-Chem parser not handling
        # incomplete SCF cycles properly
        except StopIteration:
            print('no output made: StopIteration in {}'.format(outputfilename))
            continue

        # Determine the name of the file we're writing.
        assert outputfilename.endswith('.out')
Example #29
0
    def parse(self, **kwargs):
        """
        It uses cclib to get the output dictionary.
        Herein, we report all parsed data by ccli in output_dict which
        can be parsed further at workchain level.
        If it would be an optimization run, the relaxed structure also will
        be stored under relaxed_structure key.
        """
        opt_run = False

        try:
            out_folder = self.retrieved
        except NotExistent:
            return self.exit_codes.ERROR_NO_RETRIEVED_FOLDER

        fname_out = self.node.process_class._OUTPUT_FILE  #pylint: disable=protected-access
        fname_relaxed = self.node.process_class._RELAX_COORDS_FILE  #pylint: disable=protected-access
        # fname_traj = self.node.process_class._TRAJECTORY_FILE  #pylint: disable=protected-access
        # fname_hessian = self.node.process_class._HESSIAN_FILE  #pylint: disable=protected-access

        if fname_out not in out_folder._repository.list_object_names():  #pylint: disable=protected-access
            raise OutputParsingError('Orca output file not retrieved')

        parsed_obj = io.ccread(
            os.path.join(out_folder._repository._get_base_folder().abspath,
                         fname_out))  #pylint: disable=protected-access

        parsed_dict = parsed_obj.getattributes()

        def _remove_nan(parsed_dictionary: dict) -> dict:
            """cclib parsed object may contain nan values in ndarray.
            It will results in an exception in aiida-core which comes from
            json serialization and thereofore dictionary cannot be stored.
            This removes nan values to remedy this issue.
            See:
            https://github.com/aiidateam/aiida-core/issues/2412
            https://github.com/aiidateam/aiida-core/issues/3450

            Args:
                parsed_dictionary (dict): Parsed dictionary from `cclib`

            Returns:
                dict: Parsed dictionary without `NaN`
            """

            for key, value in parsed_dictionary.items():
                if isinstance(value, np.ndarray):
                    non_nan_value = np.nan_to_num(value,
                                                  nan=123456789,
                                                  posinf=2e308,
                                                  neginf=-2e308)
                    parsed_dictionary.update({key: non_nan_value})

            return parsed_dictionary

        output_dict = _remove_nan(parsed_dict)

        keywords = output_dict['metadata']['keywords']

        #opt_pattern = re.compile('(GDIIS-)?[CZ?OPT]', re.IGNORECASE)

        #if any(re.match(opt_pattern, keyword) for keyword in keywords):
        #opt_run = True
        opt_run = False
        for keyword in keywords:
            if 'opt' in keyword.lower():
                opt_run = True

        if opt_run:
            relaxed_structure = StructureData(
                pymatgen_molecule=Molecule.from_file(
                    os.path.join(
                        out_folder._repository._get_base_folder().abspath,
                        fname_relaxed))  #pylint: disable=protected-access
            )
            # relaxation_trajectory = SinglefileData(
            #     file=os.path.join(out_folder._repository._get_base_folder().abspath, fname_traj)  #pylint: disable=protected-access
            # )
            self.out('relaxed_structure', relaxed_structure)
            # self.out('relaxation_trajectory', relaxation_trajectory)

        pt = PeriodicTable()  #pylint: disable=invalid-name

        output_dict['elements'] = [
            pt.element[Z] for Z in output_dict['atomnos'].tolist()
        ]

        self.out('output_parameters', Dict(dict=output_dict))

        return ExitCode(0)
Example #30
0
def json2latex2pdf(json, mode="clean"):

    # on construit le nom du rapport
    #rapFile = "_TEX_report"
    name = json["comp_details"]["general"]["job_type"][0]
    rapFile = (name + "_report")

    #####################################################################
    #                                                                   #
    ## production du rapport en .tex                                 #
    #                                                                   #
    #####################################################################

    ### create document and import needed packages

    doc = (Document("article"))

    # packages
    doc.packages.append(Package('datetime'))
    doc.packages.append(NoEscape(r'\usepackage[margin=2cm]{geometry}'))
    doc.packages.append(NoEscape(r'\usepackage{extramarks}'))
    doc.packages.append(NoEscape(r'\usepackage{fancyhdr}'))
    doc.packages.append(NoEscape(r'\usepackage{svg}'))
    doc.packages.append(NoEscape(r'\usepackage[utf8]{inputenc}'))
    doc.packages.append(NoEscape(r'\usepackage{titlesec}'))
    #
    doc.packages.append(NoEscape(r'\pagestyle{fancy}'))
    doc.packages.append(NoEscape(r'\fancyhf{}'))
    # define header / footer texts
    doc.packages.append(NoEscape(r'\lhead{MOLECULAR CALCULATION REPORT}'))
    doc.packages.append(NoEscape(r'\rhead{Generated by QuChemReport}'))
    doc.packages.append(NoEscape(r'\lfoot{\today ~  \currenttime}'))
    doc.packages.append(NoEscape(r'\rfoot{Page \thepage}'))
    doc.packages.append(NoEscape(r'\cfoot{}'))
    # redefine rules for header / footer
    doc.packages.append(NoEscape(r'\renewcommand\headrulewidth{0.4pt}'))
    doc.packages.append(NoEscape(r'\renewcommand\footrulewidth{0.4pt}'))
    # redefine section
    doc.packages.append(NoEscape(r'\definecolor{ufrblue}{RGB}{0,161,140}'))
    doc.packages.append(NoEscape(r'\definecolor{bordeau}{RGB}{125,31,31}'))
    doc.packages.append(
        NoEscape(
            r'\titleformat{name=\section}[block]{\sc\large}{}{0pt}{\colorsection}'
        ))
    doc.packages.append(
        NoEscape(r'\titlespacing*{\section}{0pt}{\baselineskip}{5pt}'))
    doc.packages.append(
        NoEscape(r'\titlespacing{\subsection}{4pt}{\baselineskip}{5pt}'))
    doc.packages.append(
        NoEscape(
            r'\newcommand{\colorsection}[1]{' +
            r'\colorbox{ufrblue}{\parbox{\dimexpr\textwidth-2\fboxsep}{\textcolor{white}{'
            + r'\textbf{{\thesection.\ #1}}}}}}'))

    ### section 1 : authorship TODO with the DB !
    #########################################
    #    with doc.create(Section('AUTHORSHIP')):
    #        with doc.create(LongTabu("X[1,l] X[2,l]")) as tab:
    #            tab.add_row(['Original file', json["authorship"]["log_file"]])
    #            tab.add_row(['Primary author',  json["authorship"]["primary_author"]])
    #            add_row_filter(tab, ['ORCID',  json["authorship"]["primary_author_orcid"]])
    #            add_row_filter(tab, ['Affiliation',  json["authorship"]["primary_author_affiliation"]])
    #            add_row_filter(tab, ['Publication DOI',  json["authorship"]["publication_DOI"]])

    ### section 2 : molecule

    with doc.create(Section('MOLECULE')):
        taillePng = "6cm"
        nomPng = "img-TOPOLOGY.png"
        if (not os.path.isfile("img-TOPOLOGY.png")):
            print("No PNG named " + nomPng + " found. STOP.\n")
            sys.exit()

        # figure with Chemical structure diagram
        doc.append(NoEscape(r'\begin{figure}[h]'))
        doc.append(NoEscape(r'\begin{center}'))
        doc.append(
            NoEscape(r'\includegraphics[width=' + taillePng + ']{' + nomPng +
                     '}'))
        doc.append(NoEscape(r'\end{center}'))
        doc.append(NoEscape(r'\vspace{-5mm}'))
        doc.append(
            NoEscape(
                r'\caption*{Chemical structure diagram with atomic numbering.}'
            ))
        doc.append(NoEscape(r'\end{figure}'))

        with doc.create(LongTabu("X[1,l] X[2,l]")) as mol_table:
            inchi = (json["molecule"]["inchi"])[0].rstrip().split("=")[-1]
            mol_table.add_row(['InChI', inchi])
            mol_table.add_row(['SMILES', json["molecule"]["smi"]])
            mol_table.add_row([
                'Monoisotopic mass',
                "%.5f Da" % json["molecule"]["monoisotopic_mass"]
            ])
            mol_table.add_row(['Formula', json["molecule"]["formula"]])
            mol_table.add_row(['Charge', json["molecule"]["charge"]])
            mol_table.add_row(
                ['Spin multiplicity', json["molecule"]["multiplicity"]])

    ### section 2 : computational details
    #########################################
    software = json["comp_details"]["general"]["package"]
    with doc.create(Section('COMPUTATIONAL DETAILS')):
        #with doc.create(Subsection('General parameters')) :
        with doc.create(LongTabu("X[1,l] X[1,l]")) as param_table:
            try:
                param_table.add_row([
                    'Software',
                    json["comp_details"]["general"]["package"] + ' (' +
                    json["comp_details"]["general"]["package_version"] + ')'
                ])
            except KeyError:
                pass
            param_table.add_row([
                'Computational method',
                json["comp_details"]["general"]["last_theory"]
            ])
            add_row_filter(
                param_table,
                ['Functional', json["comp_details"]["general"]["functional"]])
            try:
                add_row_filter(param_table, [
                    'Basis set name',
                    json["comp_details"]["general"]["basis_set_name"]
                ])
            except KeyError:
                pass
            param_table.add_row([
                'Number of basis set functions',
                json["comp_details"]["general"]["basis_set_size"]
            ])
            param_table.add_row([
                'Closed shell calculation',
                json["comp_details"]["general"]["is_closed_shell"]
            ])
            add_row_filter(param_table, [
                'Integration grid',
                json["comp_details"]["general"]["integration_grid"]
            ])
            add_row_filter(
                param_table,
                ['Solvent', json["comp_details"]["general"]["solvent"]])
            #  TODO Test if solvent = gas in this case no Solvent reaction filed method
            #          add_row_filter(param_table, ['Solvent reaction filed method',
            #                                         json["comp_details"]["general"]["solvent_reaction_field"]])

            scfTargets = json["comp_details"]["general"]["scf_targets"][-1]
            if software == "Gaussian":  # Gaussian or GAUSSIAN (upper/lower?
                param_table.add_row([
                    "Requested SCF convergence on RMS density matrix",
                    scfTargets[0]
                ])
                param_table.add_row([
                    "Requested SCF convergence on MAX density matrix",
                    scfTargets[1]
                ])
                param_table.add_row(
                    ["Requested SCF convergence on energy", scfTargets[2]])
            if software == "GAMESS":
                param_table.add_row(
                    ["Requested SCF convergence on density", scfTargets[0]])

            #with doc.create(Subsection('Thermochemistry and normal modes calculation parameters')) :
            ## TODO tester si freq
            try:
                add_row_filter(param_table, [
                    'Temperature',
                    "%.2f K" % json["comp_details"]["freq"]["temperature"]
                ])
            except:
                pass
            T_len = False

            try:
                len(json["comp_details"]["freq"]["temperature"])
            except KeyError:
                json["comp_details"]["freq"]["temperature"] = []
            except TypeError:
                T_len = True
                if T_len is True:
                    try:
                        add_row_filter(param_table, [
                            'Anharmonic effects',
                            json["comp_details"]["freq"]["anharmonicity"]
                        ])

                    except KeyError:
                        pass
            if (json["comp_details"]["freq"]["temperature"]) != []:
                try:
                    add_row_filter(param_table, [
                        'Anharmonic effects',
                        json["comp_details"]["freq"]["anharmonicity"]
                    ])

                except KeyError:
                    pass

            #with doc.create(Subsection('Excited states calculation parameters')) :
            try:
                add_row_filter(param_table, [
                    'Number of excited states',
                    json["comp_details"]["excited_states"]["nb_et_states"]
                ])
            except KeyError:
                pass

    ### section 3 : results
    #########################################
    with doc.create(Section('RESULTS')):
        with doc.create(Subsection('Wavefunction')):

            with doc.create(LongTabu("X[l] X[l]")) as wavef_table:
                wavef_table.add_row([
                    'Total molecular energy',
                    "%.5f Hartrees" %
                    json["results"]["wavefunction"]["total_molecular_energy"]
                ])
                homo_indexes = [
                    i + 1
                    for i in json["results"]["wavefunction"]["homo_indexes"]
                ]
                wavef_table.add_row([
                    'H**O number', ("%s" % homo_indexes)[1:-1]
                ])  # indice begins at 0, remove brackets

            MO_energies = json["results"]["wavefunction"]["MO_energies"][-1]
            homo_nb = json["results"]["wavefunction"]["homo_indexes"][-1]
            doc.append(NoEscape(r'\begin{center}'))
            with doc.create(Table()) as motab:
                doc.append(NoEscape(r'\centering'))
                with doc.create(Tabular('p{1cm}ccccp{1cm}')) as mo_table:
                    row_cells = MultiColumn(
                        6,
                        align='c',
                        data=
                        'Calculated energies for the frontier molecular orbitals (in eV)'
                    )
                    mo_table.add_row([row_cells])
                    mo_table.add_row(
                        ['', 'H**O-1', 'H**O', 'LUMO', 'LUMO+1', ''])
                    mo_table.add_hline()
                    mo_table.add_row([
                        '',
                        "%.2f" % MO_energies[homo_nb - 1],
                        "%.2f" % MO_energies[homo_nb],
                        "%.2f" % MO_energies[homo_nb + 1],
                        "%.2f" % MO_energies[homo_nb + 2], ''
                    ])
            doc.append(NoEscape(r'\end{center}'))

            # figure with MO
            #### TODO : test autosize
            try:
                if len(json["results"]["excited_states"]['MO_png_list']) > 0:
                    figure_MO_table(
                        doc, ["LU1", "LUMO", "H**O", "HO1"],
                        json["results"]["excited_states"]['MO_png_list'],
                        caption=
                        ("Representation of the frontier molecular orbitals with a cutoff "
                         "value of 0.05. From up to bottom: LUMO+1, LUMO, H**O, H**O-1."
                         ))
            except:
                pass

            # figure skel for Atom numbering scheme
            try:
                figure_two_col(doc,
                               "skel",
                               json["results"]["geometry"]['skel_png_list'],
                               caption="Atom numbering scheme.",
                               pos="h")
            except:
                pass

            # Mulliken partial charges table
            try:
                mulliken = json["results"]["wavefunction"][
                    "Mulliken_partial_charges"]
            except KeyError:
                mulliken = []
            if len(mulliken) != 0:
                mulliken = np.array(mulliken)
                mean_m = np.mean(mulliken)
                dev_m = np.std(mulliken)
                thres_max = mean_m + dev_m
                thres_min = mean_m - dev_m
                doc.append(NoEscape(r'\begin{center}'))
                ind = np.argsort(mulliken)
                with doc.create(Tabular('p{0.5cm}rrrp{0.5cm}')) as tableau:
                    row_cells = [
                        MultiColumn(
                            5,
                            align='c',
                            data='Most intense Mulliken atomic charges')
                    ]
                    tableau.add_row(row_cells)
                    row_cells = [
                        MultiColumn(5,
                                    align='c',
                                    data='mean = %.3f e, std = %.3f' %
                                    (mean_m, dev_m))
                    ]
                    tableau.add_row(row_cells)
                    tableau.add_row(
                        ["", "Atom", "number", "Mulliken charges", ""])
                    tableau.add_hline()

                    for ielt in ind:
                        if (mulliken[ielt] > thres_max) or (mulliken[ielt] <
                                                            thres_min):
                            tableau.add_row([
                                "",
                                PeriodicTable().element[json['molecule']
                                                        ["atoms_Z"][ielt]],
                                (1 + ielt),
                                "%.3f" % mulliken[ielt], ""
                            ])
                    tableau.add_hline()
                doc.append(NoEscape(r'\end{center}'))

            # figure MEP
            try:
                if len(json["results"]["wavefunction"]['MEP_png_list']) > 0:
                    figure_two_col(
                        doc,
                        "MEP",
                        json["results"]["wavefunction"]['MEP_png_list'],
                        caption=
                        ("Representation of the molecular electrostatic potential "
                         "at the distance of the van der Waals radii of the atoms. "
                         "The red/blue regions depict the most negative (-0.1) / "
                         "positive (+0.1) regions."),
                        pos="h")
            except:
                pass

        with doc.create(Subsection('Geometry')):
            ######## PB
            atomic = json["results"]["geometry"][
                "elements_3D_coords_converged"]
            oxtrf, oytrf, oxtrd, oytrd = ('N/A', 'N/A', 'N/A', 'N/A')

            ### TODO
            # if singlePoint==1 :
            #      doc.append('This calculation is a single point ')
            try:
                is_opt = json["results"]["geometry"]["OPT_DONE"] == True
            except KeyError:
                is_opt = []
            if is_opt:
                doc.append(
                    NoEscape(
                        r"This calculation is the result of a geometry optimization process."
                    ))
            else:
                doc.append(
                    NoEscape(
                        r"\textcolor{bordeau}{Warning : this calculation does not include a geometry "
                        "optimization process. This geometry may not be a stationary "
                        "point for those computational parameters. In this case, results must be "
                        "used with caution.}"))

            if is_opt:
                ### TODO tests gaussian
                doc.append(NoEscape(r'\begin{center}'))
                geomTargets = json["comp_details"]["geometry"][
                    "geometric_targets"]
                geomValues = json["results"]["geometry"]["geometric_values"][
                    -1]
                with doc.create(Tabular('p{0cm}lrrp{0cm}')) as param_table:
                    row_cells = [
                        MultiColumn(
                            5,
                            align='c',
                            data='Geometry optimization convergence criteria')
                    ]
                    param_table.add_row(row_cells)
                    param_table.add_row(["", "", "Value", "Threshold", ""])
                    param_table.add_hline()
                    if software == "Gaussian":
                        param_table.add_row([
                            "", "Maximum Force",
                            "%.6f" % geomValues[0],
                            "%.6f" % geomTargets[0], ""
                        ])
                        param_table.add_row([
                            "", "RMS Force",
                            "%.6f" % geomValues[1],
                            "%.6f" % geomTargets[1], ""
                        ])
                        param_table.add_row([
                            "", "Maximum Displacement",
                            "%.6f" % geomValues[2],
                            "%.6f" % geomTargets[2], ""
                        ])
                        param_table.add_row([
                            "", "RMS Displacement",
                            "%.6f" % geomValues[3],
                            "%.6f" % geomTargets[3], ""
                        ])
                    if software == "GAMESS":
                        # in Hartrees per Bohr
                        param_table.add_row([
                            "", "Maximum Force",
                            "%.5f" % geomValues[0],
                            "%.5f" % geomTargets[0], ""
                        ])
                        param_table.add_row([
                            "", "RMS Force",
                            "%.5f" % geomValues[1],
                            "%.5f" % geomTargets[1], ""
                        ])
                    param_table.add_hline()
                doc.append(NoEscape(r'\end{center}'))

            with doc.create(LongTabu("X[l] X[l]")) as geom_table:
                geom_table.add_row([
                    'Nuclear repulsion energy',
                    "%.5f Hartrees" % json["results"]["geometry"]
                    ["nuclear_repulsion_energy_from_xyz"]
                ])

            doc.append(NoEscape(r'\begin{center}'))
            with doc.create(Tabular('p{0.5cm}rrrrp{0.5cm}')) as tableau:
                row_cells = [
                    MultiColumn(
                        6,
                        align='c',
                        data='Cartesian atomic coordinates in Angstroms')
                ]
                tableau.add_row(row_cells)
                tableau.add_row([
                    "", "Atom",
                    MultiColumn(1, align='c', data='X'),
                    MultiColumn(1, align='c', data='Y'),
                    MultiColumn(1, align='c', data='Z'), ""
                ])
                tableau.add_hline()
                atoms = np.array(json["results"]["geometry"]
                                 ["elements_3D_coords_converged"]).reshape(
                                     (-1, 3))
                for i, a in enumerate(atoms):
                    tableau.add_row([
                        "",
                        PeriodicTable().element[json['molecule']["atoms_Z"]
                                                [i]],
                        "%.4f" % a[0],
                        "%.4f" % a[1],
                        "%.4f" % a[2], ""
                    ])
                tableau.add_hline()
            doc.append(NoEscape(r'\end{center}'))

        ### Freq
        #### TODO : tests

        with doc.create(Subsection('Thermochemistry and normal modes')):
            rtemper = json["comp_details"]["freq"]["temperature"]
            # ND-arrays
            try:
                vibrational_int = np.array(
                    json["results"]["freq"]["vibrational_int"])
            except KeyError:
                vibrational_int = []
            try:
                vibrational_freq = np.array(
                    json["results"]["freq"]["vibrational_freq"])
            except KeyError:
                vibrational_freq = []

            try:
                vibrational_sym = np.array(
                    json["results"]["freq"]["vibrational_sym"])
            except KeyError:
                vibrational_sym = []

            # filtering & orderering
            if len(vibrational_int) == 0:
                vibrational_int = []
            else:
                vib_filter = vibrational_int > 50.
                vib_order = np.argsort(vibrational_freq[vib_filter])[::-1]
                vibrational_int = vibrational_int[vib_filter][vib_order]
                vibrational_freq = vibrational_freq[vib_filter][vib_order]
                vibrational_sym = vibrational_sym[vib_filter][vib_order]

            if (len(vibrational_int) != 0) and (rtemper != "N/A"):
                with doc.create(LongTabu("X[l] X[l]")) as thermo_table:
                    thermo_table.add_row([
                        'Sum of electronic and zero-point energy in Hartrees',
                        json["results"]["freq"]["zero_point_energy"]
                    ])
                    thermo_table.add_row([
                        "Sum of electronic and thermal at "
                        "%f K energies in atomic units" % rtemper,
                        json["results"]["freq"]["electronic_thermal_energy"]
                    ])
                    thermo_table.add_row([
                        "Entropy at %f K in atomic units" % rtemper,
                        json["results"]["freq"]["entropy"]
                    ])
                    thermo_table.add_row([
                        "Enthalpy at %f K in atomic units" % rtemper,
                        json["results"]["freq"]["enthalpy"]
                    ])
                    thermo_table.add_row([
                        "Gibbs free energy at %f K in atomic units" % rtemper,
                        json["results"]["freq"]["free_energy"]
                    ])

                doc.append(
                    "Table of the most intense molecular vibrations (> 20 km/mol) (%d)\n"
                    % len(vibrational_int))
                with doc.create(Tabular('rrrc')) as tableau:
                    tableau.add_row(
                        ["", "Frequencies", "Intensity", "Symmetry"])
                    for i in range(len(vibrational_freq)):
                        tableau.add_row([
                            "",
                            "%.4f" % vibrational_freq[i],
                            "%.4f" % vibrational_int[i], vibrational_sym[i]
                        ])
            else:
                doc.append(
                    NoEscape(
                        r"\textcolor{bordeau}{Warning : force constants and the "
                        "resulting vibrational frequencies were not computed.}"
                    ))

        ### Excited states
        try:
            et_energies = json["results"]["excited_states"]["et_energies"]
        except KeyError:
            et_energies = []
        rnbExci = len(et_energies)
        if rnbExci != 0 and et_energies != 'N/A':
            with doc.create(Subsection('Excited states')):
                doc.append(NoEscape(r'\begin{center}'))
                with doc.create(Tabular('p{0cm}rrrrrrp{0cm}')) as tableau:
                    row_cells = [
                        MultiColumn(
                            8,
                            align='c',
                            data="Calculated mono-electronic excitations")
                    ]
                    tableau.add_row(row_cells)
                    tableau.add_row([
                        "", "Number", "Energy", "Symmetry",
                        "Oscillator strength", "Rotatory strength",
                        "Transitions", ""
                    ])
                    tableau.add_hline()
                    for i in range(rnbExci):
                        etr_i = (json["results"]["excited_states"]["et_rot"][i]
                                 if json["results"]["excited_states"]["et_rot"]
                                 != 'N/A' else 'N/A')
                        tableau.add_row([
                            "", (1 + i),
                            "%.2f" % et_energies[i],
                            json["results"]["excited_states"]["et_sym"][i],
                            "%.6f" %
                            json["results"]["excited_states"]["et_oscs"][i],
                            etr_i,
                            len(json["results"]["excited_states"]
                                ["et_transitions"][i]), ""
                        ])
                    tableau.add_hline()
                doc.append(NoEscape(r'\end{center}'))

    #####################################################################
    #                                                                   #
    ## 3. compilation LaTeX                                             #
    #                                                                   #
    #####################################################################

    # on compile
    # on previent si le fichier PDF est present ou pas
    # par defaut dans pylatex tout ce qui concerne latex est efface

    ### compile and output

    # doc.generate_pdf(rapFile, clean_tex=True) # comportement en routine, avec False en mode dev/debug
    # OK sans SVG doc.generate_pdf(rapFile, clean_tex=False)
    # pas de chance !! doc.generate_pdf(rapFile, clean_tex=False,compiler="pdflatex  --shell-escape")

    texFile = rapFile + ".tex"
    if (os.path.isfile(texFile)):
        os.remove(texFile)

    pdfFile = rapFile + ".pdf"
    if (os.path.isfile(pdfFile)):
        os.remove(pdfFile)

    doc.generate_tex(rapFile)
    cmd = "pdflatex  --shell-escape " + rapFile

    # si cleanMode = "--clean" on redirige vers /dev/null
    if mode == "clean":
        cmd += " > /dev/null"

    # compilation LaTeX

    os.system(cmd)

    # un peu de nettoyage si cleanMode = "--clean"

    if mode == "clean":
        if os.path.isfile(rapFile + '.aux'):
            os.remove(rapFile + '.aux')
        if os.path.isfile(rapFile + '.log'):
            os.remove(rapFile + '.log')
        if os.path.isfile(rapFile + '.tex'):
            os.remove(rapFile + '.tex')

    # message de fin

    if (os.path.isfile(pdfFile)):
        print('Report file is ' + rapFile + ".pdf")
    else:
        print('Probably a LateX error.')