def test_program_not_installed(): """ Make sure an error is raised when we try and use a program that is not available. """ g_opt = GeometryOptimiser() with pytest.raises(SpecificationError): g_opt.program = "test"
def test_optimiser_keywords(optimiser): """ For the given optimiser make sure the keywords are updated correctly. """ g = GeometryOptimiser(optimiser=optimiser, maxiter=1, convergence="GAU") keywords = g._build_optimiser_keywords(program="psi4") assert 1 in keywords.values() assert "GAU" in keywords.values()
def _run_qm_opt( self, molecule: "Ligand", conformers: List[np.array], qc_spec: QCOptions, local_options: LocalResource, ) -> "Ligand": """ Run the main QM optimisation on each of the input conformers in order and stop when we get a fully optimised structure. Args: molecule: The qubekit molecule to run the optimisation on. conformers: The list of pre-optimised conformers. qc_spec: The QCSpec used to run the QM optimisation. local_options: The local resource that is available for the optimisation. """ opt_mol = deepcopy(molecule) g_opt = GeometryOptimiser(convergence=self.convergence_criteria, maxiter=50) for i, conformer in enumerate( tqdm(conformers, desc="Optimising conformer", total=len(conformers), ncols=80)): with folder_setup(folder_name=f"conformer_{i}"): # set the coords opt_mol.coordinates = conformer # errors are auto raised from the class so catch the result, and write to file qm_result, result = g_opt.optimise( molecule=opt_mol, allow_fail=True, return_result=True, qc_spec=qc_spec, local_options=local_options, ) if result.success: break else: # grab last coords and bump coords = qm_result.coordinates + np.random.choice( a=[0, 0.01], size=(qm_result.n_atoms, 3)) opt_mol.coordinates = coords bump_mol, bump_result = g_opt.optimise( molecule=opt_mol, allow_fail=True, return_result=True, local_options=local_options, qc_spec=qc_spec, ) if bump_result.success: qm_result = bump_mol break else: raise GeometryOptimisationError( "No molecule conformer could be optimised to GAU TIGHT") return qm_result
def test_optimiser_keywords(optimiser): """ For the given optimiser make sure the keywords are updated correctly. """ if "psi4" not in qcengine.list_available_programs(): pytest.skip("Psi4 missing skipping.") g = GeometryOptimiser( optimiser=optimiser, maxiter=1, convergence="GAU", program="psi4" ) keywords = g.build_optimiser_keywords() assert 1 in keywords.values() assert "GAU" in keywords.values()
def test_optmiser_fail_no_output(tmpdir): """ Make sure we raise an error correctly when there is no output from a failed optimisation. """ if "psi4" not in qcengine.list_available_programs(): pytest.skip("Psi4 missing skipping test.") with tmpdir.as_cwd(): mol = Ligand.from_file(file_name=get_data("water.pdb")) g = GeometryOptimiser( program="psi4", method="wb97x-dbj", basis="dzvp", maxiter=10 ) with pytest.raises(RuntimeError): g.optimise(molecule=mol, allow_fail=False)
def test_optking_fail(): """ Optking currently only works with psi4 make sure we raise an error if we use a different program. """ with pytest.raises(SpecificationError): mol = Ligand.from_file(file_name=get_data("water.pdb")) g = GeometryOptimiser(optimiser="optking") g.optimise( molecule=mol, qc_spec=QCOptions(program="rdkit", basis=None, method="uff"), local_options=LocalResource(cores=1, memory=1), )
def test_optimise_fail_output(tmpdir): """ Make sure the optimised geometries and result is still wrote out if we fail the molecule and an error is rasied. """ with tmpdir.as_cwd(): mol = Ligand.from_file(file_name=get_data("water.pdb")) g = GeometryOptimiser( program="torchani", method="ani1ccx", basis=None, maxiter=5 ) with pytest.raises(RuntimeError): g.optimise(molecule=mol, allow_fail=False) files = os.listdir() assert "opt.xyz" in files assert "opt_trajectory.xyz" in files assert "result.json" in files
def test_spec_validation_pass(program, basis, method): """ Make sure we can correctly validate a specification. """ if program not in qcengine.list_available_programs(): pytest.skip(f"{program} missing skipping test") _ = GeometryOptimiser(program=program, basis=basis, method=method)
def test_optking_fail(): """ Optking currently only works with psi4 make sure we raise an error if we use a different program. """ with pytest.raises(SpecificationError): GeometryOptimiser(optimiser="optking", program="rdkit")
def test_spec_validation_fail(program, basis, method): """ Make sure than an invalid specification raises an error. """ if program not in qcengine.list_available_programs(): pytest.skip(f"{program} missing skipping test") with pytest.raises(SpecificationError): _ = GeometryOptimiser(program=program, basis=basis, method=method)
def test_local_options(): """ Make sure the task config is correctly made and keywords are converted. """ g_opt = GeometryOptimiser(cores=10, memory=2) local_options = g_opt.local_options assert g_opt.cores == local_options["ncores"] assert g_opt.memory == local_options["memory"]
def test_optimise(program, basis, method, tmpdir): """ Test running different optimisers with different programs. """ if program not in qcengine.list_available_programs(): pytest.skip(f"{program} missing skipping test.") with tmpdir.as_cwd(): mol = Ligand.from_file(get_data("water.pdb")) g = GeometryOptimiser( program=program, basis=basis, method=method, optimiser="geometric", convergence="GAU", ) result_mol, _ = g.optimise(molecule=mol, return_result=False) assert result_mol.coordinates.tolist() != mol.coordinates.tolist()
def _pre_opt(self, molecule: "Ligand", qc_spec: QCOptions, local_options: LocalResource) -> List[np.array]: """Run the pre optimisation stage and return the optimised conformers ready for QM optimisation. Args: molecule: The qubekit molecule to run the optimisation on. Returns: A list of final coordinates from the optimisations. """ from multiprocessing import Pool g_opt = GeometryOptimiser(convergence="GAU", maxiter=self.maxiter) # generate the input conformations, number will include the input conform if provided geometries = molecule.generate_conformers( n_conformers=self.seed_conformers) molecule.to_multiconformer_file(file_name="starting_coords.xyz", positions=geometries) opt_list = [] with Pool(processes=local_options.cores) as pool: for confomer in geometries: opt_mol = deepcopy(molecule) opt_mol.coordinates = confomer opt_list.append( pool.apply_async( g_opt.optimise, (opt_mol, qc_spec, local_options, True, True))) results = [] for result in tqdm( opt_list, desc= f"Optimising conformers with {self.pre_optimisation_method}", total=len(opt_list), ncols=80, ): # errors are auto raised from the class so catch the result, and write to file result_mol, opt_result = result.get() if opt_result.success: # save the final energy and molecule results.append((result_mol, opt_result.energies[-1])) else: # save the molecule and final energy from the last step if it fails results.append( (result_mol, opt_result.input_data["energies"][-1])) # sort the results results.sort(key=lambda x: x[1]) final_geometries = [re[0].coordinates for re in results] # write all conformers out molecule.to_multiconformer_file(file_name="final_pre_opt_coords.xyz", positions=final_geometries) return final_geometries
def test_optimise(qc_spec: QCOptions, tmpdir): """ Test running different optimisers with different programs. """ if qc_spec.program.lower() not in qcengine.list_available_programs(): pytest.skip(f"{qc_spec.program} missing skipping test.") with tmpdir.as_cwd(): mol = Ligand.from_file(get_data("water.pdb")) g = GeometryOptimiser( optimiser="geometric", convergence="GAU", ) result_mol, _ = g.optimise( molecule=mol, return_result=False, qc_spec=qc_spec, local_options=LocalResource(cores=1, memory=1), ) assert result_mol.coordinates.tolist() != mol.coordinates.tolist()
def test_missing_optimiser(): """ Make sure an error is raised when we try and set a missing optimiser. """ with pytest.raises(SpecificationError): _ = GeometryOptimiser(optimiser="bad_optimiser")
def test_rdkit_available(): """ Make sure the geometry optimiser allows rdkit as this comes with QUBEKit. """ g_opt = GeometryOptimiser() g_opt.program = "rdkit"