Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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()
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
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
Beispiel #8
0
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]
Beispiel #9
0
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