Example #1
0
def qd_opt(mol: Molecule,
           jobs: Tuple[Optional[Type[Job]], ...],
           settings: Tuple[Optional[Settings], ...],
           use_ff: bool = False) -> None:
    """Perform an optimization of the quantum dot.

    Performs an inplace update of **mol**.

    Parameters
    ----------
    mol : |plams.Molecule|_
        The to-be optimized molecule.

    job_recipe : |plams.Settings|_
        A Settings instance containing all jon settings.
        Expects 4 keys: ``"job1"``, ``"job2"``, ``"s1"``, ``"s2"``.

    forcefield : bool
        If ``True``, perform the job with CP2K with a user-specified forcefield.

    """
    # Prepare the job settings
    if use_ff:
        qd_opt_ff(mol, jobs, settings)
        return None

    # Expand arguments
    job1, job2 = jobs
    s1, s2 = settings

    # Extra options for AMSJob
    if job1 is AMSJob:
        s1 = Settings(s1)
        s1.input.ams.constraints.atom = mol.properties.indices
    if job2 is AMSJob:
        s2 = Settings(s2)
        s2.input.ams.constraints.atom = mol.properties.indices

    # Run the first job and fix broken angles
    mol.job_geometry_opt(job1, s1, name='QD_opt_part1')
    fix_carboxyl(mol)
    fix_h(mol)
    mol.round_coords()

    # Run the second job
    if job2 is not None:
        mol.job_geometry_opt(job2, s2, name='QD_opt_part2')
        mol.round_coords()
    return None
Example #2
0
def qd_opt_ff(mol: Molecule,
              jobs: Tuple[Optional[Type[Job]], ...],
              settings: Tuple[Optional[Settings], ...],
              name: str = 'QD_opt',
              new_psf: bool = False,
              job_func: Callable = Molecule.job_geometry_opt) -> Results:
    """Alternative implementation of :func:`.qd_opt` using CP2Ks' classical forcefields.

    Performs an inplace update of **mol**.

    Parameters
    ----------
    mol : |plams.Molecule|_
        The to-be optimized molecule.

    jobs : :class:`tuple`
        A tuple of |plams.Job| types and/or ``None``.

    settings : :class:`tuple`
        A tuple of |plams.Settings| types and/or ``None``.

    name : str
        The name of the job.

    See Also
    --------
    :func:`CAT.attachment.qd_opt.qd_opt`
        Default workflow for optimizing molecules.

    """
    psf_name = os.path.join(mol.properties.path, mol.properties.name + '.psf')

    # Prepare the job settings
    job = jobs[0] if isinstance(jobs, abc.Sequence) else jobs
    s = Settings(settings[0]) if isinstance(
        settings, abc.Sequence) else Settings(settings)

    s.runscript.pre = (f'ln "{psf_name}" ./"{name}.psf"\n'
                       f'ln "{mol.properties.prm}" ./"{name}.prm"')
    s.input.force_eval.subsys.topology.conn_file_name = f'{name}.psf'
    s.input.force_eval.mm.forcefield.parm_file_name = f'{name}.prm'
    set_cp2k_element(s, mol)

    if not os.path.isfile(psf_name) or new_psf:
        psf = get_psf(mol,
                      s.input.force_eval.mm.forcefield.get('charge', None))
        for at, charge, symbol in zip(mol, psf.charge, psf.atom_type):
            at.properties.charge_float = charge
            at.properties.symbol = symbol
        psf.write(psf_name)

    # Pull any missing non-covalent parameters from UFF
    if s.input.force_eval.mm.forcefield.nonbonded.get('lennard-jones', None):
        try:
            finalize_lj(
                mol,
                s.input.force_eval.mm.forcefield.nonbonded['lennard-jones'])
        except TypeError:
            pass
    results = job_func(mol,
                       job,
                       s,
                       name=name,
                       read_template=False,
                       ret_results=True)
    mol.round_coords()

    return results
Example #3
0
def _asa_plams_ff(
        mol_complete: Molecule, ligands: Iterable[Molecule], core: Molecule,
        read_template: bool, job: Type[Job],
        settings: Settings) -> Tuple[float, float, float, float, int]:
    """Perform an activation strain analyses with custom Job, Settings and forcefield.

    Parameters
    ----------
    mol_complete : |plams.Molecule|
        A Molecule representing the (unfragmented) relaxed structure of the system of interest.

    ligands : :class:`Iterable<collections.abc.Iterable>` [|plams.Molecule|]
        An iterable of Molecules containing all ligands in mol_complete.

    core : |plams.Molecule|, optional
        The core molecule from **mol_complete**.

    job : :class:`type` [|plams.Job|]
        The Job type for the ASA calculations.

    settings : |plams.Settings|
        The Job Settings for the ASA calculations.

    Returns
    -------
    :class:`float`, :class:`float`, :class:`float`, :class:`float` and :class:`int`
        The energy of **mol_complete**,
        the energy of **ligands**,
        the energy of **core**,
        the energy of an optimized fragment within **ligands** and
        the total number of fragments within **ligands**.

    """
    s = Settings(settings)

    # Calculate the (summed) energy of each individual ligand fragment in the total system
    E_ligands = 0.0
    E_min = np.inf
    mol_min = None
    for ligand_count, mol in enumerate(ligands, 1):
        mol.round_coords()
        qd_opt_ff(mol,
                  job,
                  s,
                  name='ASA_sp',
                  new_psf=True,
                  job_func=Molecule.job_single_point)
        E = mol.properties.energy.E
        E_ligands += E if E is not None else np.nan
        if E < E_min:
            E_min, mol_min = E, mol

    # Calculate the energy of the core fragment
    core.round_coords()
    qd_opt_ff(core,
              job,
              s,
              name='ASA_sp',
              new_psf=True,
              job_func=Molecule.job_single_point)
    E_core = core.properties.energy.E

    # Calculate the energy of the total system
    mol_complete.round_coords()
    qd_opt_ff(mol_complete,
              job,
              s,
              name='ASA_sp',
              new_psf=True,
              job_func=Molecule.job_single_point)
    E_complete = mol_complete.properties.energy.E

    # One of the calculations failed; better stop now
    if np.isnan(E_ligands):
        return np.nan, np.nan, np.nan, np.nan, ligand_count

    # Calculate the energy of an optimized fragment
    s.input.motion.geo_opt.soft_update({
        'type': 'minimization',
        'optimizer': 'LBFGS',
        'max_iter': 1000,
        'lbfgs': {
            'max_h_rank': 100
        }
    })
    s.input['global'].run_type = 'geometry_optimization'
    qd_opt_ff(mol_min, job, s, name='ASA_opt')
    E_ligand_opt = mol_min.properties.energy.E

    return E_complete, E_ligands, E_core, E_ligand_opt, ligand_count
Example #4
0
def _asa_plams(mol_complete: Molecule, ligands: Iterable[Molecule],
               core: Molecule, read_template: bool, job: Type[Job],
               settings: Settings) -> Tuple[float, float, float, float, int]:
    """Perform an activation strain analyses with custom Job and Settings.

    Parameters
    ----------
    mol_complete : |plams.Molecule|
        A Molecule representing the (unfragmented) relaxed structure of the system of interest.

    ligands : :class:`Iterable<collections.abc.Iterable>` [|plams.Molecule|]
        An iterable of Molecules containing all ligands in mol_complete.

    core : |plams.Molecule|, optional
        The core molecule from **mol_complete**.

    job : :class:`type` [|plams.Job|]
        The Job type for the ASA calculations.

    settings : |plams.Settings|
        The Job Settings for the ASA calculations.

    Returns
    -------
    :class:`float`, :class:`float`, :class:`float`, :class:`float` and :class:`int`
        The energy of **mol_complete**,
        the energy of **ligands**,
        the energy of **core**,
        the energy of an optimized fragment within **ligands** and
        the total number of fragments within **ligands**.

    """
    s = settings

    # Calculate the energy of the total system
    mol_complete.round_coords()
    mol_complete.properties.name += '_frags'
    mol_complete.job_single_point(job, s)
    E_complete = mol_complete.properties.energy.E

    # Calculate the (summed) energy of each individual fragment in the total system
    E_ligands = 0.0
    E_min = np.inf
    mol_min = None
    for ligand_count, mol in enumerate(ligands, 1):
        mol.round_coords()
        mol.job_single_point(job, s)
        E = mol.properties.energy.E
        E_ligands += E
        if E < E_min:
            E_min, mol_min = E, mol

    # One of the calculations failed; better stop now
    if np.isnan(E_ligands):
        return np.nan, np.nan, np.nan, np.nan, ligand_count

    # Calculate the energy of the core
    core.job_single_point(job, s)
    E_core = mol.properties.energy.E

    # Calculate the energy of an optimizes fragment
    mol_min.job_geometry_opt(job, s)
    E_ligand_opt = mol_min.properties.energy.E

    return E_complete, E_ligands, E_core, E_ligand_opt, ligand_count