def set_qd(qd: Molecule, mol_dict: Settings) -> Molecule: """Update quantum dots imported by :func:`.read_mol`.""" # Create ligand (and anchor) molecules ligand = molkit.from_smiles(mol_dict.ligand_smiles) ligand_rdmol = molkit.to_rdmol(ligand) anchor = molkit.from_smiles(mol_dict.ligand_anchor) anchor_rdmol = molkit.to_rdmol(anchor) qd_rdmol = molkit.to_rdmol(qd) # Create arrays of atomic indices of the core and ligands lig_idx = 1 + np.array(qd_rdmol.GetSubstructMatches(ligand_rdmol)) core_idx = np.arange(1, len(qd))[~lig_idx] lig_idx = lig_idx.ravel().tolist() core_idx = core_idx.tolist() # Guess bonds if mol_dict.guess_bonds: qd.guess_bonds(atom_subset=[qd[i] for i in lig_idx]) # Reorder all atoms: core atoms first followed by ligands qd.atoms = [qd[i] for i in core_idx] + [qd[j] for i in lig_idx for j in i] # Construct a list with the indices of all ligand anchor atoms core_idx_max = 1 + len(core_idx) _anchor_idx = ligand_rdmol.GetSubstructMatch(anchor_rdmol)[0] start = core_idx_max + _anchor_idx stop = core_idx_max + _anchor_idx + np.product(lig_idx.shape) step = len(ligand) anchor_idx = list(range(start, stop, step)) # Update the properties of **qd** for i in anchor_idx: qd[i].properties.anchor = True qd.properties.indices = list(range(1, core_idx_max)) + anchor_idx qd.properties.job_path = [] qd.properties.name = mol_dict.name qd.properties.path = mol_dict.path qd.properties.ligand_smiles = Chem.CanonSmiles(mol_dict.ligand_smiles) qd.properties.ligand_anchor = f'{ligand[_anchor_idx].symbol}{_anchor_idx}' # Update the pdb_info of all atoms for i, at in enumerate(qd, 1): at.properties.pdb_info.SerialNumber = i if i <= core_idx_max: # A core atom at.properties.pdb_info.ResidueNumber = 1 else: # A ligand atom at.properties.pdb_info.ResidueNumber = 2 + int( (i - core_idx_max) / len(ligand))
def test_SerMolecule(): """Test molecule serialization.""" mol = molkit.from_smiles("c1ccccc1CC") registry = packages.registry() encoded_molecule = registry.deep_encode(mol) decoded_molecule = registry.deep_decode(encoded_molecule) assert len(mol) == len(decoded_molecule)
def test_freeze_with_adf(): mol = molkit.from_smiles('CO') s = Settings() s.freeze = ["C", "O"] expected_settings = Settings({'freeze': ["C", "O"], 'specific': adf_const}) assert str(adf.generic2specific(s, mol)) == str(expected_settings) s.freeze = [1, 2] expected_settings = Settings({'freeze': [1, 2], 'specific': adf_const}) assert str(adf.generic2specific(s, mol)) == str(expected_settings)
def test_selected_atoms_with_dftb(): mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings( {'selected_atoms': ['H'], 'specific': dftb_const}) assert dftb.generic2specific(s, mol) == expected_settings s.selected_atoms = indices expected_settings = Settings( {'selected_atoms': indices, 'specific': dftb_const}) assert str(dftb.generic2specific(s, mol)) == str(expected_settings)
def test_freeze_with_dftb(): mol = molkit.from_smiles('CO') s = Settings() s.freeze = ["C", "O"] expected_settings = Settings( {'freeze': ['C', 'O'], 'specific': dftb_const}) assert str(dftb.generic2specific(s, mol)) == str(expected_settings) s.freeze = [1, 2] expected_settings = Settings( {'freeze': [1, 2], 'specific': dftb_const}) assert str(dftb.generic2specific(s, mol)) == str(expected_settings)
def test_freeze_with_adf(): mol = molkit.from_smiles('CO') s = Settings() s.freeze = ["C", "O"] expected_settings = Settings( {'freeze': ["C", "O"], 'specific': adf_const}) assert str(adf.generic2specific(s, mol)) == str(expected_settings) s.freeze = [1, 2] expected_settings = Settings( {'freeze': [1, 2], 'specific': adf_const}) assert str(adf.generic2specific(s, mol)) == str(expected_settings)
def test_freeze_with_dftb(): mol = molkit.from_smiles('CO') s = Settings() s.freeze = ["C", "O"] expected_settings = Settings({ 'freeze': ['C', 'O'], 'specific': dftb_const }) assert str(dftb.generic2specific(s, mol)) == str(expected_settings) s.freeze = [1, 2] expected_settings = Settings({'freeze': [1, 2], 'specific': dftb_const}) assert str(dftb.generic2specific(s, mol)) == str(expected_settings)
def test_freeze_with_adf(): """Test the freeze keyword with ADF.""" mol = molkit.from_smiles('CO') s = Settings() s.freeze = ["C", "O"] expected_settings = Settings( {'freeze': ["C", "O"], 'specific': adf_const}) assertion.eq(str(adf.generic2specific(s, mol)), str(expected_settings)) s.freeze = [1, 2] expected_settings = Settings( {'freeze': [1, 2], 'specific': adf_const}) assertion.eq(str(adf.generic2specific(s, mol)), str(expected_settings))
def test_selected_atoms_with_gamess(): mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings( {'selected_atoms': ['H'], 'specific': gamess_sett}) assert str(gamess.generic2specific(s, mol)) == str(expected_settings) s = Settings() s.selected_atoms = indices expected_settings = Settings( {'selected_atoms': indices, 'specific': gamess_sett}) assert str(gamess.generic2specific(s, mol)) == str(expected_settings)
def test_freeze_with_dftb(): """Test freeze keyword for DFTB.""" mol = molkit.from_smiles('CO') s = Settings() s.freeze = ["C", "O"] expected_settings = Settings( {'freeze': ['C', 'O'], 'specific': dftb_const}) assertion.eq(str(dftb.generic2specific(s, mol)), str(expected_settings)) s.freeze = [1, 2] expected_settings = Settings( {'freeze': [1, 2], 'specific': dftb_const}) assertion.eq(str(dftb.generic2specific(s, mol)), str(expected_settings))
def test_selected_atoms_with_dftb(): """Test the DFTB selection atoms funcionality.""" mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings( {'selected_atoms': ['H'], 'specific': dftb_const}) assertion.eq(dftb.generic2specific(s, mol), expected_settings) s.selected_atoms = indices expected_settings = Settings( {'selected_atoms': indices, 'specific': dftb_const}) assertion.eq(str(dftb.generic2specific(s, mol)), str(expected_settings))
def test_selected_atoms_with_adf(): """Test the ADF atoms selection features.""" mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings( {'selected_atoms': ['H'], 'specific': adf_const}) assertion.eq(str(adf.generic2specific(s, mol)), str(expected_settings)) s.selected_atoms = indices expected_settings = Settings( {'selected_atoms': indices, 'specific': adf_const}) assertion.eq(str(adf.generic2specific(s, mol)), str(expected_settings))
def test_dftb_props(): """ Get properties from DFTB freq calc """ mol = molkit.from_smiles('F[H]') result = run(dftb(templates.freq, mol, job_name='dftb_FH')) expected_energy = -4.76 assert abs(result.energy - expected_energy) < 0.01 assert len(result.dipole) == 3 expected_frequency = 3460.92 assert abs(result.frequencies - expected_frequency) < 0.1 assert len(result.charges) == 2 assert abs(result.charges[0] + result.charges[1]) < 1e-6
def test_adf_props(): """ Get properties from ADF freq calc """ mol = molkit.from_smiles('F[H]') result = run(adf(templates.freq, mol, job_name='adf_FH')) expected_energy = -0.30 assert abs(result.energy - expected_energy) < 0.01 assert len(result.dipole) == 3 expected_frequency = 3480.90 assert abs(result.frequencies[1] - expected_frequency) < 0.1 assert len(result.charges) == 2 assert abs(result.charges[0] + result.charges[1]) < 1e-6
def test_selected_atoms_with_orca(): """Test atom selection features for Orca.""" mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings( {'selected_atoms': ['H'], 'specific': orca_const}) assertion.eq(str(orca.generic2specific(s, mol)), str(expected_settings)) s.selected_atoms = indices expected_settings = Settings( {'selected_atoms': indices, 'specific': orca_const}) assertion.eq(str(orca.generic2specific(s, mol)), str(expected_settings))
def test_selected_atoms_with_dftb(): mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings({ 'selected_atoms': ['H'], 'specific': dftb_const }) assert dftb.generic2specific(s, mol) == expected_settings s.selected_atoms = indices expected_settings = Settings({ 'selected_atoms': indices, 'specific': dftb_const }) assert str(dftb.generic2specific(s, mol)) == str(expected_settings)
def test_selected_atoms_with_gamess(): mol = molkit.from_smiles('CO') s = Settings() s.selected_atoms = ["H"] expected_settings = Settings({ 'selected_atoms': ['H'], 'specific': gamess_sett }) assert str(gamess.generic2specific(s, mol)) == str(expected_settings) s = Settings() s.selected_atoms = indices expected_settings = Settings({ 'selected_atoms': indices, 'specific': gamess_sett }) assert str(gamess.generic2specific(s, mol)) == str(expected_settings)
def test_hessian_transfer(): """Test DFTB -> Orca hessian transfer.""" h2o = molkit.from_smiles('O') h2o.properties.symmetry = 'C1' h2o_freq = dftb(templates.freq, h2o, job_name="freq").hessian s = Settings() s.inithess = h2o_freq h2o_opt = orca(templates.geometry.overlay(s), h2o, job_name="opt") energy = h2o_opt.energy dipole = h2o_opt.dipole wf = gather(energy, dipole) logger.info(run(wf))
def test_orca_init_hessian(): """Test the translation from settings to CP2K specific keywords.""" # Read Hessian from DFTB PATH_RKF = PATH / "output_dftb" / "dftb_freq" / "dftb.rkf" assertion.truth(PATH_RKF.exists()) hess = kfreader(PATH_RKF, section="AMSResults", prop="Hessian") water = molkit.from_smiles('[OH2]', forcefield='mmff') # Tess Hessian initialization s = Settings() hess = np.array(hess).reshape(9, 9) s.inithess = hess ORCA.handle_special_keywords(s, "inithess", hess, water) # Test that the hessian is readable new_hess = parse_hessian(s.specific.orca.geom.InHessName) new_hess = np.array(new_hess, dtype=np.float64) assertion.truth(np.allclose(hess, new_hess, atol=1e-5))
def example_partial_geometry_opt(): """ Performa partial optimization freezing the Hydrogen atoms """ methanol = molkit.from_smiles('CO') # optimize only H atoms s = Settings() s.freeze = [1, 2] geom_job1 = adf(templates.geometry.overlay(s), methanol, job_name='geom_job1').molecule # optimize only H atoms s = Settings() s.selected_atoms = ['H'] geom_job2 = adf(templates.geometry.overlay(s), methanol, job_name='geom_job2').molecule geom1, geom2 = run(gather(geom_job1, geom_job2), n_processes=1) return geom1, geom2
def example_generic_constraints(): """Run different job with geometric constrains. This examples illustrates that, by using generic keywords, it is possible to call different packages interchangeably with the same Settings. """ # build hydrogen fluoride molecule hydrogen_fluoride = molkit.from_smiles('F[H]') # loop over distances jobs = [] for distance in [1.0, 1.1, 1.2]: s = Settings() s.constraint['dist 1 2'] = distance # loop over packages for package in [dftb, adf, orca]: job_name = package.pkg_name + '_' + str(distance) constraint_opt = package(templates.geometry.overlay(s), hydrogen_fluoride, job_name) jobs.append(constraint_opt) # run the jobs results = run(gather(*jobs)) energies = [r.energy for r in results] names = [r.job_name for r in results] # put resulting energies into a dictionary table = {'dftb': {}, 'adf': {}, 'orca': {}} for r in results: package, distance = r.job_name.split('_') table[package][distance] = round(r.energy, 6) # print table hartree_to_kcalpermol = 627.094 for package in ['dftb', 'adf', 'orca']: row = [package] for distance in ['1.0', '1.1', '1.2']: val = table[package][distance] - table[package]['1.0'] row.append(round(val * hartree_to_kcalpermol, 2)) logger.info('{:10s} {:10.2f} {:10.2f} {:10.2f}'.format(*row)) return names, energies
def test_hessian_transfer(): """ Test DFTB -> Orca hessian transfer """ h2o = molkit.from_smiles('O') h2o.properties.symmetry = 'C1' h2o_freq = dftb(templates.freq, h2o, job_name="freq").hessian s = Settings() s.inithess = h2o_freq h2o_opt = orca(templates.geometry.overlay(s), h2o, job_name="opt") energy = h2o_opt.energy dipole = h2o_opt.dipole wf = gather(energy, dipole) print(run(wf))
def example_freqs(): """ This examples illustrates the possibility to use different packages interchangeably. Analytical frequencies are not available for B3LYP in ADF This workflow captures the resulting error and submits the same job to ORCA. """ # Generate water molecule water = molkit.from_smiles('[OH2]', forcefield='mmff') # Pre-optimize the water molecule opt_water = dftb(templates.geometry, water, job_name="dftb_geometry") jobs = [] # Generate freq jobs for 3 functionals for functional in ['pbe', 'b3lyp', 'blyp']: s = Settings() s.basis = 'DZ' s.functional = functional # Try to perform the jobs with adf or orca, take result from first successful calculation freqjob = find_first_job(is_successful, [adf, orca], templates.freq.overlay(s), opt_water.molecule, job_name=functional) jobs.append(freqjob) # Run workflow results = run(gather(*jobs), n_processes=1) # extrac results freqs = [r.frequencies[-3:] for r in results] functionals = ['pbe', 'b3lyp', 'blyp'] # Print the result table = [ "{:10s}{:10.3f}{:10.3f}{:10.3f}\n".format(fun, *fs) for fun, fs in zip(functionals, freqs) ] print(table) return freqs
def example_partial_geometry_opt(): """Performa partial optimization freezing the Hydrogen atoms.""" methanol = molkit.from_smiles('CO') # optimize only H atoms s = Settings() s.freeze = [1, 2] geom_job1 = adf(templates.geometry.overlay(s), methanol, job_name='geom_job1').molecule # optimize only H atoms s = Settings() s.selected_atoms = ['H'] geom_job2 = adf(templates.geometry.overlay(s), methanol, job_name='geom_job2').molecule geom1, geom2 = run(gather(geom_job1, geom_job2), n_processes=1) return geom1, geom2
def example_freqs(): """ This examples illustrates the possibility to use different packages interchangeably. Analytical frequencies are not available for B3LYP in ADF This workflow captures the resulting error and submits the same job to ORCA. """ # Generate water molecule water = molkit.from_smiles('[OH2]', forcefield='mmff') # Pre-optimize the water molecule opt_water = dftb(templates.geometry, water, job_name="dftb_geometry") jobs = [] # Generate freq jobs for 3 functionals for functional in ['pbe', 'b3lyp', 'blyp']: s = Settings() s.basis = 'DZ' s.functional = functional # Try to perform the jobs with adf or orca, take result from first successful calculation freqjob = find_first_job(is_successful, [adf, orca], templates.freq.overlay(s), opt_water.molecule, job_name=functional) jobs.append(freqjob) # Run workflow results = run(gather(*jobs), n_processes=1) # extrac results freqs = [r.frequencies[-3:] for r in results] functionals = ['pbe', 'b3lyp', 'blyp'] # Print the result table = ["{:10s}{:10.3f}{:10.3f}{:10.3f}\n".format(fun, *fs) for fun, fs in zip(functionals, freqs)] print(table) return freqs
"""Mock the DFTB output.""" import numpy as np import scm.plams.interfaces.molecule.rdkit as molkit from assertionlib import assertion from pytest_mock import MockFixture from scm.plams import Molecule from qmflows import dftb, templates from qmflows.packages.SCM import DFTB_Result from qmflows.test_utils import PATH WORKDIR = PATH / "output_dftb" WATER = molkit.from_smiles('[OH2]', forcefield='mmff') def mock_runner(mocker_instance, jobname: str) -> DFTB_Result: """Create a Result instance using a mocked runner.""" run_mocked = mocker_instance.patch("qmflows.run") dill_path = WORKDIR / jobname / f"{jobname}.dill" plams_dir = WORKDIR / jobname run_mocked.return_value = DFTB_Result(templates.geometry, WATER, jobname, dill_path=dill_path, plams_dir=plams_dir) return run_mocked def test_dftb_opt_mock(mocker: MockFixture): """Mock a geometry optimization using DFTB.""" jobname = "dftb_geometry" job = dftb(templates.geometry, WATER, job_name=jobname) run_mocked = mock_runner(mocker, jobname)