Skip to content

CREST

Danger

Solvation using CREST's new default runtime, tblite, is current buggy. For details see this issue. According to this comment only gfnff and gfn0 work correctly with either alpb or gbsa solvent option. If you want to use solvation you need to use the --legacy mode which uses xtb instead of tblite for the calculation backend. Some progress is being made on this issue in this PR.

qcop.adapters.crest.CRESTAdapter

Adapter for CREST.

Note

The ProgramInput.keywords attribute is used to create the input file for CREST. This means that the structure of the keywords attribute should match that of CREST's input specification. Keywords such as method, charge, and uhf (which are stored on the Model and Structure; uhf is multiplicity - 1) will be added to the input file automatically.

supported_calctypes class-attribute instance-attribute

supported_calctypes = [
    energy,
    gradient,
    hessian,
    optimization,
    conformer_search,
]

Supported calculation types.

program_version

program_version(stdout: Optional[str] = None) -> str

Get the program version.

Parameters:

Name Type Description Default
stdout Optional[str]

The stdout from the program.

None

Returns:

Type Description
str

The program version.

Source code in qcop/adapters/crest.py
49
50
51
52
53
54
55
56
57
58
59
60
def program_version(self, stdout: Optional[str] = None) -> str:
    """Get the program version.

    Args:
        stdout: The stdout from the program.

    Returns:
        The program version.
    """
    if not stdout:
        stdout = execute_subprocess(self.program, ["--version"])
    return crest.parse_version_string(stdout)

compute_results

compute_results(
    inp_obj: ProgramInput,
    update_func: Optional[Callable] = None,
    update_interval: Optional[float] = None,
    collect_rotamers: bool = False,
    **kwargs
) -> tuple[
    Union[
        SinglePointResults,
        OptimizationResults,
        ConformerSearchResults,
    ],
    str,
]

Execute CREST on the given input.

Parameters:

Name Type Description Default
inp_obj ProgramInput

The qcio ProgramInput object for a computation.

required
update_func Optional[Callable]

A function to call with the stdout at regular intervals.

None
update_interval Optional[float]

The interval at which to call the update function.

None
collect_rotamers bool

Collect rotamers if doing a conformer_search. Defaults to False since rotamers are usually not of interest and there will be many.

False

Returns:

Type Description
tuple[Union[SinglePointResults, OptimizationResults, ConformerSearchResults], str]

A tuple of ConformerSearchResults and the stdout str.

Source code in qcop/adapters/crest.py
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def compute_results(
    self,
    inp_obj: ProgramInput,
    update_func: Optional[Callable] = None,
    update_interval: Optional[float] = None,
    collect_rotamers: bool = False,
    **kwargs,
) -> tuple[
    Union[SinglePointResults, OptimizationResults, ConformerSearchResults], str
]:
    """Execute CREST on the given input.

    Args:
        inp_obj: The qcio ProgramInput object for a computation.
        update_func: A function to call with the stdout at regular intervals.
        update_interval: The interval at which to call the update function.
        collect_rotamers: Collect rotamers if doing a conformer_search. Defaults to
            False since rotamers are usually not of interest and there will be many.

    Returns:
        A tuple of ConformerSearchResults and the stdout str.
    """
    # Create CREST native input files
    try:
        native_inp = qcparse.encode(inp_obj, self.program)
    except qcparse.exceptions.EncoderError as e:
        raise AdapterInputError(self.program, "Invalid input for CREST") from e

    # Write the input files to disk
    inp_file, struct_file = Path("input.toml"), Path(native_inp.geometry_filename)
    inp_file.write_text(native_inp.input_file)
    struct_file.write_text(native_inp.geometry_file)

    # Execute CREST
    stdout = execute_subprocess(
        self.program, [inp_file.name], update_func, update_interval
    )

    # Parse the output
    if inp_obj.calctype == CalcType.conformer_search:
        results = crest.parse_conformer_search_dir(
            ".",
            charge=inp_obj.structure.charge,
            multiplicity=inp_obj.structure.multiplicity,
            collect_rotamers=collect_rotamers,
        )
        # Add identifiers to the conformers and rotamers
        ids = inp_obj.structure.identifiers.model_dump()
        for struct_type in ["conformers", "rotamers"]:
            for struct in getattr(results, struct_type):
                struct.add_identifiers(ids)

    elif inp_obj.calctype in {CalcType.energy, CalcType.gradient}:
        results = crest.parse_singlepoint_dir(".")

    elif inp_obj.calctype == CalcType.optimization:
        results = crest.parse_optimization_dir(".", inp_obj=inp_obj, stdout=stdout)

    elif inp_obj.calctype == CalcType.hessian:
        results = crest.parse_numhess_dir(".", stdout=stdout)

    # CREST does not exit with a non-zero exit code on failure
    if "FAILED" in stdout:
        raise ExternalProgramError(
            f"CREST calculation failed. See the stdout for more information.",
            results=results,
            stdout=stdout,
        )
    return results, stdout