Esempio n. 1
0
    def test_write_and_read_trivial_trajectories(self):
        """Ensure write and read trajectory files with single structures."""
        # An open-shell single point calculation.
        data = ccopen("data/ORCA/basicORCA4.2/dvb_sp_un.out").parse()
        cclib2ase.write_trajectory("dvb_sp_un.traj", data)
        trajdata = cclib2ase.read_trajectory("dvb_sp_un.traj")

        assert np.allclose(trajdata.atomcoords, data.atomcoords)
        assert np.allclose(trajdata.scfenergies, data.scfenergies)
        # No grads here.

        assert np.allclose(trajdata.atomnos, data.atomnos)
        assert np.allclose(trajdata.atommasses, data.atommasses)
        assert np.allclose(trajdata.natom, data.natom)
        assert np.allclose(trajdata.charge, data.charge)
        assert np.allclose(trajdata.mult, data.mult)
        assert np.allclose(trajdata.moments, data.moments)

        # No temperature here.
        # No freeenergy here.

        assert np.allclose(trajdata.atomcharges["mulliken"],
                           data.atomcharges["mulliken"])
        assert np.allclose(trajdata.atomspins["mulliken"],
                           data.atomspins["mulliken"])

        # A closed-shell single structure frequency calculation.
        data = ccopen("data/ORCA/basicORCA4.2/dvb_ir.out").parse()
        cclib2ase.write_trajectory("dvb_ir.traj", data)
        trajdata = cclib2ase.read_trajectory("dvb_ir.traj")

        assert np.allclose(trajdata.atomcoords, data.atomcoords)
        assert np.allclose(trajdata.scfenergies, data.scfenergies)
        # No grads here.

        assert np.allclose(trajdata.atomnos, data.atomnos)
        assert np.allclose(trajdata.atommasses, data.atommasses)
        assert np.allclose(trajdata.natom, data.natom)
        assert np.allclose(trajdata.charge, data.charge, atol=1e-5)
        assert np.allclose(trajdata.mult, data.mult)
        assert np.allclose(trajdata.moments, data.moments)

        # No temperature here.
        # No freeenergy here.

        assert np.allclose(trajdata.atomcharges["mulliken"],
                           data.atomcharges["mulliken"])
Esempio n. 2
0
def test_gaussian_tinker(directory):
    if directory.endswith('UFF'):
        pytest.skip()
    with temporary_directory() as tmp:
        # Original data
        data_original = os.path.join(data, directory)
        outfile_original = os.path.join(data_original, directory + '.out')
        infile_original = os.path.join(data_original, directory + '.in')
        # Copied tmp paths
        data_copy = os.path.join(tmp, directory)
        infile_copy = os.path.join(data_copy, directory + '.in')
        shutil.copytree(data_original, data_copy)
        os.chdir(data_copy)
        # We are now in /tmp/garleek*****/A_1 or similar, which
        # contains copies of the original Gaussian files and types
        # guess forcefield specified in atom.types
        ff = 'qmmm3.prm'
        types = 'uff_to_mm3'
        if os.path.isfile(directory + '.key'):
            ff = directory + '.key'
        elif os.path.isfile('atom.types'):
            types = 'atom.types'
            with open(types) as f:
                for line in f:
                    if line.startswith('# forcefield:'):
                        ff = line.split(':', 1)[1].strip()
        # patch inputfile
        garleek_in = frontend_garleek(infile_copy, qm='gaussian_'+gaussian_version, mm='tinker', ff=ff, types=types)

        call([gaussian_exe, garleek_in])
        garleek_out = os.path.splitext(garleek_in)[0] + '.log'

        # Save output in working dir
        if not os.path.exists(os.path.join(WORKING_DIR, 'outputs-'+gaussian_version)):
            os.mkdir(os.path.join(WORKING_DIR, 'outputs-'+gaussian_version))
        shutil.copy(garleek_in, os.path.join(WORKING_DIR, 'outputs-'+gaussian_version, os.path.basename(garleek_in)))
        assert os.path.isfile(garleek_out)
        shutil.copy(garleek_out, os.path.join(WORKING_DIR, 'outputs-'+gaussian_version, os.path.basename(garleek_out)))
        assert no_errors(garleek_out)
        # Check values
        assert isclose(oniom_energy(outfile_original), oniom_energy(garleek_out))
        if HAS_CCLIB:
            cc_original = cclib.ccopen(outfile_original).parse()
            cc_calculated = cclib.ccopen(garleek_out).parse()
            assert isclose(cc_original.scfenergies[-1], cc_calculated.scfenergies[-1])
            assert np.sqrt(np.mean(np.square(cc_original.atomcoords[-1]-cc_calculated.atomcoords[-1]))) < 0.001
Esempio n. 3
0
def logfile_to_dict(logfile, verbose=False):
    # reading with cclib
    data = cclib.ccopen(logfile).parse()
    data_json = json.loads(data.writejson())
    # openbabel sur XYZ
    obdata = pybel.readstring("xyz", data.writexyz())
    # construct new dict    
    return full_report(logfile, data_json, data, obdata, verbose=verbose)
Esempio n. 4
0
def main(argv=sys.argv[1:]):
    """
    Pnictogen command-line interface. It writes inputs for sets of molecules.

    Parameters
    ----------
    argv : list of str
        Arguments as taken from the command-line

    Examples
    --------
    Although this function is not intended to be called from Python code (see
    the pnictogen function below for one that is), the following should work:

    >>> import pnictogen
    >>> pnictogen.main(["-g", "/tmp/opt.ORCA.inp"])
    /tmp/opt.ORCA.inp written
    >>> pnictogen.main(["/tmp/opt.ORCA.inp", "data/co.xyz",
    ...                 "data/water.xyz"])
    data/co.inp written
    data/water.inp written

    This is exactly as if pnictogen were called from the command-line.

    """
    parser = argparser()
    args = parser.parse_args(argv)
    package, extension = os.path.basename(args.template).split(".")[-2:]

    if args.generate:
        with open(REPOSITORY[package], "r") as stream:
            content = stream.read()
        with open(args.template, "w") as stream:
            stream.write(content)
        print("{:s} written".format(args.template))
    else:
        for descriptor in args.descriptors:
            input_prefix, description_extension = os.path.splitext(descriptor)

            try:
                molecule = Atoms(cclib.ccopen(descriptor).parse())
            except KeyError:
                molecule = Atoms(
                    cclib.bridge.cclib2openbabel.readfile(
                        descriptor, description_extension[1:]))

            if not molecule.name:
                molecule.name = descriptor

            written_files = pnictogen(molecule, input_prefix, args.template,
                                      extension)

            for written_file in written_files:
                print("{:s} written".format(written_file))
Esempio n. 5
0
def quality_check_lvl1(logfile, verbose=False):
    if verbose:
        print(">>> START QC lvl1 <<<")
    try:
        # reading with cclib
        data = cclib.ccopen(logfile).parse()
        if verbose:
            print("OK\n>>> END QC lvl1 <<<\n")
    except:
        raise ScanlogException("Quality check lvl1 failed : LOG file not readable (cclib failed on file %s)." % logfile)
    solver = data.metadata['package']
    return solver
Esempio n. 6
0
    def test_makease_works_with_openshells(self):
        """Ensure makease works from parsed data for open-shell molecules."""
        # make sure we can construct an open shell molecule
        data = ccopen("data/ORCA/basicORCA4.2/dvb_sp_un.out").parse()

        # Check we have no gradients, as they will be generated by ASE.
        with self.assertRaises(AttributeError):
            data.grads

        dvb_sp_un = cclib2ase.makease(
            data.atomcoords[-1],
            data.atomnos,
            data.atomcharges["mulliken"],
            data.atomspins["mulliken"],
            data.atommasses,
        )

        # check whether converting back gives the expected data
        ase_data = cclib2ase.makecclib(dvb_sp_un)
        assert np.allclose(ase_data.atomcoords, [data.atomcoords[-1]])
        assert np.allclose(ase_data.atomnos, data.atomnos)
        assert np.allclose(ase_data.atomcharges["mulliken"],
                           data.atomcharges["mulliken"])
        assert np.allclose(ase_data.atomspins["mulliken"],
                           data.atomspins["mulliken"])
        assert np.allclose(ase_data.atommasses, data.atommasses)
        assert np.isclose(ase_data.charge, data.charge)
        assert np.isclose(ase_data.mult, data.mult)
        assert np.isclose(ase_data.natom, len(data.atomnos))
        assert np.isclose(ase_data.temperature, 0)

        # make sure our object is compatible with ASE API
        dvb_sp_un.calc = EMT(label="dvb_sp_un")  # not a serious calculator!

        # converting back should give updated results
        ase_data = cclib2ase.makecclib(dvb_sp_un)
        assert np.allclose(ase_data.atomcoords, [data.atomcoords[-1]])
        assert np.allclose(ase_data.atomnos, data.atomnos)
        assert np.allclose(ase_data.atommasses, data.atommasses)
        assert np.allclose(ase_data.atomcharges["mulliken"],
                           data.atomcharges["mulliken"])
        assert np.allclose(
            ase_data.atomspins["mulliken"],
            0)  # spin densities not supported but overwritten by EMT
        assert np.isclose(ase_data.charge, data.charge)
        assert np.isclose(ase_data.mult, 1)
        assert np.isclose(ase_data.natom, len(data.atomnos))
        assert np.isclose(ase_data.temperature, 0)

        # Both energies and gradients are from the EMT calculation.
        assert np.allclose(ase_data.scfenergies, [7.016800805424298])
        assert np.shape(ase_data.grads) == (1, ase_data.natom, 3)
Esempio n. 7
0
    def extract(self, log_file_name: str) -> dict:
        """ Extract all possible features from a quantum chemistry calculation
        log file.
        """
        parsed = cclib.ccopen(log_file_name).parse()

        if not self._is_success(parsed):
            raise RuntimeError('Calculation in {} is not successful.'.format(
                log_file_name
            ))

        result = {}
        for method in self._all_methods():
            method(parsed, result)

        return result
Esempio n. 8
0
def read_energy_levels(input_file, units='eV'):
    """
    Determines the energy levels and homos from and output file
    :param input_file: input file to read
    :param units: units to return energies in
    """
    try:
        data = ccopen(input_file).parse()
        levels = np.array(data.moenergies)
        if units != 'eV':
            try:
                levels = convertor(levels, 'eV', units)
            except KeyError as e:
                raise KeyError(f'Cannot convert energy levels to {units}')
    except AttributeError as e:
        raise Exception('Cannot find appropriate data, has the SCF finished yet?')
    return levels, data.homos
Esempio n. 9
0
def read_energy_levels(input_file, units='eV'):
    """
    Determines the energy levels and homos from and output file
    :param input_file: input file to read
    :param units: units to return energies in
    """
    try:
        data = ccopen(input_file).parse()
        levels = np.array(data.moenergies)
        if units != 'eV':
            try:
                levels = convertor(levels, 'eV', units)
            except KeyError as e:
                raise KeyError(f'Cannot convert energy levels to {units}')
    except AttributeError as e:
        raise Exception(
            'Cannot find appropriate data, has the SCF finished yet?')
    return levels, data.homos
Esempio n. 10
0
    def test_write_and_read_opt_trajectories(self):
        """Ensure write and read trajectory files with optimizations."""
        # Geometry optimization.
        data = ccopen("data/ORCA/basicORCA4.2/dvb_gopt.out").parse()
        cclib2ase.write_trajectory("dvb_gopt.traj", data)
        trajdata = cclib2ase.read_trajectory("dvb_gopt.traj")

        assert np.allclose(trajdata.atomcoords, data.atomcoords)
        assert np.allclose(trajdata.scfenergies, data.scfenergies)
        assert np.allclose(trajdata.grads, data.grads)

        assert np.allclose(trajdata.atomnos, data.atomnos)
        assert np.allclose(trajdata.atommasses, data.atommasses)
        assert np.allclose(trajdata.natom, data.natom)
        assert np.allclose(trajdata.charge, data.charge)
        assert np.allclose(trajdata.mult, data.mult)
        assert np.allclose(trajdata.moments, data.moments, atol=1e-5)

        # No temperature here.
        # No freeenergy here.

        assert np.allclose(trajdata.atomcharges["mulliken"],
                           data.atomcharges["mulliken"])
Esempio n. 11
0
    def test_makease_works_with_closedshells(self):
        """Ensure makease works from parsed data for closed-shell molecules."""
        # Make sure we can construct a closed shell molecule.
        data = ccopen("data/ORCA/basicORCA4.2/dvb_ir.out").parse()
        dvb_ir = cclib2ase.makease(
            data.atomcoords[-1],
            data.atomnos,
            data.atomcharges["mulliken"],
            None,  # no atomspins
            data.atommasses,
        )

        # check whether converting back gives the expected data.
        ase_data = cclib2ase.makecclib(dvb_ir)
        assert np.allclose(ase_data.atomcoords, [data.atomcoords[-1]])
        assert np.allclose(ase_data.atomnos, data.atomnos)
        assert np.allclose(ase_data.atomcharges["mulliken"],
                           data.atomcharges["mulliken"])
        assert np.allclose(ase_data.atomspins["mulliken"], 0)
        assert np.allclose(ase_data.atommasses, data.atommasses)
        assert np.isclose(ase_data.charge, data.charge, atol=1e-5)
        assert np.isclose(ase_data.mult, data.mult)
        assert np.isclose(ase_data.natom, len(data.atomnos))
        assert np.isclose(ase_data.temperature, 0)
Esempio n. 12
0
def main():
    """Run main procedure."""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("logfile")
    # TODO(schneiderfelipe): set charge and multiplicity
    parser.add_argument(
        "-a",
        "--acc",
        help="accuracy for SCC calculation, lower is better",
        type=float,
        default=1.0,
    )
    parser.add_argument(
        "--iterations",
        help="number of iterations in SCC",
        type=int,
        default=250,
    )
    parser.add_argument(
        "--gfn", help="specify parametrisation of GFN-xTB", type=int
    )
    parser.add_argument(
        "--etemp", help="electronic temperature", type=float, default=300.0
    )
    parser.add_argument(
        "-s",
        "--solvent",
        help=("solvent (SMD/GBSA implicit solvation models)"),
        default="none",
    )
    parser.add_argument(
        "--do-not-cache-api",
        dest="cache_api",
        help="Do not reuse generate API objects (not recommended)",
        action="store_false",
    )

    parser.add_argument(
        "--pm3", help="use PM3", action="store_true",
    )
    parser.add_argument(
        "--b97-3c", help="use B97-3c", action="store_true",
    )
    parser.add_argument(
        "--minimize", action="store_true",
    )
    parser.add_argument(
        "--transition-state", action="store_true",
    )
    parser.add_argument("--max-omega", type=float, default=1.0)
    parser.add_argument("--tol", type=float, default=1e-3)
    parser.add_argument("--nprocs", type=int, default=4)
    args = parser.parse_args()
    print(args)

    data = ccopen(args.logfile).parse()
    initial_positions = data.atomcoords[-1]
    atoms = Atoms(numbers=data.atomnos, positions=initial_positions)

    if args.gfn:
        method = f"GFN{args.gfn}-xTB"
        solvent = smd2gbsa[args.solvent.lower()]

        calc = XTB(
            method=method,
            accuracy=args.acc,
            electronic_temperature=args.etemp,
            max_iterations=args.iterations,
            solvent=solvent,
            cache_api=args.cache_api,
        )
    else:

        if args.b97_3c:
            method = "B97-3c D3BJ def2-SV(P)"
        elif args.pm3:
            method = "PM3"
        else:

            def allow_keyword(keyword):
                for forbidden in {"freq", "opt", "irc", "print"}:
                    if forbidden in keyword.lower():
                        return False
                return True

            keywords = [
                keyword
                for keyword in data.metadata["keywords"]
                if allow_keyword(keyword)
            ]

            method = " ".join(keywords)

        solvent = args.solvent
        blocks = f"%pal\n nprocs {args.nprocs}\nend\n%scf\n maxiter {args.iterations}\nend"
        if solvent != "none" and not args.pm3:
            blocks += f'\n%cpcm\n smd true\n smdsolvent "{solvent}"\nend'

        if "ORCA_COMMAND" not in os.environ:
            # For parallel runs ORCA has to be called with full pathname
            os.environ["ORCA_COMMAND"] = shutil.which("orca")

        calc = ORCA(
            label="012345_swing", orcasimpleinput=method, orcablocks=blocks
        )

    print(f"*** {method} ***")
    print(f"    : solvent:              {solvent}")

    atoms.set_calculator(calc)
    potential_min = atoms.get_potential_energy()
    print(f"@ potential energy:        {potential_min} eV")

    indices = np.where(data.vibfreqs < 0)[0]
    n_indices = len(indices)
    print(f"@ imaginary frequencies:   {data.vibfreqs[indices]}")
    if not n_indices:
        print("    : nothing to be done, bye")
        return

    ignoring = None
    if args.transition_state:
        ignoring = 0
        print("    : transition state:    ignoring first imaginary frequency")

    omegas = []
    potentials = []

    def f(omega):
        atoms.set_positions(
            initial_positions
            + np.einsum("i,ijk->jk", omega, data.vibdisps[indices])
        )

        potential = 1e3 * (atoms.get_potential_energy() - potential_min)

        omegas.append(omega)
        potentials.append(potential)
        print(f"    : omega:               {omega}")
        print(f"    : potential:           {potential} meV")

        return potential

    if args.minimize:
        guesses = [np.zeros_like(indices, dtype=float)]

    for i in indices:
        if ignoring is not None and i == ignoring:
            continue

        print(f"@ searching in direction   #{i}")

        def g(w):
            z = np.zeros_like(indices, dtype=float)
            z[i] = w
            return f(z)

        if args.minimize:
            res = minimize_scalar(
                g,
                method="bounded",
                bounds=(-args.max_omega, args.max_omega),
                tol=args.tol,
            )
            print(res)

            guess = np.zeros_like(indices, dtype=float)
            guess[i] = res.x
            guesses.append(guess)
        else:
            dx = args.max_omega / 100
            x = [-dx, 0.0, dx]
            y = [g(-dx), 0.0, g(dx)]

            # p[0] * x**2 + p[1] * x + p[2] == k * (x - x0)**2 == k * x**2 - 2 * x0 * k * x + k * x0**2
            p = np.polyfit(x, y, 2)
            print(p)
            print(np.roots(p))

            dp = np.polyder(p)
            print(dp)
            r = np.roots(dp)
            print(r)

            # k = p[0]
            # x0 = np.sqrt(p[2] / k)
            # print(k, x0)
            # print(root(lambda z: [p[0] - z[0], p[1] + 2 * z[0] * z[1], p[2] - z[0] * z[1] ** 2], [k, x0]))

            best_positions = initial_positions + np.einsum(
                "i,ijk->jk", r, data.vibdisps[indices]
            )

    if args.minimize:
        print("@ choosing initial guess for global search")
        if n_indices > 1:
            guesses.append(np.sum(guesses, axis=0))
        x0 = guesses[np.argmin([f(guess) for guess in guesses])]

        print("@ searching in all directions")
        constraints = ()
        if args.transition_state and ignoring is not None:
            constraints = (
                {"type": "eq", "fun": lambda omega: omega[ignoring]},
            )
        res = minimize(
            f,
            x0=x0,
            bounds=n_indices * [(-args.max_omega, args.max_omega)],
            constraints=constraints,
            tol=args.tol,
        )
        print(res)
        best_positions = initial_positions + np.einsum(
            "i,ijk->jk", res.x, data.vibdisps[indices]
        )

        # TODO(schneiderfelipe): correct for when using --transition-state
        omegas = np.array(omegas)
        fig, ax = plt.subplots(n_indices, 1)
        if n_indices == 1:
            ax = [ax]
        xlim = (-args.max_omega - 0.05, args.max_omega + 0.05)
        ylim = (np.min(potentials) - 2.0, 40.0)
        for i in indices:
            if ignoring is not None and i == ignoring:
                continue

            ax[i].plot(omegas[:, i], potentials, "o")
            ax[i].set_title(f"view of normal mode #{i}")
            ax[i].set_ylabel(r"potential energy (meV)")
            ax[i].set_xlabel(rf"$\omega_{i}$")
            ax[i].set_ylim(ylim)
            ax[i].set_xlim(xlim)
        plt.tight_layout()
        plt.show()

    print("@ writing best geometry to swinged.xyz")
    # TODO(schneiderfelipe): print a RMSD between initial and final structures
    atoms.set_positions(best_positions)
    atoms.write("swinged.xyz", format="xyz", plain=True)
Esempio n. 13
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("logfiles", metavar="logfile", nargs="+")
    parser.add_argument("--xmin", default=400.0, type=float)
    parser.add_argument("--xmax", default=700.0, type=float)
    parser.add_argument("--xshift", default=90.0, type=float)
    parser.add_argument("--broad-only", action="store_true")
    args = parser.parse_args()

    for logfile_path in args.logfiles:
        # We ask for label=path pairs.
        parts = logfile_path.split("=")
        name = parts[0]
        logfile_path = parts[-1]
        print(name.center(80, "-"))
        print(logfile_path)

        spectrum_path = logfile_path.replace(".out", ".spectrum")
        spectrum_path_found = os.path.isfile(spectrum_path)
        if not args.broad_only and spectrum_path_found:
            print(".spectrum file found")

            spectrum = pd.read_csv(spectrum_path, sep="\s+", index_col=0)

            x = spectrum.index
            y = spectrum["TotalSpectrum"] / spectrum["TotalSpectrum"].max()
        else:
            if spectrum_path_found:
                print("Ignoring found .spectrum file, using broadened data")
            else:
                print("No .spectrum file found, using broadened data")
            data = ccopen(logfile_path).parse()
            wavelengths = 1e7 / data.etenergies  # nm conversion

            x = np.linspace(wavelengths.min() - 100.0,
                            wavelengths.max() + 100.0,
                            num=1000)
            y = broaden_spectrum(x, wavelengths, data.etoscs, scale=40.0)
            y = y / y.max()

        if args.xshift:
            print(f"Shifting all wavelengths by {args.xshift} nm")
            x += args.xshift
        plt.plot(x, y, label=name)

        f = interp1d(x,
                     y,
                     kind="cubic",
                     bounds_error=False,
                     fill_value="extrapolate")
        res = minimize_scalar(
            lambda t: -f(t),
            bracket=(args.xmin, args.xmax),
            bounds=(args.xmin, args.xmax),
            method="bounded",
        )
        print(res)

    plt.xlim(args.xmin, args.xmax)
    plt.xlabel("wavelength (nm)")
    plt.ylabel("arbitrary units")
    plt.legend()
    plt.show()