def test_geometric_retries(failure_engine, input_data): failure_engine.iter_modes = ["random_error", "pass", "random_error", "random_error", "pass"] # Iter 1 # Iter 2 failure_engine.iter_modes.extend(["pass"] * 20) input_data["initial_molecule"] = { "symbols": ["He", "He"], "geometry": [0, 0, 0, 0, 0, failure_engine.start_distance], } input_data["input_specification"]["model"] = {"method": "something"} input_data["keywords"]["program"] = failure_engine.name input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "geometric", local_options={"ncores": 13}, raise_error=True) assert ret.success is True assert ret.trajectory[0].provenance.retries == 1 assert ret.trajectory[0].provenance.ncores == 13 assert ret.trajectory[1].provenance.retries == 2 assert ret.trajectory[1].provenance.ncores == 13 assert "retries" not in ret.trajectory[2].provenance.dict() # Ensure we still fail failure_engine.iter_modes = ["random_error", "pass", "random_error", "random_error", "pass"] # Iter 1 # Iter 2 ret = qcng.compute_procedure(input_data, "geometric", local_options={"ncores": 13, "retries": 1}) assert ret.success is False assert ret.input_data["trajectory"][0]["provenance"]["retries"] == 1 assert len(ret.input_data["trajectory"]) == 2
def test_nwchem_restart(tmpdir): # Make the input file input_data = { "input_specification": { "model": { "method": "HF", "basis": "sto-3g" }, "keywords": { "driver__maxiter": 2, "set__driver:linopt": 0 }, "extras": { "allow_restarts": True }, }, "initial_molecule": qcng.get_molecule("hydrogen"), } input_data = OptimizationInput(**input_data) # Run an initial step, which should not converge local_opts = {"scratch_messy": True, "scratch_directory": str(tmpdir)} ret = qcng.compute_procedure(input_data, "nwchemdriver", local_options=local_opts, raise_error=False) assert not ret.success # Run it again, which should converge new_ret = qcng.compute_procedure(input_data, "nwchemdriver", local_options=local_opts, raise_error=True) assert new_ret.success
def test_geometric_stdout(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = dc.get_molecule("water") inp["input_specification"]["model"] = {"method": "UFF", "basis": ""} inp["keywords"]["program"] = "rdkit" ret = dc.compute_procedure(inp, "geometric") assert ret["success"] is True assert "Converged!" in ret["stdout"] assert ret["stderr"] == "No stderr recieved." with pytest.raises(ValueError): ret = dc.compute_procedure(inp, "rdkit", raise_error=True)
def test_geometric_rdkit_error(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = dc.get_molecule("water") del inp["initial_molecule"]["connectivity"] inp["input_specification"]["model"] = {"method": "UFF", "basis": ""} inp["keywords"]["program"] = "rdkit" ret = dc.compute_procedure(inp, "geometric") assert ret["success"] is False assert isinstance(ret["error_message"], str) with pytest.raises(ValueError): ret = dc.compute_procedure(inp, "rdkit", raise_error=True)
def test_geometric_psi4(input_data): input_data["initial_molecule"] = qcng.get_molecule("hydrogen") input_data["input_specification"]["model"] = { "method": "HF", "basis": "sto-3g" } input_data["input_specification"]["keywords"] = { "scf_properties": ["wiberg_lowdin_indices"] } input_data["keywords"]["program"] = "psi4" input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "geometric", raise_error=True) assert 10 > len(ret.trajectory) > 1 assert pytest.approx(ret.final_molecule.measure([0, 1]), 1.0e-4) == 1.3459150737 assert ret.provenance.creator.lower() == "geometric" assert ret.trajectory[0].provenance.creator.lower() == "psi4" # Check keywords passing for single in ret.trajectory: assert "scf_properties" in single.keywords assert "WIBERG_LOWDIN_INDICES" in single.extras["qcvars"]
def _execute_qcengine(self, input_data, procedure="geometric", local_options=None, scf_maxiter=None, geometric_maxiter=None, geometric_coordsys=None, geometric_qccnv=None): import qcengine # inject reset=True fix, set coordsys to 'dlc' input_data['keywords']['reset'] = True input_data['keywords']['coordsys'] = 'dlc' # inject exposed convergence parameters, if specified if scf_maxiter is not None: input_data['input_specification']['keywords'][ 'maxiter'] = scf_maxiter if geometric_maxiter is not None: input_data['keywords']['maxiter'] = geometric_maxiter if geometric_coordsys is not None: input_data['keywords']['coordsys'] = geometric_coordsys if geometric_qccnv is not None: input_data['keywords']['qccnv'] = geometric_qccnv return qcengine.compute_procedure(input_data, procedure=procedure, local_options=local_options)
def test_geometric_generic(input_data, program, model, bench): input_data["initial_molecule"] = qcng.get_molecule("water") input_data["input_specification"]["model"] = model input_data["keywords"]["program"] = program input_data["input_specification"]["extras"] = { "_secret_tags": { "mysecret_tag": "data1" } } ret = qcng.compute_procedure(input_data, "geometric", raise_error=True) assert ret.success is True assert "Converged!" in ret.stdout r01, r02, r12, a102 = ret.final_molecule.measure([[0, 1], [0, 2], [1, 2], [1, 0, 2]]) assert pytest.approx(r01, 1.0e-4) == bench[0] assert pytest.approx(r02, 1.0e-4) == bench[0] assert pytest.approx(r12, 1.0e-4) == bench[1] assert pytest.approx(a102, 1.0e-4) == bench[2] assert "_secret_tags" in ret.trajectory[0].extras assert "data1" == ret.trajectory[0].extras["_secret_tags"]["mysecret_tag"]
def relax_structure(smiles: str, qc_config: QCInputSpecification, compute_config: Optional[Union[TaskConfig, Dict]] = None, compute_connectivity: bool = False, code: str = _code) -> Tuple[str, float]: """Compute the atomization energy of a molecule given the SMILES string Args: smiles (str): SMILES of a molecule qc_config (dict): Quantum Chemistry configuration used for evaluating the energy compute_config (TaskConfig): Configuration for the quantum chemistry code compute_connectivity (bool): Whether we must compute connectivity before calling code code (str): Which QC code to use for the evaluation Returns: (str): Structure of the molecule (float): Electronic energy of this molecule """ # Generate 3D coordinates by minimizing MMFF forcefield xyz = generate_atomic_coordinates(smiles) mol = Molecule.from_data(xyz, dtype='xyz') # Generate connectivity, if needed if compute_connectivity: conn = guess_connectivity(mol.symbols, mol.geometry, default_connectivity=1.0) mol = Molecule.from_data({**mol.dict(), 'connectivity': conn}) # Run the relaxation opt_input = OptimizationInput(input_specification=qc_config, initial_molecule=mol, keywords={'program': code, 'convergence_set': 'GAU_VERYTIGHT'}) res: OptimizationResult = \ compute_procedure(opt_input, 'geometric', local_options=compute_config, raise_error=True) return res.final_molecule.to_string('xyz'), res.energies[-1]
def relax_structure(xyz: str, qc_config: QCInputSpecification, charge: int = 0, compute_config: Optional[Union[TaskConfig, Dict]] = None, code: str = _code) -> OptimizationResult: """Compute the atomization energy of a molecule given the SMILES string Args: xyz (str): Structure of a molecule in XYZ format qc_config (dict): Quantum Chemistry configuration used for evaluating the energy charge (int): Charge of the molecule compute_config (TaskConfig): Configuration for the quantum chemistry code, such as parallelization settings code (str): Which QC code to use for the evaluation Returns: (OptimizationResult): Full output from the calculation """ # Parse the molecule mol = Molecule.from_data(xyz, dtype='xyz', molecular_charge=charge) # Run the relaxation if code == "nwchem": keywords = {"driver__maxiter": 100, "set__driver:linopt": 0} relax_code = "nwchemdriver" else: keywords = {"program": code} relax_code = "geometric" opt_input = OptimizationInput(input_specification=qc_config, initial_molecule=mol, keywords=keywords) return compute_procedure(opt_input, relax_code, local_options=compute_config, raise_error=True)
def _spawn_optimization( grid_point: str, job: List[float], input_model: "TorsionDriveInput", config: "TaskConfig" ) -> Union[FailedOperation, OptimizationResult]: """Spawns an optimization at a particular grid point and returns the result. Parameters ---------- grid_point A string of the form 'dihedral_1_angle ... dihedral_n_angle' that encodes the current dihedrals angles to optimize at. job The flattened conformer of the molecule to start the optimization at with length=(n_atoms * 3) input_model The input model containing the relevant settings for how to optimize the structure. config The configuration to launch the task using. Returns ------- The result of the optimization if successful, otherwise an error containing object. """ from qcengine import compute_procedure input_molecule = input_model.initial_molecule[0].copy(deep=True).dict() input_molecule["geometry"] = np.array(job).reshape( len(input_molecule["symbols"]), 3) input_molecule = Molecule.from_data(input_molecule) dihedrals = input_model.keywords.dihedrals angles = grid_point.split() keywords = { **input_model.optimization_spec.keywords, "constraints": { "set": [{ "type": "dihedral", "indices": dihedral, "value": int(angle), } for dihedral, angle in zip(dihedrals, angles)] }, } input_data = OptimizationInput( keywords=keywords, extras={}, protocols=input_model.optimization_spec.protocols, input_specification=input_model.input_specification, initial_molecule=input_molecule, ) return compute_procedure( input_data, procedure=input_model.optimization_spec.procedure, local_options=config.dict())
def test_geometric_torchani(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = qcng.get_molecule("water") inp["input_specification"]["model"] = {"method": "ANI1x", "basis": None} inp["keywords"]["program"] = "torchani" ret = qcng.compute_procedure(inp, "geometric", raise_error=True) assert ret.success is True assert "Converged!" in ret.stdout
def test_geometric_torchani(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = dc.get_molecule("water") inp["input_specification"]["model"] = {"method": "ANI1", "basis": None} inp["keywords"]["program"] = "torchani" ret = dc.compute_procedure(inp, "geometric") assert ret["success"] is True assert "Converged!" in ret["stdout"] assert ret["stderr"] == "No stderr recieved."
def test_geometric_rdkit_error(input_data): input_data["initial_molecule"] = qcng.get_molecule("water").copy(exclude={"connectivity_"}) input_data["input_specification"]["model"] = {"method": "UFF", "basis": ""} input_data["keywords"]["program"] = "rdkit" input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "geometric") assert ret.success is False assert isinstance(ret.error.error_message, str)
def test_geometric_stdout(input_data): input_data["initial_molecule"] = qcng.get_molecule("water") input_data["input_specification"]["model"] = {"method": "UFF", "basis": ""} input_data["keywords"]["program"] = "rdkit" input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "geometric", raise_error=True) assert ret.success is True assert "Converged!" in ret.stdout
def test_geometric_psi4(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = dc.get_molecule("hydrogen") inp["input_specification"]["model"] = {"method": "HF", "basis": "sto-3g"} inp["keywords"]["program"] = "psi4" ret = dc.compute_procedure(inp, "geometric") assert 10 > len(ret["trajectory"]) > 1 geom = ret["final_molecule"]["geometry"] assert pytest.approx(_bond_dist(geom, 0, 1), 1.e-4) == 1.3459150737
def test_berny_stdout(input_data): input_data["initial_molecule"] = qcng.get_molecule("water") input_data["input_specification"]["model"] = { "method": "HF", "basis": "sto-3g" } input_data["keywords"]["program"] = "psi4" input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "berny", raise_error=True) assert ret.success is True assert "All criteria matched" in ret.stdout
def test_geometric_local_options(input_data): input_data["initial_molecule"] = qcng.get_molecule("hydrogen") input_data["input_specification"]["model"] = {"method": "HF", "basis": "sto-3g"} input_data["keywords"]["program"] = "psi4" input_data = OptimizationInput(**input_data) # Set some extremely large number to test ret = qcng.compute_procedure(input_data, "geometric", raise_error=True, local_options={"memory": "5000"}) assert pytest.approx(ret.trajectory[0].provenance.memory, 1) == 4900 # Make sure we cleaned up assert "_qcengine_local_config" not in ret.input_specification assert "_qcengine_local_config" not in ret.trajectory[0].extras
def test_optimization_protocols(input_data): input_data["initial_molecule"] = qcng.get_molecule("water") input_data["input_specification"]["model"] = {"method": "UFF"} input_data["keywords"]["program"] = "rdkit" input_data["protocols"] = {"trajectory": "initial_and_final"} input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "geometric", raise_error=True) assert ret.success, ret.error.error_message assert len(ret.trajectory) == 2 assert ret.initial_molecule.get_hash() == ret.trajectory[0].molecule.get_hash() assert ret.final_molecule.get_hash() == ret.trajectory[1].molecule.get_hash()
def test_geometric_psi4(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = qcng.get_molecule("hydrogen") inp["input_specification"]["model"] = {"method": "HF", "basis": "sto-3g"} inp["keywords"]["program"] = "psi4" inp = OptimizationInput(**inp) ret = qcng.compute_procedure(inp, "geometric", raise_error=True) assert 10 > len(ret.trajectory) > 1 assert pytest.approx(ret.final_molecule.measure([0, 1]), 1.e-4) == 1.3459150737 assert ret.provenance.creator.lower() == "geometric" assert ret.trajectory[0].provenance.creator.lower() == "psi4"
def test_berny_failed_gradient_computation(input_data): input_data["initial_molecule"] = qcng.get_molecule("water") input_data["input_specification"]["model"] = { "method": "HF", "basis": "sto-3g" } input_data["input_specification"]["keywords"] = { "badpsi4key": "badpsi4value" } input_data["keywords"]["program"] = "psi4" input_data = OptimizationInput(**input_data) ret = qcng.compute_procedure(input_data, "berny", raise_error=False) assert isinstance(ret, FailedOperation) assert ret.success is False assert ret.error.error_type == qcng.exceptions.InputError.error_type
def test_torsiondrive_generic(): input_data = TorsionDriveInput( keywords=TDKeywords(dihedrals=[(2, 0, 1, 5)], grid_spacing=[180]), input_specification=QCInputSpecification(driver=DriverEnum.gradient, model=Model(method="UFF", basis=None)), initial_molecule=[qcng.get_molecule("ethane")] * 2, optimization_spec=OptimizationSpecification( procedure="geomeTRIC", keywords={ "coordsys": "dlc", "maxiter": 300, "program": "rdkit", }, ), ) ret = qcng.compute_procedure(input_data, "torsiondrive", raise_error=True) assert ret.error is None assert ret.success expected_grid_ids = {"180", "0"} assert {*ret.optimization_history} == expected_grid_ids assert {*ret.final_energies} == expected_grid_ids assert {*ret.final_molecules} == expected_grid_ids assert (pytest.approx(ret.final_molecules["180"].measure([2, 0, 1, 5]), abs=1.0e-2) == 180.0 or pytest.approx(ret.final_molecules["180"].measure([2, 0, 1, 5]), abs=1.0e-2) == -180.0) assert pytest.approx(ret.final_molecules["0"].measure([2, 0, 1, 5]), abs=1.0e-2) == 0.0 assert ret.provenance.creator.lower() == "torsiondrive" assert ret.optimization_history["180"][0].provenance.creator.lower( ) == "geometric" assert ret.optimization_history["180"][0].trajectory[ 0].provenance.creator.lower() == "rdkit" assert ret.stdout == "All optimizations converged at lowest energy. Job Finished!\n"
def test_geometric_local_options(): inp = copy.deepcopy(_base_json) inp["initial_molecule"] = dc.get_molecule("hydrogen") inp["input_specification"]["model"] = {"method": "HF", "basis": "sto-3g"} inp["keywords"]["program"] = "psi4" inp = OptimizationInput(**inp) # Set some extremely large number to test ret = dc.compute_procedure(inp, "geometric", raise_error=True, local_options={"memory": "5000"}) assert pytest.approx(ret["trajectory"][0]["provenance"]["memory"], 1) == 4900 # Make sure we cleaned up assert "_qcengine_local_config" not in ret["input_specification"] assert "_qcengine_local_config" not in ret["trajectory"][0]
def test_nwchem_relax(linopt): # Make the input file input_data = { "input_specification": { "model": { "method": "HF", "basis": "sto-3g" }, "keywords": { "set__driver:linopt": linopt }, }, "initial_molecule": qcng.get_molecule("hydrogen"), } input_data = OptimizationInput(**input_data) # Run the relaxation ret = qcng.compute_procedure(input_data, "nwchemdriver", raise_error=True) assert 10 > len(ret.trajectory) > 1 assert pytest.approx(ret.final_molecule.measure([0, 1]), 1.0e-4) == 1.3459150737
def call_qcengine(self, engine, driver, input_type): """ Using the created schema, run a particular engine, specifying the driver (job type). e.g. engine: geo, driver: energies. :param engine: The engine to be used psi4 geometric :param driver: The calculation type to be done e.g. energy, gradient, hessian, properties :param input_type: The part of the molecule object that should be used when making the schema :return: The required driver information """ mol = self.generate_qschema(input_type=input_type) # Call psi4 for energy, gradient, hessian or property calculations if engine == 'psi4': psi4_task = qcel.models.ResultInput( molecule=mol, driver=driver, model={ 'method': self.molecule.theory, 'basis': self.molecule.basis }, keywords={'scf_type': 'df'}, ) ret = qcng.compute(psi4_task, 'psi4', local_options={ 'memory': self.molecule.memory, 'ncores': self.molecule.threads }) if driver == 'hessian': hess_size = 3 * len(self.molecule.atoms) conversion = constants.HA_TO_KCAL_P_MOL / ( constants.BOHR_TO_ANGS**2) hessian = np.reshape(ret.return_result, (hess_size, hess_size)) * conversion check_symmetry(hessian) return hessian else: return ret.return_result # Call geometric with psi4 to optimise a molecule elif engine == 'geometric': geo_task = { 'schema_name': 'qcschema_optimization_input', 'schema_version': 1, 'keywords': { 'coordsys': 'tric', 'maxiter': self.molecule.iterations, 'program': 'psi4', 'convergence_set': self.molecule.convergence, }, 'input_specification': { 'schema_name': 'qcschema_input', 'schema_version': 1, 'driver': 'gradient', 'model': { 'method': self.molecule.theory, 'basis': self.molecule.basis }, 'keywords': {}, }, 'initial_molecule': mol, } return qcng.compute_procedure(geo_task, 'geometric', return_dict=True, local_options={ 'memory': self.molecule.memory, 'ncores': self.molecule.threads }) else: raise KeyError( 'Invalid engine type provided. Please use "geo" or "psi4".')
def test_procedure_avail_bounce(): with pytest.raises(qcng.exceptions.InputError) as exc: qcng.compute_procedure({}, "bad_program", raise_error=True) assert "not registered" in str(exc.value)
def optimise( self, molecule: Ligand, allow_fail: bool = False, return_result: bool = False, extras: Optional[Dict[str, Any]] = None, ) -> Tuple[Ligand, Optional[qcel.models.OptimizationResult]]: """ For the given specification in the class run an optimisation on the ligand. Run the specified optimisation on the ligand the final coordinates are extracted and stored in the ligand. The optimisation schema is dumped to file along with the optimised geometry and the trajectory. Args: molecule: The molecule which should be optimised allow_fail: If we should not raise an error if the molecule fails to be optimised, this will extract the last geometry from the trajectory and return it. return_result: If the full result json should also be returned useful for extracting the trajectory. extras: A dictionary of extras that should be used to update the optimiser keywords. Returns: A new copy of the molecule at the optimised coordinates. """ # first validate the settings self._validate_specification() # now validate that the programs are installed self.check_available(program=self.program, optimiser=self.optimiser) # now we need to distribute the job model = self.qc_model specification = qcel.models.procedures.QCInputSpecification( model=model, keywords={"dft_spherical_points": 590, "dft_radial_points": 99} ) initial_mol = molecule.to_qcschema() optimiser_keywords = self.build_optimiser_keywords() if extras is not None: optimiser_keywords.update(extras) opt_task = qcel.models.OptimizationInput( initial_molecule=initial_mol, input_specification=specification, keywords=optimiser_keywords, ) opt_result = qcng.compute_procedure( input_data=opt_task, procedure=self.optimiser, raise_error=False, local_options=self.local_options, ) # dump info to file result_mol = self.handle_output(molecule=molecule, opt_output=opt_result) # check if we can/have failed and raise the error if not opt_result.success and not allow_fail: raise RuntimeError( f"{opt_result.error.error_type}: {opt_result.error.error_message}" ) full_result = opt_result if return_result else None return result_mol, full_result
def optimize_qc_engine( elements: Iterable[int] | Iterable[str], coordinates: ArrayLike2D, charge: int | None = None, multiplicity: int | None = None, connectivity_matrix: ArrayLike2D | None = None, program: str = "xtb", model: dict[str, Any] | None = None, keywords: dict[str, Any] | None = None, local_options: dict[str, Any] | None = None, procedure: str = "berny", return_trajectory: bool = False, ) -> tuple[Array2DFloat | Array3DFloat, Array1DFloat]: """Optimize molecule with QCEngine. Args: elements: Elements as atomic symbols or numbers coordinates: Coordinates (Å) charge: Molecular charge multiplicity: Multiplicity connectivity_matrix: Connectivity matrix program: QCEngine program model: QCEngine model keywords: QCEngine keywords local_options: QCEngine local options procedure: QCEngine procedure return_trajectory: Return coordinates for all steps Returns: opt_coordinates (ndarray): Conformer coordinates (Å) energies (ndarray): Energies for all steps (a.u.) Raises: Exception: When QCEngine calculation fails """ if (program.lower() == "rdkit" and charge is not None and connectivity_matrix is not None): _check_qcng_rdkit(charge, connectivity_matrix) # Set defaults if model is None: model = {"method": "GFN2-xTB"} if keywords is None: keywords = {} if local_options is None: local_options = {} # Create molecule object molecule = _generate_qcel_molecule(elements, coordinates, charge, multiplicity, connectivity_matrix) # Create optimization input opt_input = { "keywords": { "program": program }, "input_specification": { "driver": "gradient", "model": model, "keywords": keywords, }, "initial_molecule": molecule, } # Perform optimization opt = qcng.compute_procedure(opt_input, procedure=procedure, local_options=local_options) if not opt.success: raise Exception(opt.error.error_message) # Take out results energies: Array1DFloat = np.array(opt.energies) if return_trajectory: opt_coordinates: Array2DFloat = np.array( [result.molecule.geometry for result in opt.trajectory]) else: opt_coordinates = opt.final_molecule.geometry opt_coordinates *= BOHR_TO_ANGSTROM return opt_coordinates, energies
def call_qcengine(self, engine, driver): """ Using the created schema, run a particular engine, specifying the driver (job type). e.g. engine: geo, driver: energies. :param engine: The engine to be used psi4 geometric :param driver: The calculation type to be done e.g. energy, gradient, hessian, properties :return: The required driver information """ mol = self.generate_qschema() options = {"memory": self.molecule.memory, "ncores": self.molecule.threads} # Call psi4 for energy, gradient, hessian or property calculations if engine == "psi4": psi4_task = qcel.models.AtomicInput( molecule=mol, driver=driver, model={"method": self.molecule.theory, "basis": self.molecule.basis}, keywords={"scf_type": "df"}, ) ret = qcng.compute(psi4_task, "psi4", local_options=options) if driver == "hessian": hess_size = 3 * len(self.molecule.atoms) conversion = constants.HA_TO_KCAL_P_MOL / (constants.BOHR_TO_ANGS ** 2) hessian = ( np.reshape(ret.return_result, (hess_size, hess_size)) * conversion ) check_symmetry(hessian) return hessian else: return ret.return_result # Call geometric with psi4 to optimise a molecule elif engine == "geometric": geo_task = { "schema_name": "qcschema_optimization_input", "schema_version": 1, "keywords": { "coordsys": "dlc", "maxiter": self.molecule.iterations, "program": "psi4", "convergence_set": self.molecule.convergence, }, "input_specification": { "schema_name": "qcschema_input", "schema_version": 1, "driver": "gradient", "model": { "method": self.molecule.theory, "basis": self.molecule.basis, }, "keywords": {}, }, "initial_molecule": mol, } return qcng.compute_procedure( geo_task, "geometric", return_dict=True, local_options=options ) elif engine == "torchani": ani_task = qcel.models.AtomicInput( molecule=mol, driver=driver, model={"method": "ANI1x", "basis": None}, keywords={"scf_type": "df"}, ) return qcng.compute( ani_task, "torchani", local_options=options ).return_result else: raise KeyError( 'Invalid engine type provided. Please use "geometric", "psi4" or "torchani".' )
def call_qcengine(self, engine, driver, input_type): """ Using the created schema, run a particular engine, specifying the driver (job type). e.g. engine: geo, driver: energies. :param engine: The engine to be used psi4 geometric :param driver: The calculation type to be done e.g. energy, gradient, hessian, properties :param input_type: The part of the molecule object that should be used when making the schema :return: The required driver information """ mol = self.generate_qschema(input_type=input_type) # Call psi4 for energy, gradient, hessian or property calculations if engine == 'psi4': psi4_task = qcel.models.ResultInput( molecule=mol, driver=driver, model={ 'method': self.molecule.theory, 'basis': self.molecule.basis }, keywords={'scf_type': 'df'}, ) ret = qcng.compute(psi4_task, 'psi4', local_options={ 'memory': self.molecule.memory, 'ncores': self.molecule.threads }) if driver == 'hessian': hess_size = 3 * len(self.molecule.molecule[input_type]) hessian = np.reshape( ret.return_result, (hess_size, hess_size)) * 627.509391 / (0.529**2) check_symmetry(hessian) return hessian else: return ret.return_result # Call geometric with psi4 to optimise a molecule elif engine == 'geometric': geo_task = { "schema_name": "qcschema_optimization_input", "schema_version": 1, "keywords": { "coordsys": "tric", "maxiter": self.molecule.iterations, "program": "psi4", "convergence_set": self.molecule.convergence, }, "input_specification": { "schema_name": "qcschema_input", "schema_version": 1, "driver": 'gradient', "model": { 'method': self.molecule.theory, 'basis': self.molecule.basis }, "keywords": {}, }, "initial_molecule": mol, } # TODO hide the output stream so it does not spoil the terminal printing # return_dict=True seems to be default False in newer versions. Ergo docs are wrong again. ret = qcng.compute_procedure(geo_task, 'geometric', return_dict=True, local_options={ 'memory': self.molecule.memory, 'ncores': self.molecule.threads }) return ret else: raise KeyError( 'Invalid engine type provided. Please use "geo" or "psi4".')