5.2.1。调整范围分离

在这个例子中,我们优化了价值 伽玛 在ADF中的远程校正XC功能的参数(我们的案例:LCY-PBE)。 远程校正XC功能可用于XCFUN的ADF(参见 adf.手册)。

最佳范围分离参数 伽玛 产生等于电离电位(IP)的同性能。 给定分子系统,我们同时最小化HOMO和IP之间的差异,以及其阴离子(A)(具有一个电子的系统)。我们将J函数定义为:

\ [j = \ sqrt {n ^ 2 + a ^ 2} \]

并找到价值 伽玛 (在一定范围内)最小化J.

We define a new job type GammaJob by extending MultiJob. The goal of GammaJob is to calculate the J function for one fixed value of 伽玛 为此,我们需要执行3个不同的单点计算:1对于给定的系统(让我们调用S0),1对于具有一个电子(S-)和1的系统,为系统具有较少的电子(S +)。 S +计算需要找到S0的电离电位。

The constructor (__init__) of GammaJob accepts several new arguments and simply stores them. 这些新的论点定义:值的值 伽玛, 这 Molecule 与其初始电荷一起,以及S-,S0和S +的旋转值(作为长度3的元组)。 Then the prerun() 方法用于创建具有不同电荷值和自旋多数的不同值的三个子作业。 A dedicated Results 子类具有基于三个子项作业的结果提取j的值的简单方法:

class GammaResults(Results):

    @staticmethod
    def get_difference(job, jobplus):
        """Calculate the difference between HOMO and IP.
        *jobplus* should be the counterpart of *job* with one less electron."""
        homo = job.results.get_properties()['HOMO']
        IP = jobplus.results.get_energy() - job.results.get_energy()
        return IP + homo

    def get_J(self):
        N = GammaResults.get_difference(self.job.children[1], self.job.children[2])
        A = GammaResults.get_difference(self.job.children[0], self.job.children[1])
        return (N*N + A*A)**0.5

class GammaJob(MultiJob):
    _result_type = GammaResults

    def __init__(self, molecule, 伽玛, charge, spins, **kwargs):
        MultiJob.__init__(self, **kwargs)
        self.molecule = molecule
        self.charge = charge
        self.spins = spins
        self.伽玛 = 伽玛

    def prerun(self):
        charges = [self.charge-1, self.charge, self.charge+1]
        for charge, spin in zip(charges, self.spins):
            name = '{}_{}'.format(self.name, charge)
            newjob = adf.Job(name=name, molecule=self.molecule, settings=self.settings)
            newjob.settings.input.charge = '{} {}'.format(charge, spin)
            newjob.settings.input.xc.rangesep = "gamma={:f}".format(self.伽玛)
            if spin != 0:
                newjob.settings.input.unrestricted = True
            self.children.append(newjob)

Now we can treat our newly defined GammaJob as a blackbox with simple interface: input 伽玛 -> run -> extract J. The next step is to create multiple instances of GammaJob for a range of different 伽玛. 该任务可以方便地包装在一个简单的功能中:

def 伽玛_scan(伽玛, settings, molecule, name='gammascan', charge=0, spins=(1,0,1)):
    """Calculate values of J function for given range of gammas.

    Arguments:
    gammas   - list of gamma values to calculate the J function for
    settings - Settings object compatible with ADFJob
    molecule - Molecule object with the system of interest
    name     - base name of all the jobs
    charge   - base charge of the system of interest. The J function is going to be
               calculated based on two systems: with charge, and charge-1
    spins    - values of spin polarization (see keyword CHARGE of ADF) for jobs with,
               respectively, charge-1, charge and charge +1

    In other words, if charge=X and spins=(a,b,c) the three resulting jobs
    are going to have the following values of CHARGE keyword:

    CHARGE X-1  a
    CHARGE   X  b
    CHARGE X+1  c

    Returns a list of pairs (gamma, J) of the same length as the parameter *gammas*
    """
    jobs = [GammaJob(molecule=molecule, settings=settings, 伽玛=g,
            charge=charge, spins=spins, name=name+str(g)) for g in 伽玛]
    results = [j.run() for j in jobs]
    js = [r.get_J() for r in results]
    return list(zip(伽玛, js))

(或者,而不是函数,我们可以定义一种新类型 MultiJob 具有相同的功能。 Such a job would create a list of GammaJob instances as its children. The difference in that case is rather cosmetic: in case of a new job type all GammaJob data would be stored inside new job’s folder, whereas with the function defined above that data ends up directly in the main working folder. 在一个脚本中运行伽玛扫描的许多不同分子时,功能方法可能导致主工作夹以某种方式混乱,难以导航。 新的作业类型方法将在主工作夹的不同子文件夹中保留不同分子的数据。)

我们新定义的函数的示例用法:

from numpy import arange
config.default_jobrunner = JobRunner(parallel=True, maxjobs=8)

s = Settings()
s.input.basis.type = 'TZP'
s.input.basis.core = 'None'
s.input.xc.gga = 'PBE'
s.input.xc.xcfun = True
s.runscript.nproc = 1

mol = Molecule('somemolecule.xyz')
伽玛 = arange(0.4, 0.8, 0.02)

results = 伽玛_scan(伽玛, s, mol)

log('gamma \t J')
for g,j in results:
    log('{:.4f} \t {:.8f}'.format(g,j))
log('Optimal gamma value: {:.4f}'.format(min(results,key=lambda x:x[1])[0]))

All the code presented in above snippets can be put into a single file and executed with plams onebigfile.py (or $ADFBIN/plams onebigfile.py if $ADFBIN is not in your $PATH)。 Alternatively, one can place the definitions (of GammaJob and 伽玛_scan ) in one file 伽玛job.py and the execution in a separate small script rungamma.py and call it with plams 伽玛job.py rungamma.py.