MolSSI / QCEngine

Quantum chemistry program executor and IO standardizer (QCSchema).
https://molssi.github.io/QCEngine/
BSD 3-Clause "New" or "Revised" License
163 stars 79 forks source link

[BUG] berny optimization procedure fails to capture compute errors correctly #300

Closed coltonbh closed 2 years ago

coltonbh commented 3 years ago

Describe the bug The current BernyProcedure.compute() method does not correctly handle failed gradient calculations correctly. The method assumes that all values returned from qcengine.compute() are AtomicResult objects when in reality they may also be FailedOperation objects. This may occur if unsupported keywords are passed the compute engine specified for the operation.

The bug is caused by this line of code:

"final_molecule": trajectory[-1]["molecule"],

The assumption is that the trajectory array will contain only AtomicResult objects and that the final one will contain a "molecule" key. In reality, if an error occurred with the gradient calculation the final object in the trajectory array is a FailedOperation object, so this line of code raises a KeyError and the returned FailedOperation object is indicative of only the KeyError raised in the procedure instead of the underlying error computing the gradient. This makes the failures hard to debug (or impossible if you can't read the source code).

To Reproduce

from qcelemental.models import Molecule, OptimizationInput
from qcelemental.models.procedures import QCInputSpecification
import qcengine

water = Molecule.from_data("pubchem:water")

input_spec = QCInputSpecification(
    driver="gradient",
    model={"method": "b3lyp", "basis": "6-31g"},
    # Keywords specific to the compute engine that will execute gradient calculation
    # I.e., bad keywords for psi4; THIS IS WHAT CAUSES THE ERROR!
    keywords={'fake': 'bad'},
)

opt_input = OptimizationInput(
    initial_molecule=water,
    input_specification=input_spec,
    protocols={"trajectory": "all"},
    keywords={"program": "psi4", "maxsteps": 3},
)

qcengine.compute_procedure(opt_input, "berny")

The exception raised:

FailedOperation(error=ComputeError(error_type='unknown_error', error_message='QCEngine Execution Error:\nTraceback (most recent call last):\n  File "/opt/conda/lib/python3.7/site-packages/qcengine/util.py", line 114, in compute_wrapper\n    yield metadata\n  File "/opt/conda/lib/python3.7/site-packages/qcengine/compute.py", line 147, in compute_procedure\n    output_data = executor.compute(input_data, config)\n  File "/opt/conda/lib/python3.7/site-packages/qcengine/procedures/berny.py", line 78, in compute\n    "final_molecule": trajectory[-1]["molecule"],\nKeyError: \'molecule\'\n'))

Expected behavior A FailedOperation object should be returned specifying that the keywords for psi4 are incorrect. The procedure code should not incorrectly look for the molecule property on the final object in the trajectory array; rather, the code should correctly handle the case of a failed qcengine.compute call.