def update_adf_defaults(ams_settings: Settings) -> None: """Add the COSMO-RS compound defaults to *ams_settings*, returning a copy of the new settings. The engine block (*ams_settings.input.adf*) is soft updated with the following Settings: .. code:: Engine ADF Basis Type TZP Core Small End XC GGA BP86 End Relativity Level Scalar End BeckeGrid Quality Good End EndEngine """ # Find the solvation key # solvation = ams_settings.input.adf.find_case('solvation') # solvation_block = ams_settings.input.adf[solvation] adf = ams_settings.input.find_case('adf') adf_block = ams_settings.input[adf] # Find all keys for within the adf block keys = ('basis', 'xc', 'relativity', 'BeckeGrid') basis, xc, relativity, BeckeGrid = [ adf_block.find_case(item) for item in keys ] # Construct the default solvation block adf_defaults_block = Settings({ basis: { 'Type': 'TZP', 'Core': 'Small' }, xc: { 'GGA': 'BP86' }, relativity: { 'Level': 'Scalar' }, BeckeGrid: { 'Quality': 'Good' } }) # Copy ams_settings and perform a soft update ret = ams_settings.copy() ret.input.adf.soft_update(adf_defaults_block) return ret
def _md2opt(s: Settings) -> Settings: """Convert CP2K MD settings to CP2K geometry optimization settings.""" s2 = s.copy() del s2.input.motion.md s2.input['global'].run_type = 'geometry_optimization' # Delete all user-specified parameters; rely on MATCH del s2.input.force_eval.mm.forcefield.charge del s2.input.force_eval.mm.forcefield.nonbonded return s2
def test_settings_to_molecule(): s = Settings() s.input.ams.system.Atoms._1 = ' H 0.0000000000 0.0000000000 0.0000000000 ' s.input.ams.system.Atoms._2 = ' O 1.0000000000 0.0000000000 0.0000000000 ' t = s.copy() mol = AMSJob.settings_to_mol(t)[''] assert AMSJob(settings=s).get_input() == AMSJob(settings=t, molecule=mol).get_input() s = Settings() s.input.ams.system.Atoms._1 = [ ' H 0.0000000000 0.0000000000 0.0000000000 ', ' O 1.0000000000 0.0000000000 0.0000000000 ' ] t = s.copy() mol = AMSJob.settings_to_mol(t)[''] assert AMSJob(settings=s).get_input() == AMSJob(settings=t, molecule=mol).get_input() #test for headers: s.input.ams.system.Atoms._h = '[A]' s.input.ams.system.Atoms._1 = [ ' O -0.0509000000 -0.2754000000 0.6371000000 ForceField.Charge=-0.834 ForceField.Type=OW', ' H 0.0157000000 0.5063000000 0.0531000000 ForceField.Charge=0.417 ForceField.Type=HW', ' H -0.0055000000 -1.0411000000 0.0658000000 ForceField.Charge=0.417 ForceField.Type=HW', ' O 0.0981000000 1.7960000000 -1.2550000000 ForceField.Charge=-0.834 ForceField.Type=OW', ' H -0.6686000000 2.2908000000 -1.5343000000 ForceField.Charge=0.417 ForceField.Type=HW', ' H 0.8128000000 2.3488000000 -1.5619000000 ForceField.Charge=0.417 ForceField.Type=HW', ] t = s.copy() #currently plams completely ignores the [A] unit specifier, it might also not print it if it does #get implemented and instead just convert the values in the molecule. del s.input.ams.system.Atoms._h mol = AMSJob.settings_to_mol(t)[''] assert AMSJob(settings=s).get_input() == AMSJob(settings=t, molecule=mol).get_input()
def get_pka(mol: Molecule, coskf_mol: Optional[str], coskf_mol_conj: Optional[str], water: str, hydronium: str, job: Type[Job], s: Settings) -> List[float]: if coskf_mol is None: return 5 * [np.nan] elif coskf_mol_conj is None: return 5 * [np.nan] s = Settings(s) s.input.compound[1]._h = water s.ignore_molecule = True s_dict = {} for name, coskf in _iter_coskf(coskf_mol_conj, coskf_mol, water, hydronium): _s = s.copy() _s.name = name _s.input.compound[0]._h = coskf s_dict[name] = _s # Run the job mol_name = mol.properties.name job_list = [CRSJob(settings=s, name=name) for name, s in s_dict.items()] results_list = [_crs_run(job, mol_name) for job in job_list] # Extract solvation energies and activity coefficients E_solv = {} for name, results in zip(("acid", "base", "solvent", "solvent_conj"), results_list): results.wait() try: E_solv[name] = _E = results.get_energy() assert _E is not None logger.info(f'{results.job.__class__.__name__}: {mol_name} pKa ' f'calculation ({results.job.name}) is successful') except Exception: logger.error(f'{results.job.__class__.__name__}: {mol_name} pKa ' f'calculation ({results.job.name}) has failed') E_solv[name] = np.nan try: mol.properties.job_path += [ join(job.path, job.name + '.in') for job in job_list ] except IndexError: # The 'job_path' key is not available mol.properties.job_path = [ join(job.path, job.name + '.in') for job in job_list ] ret = [E_solv[k] for k in ("acid", "base", "solvent", "solvent_conj")] ret.append(_get_pka(**E_solv)) return ret
def run_cdft_job(mol: Molecule, job: Type[ADFJob], s: Settings) -> pd.Series: """Run a conceptual DFT job and extract & return all global descriptors.""" results = mol.job_single_point(job, s.copy(), name='CDFT', ret_results=True, read_template=False) if results.job.status in {'crashed', 'failed'}: return _BACKUP ret = get_global_descriptors(results) ret.index = pd.MultiIndex.from_product([['cdft'], ret.index], names=['index', 'sub index']) return ret
def _get_logp(s: Settings, name: str, logger: logging.Logger) -> float: logp_s = s.copy() logp_s.update(get_template('qd.yaml')['COSMO-RS logp']) for v in logp_s.input.compound: v._h = v._h.format(os.environ["ADFRESOURCES"]) logp_job = CRSJob(settings=logp_s, name='LogP') results = _crs_run(logp_job, name) try: logp = results.readkf('LOGP', 'logp')[0] logger.info(f'{results.job.__class__.__name__}: {name} LogP ' f'calculation ({results.job.name}) is successful') except Exception: logger.error(f'{results.job.__class__.__name__}: {name} LogP ' f'calculation ({results.job.name}) has failed') logp = np.nan return logp
def run_amsjob(mol: Molecule, s: Settings, **kwargs) -> AMSResults: """Run an :class:`.AMSJob` on *mol* using the settings provided in *s*.""" name = 'AMSJob.' + mol.properties.name if 'name' in mol.properties else 'AMSJob.mol' # Do a geometry optimization in the gas phase _s = s.copy() _s.input.ams.soft_update({"Task": "GeometryOptimization"}) job1 = AMSJob(molecule=mol, settings=_s, name=name + '.gas') del job1.settings.input.adf.solvation results1 = job1.run(**kwargs) ams_out = results1['ams.rkf'] adf_out = results1['adf.rkf'] # Construct the AMS COSMO surface _s.input.ams.Task = "SinglePoint" _s.input.ams.EngineRestart = adf_out _s.input.ams.LoadSystem.File = ams_out job2 = AMSJob(molecule=None, settings=_s, depend=[job1], name=name) results2 = job2.run(**kwargs) coskf_name = job2.name.split('.')[1] convert_to_coskf(results2, coskf_name) return results2
def get_solv(mol: Molecule, solvent_list: Iterable[str], coskf: Optional[str], job: Type[Job], s: Settings) -> List[float]: """Calculate the solvation energy of *mol* in various *solvents*. Parameters ---------- mol : |plams.Molecule|_ A PLAMS Molecule. solvent_list : |List|_ [|str|_] A list of solvent molecules (*i.e.* .coskf files). coskf : str, optional The path+filename of the .coskf file of **mol**. job : |Callable|_ A type Callable of a class derived from :class:`Job`, e.g. :class:`AMSJob` or :class:`Cp2kJob`. s : |plams.Settings|_ The settings for **job**. Returns ------- |list|_ [|float|_] & |list|_ [|float|_] A list of solvation energies and gammas. """ # Return 3x np.nan if no coskf is None (i.e. the COSMO-surface construction failed) if coskf is None: i = 1 + 2 * len(solvent_list) return i * [np.nan] # Prepare a list of job settings s = Settings(s) s.input.compound[0]._h = coskf s.ignore_molecule = True s_list = [] for solv in solvent_list: _s = s.copy() _s.name = solv.rsplit('.', 1)[0].rsplit(os.sep, 1)[-1] _s.input.compound[1]._h = solv s_list.append(_s) # Run the job mol_name = mol.properties.name job_list = [CRSJob(settings=s, name=s.name) for s in s_list] results_list = [ _crs_run(job, mol_name, calc_type="pKa") for job in job_list ] # Extract solvation energies and activity coefficients E_solv = [] Gamma = [] for results in results_list: results.wait() try: E_solv.append(results.get_energy()) Gamma.append(results.get_activity_coefficient()) logger.info( f'{results.job.__class__.__name__}: {mol_name} activity coefficient ' f'calculation ({results.job.name}) is successful') except Exception: logger.error( f'{results.job.__class__.__name__}: {mol_name} activity coefficient ' f'calculation ({results.job.name}) has failed') E_solv.append(np.nan) Gamma.append(np.nan) try: mol.properties.job_path += [ join(job.path, job.name + '.in') for job in job_list ] except IndexError: # The 'job_path' key is not available mol.properties.job_path = [ join(job.path, job.name + '.in') for job in job_list ] logp = _get_logp(s_list[0], name=mol_name, logger=logger) return E_solv + Gamma + [logp]
def add_solvation_block(ams_settings: Settings) -> None: """Add the solvation block to *ams_settings*, returning a copy of the new settings. The solvation block (*ams_settings.input.solvation*) is soft updated with the following Settings: .. code:: c-mat: Exact charged: method=Conj radii: Br: 2.16 C: 2.0 Cl: 2.05 F: 1.72 H: 1.3 I: 2.32 N: 1.83 O: 1.72 P: 2.13 S: 2.16 Si: 2.48 scf: Var All solv: name=CRS cav0=0.0 cav1=0.0 surf: Delley """ # Find the solvation key solvation = ams_settings.input.adf.find_case('solvation') solvation_block = ams_settings.input.adf[solvation] # Find all keys for within the solvation block keys = ('surf', 'solv', 'charged', 'c-mat', 'scf', 'radii') surf, solv, charged, cmat, scf, radii = [ solvation_block.find_case(item) for item in keys ] # Construct the default solvation block solvation_block_new = { surf: 'Delley', solv: 'name=CRS cav0=0.0 cav1=0.0', charged: 'method=Conj', cmat: 'Exact', scf: 'Var All', radii: { 'H': 1.30, 'C': 2.00, 'N': 1.83, 'O': 1.72, 'F': 1.72, 'Si': 2.48, 'P': 2.13, 'S': 2.16, 'Cl': 2.05, 'Br': 2.16, 'I': 2.32 } } # Copy ams_settings and perform a soft update ret = ams_settings.copy() ret.input.adf[solvation].soft_update(solvation_block_new) return ret