Skip to content

Commit

Permalink
Merge pull request #208 from arosen93/gulp
Browse files Browse the repository at this point in the history
Add `GULP` recipes
  • Loading branch information
Andrew-S-Rosen authored Apr 7, 2023
2 parents fbe810d + 527facf commit 68e8dc3
Show file tree
Hide file tree
Showing 13 changed files with 719 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,4 @@ jobs:
pip install -e .[docs]
- name: Build
run: sphinx-build docs/src docs_build
run: sphinx-build docs/src docs_build
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
QuAcc is a flexible platform for high-throughput, database-driven computational materials science and quantum chemistry.

The three main goals of QuAcc are as follows:

1. To enable a seamless interface between the [Atomic Simulation Environment](https://wiki.fysik.dtu.dk/ase/index.html) (ASE) and the high-throughput infrastructure powering the [Materials Project](https://materialsproject.org).
2. To reduce the barrier for constructing complex, mixed-code workflows.
3. To promote rapid workflow development and testing via [Jobflow](https://github.com/materialsproject/jobflow).
Expand Down
2 changes: 1 addition & 1 deletion conda_codes.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
dependencies:
- psi4::psi4
- conda-forge::dftbplus==22.2
- conda-forge::dftbplus==22.2
28 changes: 27 additions & 1 deletion docs/src/install/codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,60 @@ Here, we outline how to ensure that QuAcc can run the quantum chemistry package

## VASP

VASP is a very widely used code for plane-wave, periodic DFT calculations. To use VASP with QuAcc, you will need to do the following:

1. Define the `VASP_PP_PATH` environment variable that points to your pseudopotential library. See the [ASE documentation](https://wiki.fysik.dtu.dk/ase/ase/calculators/vasp.html#pseudopotentials) for full details. We recommend including this in your `~/.bashrc` file.
2. To use vdW functionals, define the `ASE_VASP_VDW` environment variable to point to the `vdw_kernel.bindat` file distributed with VASP. Again, refer to the [ASE documentation](https://wiki.fysik.dtu.dk/ase/ase/calculators/vasp.html#pseudopotentials) for additional details. We recommend including this in your `~/.bashrc` file.
3. To run VASP with Custodian (the default behavior in QuAcc), you will need to define a `VASP_PARALLEL_CMD` environment variable that tells Custodian how to parallelize VASP. For instance, this might look something like `export VASP_PARALLEL_CMD="srun -N 2 --ntasks-per-node 24"`. Note, the VASP executables are not included in this environment variable. For convenience, we recommend specifying this environment variable at runtime so you can easily modify it.
4. By default, Custodian will assume that the VASP executables can be run with `vasp_std` or `vasp_gam` for standard or gamma-point calculations. If you need to use different executable names or wish to change any Custodian settings from the selected defaults, refer to the section on modifying QuAcc settings.

## Gaussian

Gaussian is an extremely popular molecular DFT code that is quite robust and easy to use but lacks newer features in other packages.

As noted in the [ASE documentation](https://wiki.fysik.dtu.dk/ase/ase/calculators/gaussian.html), by default the executables named `g16`, `g09`, or `g03` will be searched (in order of decreasing preference) to run Gaussian. To use a different executable name, you will need to define an environment variable named `ASE_GAUSSIAN_COMMAND`. It should be of the form `ASE_GAUSSIAN_COMMAND="/path/to/my/gaussian_executable Gaussian.com > Gaussian.log"`.

## ORCA

ORCA is a free code that is especially useful for molecular DFT calculations with recently developed methods.

As noted in the [ASE documentation](https://wiki.fysik.dtu.dk/ase/ase/calculators/orca.html), to use ORCA in parallel mode, you will need to define an environment variable named `ASE_ORCA_COMMAND`. It should be of the form `ASE_ORCA_COMMAND="/path/to/my/orca orca.inp > orca.out"`.

## Psi4

Psi4 is especially useful for constructing and testing out new functionals, like DeepMind's DM21.

If you plan to use [Psi4](https://github.com/psi4/psi4) with QuAcc, you will need to install it prior to use. This can be done via `conda install -c psi4 psi4`.

## xTB

xTB is especially useful for semi-empirical tight-binding calculations based on the GFN-xTB method.

If you plan to use xTB with QuAcc, you will need to install the [xtb-python](https://github.com/grimme-lab/xtb-python) interface. This can be done via `conda install -c conda-forge xtb-python`.

## tblite

tblite is especially useful for xTB calculations and has a fair bit of overlap with the xTB-python package.

If you plan to use tblite with QuAcc, you will need to install the [tblite](https://github.com/tblite/tblite) interface with ASE support. This can be done via `pip install tblite[ase]`.

## Psi4

Psi4 is especially useful for constructing and testing out new functionals, like DeepMind's DM21.

If you plan to use [Psi4](https://github.com/psi4/psi4) with QuAcc, you will need to install it prior to use. This can be done via `conda install -c psi4 psi4`.

## DFTB+

If you plan to use DFTB+ (which includes a separate interface to xTB) with QuAcc, you will need to install [DFTB+](https://dftbplus.org/). This can be done via `conda install -c conda-forge dftbplus`.
DFTB+ is especially useful for periodic xTB calculations and the DFTB+ method based on Slater-Koster parameters.

If you plan to use [DFTB+](https://dftbplus.org/) (which includes a separate interface to xTB) with QuAcc, you will need to install [DFTB+](https://dftbplus.org/). This can be done via `conda install -c conda-forge dftbplus`.

# GULP

GULP is especially useful for periodic GFN-FF calculations and force field methods.

As noted in the [ASE documentation](https://wiki.fysik.dtu.dk/ase/ase/calculators/gulp.html), if you plan to use [GULP](https://gulp.curtin.edu.au/), you must set the environment variables `GULP_LIB="/path/to/my/gulp-#.#.#/Libraries"` and `ASE_GULP_COMMAND="/path/to/my/gulp-#.#.#/Src/gulp < gulp.gin > gulp.got"`.

## Other Codes

Expand Down
1 change: 1 addition & 0 deletions quacc/recipes/gulp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Recipes for GULP"""
173 changes: 173 additions & 0 deletions quacc/recipes/gulp/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"""Core recipes for GULP"""
from __future__ import annotations

import warnings
from dataclasses import dataclass, field
from typing import Any, Dict

from ase.atoms import Atoms
from ase.calculators.gulp import GULP
from jobflow import Maker, job

from quacc.schemas.calc import summarize_run
from quacc.util.basics import merge_dicts
from quacc.util.calc import run_calc


@dataclass
class StaticJob(Maker):
"""
Class to carry out a single-point calculation.
Note: 'Conditions' are not yet natively supported.
Parameters
----------
name
Name of the job.
gfnff
True if (p)GFN-FF should be used; False if not.
library
Filename of the potential library file, if required.
keyword_swaps
Dictionary of custom keyword swap kwargs for the calculator.
option_swaps
Dictionary of custom option swap kwargs for the calculator.
"""

name: str = "GULP-Static"
gfnff: bool = True
library: str = None
keyword_swaps: Dict[str, Any] = field(default_factory=dict)
option_swaps: Dict[str, Any] = field(default_factory=dict)

@job
def make(self, atoms: Atoms) -> Dict[str, Any]:
"""
Make the run.
Parameters
----------
atoms
.Atoms object
Returns
-------
Dict
Summary of the run.
"""

default_keywords = {
"gfnff": self.gfnff,
"gwolf": True if self.gfnff and atoms.pbc.any() else False,
}
default_options = {
"dump every gulp.res": True,
"output cif gulp.cif": True if atoms.pbc.any() else False,
"output xyz gulp.xyz": False if atoms.pbc.any() else True,
}

keywords = merge_dicts(
default_keywords, self.keyword_swaps, remove_none=True, remove_false=True
)
options = merge_dicts(
default_options, self.option_swaps, remove_none=True, remove_false=True
)

gulp_keywords = " ".join(list(keywords.keys()))
gulp_options = list(options.keys())

atoms.calc = GULP(keywords=gulp_keywords, options=gulp_options)
new_atoms = run_calc(
atoms, geom_file="gulp.cif" if atoms.pbc.any() else "gulp.xyz"
)
summary = summarize_run(
new_atoms, input_atoms=atoms, additional_fields={"name": self.name}
)

return summary


@dataclass
class RelaxJob(Maker):
"""
Class to carry out a single-point calculation.
Note: 'Conditions' are not yet natively supported.
Parameters
----------
name
Name of the job.
gfnff
True if (p)GFN-FF should be used; False if not.
library
Filename of the potential library file, if required.
volume_relax
True if the volume should be relaxed; False if not.
keyword_swaps
Dictionary of custom keyword swap kwargs for the calculator.
option_swaps
Dictionary of custom option swap kwargs for the calculator.
"""

name: str = "GULP-Relax"
gfnff: bool = True
library: str = None
volume_relax: bool = True
keyword_swaps: Dict[str, Any] = field(default_factory=dict)
option_swaps: Dict[str, Any] = field(default_factory=dict)

@job
def make(self, atoms: Atoms) -> Dict[str, Any]:
"""
Make the run.
Parameters
----------
atoms
.Atoms object
Returns
-------
Dict
Summary of the run.
"""
if self.volume_relax and not atoms.pbc.any():
warnings.warn("Volume relaxation requested but no PBCs found. Ignoring.")
self.volume_relax = False

default_keywords = {
"opti": True,
"gfnff": self.gfnff,
"gwolf": True if self.gfnff and atoms.pbc.any() else False,
"conp": True if self.volume_relax and atoms.pbc.any() else False,
"conv": True if not self.volume_relax or not atoms.pbc.any() else False,
}
default_options = {
"dump every gulp.res": True,
"output cif gulp.cif": True if atoms.pbc.any() else False,
"output xyz gulp.xyz": False if atoms.pbc.any() else True,
}

keywords = merge_dicts(
default_keywords, self.keyword_swaps, remove_none=True, remove_false=True
)
options = merge_dicts(
default_options, self.option_swaps, remove_none=True, remove_false=True
)

gulp_keywords = " ".join(list(keywords.keys()))
gulp_options = list(options.keys())

atoms.calc = GULP(keywords=gulp_keywords, options=gulp_options)
new_atoms = run_calc(
atoms, geom_file="gulp.cif" if atoms.pbc.any() else "gulp.xyz"
)

if not new_atoms.calc.get_opt_state():
raise ValueError("Optimization did not converge!")

summary = summarize_run(
new_atoms, input_atoms=atoms, additional_fields={"name": self.name}
)

return summary
4 changes: 2 additions & 2 deletions quacc/recipes/orca/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class StaticJob(Maker):
To enable new entries, set the value as True.
To remove entries from the defaults, set the value as None/False.
block_swaps
Dictionary of orcablocks swaps for the calculator.
Dictionary of orcablock swaps for the calculator.
To enable new entries, set the value as True.
To remove entries from the defaults, set the value as None/False.
"""
Expand Down Expand Up @@ -124,7 +124,7 @@ class RelaxJob(Maker):
To enable new entries, set the value as True.
To remove entries from the defaults, set the value as None/False.
block_swaps
Dictionary of orcablocks swaps for the calculator.
Dictionary of orcablock swaps for the calculator.
To enable new entries, set the value as True.
To remove entries from the defaults, set the value as None/False.
"""
Expand Down
Empty file added tests/recipes/gulp/__init__.py
Empty file.
49 changes: 49 additions & 0 deletions tests/recipes/gulp/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import os
from unittest import mock

import numpy as np
import pytest
from ase.atoms import Atoms
from ase.calculators.gulp import GULP


@pytest.fixture(autouse=True)
def mock_settings_env_vars():
with mock.patch.dict(os.environ, {"GULP_LIB": "."}):
yield


def mock_get_potential_energy(self, **kwargs):
# Instead of running .get_potential_energy(), we mock it by attaching
# dummy results to the atoms object and returning a fake energy. This
# works because the real atoms.get_potential_energy() takes one argument
# (i.e. self) and that self object is the atoms object.
e = -1.0
self.calc.results = {
"energy": e,
"forces": np.array([[0.0, 0.0, 0.0]] * len(self)),
}
return e


@pytest.fixture(autouse=True)
def patch_get_potential_energy(monkeypatch):
# Monkeypatch the .get_potential_energy() method of the .Atoms object so
# we aren't running the actual calculation during testing.
monkeypatch.setattr(Atoms, "get_potential_energy", mock_get_potential_energy)


def mock_get_opt_state(self, **kwargs):
# Instead of running GULP and getting the opt state, we'll just mock
# it as true

self.get_opt_state = True

return True


@pytest.fixture(autouse=True)
def patch_get_opt_state(monkeypatch):
# Monkeypatch the .get_opt_state() method of the GULP calculator object so
# we aren't fetching the actual state
monkeypatch.setattr(GULP, "get_opt_state", mock_get_opt_state)
30 changes: 30 additions & 0 deletions tests/recipes/gulp/gulp_run/gulp.cif
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
data_cif

_audit_creation_method 'generated by GULP'

_symmetry_space_group_name_H-M '(unknown) '
_symmetry_Int_Tables_number 1

_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
_cell_length_a 5.1053
_cell_length_b 5.1053
_cell_length_c 5.1053
_cell_angle_alpha 60.0000
_cell_angle_beta 60.0000
_cell_angle_gamma 60.0000

loop_
_atom_site_label
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Cu 0.00000 0.00000 0.00000 1.0000
Cu 0.00000 0.00000 0.50000 1.0000
Cu 0.00000 0.50000 0.00000 1.0000
Cu 1.00000 0.50000 0.50000 1.0000
Cu 0.50000 0.00000 0.00000 1.0000
Cu 0.50000 1.00000 0.50000 1.0000
Cu 0.50000 0.50000 0.00000 1.0000
Cu 0.50000 0.50000 0.50000 1.0000
Loading

0 comments on commit 68e8dc3

Please sign in to comment.