Пример #1
0
    def __init__(self, system, model, fn_traj=None):
        '''
            **Arguments**

            system
                An instance of the System class and contains all the
                system information

            model
                An instance of the Model class and contains all the info
                to define the total PES and its electrostatic contribution.

            **Optional Arguments**

            fn_traj
                A file name to store the perturbation trajectories to or to
                read the trajectories from if the file exists. The trajectories
                are stored/read after Pickling.
        '''
        self.system = system
        self.model = model
        self.pert_theory = RelaxedGeoPertTheory(system, model)
        self.cost = HessianFCCost(system, model)
        self.fn_traj = fn_traj
        #SHLL 1508
        #-- new attributes for storing potential types
        self.stretch_pot_kind = system.stretch_pot_kind
        self.bend_pot_kind = system.bend_pot_kind
Пример #2
0
class Program(object):
    '''
        The central class to manage the entire program.
    '''
    def __init__(self, system, model, fn_traj=None):
        '''
            **Arguments**

            system
                An instance of the System class and contains all the
                system information

            model
                An instance of the Model class and contains all the info
                to define the total PES and its electrostatic contribution.

            **Optional Arguments**

            fn_traj
                A file name to store the perturbation trajectories to or to
                read the trajectories from if the file exists. The trajectories
                are stored/read after Pickling.
        '''
        self.system = system
        self.model = model
        self.pert_theory = RelaxedGeoPertTheory(system, model)
        self.cost = HessianFCCost(system, model)
        self.fn_traj = fn_traj
        #SHLL 1508
        #-- new attributes for storing potential types
        self.stretch_pot_kind = system.stretch_pot_kind
        self.bend_pot_kind = system.bend_pot_kind
        #SHLL end

    def generate_trajectories(self, skip_dihedrals=True, verbose=True):
        '''
            Generate a perturbation trajectory for all ics (dihedrals can be
            excluded and store the coordinates in a dictionary.

            **Optional Arguments**

            skip_dihedrals
                If set to True, the dihedral ff parameters will not
                be calculated.
        '''
        maxlength = max([len(icname) for icname in self.model.val.pot.terms.keys()]) + 2
        #Check if a filename with trajectories is given. If the file exists,
        #read it and return the trajectories
        if self.fn_traj is not None:
            if os.path.isfile(self.fn_traj):
                with open(self.fn_traj,'r') as f:
                    trajectories = cPickle.load(f)
                return trajectories
        #Generate trajectories from scratch
        trajectories = {}
        all_ics = []
        for icname in sorted(self.model.val.pot.terms.keys()):
            if skip_dihedrals and icname.startswith('dihed'):
                continue
            ics = self.system.ics[icname]
            for ic in ics:
                all_ics.append(ic)
            if verbose:
                print '    %s will generate %i trajectories' %(
                    icname+' '*(maxlength-len(icname)), len(ics)
                )
        results = paracontext.map(self.pert_theory.generate, all_ics)
        for i in xrange(len(all_ics)):
            trajectories[all_ics[i].name] = results[i]
        #Check if we need to write the generated trajectories to a file
        if self.fn_traj is not None:
            with open(self.fn_traj,'w') as f:
                cPickle.dump(trajectories,f)
        return trajectories

    #SHLL 1508
    #-- extra parameters not needed anymore
    # def estimate_from_pt(self, trajectories, skip_dihedrals=True, verbose=True, stretch_pot_kind='harmonic', bend_pot_kind='harmonic'):
    #original
    def estimate_from_pt(self, trajectories, skip_dihedrals=True, verbose=True):
    #SHLL end
        '''
            Second Step of force field development: calculate harmonic force field
            parameters for every internal coordinate separately from perturbation
            trajectories.

            **Arguments**

            trajectories
                A dictionairy containing numpy arrays representing perturbation
                trajectories for each icname.

            **Optional Arguments**

            skip_dihedrals
                If set to True, the dihedral ff parameters will not
                be calculated.
        '''
        ff = FFTable()
        #SHLL 1606
        #-- transfer potential info from program to fftable
        ff.stretch_pot_kind = self.stretch_pot_kind
        ff.bend_pot_kind = self.bend_pot_kind
        #SHLL end
        maxlength = max([len(icname) for icname in self.model.val.pot.terms.keys()]) + 2
        for icname in sorted(self.model.val.pot.terms.keys()):
            ics = self.system.ics[icname]
            if skip_dihedrals and icname.startswith('dihed'):
                continue
            ks  = DataArray(unit=ics[0].kunit)
#            print 'kunit:',ics[0].kunit #GBdebug
            q0s = DataArray(unit=ics[0].qunit)
            for ic in ics:
                #SHLL 1508
                #-- estimate spf or harmcos potential params
                if icname.startswith('bond') and self.stretch_pot_kind.lower()=='spf':
                    k, q0 = self.pert_theory.estimate(ic,trajectories[ic.name], pot_kind=self.stretch_pot_kind)
                elif icname.startswith('angle') and self.bend_pot_kind.lower()=='harmcos':
                    k, q0 = self.pert_theory.estimate(ic,trajectories[ic.name], pot_kind=self.bend_pot_kind)
                else:
                    k, q0 = self.pert_theory.estimate(ic,trajectories[ic.name], pot_kind='harmonic')
                #original
                # k, q0 = self.pert_theory.estimate(ic,trajectories[ic.name])
                #SHLL end
                ks.append(k)
                q0s.append(q0)
            ff.add(icname, ks, q0s)
            descr = icname + ' '*(maxlength-len(icname))
            if verbose:
                print '    %s   K = %s    q0 = %s' % (
                    descr, ks.string(), q0s.string()
                )
        self.model.val.update_fftable(ff)
        return ff

    def refine_cost(self, verbose=True):
        '''
            Second step of force field development: refine the force constants
            using a Hessian least squares cost function.
        '''
        fcs = self.cost.estimate()
        self.model.val.update_fcs(fcs)
        fftab = self.model.val.get_fftable()
        #SHLL 1606
        #-- transfer potential info from program to fftable
        fftab.stretch_pot_kind = self.stretch_pot_kind
        fftab.bend_pot_kind = self.bend_pot_kind
        #SHLL end
        if verbose:
            fftab.print_screen()
        return fftab

    def run(self):
        '''
            Run all steps of the QuickFF methodology to derive a covalent
            force field. This method returns an instance of the class
            :class:`quickff.fftable.FFTable`, which contains all force field
            parameters.
        '''
        print header
        print sysinfo()
        print '~'*120+'\n'
        print 'System information:\n'
        self.system.print_atom_info()
        print '\nModel information:\n'
        self.model.print_info()
        print '\nDetermine dihedral potentials\n'
        self.model.val.determine_dihedral_potentials(self.system)
        print '\nDetermine the coordinates of the perturbation trajectories\n'
        self.trajectories = self.generate_trajectories()
        print '\nEstimating all pars for bonds, bends and opdists\n'
        fftab = self.estimate_from_pt(self.trajectories)
        print '\nRefining force constants using a Hessian LSQ cost\n'
        fftab = self.refine_cost()
        print '\n'+'~'*120+'\n'
        print 'Time:           ' + datetime.datetime.now().isoformat().replace('T', ' ') + '\n'
        print footer
        return fftab

    def plot_pt(self, icname, start=None, end=None, steps=51, verbose=True):
        '''
            Generate the perturbation trajectories and plot the energy
            contributions along the trajectory for all ics with a name
            compatible with icname.

            **Arguments**

            icname
                A string describing for which ics the perturbation trajectories
                should be generated.
        '''
        #Logging
        if verbose:
            print header
            print sysinfo()
            print '~'*120+'\n'
            print 'System information:\n'
            self.system.print_atom_info()
            print '\nModel information:\n'
            self.model.print_info()
            print '\nDetermine the coordinates of the perturbation trajectories\n'
        #Reading/generating trajectories
        trajectories = {}
        if self.fn_traj is not None:
            if os.path.isfile(self.fn_traj):
                with open(self.fn_traj,'r') as f:
                    trajectories = cPickle.load(f)
        for i, ic in enumerate(self.system.ics[icname]):
            if ic.name in trajectories.keys():
                #already read
                if verbose:
                    print '    %s Read %2i/%i from %s' %(
                        icname, i+1, len(self.system.ics[icname]), self.fn_traj
                    )
            else:
                #generating
                if verbose:
                    sys.stdout.write('    %s Generating %2i/%i' %(
                        icname, i+1, len(self.system.ics[icname])
                    ))
                    sys.stdout.flush()
                try:
                    trajectories[ic.name] = self.pert_theory.generate(ic, start=start, end=end, steps=51)
                    print ''
                except KeyboardInterrupt:
                    if verbose:
                        sys.stdout.write(' INTERRUPTED\n')
                        sys.stdout.flush()
        #Writing trajectories
        if self.fn_traj is not None:
            with open(self.fn_traj,'w') as f:
                cPickle.dump(trajectories, f)
        #Plotting/writing output
        for ic in self.system.ics[icname]:
            if ic.name in trajectories.keys():
                name = ic.name.replace('/', '-')
                self.pert_theory.plot(ic, trajectories[ic.name], 'energies-'+name+'.pdf')
                self.pert_theory.write(trajectories[ic.name], 'trajectory-'+name+'.xyz')
        #Logging
        if verbose:
            print ''
            print '\n'+'~'*120+'\n'
            print 'Time:           ' + datetime.datetime.now().isoformat().replace('T', ' ') + '\n'
            print footer
Пример #3
0
    def do_hc_estimatefc(self, tasks, logger_level=3):
        '''
            Refine force constants using Hessian Cost function.

            **Arguments**

            tasks
                A list of strings identifying which terms should have their
                force constant estimated from the hessian cost function. Using
                such a flag, one can distinguish between for example force
                constant refinement (flag=HC_FC_DIAG) of the diagonal terms and
                force constant estimation of the cross terms (flag=HC_FC_CROSS).
                If the string 'all' is present in tasks, all fc's will be
                estimated.

            **Optional Arguments**

            logger_level
                print level at which the resulting parameters should be dumped to
                the logger. By default, the parameters will only be dumped at
                the highest log level.
        '''
        with log.section('HCEST', 2, timer='HC Estimate FC'):
            self.reset_system()
            log.dump(
                'Estimating force constants from Hessian cost for tasks %s' %
                ' '.join(tasks))
            ffrefs = self.kwargs.get('ffrefs', [])
            term_indices = []
            for index in xrange(self.valence.vlist.nv):
                term = self.valence.terms[index]
                flagged = False
                for flag in tasks:
                    if flag in term.tasks:
                        flagged = True
                        break
                if flagged:
                    #first check if all rest values and multiplicities have been defined
                    if term.kind == 0: self.valence.check_params(term, ['rv'])
                    if term.kind == 1:
                        self.valence.check_params(term,
                                                  ['a0', 'a1', 'a2', 'a3'])
                    if term.kind == 3:
                        self.valence.check_params(term, ['rv0', 'rv1'])
                    if term.kind == 4:
                        self.valence.check_params(term, ['rv', 'm'])
                    if term.is_master():
                        term_indices.append(index)
                else:
                    #first check if all pars have been defined
                    if term.kind == 0:
                        self.valence.check_params(term, ['fc', 'rv'])
                    if term.kind == 1:
                        self.valence.check_params(term,
                                                  ['a0', 'a1', 'a2', 'a3'])
                    if term.kind == 3:
                        self.valence.check_params(term, ['fc', 'rv0', 'rv1'])
                    if term.kind == 4:
                        self.valence.check_params(term, ['fc', 'rv', 'm'])
            cost = HessianFCCost(self.system,
                                 self.ai,
                                 self.valence,
                                 term_indices,
                                 ffrefs=ffrefs)
            fcs = cost.estimate()
            for index, fc in zip(term_indices, fcs):
                master = self.valence.terms[index]
                assert master.is_master()
                self.valence.set_params(index, fc=fc)
                for islave in master.slaves:
                    self.valence.set_params(islave, fc=fc)
            self.valence.dump_logger(print_level=logger_level)
Пример #4
0
    def do_hc_estimatefc(self,
                         tasks,
                         logger_level=3,
                         do_svd=False,
                         svd_rcond=0.0,
                         do_mass_weighting=True):
        '''
            Refine force constants using Hessian Cost function.

            **Arguments**

            tasks
                A list of strings identifying which terms should have their
                force constant estimated from the hessian cost function. Using
                such a flag, one can distinguish between for example force
                constant refinement (flag=HC_FC_DIAG) of the diagonal terms and
                force constant estimation of the cross terms (flag=HC_FC_CROSS).
                If the string 'all' is present in tasks, all fc's will be
                estimated.

            **Optional Arguments**

            logger_level
                print level at which the resulting parameters should be dumped to
                the logger. By default, the parameters will only be dumped at
                the highest log level.

            do_svd
                whether or not to do an SVD decomposition before solving the
                set of equations and explicitly throw out the degrees of
                freedom that correspond to the lowest singular values.

            do_mass_weighting
                whether or not to apply mass weighing to the ab initio hessian
                and the force field contributions before doing the fitting.
        '''
        with log.section('HCEST', 2, timer='HC Estimate FC'):
            self.reset_system()
            log.dump(
                'Estimating force constants from Hessian cost for tasks %s' %
                ' '.join(tasks))
            term_indices = []
            for index in range(self.valence.vlist.nv):
                term = self.valence.terms[index]
                flagged = False
                for flag in tasks:
                    if flag in term.tasks:
                        flagged = True
                        break
                if flagged:
                    #first check if all rest values and multiplicities have been defined
                    if term.kind == 0: self.valence.check_params(term, ['rv'])
                    if term.kind == 1:
                        self.valence.check_params(term,
                                                  ['a0', 'a1', 'a2', 'a3'])
                    if term.kind == 3:
                        self.valence.check_params(term, ['rv0', 'rv1'])
                    if term.kind == 4:
                        self.valence.check_params(term, ['rv', 'm'])
                    if term.is_master():
                        term_indices.append(index)
                else:
                    #first check if all pars have been defined
                    if term.kind == 0:
                        self.valence.check_params(term, ['fc', 'rv'])
                    if term.kind == 1:
                        self.valence.check_params(term,
                                                  ['a0', 'a1', 'a2', 'a3'])
                    if term.kind == 3:
                        self.valence.check_params(term, ['fc', 'rv0', 'rv1'])
                    if term.kind == 4:
                        self.valence.check_params(term, ['fc', 'rv', 'm'])
            if len(term_indices) == 0:
                log.dump(
                    'No terms (with task in %s) found to estimate FC from HC' %
                    (str(tasks)))
                return
            # Try to estimate force constants; if the remove_dysfunctional_cross
            # keyword is True, a loop is performed which checks whether there
            # are cross terms for which corresponding diagonal terms have zero
            # force constants. If this is the case, those cross terms are removed
            # from the fit and we try again until such cases do no longer occur
            max_iter = 100
            niter = 0
            while niter < max_iter:
                cost = HessianFCCost(self.system,
                                     self.ai,
                                     self.valence,
                                     term_indices,
                                     ffrefs=self.ffrefs,
                                     do_mass_weighting=do_mass_weighting)
                fcs = cost.estimate(do_svd=do_svd, svd_rcond=svd_rcond)
                # No need to continue, if cross terms with corresponding diagonal
                # terms with negative force constants are allowed
                if self.settings.remove_dysfunctional_cross is False: break
                to_remove = []
                for index, fc in zip(term_indices, fcs):
                    term = self.valence.terms[index]
                    if term.basename.startswith('Cross'):
                        # Find force constants of corresponding diagonal terms
                        diag_fcs = np.zeros((2))
                        for idiag in range(2):
                            diag_index = term.diag_term_indexes[idiag]
                            if diag_index in term_indices:
                                fc_diag = fcs[term_indices.index(diag_index)]
                            else:
                                fc_diag = self.valence.get_params(diag_index,
                                                                  only='fc')
                            diag_fcs[idiag] = fc_diag
                        # If a force constant from any corresponding diagonal term is negative,
                        # we remove the cross term for the next iteration
                        if np.any(diag_fcs <= 0.0):
                            to_remove.append(index)
                            self.valence.set_params(index, fc=0.0)
                            log.dump(
                                'WARNING! Dysfunctional cross term %s detected, removing from the hessian fit.'
                                % term.basename)
                if len(to_remove) == 0: break
                else:
                    for index in to_remove:
                        term_indices.remove(index)
                niter += 1
            assert niter < max_iter, "Could not remove all dysfunctional cross terms in %d iterations, something is seriously wrong" % max_iter
            for index, fc in zip(term_indices, fcs):
                master = self.valence.terms[index]
                assert master.is_master()
                self.valence.set_params(index, fc=fc)
                for islave in master.slaves:
                    self.valence.set_params(islave, fc=fc)
            self.valence.dump_logger(print_level=logger_level)
Пример #5
0
    def do_hc_estimatefc(self, tasks, logger_level=3, do_svd=False, svd_rcond=0.0, do_mass_weighting=True):
        '''
            Refine force constants using Hessian Cost function.

            **Arguments**

            tasks
                A list of strings identifying which terms should have their
                force constant estimated from the hessian cost function. Using
                such a flag, one can distinguish between for example force
                constant refinement (flag=HC_FC_DIAG) of the diagonal terms and
                force constant estimation of the cross terms (flag=HC_FC_CROSS).
                If the string 'all' is present in tasks, all fc's will be
                estimated.

            **Optional Arguments**

            logger_level
                print level at which the resulting parameters should be dumped to
                the logger. By default, the parameters will only be dumped at
                the highest log level.

            do_svd
                whether or not to do an SVD decomposition before solving the
                set of equations and explicitly throw out the degrees of
                freedom that correspond to the lowest singular values.

            do_mass_weighting
                whether or not to apply mass weighing to the ab initio hessian
                and the force field contributions before doing the fitting.
        '''
        with log.section('HCEST', 2, timer='HC Estimate FC'):
            self.reset_system()
            log.dump('Estimating force constants from Hessian cost for tasks %s' %' '.join(tasks))
            term_indices = []
            for index in range(self.valence.vlist.nv):
                term = self.valence.terms[index]
                flagged = False
                for flag in tasks:
                    if flag in term.tasks:
                        flagged = True
                        break
                if flagged:
                    #first check if all rest values and multiplicities have been defined
                    if term.kind==0: self.valence.check_params(term, ['rv'])
                    if term.kind==1: self.valence.check_params(term, ['a0', 'a1', 'a2', 'a3'])
                    if term.kind==3: self.valence.check_params(term, ['rv0','rv1'])
                    if term.kind==4: self.valence.check_params(term, ['rv', 'm'])
                    if term.is_master():
                        term_indices.append(index)
                else:
                    #first check if all pars have been defined
                    if term.kind==0: self.valence.check_params(term, ['fc', 'rv'])
                    if term.kind==1: self.valence.check_params(term, ['a0', 'a1', 'a2', 'a3'])
                    if term.kind==3: self.valence.check_params(term, ['fc', 'rv0','rv1'])
                    if term.kind==4: self.valence.check_params(term, ['fc', 'rv', 'm'])
            if len(term_indices)==0:
                log.dump('No terms (with task in %s) found to estimate FC from HC' %(str(tasks)))
                return
            # Try to estimate force constants; if the remove_dysfunctional_cross
            # keyword is True, a loop is performed which checks whether there
            # are cross terms for which corresponding diagonal terms have zero
            # force constants. If this is the case, those cross terms are removed
            # from the fit and we try again until such cases do no longer occur
            max_iter = 100
            niter = 0
            while niter<max_iter:
                cost = HessianFCCost(self.system, self.ai, self.valence, term_indices, ffrefs=self.ffrefs, do_mass_weighting=do_mass_weighting)
                fcs = cost.estimate(do_svd=do_svd, svd_rcond=svd_rcond)
                # No need to continue, if cross terms with corresponding diagonal
                # terms with negative force constants are allowed
                if self.settings.remove_dysfunctional_cross is False: break
                to_remove = []
                for index, fc in zip(term_indices, fcs):
                    term = self.valence.terms[index]
                    if term.basename.startswith('Cross'):
                        # Find force constants of corresponding diagonal terms
                        diag_fcs = np.zeros((2))
                        for idiag in range(2):
                            diag_index = term.diag_term_indexes[idiag]
                            if diag_index in term_indices:
                                fc_diag = fcs[term_indices.index(diag_index)]
                            else:
                                fc_diag = self.valence.get_params(diag_index, only='fc')
                            diag_fcs[idiag] = fc_diag
                        # If a force constant from any corresponding diagonal term is negative,
                        # we remove the cross term for the next iteration
                        if np.any(diag_fcs<=0.0):
                            to_remove.append(index)
                            self.valence.set_params(index, fc=0.0)
                            log.dump('WARNING! Dysfunctional cross term %s detected, removing from the hessian fit.'%term.basename)
                if len(to_remove)==0: break
                else:
                    for index in to_remove:
                        term_indices.remove(index)
                niter += 1
            assert niter<max_iter, "Could not remove all dysfunctional cross terms in %d iterations, something is seriously wrong"%max_iter
            for index, fc in zip(term_indices, fcs):
                master = self.valence.terms[index]
                assert master.is_master()
                self.valence.set_params(index, fc=fc)
                for islave in master.slaves:
                    self.valence.set_params(islave, fc=fc)
            self.valence.dump_logger(print_level=logger_level)
Пример #6
0
    def do_hc_estimatefc(self,
                         tasks,
                         logger_level=3,
                         do_svd=False,
                         do_mass_weighting=True):
        '''
            Refine force constants using Hessian Cost function.

            **Arguments**

            tasks
                A list of strings identifying which terms should have their
                force constant estimated from the hessian cost function. Using
                such a flag, one can distinguish between for example force
                constant refinement (flag=HC_FC_DIAG) of the diagonal terms and
                force constant estimation of the cross terms (flag=HC_FC_CROSS).
                If the string 'all' is present in tasks, all fc's will be
                estimated.

            **Optional Arguments**

            logger_level
                print level at which the resulting parameters should be dumped to
                the logger. By default, the parameters will only be dumped at
                the highest log level.

            do_svd
                whether or not to do an SVD decomposition before solving the
                set of equations and explicitly throw out the degrees of
                freedom that correspond to the lowest singular values.

            do_mass_weighting
                whether or not to apply mass weighing to the ab initio hessian
                and the force field contributions before doing the fitting.
        '''
        with log.section('HCEST', 2, timer='HC Estimate FC'):
            self.reset_system()
            log.dump(
                'Estimating force constants from Hessian cost for tasks %s' %
                ' '.join(tasks))
            term_indices = []
            for index in range(self.valence.vlist.nv):
                term = self.valence.terms[index]
                flagged = False
                for flag in tasks:
                    if flag in term.tasks:
                        flagged = True
                        break
                if flagged:
                    #first check if all rest values and multiplicities have been defined
                    if term.kind == 0: self.valence.check_params(term, ['rv'])
                    if term.kind == 1:
                        self.valence.check_params(term,
                                                  ['a0', 'a1', 'a2', 'a3'])
                    if term.kind == 3:
                        self.valence.check_params(term, ['rv0', 'rv1'])
                    if term.kind == 4:
                        self.valence.check_params(term, ['rv', 'm'])
                    if term.is_master():
                        term_indices.append(index)
                else:
                    #first check if all pars have been defined
                    if term.kind == 0:
                        self.valence.check_params(term, ['fc', 'rv'])
                    if term.kind == 1:
                        self.valence.check_params(term,
                                                  ['a0', 'a1', 'a2', 'a3'])
                    if term.kind == 3:
                        self.valence.check_params(term, ['fc', 'rv0', 'rv1'])
                    if term.kind == 4:
                        self.valence.check_params(term, ['fc', 'rv', 'm'])
            if len(term_indices) == 0:
                log.dump(
                    'No terms (with task in %s) found to estimate FC from HC' %
                    (str(tasks)))
                return
            cost = HessianFCCost(self.system,
                                 self.ai,
                                 self.valence,
                                 term_indices,
                                 ffrefs=self.ffrefs,
                                 do_mass_weighting=do_mass_weighting)
            fcs = cost.estimate(do_svd=do_svd)
            for index, fc in zip(term_indices, fcs):
                master = self.valence.terms[index]
                assert master.is_master()
                self.valence.set_params(index, fc=fc)
                for islave in master.slaves:
                    self.valence.set_params(islave, fc=fc)
            self.valence.dump_logger(print_level=logger_level)