Skip to content

Overview

Calculations are run by calling the qcop.compute() function with the relevant arguments and keywords like this:

from qcio import Structure, ProgramInput
from qcop import compute

# Create the Structure
structure = Structure(
    symbols=["O", "H", "H"],
    geometry=[  # type: ignore
        [0.0, 0.0, 0.0],
        [0.52421003, 1.68733646, 0.48074633],
        [1.14668581, -0.45032174, -1.35474466],
    ],
)

# Define the program input
prog_input = ProgramInput(
    structure=h2o,
    calctype="energy",
    model={"method": "hf", "basis": "sto-3g"},
    keywords={"purify": "no", "restricted": False},
)

# Run the calculation; will return ProgramOutput or raise an exception
prog_output = compute("terachem", prog_input, collect_files=True)

The compute selects the correct program adapter and then calls adapter.compute(). The available arguments and keywords for the top level compute() function match those shown here:

qcop.adapters.BaseAdapter.compute

compute(
    inp_obj: InputType,
    *,
    scratch_dir: Optional[StrOrPath] = None,
    rm_scratch_dir: bool = True,
    collect_stdout: bool = True,
    collect_files: bool = False,
    collect_wfn: bool = False,
    update_func: Optional[Callable] = None,
    update_interval: Optional[float] = None,
    print_stdout: bool = False,
    raise_exc: bool = True,
    propagate_wfn: bool = False,
    **adapter_kwargs
) -> ProgramOutput[InputType, ResultsType]

Compute the given input using the adapter's program.

Parameters:

Name Type Description Default
inp_obj InputType

A qcio input object for a computation. E.g. A FileInput, ProgramInput or DualProgramInput.

required
scratch_dir Optional[StrOrPath]

The scratch directory for the program. If None, a new directory is created in the system default temporary directory. If rm_scratch_dir is True this directory will be deleted after the program finishes.

None
rm_scratch_dir bool

Delete the scratch directory after the program exits.

True
collect_stdout bool

Whether to collect stdout/stderr from the program as output. Failed computations will always collect stdout/stderr.

True
collect_files bool

Collect all files generated by the QC program as output.

False
collect_wfn bool

Collect the wavefunction file(s) from the calculation. Not every program will support this. Use collect_files to collect all files including the wavefunction.

False
update_func Optional[Callable]

A function to call as the program executes. The function must accept the in-process stdout/stderr output as a string for its first argument.

None
update_interval Optional[float]

The minimum time in seconds between calls to the update_func.

None
print_stdout bool

Whether to print stdout/stderr to the terminal in real time as the program executes. Will be ignored if an update_func passed.

False
raise_exc bool

If False, qcop will return a ProgramOutput object when the QC program fails rather than raise an exception.

True
propagate_wfn bool

For any adapter performing a sequential task, such as a geometry optimization, propagate the wavefunction from the previous step to the next step. This is useful for accelerating convergence by using a previously computed wavefunction as a starting guess. If an adapter does not support wavefunction propagation, an AdapterInputError will be raised.

False
**adapter_kwargs

Additional keyword arguments to pass to the adapter or qcng.compute().

{}

Returns:

Type Description
ProgramOutput[InputType, ResultsType]

A ProgramOutput object containing the results of the computation.

Raises:

Type Description
AdapterNotFoundError

If the program is not supported (i.e., no Adapter is implemented for the program in qcop or qcengine).

ProgramNotFoundError

If the program executable is not found on the system at execution time. This likely means the program is not installed or not available on the $PATH.

AdapterInputError

If the input is invalid for the adapter.

ExternalProgramExecutionError

If the QC program fails during execution.

QCEngineError

If QCEngine performs the computation raises an error.

Source code in qcop/adapters/base.py
 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def compute(
    self,
    inp_obj: InputType,
    *,
    scratch_dir: Optional[StrOrPath] = None,
    rm_scratch_dir: bool = True,
    collect_stdout: bool = True,
    collect_files: bool = False,
    collect_wfn: bool = False,
    update_func: Optional[Callable] = None,
    update_interval: Optional[float] = None,
    print_stdout: bool = False,
    raise_exc: bool = True,
    propagate_wfn: bool = False,
    **adapter_kwargs,
) -> ProgramOutput[InputType, ResultsType]:
    """Compute the given input using the adapter's program.

    Args:
        inp_obj: A qcio input object for a computation. E.g. A FileInput,
            ProgramInput or DualProgramInput.
        scratch_dir: The scratch directory for the program. If None, a new directory
            is created in the system default temporary directory. If rm_scratch_dir
            is True this directory will be deleted after the program finishes.
        rm_scratch_dir: Delete the scratch directory after the program exits.
        collect_stdout: Whether to collect stdout/stderr from the program as output.
            Failed computations will always collect stdout/stderr.
        collect_files: Collect all files generated by the QC program as output.
        collect_wfn: Collect the wavefunction file(s) from the calculation.
            Not every program will support this. Use collect_files to collect
            all files including the wavefunction.
        update_func: A function to call as the program executes. The function must
            accept the in-process stdout/stderr output as a string for its first
            argument.
        update_interval: The minimum time in seconds between calls to the
            update_func.
        print_stdout: Whether to print stdout/stderr to the terminal in real time as
            the program executes. Will be ignored if an update_func passed.
        raise_exc: If False, qcop will return a ProgramOutput object when the QC
            program fails rather than raise an exception.
        propagate_wfn: For any adapter performing a sequential task, such
            as a geometry optimization, propagate the wavefunction from the previous
            step to the next step. This is useful for accelerating convergence by
            using a previously computed wavefunction as a starting guess. If an
            adapter does not support wavefunction propagation, an AdapterInputError
            will be raised.
        **adapter_kwargs: Additional keyword arguments to pass to the adapter or
            qcng.compute().

    Returns:
        A ProgramOutput object containing the results of the computation.

    Raises:
        AdapterNotFoundError: If the program is not supported (i.e., no Adapter
            is implemented for the program in qcop or qcengine).
        ProgramNotFoundError: If the program executable is not found on the
            system at execution time. This likely means the program is not installed
            or not available on the $PATH.
        AdapterInputError: If the input is invalid for the adapter.
        ExternalProgramExecutionError: If the QC program fails during execution.
        QCEngineError: If QCEngine performs the computation raises an error.
    """
    # Print stdout to terminal in real time as program executes
    if print_stdout and update_func is None:
        update_func, update_interval = (
            lambda _, stdout_new: print(stdout_new),
            0.1,
        )

    # cd to a temporary directory to run the program.
    with tmpdir(self.uses_files, scratch_dir, rm_scratch_dir) as final_scratch_dir:
        if self.uses_files:  # Write non structured input files to disk.
            inp_obj.save_files()

        # Define outputs
        output_dict: dict[str, Any] = {}
        stdout: Optional[str] = None
        results: Results
        exc: Optional[QCOPBaseError] = None
        program_version: Optional[str] = None

        start = time()
        try:
            # Validate input object
            self.validate_input(inp_obj)

            # Execute the program. results will be None if FileInput
            results, stdout = self.compute_results(
                inp_obj,
                update_func,
                update_interval,
                propagate_wfn=propagate_wfn,
                **adapter_kwargs,
            )
            # None value covers FileInput case
            # TODO: Is there a type safe way to handle this??
            output_dict["success"] = True

            # Optionally collect wavefunction file
            if collect_wfn and not collect_files:
                output_dict["files"] = self.collect_wfn()

        except QCOPBaseError as e:
            # TODO: Is there a type safe way to handle this??
            exc = e
            output_dict["success"] = False
            # Any half-completed results
            results = getattr(e, "results") or Files()
            stdout = getattr(e, "stdout", stdout)
            # For mypy because e.stdout is not of a known type
            stdout = str(stdout) if stdout is not None else None
            output_dict["traceback"] = traceback.format_exc()

        wall_time = time() - start
        program_version = self.program_version(stdout)

        # Construct Provenance object
        provenance = construct_provenance(
            self.program,
            program_version,
            final_scratch_dir,
            wall_time,
        )

        # Always collect for failures; otherwise obey collect_stdout
        stdout = stdout if not output_dict["success"] or collect_stdout else None

        # Construct output object
        output_dict.update(
            {
                "input_data": inp_obj,
                "stdout": stdout,
                "results": results,
                "provenance": provenance,
            }
        )
        output_obj = ProgramOutput[InputType, ResultsType](**output_dict)

        # Collect files generated by the program
        if self.uses_files and (collect_files or type(inp_obj) is FileInput):
            output_obj.results.add_files(
                final_scratch_dir,
                recursive=True,
                exclude=list(inp_obj.files.keys()),
            )

    # Append ProgramOutput to exception and raise if raise_exc=True
    # Helpful for BigChem and ChemCloud exception handling
    if raise_exc and exc:
        exc.program_output = output_obj
        # Updating .args is necessary for Celery to properly serialize the exception
        exc.args = (*exc.args, output_obj)
        raise exc

    return output_obj