Skip to content

Commit

Permalink
Merge pull request #277 from maxscheurer/adcc-harness
Browse files Browse the repository at this point in the history
Add adcc harness
  • Loading branch information
loriab authored Feb 11, 2021
2 parents 5fb99be + 4ac55c3 commit d49748a
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 1 deletion.
8 changes: 7 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ matrix:
env:
- PYTHON_VER=3.6
- PROG=ANI
- os: linux
env:
- PYTHON_VER=3.7
- PROG=ADCC
- os: linux
env:
- PYTHON_VER=3.6
Expand Down Expand Up @@ -63,6 +67,8 @@ install:
python devtools/scripts/conda_env.py -n=test -p=$PYTHON_VER devtools/conda-envs/psi-nightly.yaml
elif [ $PROG == "ANI" ]; then
python devtools/scripts/conda_env.py -n=test -p=$PYTHON_VER devtools/conda-envs/torchani.yaml
elif [ $PROG == "ADCC" ]; then
python devtools/scripts/conda_env.py -n=test -p=$PYTHON_VER devtools/conda-envs/adcc.yaml
elif [ $PROG == "OPENMM" ]; then
python devtools/scripts/conda_env.py -n=test -p=$PYTHON_VER devtools/conda-envs/openmm.yaml
elif [ $PROG == "NWCHEM" ]; then
Expand All @@ -81,7 +87,7 @@ install:

# Any kind of post env ceation
- |
if [ $PROG == "PSI4DEV" ]; then
if [[ $PROG == "PSI4DEV" || $PROG == "ADCC" ]]; then
conda remove qcengine --force
elif [ $PROG == "QCORE" ]; then
qcore --accept-license
Expand Down
17 changes: 17 additions & 0 deletions codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,23 @@
"affiliation": "Hylleraas Centre for Quantum Molecular Sciences, UiT -- The Arctic University of Norway",
"@id": "https://orcid.org/0000-0002-5452-9239",
"identifier": "https://github.com/robertodr"
},
{
"@type": "Person",
"givenName": "Maximilian",
"familyName": "Scheurer",
"affiliation": "Interdisciplinary Center for Scientific Computing, Heidelberg University",
"@id": "https://orcid.org/0000-0003-0592-3464",
"identifier": "https://github.com/maxscheurer"
},
{
"@type": "Person",
"givenName": "Michael",
"additionalName": "F.",
"familyName": "Herbst",
"affiliation": "Inria Paris and CERMICS, \u00C9cole des Ponts ParisTech",
"@id": "https://orcid.org/0000-0003-0378-7921",
"identifier": "https://github.com/mfherbst"
}
],
"developmentStatus": "active",
Expand Down
25 changes: 25 additions & 0 deletions devtools/conda-envs/adcc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: test
channels:
- adcc
- psi4/label/dev
- conda-forge
dependencies:
- adcc>=0.15.7
- psi4
- blas=*=mkl # not needed but an example of disuading solver from openblas and old psi
- intel-openmp!=2019.5

# Core
- python
- pyyaml
- py-cpuinfo
- psutil
- qcelemental >=0.13.0
- pydantic>=1.0.0
- msgpack-python

# Testing
- pytest
- pytest-cov
- codecov

1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Currently available compute backends for single results are as follow:

- Quantum Chemistry:

- `adcc <https://adc-connect.org>`_
- `Entos <https://www.entos.info>`_
- `Molpro <https://www.molpro.net>`_
- `Psi4 <http://www.psicode.org>`_
Expand Down
2 changes: 2 additions & 0 deletions docs/source/program_overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Quantum Chemistry
+---------------+------------+---+---+---+------------+--------------+
| Program | Production | E | G | H | Properties | Wavefunction +
+===============+============+===+===+===+============+==============+
| adcc |||||||
+---------------+------------+---+---+---+------------+--------------+
| CFOUR |||||||
+---------------+------------+---+---+---+------------+--------------+
| Qcore (Entos) |||||||
Expand Down
128 changes: 128 additions & 0 deletions qcengine/programs/adcc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""
Calls adcc
"""
from typing import Dict, TYPE_CHECKING

from qcelemental.util import safe_version, which_import
from qcelemental.models import AtomicResult, Provenance
from .qcvar_identities_resources import build_atomicproperties

from .model import ProgramHarness
from ..exceptions import InputError, UnknownError

if TYPE_CHECKING:
from qcelemental.models import AtomicInput

from ..config import TaskConfig

#


class AdccHarness(ProgramHarness):
_defaults = {
"name": "adcc",
"scratch": False,
"thread_safe": False,
"thread_parallel": True,
"node_parallel": False,
"managed_memory": True,
}
version_cache: Dict[str, str] = {}

class Config(ProgramHarness.Config):
pass

@staticmethod
def found(raise_error: bool = False) -> bool:
"""Whether adcc harness is ready for operation.
Parameters
----------
raise_error: bool
Passed on to control negative return between False and ModuleNotFoundError raised.
Returns
-------
bool
If adcc and psi4 are found, returns True.
If raise_error is False and adcc or psi4 is missing, returns False.
If raise_error is True and adcc or psi4 are missing, the error message is raised.
"""
found_adcc = which_import(
"adcc",
return_bool=True,
raise_error=raise_error,
raise_msg="Please install via `conda install adcc -c adcc`.",
)
found_psi4 = which_import(
"psi4",
return_bool=True,
raise_error=raise_error,
raise_msg="Please install psi4 for adcc harness via `conda install psi4 -c psi4`.",
)
return found_adcc and found_psi4

def get_version(self) -> str:
"""Return the currently used version of adcc"""
self.found(raise_error=True)

which_prog = which_import("adcc")
if which_prog not in self.version_cache:
import adcc

self.version_cache[which_prog] = safe_version(adcc.__version__)
return self.version_cache[which_prog]

def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicResult":
"""
Runs adcc
"""
self.found(raise_error=True)

import adcc
import psi4

mol = input_model.molecule
model = input_model.model
conv_tol = input_model.keywords.get("conv_tol", 1e-6)

psi4_molecule = psi4.core.Molecule.from_schema(dict(mol.dict(), fix_symmetry="c1"))
psi4.core.clean()
psi4.core.be_quiet()
psi4.set_options(
{
"basis": model.basis,
"scf_type": "pk",
"e_convergence": conv_tol / 100,
"d_convergence": conv_tol / 10,
# 'maxiter': max_iter,
"reference": "RHF" if mol.molecular_multiplicity == 1 else "UHF",
}
)
_, wfn = psi4.energy("HF", return_wfn=True, molecule=psi4_molecule)
adcc.set_n_threads(config.ncores)
compute_success = False
try:
adcc_state = adcc.run_adc(wfn, method=model.method, **input_model.keywords)
compute_success = adcc_state.converged
except adcc.InputError as e:
raise InputError(str(e))
except Exception as e:
raise UnknownError(str(e))

input_data = input_model.dict(encoding="json")
output_data = input_data.copy()
output_data["success"] = compute_success

if compute_success:
output_data["return_result"] = adcc_state.excitation_energy[0]

extract_props = input_model.driver == "properties"
qcvars = adcc_state.to_qcvars(recurse=True, properties=extract_props)
atprop = build_atomicproperties(qcvars)
output_data["extras"]["qcvars"] = qcvars
output_data["properties"] = atprop

provenance = Provenance(creator="adcc", version=self.get_version(), routine="adcc").dict()
provenance["nthreads"] = adcc.get_n_threads()
output_data["provenance"] = provenance

return AtomicResult(**output_data)
2 changes: 2 additions & 0 deletions qcengine/programs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Set

from ..exceptions import InputError, ResourceError
from .adcc import AdccHarness
from .cfour import CFOURHarness
from .dftd3 import DFTD3Harness
from .gcp import GCPHarness
Expand Down Expand Up @@ -99,6 +100,7 @@ def list_available_programs() -> Set[str]:


# Quantum
register_program(AdccHarness())
register_program(CFOURHarness())
register_program(EntosHarness()) # Duplicate of Qcore harness to transition the namespace, to be deprecated
register_program(GAMESSHarness())
Expand Down
40 changes: 40 additions & 0 deletions qcengine/programs/tests/test_adcc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Tests for adcc functionality"""
import pytest
import numpy as np
import qcengine as qcng
import qcelemental as qcel

from qcengine.testing import using
from qcelemental.testing import compare_values


@pytest.fixture
def h2o():
return qcel.models.Molecule.from_data(
"""
O 0.0 0.000 -0.129
H 0.0 -1.494 1.027
H 0.0 1.494 1.027
"""
)


@using("adcc")
def test_run(h2o):
inp = qcel.models.AtomicInput(
molecule=h2o, driver="properties", model={"method": "adc2", "basis": "sto-3g"}, keywords={"n_singlets": 3}
)
ret = qcng.compute(inp, "adcc", raise_error=True, local_options={"ncores": 1}, return_dict=True)

ref_excitations = np.array([0.0693704245883876, 0.09773854881340478, 0.21481589246935925])
ref_hf_energy = -74.45975898670224
ref_mp2_energy = -74.67111187456267
assert ret["success"] is True

qcvars = ret["extras"]["qcvars"]

assert qcvars["EXCITATION KIND"] == "SINGLET"
assert compare_values(ref_excitations[0], ret["return_result"])
assert compare_values(ref_hf_energy, ret["properties"]["scf_total_energy"])
assert compare_values(ref_mp2_energy, ret["properties"]["mp2_total_energy"])
assert compare_values(ref_excitations, qcvars["ADC2 EXCITATION ENERGIES"])
1 change: 1 addition & 0 deletions qcengine/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def get_job(self):

# Figure out what is imported
_programs = {
"adcc": is_program_new_enough("adcc", "0.15.7"),
"cfour": which("xcfour", return_bool=True),
"dftd3": which("dftd3", return_bool=True),
"dftd3_321": is_program_new_enough("dftd3", "3.2.1"),
Expand Down

0 comments on commit d49748a

Please sign in to comment.